Index: patched-ffmpeg-mt/libavformat/rtsp.c |
=================================================================== |
--- patched-ffmpeg-mt/libavformat/rtsp.c (revision 41250) |
+++ patched-ffmpeg-mt/libavformat/rtsp.c (working copy) |
@@ -19,9 +19,6 @@ |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
*/ |
-/* needed by inet_aton() */ |
-#define _SVID_SOURCE |
- |
#include "libavutil/base64.h" |
#include "libavutil/avstring.h" |
#include "libavutil/intreadwrite.h" |
@@ -37,8 +34,8 @@ |
#include "rtpdec.h" |
#include "rdt.h" |
-#include "rtp_asf.h" |
-#include "rtp_vorbis.h" |
+#include "rtpdec_asf.h" |
+#include "rtpdec_vorbis.h" |
//#define DEBUG |
//#define DEBUG_RTP_TCP |
@@ -131,42 +128,38 @@ |
if (c && c->name) |
c_name = c->name; |
else |
- c_name = (char *) NULL; |
+ c_name = "(null)"; |
- if (c_name) { |
- get_word_sep(buf, sizeof(buf), "/", &p); |
- i = atoi(buf); |
- switch (codec->codec_type) { |
- case CODEC_TYPE_AUDIO: |
- av_log(s, AV_LOG_DEBUG, "audio codec set to: %s\n", c_name); |
- codec->sample_rate = RTSP_DEFAULT_AUDIO_SAMPLERATE; |
- codec->channels = RTSP_DEFAULT_NB_AUDIO_CHANNELS; |
- if (i > 0) { |
- codec->sample_rate = i; |
- get_word_sep(buf, sizeof(buf), "/", &p); |
- i = atoi(buf); |
- if (i > 0) |
- codec->channels = i; |
- // TODO: there is a bug here; if it is a mono stream, and |
- // less than 22000Hz, faad upconverts to stereo and twice |
- // the frequency. No problem, but the sample rate is being |
- // set here by the sdp line. Patch on its way. (rdm) |
- } |
- av_log(s, AV_LOG_DEBUG, "audio samplerate set to: %i\n", |
- codec->sample_rate); |
- av_log(s, AV_LOG_DEBUG, "audio channels set to: %i\n", |
- codec->channels); |
- break; |
- case CODEC_TYPE_VIDEO: |
- av_log(s, AV_LOG_DEBUG, "video codec set to: %s\n", c_name); |
- break; |
- default: |
- break; |
+ get_word_sep(buf, sizeof(buf), "/", &p); |
+ i = atoi(buf); |
+ switch (codec->codec_type) { |
+ case CODEC_TYPE_AUDIO: |
+ av_log(s, AV_LOG_DEBUG, "audio codec set to: %s\n", c_name); |
+ codec->sample_rate = RTSP_DEFAULT_AUDIO_SAMPLERATE; |
+ codec->channels = RTSP_DEFAULT_NB_AUDIO_CHANNELS; |
+ if (i > 0) { |
+ codec->sample_rate = i; |
+ get_word_sep(buf, sizeof(buf), "/", &p); |
+ i = atoi(buf); |
+ if (i > 0) |
+ codec->channels = i; |
+ // TODO: there is a bug here; if it is a mono stream, and |
+ // less than 22000Hz, faad upconverts to stereo and twice |
+ // the frequency. No problem, but the sample rate is being |
+ // set here by the sdp line. Patch on its way. (rdm) |
} |
- return 0; |
+ av_log(s, AV_LOG_DEBUG, "audio samplerate set to: %i\n", |
+ codec->sample_rate); |
+ av_log(s, AV_LOG_DEBUG, "audio channels set to: %i\n", |
+ codec->channels); |
+ break; |
+ case CODEC_TYPE_VIDEO: |
+ av_log(s, AV_LOG_DEBUG, "video codec set to: %s\n", c_name); |
+ break; |
+ default: |
+ break; |
} |
- |
- return -1; |
+ return 0; |
} |
/* return the length and optionally the data */ |
@@ -254,8 +247,8 @@ |
/* parse the attribute line from the fmtp a line of an sdp resonse. This |
* is broken out as a function because it is used in rtp_h264.c, which is |
* forthcoming. */ |
-int rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, |
- char *value, int value_size) |
+int ff_rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, |
+ char *value, int value_size) |
{ |
skip_spaces(p); |
if (**p) { |
@@ -283,8 +276,8 @@ |
RTPPayloadData *rtp_payload_data = &rtsp_st->rtp_payload_data; |
/* loop on each attribute */ |
- while (rtsp_next_attr_and_value(&p, attr, sizeof(attr), |
- value, sizeof(value))) { |
+ while (ff_rtsp_next_attr_and_value(&p, attr, sizeof(attr), |
+ value, sizeof(value))) { |
/* grab the codec extra_data from the config parameter of the fmtp |
* line */ |
sdp_parse_fmtp_config(codec, rtsp_st->dynamic_protocol_context, |
@@ -363,7 +356,7 @@ |
if (strcmp(buf1, "IP4") != 0) |
return; |
get_word_sep(buf1, sizeof(buf1), "/", &p); |
- if (inet_aton(buf1, &sdp_ip) == 0) |
+ if (ff_inet_aton(buf1, &sdp_ip) == 0) |
return; |
ttl = 16; |
if (*p == '/') { |
@@ -453,8 +446,8 @@ |
rtsp_st = st->priv_data; |
/* XXX: may need to add full url resolution */ |
- url_split(proto, sizeof(proto), NULL, 0, NULL, 0, |
- NULL, NULL, 0, p); |
+ ff_url_split(proto, sizeof(proto), NULL, 0, NULL, 0, |
+ NULL, NULL, 0, p); |
if (proto[0] == '\0') { |
/* relative control URL */ |
if (rtsp_st->control_url[strlen(rtsp_st->control_url)-1]!='/') |
@@ -576,8 +569,9 @@ |
} |
/* close and free RTSP streams */ |
-static void rtsp_close_streams(RTSPState *rt) |
+void ff_rtsp_close_streams(AVFormatContext *s) |
{ |
+ RTSPState *rt = s->priv_data; |
int i; |
RTSPStream *rtsp_st; |
@@ -585,7 +579,15 @@ |
rtsp_st = rt->rtsp_streams[i]; |
if (rtsp_st) { |
if (rtsp_st->transport_priv) { |
- if (rt->transport == RTSP_TRANSPORT_RDT) |
+ if (s->oformat) { |
+ AVFormatContext *rtpctx = rtsp_st->transport_priv; |
+ av_write_trailer(rtpctx); |
+ url_fclose(rtpctx->pb); |
+ av_metadata_free(&rtpctx->streams[0]->metadata); |
+ av_metadata_free(&rtpctx->metadata); |
+ av_free(rtpctx->streams[0]); |
+ av_free(rtpctx); |
+ } else if (rt->transport == RTSP_TRANSPORT_RDT) |
ff_rdt_parse_close(rtsp_st->transport_priv); |
else |
rtp_parse_close(rtsp_st->transport_priv); |
@@ -605,6 +607,50 @@ |
av_freep(&rt->auth_b64); |
} |
+static void *rtsp_rtp_mux_open(AVFormatContext *s, AVStream *st, URLContext *handle) { |
+ AVFormatContext *rtpctx; |
+ int ret; |
+ AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL); |
+ |
+ if (!rtp_format) |
+ return NULL; |
+ |
+ /* Allocate an AVFormatContext for each output stream */ |
+ rtpctx = avformat_alloc_context(); |
+ if (!rtpctx) |
+ return NULL; |
+ |
+ rtpctx->oformat = rtp_format; |
+ if (!av_new_stream(rtpctx, 0)) { |
+ av_free(rtpctx); |
+ return NULL; |
+ } |
+ /* Copy the max delay setting; the rtp muxer reads this. */ |
+ rtpctx->max_delay = s->max_delay; |
+ /* Copy other stream parameters. */ |
+ rtpctx->streams[0]->sample_aspect_ratio = st->sample_aspect_ratio; |
+ |
+ /* Remove the local codec, link to the original codec |
+ * context instead, to give the rtp muxer access to |
+ * codec parameters. */ |
+ av_free(rtpctx->streams[0]->codec); |
+ rtpctx->streams[0]->codec = st->codec; |
+ |
+ url_fdopen(&rtpctx->pb, handle); |
+ ret = av_write_header(rtpctx); |
+ |
+ if (ret) { |
+ url_fclose(rtpctx->pb); |
+ av_free(rtpctx->streams[0]); |
+ av_free(rtpctx); |
+ return NULL; |
+ } |
+ |
+ /* Copy the RTP AVStream timebase back to the original AVStream */ |
+ st->time_base = rtpctx->streams[0]->time_base; |
+ return rtpctx; |
+} |
+ |
static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) |
{ |
RTSPState *rt = s->priv_data; |
@@ -616,7 +662,11 @@ |
if (!st) |
s->ctx_flags |= AVFMTCTX_NOHEADER; |
- if (rt->transport == RTSP_TRANSPORT_RDT) |
+ if (s->oformat) { |
+ rtsp_st->transport_priv = rtsp_rtp_mux_open(s, st, rtsp_st->rtp_handle); |
+ /* Ownage of rtp_handle is passed to the rtp mux context */ |
+ rtsp_st->rtp_handle = NULL; |
+ } else if (rt->transport == RTSP_TRANSPORT_RDT) |
rtsp_st->transport_priv = ff_rdt_parse_open(s, st->index, |
rtsp_st->dynamic_protocol_context, |
rtsp_st->dynamic_handler); |
@@ -638,7 +688,7 @@ |
return 0; |
} |
-#if CONFIG_RTSP_DEMUXER |
+#if CONFIG_RTSP_DEMUXER || CONFIG_RTSP_MUXER |
static int rtsp_probe(AVProbeData *p) |
{ |
if (av_strstart(p->filename, "rtsp:", NULL)) |
@@ -750,7 +800,7 @@ |
if (*p == '=') { |
p++; |
get_word_sep(buf, sizeof(buf), ";,", &p); |
- if (inet_aton(buf, &ipaddr)) |
+ if (ff_inet_aton(buf, &ipaddr)) |
th->destination = ntohl(ipaddr.s_addr); |
} |
} |
@@ -766,7 +816,7 @@ |
} |
} |
-void rtsp_parse_line(RTSPMessageHeader *reply, const char *buf) |
+void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf) |
{ |
const char *p; |
@@ -828,30 +878,9 @@ |
} |
} |
-/** |
- * Read a RTSP message from the server, or prepare to read data |
- * packets if we're reading data interleaved over the TCP/RTSP |
- * connection as well. |
- * |
- * @param s RTSP demuxer context |
- * @param reply pointer where the RTSP message header will be stored |
- * @param content_ptr pointer where the RTSP message body, if any, will |
- * be stored (length is in reply) |
- * @param return_on_interleaved_data whether the function may return if we |
- * encounter a data marker ('$'), which precedes data |
- * packets over interleaved TCP/RTSP connections. If this |
- * is set, this function will return 1 after encountering |
- * a '$'. If it is not set, the function will skip any |
- * data packets (if they are encountered), until a reply |
- * has been fully parsed. If no more data is available |
- * without parsing a reply, it will return an error. |
- * |
- * @returns 1 if a data packets is ready to be received, -1 on error, |
- * and 0 on success. |
- */ |
-static int rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, |
- unsigned char **content_ptr, |
- int return_on_interleaved_data) |
+int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, |
+ unsigned char **content_ptr, |
+ int return_on_interleaved_data) |
{ |
RTSPState *rt = s->priv_data; |
char buf[4096], buf1[1024], *q; |
@@ -900,7 +929,7 @@ |
get_word(buf1, sizeof(buf1), &p); |
reply->status_code = atoi(buf1); |
} else { |
- rtsp_parse_line(reply, p); |
+ ff_rtsp_parse_line(reply, p); |
av_strlcat(rt->last_reply, p, sizeof(rt->last_reply)); |
av_strlcat(rt->last_reply, "\n", sizeof(rt->last_reply)); |
} |
@@ -936,7 +965,10 @@ |
return 0; |
} |
-static void rtsp_send_cmd_async(AVFormatContext *s, const char *cmd) |
+void ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, |
+ const char *cmd, |
+ const unsigned char *send_content, |
+ int send_content_length) |
{ |
RTSPState *rt = s->priv_data; |
char buf[4096], buf1[1024]; |
@@ -953,23 +985,44 @@ |
av_strlcatf(buf, sizeof(buf), |
"Authorization: Basic %s\r\n", |
rt->auth_b64); |
+ if (send_content_length > 0 && send_content) |
+ av_strlcatf(buf, sizeof(buf), "Content-Length: %d\r\n", send_content_length); |
av_strlcat(buf, "\r\n", sizeof(buf)); |
dprintf(s, "Sending:\n%s--\n", buf); |
url_write(rt->rtsp_hd, buf, strlen(buf)); |
+ if (send_content_length > 0 && send_content) |
+ url_write(rt->rtsp_hd, send_content, send_content_length); |
rt->last_cmd_time = av_gettime(); |
} |
-static void rtsp_send_cmd(AVFormatContext *s, |
- const char *cmd, RTSPMessageHeader *reply, |
- unsigned char **content_ptr) |
+void ff_rtsp_send_cmd_async(AVFormatContext *s, const char *cmd) |
{ |
- rtsp_send_cmd_async(s, cmd); |
+ ff_rtsp_send_cmd_with_content_async(s, cmd, NULL, 0); |
+} |
- rtsp_read_reply(s, reply, content_ptr, 0); |
+void ff_rtsp_send_cmd(AVFormatContext *s, |
+ const char *cmd, RTSPMessageHeader *reply, |
+ unsigned char **content_ptr) |
+{ |
+ ff_rtsp_send_cmd_async(s, cmd); |
+ |
+ ff_rtsp_read_reply(s, reply, content_ptr, 0); |
} |
+void ff_rtsp_send_cmd_with_content(AVFormatContext *s, |
+ const char *cmd, |
+ RTSPMessageHeader *reply, |
+ unsigned char **content_ptr, |
+ const unsigned char *send_content, |
+ int send_content_length) |
+{ |
+ ff_rtsp_send_cmd_with_content_async(s, cmd, send_content, send_content_length); |
+ |
+ ff_rtsp_read_reply(s, reply, content_ptr, 0); |
+} |
+ |
/** |
* @returns 0 on success, <0 on error, 1 if protocol is unavailable. |
*/ |
@@ -1034,8 +1087,8 @@ |
/* first try in specified port range */ |
if (RTSP_RTP_PORT_MIN != 0) { |
while (j <= RTSP_RTP_PORT_MAX) { |
- snprintf(buf, sizeof(buf), "rtp://%s?localport=%d", |
- host, j); |
+ ff_url_join(buf, sizeof(buf), "rtp", NULL, host, -1, |
+ "?localport=%d", j); |
/* we will use two ports per rtp stream (rtp and rtcp) */ |
j += 2; |
if (url_open(&rtsp_st->rtp_handle, buf, URL_RDWR) == 0) |
@@ -1088,8 +1141,10 @@ |
snprintf(transport, sizeof(transport) - 1, |
"%s/UDP;multicast", trans_pref); |
} |
- if (rt->server_type == RTSP_SERVER_REAL || |
- rt->server_type == RTSP_SERVER_WMS) |
+ if (s->oformat) { |
+ av_strlcat(transport, ";mode=receive", sizeof(transport)); |
+ } else if (rt->server_type == RTSP_SERVER_REAL || |
+ rt->server_type == RTSP_SERVER_WMS) |
av_strlcat(transport, ";mode=play", sizeof(transport)); |
snprintf(cmd, sizeof(cmd), |
"SETUP %s RTSP/1.0\r\n" |
@@ -1104,7 +1159,7 @@ |
"RealChallenge2: %s, sd=%s\r\n", |
rt->session_id, real_res, real_csum); |
} |
- rtsp_send_cmd(s, cmd, reply, NULL); |
+ ff_rtsp_send_cmd(s, cmd, reply, NULL); |
if (reply->status_code == 461 /* Unsupported protocol */ && i == 0) { |
err = 1; |
goto fail; |
@@ -1143,13 +1198,19 @@ |
char url[1024]; |
/* XXX: also use address if specified */ |
- snprintf(url, sizeof(url), "rtp://%s:%d", |
- host, reply->transports[0].server_port_min); |
+ ff_url_join(url, sizeof(url), "rtp", NULL, host, |
+ reply->transports[0].server_port_min, NULL); |
if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) && |
rtp_set_remote_url(rtsp_st->rtp_handle, url) < 0) { |
err = AVERROR_INVALIDDATA; |
goto fail; |
} |
+ /* Try to initialize the connection state in a |
+ * potential NAT router by sending dummy packets. |
+ * RTP/RTCP dummy packets are used for RDT, too. |
+ */ |
+ if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) && s->iformat) |
+ rtp_send_punch_packets(rtsp_st->rtp_handle); |
break; |
} |
case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: { |
@@ -1166,8 +1227,8 @@ |
port = rtsp_st->sdp_port; |
ttl = rtsp_st->sdp_ttl; |
} |
- snprintf(url, sizeof(url), "rtp://%s:%d?ttl=%d", |
- inet_ntoa(in), port, ttl); |
+ ff_url_join(url, sizeof(url), "rtp", NULL, inet_ntoa(in), |
+ port, "?ttl=%d", ttl); |
if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { |
err = AVERROR_INVALIDDATA; |
goto fail; |
@@ -1218,31 +1279,119 @@ |
rt->control_uri, |
(double)rt->seek_timestamp / AV_TIME_BASE); |
} |
- rtsp_send_cmd(s, cmd, reply, NULL); |
+ ff_rtsp_send_cmd(s, cmd, reply, NULL); |
if (reply->status_code != RTSP_STATUS_OK) { |
return -1; |
} |
} |
- rt->state = RTSP_STATE_PLAYING; |
+ rt->state = RTSP_STATE_STREAMING; |
return 0; |
} |
-static int rtsp_read_header(AVFormatContext *s, |
- AVFormatParameters *ap) |
+static int rtsp_setup_input_streams(AVFormatContext *s) |
{ |
RTSPState *rt = s->priv_data; |
+ RTSPMessageHeader reply1, *reply = &reply1; |
+ char cmd[1024]; |
+ unsigned char *content = NULL; |
+ int ret; |
+ |
+ /* describe the stream */ |
+ snprintf(cmd, sizeof(cmd), |
+ "DESCRIBE %s RTSP/1.0\r\n" |
+ "Accept: application/sdp\r\n", |
+ s->filename); |
+ if (rt->server_type == RTSP_SERVER_REAL) { |
+ /** |
+ * The Require: attribute is needed for proper streaming from |
+ * Realmedia servers. |
+ */ |
+ av_strlcat(cmd, |
+ "Require: com.real.retain-entity-for-setup\r\n", |
+ sizeof(cmd)); |
+ } |
+ ff_rtsp_send_cmd(s, cmd, reply, &content); |
+ if (!content) |
+ return AVERROR_INVALIDDATA; |
+ if (reply->status_code != RTSP_STATUS_OK) { |
+ av_freep(&content); |
+ return AVERROR_INVALIDDATA; |
+ } |
+ |
+ /* now we got the SDP description, we parse it */ |
+ ret = sdp_parse(s, (const char *)content); |
+ av_freep(&content); |
+ if (ret < 0) |
+ return AVERROR_INVALIDDATA; |
+ |
+ return 0; |
+} |
+ |
+static int rtsp_setup_output_streams(AVFormatContext *s) |
+{ |
+ RTSPState *rt = s->priv_data; |
+ RTSPMessageHeader reply1, *reply = &reply1; |
+ char cmd[1024]; |
+ int i; |
+ char *sdp; |
+ |
+ /* Announce the stream */ |
+ snprintf(cmd, sizeof(cmd), |
+ "ANNOUNCE %s RTSP/1.0\r\n" |
+ "Content-Type: application/sdp\r\n", |
+ s->filename); |
+ sdp = av_mallocz(8192); |
+ if (sdp == NULL) |
+ return AVERROR(ENOMEM); |
+ if (avf_sdp_create(&s, 1, sdp, 8192)) { |
+ av_free(sdp); |
+ return AVERROR_INVALIDDATA; |
+ } |
+ av_log(s, AV_LOG_INFO, "SDP:\n%s\n", sdp); |
+ ff_rtsp_send_cmd_with_content(s, cmd, reply, NULL, sdp, strlen(sdp)); |
+ av_free(sdp); |
+ if (reply->status_code != RTSP_STATUS_OK) |
+ return AVERROR_INVALIDDATA; |
+ |
+ /* Set up the RTSPStreams for each AVStream */ |
+ for (i = 0; i < s->nb_streams; i++) { |
+ RTSPStream *rtsp_st; |
+ AVStream *st = s->streams[i]; |
+ |
+ rtsp_st = av_mallocz(sizeof(RTSPStream)); |
+ if (!rtsp_st) |
+ return AVERROR(ENOMEM); |
+ dynarray_add(&rt->rtsp_streams, &rt->nb_rtsp_streams, rtsp_st); |
+ |
+ st->priv_data = rtsp_st; |
+ rtsp_st->stream_index = i; |
+ |
+ av_strlcpy(rtsp_st->control_url, s->filename, sizeof(rtsp_st->control_url)); |
+ /* Note, this must match the relative uri set in the sdp content */ |
+ av_strlcatf(rtsp_st->control_url, sizeof(rtsp_st->control_url), |
+ "/streamid=%d", i); |
+ } |
+ |
+ return 0; |
+} |
+ |
+int ff_rtsp_connect(AVFormatContext *s) |
+{ |
+ RTSPState *rt = s->priv_data; |
char host[1024], path[1024], tcpname[1024], cmd[2048], auth[128]; |
char *option_list, *option, *filename; |
URLContext *rtsp_hd; |
- int port, ret, err; |
+ int port, err; |
RTSPMessageHeader reply1, *reply = &reply1; |
- unsigned char *content = NULL; |
int lower_transport_mask = 0; |
char real_challenge[64]; |
+ |
+ if (!ff_network_init()) |
+ return AVERROR(EIO); |
redirect: |
/* extract hostname and port */ |
- url_split(NULL, 0, auth, sizeof(auth), |
- host, sizeof(host), &port, path, sizeof(path), s->filename); |
+ ff_url_split(NULL, 0, auth, sizeof(auth), |
+ host, sizeof(host), &port, path, sizeof(path), s->filename); |
if (*auth) { |
int auth_len = strlen(auth), b64_len = ((auth_len + 2) / 3) * 4 + 1; |
@@ -1286,8 +1435,19 @@ |
if (!lower_transport_mask) |
lower_transport_mask = (1 << RTSP_LOWER_TRANSPORT_NB) - 1; |
+ if (s->oformat) { |
+ /* Only UDP output is supported at the moment. */ |
+ lower_transport_mask &= 1 << RTSP_LOWER_TRANSPORT_UDP; |
+ if (!lower_transport_mask) { |
+ av_log(s, AV_LOG_ERROR, "Unsupported lower transport method, " |
+ "only UDP is supported for output.\n"); |
+ err = AVERROR(EINVAL); |
+ goto fail; |
+ } |
+ } |
+ |
/* open the tcp connexion */ |
- snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", host, port); |
+ ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL); |
if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) { |
err = AVERROR(EIO); |
goto fail; |
@@ -1318,7 +1478,7 @@ |
"CompanyID: KnKV4M4I/B2FjJ1TToLycw==\r\n" |
"GUID: 00000000-0000-0000-0000-000000000000\r\n", |
sizeof(cmd)); |
- rtsp_send_cmd(s, cmd, reply, NULL); |
+ ff_rtsp_send_cmd(s, cmd, reply, NULL); |
if (reply->status_code != RTSP_STATUS_OK) { |
err = AVERROR_INVALIDDATA; |
goto fail; |
@@ -1335,38 +1495,13 @@ |
break; |
} |
- /* describe the stream */ |
- snprintf(cmd, sizeof(cmd), |
- "DESCRIBE %s RTSP/1.0\r\n" |
- "Accept: application/sdp\r\n", |
- s->filename); |
- if (rt->server_type == RTSP_SERVER_REAL) { |
- /** |
- * The Require: attribute is needed for proper streaming from |
- * Realmedia servers. |
- */ |
- av_strlcat(cmd, |
- "Require: com.real.retain-entity-for-setup\r\n", |
- sizeof(cmd)); |
- } |
- rtsp_send_cmd(s, cmd, reply, &content); |
- if (!content) { |
- err = AVERROR_INVALIDDATA; |
+ if (s->iformat) |
+ err = rtsp_setup_input_streams(s); |
+ else |
+ err = rtsp_setup_output_streams(s); |
+ if (err) |
goto fail; |
- } |
- if (reply->status_code != RTSP_STATUS_OK) { |
- err = AVERROR_INVALIDDATA; |
- goto fail; |
- } |
- /* now we got the SDP description, we parse it */ |
- ret = sdp_parse(s, (const char *)content); |
- av_freep(&content); |
- if (ret < 0) { |
- err = AVERROR_INVALIDDATA; |
- goto fail; |
- } |
- |
do { |
int lower_transport = ff_log2_tab[lower_transport_mask & |
~(lower_transport_mask - 1)]; |
@@ -1385,29 +1520,46 @@ |
rt->state = RTSP_STATE_IDLE; |
rt->seek_timestamp = 0; /* default is to start stream at position zero */ |
- if (ap->initial_pause) { |
- /* do not start immediately */ |
- } else { |
- if (rtsp_read_play(s) < 0) { |
- err = AVERROR_INVALIDDATA; |
- goto fail; |
- } |
- } |
return 0; |
fail: |
- rtsp_close_streams(rt); |
- av_freep(&content); |
+ ff_rtsp_close_streams(s); |
url_close(rt->rtsp_hd); |
- if (reply->status_code >=300 && reply->status_code < 400) { |
+ if (reply->status_code >=300 && reply->status_code < 400 && s->iformat) { |
av_strlcpy(s->filename, reply->location, sizeof(s->filename)); |
av_log(s, AV_LOG_INFO, "Status %d: Redirecting to %s\n", |
reply->status_code, |
s->filename); |
goto redirect; |
} |
+ ff_network_close(); |
return err; |
} |
+#endif |
+#if CONFIG_RTSP_DEMUXER |
+static int rtsp_read_header(AVFormatContext *s, |
+ AVFormatParameters *ap) |
+{ |
+ RTSPState *rt = s->priv_data; |
+ int ret; |
+ |
+ ret = ff_rtsp_connect(s); |
+ if (ret) |
+ return ret; |
+ |
+ if (ap->initial_pause) { |
+ /* do not start immediately */ |
+ } else { |
+ if (rtsp_read_play(s) < 0) { |
+ ff_rtsp_close_streams(s); |
+ url_close(rt->rtsp_hd); |
+ return AVERROR_INVALIDDATA; |
+ } |
+ } |
+ |
+ return 0; |
+} |
+ |
static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, |
uint8_t *buf, int buf_size) |
{ |
@@ -1460,9 +1612,9 @@ |
if (tcp_fd != -1 && FD_ISSET(tcp_fd, &rfds)) { |
RTSPMessageHeader reply; |
- rtsp_read_reply(s, &reply, NULL, 0); |
+ ff_rtsp_read_reply(s, &reply, NULL, 0); |
/* XXX: parse message */ |
- if (rt->state != RTSP_STATE_PLAYING) |
+ if (rt->state != RTSP_STATE_STREAMING) |
return 0; |
} |
#endif |
@@ -1484,13 +1636,13 @@ |
for (;;) { |
RTSPMessageHeader reply; |
- ret = rtsp_read_reply(s, &reply, NULL, 1); |
+ ret = ff_rtsp_read_reply(s, &reply, NULL, 1); |
if (ret == -1) |
return -1; |
if (ret == 1) /* received '$' */ |
break; |
/* XXX: parse message */ |
- if (rt->state != RTSP_STATE_PLAYING) |
+ if (rt->state != RTSP_STATE_STREAMING) |
return 0; |
} |
ret = url_read_complete(rt->rtsp_hd, buf, 3); |
@@ -1600,7 +1752,7 @@ |
"SET_PARAMETER %s RTSP/1.0\r\n" |
"Unsubscribe: %s\r\n", |
rt->control_uri, rt->last_subscription); |
- rtsp_send_cmd(s, cmd, reply, NULL); |
+ ff_rtsp_send_cmd(s, cmd, reply, NULL); |
if (reply->status_code != RTSP_STATUS_OK) |
return AVERROR_INVALIDDATA; |
rt->need_subscription = 1; |
@@ -1636,12 +1788,12 @@ |
} |
} |
av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription); |
- rtsp_send_cmd(s, cmd, reply, NULL); |
+ ff_rtsp_send_cmd(s, cmd, reply, NULL); |
if (reply->status_code != RTSP_STATUS_OK) |
return AVERROR_INVALIDDATA; |
rt->need_subscription = 0; |
- if (rt->state == RTSP_STATE_PLAYING) |
+ if (rt->state == RTSP_STATE_STREAMING) |
rtsp_read_play (s); |
} |
} |
@@ -1658,9 +1810,9 @@ |
snprintf(cmd, sizeof(cmd) - 1, |
"GET_PARAMETER %s RTSP/1.0\r\n", |
rt->control_uri); |
- rtsp_send_cmd_async(s, cmd); |
+ ff_rtsp_send_cmd_async(s, cmd); |
} else { |
- rtsp_send_cmd_async(s, "OPTIONS * RTSP/1.0\r\n"); |
+ ff_rtsp_send_cmd_async(s, "OPTIONS * RTSP/1.0\r\n"); |
} |
} |
@@ -1676,13 +1828,13 @@ |
rt = s->priv_data; |
- if (rt->state != RTSP_STATE_PLAYING) |
+ if (rt->state != RTSP_STATE_STREAMING) |
return 0; |
else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { |
snprintf(cmd, sizeof(cmd), |
"PAUSE %s RTSP/1.0\r\n", |
rt->control_uri); |
- rtsp_send_cmd(s, cmd, reply, NULL); |
+ ff_rtsp_send_cmd(s, cmd, reply, NULL); |
if (reply->status_code != RTSP_STATUS_OK) { |
return -1; |
} |
@@ -1703,7 +1855,7 @@ |
default: |
case RTSP_STATE_IDLE: |
break; |
- case RTSP_STATE_PLAYING: |
+ case RTSP_STATE_STREAMING: |
if (rtsp_read_pause(s) != 0) |
return -1; |
rt->state = RTSP_STATE_SEEKING; |
@@ -1731,10 +1883,11 @@ |
snprintf(cmd, sizeof(cmd), |
"TEARDOWN %s RTSP/1.0\r\n", |
s->filename); |
- rtsp_send_cmd_async(s, cmd); |
+ ff_rtsp_send_cmd_async(s, cmd); |
- rtsp_close_streams(rt); |
+ ff_rtsp_close_streams(s); |
url_close(rt->rtsp_hd); |
+ ff_network_close(); |
return 0; |
} |
@@ -1782,6 +1935,9 @@ |
char *content; |
char url[1024]; |
+ if (!ff_network_init()) |
+ return AVERROR(EIO); |
+ |
/* read the whole sdp file */ |
/* XXX: better loading */ |
content = av_malloc(SDP_MAX_SIZE); |
@@ -1799,11 +1955,10 @@ |
for (i = 0; i < rt->nb_rtsp_streams; i++) { |
rtsp_st = rt->rtsp_streams[i]; |
- snprintf(url, sizeof(url), "rtp://%s:%d?localport=%d&ttl=%d", |
- inet_ntoa(rtsp_st->sdp_ip), |
- rtsp_st->sdp_port, |
- rtsp_st->sdp_port, |
- rtsp_st->sdp_ttl); |
+ ff_url_join(url, sizeof(url), "rtp", NULL, |
+ inet_ntoa(rtsp_st->sdp_ip), rtsp_st->sdp_port, |
+ "?localport=%d&ttl=%d", rtsp_st->sdp_port, |
+ rtsp_st->sdp_ttl); |
if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { |
err = AVERROR_INVALIDDATA; |
goto fail; |
@@ -1813,14 +1968,15 @@ |
} |
return 0; |
fail: |
- rtsp_close_streams(rt); |
+ ff_rtsp_close_streams(s); |
+ ff_network_close(); |
return err; |
} |
static int sdp_read_close(AVFormatContext *s) |
{ |
- RTSPState *rt = s->priv_data; |
- rtsp_close_streams(rt); |
+ ff_rtsp_close_streams(s); |
+ ff_network_close(); |
return 0; |
} |