|
|
@ -994,7 +994,8 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, |
|
|
|
void ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, |
|
|
|
const char *cmd, |
|
|
|
const char *method, const char *url, |
|
|
|
|
|
|
|
const char *headers, |
|
|
|
const unsigned char *send_content, |
|
|
|
const unsigned char *send_content, |
|
|
|
int send_content_length) |
|
|
|
int send_content_length) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -1002,10 +1003,13 @@ void ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, |
|
|
|
char buf[4096], buf1[1024]; |
|
|
|
char buf[4096], buf1[1024]; |
|
|
|
|
|
|
|
|
|
|
|
rt->seq++; |
|
|
|
rt->seq++; |
|
|
|
av_strlcpy(buf, cmd, sizeof(buf)); |
|
|
|
snprintf(buf, sizeof(buf), "%s %s RTSP/1.0\r\n", method, url); |
|
|
|
|
|
|
|
if (headers) |
|
|
|
|
|
|
|
av_strlcat(buf, headers, sizeof(buf)); |
|
|
|
snprintf(buf1, sizeof(buf1), "CSeq: %d\r\n", rt->seq); |
|
|
|
snprintf(buf1, sizeof(buf1), "CSeq: %d\r\n", rt->seq); |
|
|
|
av_strlcat(buf, buf1, sizeof(buf)); |
|
|
|
av_strlcat(buf, buf1, sizeof(buf)); |
|
|
|
if (rt->session_id[0] != '\0' && !strstr(cmd, "\nIf-Match:")) { |
|
|
|
if (rt->session_id[0] != '\0' && (!headers || |
|
|
|
|
|
|
|
!strstr(headers, "\nIf-Match:"))) { |
|
|
|
snprintf(buf1, sizeof(buf1), "Session: %s\r\n", rt->session_id); |
|
|
|
snprintf(buf1, sizeof(buf1), "Session: %s\r\n", rt->session_id); |
|
|
|
av_strlcat(buf, buf1, sizeof(buf)); |
|
|
|
av_strlcat(buf, buf1, sizeof(buf)); |
|
|
|
} |
|
|
|
} |
|
|
@ -1025,26 +1029,30 @@ void ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, |
|
|
|
rt->last_cmd_time = av_gettime(); |
|
|
|
rt->last_cmd_time = av_gettime(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void ff_rtsp_send_cmd_async(AVFormatContext *s, const char *cmd) |
|
|
|
void ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method, |
|
|
|
|
|
|
|
const char *url, const char *headers) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ff_rtsp_send_cmd_with_content_async(s, cmd, NULL, 0); |
|
|
|
ff_rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void ff_rtsp_send_cmd(AVFormatContext *s, |
|
|
|
void ff_rtsp_send_cmd(AVFormatContext *s, const char *method, const char *url, |
|
|
|
const char *cmd, RTSPMessageHeader *reply, |
|
|
|
const char *headers, RTSPMessageHeader *reply, |
|
|
|
unsigned char **content_ptr) |
|
|
|
unsigned char **content_ptr) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ff_rtsp_send_cmd_with_content(s, cmd, reply, content_ptr, NULL, 0); |
|
|
|
ff_rtsp_send_cmd_with_content(s, method, url, headers, reply, |
|
|
|
|
|
|
|
content_ptr, NULL, 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void ff_rtsp_send_cmd_with_content(AVFormatContext *s, |
|
|
|
void ff_rtsp_send_cmd_with_content(AVFormatContext *s, |
|
|
|
const char *cmd, |
|
|
|
const char *method, const char *url, |
|
|
|
|
|
|
|
const char *header, |
|
|
|
RTSPMessageHeader *reply, |
|
|
|
RTSPMessageHeader *reply, |
|
|
|
unsigned char **content_ptr, |
|
|
|
unsigned char **content_ptr, |
|
|
|
const unsigned char *send_content, |
|
|
|
const unsigned char *send_content, |
|
|
|
int send_content_length) |
|
|
|
int send_content_length) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ff_rtsp_send_cmd_with_content_async(s, cmd, send_content, send_content_length); |
|
|
|
ff_rtsp_send_cmd_with_content_async(s, method, url, header, |
|
|
|
|
|
|
|
send_content, send_content_length); |
|
|
|
|
|
|
|
|
|
|
|
ff_rtsp_read_reply(s, reply, content_ptr, 0); |
|
|
|
ff_rtsp_read_reply(s, reply, content_ptr, 0); |
|
|
|
} |
|
|
|
} |
|
|
@ -1173,9 +1181,8 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, |
|
|
|
rt->server_type == RTSP_SERVER_WMS) |
|
|
|
rt->server_type == RTSP_SERVER_WMS) |
|
|
|
av_strlcat(transport, ";mode=play", sizeof(transport)); |
|
|
|
av_strlcat(transport, ";mode=play", sizeof(transport)); |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
"SETUP %s RTSP/1.0\r\n" |
|
|
|
|
|
|
|
"Transport: %s\r\n", |
|
|
|
"Transport: %s\r\n", |
|
|
|
rtsp_st->control_url, transport); |
|
|
|
transport); |
|
|
|
if (i == 0 && rt->server_type == RTSP_SERVER_REAL) { |
|
|
|
if (i == 0 && rt->server_type == RTSP_SERVER_REAL) { |
|
|
|
char real_res[41], real_csum[9]; |
|
|
|
char real_res[41], real_csum[9]; |
|
|
|
ff_rdt_calc_response_and_checksum(real_res, real_csum, |
|
|
|
ff_rdt_calc_response_and_checksum(real_res, real_csum, |
|
|
@ -1185,7 +1192,7 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, |
|
|
|
"RealChallenge2: %s, sd=%s\r\n", |
|
|
|
"RealChallenge2: %s, sd=%s\r\n", |
|
|
|
rt->session_id, real_res, real_csum); |
|
|
|
rt->session_id, real_res, real_csum); |
|
|
|
} |
|
|
|
} |
|
|
|
ff_rtsp_send_cmd(s, cmd, reply, NULL); |
|
|
|
ff_rtsp_send_cmd(s, "SETUP", rtsp_st->control_url, cmd, reply, NULL); |
|
|
|
if (reply->status_code == 461 /* Unsupported protocol */ && i == 0) { |
|
|
|
if (reply->status_code == 461 /* Unsupported protocol */ && i == 0) { |
|
|
|
err = 1; |
|
|
|
err = 1; |
|
|
|
goto fail; |
|
|
|
goto fail; |
|
|
@ -1295,17 +1302,13 @@ static int rtsp_read_play(AVFormatContext *s) |
|
|
|
|
|
|
|
|
|
|
|
if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { |
|
|
|
if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { |
|
|
|
if (rt->state == RTSP_STATE_PAUSED) { |
|
|
|
if (rt->state == RTSP_STATE_PAUSED) { |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
cmd[0] = 0; |
|
|
|
"PLAY %s RTSP/1.0\r\n", |
|
|
|
|
|
|
|
rt->control_uri); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
"PLAY %s RTSP/1.0\r\n" |
|
|
|
|
|
|
|
"Range: npt=%0.3f-\r\n", |
|
|
|
"Range: npt=%0.3f-\r\n", |
|
|
|
rt->control_uri, |
|
|
|
|
|
|
|
(double)rt->seek_timestamp / AV_TIME_BASE); |
|
|
|
(double)rt->seek_timestamp / AV_TIME_BASE); |
|
|
|
} |
|
|
|
} |
|
|
|
ff_rtsp_send_cmd(s, cmd, reply, NULL); |
|
|
|
ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) { |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) { |
|
|
|
return -1; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
@ -1323,9 +1326,7 @@ static int rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply |
|
|
|
|
|
|
|
|
|
|
|
/* describe the stream */ |
|
|
|
/* describe the stream */ |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
"DESCRIBE %s RTSP/1.0\r\n" |
|
|
|
"Accept: application/sdp\r\n"); |
|
|
|
"Accept: application/sdp\r\n", |
|
|
|
|
|
|
|
rt->control_uri); |
|
|
|
|
|
|
|
if (rt->server_type == RTSP_SERVER_REAL) { |
|
|
|
if (rt->server_type == RTSP_SERVER_REAL) { |
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* The Require: attribute is needed for proper streaming from |
|
|
|
* The Require: attribute is needed for proper streaming from |
|
|
@ -1335,7 +1336,7 @@ static int rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply |
|
|
|
"Require: com.real.retain-entity-for-setup\r\n", |
|
|
|
"Require: com.real.retain-entity-for-setup\r\n", |
|
|
|
sizeof(cmd)); |
|
|
|
sizeof(cmd)); |
|
|
|
} |
|
|
|
} |
|
|
|
ff_rtsp_send_cmd(s, cmd, reply, &content); |
|
|
|
ff_rtsp_send_cmd(s, "DESCRIBE", rt->control_uri, cmd, reply, &content); |
|
|
|
if (!content) |
|
|
|
if (!content) |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) { |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) { |
|
|
@ -1356,7 +1357,6 @@ static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr) |
|
|
|
{ |
|
|
|
{ |
|
|
|
RTSPState *rt = s->priv_data; |
|
|
|
RTSPState *rt = s->priv_data; |
|
|
|
RTSPMessageHeader reply1, *reply = &reply1; |
|
|
|
RTSPMessageHeader reply1, *reply = &reply1; |
|
|
|
char cmd[1024]; |
|
|
|
|
|
|
|
int i; |
|
|
|
int i; |
|
|
|
char *sdp; |
|
|
|
char *sdp; |
|
|
|
AVFormatContext sdp_ctx, *ctx_array[1]; |
|
|
|
AVFormatContext sdp_ctx, *ctx_array[1]; |
|
|
@ -1364,10 +1364,6 @@ static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr) |
|
|
|
rt->start_time = av_gettime(); |
|
|
|
rt->start_time = av_gettime(); |
|
|
|
|
|
|
|
|
|
|
|
/* Announce the stream */ |
|
|
|
/* Announce the stream */ |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
|
|
|
|
"ANNOUNCE %s RTSP/1.0\r\n" |
|
|
|
|
|
|
|
"Content-Type: application/sdp\r\n", |
|
|
|
|
|
|
|
rt->control_uri); |
|
|
|
|
|
|
|
sdp = av_mallocz(8192); |
|
|
|
sdp = av_mallocz(8192); |
|
|
|
if (sdp == NULL) |
|
|
|
if (sdp == NULL) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
return AVERROR(ENOMEM); |
|
|
@ -1392,7 +1388,9 @@ static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr) |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
} |
|
|
|
} |
|
|
|
av_log(s, AV_LOG_INFO, "SDP:\n%s\n", sdp); |
|
|
|
av_log(s, AV_LOG_INFO, "SDP:\n%s\n", sdp); |
|
|
|
ff_rtsp_send_cmd_with_content(s, cmd, reply, NULL, sdp, strlen(sdp)); |
|
|
|
ff_rtsp_send_cmd_with_content(s, "ANNOUNCE", rt->control_uri, |
|
|
|
|
|
|
|
"Content-Type: application/sdp\r\n", |
|
|
|
|
|
|
|
reply, NULL, sdp, strlen(sdp)); |
|
|
|
av_free(sdp); |
|
|
|
av_free(sdp); |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
@ -1521,8 +1519,7 @@ redirect: |
|
|
|
/* request options supported by the server; this also detects server
|
|
|
|
/* request options supported by the server; this also detects server
|
|
|
|
* type */ |
|
|
|
* type */ |
|
|
|
for (rt->server_type = RTSP_SERVER_RTP;;) { |
|
|
|
for (rt->server_type = RTSP_SERVER_RTP;;) { |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
cmd[0] = 0; |
|
|
|
"OPTIONS %s RTSP/1.0\r\n", rt->control_uri); |
|
|
|
|
|
|
|
if (rt->server_type == RTSP_SERVER_REAL) |
|
|
|
if (rt->server_type == RTSP_SERVER_REAL) |
|
|
|
av_strlcat(cmd, |
|
|
|
av_strlcat(cmd, |
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -1539,7 +1536,7 @@ redirect: |
|
|
|
"CompanyID: KnKV4M4I/B2FjJ1TToLycw==\r\n" |
|
|
|
"CompanyID: KnKV4M4I/B2FjJ1TToLycw==\r\n" |
|
|
|
"GUID: 00000000-0000-0000-0000-000000000000\r\n", |
|
|
|
"GUID: 00000000-0000-0000-0000-000000000000\r\n", |
|
|
|
sizeof(cmd)); |
|
|
|
sizeof(cmd)); |
|
|
|
ff_rtsp_send_cmd(s, cmd, reply, NULL); |
|
|
|
ff_rtsp_send_cmd(s, "OPTIONS", rt->control_uri, cmd, reply, NULL); |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) { |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) { |
|
|
|
err = AVERROR_INVALIDDATA; |
|
|
|
err = AVERROR_INVALIDDATA; |
|
|
|
goto fail; |
|
|
|
goto fail; |
|
|
@ -1812,10 +1809,10 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
if (memcmp (cache, rt->real_setup_cache, |
|
|
|
if (memcmp (cache, rt->real_setup_cache, |
|
|
|
sizeof(enum AVDiscard) * s->nb_streams)) { |
|
|
|
sizeof(enum AVDiscard) * s->nb_streams)) { |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
"SET_PARAMETER %s RTSP/1.0\r\n" |
|
|
|
|
|
|
|
"Unsubscribe: %s\r\n", |
|
|
|
"Unsubscribe: %s\r\n", |
|
|
|
rt->control_uri, rt->last_subscription); |
|
|
|
rt->last_subscription); |
|
|
|
ff_rtsp_send_cmd(s, cmd, reply, NULL); |
|
|
|
ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, |
|
|
|
|
|
|
|
cmd, reply, NULL); |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
rt->need_subscription = 1; |
|
|
|
rt->need_subscription = 1; |
|
|
@ -1830,9 +1827,7 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
rt->last_subscription[0] = 0; |
|
|
|
rt->last_subscription[0] = 0; |
|
|
|
|
|
|
|
|
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
"SET_PARAMETER %s RTSP/1.0\r\n" |
|
|
|
"Subscribe: "); |
|
|
|
"Subscribe: ", |
|
|
|
|
|
|
|
rt->control_uri); |
|
|
|
|
|
|
|
for (i = 0; i < rt->nb_rtsp_streams; i++) { |
|
|
|
for (i = 0; i < rt->nb_rtsp_streams; i++) { |
|
|
|
rule_nr = 0; |
|
|
|
rule_nr = 0; |
|
|
|
for (r = 0; r < s->nb_streams; r++) { |
|
|
|
for (r = 0; r < s->nb_streams; r++) { |
|
|
@ -1851,7 +1846,8 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription); |
|
|
|
av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription); |
|
|
|
ff_rtsp_send_cmd(s, cmd, reply, NULL); |
|
|
|
ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, |
|
|
|
|
|
|
|
cmd, reply, NULL); |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
rt->need_subscription = 0; |
|
|
|
rt->need_subscription = 0; |
|
|
@ -1870,12 +1866,9 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
rt->server_type == RTSP_SERVER_REAL) && |
|
|
|
rt->server_type == RTSP_SERVER_REAL) && |
|
|
|
(av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2) { |
|
|
|
(av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2) { |
|
|
|
if (rt->server_type == RTSP_SERVER_WMS) { |
|
|
|
if (rt->server_type == RTSP_SERVER_WMS) { |
|
|
|
snprintf(cmd, sizeof(cmd) - 1, |
|
|
|
ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL); |
|
|
|
"GET_PARAMETER %s RTSP/1.0\r\n", |
|
|
|
|
|
|
|
rt->control_uri); |
|
|
|
|
|
|
|
ff_rtsp_send_cmd_async(s, cmd); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
ff_rtsp_send_cmd_async(s, "OPTIONS * RTSP/1.0\r\n"); |
|
|
|
ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1887,17 +1880,13 @@ static int rtsp_read_pause(AVFormatContext *s) |
|
|
|
{ |
|
|
|
{ |
|
|
|
RTSPState *rt = s->priv_data; |
|
|
|
RTSPState *rt = s->priv_data; |
|
|
|
RTSPMessageHeader reply1, *reply = &reply1; |
|
|
|
RTSPMessageHeader reply1, *reply = &reply1; |
|
|
|
char cmd[1024]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rt = s->priv_data; |
|
|
|
rt = s->priv_data; |
|
|
|
|
|
|
|
|
|
|
|
if (rt->state != RTSP_STATE_STREAMING) |
|
|
|
if (rt->state != RTSP_STATE_STREAMING) |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { |
|
|
|
else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, NULL); |
|
|
|
"PAUSE %s RTSP/1.0\r\n", |
|
|
|
|
|
|
|
rt->control_uri); |
|
|
|
|
|
|
|
ff_rtsp_send_cmd(s, cmd, reply, NULL); |
|
|
|
|
|
|
|
if (reply->status_code != RTSP_STATUS_OK) { |
|
|
|
if (reply->status_code != RTSP_STATUS_OK) { |
|
|
|
return -1; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
@ -1935,7 +1924,6 @@ static int rtsp_read_seek(AVFormatContext *s, int stream_index, |
|
|
|
static int rtsp_read_close(AVFormatContext *s) |
|
|
|
static int rtsp_read_close(AVFormatContext *s) |
|
|
|
{ |
|
|
|
{ |
|
|
|
RTSPState *rt = s->priv_data; |
|
|
|
RTSPState *rt = s->priv_data; |
|
|
|
char cmd[1024]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if 0 |
|
|
|
#if 0 |
|
|
|
/* NOTE: it is valid to flush the buffer here */ |
|
|
|
/* NOTE: it is valid to flush the buffer here */ |
|
|
@ -1943,10 +1931,7 @@ static int rtsp_read_close(AVFormatContext *s) |
|
|
|
url_fclose(&rt->rtsp_gb); |
|
|
|
url_fclose(&rt->rtsp_gb); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
snprintf(cmd, sizeof(cmd), |
|
|
|
ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); |
|
|
|
"TEARDOWN %s RTSP/1.0\r\n", |
|
|
|
|
|
|
|
rt->control_uri); |
|
|
|
|
|
|
|
ff_rtsp_send_cmd_async(s, cmd); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ff_rtsp_close_streams(s); |
|
|
|
ff_rtsp_close_streams(s); |
|
|
|
url_close(rt->rtsp_hd); |
|
|
|
url_close(rt->rtsp_hd); |
|
|
|