X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtsp.c;h=bbe08e686e254fa9efbbfd566ac04a82d56d0686;hb=2f41eaa9c6b5699ed7c3c09b1a39b3322d8e7175;hp=359d91007659355f690f8b3cd1cd952edf76dcb3;hpb=298a587f447fbc9103f66a50b8433aab977afc9b;p=ffmpeg diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 359d9100765..bbe08e686e2 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -27,10 +27,10 @@ #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 @@ -46,6 +46,7 @@ #include "rtpenc_chain.h" #include "url.h" #include "rtpenc.h" +#include "mpegts.h" //#define DEBUG @@ -56,44 +57,53 @@ #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, {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" } + { 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, { (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 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, {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"), 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 }, + { "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"), 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 }, }; @@ -150,14 +160,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; @@ -198,7 +205,14 @@ static int sdp_parse_rtpmap(AVFormatContext *s, * 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). */ + /* 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); @@ -208,11 +222,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); @@ -366,7 +375,9 @@ 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; /* XXX: handle list of formats */ get_word(buf1, sizeof(buf1), &p); /* format list */ @@ -374,6 +385,12 @@ 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 = avformat_new_stream(s, NULL); if (!st) @@ -430,9 +447,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 */ @@ -467,14 +486,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); } } @@ -496,9 +516,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); @@ -553,7 +572,7 @@ 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) + else if (rt->transport == RTSP_TRANSPORT_RTP && CONFIG_RTPDEC) ff_rtp_parse_close(rtsp_st->transport_priv); } rtsp_st->transport_priv = NULL; @@ -584,14 +603,23 @@ void ff_rtsp_close_streams(AVFormatContext *s) if (rt->asf_ctx) { 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) @@ -600,11 +628,15 @@ 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); /* 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, @@ -612,12 +644,11 @@ static int rtsp_open_transport_ctx(AVFormatContext *s, RTSPStream *rtsp_st) else if (CONFIG_RTPDEC) rtsp_st->transport_priv = ff_rtp_parse_open(s, st, rtsp_st->rtp_handle, 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) { ff_rtp_parse_set_dynamic_protocol(rtsp_st->transport_priv, rtsp_st->dynamic_protocol_context, @@ -631,16 +662,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; @@ -685,6 +717,15 @@ static void rtsp_parse_transport(RTSPMessageHeader *reply, const char *p) 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 (!av_strcasecmp(lower_transport, "TCP")) th->lower_transport = RTSP_LOWER_TRANSPORT_TCP; @@ -739,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 != ',') @@ -863,6 +912,9 @@ void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf, } 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)); } } @@ -900,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) */ @@ -938,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)); @@ -949,7 +1011,7 @@ 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; @@ -964,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); @@ -1074,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; @@ -1085,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){ @@ -1113,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"; @@ -1201,8 +1305,9 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, * 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); @@ -1219,7 +1324,7 @@ 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)); @@ -1302,7 +1407,7 @@ int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port, 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; @@ -1315,10 +1420,12 @@ 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); + port, "%s", optbuf); if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, &s->interrupt_callback, NULL) < 0) { err = AVERROR_INVALIDDATA; @@ -1328,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) @@ -1374,6 +1481,9 @@ int ff_rtsp_connect(AVFormatContext *s) if (!ff_network_init()) return AVERROR(EIO); + 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; @@ -1595,6 +1705,7 @@ 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 (ff_check_interrupt(&s->interrupt_callback)) @@ -1612,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 = ffurl_get_file_handle(rtsp_st->rtp_handle); - p[max_p++].events = POLLIN; - p[max_p].fd = ff_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); @@ -1637,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) { @@ -1668,8 +1800,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 + } 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 = -1; if (ret == 0) { rt->cur_transport_priv = NULL; return 0; @@ -1732,7 +1872,7 @@ 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 { + } else if (rt->transport == RTSP_TRANSPORT_RTP) { ret = ff_rtp_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); if (ret < 0) { /* Either bad packet, or a RTCP packet. Check if the @@ -1771,6 +1911,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) @@ -1814,6 +1968,9 @@ static int sdp_read_header(AVFormatContext *s) if (!ff_network_init()) return AVERROR(EIO); + if (s->max_delay < 0) /* Not set by the caller */ + s->max_delay = DEFAULT_REORDERING_DELAY; + /* read the whole sdp file */ /* XXX: better loading */ content = av_malloc(SDP_MAX_SIZE); @@ -1845,7 +2002,7 @@ static int sdp_read_header(AVFormatContext *s) 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; @@ -1877,7 +2034,7 @@ AVInputFormat ff_sdp_demuxer = { .read_header = sdp_read_header, .read_packet = ff_rtsp_fetch_packet, .read_close = sdp_read_close, - .priv_class = &sdp_demuxer_class + .priv_class = &sdp_demuxer_class, }; #endif /* CONFIG_SDP_DEMUXER */ @@ -1896,7 +2053,7 @@ static int rtp_read_header(AVFormatContext *s) 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); @@ -1937,7 +2094,6 @@ static int rtp_read_header(AVFormatContext *s) 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", @@ -1989,13 +2145,13 @@ static const AVClass rtp_demuxer_class = { AVInputFormat ff_rtp_demuxer = { .name = "rtp", - .long_name = NULL_IF_CONFIG_SMALL("RTP input format"), + .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 + .flags = AVFMT_NOFILE, + .priv_class = &rtp_demuxer_class, }; #endif /* CONFIG_RTP_DEMUXER */