X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtsp.c;h=949c174070a5e96d0294ab8213c40b629b41ab1a;hb=9bc8bcddbd4fc394e2268e9849dcbf3bad6de980;hp=4ab2c334894d8291a828c19de7b0707541f8a07c;hpb=619298a84dcad59e42062a00b8ffe422a1a38d7c;p=ffmpeg diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 4ab2c334894..949c174070a 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -2,32 +2,36 @@ * RTSP/SDP client * Copyright (c) 2002 Fabrice Bellard * - * This file is part of FFmpeg. + * This file is part of Libav. * - * FFmpeg is free software; you can redistribute it and/or + * Libav is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * FFmpeg is distributed in the hope that it will be useful, + * Libav is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software + * License along with Libav; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/base64.h" #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" +#include "libavutil/parseutils.h" #include "libavutil/random_seed.h" +#include "libavutil/dict.h" #include "avformat.h" +#include "avio_internal.h" #include -#if HAVE_SYS_SELECT_H -#include +#if HAVE_POLL_H +#include #endif #include #include "internal.h" @@ -39,20 +43,18 @@ #include "rtpdec.h" #include "rdt.h" #include "rtpdec_formats.h" +#include "rtpenc_chain.h" +#include "url.h" //#define DEBUG -//#define DEBUG_RTP_TCP -#if LIBAVFORMAT_VERSION_INT < (53 << 16) -int rtsp_default_protocols = (1 << RTSP_LOWER_TRANSPORT_UDP); -#endif - -/* Timeout values for socket select, in ms, +/* Timeout values for socket poll, in ms, * and read_packet(), in seconds */ -#define SELECT_TIMEOUT_MS 100 +#define POLL_TIMEOUT_MS 100 #define READ_PACKET_TIMEOUT_S 10 -#define MAX_TIMEOUTS READ_PACKET_TIMEOUT_S * 1000 / SELECT_TIMEOUT_MS +#define MAX_TIMEOUTS READ_PACKET_TIMEOUT_S * 1000 / POLL_TIMEOUT_MS #define SDP_MAX_SIZE 16384 +#define RECVBUF_SIZE 10 * RTP_MAX_PACKET_LENGTH static void get_word_until_chars(char *buf, int buf_size, const char *sep, const char **pp) @@ -85,11 +87,62 @@ static void get_word(char *buf, int buf_size, const char **pp) get_word_until_chars(buf, buf_size, SPACE_CHARS, pp); } +/** Parse a string p in the form of Range:npt=xx-xx, and determine the start + * and end time. + * Used for seeking in the rtp stream. + */ +static void rtsp_parse_range_npt(const char *p, int64_t *start, int64_t *end) +{ + char buf[256]; + + p += strspn(p, SPACE_CHARS); + if (!av_stristart(p, "npt=", &p)) + return; + + *start = AV_NOPTS_VALUE; + *end = AV_NOPTS_VALUE; + + get_word_sep(buf, sizeof(buf), "-", &p); + av_parse_time(start, buf, 1); + if (*p == '-') { + p++; + get_word_sep(buf, sizeof(buf), "-", &p); + av_parse_time(end, buf, 1); + } +// av_log(NULL, AV_LOG_DEBUG, "Range Start: %lld\n", *start); +// av_log(NULL, AV_LOG_DEBUG, "Range End: %lld\n", *end); +} + +static int get_sockaddr(const char *buf, struct sockaddr_storage *sock) +{ + struct addrinfo hints, *ai = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(buf, NULL, &hints, &ai)) + return -1; + memcpy(sock, ai->ai_addr, FFMIN(sizeof(*sock), ai->ai_addrlen)); + freeaddrinfo(ai); + return 0; +} + +#if CONFIG_RTPDEC +static void init_rtp_handler(RTPDynamicProtocolHandler *handler, + RTSPStream *rtsp_st, AVCodecContext *codec) +{ + if (!handler) + return; + codec->codec_id = handler->codec_id; + rtsp_st->dynamic_handler = handler; + if (handler->alloc) + rtsp_st->dynamic_protocol_context = handler->alloc(); +} + /* parse the rtpmap description: /[/] */ static int sdp_parse_rtpmap(AVFormatContext *s, - AVCodecContext *codec, RTSPStream *rtsp_st, + AVStream *st, RTSPStream *rtsp_st, int payload_type, const char *p) { + AVCodecContext *codec = st->codec; char buf[256]; int i; AVCodec *c; @@ -102,18 +155,15 @@ static int sdp_parse_rtpmap(AVFormatContext *s, * have a trailing space. */ get_word_sep(buf, sizeof(buf), "/ ", &p); if (payload_type >= RTP_PT_PRIVATE) { - RTPDynamicProtocolHandler *handler; - for (handler = RTPFirstDynamicPayloadHandler; - handler; handler = handler->next) { - if (!strcasecmp(buf, handler->enc_name) && - codec->codec_type == handler->codec_type) { - codec->codec_id = handler->codec_id; - rtsp_st->dynamic_handler = handler; - if (handler->open) - rtsp_st->dynamic_protocol_context = handler->open(); - break; - } - } + RTPDynamicProtocolHandler *handler = + ff_rtp_handler_find_by_name(buf, codec->codec_type); + init_rtp_handler(handler, rtsp_st, codec); + /* If no dynamic handler was found, check with the list of standard + * allocated types, if such a stream for some reason happens to + * use a private payload type. This isn't handled in rtpdec.c, since + * the format name from the rtpmap line never is passed into rtpdec. */ + if (!rtsp_st->dynamic_handler) + codec->codec_id = ff_rtp_codec_id(buf, codec->codec_type); } else { /* We are in a standard case * (from http://www.iana.org/assignments/rtp-parameters). */ @@ -136,6 +186,7 @@ static int sdp_parse_rtpmap(AVFormatContext *s, codec->channels = RTSP_DEFAULT_NB_AUDIO_CHANNELS; if (i > 0) { codec->sample_rate = i; + av_set_pts_info(st, 32, 1, codec->sample_rate); get_word_sep(buf, sizeof(buf), "/", &p); i = atoi(buf); if (i > 0) @@ -152,6 +203,8 @@ static int sdp_parse_rtpmap(AVFormatContext *s, break; case AVMEDIA_TYPE_VIDEO: av_log(s, AV_LOG_DEBUG, "video codec set to: %s\n", c_name); + if (i > 0) + av_set_pts_info(st, 32, 1, i); break; default: break; @@ -178,44 +231,6 @@ int ff_rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, return 0; } -/** Parse a string p in the form of Range:npt=xx-xx, and determine the start - * and end time. - * Used for seeking in the rtp stream. - */ -static void rtsp_parse_range_npt(const char *p, int64_t *start, int64_t *end) -{ - char buf[256]; - - p += strspn(p, SPACE_CHARS); - if (!av_stristart(p, "npt=", &p)) - return; - - *start = AV_NOPTS_VALUE; - *end = AV_NOPTS_VALUE; - - get_word_sep(buf, sizeof(buf), "-", &p); - *start = parse_date(buf, 1); - if (*p == '-') { - p++; - get_word_sep(buf, sizeof(buf), "-", &p); - *end = parse_date(buf, 1); - } -// av_log(NULL, AV_LOG_DEBUG, "Range Start: %lld\n", *start); -// av_log(NULL, AV_LOG_DEBUG, "Range End: %lld\n", *end); -} - -static int get_sockaddr(const char *buf, struct sockaddr_storage *sock) -{ - struct addrinfo hints, *ai = NULL; - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_NUMERICHOST; - if (getaddrinfo(buf, NULL, &hints, &ai)) - return -1; - memcpy(sock, ai->ai_addr, FFMIN(sizeof(*sock), ai->ai_addrlen)); - freeaddrinfo(ai); - return 0; -} - typedef struct SDPParseState { /* SDP only */ struct sockaddr_storage default_ip; @@ -236,7 +251,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, struct sockaddr_storage sdp_ip; int ttl; - dprintf(s, "sdp: %c='%s'\n", letter, buf); + av_dlog(s, "sdp: %c='%s'\n", letter, buf); p = buf; if (s1->skip_media && letter != 'm') @@ -262,18 +277,17 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, s1->default_ip = sdp_ip; s1->default_ttl = ttl; } else { - st = s->streams[s->nb_streams - 1]; - rtsp_st = st->priv_data; + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; rtsp_st->sdp_ip = sdp_ip; rtsp_st->sdp_ttl = ttl; } break; case 's': - av_metadata_set2(&s->metadata, "title", p, 0); + av_dict_set(&s->metadata, "title", p, 0); break; case 'i': if (s->nb_streams == 0) { - av_metadata_set2(&s->metadata, "comment", p, 0); + av_dict_set(&s->metadata, "comment", p, 0); break; } break; @@ -312,15 +326,22 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, if (!strcmp(ff_rtp_enc_name(rtsp_st->sdp_payload_type), "MP2T")) { /* no corresponding stream */ } else { - st = av_new_stream(s, 0); + st = av_new_stream(s, rt->nb_rtsp_streams - 1); if (!st) return; - st->priv_data = rtsp_st; rtsp_st->stream_index = st->index; st->codec->codec_type = codec_type; if (rtsp_st->sdp_payload_type < RTP_PT_PRIVATE) { + RTPDynamicProtocolHandler *handler; /* if standard payload type, we can find the codec right now */ ff_rtp_get_codec_info(st->codec, rtsp_st->sdp_payload_type); + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && + st->codec->sample_rate > 0) + av_set_pts_info(st, 32, 1, st->codec->sample_rate); + /* Even static payload types may need a custom depacketizer */ + handler = ff_rtp_handler_find_by_id( + rtsp_st->sdp_payload_type, st->codec->codec_type); + init_rtp_handler(handler, rtsp_st, st->codec); } } /* put a default control url */ @@ -334,41 +355,39 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, av_strlcpy(rt->control_uri, p, sizeof(rt->control_uri)); } else { - char proto[32]; - /* get the control url */ - st = s->streams[s->nb_streams - 1]; - rtsp_st = st->priv_data; - - /* XXX: may need to add full url resolution */ - av_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]!='/') - av_strlcat(rtsp_st->control_url, "/", - sizeof(rtsp_st->control_url)); - av_strlcat(rtsp_st->control_url, p, - sizeof(rtsp_st->control_url)); - } else - av_strlcpy(rtsp_st->control_url, p, - sizeof(rtsp_st->control_url)); + char proto[32]; + /* get the control url */ + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; + + /* XXX: may need to add full url resolution */ + av_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]!='/') + av_strlcat(rtsp_st->control_url, "/", + sizeof(rtsp_st->control_url)); + av_strlcat(rtsp_st->control_url, p, + sizeof(rtsp_st->control_url)); + } else + av_strlcpy(rtsp_st->control_url, p, + sizeof(rtsp_st->control_url)); } } else if (av_strstart(p, "rtpmap:", &p) && s->nb_streams > 0) { /* NOTE: rtpmap is only supported AFTER the 'm=' tag */ get_word(buf1, sizeof(buf1), &p); payload_type = atoi(buf1); st = s->streams[s->nb_streams - 1]; - rtsp_st = st->priv_data; - sdp_parse_rtpmap(s, st->codec, rtsp_st, payload_type, p); + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; + sdp_parse_rtpmap(s, st, rtsp_st, payload_type, p); } else if (av_strstart(p, "fmtp:", &p) || av_strstart(p, "framesize:", &p)) { /* NOTE: fmtp is only supported AFTER the 'a=rtpmap:xxx' tag */ // let dynamic protocol handlers have a stab at the line. get_word(buf1, sizeof(buf1), &p); payload_type = atoi(buf1); - for (i = 0; i < s->nb_streams; i++) { - st = s->streams[i]; - rtsp_st = st->priv_data; + for (i = 0; i < rt->nb_rtsp_streams; i++) { + rtsp_st = rt->rtsp_streams[i]; if (rtsp_st->sdp_payload_type == payload_type && rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->parse_sdp_a_line) @@ -387,6 +406,10 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, } else if (av_strstart(p, "IsRealDataType:integer;",&p)) { if (atoi(p) == 1) rt->transport = RTSP_TRANSPORT_RDT; + } else if (av_strstart(p, "SampleRate:integer;", &p) && + s->nb_streams > 0) { + st = s->streams[s->nb_streams - 1]; + st->codec->sample_rate = atoi(p); } else { if (rt->server_type == RTSP_SERVER_WMS) ff_wms_parse_sdp_a_line(s, p); @@ -394,7 +417,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, if (rt->server_type == RTSP_SERVER_REAL) ff_real_parse_sdp_a_line(s, s->nb_streams - 1, p); - rtsp_st = s->streams[s->nb_streams - 1]->priv_data; + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; if (rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->parse_sdp_a_line) rtsp_st->dynamic_handler->parse_sdp_a_line(s, @@ -406,8 +429,9 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, } } -static int sdp_parse(AVFormatContext *s, const char *content) +int ff_sdp_parse(AVFormatContext *s, const char *content) { + RTSPState *rt = s->priv_data; const char *p; int letter; /* Some SDP lines, particularly for Realmedia or ASF RTSP streams, @@ -447,8 +471,44 @@ static int sdp_parse(AVFormatContext *s, const char *content) if (*p == '\n') p++; } + rt->p = av_malloc(sizeof(struct pollfd)*2*(rt->nb_rtsp_streams+1)); + if (!rt->p) return AVERROR(ENOMEM); return 0; } +#endif /* CONFIG_RTPDEC */ + +void ff_rtsp_undo_setup(AVFormatContext *s) +{ + RTSPState *rt = s->priv_data; + int i; + + for (i = 0; i < rt->nb_rtsp_streams; i++) { + RTSPStream *rtsp_st = rt->rtsp_streams[i]; + if (!rtsp_st) + continue; + if (rtsp_st->transport_priv) { + if (s->oformat) { + AVFormatContext *rtpctx = rtsp_st->transport_priv; + av_write_trailer(rtpctx); + if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP) { + uint8_t *ptr; + avio_close_dyn_buf(rtpctx->pb, &ptr); + av_free(ptr); + } else { + avio_close(rtpctx->pb); + } + avformat_free_context(rtpctx); + } else if (rt->transport == RTSP_TRANSPORT_RDT && CONFIG_RTPDEC) + ff_rdt_parse_close(rtsp_st->transport_priv); + else if (CONFIG_RTPDEC) + rtp_parse_close(rtsp_st->transport_priv); + } + rtsp_st->transport_priv = NULL; + if (rtsp_st->rtp_handle) + ffurl_close(rtsp_st->rtp_handle); + rtsp_st->rtp_handle = NULL; + } +} /* close and free RTSP streams */ void ff_rtsp_close_streams(AVFormatContext *s) @@ -457,34 +517,14 @@ void ff_rtsp_close_streams(AVFormatContext *s) int i; RTSPStream *rtsp_st; + ff_rtsp_undo_setup(s); for (i = 0; i < rt->nb_rtsp_streams; i++) { rtsp_st = rt->rtsp_streams[i]; if (rtsp_st) { - if (rtsp_st->transport_priv) { - if (s->oformat) { - AVFormatContext *rtpctx = rtsp_st->transport_priv; - av_write_trailer(rtpctx); - if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP) { - uint8_t *ptr; - url_close_dyn_buf(rtpctx->pb, &ptr); - av_free(ptr); - } else { - 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); - } - if (rtsp_st->rtp_handle) - url_close(rtsp_st->rtp_handle); if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context) - rtsp_st->dynamic_handler->close( + rtsp_st->dynamic_handler->free( rtsp_st->dynamic_protocol_context); + av_free(rtsp_st); } } av_free(rt->rtsp_streams); @@ -492,65 +532,8 @@ void ff_rtsp_close_streams(AVFormatContext *s) av_close_input_stream (rt->asf_ctx); rt->asf_ctx = NULL; } -} - -static void *rtsp_rtp_mux_open(AVFormatContext *s, AVStream *st, - URLContext *handle) -{ - RTSPState *rt = s->priv_data; - 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; - - /* Set the synchronized start time. */ - rtpctx->start_time_realtime = rt->start_time; - - /* 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; - - if (handle) { - url_fdopen(&rtpctx->pb, handle); - } else - url_open_dyn_packet_buf(&rtpctx->pb, RTSP_TCP_MAX_PACKET_SIZE); - ret = av_write_header(rtpctx); - - if (ret) { - if (handle) { - url_fclose(rtpctx->pb); - } else { - uint8_t *ptr; - url_close_dyn_buf(rtpctx->pb, &ptr); - av_free(ptr); - } - 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; + av_free(rt->p); + av_free(rt->recvbuf); } static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) @@ -564,21 +547,25 @@ static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) if (!st) s->ctx_flags |= AVFMTCTX_NOHEADER; - if (s->oformat) { - rtsp_st->transport_priv = rtsp_rtp_mux_open(s, st, rtsp_st->rtp_handle); + if (s->oformat && CONFIG_RTSP_MUXER) { + rtsp_st->transport_priv = ff_rtp_chain_mux_open(s, st, + rtsp_st->rtp_handle, + RTSP_TCP_MAX_PACKET_SIZE); /* Ownership of rtp_handle is passed to the rtp mux context */ rtsp_st->rtp_handle = NULL; - } else if (rt->transport == RTSP_TRANSPORT_RDT) + } else if (rt->transport == RTSP_TRANSPORT_RDT && CONFIG_RTPDEC) rtsp_st->transport_priv = ff_rdt_parse_open(s, st->index, rtsp_st->dynamic_protocol_context, rtsp_st->dynamic_handler); - else + else if (CONFIG_RTPDEC) rtsp_st->transport_priv = rtp_parse_open(s, st, rtsp_st->rtp_handle, - rtsp_st->sdp_payload_type); + rtsp_st->sdp_payload_type, + (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP || !s->max_delay) + ? 0 : RTP_REORDER_QUEUE_DEFAULT_SIZE); if (!rtsp_st->transport_priv) { return AVERROR(ENOMEM); - } else if (rt->transport != RTSP_TRANSPORT_RDT) { + } else if (rt->transport != RTSP_TRANSPORT_RDT && CONFIG_RTPDEC) { if (rtsp_st->dynamic_handler) { rtp_parse_set_dynamic_protocol(rtsp_st->transport_priv, rtsp_st->dynamic_protocol_context, @@ -590,13 +577,6 @@ static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) } #if CONFIG_RTSP_DEMUXER || CONFIG_RTSP_MUXER -static int rtsp_probe(AVProbeData *p) -{ - if (av_strstart(p->filename, "rtsp:", NULL)) - return AVPROBE_SCORE_MAX; - return 0; -} - static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp) { const char *p; @@ -721,8 +701,63 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) } } +static void handle_rtp_info(RTSPState *rt, const char *url, + uint32_t seq, uint32_t rtptime) +{ + int i; + if (!rtptime || !url[0]) + return; + if (rt->transport != RTSP_TRANSPORT_RTP) + return; + for (i = 0; i < rt->nb_rtsp_streams; i++) { + RTSPStream *rtsp_st = rt->rtsp_streams[i]; + RTPDemuxContext *rtpctx = rtsp_st->transport_priv; + if (!rtpctx) + continue; + if (!strcmp(rtsp_st->control_url, url)) { + rtpctx->base_timestamp = rtptime; + break; + } + } +} + +static void rtsp_parse_rtp_info(RTSPState *rt, const char *p) +{ + int read = 0; + char key[20], value[1024], url[1024] = ""; + uint32_t seq = 0, rtptime = 0; + + for (;;) { + p += strspn(p, SPACE_CHARS); + if (!*p) + break; + get_word_sep(key, sizeof(key), "=", &p); + if (*p != '=') + break; + p++; + get_word_sep(value, sizeof(value), ";, ", &p); + read++; + if (!strcmp(key, "url")) + av_strlcpy(url, value, sizeof(url)); + else if (!strcmp(key, "seq")) + seq = strtoul(value, NULL, 10); + else if (!strcmp(key, "rtptime")) + rtptime = strtoul(value, NULL, 10); + if (*p == ',') { + handle_rtp_info(rt, url, seq, rtptime); + url[0] = '\0'; + seq = rtptime = 0; + read = 0; + } + if (*p) + p++; + } + if (read > 0) + handle_rtp_info(rt, url, seq, rtptime); +} + void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf, - HTTPAuthState *auth_state) + RTSPState *rt, const char *method) { const char *p; @@ -755,12 +790,24 @@ void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf, } else if (av_stristart(p, "Location:", &p)) { p += strspn(p, SPACE_CHARS); av_strlcpy(reply->location, p , sizeof(reply->location)); - } else if (av_stristart(p, "WWW-Authenticate:", &p) && auth_state) { + } else if (av_stristart(p, "WWW-Authenticate:", &p) && rt) { p += strspn(p, SPACE_CHARS); - ff_http_auth_handle_header(auth_state, "WWW-Authenticate", p); - } else if (av_stristart(p, "Authentication-Info:", &p) && auth_state) { + ff_http_auth_handle_header(&rt->auth_state, "WWW-Authenticate", p); + } else if (av_stristart(p, "Authentication-Info:", &p) && rt) { p += strspn(p, SPACE_CHARS); - ff_http_auth_handle_header(auth_state, "Authentication-Info", p); + ff_http_auth_handle_header(&rt->auth_state, "Authentication-Info", p); + } else if (av_stristart(p, "Content-Base:", &p) && rt) { + p += strspn(p, SPACE_CHARS); + if (method && !strcmp(method, "DESCRIBE")) + av_strlcpy(rt->control_uri, p , sizeof(rt->control_uri)); + } else if (av_stristart(p, "RTP-Info:", &p) && rt) { + p += strspn(p, SPACE_CHARS); + if (method && !strcmp(method, "PLAY")) + rtsp_parse_rtp_info(rt, p); + } else if (av_stristart(p, "Public:", &p) && rt) { + if (strstr(p, "GET_PARAMETER") && + method && !strcmp(method, "OPTIONS")) + rt->get_parameter_supported = 1; } } @@ -771,19 +818,19 @@ void ff_rtsp_skip_packet(AVFormatContext *s) int ret, len, len1; uint8_t buf[1024]; - ret = url_read_complete(rt->rtsp_hd, buf, 3); + ret = ffurl_read_complete(rt->rtsp_hd, buf, 3); if (ret != 3) return; len = AV_RB16(buf + 1); - dprintf(s, "skipping RTP packet len=%d\n", len); + av_dlog(s, "skipping RTP packet len=%d\n", len); /* skip payload */ while (len > 0) { len1 = len; if (len1 > sizeof(buf)) len1 = sizeof(buf); - ret = url_read_complete(rt->rtsp_hd, buf, len1); + ret = ffurl_read_complete(rt->rtsp_hd, buf, len1); if (ret != len1) return; len -= len1; @@ -792,7 +839,7 @@ void ff_rtsp_skip_packet(AVFormatContext *s) int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, unsigned char **content_ptr, - int return_on_interleaved_data) + int return_on_interleaved_data, const char *method) { RTSPState *rt = s->priv_data; char buf[4096], buf1[1024], *q; @@ -808,10 +855,8 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, for (;;) { q = buf; for (;;) { - ret = url_read_complete(rt->rtsp_hd, &ch, 1); -#ifdef DEBUG_RTP_TCP - dprintf(s, "ret=%d c=%02x [%c]\n", ret, ch, ch); -#endif + ret = ffurl_read_complete(rt->rtsp_hd, &ch, 1); + av_dlog(s, "ret=%d c=%02x [%c]\n", ret, ch, ch); if (ret != 1) return AVERROR_EOF; if (ch == '\n') @@ -829,7 +874,7 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, } *q = '\0'; - dprintf(s, "line='%s'\n", buf); + av_dlog(s, "line='%s'\n", buf); /* test if last line */ if (buf[0] == '\0') @@ -842,7 +887,7 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, reply->status_code = atoi(buf1); av_strlcpy(reply->reason, p, sizeof(reply->reason)); } else { - ff_rtsp_parse_line(reply, p, &rt->auth_state); + ff_rtsp_parse_line(reply, p, rt, method); av_strlcat(rt->last_reply, p, sizeof(rt->last_reply)); av_strlcat(rt->last_reply, "\n", sizeof(rt->last_reply)); } @@ -856,7 +901,7 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, if (content_length > 0) { /* leave some room for a trailing '\0' (useful for simple parsing) */ content = av_malloc(content_length + 1); - (void)url_read_complete(rt->rtsp_hd, content, content_length); + ffurl_read_complete(rt->rtsp_hd, content, content_length); content[content_length] = '\0'; } if (content_ptr) @@ -883,11 +928,24 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, return 0; } -int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, - const char *method, const char *url, - const char *headers, - const unsigned char *send_content, - int send_content_length) +/** + * Send a command to the RTSP server without waiting for the reply. + * + * @param s RTSP (de)muxer context + * @param method the method for the request + * @param url the target url for the request + * @param headers extra header lines to include in the request + * @param send_content if non-null, the data to send as request body content + * @param send_content_length the length of the send_content data, or 0 if + * send_content is null + * + * @return zero if success, nonzero otherwise + */ +static int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, + const char *method, const char *url, + const char *headers, + const unsigned char *send_content, + int send_content_length) { RTSPState *rt = s->priv_data; char buf[4096], *out_buf; @@ -921,16 +979,16 @@ int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, out_buf = base64buf; } - dprintf(s, "Sending:\n%s--\n", buf); + av_dlog(s, "Sending:\n%s--\n", buf); - url_write(rt->rtsp_hd_out, out_buf, strlen(out_buf)); + ffurl_write(rt->rtsp_hd_out, out_buf, strlen(out_buf)); if (send_content_length > 0 && send_content) { if (rt->control_transport == RTSP_MODE_TUNNEL) { av_log(s, AV_LOG_ERROR, "tunneling of RTSP requests " "with content data not supported\n"); return AVERROR_PATCHWELCOME; } - url_write(rt->rtsp_hd_out, send_content, send_content_length); + ffurl_write(rt->rtsp_hd_out, send_content, send_content_length); } rt->last_cmd_time = av_gettime(); @@ -970,7 +1028,7 @@ retry: send_content_length))) return ret; - if ((ret = ff_rtsp_read_reply(s, reply, content_ptr, 0) ) < 0) + if ((ret = ff_rtsp_read_reply(s, reply, content_ptr, 0, method) ) < 0) return ret; if (reply->status_code == 401 && cur_auth_type == HTTP_AUTH_NONE && @@ -988,10 +1046,7 @@ retry: return 0; } -/** - * @return 0 on success, <0 on error, 1 if protocol is unavailable. - */ -static int make_setup_request(AVFormatContext *s, const char *host, int port, +int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, int lower_transport, const char *real_challenge) { RTSPState *rt = s->priv_data; @@ -1016,7 +1071,7 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, for (j = RTSP_RTP_PORT_MIN, i = 0; i < rt->nb_rtsp_streams; ++i) { char transport[2048]; - /** + /* * WMS serves all UDP data over a single connection, the RTX, which * isn't necessarily the first in the SDP but has to be the first * to be set up, else the second/third SETUP will fail with a 461. @@ -1056,21 +1111,25 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, "?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) + if (ffurl_open(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE) == 0) goto rtp_opened; } } #if 0 /* then try on any port */ - if (url_open(&rtsp_st->rtp_handle, "rtp://", URL_RDONLY) < 0) { + if (ffurl_open(&rtsp_st->rtp_handle, "rtp://", AVIO_FLAG_READ) < 0) { err = AVERROR_INVALIDDATA; goto fail; } +#else + av_log(s, AV_LOG_ERROR, "Unable to open an input RTP port\n"); + err = AVERROR(EIO); + goto fail; #endif rtp_opened: - port = rtp_get_local_port(rtsp_st->rtp_handle); + port = rtp_get_local_rtp_port(rtsp_st->rtp_handle); have_port: snprintf(transport, sizeof(transport) - 1, "%s/UDP;", trans_pref); @@ -1085,7 +1144,7 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, /* RTP/TCP */ else if (lower_transport == RTSP_LOWER_TRANSPORT_TCP) { - /** For WMS streams, the application streams are only used for + /* For WMS streams, the application streams are only used for * UDP. When trying to set it up for TCP streams, the server * will return an error. Therefore, we skip those streams. */ if (rt->server_type == RTSP_SERVER_WMS && @@ -1094,7 +1153,7 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, continue; snprintf(transport, sizeof(transport) - 1, "%s/TCP;", trans_pref); - if (rt->server_type == RTSP_SERVER_WMS) + if (rt->transport != RTSP_TRANSPORT_RDT) av_strlcat(transport, "unicast;", sizeof(transport)); av_strlcatf(transport, sizeof(transport), "interleaved=%d-%d", @@ -1114,7 +1173,7 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, snprintf(cmd, sizeof(cmd), "Transport: %s\r\n", transport); - if (i == 0 && rt->server_type == RTSP_SERVER_REAL) { + if (i == 0 && rt->server_type == RTSP_SERVER_REAL && CONFIG_RTPDEC) { char real_res[41], real_csum[9]; ff_rdt_calc_response_and_checksum(real_res, real_csum, real_challenge); @@ -1145,11 +1204,12 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, rt->transport = reply->transports[0].transport; } - /* close RTP connection if not chosen */ - if (reply->transports[0].lower_transport != RTSP_LOWER_TRANSPORT_UDP && - (lower_transport == RTSP_LOWER_TRANSPORT_UDP)) { - url_close(rtsp_st->rtp_handle); - rtsp_st->rtp_handle = NULL; + /* Fail if the server responded with another lower transport mode + * than what we requested. */ + if (reply->transports[0].lower_transport != lower_transport) { + av_log(s, AV_LOG_ERROR, "Nonmatching transport in server reply\n"); + err = AVERROR_INVALIDDATA; + goto fail; } switch(reply->transports[0].lower_transport) { @@ -1159,16 +1219,18 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, break; case RTSP_LOWER_TRANSPORT_UDP: { - char url[1024]; + char url[1024], options[30] = ""; + if (rt->filter_source) + av_strlcpy(options, "?connect=1", sizeof(options)); /* Use source address if specified */ if (reply->transports[0].source[0]) { ff_url_join(url, sizeof(url), "rtp", NULL, reply->transports[0].source, - reply->transports[0].server_port_min, NULL); + reply->transports[0].server_port_min, "%s", options); } else { - ff_url_join(url, sizeof(url), "rtp", NULL, host, - reply->transports[0].server_port_min, NULL); + ff_url_join(url, sizeof(url), "rtp", NULL, host, + reply->transports[0].server_port_min, "%s", options); } if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) && rtp_set_remote_url(rtsp_st->rtp_handle, url) < 0) { @@ -1179,7 +1241,8 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, * 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) + if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) && s->iformat && + CONFIG_RTPDEC) rtp_send_punch_packets(rtsp_st->rtp_handle); break; } @@ -1201,7 +1264,7 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); ff_url_join(url, sizeof(url), "rtp", NULL, namebuf, port, "?ttl=%d", ttl); - if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { + if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE) < 0) { err = AVERROR_INVALIDDATA; goto fail; } @@ -1222,165 +1285,15 @@ static int make_setup_request(AVFormatContext *s, const char *host, int port, return 0; fail: - for (i = 0; i < rt->nb_rtsp_streams; i++) { - if (rt->rtsp_streams[i]->rtp_handle) { - url_close(rt->rtsp_streams[i]->rtp_handle); - rt->rtsp_streams[i]->rtp_handle = NULL; - } - } + ff_rtsp_undo_setup(s); return err; } -static int rtsp_read_play(AVFormatContext *s) -{ - RTSPState *rt = s->priv_data; - RTSPMessageHeader reply1, *reply = &reply1; - int i; - char cmd[1024]; - - av_log(s, AV_LOG_DEBUG, "hello state=%d\n", rt->state); - rt->nb_byes = 0; - - if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { - if (rt->state == RTSP_STATE_PAUSED) { - cmd[0] = 0; - } else { - snprintf(cmd, sizeof(cmd), - "Range: npt=%0.3f-\r\n", - (double)rt->seek_timestamp / AV_TIME_BASE); - } - ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); - if (reply->status_code != RTSP_STATUS_OK) { - return -1; - } - if (reply->range_start != AV_NOPTS_VALUE && - rt->transport == RTSP_TRANSPORT_RTP) { - for (i = 0; i < rt->nb_rtsp_streams; i++) { - RTSPStream *rtsp_st = rt->rtsp_streams[i]; - RTPDemuxContext *rtpctx = rtsp_st->transport_priv; - AVStream *st = NULL; - if (!rtpctx) - continue; - if (rtsp_st->stream_index >= 0) - st = s->streams[rtsp_st->stream_index]; - rtpctx->last_rtcp_ntp_time = AV_NOPTS_VALUE; - rtpctx->first_rtcp_ntp_time = AV_NOPTS_VALUE; - if (st) - rtpctx->range_start_offset = av_rescale_q(reply->range_start, - AV_TIME_BASE_Q, - st->time_base); - } - } - } - rt->state = RTSP_STATE_STREAMING; - return 0; -} - -static int rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) -{ - RTSPState *rt = s->priv_data; - char cmd[1024]; - unsigned char *content = NULL; - int ret; - - /* describe the stream */ - snprintf(cmd, sizeof(cmd), - "Accept: application/sdp\r\n"); - 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, "DESCRIBE", rt->control_uri, 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, const char *addr) -{ - RTSPState *rt = s->priv_data; - RTSPMessageHeader reply1, *reply = &reply1; - int i; - char *sdp; - AVFormatContext sdp_ctx, *ctx_array[1]; - - rt->start_time = av_gettime(); - - /* Announce the stream */ - sdp = av_mallocz(SDP_MAX_SIZE); - if (sdp == NULL) - return AVERROR(ENOMEM); - /* We create the SDP based on the RTSP AVFormatContext where we - * aren't allowed to change the filename field. (We create the SDP - * based on the RTSP context since the contexts for the RTP streams - * don't exist yet.) In order to specify a custom URL with the actual - * peer IP instead of the originally specified hostname, we create - * a temporary copy of the AVFormatContext, where the custom URL is set. - * - * FIXME: Create the SDP without copying the AVFormatContext. - * This either requires setting up the RTP stream AVFormatContexts - * already here (complicating things immensely) or getting a more - * flexible SDP creation interface. - */ - sdp_ctx = *s; - ff_url_join(sdp_ctx.filename, sizeof(sdp_ctx.filename), - "rtsp", NULL, addr, -1, NULL); - ctx_array[0] = &sdp_ctx; - if (avf_sdp_create(ctx_array, 1, sdp, SDP_MAX_SIZE)) { - av_free(sdp); - return AVERROR_INVALIDDATA; - } - av_log(s, AV_LOG_INFO, "SDP:\n%s\n", 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); - 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, rt->control_uri, 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; -} - void ff_rtsp_close_connections(AVFormatContext *s) { RTSPState *rt = s->priv_data; - if (rt->rtsp_hd_out != rt->rtsp_hd) url_close(rt->rtsp_hd_out); - url_close(rt->rtsp_hd); + if (rt->rtsp_hd_out != rt->rtsp_hd) ffurl_close(rt->rtsp_hd_out); + ffurl_close(rt->rtsp_hd); rt->rtsp_hd = rt->rtsp_hd_out = NULL; } @@ -1392,7 +1305,7 @@ int ff_rtsp_connect(AVFormatContext *s) int port, err, tcp_fd; RTSPMessageHeader reply1 = {0}, *reply = &reply1; int lower_transport_mask = 0; - char real_challenge[64]; + char real_challenge[64] = ""; struct sockaddr_storage peer; socklen_t peer_len = sizeof(peer); @@ -1432,6 +1345,8 @@ redirect: } else if(!strcmp(option, "http")) { lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_TCP); rt->control_transport = RTSP_MODE_TUNNEL; + } else if (!strcmp(option, "filter_src")) { + rt->filter_source = 1; } else { /* Write options back into the buffer, using memmove instead * of strcpy since the strings may overlap. */ @@ -1476,7 +1391,7 @@ redirect: av_get_random_seed(), av_get_random_seed()); /* GET requests */ - if (url_alloc(&rt->rtsp_hd, httpname, URL_RDONLY) < 0) { + if (ffurl_alloc(&rt->rtsp_hd, httpname, AVIO_FLAG_READ) < 0) { err = AVERROR(EIO); goto fail; } @@ -1491,13 +1406,13 @@ redirect: ff_http_set_headers(rt->rtsp_hd, headers); /* complete the connection */ - if (url_connect(rt->rtsp_hd)) { + if (ffurl_connect(rt->rtsp_hd)) { err = AVERROR(EIO); goto fail; } /* POST requests */ - if (url_alloc(&rt->rtsp_hd_out, httpname, URL_WRONLY) < 0 ) { + if (ffurl_alloc(&rt->rtsp_hd_out, httpname, AVIO_FLAG_WRITE) < 0 ) { err = AVERROR(EIO); goto fail; } @@ -1533,14 +1448,14 @@ redirect: ff_http_init_auth_state(rt->rtsp_hd_out, rt->rtsp_hd); /* complete the connection */ - if (url_connect(rt->rtsp_hd_out)) { + if (ffurl_connect(rt->rtsp_hd_out)) { err = AVERROR(EIO); goto fail; } } else { /* open the tcp connection */ ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL); - if (url_open(&rt->rtsp_hd, tcpname, URL_RDWR) < 0) { + if (ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE) < 0) { err = AVERROR(EIO); goto fail; } @@ -1548,7 +1463,7 @@ redirect: } rt->seq = 0; - tcp_fd = url_get_file_handle(rt->rtsp_hd); + tcp_fd = ffurl_get_file_handle(rt->rtsp_hd); if (!getpeername(tcp_fd, (struct sockaddr*) &peer, &peer_len)) { getnameinfo((struct sockaddr*) &peer, peer_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST); @@ -1560,14 +1475,14 @@ redirect: cmd[0] = 0; if (rt->server_type == RTSP_SERVER_REAL) av_strlcat(cmd, - /** + /* * The following entries are required for proper * streaming from a Realmedia server. They are * interdependent in some way although we currently * don't quite understand how. Values were copied * from mplayer SVN r23589. - * @param CompanyID is a 16-byte ID in base64 - * @param ClientChallenge is a 16-byte ID in hex + * ClientChallenge is a 16-byte ID in hex + * CompanyID is a 16-byte ID in base64 */ "ClientChallenge: 9e26d33f2984236010ef6253fb1887f7\r\n" "PlayerStarttime: [28/03/2003:22:50:23 00:00]\r\n" @@ -1591,10 +1506,10 @@ redirect: break; } - if (s->iformat) - err = rtsp_setup_input_streams(s, reply); - else - err = rtsp_setup_output_streams(s, host); + if (s->iformat && CONFIG_RTSP_DEMUXER) + err = ff_rtsp_setup_input_streams(s, reply); + else if (CONFIG_RTSP_MUXER) + err = ff_rtsp_setup_output_streams(s, host); if (err) goto fail; @@ -1602,18 +1517,20 @@ redirect: int lower_transport = ff_log2_tab[lower_transport_mask & ~(lower_transport_mask - 1)]; - err = make_setup_request(s, host, port, lower_transport, + err = ff_rtsp_make_setup_request(s, host, port, lower_transport, rt->server_type == RTSP_SERVER_REAL ? real_challenge : NULL); if (err < 0) goto fail; lower_transport_mask &= ~(1 << lower_transport); if (lower_transport_mask == 0 && err == 1) { - err = FF_NETERROR(EPROTONOSUPPORT); + err = AVERROR(EPROTONOSUPPORT); goto fail; } } while (err); + rt->lower_transport_mask = lower_transport_mask; + av_strlcpy(rt->real_challenge, real_challenge, sizeof(rt->real_challenge)); rt->state = RTSP_STATE_IDLE; rt->seek_timestamp = 0; /* default is to start stream at position zero */ return 0; @@ -1630,92 +1547,62 @@ 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; - - rt->real_setup_cache = av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache)); - if (!rt->real_setup_cache) - return AVERROR(ENOMEM); - rt->real_setup = rt->real_setup_cache + s->nb_streams * sizeof(*rt->real_setup); - - if (ap->initial_pause) { - /* do not start immediately */ - } else { - if (rtsp_read_play(s) < 0) { - ff_rtsp_close_streams(s); - ff_rtsp_close_connections(s); - return AVERROR_INVALIDDATA; - } - } - - return 0; -} +#endif /* CONFIG_RTSP_DEMUXER || CONFIG_RTSP_MUXER */ +#if CONFIG_RTPDEC static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, - uint8_t *buf, int buf_size) + uint8_t *buf, int buf_size, int64_t wait_end) { RTSPState *rt = s->priv_data; RTSPStream *rtsp_st; - fd_set rfds; - int fd, fd_rtcp, fd_max, n, i, ret, tcp_fd, timeout_cnt = 0; - struct timeval tv; + int n, i, ret, tcp_fd, timeout_cnt = 0; + int max_p = 0; + struct pollfd *p = rt->p; for (;;) { if (url_interrupt_cb()) - return AVERROR(EINTR); - FD_ZERO(&rfds); + return AVERROR_EXIT; + if (wait_end && wait_end - av_gettime() < 0) + return AVERROR(EAGAIN); + max_p = 0; if (rt->rtsp_hd) { - tcp_fd = fd_max = url_get_file_handle(rt->rtsp_hd); - FD_SET(tcp_fd, &rfds); + tcp_fd = ffurl_get_file_handle(rt->rtsp_hd); + p[max_p].fd = tcp_fd; + p[max_p++].events = POLLIN; } else { - fd_max = 0; tcp_fd = -1; } for (i = 0; i < rt->nb_rtsp_streams; i++) { rtsp_st = rt->rtsp_streams[i]; if (rtsp_st->rtp_handle) { - fd = url_get_file_handle(rtsp_st->rtp_handle); - fd_rtcp = rtp_get_rtcp_file_handle(rtsp_st->rtp_handle); - if (FFMAX(fd, fd_rtcp) > fd_max) - fd_max = FFMAX(fd, fd_rtcp); - FD_SET(fd, &rfds); - FD_SET(fd_rtcp, &rfds); + p[max_p].fd = ffurl_get_file_handle(rtsp_st->rtp_handle); + p[max_p++].events = POLLIN; + p[max_p].fd = rtp_get_rtcp_file_handle(rtsp_st->rtp_handle); + p[max_p++].events = POLLIN; } } - tv.tv_sec = 0; - tv.tv_usec = SELECT_TIMEOUT_MS * 1000; - n = select(fd_max + 1, &rfds, NULL, NULL, &tv); + n = poll(p, max_p, POLL_TIMEOUT_MS); if (n > 0) { + int j = 1 - (tcp_fd == -1); timeout_cnt = 0; for (i = 0; i < rt->nb_rtsp_streams; i++) { rtsp_st = rt->rtsp_streams[i]; if (rtsp_st->rtp_handle) { - fd = url_get_file_handle(rtsp_st->rtp_handle); - fd_rtcp = rtp_get_rtcp_file_handle(rtsp_st->rtp_handle); - if (FD_ISSET(fd_rtcp, &rfds) || FD_ISSET(fd, &rfds)) { - ret = url_read(rtsp_st->rtp_handle, buf, buf_size); + if (p[j].revents & POLLIN || p[j+1].revents & POLLIN) { + ret = ffurl_read(rtsp_st->rtp_handle, buf, buf_size); if (ret > 0) { *prtsp_st = rtsp_st; return ret; } } + j+=2; } } #if CONFIG_RTSP_DEMUXER - if (tcp_fd != -1 && FD_ISSET(tcp_fd, &rfds)) { + if (tcp_fd != -1 && p[0].revents & POLLIN) { RTSPMessageHeader reply; - ret = ff_rtsp_read_reply(s, &reply, NULL, 0); + ret = ff_rtsp_read_reply(s, &reply, NULL, 0, NULL); if (ret < 0) return ret; /* XXX: parse message */ @@ -1724,72 +1611,18 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, } #endif } else if (n == 0 && ++timeout_cnt >= MAX_TIMEOUTS) { - return FF_NETERROR(ETIMEDOUT); + return AVERROR(ETIMEDOUT); } else if (n < 0 && errno != EINTR) return AVERROR(errno); } } -static int tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, - uint8_t *buf, int buf_size) -{ - RTSPState *rt = s->priv_data; - int id, len, i, ret; - RTSPStream *rtsp_st; - -#ifdef DEBUG_RTP_TCP - dprintf(s, "tcp_read_packet:\n"); -#endif -redo: - for (;;) { - RTSPMessageHeader reply; - - ret = ff_rtsp_read_reply(s, &reply, NULL, 1); - if (ret < 0) - return ret; - if (ret == 1) /* received '$' */ - break; - /* XXX: parse message */ - if (rt->state != RTSP_STATE_STREAMING) - return 0; - } - ret = url_read_complete(rt->rtsp_hd, buf, 3); - if (ret != 3) - return -1; - id = buf[0]; - len = AV_RB16(buf + 1); -#ifdef DEBUG_RTP_TCP - dprintf(s, "id=%d len=%d\n", id, len); -#endif - if (len > buf_size || len < 12) - goto redo; - /* get the data */ - ret = url_read_complete(rt->rtsp_hd, buf, len); - if (ret != len) - return -1; - if (rt->transport == RTSP_TRANSPORT_RDT && - ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL) < 0) - return -1; - - /* find the matching stream */ - for (i = 0; i < rt->nb_rtsp_streams; i++) { - rtsp_st = rt->rtsp_streams[i]; - if (id >= rtsp_st->interleaved_min && - id <= rtsp_st->interleaved_max) - goto found; - } - goto redo; -found: - *prtsp_st = rtsp_st; - return len; -} - -static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) +int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) { RTSPState *rt = s->priv_data; int ret, len; - uint8_t buf[10 * RTP_MAX_PACKET_LENGTH]; - RTSPStream *rtsp_st; + RTSPStream *rtsp_st, *first_queue_st = NULL; + int64_t wait_end = 0; if (rt->nb_byes == rt->nb_rtsp_streams) return AVERROR_EOF; @@ -1809,30 +1642,61 @@ static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) rt->cur_transport_priv = NULL; } + if (rt->transport == RTSP_TRANSPORT_RTP) { + int i; + int64_t first_queue_time = 0; + for (i = 0; i < rt->nb_rtsp_streams; i++) { + RTPDemuxContext *rtpctx = rt->rtsp_streams[i]->transport_priv; + int64_t queue_time; + if (!rtpctx) + continue; + queue_time = ff_rtp_queued_packet_time(rtpctx); + if (queue_time && (queue_time - first_queue_time < 0 || + !first_queue_time)) { + first_queue_time = queue_time; + first_queue_st = rt->rtsp_streams[i]; + } + } + if (first_queue_time) + wait_end = first_queue_time + s->max_delay; + } + /* read next RTP packet */ redo: + if (!rt->recvbuf) { + rt->recvbuf = av_malloc(RECVBUF_SIZE); + if (!rt->recvbuf) + return AVERROR(ENOMEM); + } + switch(rt->lower_transport) { default: #if CONFIG_RTSP_DEMUXER case RTSP_LOWER_TRANSPORT_TCP: - len = tcp_read_packet(s, &rtsp_st, buf, sizeof(buf)); + len = ff_rtsp_tcp_read_packet(s, &rtsp_st, rt->recvbuf, RECVBUF_SIZE); break; #endif case RTSP_LOWER_TRANSPORT_UDP: case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: - len = udp_read_packet(s, &rtsp_st, buf, sizeof(buf)); - if (len >=0 && rtsp_st->transport_priv && rt->transport == RTSP_TRANSPORT_RTP) + len = udp_read_packet(s, &rtsp_st, rt->recvbuf, RECVBUF_SIZE, wait_end); + if (len > 0 && rtsp_st->transport_priv && rt->transport == RTSP_TRANSPORT_RTP) rtp_check_and_send_back_rr(rtsp_st->transport_priv, len); break; } + if (len == AVERROR(EAGAIN) && first_queue_st && + rt->transport == RTSP_TRANSPORT_RTP) { + rtsp_st = first_queue_st; + ret = rtp_parse_packet(rtsp_st->transport_priv, pkt, NULL, 0); + goto end; + } if (len < 0) return len; if (len == 0) return AVERROR_EOF; if (rt->transport == RTSP_TRANSPORT_RDT) { - ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, buf, len); + ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); } else { - ret = rtp_parse_packet(rtsp_st->transport_priv, pkt, buf, len); + ret = rtp_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); if (ret < 0) { /* Either bad packet, or a RTCP packet. Check if the * first_rtcp_ntp_time field was initialized. */ @@ -1843,11 +1707,21 @@ static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) * in order to map their timestamp origin to the same ntp time * as this one. */ int i; + AVStream *st = NULL; + if (rtsp_st->stream_index >= 0) + st = s->streams[rtsp_st->stream_index]; for (i = 0; i < rt->nb_rtsp_streams; i++) { RTPDemuxContext *rtpctx2 = rt->rtsp_streams[i]->transport_priv; - if (rtpctx2 && - rtpctx2->first_rtcp_ntp_time == AV_NOPTS_VALUE) + AVStream *st2 = NULL; + if (rt->rtsp_streams[i]->stream_index >= 0) + st2 = s->streams[rt->rtsp_streams[i]->stream_index]; + if (rtpctx2 && st && st2 && + rtpctx2->first_rtcp_ntp_time == AV_NOPTS_VALUE) { rtpctx2->first_rtcp_ntp_time = rtpctx->first_rtcp_ntp_time; + rtpctx2->rtcp_ts_offset = av_rescale_q( + rtpctx->rtcp_ts_offset, st->time_base, + st2->time_base); + } } } if (ret == -RTCP_BYE) { @@ -1861,6 +1735,7 @@ static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) } } } +end: if (ret < 0) goto redo; if (ret == 1) @@ -1869,167 +1744,9 @@ static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) return ret; } +#endif /* CONFIG_RTPDEC */ -static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) -{ - RTSPState *rt = s->priv_data; - int ret; - RTSPMessageHeader reply1, *reply = &reply1; - char cmd[1024]; - - if (rt->server_type == RTSP_SERVER_REAL) { - int i; - - for (i = 0; i < s->nb_streams; i++) - rt->real_setup[i] = s->streams[i]->discard; - - if (!rt->need_subscription) { - if (memcmp (rt->real_setup, rt->real_setup_cache, - sizeof(enum AVDiscard) * s->nb_streams)) { - snprintf(cmd, sizeof(cmd), - "Unsubscribe: %s\r\n", - rt->last_subscription); - ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, - cmd, reply, NULL); - if (reply->status_code != RTSP_STATUS_OK) - return AVERROR_INVALIDDATA; - rt->need_subscription = 1; - } - } - - if (rt->need_subscription) { - int r, rule_nr, first = 1; - - memcpy(rt->real_setup_cache, rt->real_setup, - sizeof(enum AVDiscard) * s->nb_streams); - rt->last_subscription[0] = 0; - - snprintf(cmd, sizeof(cmd), - "Subscribe: "); - for (i = 0; i < rt->nb_rtsp_streams; i++) { - rule_nr = 0; - for (r = 0; r < s->nb_streams; r++) { - if (s->streams[r]->priv_data == rt->rtsp_streams[i]) { - if (s->streams[r]->discard != AVDISCARD_ALL) { - if (!first) - av_strlcat(rt->last_subscription, ",", - sizeof(rt->last_subscription)); - ff_rdt_subscribe_rule( - rt->last_subscription, - sizeof(rt->last_subscription), i, rule_nr); - first = 0; - } - rule_nr++; - } - } - } - av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription); - ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, - cmd, reply, NULL); - if (reply->status_code != RTSP_STATUS_OK) - return AVERROR_INVALIDDATA; - rt->need_subscription = 0; - - if (rt->state == RTSP_STATE_STREAMING) - rtsp_read_play (s); - } - } - - ret = rtsp_fetch_packet(s, pkt); - if (ret < 0) - return ret; - - /* send dummy request to keep TCP connection alive */ - if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2) { - if (rt->server_type == RTSP_SERVER_WMS) { - ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL); - } else { - ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL); - } - } - - return 0; -} - -/* pause the stream */ -static int rtsp_read_pause(AVFormatContext *s) -{ - RTSPState *rt = s->priv_data; - RTSPMessageHeader reply1, *reply = &reply1; - - if (rt->state != RTSP_STATE_STREAMING) - return 0; - else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { - ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, NULL); - if (reply->status_code != RTSP_STATUS_OK) { - return -1; - } - } - rt->state = RTSP_STATE_PAUSED; - return 0; -} - -static int rtsp_read_seek(AVFormatContext *s, int stream_index, - int64_t timestamp, int flags) -{ - RTSPState *rt = s->priv_data; - - rt->seek_timestamp = av_rescale_q(timestamp, - s->streams[stream_index]->time_base, - AV_TIME_BASE_Q); - switch(rt->state) { - default: - case RTSP_STATE_IDLE: - break; - case RTSP_STATE_STREAMING: - if (rtsp_read_pause(s) != 0) - return -1; - rt->state = RTSP_STATE_SEEKING; - if (rtsp_read_play(s) != 0) - return -1; - break; - case RTSP_STATE_PAUSED: - rt->state = RTSP_STATE_IDLE; - break; - } - return 0; -} - -static int rtsp_read_close(AVFormatContext *s) -{ - RTSPState *rt = s->priv_data; - -#if 0 - /* NOTE: it is valid to flush the buffer here */ - if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP) { - url_fclose(&rt->rtsp_gb); - } -#endif - ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); - - ff_rtsp_close_streams(s); - ff_rtsp_close_connections(s); - ff_network_close(); - rt->real_setup = NULL; - av_freep(&rt->real_setup_cache); - return 0; -} - -AVInputFormat rtsp_demuxer = { - "rtsp", - NULL_IF_CONFIG_SMALL("RTSP input format"), - sizeof(RTSPState), - rtsp_probe, - rtsp_read_header, - rtsp_read_packet, - rtsp_read_close, - rtsp_read_seek, - .flags = AVFMT_NOFILE, - .read_play = rtsp_read_play, - .read_pause = rtsp_read_pause, -}; -#endif - +#if CONFIG_SDP_DEMUXER static int sdp_probe(AVProbeData *p1) { const char *p = p1->buf, *p_end = p1->buf + p1->buf_size; @@ -2063,15 +1780,16 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) /* read the whole sdp file */ /* XXX: better loading */ content = av_malloc(SDP_MAX_SIZE); - size = get_buffer(s->pb, content, SDP_MAX_SIZE - 1); + size = avio_read(s->pb, content, SDP_MAX_SIZE - 1); if (size <= 0) { av_free(content); return AVERROR_INVALIDDATA; } content[size] ='\0'; - sdp_parse(s, content); + err = ff_sdp_parse(s, content); av_free(content); + if (err) goto fail; /* open each RTP stream */ for (i = 0; i < rt->nb_rtsp_streams; i++) { @@ -2084,7 +1802,7 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) namebuf, 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) { + if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE) < 0) { err = AVERROR_INVALIDDATA; goto fail; } @@ -2105,12 +1823,119 @@ static int sdp_read_close(AVFormatContext *s) return 0; } -AVInputFormat sdp_demuxer = { - "sdp", - NULL_IF_CONFIG_SMALL("SDP"), - sizeof(RTSPState), - sdp_probe, - sdp_read_header, - rtsp_fetch_packet, - sdp_read_close, +AVInputFormat ff_sdp_demuxer = { + .name = "sdp", + .long_name = NULL_IF_CONFIG_SMALL("SDP"), + .priv_data_size = sizeof(RTSPState), + .read_probe = sdp_probe, + .read_header = sdp_read_header, + .read_packet = ff_rtsp_fetch_packet, + .read_close = sdp_read_close, }; +#endif /* CONFIG_SDP_DEMUXER */ + +#if CONFIG_RTP_DEMUXER +static int rtp_probe(AVProbeData *p) +{ + if (av_strstart(p->filename, "rtp:", NULL)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int rtp_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + uint8_t recvbuf[1500]; + char host[500], sdp[500]; + int ret, port; + URLContext* in = NULL; + int payload_type; + AVCodecContext codec; + struct sockaddr_storage addr; + AVIOContext pb; + socklen_t addrlen = sizeof(addr); + + if (!ff_network_init()) + return AVERROR(EIO); + + ret = ffurl_open(&in, s->filename, AVIO_FLAG_READ); + if (ret) + goto fail; + + while (1) { + ret = ffurl_read(in, recvbuf, sizeof(recvbuf)); + if (ret == AVERROR(EAGAIN)) + continue; + if (ret < 0) + goto fail; + if (ret < 12) { + av_log(s, AV_LOG_WARNING, "Received too short packet\n"); + continue; + } + + if ((recvbuf[0] & 0xc0) != 0x80) { + av_log(s, AV_LOG_WARNING, "Unsupported RTP version packet " + "received\n"); + continue; + } + + payload_type = recvbuf[1] & 0x7f; + break; + } + getsockname(ffurl_get_file_handle(in), (struct sockaddr*) &addr, &addrlen); + ffurl_close(in); + in = NULL; + + memset(&codec, 0, sizeof(codec)); + if (ff_rtp_get_codec_info(&codec, payload_type)) { + av_log(s, AV_LOG_ERROR, "Unable to receive RTP payload type %d " + "without an SDP file describing it\n", + payload_type); + goto fail; + } + if (codec.codec_type != AVMEDIA_TYPE_DATA) { + av_log(s, AV_LOG_WARNING, "Guessing on RTP content - if not received " + "properly you need an SDP file " + "describing it\n"); + } + + av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, + NULL, 0, s->filename); + + snprintf(sdp, sizeof(sdp), + "v=0\r\nc=IN IP%d %s\r\nm=%s %d RTP/AVP %d\r\n", + addr.ss_family == AF_INET ? 4 : 6, host, + codec.codec_type == AVMEDIA_TYPE_DATA ? "application" : + codec.codec_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio", + port, payload_type); + av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", sdp); + + ffio_init_context(&pb, sdp, strlen(sdp), 0, NULL, NULL, NULL, NULL); + s->pb = &pb; + + /* sdp_read_header initializes this again */ + ff_network_close(); + + ret = sdp_read_header(s, ap); + s->pb = NULL; + return ret; + +fail: + if (in) + ffurl_close(in); + ff_network_close(); + return ret; +} + +AVInputFormat ff_rtp_demuxer = { + .name = "rtp", + .long_name = NULL_IF_CONFIG_SMALL("RTP input format"), + .priv_data_size = sizeof(RTSPState), + .read_probe = rtp_probe, + .read_header = rtp_read_header, + .read_packet = ff_rtsp_fetch_packet, + .read_close = sdp_read_close, + .flags = AVFMT_NOFILE, +}; +#endif /* CONFIG_RTP_DEMUXER */ +