X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Frtspdec.c;h=88daa9df1ade9534c34503aa3ed54a0c8e8f2bf9;hb=1430ae44e82074cc4edee508f11431a0d0fcbe12;hp=fd74b08c5370f4916470a5c2a3f620f65f7299b0;hpb=c77549c510370eaaa2e2bb1f15d1a30f29e30950;p=ffmpeg diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c index fd74b08c537..88daa9df1ad 100644 --- a/libavformat/rtspdec.c +++ b/libavformat/rtspdec.c @@ -2,25 +2,27 @@ * 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 "libavutil/opt.h" #include "avformat.h" #include "internal.h" @@ -28,9 +30,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,35 +43,43 @@ 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->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; - 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); } } } @@ -79,6 +87,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; @@ -111,7 +137,7 @@ int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) ret = ff_sdp_parse(s, (const char *)content); av_freep(&content); if (ret < 0) - return AVERROR_INVALIDDATA; + return ret; return 0; } @@ -138,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) { @@ -158,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 '$' */ @@ -174,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 && @@ -204,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; @@ -211,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; @@ -243,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, ",", @@ -270,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); @@ -285,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) { @@ -333,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); @@ -349,16 +388,29 @@ 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, +static const AVOption options[] = { + { "initial_pause", "Don't start playing the stream immediately", offsetof(RTSPState, initial_pause), FF_OPT_TYPE_INT, {.dbl = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +const AVClass rtsp_demuxer_class = { + .class_name = "RTSP demuxer", + .item_name = av_default_item_name, + .option = 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, };