X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtsp.c;h=2c616e80b7995f04d94b14ccd75ef65e6383df73;hb=2c17fb61ced2059034856a6c6cd303014aed01fe;hp=ce18610b4be84352b7da509aa6402e0a3bac7c65;hpb=e7993ece9be619fe70da32f8b9f616b697b282bc;p=ffmpeg diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index ce18610b4be..2c616e80b79 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -41,6 +41,7 @@ #include "rtsp.h" #include "rtpdec.h" +#include "rtpproto.h" #include "rdt.h" #include "rtpdec_formats.h" #include "rtpenc_chain.h" @@ -48,8 +49,6 @@ #include "rtpenc.h" #include "mpegts.h" -//#define DEBUG - /* Timeout values for socket poll, in ms, * and read_packet(), in seconds */ #define POLL_TIMEOUT_MS 100 @@ -64,43 +63,65 @@ #define ENC AV_OPT_FLAG_ENCODING_PARAM #define RTSP_FLAG_OPTS(name, longname) \ - { name, longname, OFFSET(rtsp_flags), AV_OPT_TYPE_FLAGS, {0}, INT_MIN, INT_MAX, DEC, "rtsp_flags" }, \ - { "filter_src", "Only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" }, \ - { "listen", "Wait for incoming connections", 0, AV_OPT_TYPE_CONST, {RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" } + { name, longname, OFFSET(rtsp_flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtsp_flags" }, \ + { "filter_src", "Only receive packets from the negotiated peer IP", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_FILTER_SRC}, 0, 0, DEC, "rtsp_flags" } #define RTSP_MEDIATYPE_OPTS(name, longname) \ - { name, longname, OFFSET(media_type_mask), AV_OPT_TYPE_FLAGS, { (1 << (AVMEDIA_TYPE_DATA+1)) - 1 }, INT_MIN, INT_MAX, DEC, "allowed_media_types" }, \ - { "video", "Video", 0, AV_OPT_TYPE_CONST, {1 << AVMEDIA_TYPE_VIDEO}, 0, 0, DEC, "allowed_media_types" }, \ - { "audio", "Audio", 0, AV_OPT_TYPE_CONST, {1 << AVMEDIA_TYPE_AUDIO}, 0, 0, DEC, "allowed_media_types" }, \ - { "data", "Data", 0, AV_OPT_TYPE_CONST, {1 << AVMEDIA_TYPE_DATA}, 0, 0, DEC, "allowed_media_types" } + { name, longname, OFFSET(media_type_mask), AV_OPT_TYPE_FLAGS, { .i64 = (1 << (AVMEDIA_TYPE_DATA+1)) - 1 }, INT_MIN, INT_MAX, DEC, "allowed_media_types" }, \ + { "video", "Video", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_VIDEO}, 0, 0, DEC, "allowed_media_types" }, \ + { "audio", "Audio", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_AUDIO}, 0, 0, DEC, "allowed_media_types" }, \ + { "data", "Data", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << AVMEDIA_TYPE_DATA}, 0, 0, DEC, "allowed_media_types" } + +#define COMMON_OPTS() \ + { "reorder_queue_size", "Number of packets to buffer for handling of reordered packets", OFFSET(reordering_queue_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC }, \ + { "buffer_size", "Underlying protocol send/receive buffer size", OFFSET(buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, DEC|ENC } \ + const AVOption ff_rtsp_options[] = { - { "initial_pause", "Don't start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_INT, {0}, 0, 1, DEC }, + { "initial_pause", "Don't start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC }, FF_RTP_FLAG_OPTS(RTSPState, rtp_muxer_flags), - { "rtsp_transport", "RTSP transport protocols", OFFSET(lower_transport_mask), AV_OPT_TYPE_FLAGS, {0}, INT_MIN, INT_MAX, DEC|ENC, "rtsp_transport" }, \ - { "udp", "UDP", 0, AV_OPT_TYPE_CONST, {1 << RTSP_LOWER_TRANSPORT_UDP}, 0, 0, DEC|ENC, "rtsp_transport" }, \ - { "tcp", "TCP", 0, AV_OPT_TYPE_CONST, {1 << RTSP_LOWER_TRANSPORT_TCP}, 0, 0, DEC|ENC, "rtsp_transport" }, \ - { "udp_multicast", "UDP multicast", 0, AV_OPT_TYPE_CONST, {1 << RTSP_LOWER_TRANSPORT_UDP_MULTICAST}, 0, 0, DEC, "rtsp_transport" }, - { "http", "HTTP tunneling", 0, AV_OPT_TYPE_CONST, {(1 << RTSP_LOWER_TRANSPORT_HTTP)}, 0, 0, DEC, "rtsp_transport" }, + { "rtsp_transport", "RTSP transport protocols", OFFSET(lower_transport_mask), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC|ENC, "rtsp_transport" }, \ + { "udp", "UDP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP}, 0, 0, DEC|ENC, "rtsp_transport" }, \ + { "tcp", "TCP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_TCP}, 0, 0, DEC|ENC, "rtsp_transport" }, \ + { "udp_multicast", "UDP multicast", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP_MULTICAST}, 0, 0, DEC, "rtsp_transport" }, + { "http", "HTTP tunneling", 0, AV_OPT_TYPE_CONST, {.i64 = (1 << RTSP_LOWER_TRANSPORT_HTTP)}, 0, 0, DEC, "rtsp_transport" }, RTSP_FLAG_OPTS("rtsp_flags", "RTSP flags"), + { "listen", "Wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" }, RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"), - { "min_port", "Minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC }, - { "max_port", "Maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC }, - { "timeout", "Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies flag listen", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {-1}, INT_MIN, INT_MAX, DEC }, + { "min_port", "Minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC }, + { "max_port", "Maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC }, + { "timeout", "Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies flag listen", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC }, + COMMON_OPTS(), { NULL }, }; static const AVOption sdp_options[] = { RTSP_FLAG_OPTS("sdp_flags", "SDP flags"), + { "custom_io", "Use custom IO", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_CUSTOM_IO}, 0, 0, DEC, "rtsp_flags" }, + { "rtcp_to_source", "Send RTCP packets to the source address of received packets", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_RTCP_TO_SOURCE}, 0, 0, DEC, "rtsp_flags" }, RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"), + COMMON_OPTS(), { NULL }, }; static const AVOption rtp_options[] = { RTSP_FLAG_OPTS("rtp_flags", "RTP flags"), + COMMON_OPTS(), { NULL }, }; + +static AVDictionary *map_to_opts(RTSPState *rt) +{ + AVDictionary *opts = NULL; + char buf[256]; + + snprintf(buf, sizeof(buf), "%d", rt->buffer_size); + av_dict_set(&opts, "buffer_size", buf, 0); + + return opts; +} + static void get_word_until_chars(char *buf, int buf_size, const char *sep, const char **pp) { @@ -148,22 +169,28 @@ static void rtsp_parse_range_npt(const char *p, int64_t *start, int64_t *end) *end = AV_NOPTS_VALUE; get_word_sep(buf, sizeof(buf), "-", &p); - av_parse_time(start, buf, 1); + if (av_parse_time(start, buf, 1) < 0) + return; 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) +static int get_sockaddr(AVFormatContext *s, + const char *buf, struct sockaddr_storage *sock) { struct addrinfo hints = { 0 }, *ai = NULL; + int ret; + hints.ai_flags = AI_NUMERICHOST; - if (getaddrinfo(buf, NULL, &hints, &ai)) + if ((ret = getaddrinfo(buf, NULL, &hints, &ai))) { + av_log(s, AV_LOG_ERROR, "getaddrinfo(%s): %s\n", + buf, + gai_strerror(ret)); return -1; + } memcpy(sock, ai->ai_addr, FFMIN(sizeof(*sock), ai->ai_addrlen)); freeaddrinfo(ai); return 0; @@ -171,19 +198,42 @@ static int get_sockaddr(const char *buf, struct sockaddr_storage *sock) #if CONFIG_RTPDEC static void init_rtp_handler(RTPDynamicProtocolHandler *handler, - RTSPStream *rtsp_st, AVCodecContext *codec) + RTSPStream *rtsp_st, AVStream *st) { + AVCodecContext *codec = st ? st->codec : NULL; if (!handler) return; - codec->codec_id = handler->codec_id; + if (codec) + codec->codec_id = handler->codec_id; rtsp_st->dynamic_handler = handler; - if (handler->alloc) { - rtsp_st->dynamic_protocol_context = handler->alloc(); + if (st) + st->need_parsing = handler->need_parsing; + if (handler->priv_data_size) { + rtsp_st->dynamic_protocol_context = av_mallocz(handler->priv_data_size); if (!rtsp_st->dynamic_protocol_context) rtsp_st->dynamic_handler = NULL; } } +static void finalize_rtp_handler_init(AVFormatContext *s, RTSPStream *rtsp_st, + AVStream *st) +{ + if (rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->init) { + int ret = rtsp_st->dynamic_handler->init(s, st ? st->index : -1, + rtsp_st->dynamic_protocol_context); + if (ret < 0) { + if (rtsp_st->dynamic_protocol_context) { + if (rtsp_st->dynamic_handler->close) + rtsp_st->dynamic_handler->close( + rtsp_st->dynamic_protocol_context); + av_free(rtsp_st->dynamic_protocol_context); + } + rtsp_st->dynamic_protocol_context = NULL; + rtsp_st->dynamic_handler = NULL; + } + } +} + /* parse the rtpmap description: /[/] */ static int sdp_parse_rtpmap(AVFormatContext *s, AVStream *st, RTSPStream *rtsp_st, @@ -195,8 +245,7 @@ static int sdp_parse_rtpmap(AVFormatContext *s, AVCodec *c; const char *c_name; - /* Loop into AVRtpDynamicPayloadTypes[] and AVRtpPayloadTypes[] and - * see if we can handle this kind of payload. + /* See if we can handle this kind of payload. * The space should normally not be there but some Real streams or * particular servers ("RealServer Version 6.1.3.970", see issue 1658) * have a trailing space. */ @@ -204,14 +253,13 @@ static int sdp_parse_rtpmap(AVFormatContext *s, if (payload_type < RTP_PT_PRIVATE) { /* We are in a standard case * (from http://www.iana.org/assignments/rtp-parameters). */ - /* search into AVRtpPayloadTypes[] */ codec->codec_id = ff_rtp_codec_id(buf, codec->codec_type); } if (codec->codec_id == AV_CODEC_ID_NONE) { RTPDynamicProtocolHandler *handler = ff_rtp_handler_find_by_name(buf, codec->codec_type); - init_rtp_handler(handler, rtsp_st, codec); + init_rtp_handler(handler, rtsp_st, st); /* 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 @@ -240,10 +288,6 @@ static int sdp_parse_rtpmap(AVFormatContext *s, 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); @@ -258,9 +302,7 @@ static int sdp_parse_rtpmap(AVFormatContext *s, default: break; } - if (rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->init) - rtsp_st->dynamic_handler->init(s, st->index, - rtsp_st->dynamic_protocol_context); + finalize_rtp_handler_init(s, rtsp_st, st); return 0; } @@ -288,8 +330,46 @@ typedef struct SDPParseState { struct sockaddr_storage default_ip; int default_ttl; int skip_media; ///< set if an unknown m= line occurs + int nb_default_include_source_addrs; /**< Number of source-specific multicast include source IP address (from SDP content) */ + struct RTSPSource **default_include_source_addrs; /**< Source-specific multicast include source IP address (from SDP content) */ + int nb_default_exclude_source_addrs; /**< Number of source-specific multicast exclude source IP address (from SDP content) */ + struct RTSPSource **default_exclude_source_addrs; /**< Source-specific multicast exclude source IP address (from SDP content) */ + int seen_rtpmap; + int seen_fmtp; + char delayed_fmtp[2048]; } SDPParseState; +static void copy_default_source_addrs(struct RTSPSource **addrs, int count, + struct RTSPSource ***dest, int *dest_count) +{ + RTSPSource *rtsp_src, *rtsp_src2; + int i; + for (i = 0; i < count; i++) { + rtsp_src = addrs[i]; + rtsp_src2 = av_malloc(sizeof(*rtsp_src2)); + if (!rtsp_src2) + continue; + memcpy(rtsp_src2, rtsp_src, sizeof(*rtsp_src)); + dynarray_add(dest, dest_count, rtsp_src2); + } +} + +static void parse_fmtp(AVFormatContext *s, RTSPState *rt, + int payload_type, const char *line) +{ + int i; + + for (i = 0; i < rt->nb_rtsp_streams; i++) { + RTSPStream *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) { + rtsp_st->dynamic_handler->parse_sdp_a_line(s, i, + rtsp_st->dynamic_protocol_context, line); + } + } +} + static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, int letter, const char *buf) { @@ -297,13 +377,14 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, char buf1[64], st_type[64]; const char *p; enum AVMediaType codec_type; - int payload_type, i; + int payload_type; AVStream *st; RTSPStream *rtsp_st; + RTSPSource *rtsp_src; struct sockaddr_storage sdp_ip; int ttl; - av_dlog(s, "sdp: %c='%s'\n", letter, buf); + av_log(s, AV_LOG_TRACE, "sdp: %c='%s'\n", letter, buf); p = buf; if (s1->skip_media && letter != 'm') @@ -317,7 +398,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6")) return; get_word_sep(buf1, sizeof(buf1), "/", &p); - if (get_sockaddr(buf1, &sdp_ip)) + if (get_sockaddr(s, buf1, &sdp_ip)) return; ttl = 16; if (*p == '/') { @@ -345,14 +426,16 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, break; case 'm': /* new stream */ - s1->skip_media = 0; + s1->skip_media = 0; + s1->seen_fmtp = 0; + s1->seen_rtpmap = 0; codec_type = AVMEDIA_TYPE_UNKNOWN; get_word(st_type, sizeof(st_type), &p); if (!strcmp(st_type, "audio")) { codec_type = AVMEDIA_TYPE_AUDIO; } else if (!strcmp(st_type, "video")) { codec_type = AVMEDIA_TYPE_VIDEO; - } else if (!strcmp(st_type, "application")) { + } else if (!strcmp(st_type, "application") || !strcmp(st_type, "text")) { codec_type = AVMEDIA_TYPE_DATA; } if (codec_type == AVMEDIA_TYPE_UNKNOWN || !(rt->media_type_mask & (1 << codec_type))) { @@ -368,12 +451,23 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, rtsp_st->sdp_ip = s1->default_ip; rtsp_st->sdp_ttl = s1->default_ttl; + copy_default_source_addrs(s1->default_include_source_addrs, + s1->nb_default_include_source_addrs, + &rtsp_st->include_source_addrs, + &rtsp_st->nb_include_source_addrs); + copy_default_source_addrs(s1->default_exclude_source_addrs, + s1->nb_default_exclude_source_addrs, + &rtsp_st->exclude_source_addrs, + &rtsp_st->nb_exclude_source_addrs); + get_word(buf1, sizeof(buf1), &p); /* port */ rtsp_st->sdp_port = atoi(buf1); get_word(buf1, sizeof(buf1), &p); /* protocol */ if (!strcmp(buf1, "udp")) rt->transport = RTSP_TRANSPORT_RAW; + else if (strstr(buf1, "/AVPF") || strstr(buf1, "/SAVPF")) + rtsp_st->feedback = 1; /* XXX: handle list of formats */ get_word(buf1, sizeof(buf1), &p); /* format list */ @@ -381,8 +475,16 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, if (!strcmp(ff_rtp_enc_name(rtsp_st->sdp_payload_type), "MP2T")) { /* no corresponding stream */ - if (rt->transport == RTSP_TRANSPORT_RAW && !rt->ts && CONFIG_RTPDEC) - rt->ts = ff_mpegts_parse_open(s); + if (rt->transport == RTSP_TRANSPORT_RAW) { + if (CONFIG_RTPDEC && !rt->ts) + rt->ts = ff_mpegts_parse_open(s); + } else { + RTPDynamicProtocolHandler *handler; + handler = ff_rtp_handler_find_by_id( + rtsp_st->sdp_payload_type, AVMEDIA_TYPE_DATA); + init_rtp_handler(handler, rtsp_st, NULL); + finalize_rtp_handler_init(s, rtsp_st, NULL); + } } else if (rt->server_type == RTSP_SERVER_WMS && codec_type == AVMEDIA_TYPE_DATA) { /* RTX stream, a stream that carries all the other actual @@ -404,11 +506,11 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, /* 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); - if (handler && handler->init) - handler->init(s, st->index, - rtsp_st->dynamic_protocol_context); + init_rtp_handler(handler, rtsp_st, st); + finalize_rtp_handler_init(s, rtsp_st, st); } + if (rt->default_lang[0]) + av_dict_set(&st->metadata, "language", rt->default_lang, 0); } /* put a default control url */ av_strlcpy(rtsp_st->control_url, rt->control_uri, @@ -448,19 +550,20 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, st = s->streams[rtsp_st->stream_index]; sdp_parse_rtpmap(s, st, rtsp_st, payload_type, p); } + s1->seen_rtpmap = 1; + if (s1->seen_fmtp) { + parse_fmtp(s, rt, payload_type, s1->delayed_fmtp); + } } 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 < 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) - rtsp_st->dynamic_handler->parse_sdp_a_line(s, i, - rtsp_st->dynamic_protocol_context, buf); + if (s1->seen_rtpmap) { + parse_fmtp(s, rt, payload_type, buf); + } else { + s1->seen_fmtp = 1; + av_strlcpy(s1->delayed_fmtp, buf, sizeof(s1->delayed_fmtp)); } } else if (av_strstart(p, "range:", &p)) { int64_t start, end; @@ -471,6 +574,16 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, /* AV_NOPTS_VALUE means live broadcast (and can't seek) */ s->duration = (end == AV_NOPTS_VALUE) ? AV_NOPTS_VALUE : end - start; + } else if (av_strstart(p, "lang:", &p)) { + if (s->nb_streams > 0) { + get_word(buf1, sizeof(buf1), &p); + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; + if (rtsp_st->stream_index >= 0) { + st = s->streams[rtsp_st->stream_index]; + av_dict_set(&st->metadata, "language", buf1, 0); + } + } else + get_word(rt->default_lang, sizeof(rt->default_lang), &p); } else if (av_strstart(p, "IsRealDataType:integer;",&p)) { if (atoi(p) == 1) rt->transport = RTSP_TRANSPORT_RDT; @@ -478,6 +591,51 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, s->nb_streams > 0) { st = s->streams[s->nb_streams - 1]; st->codec->sample_rate = atoi(p); + } else if (av_strstart(p, "crypto:", &p) && s->nb_streams > 0) { + // RFC 4568 + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; + get_word(buf1, sizeof(buf1), &p); // ignore tag + get_word(rtsp_st->crypto_suite, sizeof(rtsp_st->crypto_suite), &p); + p += strspn(p, SPACE_CHARS); + if (av_strstart(p, "inline:", &p)) + get_word(rtsp_st->crypto_params, sizeof(rtsp_st->crypto_params), &p); + } else if (av_strstart(p, "source-filter:", &p)) { + int exclude = 0; + get_word(buf1, sizeof(buf1), &p); + if (strcmp(buf1, "incl") && strcmp(buf1, "excl")) + return; + exclude = !strcmp(buf1, "excl"); + + get_word(buf1, sizeof(buf1), &p); + if (strcmp(buf1, "IN") != 0) + return; + get_word(buf1, sizeof(buf1), &p); + if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6") && strcmp(buf1, "*")) + return; + // not checking that the destination address actually matches or is wildcard + get_word(buf1, sizeof(buf1), &p); + + while (*p != '\0') { + rtsp_src = av_mallocz(sizeof(*rtsp_src)); + if (!rtsp_src) + return; + get_word(rtsp_src->addr, sizeof(rtsp_src->addr), &p); + if (exclude) { + if (s->nb_streams == 0) { + dynarray_add(&s1->default_exclude_source_addrs, &s1->nb_default_exclude_source_addrs, rtsp_src); + } else { + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; + dynarray_add(&rtsp_st->exclude_source_addrs, &rtsp_st->nb_exclude_source_addrs, rtsp_src); + } + } else { + if (s->nb_streams == 0) { + dynarray_add(&s1->default_include_source_addrs, &s1->nb_default_include_source_addrs, rtsp_src); + } else { + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; + dynarray_add(&rtsp_st->include_source_addrs, &rtsp_st->nb_include_source_addrs, rtsp_src); + } + } + } } else { if (rt->server_type == RTSP_SERVER_WMS) ff_wms_parse_sdp_a_line(s, p); @@ -502,7 +660,7 @@ int ff_sdp_parse(AVFormatContext *s, const char *content) { RTSPState *rt = s->priv_data; const char *p; - int letter; + int letter, i; /* Some SDP lines, particularly for Realmedia or ASF RTSP streams, * contain long SDP lines containing complete ASF Headers (several * kB) or arrays of MDPR (RM stream descriptor) headers plus @@ -539,13 +697,21 @@ int ff_sdp_parse(AVFormatContext *s, const char *content) if (*p == '\n') p++; } + + for (i = 0; i < s1->nb_default_include_source_addrs; i++) + av_free(s1->default_include_source_addrs[i]); + av_freep(&s1->default_include_source_addrs); + for (i = 0; i < s1->nb_default_exclude_source_addrs; i++) + av_free(s1->default_exclude_source_addrs[i]); + av_freep(&s1->default_exclude_source_addrs); + 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) +void ff_rtsp_undo_setup(AVFormatContext *s, int send_packets) { RTSPState *rt = s->priv_data; int i; @@ -559,16 +725,16 @@ void ff_rtsp_undo_setup(AVFormatContext *s) 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); + if (CONFIG_RTSP_MUXER && rtpctx->pb && send_packets) + ff_rtsp_tcp_write_packet(s, rtsp_st); + ffio_free_dyn_buf(&rtpctx->pb); } else { avio_close(rtpctx->pb); } avformat_free_context(rtpctx); - } else if (rt->transport == RTSP_TRANSPORT_RDT && CONFIG_RTPDEC) + } else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RDT) ff_rdt_parse_close(rtsp_st->transport_priv); - else if (rt->transport == RTSP_TRANSPORT_RAW && CONFIG_RTPDEC) + else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RTP) ff_rtp_parse_close(rtsp_st->transport_priv); } rtsp_st->transport_priv = NULL; @@ -582,16 +748,26 @@ void ff_rtsp_undo_setup(AVFormatContext *s) void ff_rtsp_close_streams(AVFormatContext *s) { RTSPState *rt = s->priv_data; - int i; + int i, j; RTSPStream *rtsp_st; - ff_rtsp_undo_setup(s); + ff_rtsp_undo_setup(s, 0); for (i = 0; i < rt->nb_rtsp_streams; i++) { rtsp_st = rt->rtsp_streams[i]; if (rtsp_st) { - if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context) - rtsp_st->dynamic_handler->free( - rtsp_st->dynamic_protocol_context); + if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context) { + if (rtsp_st->dynamic_handler->close) + rtsp_st->dynamic_handler->close( + rtsp_st->dynamic_protocol_context); + av_free(rtsp_st->dynamic_protocol_context); + } + for (j = 0; j < rtsp_st->nb_include_source_addrs; j++) + av_free(rtsp_st->include_source_addrs[j]); + av_freep(&rtsp_st->include_source_addrs); + for (j = 0; j < rtsp_st->nb_exclude_source_addrs; j++) + av_free(rtsp_st->exclude_source_addrs[j]); + av_freep(&rtsp_st->exclude_source_addrs); + av_free(rtsp_st); } } @@ -599,7 +775,7 @@ void ff_rtsp_close_streams(AVFormatContext *s) if (rt->asf_ctx) { avformat_close_input(&rt->asf_ctx); } - if (rt->ts && CONFIG_RTPDEC) + if (CONFIG_RTPDEC && rt->ts) ff_mpegts_parse_close(rt->ts); av_free(rt->p); av_free(rt->recvbuf); @@ -609,6 +785,13 @@ int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) { RTSPState *rt = s->priv_data; AVStream *st = NULL; + int reordering_queue_size = rt->reordering_queue_size; + if (reordering_queue_size < 0) { + if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP || !s->max_delay) + reordering_queue_size = 0; + else + reordering_queue_size = RTP_REORDER_QUEUE_DEFAULT_SIZE; + } /* open the RTP context */ if (rtsp_st->stream_index >= 0) @@ -616,34 +799,39 @@ int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) if (!st) s->ctx_flags |= AVFMTCTX_NOHEADER; - if (s->oformat && CONFIG_RTSP_MUXER) { - int ret = ff_rtp_chain_mux_open(&rtsp_st->transport_priv, s, st, - rtsp_st->rtp_handle, - RTSP_TCP_MAX_PACKET_SIZE); + if (CONFIG_RTSP_MUXER && s->oformat) { + int ret = ff_rtp_chain_mux_open((AVFormatContext **)&rtsp_st->transport_priv, + s, st, rtsp_st->rtp_handle, + RTSP_TCP_MAX_PACKET_SIZE, + rtsp_st->stream_index); /* Ownership of rtp_handle is passed to the rtp mux context */ rtsp_st->rtp_handle = NULL; if (ret < 0) return ret; + st->time_base = ((AVFormatContext*)rtsp_st->transport_priv)->streams[0]->time_base; } else if (rt->transport == RTSP_TRANSPORT_RAW) { return 0; // Don't need to open any parser here - } else if (rt->transport == RTSP_TRANSPORT_RDT && CONFIG_RTPDEC) + } else if (CONFIG_RTPDEC && 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); else if (CONFIG_RTPDEC) - rtsp_st->transport_priv = ff_rtp_parse_open(s, st, rtsp_st->rtp_handle, + rtsp_st->transport_priv = ff_rtp_parse_open(s, st, rtsp_st->sdp_payload_type, - (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP || !s->max_delay) - ? 0 : RTP_REORDER_QUEUE_DEFAULT_SIZE); + reordering_queue_size); if (!rtsp_st->transport_priv) { return AVERROR(ENOMEM); - } else if (rt->transport == RTSP_TRANSPORT_RTP && CONFIG_RTPDEC) { + } else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RTP) { if (rtsp_st->dynamic_handler) { ff_rtp_parse_set_dynamic_protocol(rtsp_st->transport_priv, rtsp_st->dynamic_protocol_context, rtsp_st->dynamic_handler); } + if (rtsp_st->crypto_suite[0]) + ff_rtp_parse_set_crypto(rtsp_st->transport_priv, + rtsp_st->crypto_suite, + rtsp_st->crypto_params); } return 0; @@ -672,7 +860,8 @@ static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp) } /* XXX: only one transport specification is parsed */ -static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) +static void rtsp_parse_transport(AVFormatContext *s, + RTSPMessageHeader *reply, const char *p) { char transport_protocol[16]; char profile[16]; @@ -755,14 +944,16 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) th->lower_transport = RTSP_LOWER_TRANSPORT_UDP_MULTICAST; } else if (!strcmp(parameter, "ttl")) { if (*p == '=') { + char *end; p++; - th->ttl = strtol(p, (char **)&p, 10); + th->ttl = strtol(p, &end, 10); + p = end; } } else if (!strcmp(parameter, "destination")) { if (*p == '=') { p++; get_word_sep(buf, sizeof(buf), ";,", &p); - get_sockaddr(buf, &th->destination); + get_sockaddr(s, buf, &th->destination); } } else if (!strcmp(parameter, "source")) { if (*p == '=') { @@ -789,6 +980,8 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) p++; reply->nb_transports++; + if (reply->nb_transports >= RTSP_MAX_TRANSPORTS) + break; } } @@ -847,7 +1040,8 @@ static void rtsp_parse_rtp_info(RTSPState *rt, const char *p) handle_rtp_info(rt, url, seq, rtptime); } -void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf, +void ff_rtsp_parse_line(AVFormatContext *s, + RTSPMessageHeader *reply, const char *buf, RTSPState *rt, const char *method) { const char *p; @@ -864,7 +1058,7 @@ void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf, } else if (av_stristart(p, "Content-Length:", &p)) { reply->content_length = strtol(p, NULL, 10); } else if (av_stristart(p, "Transport:", &p)) { - rtsp_parse_transport(reply, p); + rtsp_parse_transport(s, reply, p); } else if (av_stristart(p, "CSeq:", &p)) { reply->seq = strtol(p, NULL, 10); } else if (av_stristart(p, "Range:", &p)) { @@ -920,7 +1114,7 @@ void ff_rtsp_skip_packet(AVFormatContext *s) return; len = AV_RB16(buf + 1); - av_dlog(s, "skipping RTP packet len=%d\n", len); + av_log(s, AV_LOG_TRACE, "skipping RTP packet len=%d\n", len); /* skip payload */ while (len > 0) { @@ -957,13 +1151,12 @@ start: q = buf; for (;;) { ret = ffurl_read_complete(rt->rtsp_hd, &ch, 1); - av_dlog(s, "ret=%d c=%02x [%c]\n", ret, ch, ch); + av_log(s, AV_LOG_TRACE, "ret=%d c=%02x [%c]\n", ret, ch, ch); if (ret != 1) return AVERROR_EOF; if (ch == '\n') break; - if (ch == '$') { - /* XXX: only parse it if first char on line ? */ + if (ch == '$' && q == buf) { if (return_on_interleaved_data) { return 1; } else @@ -975,7 +1168,7 @@ start: } *q = '\0'; - av_dlog(s, "line='%s'\n", buf); + av_log(s, AV_LOG_TRACE, "line='%s'\n", buf); /* test if last line */ if (buf[0] == '\0') @@ -994,7 +1187,7 @@ start: request = 1; } } else { - ff_rtsp_parse_line(reply, p, rt, method); + ff_rtsp_parse_line(s, reply, p, rt, method); av_strlcat(rt->last_reply, p, sizeof(rt->last_reply)); av_strlcat(rt->last_reply, "\n", sizeof(rt->last_reply)); } @@ -1008,6 +1201,8 @@ start: if (content_length > 0) { /* leave some room for a trailing '\0' (useful for simple parsing) */ content = av_malloc(content_length + 1); + if (!content) + return AVERROR(ENOMEM); ffurl_read_complete(rt->rtsp_hd, content, content_length); content[content_length] = '\0'; } @@ -1039,7 +1234,7 @@ start: } ffurl_write(rt->rtsp_hd_out, ptr, strlen(ptr)); - rt->last_cmd_time = av_gettime(); + rt->last_cmd_time = av_gettime_relative(); /* Even if the request from the server had data, it is not the data * that the caller wants or expects. The memory could also be leaked * if the actual following reply has content data. */ @@ -1086,11 +1281,11 @@ start: * * @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) +static int 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; @@ -1103,6 +1298,7 @@ static int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, if (headers) av_strlcat(buf, headers, sizeof(buf)); av_strlcatf(buf, sizeof(buf), "CSeq: %d\r\n", rt->seq); + av_strlcatf(buf, sizeof(buf), "User-Agent: %s\r\n", LIBAVFORMAT_IDENT); if (rt->session_id[0] != '\0' && (!headers || !strstr(headers, "\nIf-Match:"))) { av_strlcatf(buf, sizeof(buf), "Session: %s\r\n", rt->session_id); @@ -1124,7 +1320,7 @@ static int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, out_buf = base64buf; } - av_dlog(s, "Sending:\n%s--\n", buf); + av_log(s, AV_LOG_TRACE, "Sending:\n%s--\n", buf); ffurl_write(rt->rtsp_hd_out, out_buf, strlen(out_buf)); if (send_content_length > 0 && send_content) { @@ -1135,7 +1331,7 @@ static int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, } ffurl_write(rt->rtsp_hd_out, send_content, send_content_length); } - rt->last_cmd_time = av_gettime(); + rt->last_cmd_time = av_gettime_relative(); return 0; } @@ -1143,7 +1339,7 @@ static int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, int ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method, const char *url, const char *headers) { - return ff_rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 0); + return rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 0); } int ff_rtsp_send_cmd(AVFormatContext *s, const char *method, const char *url, @@ -1168,9 +1364,9 @@ int ff_rtsp_send_cmd_with_content(AVFormatContext *s, retry: cur_auth_type = rt->auth_state.auth_type; - if ((ret = ff_rtsp_send_cmd_with_content_async(s, method, url, header, - send_content, - send_content_length))) + if ((ret = rtsp_send_cmd_with_content_async(s, method, url, header, + send_content, + send_content_length))) return ret; if ((ret = ff_rtsp_read_reply(s, reply, content_ptr, 0, method) ) < 0) @@ -1262,12 +1458,18 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, /* first try in specified port range */ while (j <= rt->rtp_port_max) { + AVDictionary *opts = map_to_opts(rt); + 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 (!ffurl_open(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE, - &s->interrupt_callback, NULL)) + err = ffurl_open(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE, + &s->interrupt_callback, &opts); + + av_dict_free(&opts); + + if (!err) goto rtp_opened; } @@ -1323,7 +1525,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, transport); if (rt->accept_dynamic_rate) av_strlcat(cmd, "x-Dynamic-Rate: 0\r\n", sizeof(cmd)); - if (i == 0 && rt->server_type == RTSP_SERVER_REAL && CONFIG_RTPDEC) { + if (CONFIG_RTPDEC && i == 0 && rt->server_type == RTSP_SERVER_REAL) { char real_res[41], real_csum[9]; ff_rdt_calc_response_and_checksum(real_res, real_csum, real_challenge); @@ -1370,30 +1572,20 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, case RTSP_LOWER_TRANSPORT_UDP: { char url[1024], options[30] = ""; + const char *peer = host; if (rt->rtsp_flags & RTSP_FLAG_FILTER_SRC) 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, "%s", options); - } else { - ff_url_join(url, sizeof(url), "rtp", NULL, host, - reply->transports[0].server_port_min, "%s", options); - } + if (reply->transports[0].source[0]) + peer = reply->transports[0].source; + ff_url_join(url, sizeof(url), "rtp", NULL, peer, + reply->transports[0].server_port_min, "%s", options); if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) && ff_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 && - CONFIG_RTPDEC) - ff_rtp_send_punch_packets(rtsp_st->rtp_handle); break; } case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: { @@ -1438,7 +1630,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, return 0; fail: - ff_rtsp_undo_setup(s); + ff_rtsp_undo_setup(s, 0); return err; } @@ -1453,10 +1645,13 @@ void ff_rtsp_close_connections(AVFormatContext *s) int ff_rtsp_connect(AVFormatContext *s) { RTSPState *rt = s->priv_data; - char host[1024], path[1024], tcpname[1024], cmd[2048], auth[128]; + char proto[128], host[1024], path[1024]; + char tcpname[1024], cmd[2048], auth[128]; + const char *lower_rtsp_proto = "tcp"; int port, err, tcp_fd; RTSPMessageHeader reply1 = {0}, *reply = &reply1; int lower_transport_mask = 0; + int default_port = RTSP_DEFAULT_PORT; char real_challenge[64] = ""; struct sockaddr_storage peer; socklen_t peer_len = sizeof(peer); @@ -1483,15 +1678,23 @@ int ff_rtsp_connect(AVFormatContext *s) rt->lower_transport_mask &= (1 << RTSP_LOWER_TRANSPORT_NB) - 1; redirect: - lower_transport_mask = rt->lower_transport_mask; /* extract hostname and port */ - av_url_split(NULL, 0, auth, sizeof(auth), + av_url_split(proto, sizeof(proto), auth, sizeof(auth), host, sizeof(host), &port, path, sizeof(path), s->filename); + + if (!strcmp(proto, "rtsps")) { + lower_rtsp_proto = "tls"; + default_port = RTSPS_DEFAULT_PORT; + rt->lower_transport_mask = 1 << RTSP_LOWER_TRANSPORT_TCP; + } + if (*auth) { av_strlcpy(rt->auth, auth, sizeof(rt->auth)); } if (port < 0) - port = RTSP_DEFAULT_PORT; + port = default_port; + + lower_transport_mask = rt->lower_transport_mask; if (!lower_transport_mask) lower_transport_mask = (1 << RTSP_LOWER_TRANSPORT_NB) - 1; @@ -1511,7 +1714,7 @@ redirect: /* Construct the URI used in request; this is similar to s->filename, * but with authentication credentials removed and RTSP specific options * stripped out. */ - ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL, + ff_url_join(rt->control_uri, sizeof(rt->control_uri), proto, NULL, host, port, "%s", path); if (rt->control_transport == RTSP_MODE_TUNNEL) { @@ -1590,7 +1793,8 @@ redirect: } } else { /* open the tcp connection */ - ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL); + ff_url_join(tcpname, sizeof(tcpname), lower_rtsp_proto, NULL, + host, port, NULL); if (ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE, &s->interrupt_callback, NULL) < 0) { err = AVERROR(EIO); @@ -1601,6 +1805,10 @@ redirect: rt->seq = 0; tcp_fd = ffurl_get_file_handle(rt->rtsp_hd); + if (tcp_fd < 0) { + err = tcp_fd; + goto fail; + } if (!getpeername(tcp_fd, (struct sockaddr*) &peer, &peer_len)) { getnameinfo((struct sockaddr*) &peer, peer_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST); @@ -1643,7 +1851,7 @@ redirect: break; } - if (s->iformat && CONFIG_RTSP_DEMUXER) + if (CONFIG_RTSP_DEMUXER && s->iformat) err = ff_rtsp_setup_input_streams(s, reply); else if (CONFIG_RTSP_MUXER) err = ff_rtsp_setup_output_streams(s, host); @@ -1676,6 +1884,7 @@ redirect: ff_rtsp_close_connections(s); if (reply->status_code >=300 && reply->status_code < 400 && s->iformat) { av_strlcpy(s->filename, reply->location, sizeof(s->filename)); + rt->session_id[0] = '\0'; av_log(s, AV_LOG_INFO, "Status %d: Redirecting to %s\n", reply->status_code, s->filename); @@ -1700,7 +1909,7 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, for (;;) { if (ff_check_interrupt(&s->interrupt_callback)) return AVERROR_EXIT; - if (wait_end && wait_end - av_gettime() < 0) + if (wait_end && wait_end - av_gettime_relative() < 0) return AVERROR(EAGAIN); max_p = 0; if (rt->rtsp_hd) { @@ -1776,6 +1985,50 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, } } +static int pick_stream(AVFormatContext *s, RTSPStream **rtsp_st, + const uint8_t *buf, int len) +{ + RTSPState *rt = s->priv_data; + int i; + if (len < 0) + return len; + if (rt->nb_rtsp_streams == 1) { + *rtsp_st = rt->rtsp_streams[0]; + return len; + } + if (len >= 8 && rt->transport == RTSP_TRANSPORT_RTP) { + if (RTP_PT_IS_RTCP(rt->recvbuf[1])) { + int no_ssrc = 0; + for (i = 0; i < rt->nb_rtsp_streams; i++) { + RTPDemuxContext *rtpctx = rt->rtsp_streams[i]->transport_priv; + if (!rtpctx) + continue; + if (rtpctx->ssrc == AV_RB32(&buf[4])) { + *rtsp_st = rt->rtsp_streams[i]; + return len; + } + if (!rtpctx->ssrc) + no_ssrc = 1; + } + if (no_ssrc) { + av_log(s, AV_LOG_WARNING, + "Unable to pick stream for packet - SSRC not known for " + "all streams\n"); + return AVERROR(EAGAIN); + } + } else { + for (i = 0; i < rt->nb_rtsp_streams; i++) { + if ((buf[1] & 0x7f) == rt->rtsp_streams[i]->sdp_payload_type) { + *rtsp_st = rt->rtsp_streams[i]; + return len; + } + } + } + } + av_log(s, AV_LOG_WARNING, "Unable to pick stream for packet\n"); + return AVERROR(EAGAIN); +} + int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) { RTSPState *rt = s->priv_data; @@ -1792,13 +2045,14 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) ret = ff_rdt_parse_packet(rt->cur_transport_priv, pkt, NULL, 0); } else if (rt->transport == RTSP_TRANSPORT_RTP) { ret = ff_rtp_parse_packet(rt->cur_transport_priv, pkt, NULL, 0); - } else if (rt->ts && CONFIG_RTPDEC) { + } else if (CONFIG_RTPDEC && rt->ts) { ret = ff_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf + rt->recvbuf_pos, rt->recvbuf_len - rt->recvbuf_pos); if (ret >= 0) { rt->recvbuf_pos += ret; ret = rt->recvbuf_pos < rt->recvbuf_len; } - } + } else + ret = -1; if (ret == 0) { rt->cur_transport_priv = NULL; return 0; @@ -1808,6 +2062,7 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) rt->cur_transport_priv = NULL; } +redo: if (rt->transport == RTSP_TRANSPORT_RTP) { int i; int64_t first_queue_time = 0; @@ -1823,12 +2078,15 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) first_queue_st = rt->rtsp_streams[i]; } } - if (first_queue_time) + if (first_queue_time) { wait_end = first_queue_time + s->max_delay; + } else { + wait_end = 0; + first_queue_st = NULL; + } } /* read next RTP packet */ - redo: if (!rt->recvbuf) { rt->recvbuf = av_malloc(RECVBUF_SIZE); if (!rt->recvbuf) @@ -1846,11 +2104,23 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: 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) - ff_rtp_check_and_send_back_rr(rtsp_st->transport_priv, len); + ff_rtp_check_and_send_back_rr(rtsp_st->transport_priv, rtsp_st->rtp_handle, NULL, len); + break; + case RTSP_LOWER_TRANSPORT_CUSTOM: + if (first_queue_st && rt->transport == RTSP_TRANSPORT_RTP && + wait_end && wait_end < av_gettime_relative()) + len = AVERROR(EAGAIN); + else + len = ffio_read_partial(s->pb, rt->recvbuf, RECVBUF_SIZE); + len = pick_stream(s, &rtsp_st, rt->recvbuf, len); + if (len > 0 && rtsp_st->transport_priv && rt->transport == RTSP_TRANSPORT_RTP) + ff_rtp_check_and_send_back_rr(rtsp_st->transport_priv, NULL, s->pb, len); break; } if (len == AVERROR(EAGAIN) && first_queue_st && rt->transport == RTSP_TRANSPORT_RTP) { + av_log(s, AV_LOG_WARNING, + "max delay reached. need to consume packet\n"); rtsp_st = first_queue_st; ret = ff_rtp_parse_packet(rtsp_st->transport_priv, pkt, NULL, 0); goto end; @@ -1863,6 +2133,12 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); } else if (rt->transport == RTSP_TRANSPORT_RTP) { ret = ff_rtp_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); + if (rtsp_st->feedback) { + AVIOContext *pb = NULL; + if (rt->lower_transport == RTSP_LOWER_TRANSPORT_CUSTOM) + pb = s->pb; + ff_rtp_send_rtcp_feedback(rtsp_st->transport_priv, rtsp_st->rtp_handle, pb); + } if (ret < 0) { /* Either bad packet, or a RTCP packet. Check if the * first_rtcp_ntp_time field was initialized. */ @@ -1900,7 +2176,7 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } } - } else if (rt->ts && CONFIG_RTPDEC) { + } else if (CONFIG_RTPDEC && rt->ts) { ret = ff_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf, len); if (ret >= 0) { if (ret < len) { @@ -1935,7 +2211,7 @@ static int sdp_probe(AVProbeData *p1) while (p < p_end && *p != '\0') { if (p + sizeof("c=IN IP") - 1 < p_end && av_strstart(p, "c=IN IP", NULL)) - return AVPROBE_SCORE_MAX / 2; + return AVPROBE_SCORE_EXTENSION; while (p < p_end - 1 && *p != '\n') p++; if (++p >= p_end) @@ -1946,6 +2222,17 @@ static int sdp_probe(AVProbeData *p1) return 0; } +static void append_source_addrs(char *buf, int size, const char *name, + int count, struct RTSPSource **addrs) +{ + int i; + if (!count) + return; + av_strlcatf(buf, size, "&%s=%s", name, addrs[0]->addr); + for (i = 1; i < count; i++) + av_strlcatf(buf, size, ",%s", addrs[i]->addr); +} + static int sdp_read_header(AVFormatContext *s) { RTSPState *rt = s->priv_data; @@ -1959,10 +2246,14 @@ static int sdp_read_header(AVFormatContext *s) if (s->max_delay < 0) /* Not set by the caller */ s->max_delay = DEFAULT_REORDERING_DELAY; + if (rt->rtsp_flags & RTSP_FLAG_CUSTOM_IO) + rt->lower_transport = RTSP_LOWER_TRANSPORT_CUSTOM; /* read the whole sdp file */ /* XXX: better loading */ content = av_malloc(SDP_MAX_SIZE); + if (!content) + return AVERROR(ENOMEM); size = avio_read(s->pb, content, SDP_MAX_SIZE - 1); if (size <= 0) { av_free(content); @@ -1979,17 +2270,39 @@ static int sdp_read_header(AVFormatContext *s) char namebuf[50]; rtsp_st = rt->rtsp_streams[i]; - getnameinfo((struct sockaddr*) &rtsp_st->sdp_ip, sizeof(rtsp_st->sdp_ip), - namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); - ff_url_join(url, sizeof(url), "rtp", NULL, - namebuf, rtsp_st->sdp_port, - "?localport=%d&ttl=%d&connect=%d", rtsp_st->sdp_port, - rtsp_st->sdp_ttl, - rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0); - if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, - &s->interrupt_callback, NULL) < 0) { - err = AVERROR_INVALIDDATA; - goto fail; + if (!(rt->rtsp_flags & RTSP_FLAG_CUSTOM_IO)) { + AVDictionary *opts = map_to_opts(rt); + + err = getnameinfo((struct sockaddr*) &rtsp_st->sdp_ip, + sizeof(rtsp_st->sdp_ip), + namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); + if (err) { + av_log(s, AV_LOG_ERROR, "getnameinfo: %s\n", gai_strerror(err)); + err = AVERROR(EIO); + goto fail; + } + ff_url_join(url, sizeof(url), "rtp", NULL, + namebuf, rtsp_st->sdp_port, + "?localport=%d&ttl=%d&connect=%d&write_to_source=%d", + rtsp_st->sdp_port, rtsp_st->sdp_ttl, + rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0, + rt->rtsp_flags & RTSP_FLAG_RTCP_TO_SOURCE ? 1 : 0); + + append_source_addrs(url, sizeof(url), "sources", + rtsp_st->nb_include_source_addrs, + rtsp_st->include_source_addrs); + append_source_addrs(url, sizeof(url), "block", + rtsp_st->nb_exclude_source_addrs, + rtsp_st->exclude_source_addrs); + err = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, + &s->interrupt_callback, &opts); + + av_dict_free(&opts); + + if (err < 0) { + err = AVERROR_INVALIDDATA; + goto fail; + } } if ((err = ff_rtsp_open_transport_ctx(s, rtsp_st))) goto fail; @@ -2037,7 +2350,7 @@ static int rtp_probe(AVProbeData *p) static int rtp_read_header(AVFormatContext *s) { - uint8_t recvbuf[1500]; + uint8_t recvbuf[RTP_MAX_PACKET_LENGTH]; char host[500], sdp[500]; int ret, port; URLContext* in = NULL;