X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtsp.c;h=60f13275e5e7e9dcdc69460061dddf6ea2daf184;hb=ac11d562e51a7764abc20447303ce95225d07a0b;hp=d1738dd0469691ca46e7619eadfbe61320b40446;hpb=27000636556d8fb81a57e9eae71c79637959ced6;p=ffmpeg diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index d1738dd0469..60f13275e5e 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -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 @@ static int sdp_parse_rtpmap(AVFormatContext *s, if (c && c->name) c_name = c->name; else - c_name = (char *) 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; + c_name = "(null)"; + + 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 @@ static const AttrNameMap attr_names[]= /* 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 @@ static void sdp_parse_fmtp(AVStream *st, const char *p) 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 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, 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 == '/') { @@ -576,8 +569,9 @@ static int sdp_parse(AVFormatContext *s, const char *content) } /* 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 @@ static void rtsp_close_streams(RTSPState *rt) 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 @@ static void rtsp_close_streams(RTSPState *rt) 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 @@ static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) 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 @@ static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) 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 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) 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 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) } } -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 @@ static void rtsp_skip_packet(AVFormatContext *s) } } -/** - * 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 @@ static int rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, 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 @@ static int rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, 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,21 +985,42 @@ static void rtsp_send_cmd_async(AVFormatContext *s, const char *cmd) 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); } /** @@ -1034,8 +1087,8 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, /* 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 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, 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 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, "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 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, 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 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, 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,27 +1279,115 @@ static int rtsp_read_play(AVFormatContext *s) 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), @@ -1286,8 +1435,19 @@ redirect: 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 @@ redirect: "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,37 +1495,12 @@ redirect: 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; - 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; + if (s->iformat) + err = rtsp_setup_input_streams(s); + else + err = rtsp_setup_output_streams(s); + if (err) goto fail; - } do { int lower_transport = ff_log2_tab[lower_transport_mask & @@ -1385,28 +1520,45 @@ redirect: 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 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, 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 @@ redo: 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 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) "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 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) } } 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 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) 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 @@ static int rtsp_read_pause(AVFormatContext *s) 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 @@ static int rtsp_read_seek(AVFormatContext *s, int stream_index, 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 @@ static int rtsp_read_close(AVFormatContext *s) 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 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) 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 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) 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 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) } 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; }