rtsp: Split out the RTSP demuxer functions to a separate, new file
authorMartin Storsjö <martin@martin.st>
Fri, 29 Oct 2010 08:43:57 +0000 (08:43 +0000)
committerMartin Storsjö <martin@martin.st>
Fri, 29 Oct 2010 08:43:57 +0000 (08:43 +0000)
Originally committed as revision 25601 to svn://svn.ffmpeg.org/ffmpeg/trunk

libavformat/Makefile
libavformat/rtsp.c
libavformat/rtsp.h
libavformat/rtspdec.c [new file with mode: 0644]

index 40adb53..e62a5ea 100644 (file)
@@ -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
index a604a7a..1f55016 100644 (file)
@@ -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,
 };
index 03edc4e..8997532 100644 (file)
@@ -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 (file)
index 0000000..d2f49a4
--- /dev/null
@@ -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,
+};