diff --git a/libavformat/Makefile b/libavformat/Makefile index 40adb537fb..e62a5ea982 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -243,7 +243,7 @@ OBJS-$(CONFIG_RTPDEC) += rdt.o \ rtpdec_svq3.o \ rtpdec_vp8.o \ rtpdec_xiph.o -OBJS-$(CONFIG_RTSP_DEMUXER) += rtsp.o httpauth.o +OBJS-$(CONFIG_RTSP_DEMUXER) += rtsp.o rtspdec.o httpauth.o OBJS-$(CONFIG_RTSP_MUXER) += rtsp.o rtspenc.o httpauth.o \ rtpenc_chain.o OBJS-$(CONFIG_SAP_DEMUXER) += sapdec.o diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index a604a7ae30..1f55016366 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -411,7 +411,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, } } -static int sdp_parse(AVFormatContext *s, const char *content) +int ff_sdp_parse(AVFormatContext *s, const char *content) { const char *p; int letter; @@ -1177,98 +1177,6 @@ fail: return err; } -static int rtsp_read_play(AVFormatContext *s) -{ - RTSPState *rt = s->priv_data; - RTSPMessageHeader reply1, *reply = &reply1; - int i; - char cmd[1024]; - - av_log(s, AV_LOG_DEBUG, "hello state=%d\n", rt->state); - rt->nb_byes = 0; - - if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { - 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); - } - 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) { - 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) - 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); - } - } - } - } - rt->state = RTSP_STATE_STREAMING; - return 0; -} - -#if CONFIG_RTSP_DEMUXER -static int rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) -{ - RTSPState *rt = s->priv_data; - char cmd[1024]; - unsigned char *content = NULL; - int ret; - - /* describe the stream */ - snprintf(cmd, sizeof(cmd), - "Accept: application/sdp\r\n"); - if (rt->server_type == RTSP_SERVER_REAL) { - /** - * The Require: attribute is needed for proper streaming from - * Realmedia servers. - */ - av_strlcat(cmd, - "Require: com.real.retain-entity-for-setup\r\n", - sizeof(cmd)); - } - ff_rtsp_send_cmd(s, "DESCRIBE", rt->control_uri, cmd, reply, &content); - if (!content) - return AVERROR_INVALIDDATA; - if (reply->status_code != RTSP_STATUS_OK) { - av_freep(&content); - return AVERROR_INVALIDDATA; - } - - av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", content); - /* now we got the SDP description, we parse it */ - ret = sdp_parse(s, (const char *)content); - av_freep(&content); - if (ret < 0) - return AVERROR_INVALIDDATA; - - return 0; -} -#else /* !CONFIG_RTSP_DEMUXER */ -/* A declaration of this function is needed so that the function is - * defined when parsing the call to it, even if dead code elimination - * will remove the call later. - */ -static int rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply); -#endif /* !CONFIG_RTSP_DEMUXER */ - void ff_rtsp_close_connections(AVFormatContext *s) { RTSPState *rt = s->priv_data; @@ -1485,7 +1393,7 @@ redirect: } if (s->iformat && CONFIG_RTSP_DEMUXER) - err = rtsp_setup_input_streams(s, reply); + err = ff_rtsp_setup_input_streams(s, reply); else if (CONFIG_RTSP_MUXER) err = ff_rtsp_setup_output_streams(s, host); if (err) @@ -1597,10 +1505,7 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, } } -static int tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, - uint8_t *buf, int buf_size); - -static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) +int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) { RTSPState *rt = s->priv_data; int ret, len; @@ -1653,7 +1558,7 @@ static int rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt) default: #if CONFIG_RTSP_DEMUXER case RTSP_LOWER_TRANSPORT_TCP: - len = tcp_read_packet(s, &rtsp_st, rt->recvbuf, RECVBUF_SIZE); + len = ff_rtsp_tcp_read_packet(s, &rtsp_st, rt->recvbuf, RECVBUF_SIZE); break; #endif case RTSP_LOWER_TRANSPORT_UDP: @@ -1716,255 +1621,6 @@ end: } #endif /* CONFIG_RTPDEC */ -#if CONFIG_RTSP_DEMUXER -static int rtsp_probe(AVProbeData *p) -{ - if (av_strstart(p->filename, "rtsp:", NULL)) - return AVPROBE_SCORE_MAX; - return 0; -} - -static int rtsp_read_header(AVFormatContext *s, - AVFormatParameters *ap) -{ - RTSPState *rt = s->priv_data; - int ret; - - ret = ff_rtsp_connect(s); - if (ret) - return ret; - - rt->real_setup_cache = av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache)); - if (!rt->real_setup_cache) - return AVERROR(ENOMEM); - rt->real_setup = rt->real_setup_cache + s->nb_streams * sizeof(*rt->real_setup); - - if (ap->initial_pause) { - /* do not start immediately */ - } else { - if (rtsp_read_play(s) < 0) { - ff_rtsp_close_streams(s); - ff_rtsp_close_connections(s); - return AVERROR_INVALIDDATA; - } - } - - return 0; -} - -static int tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, - uint8_t *buf, int buf_size) -{ - RTSPState *rt = s->priv_data; - int id, len, i, ret; - RTSPStream *rtsp_st; - -#ifdef DEBUG_RTP_TCP - dprintf(s, "tcp_read_packet:\n"); -#endif -redo: - for (;;) { - RTSPMessageHeader reply; - - ret = ff_rtsp_read_reply(s, &reply, NULL, 1); - if (ret < 0) - return ret; - if (ret == 1) /* received '$' */ - break; - /* XXX: parse message */ - if (rt->state != RTSP_STATE_STREAMING) - return 0; - } - ret = url_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 - if (len > buf_size || len < 12) - goto redo; - /* get the data */ - ret = url_read_complete(rt->rtsp_hd, buf, len); - if (ret != len) - return -1; - if (rt->transport == RTSP_TRANSPORT_RDT && - ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL) < 0) - return -1; - - /* find the matching stream */ - for (i = 0; i < rt->nb_rtsp_streams; i++) { - rtsp_st = rt->rtsp_streams[i]; - if (id >= rtsp_st->interleaved_min && - id <= rtsp_st->interleaved_max) - goto found; - } - goto redo; -found: - *prtsp_st = rtsp_st; - return len; -} -static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) -{ - RTSPState *rt = s->priv_data; - int ret; - RTSPMessageHeader reply1, *reply = &reply1; - char cmd[1024]; - - if (rt->server_type == RTSP_SERVER_REAL) { - int i; - - for (i = 0; i < s->nb_streams; i++) - rt->real_setup[i] = s->streams[i]->discard; - - if (!rt->need_subscription) { - if (memcmp (rt->real_setup, rt->real_setup_cache, - sizeof(enum AVDiscard) * s->nb_streams)) { - snprintf(cmd, sizeof(cmd), - "Unsubscribe: %s\r\n", - rt->last_subscription); - ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, - cmd, reply, NULL); - if (reply->status_code != RTSP_STATUS_OK) - return AVERROR_INVALIDDATA; - rt->need_subscription = 1; - } - } - - if (rt->need_subscription) { - int r, rule_nr, first = 1; - - memcpy(rt->real_setup_cache, rt->real_setup, - sizeof(enum AVDiscard) * s->nb_streams); - rt->last_subscription[0] = 0; - - snprintf(cmd, sizeof(cmd), - "Subscribe: "); - 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]->discard != AVDISCARD_ALL) { - if (!first) - av_strlcat(rt->last_subscription, ",", - sizeof(rt->last_subscription)); - ff_rdt_subscribe_rule( - rt->last_subscription, - sizeof(rt->last_subscription), i, rule_nr); - first = 0; - } - rule_nr++; - } - } - } - av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription); - ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, - cmd, reply, NULL); - if (reply->status_code != RTSP_STATUS_OK) - return AVERROR_INVALIDDATA; - rt->need_subscription = 0; - - if (rt->state == RTSP_STATE_STREAMING) - rtsp_read_play (s); - } - } - - ret = rtsp_fetch_packet(s, pkt); - if (ret < 0) - return ret; - - /* 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) { - ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL); - } else { - ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL); - } - } - - 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) -{ - RTSPState *rt = s->priv_data; - - rt->seek_timestamp = av_rescale_q(timestamp, - s->streams[stream_index]->time_base, - AV_TIME_BASE_Q); - switch(rt->state) { - default: - case RTSP_STATE_IDLE: - break; - case RTSP_STATE_STREAMING: - if (rtsp_read_pause(s) != 0) - return -1; - rt->state = RTSP_STATE_SEEKING; - if (rtsp_read_play(s) != 0) - return -1; - break; - case RTSP_STATE_PAUSED: - rt->state = RTSP_STATE_IDLE; - break; - } - return 0; -} - -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); - ff_rtsp_close_connections(s); - ff_network_close(); - rt->real_setup = NULL; - av_freep(&rt->real_setup_cache); - 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, - .flags = AVFMT_NOFILE, - .read_play = rtsp_read_play, - .read_pause = rtsp_read_pause, -}; -#endif /* CONFIG_RTSP_DEMUXER */ - #if CONFIG_SDP_DEMUXER static int sdp_probe(AVProbeData *p1) { @@ -2006,7 +1662,7 @@ static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) } content[size] ='\0'; - sdp_parse(s, content); + ff_sdp_parse(s, content); av_free(content); /* open each RTP stream */ @@ -2047,7 +1703,7 @@ AVInputFormat sdp_demuxer = { sizeof(RTSPState), sdp_probe, sdp_read_header, - rtsp_fetch_packet, + ff_rtsp_fetch_packet, sdp_read_close, }; #endif /* CONFIG_SDP_DEMUXER */ @@ -2151,7 +1807,7 @@ AVInputFormat rtp_demuxer = { sizeof(RTSPState), rtp_probe, rtp_read_header, - rtsp_fetch_packet, + ff_rtsp_fetch_packet, sdp_read_close, .flags = AVFMT_NOFILE, }; diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 03edc4e5bc..8997532cc2 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -468,10 +468,34 @@ void ff_rtsp_close_streams(AVFormatContext *s); */ void ff_rtsp_close_connections(AVFormatContext *rt); +/** + * Get the description of the stream and set up the RTSPStream child + * objects. + */ +int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply); + /** * Announce the stream to the server and set up the RTSPStream child * objects for each media stream. */ int ff_rtsp_setup_output_streams(AVFormatContext *s, const char *addr); +/** + * Parse a SDP description of streams by populating an RTSPState struct + * within the AVFormatContext. + */ +int ff_sdp_parse(AVFormatContext *s, const char *content); + +/** + * Receive one RTP packet from an TCP interleaved RTSP stream. + */ +int ff_rtsp_tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, + uint8_t *buf, int buf_size); + +/** + * Receive one packet from the RTSPStreams set up in the AVFormatContext + * (which should contain a RTSPState struct as priv_data). + */ +int ff_rtsp_fetch_packet(AVFormatContext *s, AVPacket *pkt); + #endif /* AVFORMAT_RTSP_H */ diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c new file mode 100644 index 0000000000..d2f49a41fa --- /dev/null +++ b/libavformat/rtspdec.c @@ -0,0 +1,364 @@ +/* + * RTSP demuxer + * Copyright (c) 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg 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, + * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" + +#include "internal.h" +#include "network.h" +#include "os_support.h" +#include "rtsp.h" +#include "rdt.h" + +//#define DEBUG +//#define DEBUG_RTP_TCP + +static int rtsp_read_play(AVFormatContext *s) +{ + RTSPState *rt = s->priv_data; + RTSPMessageHeader reply1, *reply = &reply1; + int i; + char cmd[1024]; + + av_log(s, AV_LOG_DEBUG, "hello state=%d\n", rt->state); + rt->nb_byes = 0; + + if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { + 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); + } + 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) { + 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) + 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); + } + } + } + } + rt->state = RTSP_STATE_STREAMING; + return 0; +} + +int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) +{ + RTSPState *rt = s->priv_data; + char cmd[1024]; + unsigned char *content = NULL; + int ret; + + /* describe the stream */ + snprintf(cmd, sizeof(cmd), + "Accept: application/sdp\r\n"); + if (rt->server_type == RTSP_SERVER_REAL) { + /** + * The Require: attribute is needed for proper streaming from + * Realmedia servers. + */ + av_strlcat(cmd, + "Require: com.real.retain-entity-for-setup\r\n", + sizeof(cmd)); + } + ff_rtsp_send_cmd(s, "DESCRIBE", rt->control_uri, cmd, reply, &content); + if (!content) + return AVERROR_INVALIDDATA; + if (reply->status_code != RTSP_STATUS_OK) { + av_freep(&content); + return AVERROR_INVALIDDATA; + } + + 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 0; +} + +static int rtsp_probe(AVProbeData *p) +{ + if (av_strstart(p->filename, "rtsp:", NULL)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int rtsp_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + RTSPState *rt = s->priv_data; + int ret; + + ret = ff_rtsp_connect(s); + if (ret) + return ret; + + rt->real_setup_cache = av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache)); + if (!rt->real_setup_cache) + return AVERROR(ENOMEM); + rt->real_setup = rt->real_setup_cache + s->nb_streams * sizeof(*rt->real_setup); + + if (ap->initial_pause) { + /* do not start immediately */ + } else { + if (rtsp_read_play(s) < 0) { + ff_rtsp_close_streams(s); + ff_rtsp_close_connections(s); + return AVERROR_INVALIDDATA; + } + } + + return 0; +} + +int ff_rtsp_tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, + uint8_t *buf, int buf_size) +{ + RTSPState *rt = s->priv_data; + int id, len, i, ret; + RTSPStream *rtsp_st; + +#ifdef DEBUG_RTP_TCP + dprintf(s, "tcp_read_packet:\n"); +#endif +redo: + for (;;) { + RTSPMessageHeader reply; + + ret = ff_rtsp_read_reply(s, &reply, NULL, 1); + if (ret < 0) + return ret; + if (ret == 1) /* received '$' */ + break; + /* XXX: parse message */ + if (rt->state != RTSP_STATE_STREAMING) + return 0; + } + ret = url_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 + if (len > buf_size || len < 12) + goto redo; + /* get the data */ + ret = url_read_complete(rt->rtsp_hd, buf, len); + if (ret != len) + return -1; + if (rt->transport == RTSP_TRANSPORT_RDT && + ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL) < 0) + return -1; + + /* find the matching stream */ + for (i = 0; i < rt->nb_rtsp_streams; i++) { + rtsp_st = rt->rtsp_streams[i]; + if (id >= rtsp_st->interleaved_min && + id <= rtsp_st->interleaved_max) + goto found; + } + goto redo; +found: + *prtsp_st = rtsp_st; + return len; +} +static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + RTSPState *rt = s->priv_data; + int ret; + RTSPMessageHeader reply1, *reply = &reply1; + char cmd[1024]; + + if (rt->server_type == RTSP_SERVER_REAL) { + int i; + + for (i = 0; i < s->nb_streams; i++) + rt->real_setup[i] = s->streams[i]->discard; + + if (!rt->need_subscription) { + if (memcmp (rt->real_setup, rt->real_setup_cache, + sizeof(enum AVDiscard) * s->nb_streams)) { + snprintf(cmd, sizeof(cmd), + "Unsubscribe: %s\r\n", + rt->last_subscription); + ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, + cmd, reply, NULL); + if (reply->status_code != RTSP_STATUS_OK) + return AVERROR_INVALIDDATA; + rt->need_subscription = 1; + } + } + + if (rt->need_subscription) { + int r, rule_nr, first = 1; + + memcpy(rt->real_setup_cache, rt->real_setup, + sizeof(enum AVDiscard) * s->nb_streams); + rt->last_subscription[0] = 0; + + snprintf(cmd, sizeof(cmd), + "Subscribe: "); + 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]->discard != AVDISCARD_ALL) { + if (!first) + av_strlcat(rt->last_subscription, ",", + sizeof(rt->last_subscription)); + ff_rdt_subscribe_rule( + rt->last_subscription, + sizeof(rt->last_subscription), i, rule_nr); + first = 0; + } + rule_nr++; + } + } + } + av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription); + ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, + cmd, reply, NULL); + if (reply->status_code != RTSP_STATUS_OK) + return AVERROR_INVALIDDATA; + rt->need_subscription = 0; + + if (rt->state == RTSP_STATE_STREAMING) + rtsp_read_play (s); + } + } + + ret = ff_rtsp_fetch_packet(s, pkt); + if (ret < 0) + return ret; + + /* 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) { + ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL); + } else { + ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL); + } + } + + 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) +{ + RTSPState *rt = s->priv_data; + + rt->seek_timestamp = av_rescale_q(timestamp, + s->streams[stream_index]->time_base, + AV_TIME_BASE_Q); + switch(rt->state) { + default: + case RTSP_STATE_IDLE: + break; + case RTSP_STATE_STREAMING: + if (rtsp_read_pause(s) != 0) + return -1; + rt->state = RTSP_STATE_SEEKING; + if (rtsp_read_play(s) != 0) + return -1; + break; + case RTSP_STATE_PAUSED: + rt->state = RTSP_STATE_IDLE; + break; + } + return 0; +} + +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); + ff_rtsp_close_connections(s); + ff_network_close(); + rt->real_setup = NULL; + av_freep(&rt->real_setup_cache); + 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, + .flags = AVFMT_NOFILE, + .read_play = rtsp_read_play, + .read_pause = rtsp_read_pause, +};