[FFmpeg-devel] [PATCH] RTSP-MS 14/15: ASF packet parsing

Ronald S. Bultje rsbultje
Tue Apr 21 14:47:40 CEST 2009


Hi,

(Michael before you start reviewing this patch, this one is not for
your review (yet), it just fixes one of Luca's concerns.)

On Sat, Apr 18, 2009 at 6:08 PM, Michael Niedermayer <michaelni at gmx.at> wrote:
> On Sat, Apr 18, 2009 at 03:28:25PM -0400, Ronald S. Bultje wrote:
> you do not explain why you reach an invalid internal state (ake EOF
> in the middle of the stream) you explain only that because the state
> is not yet invalid the demuxer tries to contiunue and then it becomes
> invalid (=EOF) and that you now want to break out but thats too late,
>
> we first need to discuss WHY it becomes EOF and then either if
> A. this can be avoided (does the demuxer read more than it needs, or
> ? does the RTP code not provide enough data for the ASF demuxer to
> ? return a single frame)
> ? its very unclear from your description if you pass enough data
> ? or not but my guess is you do not.
> B. Change the ASF demuxer to support failing with EAGAIN on unavailable
> ? next packet, and before you attempt it no, you cannot treat url_feof()
> ? there by return EAGAIN that could end in an infinite loop if its real
> ? EOF
>
> either way your eof check is broken and wrong, for example at the point
> where you break out you could already have read 2 bytes from the next
> packet successfully and i am not sure if the demuxer would recover from
> that.

OK, so let's head on to the complex stuff now. Attached is the same
patch as last time, now using url_*_dyn_buf() instead of that
ff_rtp_merge_data_packet() function (thanks Luca A.!), no need to
review because it still has the same issue as per above.

OK, let's answer this question so that we can decide what to do next
to solve this issue. So the invalid state (EOF) is reached because I
feed the demuxer individual packets, which naturally reach
end-of-buffer ("EOF") at the end of each packet rather than only at
the end of a file.

So my ByteIOContext for a file contains:
<Header><data><Header><data><Header><data>[..]<EOF>

my RTP payloads and ByteIOContext for RTP contain:
<Header><data><EOF>
and then the next ByteIOContext again contains:
<Header><data><EOF>
and so on ([..]) until the RTSP stream stops. What you mention in your
last paragraph (reading 2 bytes from the next frame) will thus not
occur (I think) because that packet has simply not been loaded yet.
The ASF packets appear perfectly framed between the RTP payloads.

OK, so on to the potential solutions. Can this invalid EOF state be
avoided? Probably...
- I could, for example, add a check in rtp_asf.c to automatically load
the next packet if the position in the ByteIOContext is exactly the
last byte in the current packet. However, then you get in the
situation of "what to do if we're one byte away from the
end-of-packet"? I haven't tried implementing it because it seems
breakable to me. We'd still have the same problem, it'd only work for
"perfect" streams, no error concealment, any single stream problem
would lead to the issue that we're seeing here now. So I'd rather not
go that way. Do you agree?

- it's essentially true that we're not feeding the demuxer enough data
to read a single frame, because we basically don't know how much data
to feed the demuxer to read a single frame. A video frame could be
spread over multiple ASF packets (RTP payloads), but an audio packet
could contain multiple frames... This doesn't seem very promising to
me.

- what my patch (as you said, probably wrongly) does is to make sure
the demuxer doesn't attempt to read more when we've reached this
"end-of-packet" / "end-of-buffer" / "end-of-payload" state. Probably
not right either, but it was simpler to implement without being
terribly breakable (just my personal impression, based no nothing)...

So although it's doable, I don't think it's terribly promising if
we're looking for a stable way to solve this... Is your solution (B)
more appropriate, or should I change my packet loading to get
something in the direction of (A)? If (B), how would you suggest I do
that?

Ronald
-------------- next part --------------
Index: ffmpeg-svn/libavformat/rtp_asf.c
===================================================================
--- ffmpeg-svn.orig/libavformat/rtp_asf.c	2009-04-17 10:11:28.000000000 -0400
+++ ffmpeg-svn/libavformat/rtp_asf.c	2009-04-21 08:28:44.000000000 -0400
@@ -27,11 +27,45 @@
 
 #include <libavutil/base64.h>
 #include <libavutil/avstring.h>
+#include <libavcodec/internal.h>
+#include <libavutil/intreadwrite.h>
 #include "rtp.h"
 #include "rtp_asf.h"
 #include "rtsp.h"
 #include "asf.h"
 
+static void
+rtp_asf_fix_header(uint8_t *buf, int len)
+{
+    /**
+     * From MSDN 2.2.1.4, we learn that ASF data packets over RTP should
+     * not contain any padding. Unfortunately, the header min/max_pktsize
+     * are not updated (thus making min_pktsize invalid). Here, we "fix"
+     * these faulty min_pktsize values in the ASF file header.
+     */
+    uint8_t *p = buf, *end = buf + len;
+
+    if (len < sizeof(ff_asf_guid) * 2 + 22 ||
+        memcmp(p, ff_asf_header, sizeof(ff_asf_guid))) {
+        return;
+    }
+    p += sizeof(ff_asf_guid) + 14;
+    do {
+        uint64_t len = AV_RL64(p + sizeof(ff_asf_guid));
+        if (memcmp(p, ff_asf_file_header, sizeof(ff_asf_guid))) {
+            p += len;
+            continue;
+        }
+        /* skip most of the file header, to min_pktsize */
+        p += 6 * 8 + 3 * 4 + sizeof(ff_asf_guid) * 2;
+        if (p + 8 <= end && AV_RL32(p) == AV_RL32(p + 4)) {
+            /* and set that to zero */
+            AV_WL32(p, 0);
+        }
+        break;
+    } while (end - p >= sizeof(ff_asf_guid) + 8);
+}
+
 void ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p)
 {
     if (av_strstart(p, "pgmpu:data:application/vnd.ms.wms-hdr.asfv1;base64,", &p)) {
@@ -41,12 +75,14 @@
         char *buf = av_mallocz(len);
         av_base64_decode(buf, p, len);
 
+        rtp_asf_fix_header(buf, len);
         init_put_byte(&pb, buf, len, 0, NULL, NULL, NULL, NULL);
         if (rt->asf_ctx) {
             av_close_input_stream(rt->asf_ctx);
             rt->asf_ctx = NULL;
         }
         av_open_input_stream(&rt->asf_ctx, &pb, "", &asf_demuxer, NULL);
+        rt->asf_pb_pos = url_ftell(&pb);
         av_free(buf);
         rt->asf_ctx->pb = NULL;
     }
@@ -79,12 +115,124 @@
     return 0;
 }
 
+struct PayloadContext {
+    ByteIOContext *pktbuf, pb;
+    char *buf;
+    int pos;
+};
+
+/**< return 0 on packet, no more left, 1 on packet, 1 on partial packet... */
+static int
+asfrtp_parse_packet (AVFormatContext *s, PayloadContext *asf, AVStream *st,
+                     AVPacket *pkt, uint32_t *timestamp,
+                     const uint8_t *buf, int len, int flags)
+{
+    ByteIOContext *pb = &asf->pb;
+    int res, mflags, len_off;
+    RTSPState *rt = s->priv_data;
+
+    if (!rt->asf_ctx)
+        return -1;
+
+    if (len > 0) {
+        int off;
+
+        if (len < 4)
+            return -1;
+
+        init_put_byte(pb, buf, len, 0, NULL, NULL, NULL, NULL);
+        mflags = get_byte(pb);
+        if (mflags & 0x80)
+            flags |= RTP_FLAG_KEY;
+        len_off = get_be24(pb);
+        if (mflags & 0x20) /* relative timestamp */
+            url_fskip(pb, 4);
+        if (mflags & 0x10) /* has duration */
+            url_fskip(pb, 4);
+        if (mflags & 0x8) /* has location ID */
+            url_fskip(pb, 4);
+        off = url_ftell(pb);
+
+        av_freep(&asf->buf);
+        if (!(mflags & 0x40)) {
+            if (!len_off && !asf->pktbuf) {
+                if (!(res = url_open_dyn_buf(&asf->pktbuf)))
+                    return res;
+            } else if (len_off != url_ftell(asf->pktbuf)) {
+                uint8_t *p;
+                url_close_dyn_buf(asf->pktbuf, &p);
+                asf->pktbuf = NULL;
+                av_free(p);
+                return -1;
+            }
+            put_buffer(asf->pktbuf, buf + off, len - off);
+            if (!(flags & RTP_FLAG_MARKER))
+                return -1;
+            asf->pos = url_close_dyn_buf(asf->pktbuf, &asf->buf);
+            asf->pktbuf = NULL;
+        } else if (len_off != len) {
+            ff_log_missing_feature(s,
+                "RTSP-MS packet splitting", 1);
+            return -1;
+        } else {
+            asf->buf = av_malloc(len - off);
+            asf->pos = len - off;
+            memcpy(asf->buf, buf + off, len - off);
+        }
+
+        init_put_byte(pb, asf->buf, asf->pos, 0, NULL, NULL, NULL, NULL);
+        pb->pos += rt->asf_pb_pos;
+        pb->eof_reached = 0;
+        rt->asf_ctx->pb = pb;
+    }
+
+    for (;;) {
+        int i;
+
+        res = av_read_packet(rt->asf_ctx, pkt);
+        rt->asf_pb_pos = url_ftell(pb);
+        if (res != 0)
+            break;
+        for (i = 0; i < s->nb_streams; i++) {
+            if (s->streams[i]->id == rt->asf_ctx->streams[pkt->stream_index]->id) {
+                pkt->stream_index = i;
+                return 1; // FIXME: return 0 if last packet
+            }
+        }
+        av_free_packet(pkt);
+    }
+
+    return res == 1 ? -1 : res;
+}
+
+static PayloadContext *
+asfrtp_new_context (void)
+{
+    return av_mallocz(sizeof(PayloadContext));
+}
+
+static void
+asfrtp_free_context (PayloadContext *asf)
+{
+    if (asf->pktbuf) {
+        uint8_t *p = NULL;
+        url_close_dyn_buf(asf->pktbuf, &p);
+        asf->pktbuf = NULL;
+        av_free(p);
+    }
+    av_freep(&asf->buf);
+    av_free(asf);
+}
+
 #define RTP_ASF_HANDLER(n, s, t) \
 RTPDynamicProtocolHandler ff_ms_rtp_ ## n ## _handler = { \
     s, \
     t, \
     CODEC_ID_NONE, \
     asfrtp_parse_sdp_line, \
+    asfrtp_new_context, \
+    asfrtp_free_context, \
+    asfrtp_parse_packet,   \
 };
 
 RTP_ASF_HANDLER(asf_pfv, "x-asf-pf",  CODEC_TYPE_VIDEO);
Index: ffmpeg-svn/libavformat/rtsp.h
===================================================================
--- ffmpeg-svn.orig/libavformat/rtsp.h	2009-04-17 10:11:28.000000000 -0400
+++ ffmpeg-svn/libavformat/rtsp.h	2009-04-17 10:11:39.000000000 -0400
@@ -248,6 +248,10 @@
     //@{
     /** ASF demuxer context for the embedded ASF stream from WMS servers */
     AVFormatContext *asf_ctx;
+
+    /** cache for position of the asf demuxer, since we load a new
+     * data packet in the bytecontext for each incoming RTSP packet. */
+    uint64_t asf_pb_pos;
     //@}
 } RTSPState;
 
Index: ffmpeg-svn/Changelog
===================================================================
--- ffmpeg-svn.orig/Changelog	2009-04-17 10:11:28.000000000 -0400
+++ ffmpeg-svn/Changelog	2009-04-17 10:11:39.000000000 -0400
@@ -13,6 +13,7 @@
 - RTP packetization of H.263
 - RTP packetization of AMR
 - RTP depacketization of Vorbis
+- RTP depacketization of ASF and RTSP from WMS servers
 
 
 
Index: ffmpeg-svn/libavformat/asfdec.c
===================================================================
--- ffmpeg-svn.orig/libavformat/asfdec.c	2009-04-17 10:11:28.000000000 -0400
+++ ffmpeg-svn/libavformat/asfdec.c	2009-04-18 10:24:43.000000000 -0400
@@ -572,6 +572,8 @@
     if (c != 0x82) {
         if (!url_feof(pb))
             av_log(s, AV_LOG_ERROR, "ff asf bad header %x  at:%"PRId64"\n", c, url_ftell(pb));
+        else
+            return AVERROR_EOF;
     }
     if ((c & 0x8f) == 0x82) {
         if (d || e) {



More information about the ffmpeg-devel mailing list