X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtsp.c;h=f62e370ae5ab8aa8c5c02a1dafd9b1ae5de512ef;hb=5f2c8315b3c1b28da0386fddb118ad6a0ed77a0c;hp=ba9b756879c55e8ca9e4b6cdca55340852a9b4ec;hpb=f811cd2d47ade37a3e66f12260807aa63375ca50;p=ffmpeg diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index ba9b756879c..f62e370ae5a 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 @@ -65,8 +64,7 @@ #define RTSP_FLAG_OPTS(name, longname) \ { 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" }, \ - { "listen", "Wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, 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, { .i64 = (1 << (AVMEDIA_TYPE_DATA+1)) - 1 }, INT_MIN, INT_MAX, DEC, "allowed_media_types" }, \ @@ -74,8 +72,10 @@ { "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 RTSP_REORDERING_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 } +#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, {.i64 = 0}, 0, 1, DEC }, @@ -86,28 +86,42 @@ const AVOption ff_rtsp_options[] = { { "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, {.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 }, - RTSP_REORDERING_OPTS(), + 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"), - RTSP_REORDERING_OPTS(), + COMMON_OPTS(), { NULL }, }; static const AVOption rtp_options[] = { RTSP_FLAG_OPTS("rtp_flags", "RTP flags"), - RTSP_REORDERING_OPTS(), + 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) { @@ -155,7 +169,8 @@ 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); @@ -176,19 +191,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, @@ -200,8 +238,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. */ @@ -209,14 +246,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 @@ -245,10 +281,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); @@ -263,9 +295,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; } @@ -293,8 +323,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) { @@ -302,13 +370,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') @@ -350,14 +419,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))) { @@ -373,12 +444,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 */ @@ -386,8 +468,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 @@ -409,11 +499,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, @@ -453,19 +543,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; @@ -476,6 +567,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; @@ -483,6 +584,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); @@ -507,7 +653,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 @@ -544,13 +690,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; @@ -564,16 +718,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_RTP && CONFIG_RTPDEC) + else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RTP) ff_rtp_parse_close(rtsp_st->transport_priv); } rtsp_st->transport_priv = NULL; @@ -587,16 +741,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); } } @@ -604,7 +768,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); @@ -628,18 +792,19 @@ 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, + 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); @@ -650,12 +815,16 @@ int ff_rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) 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; @@ -803,6 +972,8 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) p++; reply->nb_transports++; + if (reply->nb_transports >= RTSP_MAX_TRANSPORTS) + break; } } @@ -934,7 +1105,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) { @@ -971,13 +1142,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 @@ -989,7 +1159,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') @@ -1022,6 +1192,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'; } @@ -1053,7 +1225,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. */ @@ -1100,11 +1272,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; @@ -1117,6 +1289,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); @@ -1138,7 +1311,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) { @@ -1149,7 +1322,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; } @@ -1157,7 +1330,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, @@ -1182,9 +1355,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) @@ -1276,12 +1449,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; } @@ -1337,7 +1516,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); @@ -1384,30 +1563,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: { @@ -1452,7 +1621,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; } @@ -1467,10 +1636,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); @@ -1497,15 +1669,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; @@ -1525,7 +1705,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) { @@ -1604,7 +1784,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); @@ -1615,6 +1796,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); @@ -1657,7 +1842,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); @@ -1690,6 +1875,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); @@ -1714,7 +1900,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) { @@ -1850,7 +2036,7 @@ 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; @@ -1913,7 +2099,7 @@ redo: break; case RTSP_LOWER_TRANSPORT_CUSTOM: if (first_queue_st && rt->transport == RTSP_TRANSPORT_RTP && - wait_end && wait_end < av_gettime()) + wait_end && wait_end < av_gettime_relative()) len = AVERROR(EAGAIN); else len = ffio_read_partial(s->pb, rt->recvbuf, RECVBUF_SIZE); @@ -1924,6 +2110,8 @@ redo: } 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; @@ -1936,6 +2124,12 @@ redo: 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. */ @@ -1973,7 +2167,7 @@ redo: 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) { @@ -2008,7 +2202,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) @@ -2019,6 +2213,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; @@ -2038,6 +2243,8 @@ static int sdp_read_header(AVFormatContext *s) /* 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); @@ -2055,15 +2262,29 @@ static int sdp_read_header(AVFormatContext *s) rtsp_st = rt->rtsp_streams[i]; if (!(rt->rtsp_flags & RTSP_FLAG_CUSTOM_IO)) { + AVDictionary *opts = map_to_opts(rt); + 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) { + "?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; } @@ -2114,7 +2335,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;