X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtspdec.c;h=785d162b413dc45168742d821a0d16ff7fd1ab65;hb=e4169612a150353cb463ba2c23e88ce1ac254e93;hp=ad13b170aa2284b2aeb8ecad6740dc398cdafc49;hpb=3a1cdcc798fd7cb60ef69d1d010f5d56218c4b3a;p=ffmpeg diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index ad13b170aa2..785d162b413 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -2,25 +2,26 @@ * RTSP demuxer * Copyright (c) 2002 Fabrice Bellard * - * This file is part of FFmpeg. + * This file is part of Libav. * - * FFmpeg is free software; you can redistribute it and/or + * Libav is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * FFmpeg is distributed in the hope that it will be useful, + * Libav is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software + * License along with Libav; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" +#include "libavutil/mathematics.h" #include "avformat.h" #include "internal.h" @@ -28,9 +29,7 @@ #include "os_support.h" #include "rtsp.h" #include "rdt.h" - -//#define DEBUG -//#define DEBUG_RTP_TCP +#include "url.h" static int rtsp_read_play(AVFormatContext *s) { @@ -43,37 +42,45 @@ static int rtsp_read_play(AVFormatContext *s) rt->nb_byes = 0; if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { + if (rt->transport == RTSP_TRANSPORT_RTP) { + for (i = 0; i < rt->nb_rtsp_streams; i++) { + RTSPStream *rtsp_st = rt->rtsp_streams[i]; + RTPDemuxContext *rtpctx = rtsp_st->transport_priv; + if (!rtpctx) + continue; + ff_rtp_reset_packet_queue(rtpctx); + rtpctx->last_rtcp_ntp_time = AV_NOPTS_VALUE; + rtpctx->first_rtcp_ntp_time = AV_NOPTS_VALUE; + rtpctx->base_timestamp = 0; + rtpctx->timestamp = 0; + rtpctx->unwrapped_timestamp = 0; + rtpctx->rtcp_ts_offset = 0; + } + } if (rt->state == RTSP_STATE_PAUSED) { cmd[0] = 0; } else { snprintf(cmd, sizeof(cmd), - "Range: npt=%0.3f-\r\n", - (double)rt->seek_timestamp / AV_TIME_BASE); + "Range: npt=%"PRId64".%03"PRId64"-\r\n", + rt->seek_timestamp / AV_TIME_BASE, + rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); } ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); if (reply->status_code != RTSP_STATUS_OK) { return -1; } - if (rt->transport == RTSP_TRANSPORT_RTP) { + if (rt->transport == RTSP_TRANSPORT_RTP && + reply->range_start != AV_NOPTS_VALUE) { for (i = 0; i < rt->nb_rtsp_streams; i++) { RTSPStream *rtsp_st = rt->rtsp_streams[i]; RTPDemuxContext *rtpctx = rtsp_st->transport_priv; AVStream *st = NULL; - if (!rtpctx) + if (!rtpctx || rtsp_st->stream_index < 0) continue; - if (rtsp_st->stream_index >= 0) - st = s->streams[rtsp_st->stream_index]; - ff_rtp_reset_packet_queue(rtpctx); - if (reply->range_start != AV_NOPTS_VALUE) { - rtpctx->last_rtcp_ntp_time = AV_NOPTS_VALUE; - rtpctx->first_rtcp_ntp_time = AV_NOPTS_VALUE; - rtpctx->base_timestamp = 0; - rtpctx->rtcp_ts_offset = 0; - if (st) - rtpctx->range_start_offset = - av_rescale_q(reply->range_start, AV_TIME_BASE_Q, - st->time_base); - } + st = s->streams[rtsp_st->stream_index]; + rtpctx->range_start_offset = + av_rescale_q(reply->range_start, AV_TIME_BASE_Q, + st->time_base); } } } @@ -81,6 +88,24 @@ static int rtsp_read_play(AVFormatContext *s) return 0; } +/* pause the stream */ +static int rtsp_read_pause(AVFormatContext *s) +{ + RTSPState *rt = s->priv_data; + RTSPMessageHeader reply1, *reply = &reply1; + + if (rt->state != RTSP_STATE_STREAMING) + return 0; + else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { + ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, NULL); + if (reply->status_code != RTSP_STATUS_OK) { + return -1; + } + } + rt->state = RTSP_STATE_PAUSED; + return 0; +} + int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) { RTSPState *rt = s->priv_data; @@ -107,16 +132,13 @@ int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) av_freep(&content); return AVERROR_INVALIDDATA; } - if (reply->content_base[0]) - av_strlcpy(rt->control_uri, reply->content_base, - sizeof(rt->control_uri)); av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", content); /* now we got the SDP description, we parse it */ ret = ff_sdp_parse(s, (const char *)content); av_freep(&content); if (ret < 0) - return AVERROR_INVALIDDATA; + return ret; return 0; } @@ -128,8 +150,7 @@ static int rtsp_probe(AVProbeData *p) return 0; } -static int rtsp_read_header(AVFormatContext *s, - AVFormatParameters *ap) +static int rtsp_read_header(AVFormatContext *s) { RTSPState *rt = s->priv_data; int ret; @@ -143,7 +164,7 @@ static int rtsp_read_header(AVFormatContext *s, return AVERROR(ENOMEM); rt->real_setup = rt->real_setup_cache + s->nb_streams; - if (ap->initial_pause) { + if (rt->initial_pause) { /* do not start immediately */ } else { if (rtsp_read_play(s) < 0) { @@ -163,14 +184,12 @@ int ff_rtsp_tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, int id, len, i, ret; RTSPStream *rtsp_st; -#ifdef DEBUG_RTP_TCP - dprintf(s, "tcp_read_packet:\n"); -#endif + av_dlog(s, "tcp_read_packet:\n"); redo: for (;;) { RTSPMessageHeader reply; - ret = ff_rtsp_read_reply(s, &reply, NULL, 1); + ret = ff_rtsp_read_reply(s, &reply, NULL, 1, NULL); if (ret < 0) return ret; if (ret == 1) /* received '$' */ @@ -179,18 +198,16 @@ redo: if (rt->state != RTSP_STATE_STREAMING) return 0; } - ret = url_read_complete(rt->rtsp_hd, buf, 3); + ret = ffurl_read_complete(rt->rtsp_hd, buf, 3); if (ret != 3) return -1; id = buf[0]; len = AV_RB16(buf + 1); -#ifdef DEBUG_RTP_TCP - dprintf(s, "id=%d len=%d\n", id, len); -#endif + av_dlog(s, "id=%d len=%d\n", id, len); if (len > buf_size || len < 12) goto redo; /* get the data */ - ret = url_read_complete(rt->rtsp_hd, buf, len); + ret = ffurl_read_complete(rt->rtsp_hd, buf, len); if (ret != len) return -1; if (rt->transport == RTSP_TRANSPORT_RDT && @@ -209,6 +226,20 @@ found: *prtsp_st = rtsp_st; return len; } + +static int resetup_tcp(AVFormatContext *s) +{ + RTSPState *rt = s->priv_data; + char host[1024]; + int port; + + av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, + s->filename); + ff_rtsp_undo_setup(s); + return ff_rtsp_make_setup_request(s, host, port, RTSP_LOWER_TRANSPORT_TCP, + rt->real_challenge); +} + static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) { RTSPState *rt = s->priv_data; @@ -216,6 +247,7 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) RTSPMessageHeader reply1, *reply = &reply1; char cmd[1024]; +retry: if (rt->server_type == RTSP_SERVER_REAL) { int i; @@ -248,7 +280,7 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) for (i = 0; i < rt->nb_rtsp_streams; i++) { rule_nr = 0; for (r = 0; r < s->nb_streams; r++) { - if (s->streams[r]->priv_data == rt->rtsp_streams[i]) { + if (s->streams[r]->id == i) { if (s->streams[r]->discard != AVDISCARD_ALL) { if (!first) av_strlcat(rt->last_subscription, ",", @@ -275,12 +307,38 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) } ret = ff_rtsp_fetch_packet(s, pkt); - if (ret < 0) + if (ret < 0) { + if (ret == AVERROR(ETIMEDOUT) && !rt->packets) { + if (rt->lower_transport == RTSP_LOWER_TRANSPORT_UDP && + rt->lower_transport_mask & (1 << RTSP_LOWER_TRANSPORT_TCP)) { + RTSPMessageHeader reply1, *reply = &reply1; + av_log(s, AV_LOG_WARNING, "UDP timeout, retrying with TCP\n"); + if (rtsp_read_pause(s) != 0) + return -1; + // TEARDOWN is required on Real-RTSP, but might make + // other servers close the connection. + if (rt->server_type == RTSP_SERVER_REAL) + ff_rtsp_send_cmd(s, "TEARDOWN", rt->control_uri, NULL, + reply, NULL); + rt->session_id[0] = '\0'; + if (resetup_tcp(s) == 0) { + rt->state = RTSP_STATE_IDLE; + rt->need_subscription = 1; + if (rtsp_read_play(s) != 0) + return -1; + goto retry; + } + } + } return ret; + } + rt->packets++; /* send dummy request to keep TCP connection alive */ if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2) { - if (rt->server_type == RTSP_SERVER_WMS) { + if (rt->server_type == RTSP_SERVER_WMS || + (rt->server_type != RTSP_SERVER_REAL && + rt->get_parameter_supported)) { ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL); } else { ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL); @@ -290,24 +348,6 @@ static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -/* pause the stream */ -static int rtsp_read_pause(AVFormatContext *s) -{ - RTSPState *rt = s->priv_data; - RTSPMessageHeader reply1, *reply = &reply1; - - if (rt->state != RTSP_STATE_STREAMING) - return 0; - else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { - ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, NULL); - if (reply->status_code != RTSP_STATUS_OK) { - return -1; - } - } - rt->state = RTSP_STATE_PAUSED; - return 0; -} - static int rtsp_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { @@ -338,12 +378,6 @@ static int rtsp_read_close(AVFormatContext *s) { RTSPState *rt = s->priv_data; -#if 0 - /* NOTE: it is valid to flush the buffer here */ - if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP) { - url_fclose(&rt->rtsp_gb); - } -#endif ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); ff_rtsp_close_streams(s); @@ -354,16 +388,24 @@ static int rtsp_read_close(AVFormatContext *s) return 0; } -AVInputFormat rtsp_demuxer = { - "rtsp", - NULL_IF_CONFIG_SMALL("RTSP input format"), - sizeof(RTSPState), - rtsp_probe, - rtsp_read_header, - rtsp_read_packet, - rtsp_read_close, - rtsp_read_seek, +const AVClass rtsp_demuxer_class = { + .class_name = "RTSP demuxer", + .item_name = av_default_item_name, + .option = ff_rtsp_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_rtsp_demuxer = { + .name = "rtsp", + .long_name = NULL_IF_CONFIG_SMALL("RTSP input format"), + .priv_data_size = sizeof(RTSPState), + .read_probe = rtsp_probe, + .read_header = rtsp_read_header, + .read_packet = rtsp_read_packet, + .read_close = rtsp_read_close, + .read_seek = rtsp_read_seek, .flags = AVFMT_NOFILE, .read_play = rtsp_read_play, .read_pause = rtsp_read_pause, + .priv_class = &rtsp_demuxer_class, };