[FFmpeg-devel] [RFC] RM demuxer should be rewritten

Ronald S. Bultje rsbultje
Mon Feb 16 13:55:15 CET 2009


Hi,

On Mon, Feb 16, 2009 at 2:00 AM, Kostya <kostya.shishkov at gmail.com> wrote:
> Let's see your patches first. It's better to design new demuxer with
> multirate support in mind.

See attached or my current version. I'm not very interested in a
review yet (unless somebody else wants to handle the comments) because
I'm working on other sets of patches now.

1. rmdec-prevent-remaining-len-overflow.patch
prevent overflow of max-integer value in that value. Basically just
have it set in one location and always reset it next (this is the
intended behaviour) instead of doing unnecessary adds/subtractions
2. rmdec-fix-indx.patch
not sure why I did this separate but it parses and skips INDX
3. fix-multirate-rm.patch
parse multirate files (interleaved). I submitted it before and Michael
had some comments, just look in gmane archives
4. rmdec-parse-indx.patch
parse instead of skip the index, useful for seeking in general and (I
think) necessary for seeking in multirate files. some more general
work might be good, for example I'd like to add a feature to
av_seek_frame() to return an error if the demuxer seek func returns an
error (there was discussion about this a while ago), rather than
trying generic functions (which will fail for multirate files anyway).

Not very clean yet, parts might not be finished, so don't see this as
an official patch submission.

Ronald
-------------- next part --------------
Index: ffmpeg-svn/libavformat/rmdec.c
===================================================================
--- ffmpeg-svn.orig/libavformat/rmdec.c	2008-12-30 16:38:06.000000000 -0500
+++ ffmpeg-svn/libavformat/rmdec.c	2008-12-31 16:10:27.000000000 -0500
@@ -46,6 +46,16 @@
     int remaining_len;
     int audio_stream_num; ///< Stream number for audio packets
     int audio_pkt_cnt; ///< Output packet counter
+
+    /* only valid for multirate files, unused otherwise */
+    struct {
+        int64_t last_pts;    ///< timestamp of last RM packet
+        uint64_t cur_offset; ///< file offset of data in next RM packet
+        uint64_t start_off;  ///< offset of first packet in this data chunk
+        unsigned int size;   ///< size of the data chunk
+    } data_chunks[MAX_STREAMS];
+    int n_data_chunks;       ///< number of data chunks, >1 for multirate files
+    int cur_data_chunk;      ///< index of data chunk of last packet
 } RMDemuxContext;
 
 static inline void get_strl(ByteIOContext *pb, char *buf, int buf_size, int len)
@@ -315,7 +325,7 @@
     ByteIOContext *pb = s->pb;
     unsigned int tag;
     int tag_size;
-    unsigned int start_time, duration;
+    unsigned int start_time, duration, next_data_header;
     char buf[128];
     int flags = 0;
 
@@ -404,7 +414,23 @@
     rm->nb_packets = get_be32(pb); /* number of packets */
     if (!rm->nb_packets && (flags & 4))
         rm->nb_packets = 3600 * 25;
-    get_be32(pb); /* next data header */
+
+    do {
+        rm->data_chunks[rm->n_data_chunks].size = FFMAX(0, tag_size - 18);
+        next_data_header = get_be32(pb);
+        rm->data_chunks[rm->n_data_chunks].start_off =
+            rm->data_chunks[rm->n_data_chunks].cur_offset = url_ftell(pb);
+        rm->n_data_chunks++;
+        if (!next_data_header ||
+            url_fseek(pb, next_data_header, SEEK_SET) < 0 ||
+            get_le32(pb) != MKTAG('D','A','T','A'))
+            break;
+        tag_size = get_be32(pb);
+        url_fskip(pb, 6); // flags, n_packets
+    } while (rm->n_data_chunks < MAX_STREAMS);
+    if (rm->n_data_chunks > 1)
+        url_fseek(pb, rm->data_chunks[0].start_off, SEEK_SET);
+
     return 0;
 }
 
@@ -442,6 +468,23 @@
             *timestamp = AV_NOPTS_VALUE;
             *flags= 0;
         }else{
+            int cnt, cur_chunk = -1;
+
+            for (cnt = 0; cnt < rm->n_data_chunks; cnt++)
+                if ((cur_chunk == -1 ||
+                     rm->data_chunks[cnt].last_pts <
+                         rm->data_chunks[cur_chunk].last_pts) &&
+                    !(rm->data_chunks[cnt].size > 0 &&
+                      rm->data_chunks[cnt].cur_offset >=
+                          rm->data_chunks[cnt].start_off +
+                          rm->data_chunks[cnt].size))
+                    cur_chunk = cnt;
+            if (cur_chunk == -1) return -1; //EOF
+            if (cur_chunk != rm->cur_data_chunk) {
+                url_fseek(pb, rm->data_chunks[cur_chunk].cur_offset, SEEK_SET);
+                rm->cur_data_chunk = cur_chunk;
+            }
+
             state= (state<<8) + get_byte(pb);
 
             if(state == MKBETAG('I', 'N', 'D', 'X')){
@@ -453,20 +496,32 @@
                 if(len<0)
                     continue;
                 goto skip;
+            } else if (state == MKBETAG('D','A','T','A')) {
+                av_log(s, AV_LOG_WARNING,
+                       "DATA tag in middle of chunk, file may be broken.\n");
             }
 
-            if(state > (unsigned)0xFFFF || state < 12)
+
+            /* 0xFFFF for first datachunk, 0x1FFFF for all following
+             * datachunks in multirate .rm files. */
+            if(state > (unsigned)0x1FFFF || state < 12)
                 continue;
-            len=state;
-            state= 0xFFFFFFFF;
+            len=state & 0xFFFF;
 
             num = get_be16(pb);
             *timestamp = get_be32(pb);
             res= get_byte(pb); /* reserved */
             *flags = get_byte(pb); /* flags */
 
-
+            if (state >> 16 == 1) {
+                url_fskip(pb, 1);
+                len--;
+            }
             len -= 12;
+
+            state= 0xFFFFFFFF;
+            rm->data_chunks[cur_chunk].last_pts = *timestamp;
+            rm->data_chunks[cur_chunk].cur_offset = url_ftell(pb)+len;
         }
         for(i=0;i<s->nb_streams;i++) {
             st = s->streams[i];
-------------- next part --------------
Index: ffmpeg-svn/libavformat/rmdec.c
===================================================================
--- ffmpeg-svn.orig/libavformat/rmdec.c	2008-12-31 16:10:27.000000000 -0500
+++ ffmpeg-svn/libavformat/rmdec.c	2009-01-02 08:39:20.000000000 -0500
@@ -304,6 +304,51 @@
     return 0;
 }
 
+/** this function assumes that the demuxer has already seeked to the start
+ * of the INDX chunk, and will bail out if not. */
+static int rm_read_index(AVFormatContext *s)
+{
+    ByteIOContext *pb = s->pb;
+    unsigned int size, n_pkts, str_id, next_off, n, pos, pts;
+    AVStream *st;
+
+    do {
+        if (get_le32(pb) != MKTAG('I','N','D','X'))
+            return -1;
+        size     = get_be32(pb);
+        if (size < 20)
+            return -1;
+        url_fskip(pb, 2);
+        n_pkts   = get_be32(pb);
+        str_id   = get_be16(pb);
+        next_off = get_be32(pb);
+        if (size > 20)
+            url_fskip(pb, size - 20);
+        for (n = 0; n < s->nb_streams; n++)
+            if (s->streams[n]->id == str_id) {
+                st = s->streams[n];
+                break;
+            }
+        if (n == s->nb_streams)
+            goto skip;
+
+        for (n = 0; n < n_pkts; n++) {
+            url_fskip(pb, 2);
+            pts = get_be32(pb);
+            pos = get_be32(pb);
+            url_fskip(pb, 4); /* packet no. */
+
+            av_add_index_entry(st, pos, pts, 0, 0, AVINDEX_KEYFRAME);
+        }
+
+skip:
+        if (url_ftell(pb) != next_off &&
+            url_fseek(pb, next_off, SEEK_SET) < 0)
+            return -1;
+    } while (next_off);
+
+    return 0;
+}
 
 static int rm_read_header_old(AVFormatContext *s, AVFormatParameters *ap)
 {
@@ -326,6 +371,7 @@
     unsigned int tag;
     int tag_size;
     unsigned int start_time, duration, next_data_header;
+    unsigned int data_off = 0, indx_off = 0;
     char buf[128];
     int flags = 0;
 
@@ -369,8 +415,8 @@
             get_be32(pb); /* nb packets */
             get_be32(pb); /* duration */
             get_be32(pb); /* preroll */
-            get_be32(pb); /* index offset */
-            get_be32(pb); /* data offset */
+            indx_off = get_be32(pb); /* index offset */
+            data_off = get_be32(pb); /* data offset */
             get_be16(pb); /* nb streams */
             flags = get_be16(pb); /* flags */
             break;
@@ -415,6 +461,13 @@
     if (!rm->nb_packets && (flags & 4))
         rm->nb_packets = 3600 * 25;
 
+    if (!data_off)
+        data_off = url_ftell(pb) - 14;
+    if (indx_off && url_fseek(pb, indx_off, SEEK_SET) >= 0) {
+        rm_read_index(s);
+        url_fseek(pb, data_off + 14, SEEK_SET);
+    }
+
     do {
         rm->data_chunks[rm->n_data_chunks].size = FFMAX(0, tag_size - 18);
         next_data_header = get_be32(pb);
-------------- next part --------------
Index: ffmpeg-svn/libavformat/rmdec.c
===================================================================
--- ffmpeg-svn.orig/libavformat/rmdec.c	2008-12-30 15:53:32.000000000 -0500
+++ ffmpeg-svn/libavformat/rmdec.c	2008-12-30 16:01:59.000000000 -0500
@@ -473,7 +473,7 @@
 skip:
             /* skip packet if unknown number */
             url_fskip(pb, len);
-            rm->remaining_len -= len;
+            rm->remaining_len = 0;
             continue;
         }
         *stream_index= i;
-------------- next part --------------
Index: ffmpeg-svn/libavformat/rmdec.c
===================================================================
--- ffmpeg-svn.orig/libavformat/rmdec.c	2008-12-30 13:44:01.000000000 -0500
+++ ffmpeg-svn/libavformat/rmdec.c	2008-12-30 15:33:15.000000000 -0500
@@ -445,7 +445,11 @@
             state= (state<<8) + get_byte(pb);
 
             if(state == MKBETAG('I', 'N', 'D', 'X')){
-                len = get_be16(pb) - 6;
+                int size, n_pkts;
+                size = get_be32(pb);
+                url_fskip(pb, 2);
+                n_pkts = get_be32(pb);
+                len = size - 14 + n_pkts * 14;
                 if(len<0)
                     continue;
                 goto skip;



More information about the ffmpeg-devel mailing list