X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtsp.c;h=05058d014ab696607c35c135374d38d1d92abbad;hb=d641ee94b57a581ab0e45bb21c64bb5a32b0502c;hp=cf36814afaa4de6293bb95d05acc3bc1d65b71ec;hpb=bc371aca468c3dd8d1a8cb2b44c99798f232b145;p=ffmpeg diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index cf36814afaa..05058d014ab 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -22,16 +22,18 @@ #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 "libavutil/opt.h" +#include "libavutil/time.h" #include "avformat.h" #include "avio_internal.h" -#include #if HAVE_POLL_H #include #endif -#include #include "internal.h" #include "network.h" #include "os_support.h" @@ -43,9 +45,10 @@ #include "rtpdec_formats.h" #include "rtpenc_chain.h" #include "url.h" +#include "rtpenc.h" +#include "mpegts.h" //#define DEBUG -//#define DEBUG_RTP_TCP /* Timeout values for socket poll, in ms, * and read_packet(), in seconds */ @@ -54,6 +57,56 @@ #define MAX_TIMEOUTS READ_PACKET_TIMEOUT_S * 1000 / POLL_TIMEOUT_MS #define SDP_MAX_SIZE 16384 #define RECVBUF_SIZE 10 * RTP_MAX_PACKET_LENGTH +#define DEFAULT_REORDERING_DELAY 100000 + +#define OFFSET(x) offsetof(RTSPState, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +#define ENC AV_OPT_FLAG_ENCODING_PARAM + +#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" } + +#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" }, \ + { "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 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 } + +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 }, + FF_RTP_FLAG_OPTS(RTSPState, rtp_muxer_flags), + { "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"), + 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(), + { 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" }, + RTSP_MEDIATYPE_OPTS("allowed_media_types", "Media types to accept from the server"), + RTSP_REORDERING_OPTS(), + { NULL }, +}; + +static const AVOption rtp_options[] = { + RTSP_FLAG_OPTS("rtp_flags", "RTP flags"), + RTSP_REORDERING_OPTS(), + { NULL }, +}; static void get_word_until_chars(char *buf, int buf_size, const char *sep, const char **pp) @@ -108,14 +161,11 @@ static void rtsp_parse_range_npt(const char *p, int64_t *start, int64_t *end) 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)); + struct addrinfo hints = { 0 }, *ai = NULL; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(buf, NULL, &hints, &ai)) return -1; @@ -132,8 +182,11 @@ static void init_rtp_handler(RTPDynamicProtocolHandler *handler, return; codec->codec_id = handler->codec_id; rtsp_st->dynamic_handler = handler; - if (handler->open) - rtsp_st->dynamic_protocol_context = handler->open(); + if (handler->alloc) { + rtsp_st->dynamic_protocol_context = handler->alloc(); + if (!rtsp_st->dynamic_protocol_context) + rtsp_st->dynamic_handler = NULL; + } } /* parse the rtpmap description: /[/] */ @@ -147,13 +200,18 @@ 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. */ get_word_sep(buf, sizeof(buf), "/ ", &p); - if (payload_type >= RTP_PT_PRIVATE) { + if (payload_type < RTP_PT_PRIVATE) { + /* We are in a standard case + * (from http://www.iana.org/assignments/rtp-parameters). */ + 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); @@ -163,11 +221,6 @@ static int sdp_parse_rtpmap(AVFormatContext *s, * 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). */ - /* search into AVRtpPayloadTypes[] */ - codec->codec_id = ff_rtp_codec_id(buf, codec->codec_type); } c = avcodec_find_decoder(codec->codec_id); @@ -185,15 +238,11 @@ 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); + avpriv_set_pts_info(st, 32, 1, codec->sample_rate); get_word_sep(buf, sizeof(buf), "/", &p); i = atoi(buf); if (i > 0) codec->channels = i; - // TODO: there is a bug here; if it is a mono stream, and - // less than 22000Hz, faad upconverts to stereo and twice - // the frequency. No problem, but the sample rate is being - // set here by the sdp line. Patch on its way. (rdm) } av_log(s, AV_LOG_DEBUG, "audio samplerate set to: %i\n", codec->sample_rate); @@ -203,11 +252,14 @@ static int sdp_parse_rtpmap(AVFormatContext *s, 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); + avpriv_set_pts_info(st, 32, 1, i); break; 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); return 0; } @@ -282,17 +334,18 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, } 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; case 'm': /* new stream */ s1->skip_media = 0; + codec_type = AVMEDIA_TYPE_UNKNOWN; get_word(st_type, sizeof(st_type), &p); if (!strcmp(st_type, "audio")) { codec_type = AVMEDIA_TYPE_AUDIO; @@ -300,7 +353,8 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, codec_type = AVMEDIA_TYPE_VIDEO; } else if (!strcmp(st_type, "application")) { codec_type = AVMEDIA_TYPE_DATA; - } else { + } + if (codec_type == AVMEDIA_TYPE_UNKNOWN || !(rt->media_type_mask & (1 << codec_type))) { s1->skip_media = 1; return; } @@ -316,7 +370,11 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, get_word(buf1, sizeof(buf1), &p); /* port */ rtsp_st->sdp_port = atoi(buf1); - get_word(buf1, sizeof(buf1), &p); /* protocol (ignored) */ + 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 */ @@ -324,10 +382,17 @@ 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); + } else if (rt->server_type == RTSP_SERVER_WMS && + codec_type == AVMEDIA_TYPE_DATA) { + /* RTX stream, a stream that carries all the other actual + * audio/video streams. Don't expose this to the callers. */ } else { - st = av_new_stream(s, rt->nb_rtsp_streams - 1); + st = avformat_new_stream(s, NULL); if (!st) return; + st->id = rt->nb_rtsp_streams - 1; rtsp_st->stream_index = st->index; st->codec->codec_type = codec_type; if (rtsp_st->sdp_payload_type < RTP_PT_PRIVATE) { @@ -336,11 +401,14 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, 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); + avpriv_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); + if (handler && handler->init) + handler->init(s, st->index, + rtsp_st->dynamic_protocol_context); } } /* put a default control url */ @@ -376,9 +444,11 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, /* 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 = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; - sdp_parse_rtpmap(s, st, rtsp_st, payload_type, p); + if (rtsp_st->stream_index >= 0) { + st = s->streams[rtsp_st->stream_index]; + 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 */ @@ -413,14 +483,15 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, if (rt->server_type == RTSP_SERVER_WMS) ff_wms_parse_sdp_a_line(s, p); if (s->nb_streams > 0) { + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; + if (rt->server_type == RTSP_SERVER_REAL) - ff_real_parse_sdp_a_line(s, s->nb_streams - 1, p); + ff_real_parse_sdp_a_line(s, rtsp_st->stream_index, p); - 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, - s->nb_streams - 1, + rtsp_st->stream_index, rtsp_st->dynamic_protocol_context, buf); } } @@ -428,11 +499,6 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, } } -/** - * Parse the sdp description and allocate the rtp streams and the - * pollfd array used for udp ones. - */ - int ff_sdp_parse(AVFormatContext *s, const char *content) { RTSPState *rt = s->priv_data; @@ -447,9 +513,8 @@ int ff_sdp_parse(AVFormatContext *s, const char *content) * The Vorbis FMTP line can be up to 16KB - see xiph_parse_sdp_line * in rtpdec_xiph.c. */ char buf[16384], *q; - SDPParseState sdp_parse_state, *s1 = &sdp_parse_state; + SDPParseState sdp_parse_state = { { 0 } }, *s1 = &sdp_parse_state; - memset(s1, 0, sizeof(SDPParseState)); p = content; for (;;) { p += strspn(p, SPACE_CHARS); @@ -504,12 +569,12 @@ void ff_rtsp_undo_setup(AVFormatContext *s) 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); + else if (rt->transport == RTSP_TRANSPORT_RTP && CONFIG_RTPDEC) + ff_rtp_parse_close(rtsp_st->transport_priv); } rtsp_st->transport_priv = NULL; if (rtsp_st->rtp_handle) - url_close(rtsp_st->rtp_handle); + ffurl_close(rtsp_st->rtp_handle); rtsp_st->rtp_handle = NULL; } } @@ -526,24 +591,32 @@ void ff_rtsp_close_streams(AVFormatContext *s) rtsp_st = rt->rtsp_streams[i]; if (rtsp_st) { 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); if (rt->asf_ctx) { - av_close_input_stream (rt->asf_ctx); - rt->asf_ctx = NULL; + avformat_close_input(&rt->asf_ctx); } + if (rt->ts && CONFIG_RTPDEC) + ff_mpegts_parse_close(rt->ts); av_free(rt->p); av_free(rt->recvbuf); } -static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) +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) @@ -552,28 +625,32 @@ static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) s->ctx_flags |= AVFMTCTX_NOHEADER; 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); + int ret = ff_rtp_chain_mux_open(&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; + } 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) 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 = 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_RDT && CONFIG_RTPDEC) { + } else if (rt->transport == RTSP_TRANSPORT_RTP && CONFIG_RTPDEC) { if (rtsp_st->dynamic_handler) { - rtp_parse_set_dynamic_protocol(rtsp_st->transport_priv, - rtsp_st->dynamic_protocol_context, - rtsp_st->dynamic_handler); + ff_rtp_parse_set_dynamic_protocol(rtsp_st->transport_priv, + rtsp_st->dynamic_protocol_context, + rtsp_st->dynamic_handler); } } @@ -583,16 +660,17 @@ static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) #if CONFIG_RTSP_DEMUXER || CONFIG_RTSP_MUXER static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp) { - const char *p; + const char *q; + char *p; int v; - p = *pp; - p += strspn(p, SPACE_CHARS); - v = strtol(p, (char **)&p, 10); + q = *pp; + q += strspn(q, SPACE_CHARS); + v = strtol(q, &p, 10); if (*p == '-') { p++; *min_ptr = v; - v = strtol(p, (char **)&p, 10); + v = strtol(p, &p, 10); *max_ptr = v; } else { *min_ptr = v; @@ -622,7 +700,7 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) get_word_sep(transport_protocol, sizeof(transport_protocol), "/", &p); - if (!strcasecmp (transport_protocol, "rtp")) { + if (!av_strcasecmp (transport_protocol, "rtp")) { get_word_sep(profile, sizeof(profile), "/;,", &p); lower_transport[0] = '\0'; /* rtp/avp/ */ @@ -631,14 +709,23 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) ";,", &p); } th->transport = RTSP_TRANSPORT_RTP; - } else if (!strcasecmp (transport_protocol, "x-pn-tng") || - !strcasecmp (transport_protocol, "x-real-rdt")) { + } else if (!av_strcasecmp (transport_protocol, "x-pn-tng") || + !av_strcasecmp (transport_protocol, "x-real-rdt")) { /* x-pn-tng/ */ get_word_sep(lower_transport, sizeof(lower_transport), "/;,", &p); profile[0] = '\0'; th->transport = RTSP_TRANSPORT_RDT; + } else if (!av_strcasecmp(transport_protocol, "raw")) { + get_word_sep(profile, sizeof(profile), "/;,", &p); + lower_transport[0] = '\0'; + /* raw/raw/ */ + if (*p == '/') { + get_word_sep(lower_transport, sizeof(lower_transport), + ";,", &p); + } + th->transport = RTSP_TRANSPORT_RAW; } - if (!strcasecmp(lower_transport, "TCP")) + if (!av_strcasecmp(lower_transport, "TCP")) th->lower_transport = RTSP_LOWER_TRANSPORT_TCP; else th->lower_transport = RTSP_LOWER_TRANSPORT_UDP; @@ -676,8 +763,10 @@ 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 == '=') { @@ -691,6 +780,14 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) get_word_sep(buf, sizeof(buf), ";,", &p); av_strlcpy(th->source, buf, sizeof(th->source)); } + } else if (!strcmp(parameter, "mode")) { + if (*p == '=') { + p++; + get_word_sep(buf, sizeof(buf), ";, ", &p); + if (!strcmp(buf, "record") || + !strcmp(buf, "receive")) + th->mode_record = 1; + } } while (*p != ';' && *p != '\0' && *p != ',') @@ -744,9 +841,9 @@ static void rtsp_parse_rtp_info(RTSPState *rt, const char *p) if (!strcmp(key, "url")) av_strlcpy(url, value, sizeof(url)); else if (!strcmp(key, "seq")) - seq = strtol(value, NULL, 10); + seq = strtoul(value, NULL, 10); else if (!strcmp(key, "rtptime")) - rtptime = strtol(value, NULL, 10); + rtptime = strtoul(value, NULL, 10); if (*p == ',') { handle_rtp_info(rt, url, seq, rtptime); url[0] = '\0'; @@ -808,6 +905,16 @@ void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf, 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; + } else if (av_stristart(p, "x-Accept-Dynamic-Rate:", &p) && rt) { + p += strspn(p, SPACE_CHARS); + rt->accept_dynamic_rate = atoi(p); + } else if (av_stristart(p, "Content-Type:", &p)) { + p += strspn(p, SPACE_CHARS); + av_strlcpy(reply->content_type, p, sizeof(reply->content_type)); } } @@ -818,7 +925,7 @@ 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); @@ -830,7 +937,7 @@ void ff_rtsp_skip_packet(AVFormatContext *s) 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; @@ -845,9 +952,13 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, char buf[4096], buf1[1024], *q; unsigned char ch; const char *p; - int ret, content_length, line_count = 0; + int ret, content_length, line_count = 0, request = 0; unsigned char *content = NULL; +start: + line_count = 0; + request = 0; + content = NULL; memset(reply, 0, sizeof(*reply)); /* parse reply (XXX: use buffers) */ @@ -855,10 +966,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 + ret = ffurl_read_complete(rt->rtsp_hd, &ch, 1); av_dlog(s, "ret=%d c=%02x [%c]\n", ret, ch, ch); -#endif if (ret != 1) return AVERROR_EOF; if (ch == '\n') @@ -885,9 +994,15 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, if (line_count == 0) { /* get reply code */ get_word(buf1, sizeof(buf1), &p); - get_word(buf1, sizeof(buf1), &p); - reply->status_code = atoi(buf1); - av_strlcpy(reply->reason, p, sizeof(reply->reason)); + if (!strncmp(buf1, "RTSP/", 5)) { + get_word(buf1, sizeof(buf1), &p); + reply->status_code = atoi(buf1); + av_strlcpy(reply->reason, p, sizeof(reply->reason)); + } else { + av_strlcpy(reply->reason, buf1, sizeof(reply->reason)); // method + get_word(buf1, sizeof(buf1), &p); // object + request = 1; + } } else { ff_rtsp_parse_line(reply, p, rt, method); av_strlcat(rt->last_reply, p, sizeof(rt->last_reply)); @@ -896,14 +1011,14 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, line_count++; } - if (rt->session_id[0] == '\0' && reply->session_id[0] != '\0') + if (rt->session_id[0] == '\0' && reply->session_id[0] != '\0' && !request) av_strlcpy(rt->session_id, reply->session_id, sizeof(rt->session_id)); content_length = reply->content_length; 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) @@ -911,6 +1026,44 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply, else av_free(content); + if (request) { + char buf[1024]; + char base64buf[AV_BASE64_SIZE(sizeof(buf))]; + const char* ptr = buf; + + if (!strcmp(reply->reason, "OPTIONS")) { + snprintf(buf, sizeof(buf), "RTSP/1.0 200 OK\r\n"); + if (reply->seq) + av_strlcatf(buf, sizeof(buf), "CSeq: %d\r\n", reply->seq); + if (reply->session_id[0]) + av_strlcatf(buf, sizeof(buf), "Session: %s\r\n", + reply->session_id); + } else { + snprintf(buf, sizeof(buf), "RTSP/1.0 501 Not Implemented\r\n"); + } + av_strlcat(buf, "\r\n", sizeof(buf)); + + if (rt->control_transport == RTSP_MODE_TUNNEL) { + av_base64_encode(base64buf, sizeof(base64buf), buf, strlen(buf)); + ptr = base64buf; + } + ffurl_write(rt->rtsp_hd_out, ptr, strlen(ptr)); + + rt->last_cmd_time = av_gettime(); + /* 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. */ + if (content_ptr) + av_freep(content_ptr); + /* If method is set, this is called from ff_rtsp_send_cmd, + * where a reply to exactly this request is awaited. For + * callers from within packet receiving, we just want to + * return to the caller and go back to receiving packets. */ + if (method) + goto start; + return 0; + } + if (rt->seq != reply->seq) { av_log(s, AV_LOG_WARNING, "CSeq %d expected, %d received.\n", rt->seq, reply->seq); @@ -983,14 +1136,14 @@ static int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, 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(); @@ -1021,7 +1174,7 @@ int ff_rtsp_send_cmd_with_content(AVFormatContext *s, { RTSPState *rt = s->priv_data; HTTPAuthType cur_auth_type; - int ret; + int ret, attempts = 0; retry: cur_auth_type = rt->auth_state.auth_type; @@ -1032,9 +1185,11 @@ retry: if ((ret = ff_rtsp_read_reply(s, reply, content_ptr, 0, method) ) < 0) return ret; + attempts++; - if (reply->status_code == 401 && cur_auth_type == HTTP_AUTH_NONE && - rt->auth_state.auth_type != HTTP_AUTH_NONE) + if (reply->status_code == 401 && + (cur_auth_type == HTTP_AUTH_NONE || rt->auth_state.stale) && + rt->auth_state.auth_type != HTTP_AUTH_NONE && attempts < 2) goto retry; if (reply->status_code > 400){ @@ -1048,14 +1203,11 @@ retry: return 0; } -/** - * @return 0 on success, <0 on error, 1 if protocol is unavailable. - */ 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; - int rtx, j, i, err, interleave = 0; + int rtx = 0, j, i, err, interleave = 0, port_off; RTSPStream *rtsp_st; RTSPMessageHeader reply1, *reply = &reply1; char cmd[2048]; @@ -1063,6 +1215,8 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, if (rt->transport == RTSP_TRANSPORT_RDT) trans_pref = "x-pn-tng"; + else if (rt->transport == RTSP_TRANSPORT_RAW) + trans_pref = "RAW/RAW"; else trans_pref = "RTP/AVP"; @@ -1073,10 +1227,17 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, /* XXX: we assume the same server is used for the control of each * RTSP stream */ - for (j = RTSP_RTP_PORT_MIN, i = 0; i < rt->nb_rtsp_streams; ++i) { + /* Choose a random starting offset within the first half of the + * port range, to allow for a number of ports to try even if the offset + * happens to be at the end of the random range. */ + port_off = av_get_random_seed() % ((rt->rtp_port_max - rt->rtp_port_min)/2); + /* even random offset */ + port_off -= port_off & 0x01; + + for (j = rt->rtp_port_min + port_off, 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. @@ -1110,31 +1271,22 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, } /* first try in specified port range */ - if (RTSP_RTP_PORT_MIN != 0) { - while (j <= RTSP_RTP_PORT_MAX) { - 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, URL_RDWR) == 0) - goto rtp_opened; - } + while (j <= rt->rtp_port_max) { + 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)) + goto rtp_opened; } -#if 0 - /* then try on any port */ - if (ffurl_open(&rtsp_st->rtp_handle, "rtp://", URL_RDONLY) < 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_rtp_port(rtsp_st->rtp_handle); + port = ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle); have_port: snprintf(transport, sizeof(transport) - 1, "%s/UDP;", trans_pref); @@ -1149,12 +1301,13 @@ int ff_rtsp_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 && - s->streams[rtsp_st->stream_index]->codec->codec_type == - AVMEDIA_TYPE_DATA) + (rtsp_st->stream_index < 0 || + s->streams[rtsp_st->stream_index]->codec->codec_type == + AVMEDIA_TYPE_DATA)) continue; snprintf(transport, sizeof(transport) - 1, "%s/TCP;", trans_pref); @@ -1171,13 +1324,15 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, "%s/UDP;multicast", trans_pref); } if (s->oformat) { - av_strlcat(transport, ";mode=receive", sizeof(transport)); + av_strlcat(transport, ";mode=record", sizeof(transport)); } else if (rt->server_type == RTSP_SERVER_REAL || rt->server_type == RTSP_SERVER_WMS) av_strlcat(transport, ";mode=play", sizeof(transport)); snprintf(cmd, sizeof(cmd), "Transport: %s\r\n", 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) { char real_res[41], real_csum[9]; ff_rdt_calc_response_and_checksum(real_res, real_csum, @@ -1226,19 +1381,19 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, case RTSP_LOWER_TRANSPORT_UDP: { char url[1024], options[30] = ""; - if (rt->filter_source) + 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, options); + reply->transports[0].server_port_min, "%s", options); } else { ff_url_join(url, sizeof(url), "rtp", NULL, host, - reply->transports[0].server_port_min, options); + 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) { + ff_rtp_set_remote_url(rtsp_st->rtp_handle, url) < 0) { err = AVERROR_INVALIDDATA; goto fail; } @@ -1248,11 +1403,11 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, */ if (!(rt->server_type == RTSP_SERVER_WMS && i > 1) && s->iformat && CONFIG_RTPDEC) - rtp_send_punch_packets(rtsp_st->rtp_handle); + ff_rtp_send_punch_packets(rtsp_st->rtp_handle); break; } case RTSP_LOWER_TRANSPORT_UDP_MULTICAST: { - char url[1024], namebuf[50]; + char url[1024], namebuf[50], optbuf[20] = ""; struct sockaddr_storage addr; int port, ttl; @@ -1265,11 +1420,14 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, port = rtsp_st->sdp_port; ttl = rtsp_st->sdp_ttl; } + if (ttl > 0) + snprintf(optbuf, sizeof(optbuf), "?ttl=%d", ttl); getnameinfo((struct sockaddr*) &addr, sizeof(addr), namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); ff_url_join(url, sizeof(url), "rtp", NULL, namebuf, - port, "?ttl=%d", ttl); - if (ffurl_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { + port, "%s", optbuf); + if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, + &s->interrupt_callback, NULL) < 0) { err = AVERROR_INVALIDDATA; goto fail; } @@ -1277,11 +1435,11 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, } } - if ((err = rtsp_open_transport_ctx(s, rtsp_st))) + if ((err = ff_rtsp_open_transport_ctx(s, rtsp_st))) goto fail; } - if (reply->timeout > 0) + if (rt->nb_rtsp_streams && reply->timeout > 0) rt->timeout = reply->timeout; if (rt->server_type == RTSP_SERVER_REAL) @@ -1297,8 +1455,8 @@ fail: 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; } @@ -1306,7 +1464,6 @@ int ff_rtsp_connect(AVFormatContext *s) { RTSPState *rt = s->priv_data; char host[1024], path[1024], tcpname[1024], cmd[2048], auth[128]; - char *option_list, *option, *filename; int port, err, tcp_fd; RTSPMessageHeader reply1 = {0}, *reply = &reply1; int lower_transport_mask = 0; @@ -1314,10 +1471,29 @@ int ff_rtsp_connect(AVFormatContext *s) struct sockaddr_storage peer; socklen_t peer_len = sizeof(peer); + if (rt->rtp_port_max < rt->rtp_port_min) { + av_log(s, AV_LOG_ERROR, "Invalid UDP port range, max port %d less " + "than min port %d\n", rt->rtp_port_max, + rt->rtp_port_min); + return AVERROR(EINVAL); + } + if (!ff_network_init()) return AVERROR(EIO); -redirect: + + if (s->max_delay < 0) /* Not set by the caller */ + s->max_delay = s->iformat ? DEFAULT_REORDERING_DELAY : 0; + rt->control_transport = RTSP_MODE_PLAIN; + if (rt->lower_transport_mask & (1 << RTSP_LOWER_TRANSPORT_HTTP)) { + rt->lower_transport_mask = 1 << RTSP_LOWER_TRANSPORT_TCP; + rt->control_transport = RTSP_MODE_TUNNEL; + } + /* Only pass through valid flags from here */ + 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), host, sizeof(host), &port, path, sizeof(path), s->filename); @@ -1327,43 +1503,6 @@ redirect: if (port < 0) port = RTSP_DEFAULT_PORT; - /* search for options */ - option_list = strrchr(path, '?'); - if (option_list) { - /* Strip out the RTSP specific options, write out the rest of - * the options back into the same string. */ - filename = option_list; - while (option_list) { - /* move the option pointer */ - option = ++option_list; - option_list = strchr(option_list, '&'); - if (option_list) - *option_list = 0; - - /* handle the options */ - if (!strcmp(option, "udp")) { - lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_UDP); - } else if (!strcmp(option, "multicast")) { - lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_UDP_MULTICAST); - } else if (!strcmp(option, "tcp")) { - lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_TCP); - } 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. */ - int len = strlen(option); - memmove(++filename, option, len); - filename += len; - if (option_list) *filename = '&'; - } - } - *filename = 0; - } - if (!lower_transport_mask) lower_transport_mask = (1 << RTSP_LOWER_TRANSPORT_NB) - 1; @@ -1396,7 +1535,8 @@ redirect: av_get_random_seed(), av_get_random_seed()); /* GET requests */ - if (ffurl_alloc(&rt->rtsp_hd, httpname, URL_RDONLY) < 0) { + if (ffurl_alloc(&rt->rtsp_hd, httpname, AVIO_FLAG_READ, + &s->interrupt_callback) < 0) { err = AVERROR(EIO); goto fail; } @@ -1408,16 +1548,17 @@ redirect: "Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n", sessioncookie); - ff_http_set_headers(rt->rtsp_hd, headers); + av_opt_set(rt->rtsp_hd->priv_data, "headers", headers, 0); /* complete the connection */ - if (ffurl_connect(rt->rtsp_hd)) { + if (ffurl_connect(rt->rtsp_hd, NULL)) { err = AVERROR(EIO); goto fail; } /* POST requests */ - if (ffurl_alloc(&rt->rtsp_hd_out, httpname, URL_WRONLY) < 0 ) { + if (ffurl_alloc(&rt->rtsp_hd_out, httpname, AVIO_FLAG_WRITE, + &s->interrupt_callback) < 0 ) { err = AVERROR(EIO); goto fail; } @@ -1431,8 +1572,8 @@ redirect: "Content-Length: 32767\r\n" "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n", sessioncookie); - ff_http_set_headers(rt->rtsp_hd_out, headers); - ff_http_set_chunked_transfer_encoding(rt->rtsp_hd_out, 0); + av_opt_set(rt->rtsp_hd_out->priv_data, "headers", headers, 0); + av_opt_set(rt->rtsp_hd_out->priv_data, "chunked_post", "0", 0); /* Initialize the authentication state for the POST session. The HTTP * protocol implementation doesn't properly handle multi-pass @@ -1453,14 +1594,15 @@ redirect: ff_http_init_auth_state(rt->rtsp_hd_out, rt->rtsp_hd); /* complete the connection */ - if (ffurl_connect(rt->rtsp_hd_out)) { + if (ffurl_connect(rt->rtsp_hd_out, NULL)) { err = AVERROR(EIO); goto fail; } } else { /* open the tcp connection */ ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL); - if (ffurl_open(&rt->rtsp_hd, tcpname, URL_RDWR) < 0) { + if (ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE, + &s->interrupt_callback, NULL) < 0) { err = AVERROR(EIO); goto fail; } @@ -1468,7 +1610,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); @@ -1480,14 +1622,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" @@ -1504,7 +1646,7 @@ redirect: if (rt->server_type != RTSP_SERVER_REAL && reply->real_challenge[0]) { rt->server_type = RTSP_SERVER_REAL; continue; - } else if (!strncasecmp(reply->server, "WMServer/", 9)) { + } else if (!av_strncasecmp(reply->server, "WMServer/", 9)) { rt->server_type = RTSP_SERVER_WMS; } else if (rt->server_type == RTSP_SERVER_REAL) strcpy(real_challenge, reply->real_challenge); @@ -1563,15 +1705,16 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, int n, i, ret, tcp_fd, timeout_cnt = 0; int max_p = 0; struct pollfd *p = rt->p; + int *fds = NULL, fdsnum, fdsidx; for (;;) { - if (url_interrupt_cb()) + if (ff_check_interrupt(&s->interrupt_callback)) return AVERROR_EXIT; if (wait_end && wait_end - av_gettime() < 0) return AVERROR(EAGAIN); max_p = 0; if (rt->rtsp_hd) { - tcp_fd = url_get_file_handle(rt->rtsp_hd); + tcp_fd = ffurl_get_file_handle(rt->rtsp_hd); p[max_p].fd = tcp_fd; p[max_p++].events = POLLIN; } else { @@ -1580,10 +1723,21 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, for (i = 0; i < rt->nb_rtsp_streams; i++) { rtsp_st = rt->rtsp_streams[i]; if (rtsp_st->rtp_handle) { - p[max_p].fd = url_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; + if (ret = ffurl_get_multi_file_handle(rtsp_st->rtp_handle, + &fds, &fdsnum)) { + av_log(s, AV_LOG_ERROR, "Unable to recover rtp ports\n"); + return ret; + } + if (fdsnum != 2) { + av_log(s, AV_LOG_ERROR, + "Number of fds %d not supported\n", fdsnum); + return AVERROR_INVALIDDATA; + } + for (fdsidx = 0; fdsidx < fdsnum; fdsidx++) { + p[max_p].fd = fds[fdsidx]; + p[max_p++].events = POLLIN; + } + av_free(fds); } } n = poll(p, max_p, POLL_TIMEOUT_MS); @@ -1605,14 +1759,24 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, } #if CONFIG_RTSP_DEMUXER if (tcp_fd != -1 && p[0].revents & POLLIN) { - RTSPMessageHeader reply; - - ret = ff_rtsp_read_reply(s, &reply, NULL, 0, NULL); - if (ret < 0) - return ret; - /* XXX: parse message */ - if (rt->state != RTSP_STATE_STREAMING) - return 0; + if (rt->rtsp_flags & RTSP_FLAG_LISTEN) { + if (rt->state == RTSP_STATE_STREAMING) { + if (!ff_rtsp_parse_streaming_commands(s)) + return AVERROR_EOF; + else + av_log(s, AV_LOG_WARNING, + "Unable to answer to TEARDOWN\n"); + } else + return 0; + } else { + RTSPMessageHeader reply; + ret = ff_rtsp_read_reply(s, &reply, NULL, 0, NULL); + if (ret < 0) + return ret; + /* XXX: parse message */ + if (rt->state != RTSP_STATE_STREAMING) + return 0; + } } #endif } else if (n == 0 && ++timeout_cnt >= MAX_TIMEOUTS) { @@ -1622,6 +1786,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; @@ -1636,8 +1844,16 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) if (rt->cur_transport_priv) { if (rt->transport == RTSP_TRANSPORT_RDT) { 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) { + 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 = rtp_parse_packet(rt->cur_transport_priv, pkt, NULL, 0); + ret = -1; if (ret == 0) { rt->cur_transport_priv = NULL; return 0; @@ -1647,6 +1863,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; @@ -1662,12 +1879,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) @@ -1685,13 +1905,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) - 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()) + 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) { rtsp_st = first_queue_st; - ret = rtp_parse_packet(rtsp_st->transport_priv, pkt, NULL, 0); + ret = ff_rtp_parse_packet(rtsp_st->transport_priv, pkt, NULL, 0); goto end; } if (len < 0) @@ -1700,8 +1930,14 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; if (rt->transport == RTSP_TRANSPORT_RDT) { ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); - } else { - ret = rtp_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. */ @@ -1739,6 +1975,20 @@ int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } } + } else if (rt->ts && CONFIG_RTPDEC) { + ret = ff_mpegts_parse_packet(rt->ts, pkt, rt->recvbuf, len); + if (ret >= 0) { + if (ret < len) { + rt->recvbuf_len = len; + rt->recvbuf_pos = ret; + rt->cur_transport_priv = rt->ts; + return 1; + } else { + ret = 0; + } + } + } else { + return AVERROR_INVALIDDATA; } end: if (ret < 0) @@ -1771,7 +2021,7 @@ static int sdp_probe(AVProbeData *p1) return 0; } -static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) +static int sdp_read_header(AVFormatContext *s) { RTSPState *rt = s->priv_data; RTSPStream *rtsp_st; @@ -1782,6 +2032,11 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) if (!ff_network_init()) return AVERROR(EIO); + 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); @@ -1801,17 +2056,21 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) 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", rtsp_st->sdp_port, - rtsp_st->sdp_ttl); - if (ffurl_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { - err = AVERROR_INVALIDDATA; - goto fail; + if (!(rt->rtsp_flags & RTSP_FLAG_CUSTOM_IO)) { + 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 ((err = rtsp_open_transport_ctx(s, rtsp_st))) + if ((err = ff_rtsp_open_transport_ctx(s, rtsp_st))) goto fail; } return 0; @@ -1828,14 +2087,22 @@ static int sdp_read_close(AVFormatContext *s) return 0; } +static const AVClass sdp_demuxer_class = { + .class_name = "SDP demuxer", + .item_name = av_default_item_name, + .option = sdp_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVInputFormat ff_sdp_demuxer = { - "sdp", - NULL_IF_CONFIG_SMALL("SDP"), - sizeof(RTSPState), - sdp_probe, - sdp_read_header, - ff_rtsp_fetch_packet, - sdp_read_close, + .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, + .priv_class = &sdp_demuxer_class, }; #endif /* CONFIG_SDP_DEMUXER */ @@ -1847,23 +2114,24 @@ static int rtp_probe(AVProbeData *p) return 0; } -static int rtp_read_header(AVFormatContext *s, - AVFormatParameters *ap) +static int rtp_read_header(AVFormatContext *s) { uint8_t recvbuf[1500]; char host[500], sdp[500]; int ret, port; URLContext* in = NULL; int payload_type; - AVCodecContext codec; + AVCodecContext codec = { 0 }; struct sockaddr_storage addr; AVIOContext pb; socklen_t addrlen = sizeof(addr); + RTSPState *rt = s->priv_data; if (!ff_network_init()) return AVERROR(EIO); - ret = ffurl_open(&in, s->filename, URL_RDONLY); + ret = ffurl_open(&in, s->filename, AVIO_FLAG_READ, + &s->interrupt_callback, NULL); if (ret) goto fail; @@ -1884,14 +2152,16 @@ static int rtp_read_header(AVFormatContext *s, continue; } + if (RTP_PT_IS_RTCP(recvbuf[1])) + continue; + payload_type = recvbuf[1] & 0x7f; break; } - getsockname(url_get_file_handle(in), (struct sockaddr*) &addr, &addrlen); - url_close(in); + 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", @@ -1921,26 +2191,35 @@ static int rtp_read_header(AVFormatContext *s, /* sdp_read_header initializes this again */ ff_network_close(); - ret = sdp_read_header(s, ap); + rt->media_type_mask = (1 << (AVMEDIA_TYPE_DATA+1)) - 1; + + ret = sdp_read_header(s); s->pb = NULL; return ret; fail: if (in) - url_close(in); + ffurl_close(in); ff_network_close(); return ret; } +static const AVClass rtp_demuxer_class = { + .class_name = "RTP demuxer", + .item_name = av_default_item_name, + .option = rtp_options, + .version = LIBAVUTIL_VERSION_INT, +}; + AVInputFormat ff_rtp_demuxer = { - "rtp", - NULL_IF_CONFIG_SMALL("RTP input format"), - sizeof(RTSPState), - rtp_probe, - rtp_read_header, - ff_rtsp_fetch_packet, - sdp_read_close, - .flags = AVFMT_NOFILE, + .name = "rtp", + .long_name = NULL_IF_CONFIG_SMALL("RTP input"), + .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, + .priv_class = &rtp_demuxer_class, }; #endif /* CONFIG_RTP_DEMUXER */ -