[FFmpeg-devel] [PATCH] lavf/mov: add support for sidx fragment indexes

Rodger Combs rodger.combs at gmail.com
Thu Oct 1 02:24:03 CEST 2015


Fixes trac #3842
---
 libavformat/isom.h |   1 +
 libavformat/mov.c  | 192 +++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 179 insertions(+), 14 deletions(-)

diff --git a/libavformat/isom.h b/libavformat/isom.h
index aee9d6e..8f22ea5 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -103,6 +103,7 @@ typedef struct MOVSbgp {
 typedef struct MOVFragmentIndexItem {
     int64_t moof_offset;
     int64_t time;
+    int headers_read;
 } MOVFragmentIndexItem;
 
 typedef struct MOVFragmentIndex {
diff --git a/libavformat/mov.c b/libavformat/mov.c
index da170a6..7f3d3ac 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -3596,7 +3596,96 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
         return AVERROR_EOF;
 
     frag->implicit_offset = offset;
-    st->duration = sc->track_end = dts + sc->time_offset;
+
+    sc->track_end = dts + sc->time_offset;
+    if (st->duration < sc->track_end)
+        st->duration = sc->track_end;
+
+    return 0;
+}
+
+static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+    int64_t offset = avio_tell(pb) + atom.size, pts;
+    uint8_t version;
+    int i, track_id;
+    AVStream *st = NULL;
+    MOVStreamContext *sc;
+    MOVFragmentIndex *index;
+    MOVFragmentIndex **tmp;
+    AVRational timescale;
+
+    version = avio_r8(pb);
+    if (version > 1)
+        return AVERROR_PATCHWELCOME;
+
+    avio_rb24(pb); // flags
+
+    track_id = avio_rb32(pb); // Reference ID
+    for (i = 0; i < c->fc->nb_streams; i++) {
+        if (c->fc->streams[i]->id == track_id) {
+            st = c->fc->streams[i];
+            break;
+        }
+    }
+    if (!st) {
+        av_log(c->fc, AV_LOG_ERROR, "could not find corresponding track id %d\n", track_id);
+        return AVERROR_INVALIDDATA;
+    }
+
+    sc = st->priv_data;
+
+    timescale = av_make_q(1, avio_rb32(pb));
+
+    if (version == 0) {
+        pts = avio_rb32(pb);
+        offset += avio_rb32(pb);
+    } else {
+        pts = avio_rb64(pb);
+        offset += avio_rb64(pb);
+    }
+
+    avio_rb16(pb); // reserved
+
+    index = av_mallocz(sizeof(MOVFragmentIndex));
+    if (!index) {
+        return AVERROR(ENOMEM);
+    }
+
+    index->track_id = track_id;
+
+    index->item_count = avio_rb16(pb);
+    index->items = av_mallocz_array(
+            index->item_count, sizeof(MOVFragmentIndexItem));
+
+    if (!index->items) {
+        av_freep(&index);
+        return AVERROR(ENOMEM);
+    }
+
+    tmp = av_realloc_array(c->fragment_index_data,
+                           c->fragment_index_count + 1,
+                           sizeof(MOVFragmentIndex*));
+    if (!tmp) {
+        av_freep(&index->items);
+        av_freep(&index);
+        return AVERROR(ENOMEM);
+    }
+    c->fragment_index_data = tmp;
+    c->fragment_index_data[c->fragment_index_count++] = index;
+
+    for (i = 0; i < index->item_count; i++) {
+        uint32_t size = avio_rb32(pb) & 0x7FFFFFFF;
+        uint32_t duration = avio_rb32(pb);
+        avio_rb32(pb); // sap_flags
+        index->items[i].moof_offset = offset;
+        index->items[i].time = av_rescale_q(pts, st->time_base, timescale);
+        offset += size;
+        pts += duration;
+    }
+
+    st->duration = sc->track_end = pts;
+
     return 0;
 }
 
@@ -3854,6 +3943,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
 { MKTAG('a','l','a','c'), mov_read_alac }, /* alac specific atom */
 { MKTAG('a','v','c','C'), mov_read_glbl },
 { MKTAG('p','a','s','p'), mov_read_pasp },
+{ MKTAG('s','i','d','x'), mov_read_sidx },
 { MKTAG('s','t','b','l'), mov_read_default },
 { MKTAG('s','t','c','o'), mov_read_stco },
 { MKTAG('s','t','p','s'), mov_read_stps },
@@ -3978,9 +4068,9 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
                 return err;
             }
             if (c->found_moov && c->found_mdat &&
-                ((!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX) ||
+                ((!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX || c->fragment_index_data) ||
                  start_pos + a.size == avio_size(pb))) {
-                if (!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX)
+                if (!pb->seekable || c->fc->flags & AVFMT_FLAG_IGNIDX || c->fragment_index_data)
                     c->next_root_atom = start_pos + a.size;
                 c->atom_depth --;
                 return 0;
@@ -4585,6 +4675,49 @@ static int should_retry(AVIOContext *pb, int error_code) {
     return 1;
 }
 
+static int mov_switch_root(AVFormatContext *s, int64_t target)
+{
+    MOVContext *mov = s->priv_data;
+    int i, j;
+    int already_read = 0;
+    int found = 0;
+
+    if (avio_seek(s->pb, target, SEEK_SET) != target) {
+        av_log(mov->fc, AV_LOG_ERROR, "root atom offset 0x%"PRIx64": partial file\n", target);
+        return AVERROR_INVALIDDATA;
+    }
+
+    for (i = 0; i < mov->fragment_index_count; i++) {
+        MOVFragmentIndex *index = mov->fragment_index_data[i];
+        for (j = 0; j < index->item_count; j++) {
+            MOVFragmentIndexItem *item = &index->items[j];
+            if (found) {
+                mov->next_root_atom = item->moof_offset;
+                break; // Advance to next index in outer loop
+            } else if (item->moof_offset == target) {
+                index->current_item = j;
+                if (item->headers_read)
+                    already_read = 1;
+                item->headers_read = 1;
+                found = 1;
+                mov->next_root_atom = 0;
+            }
+        }
+    }
+
+    if (already_read)
+        return 0;
+
+    mov->found_mdat = 0;
+
+    if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 ||
+        avio_feof(s->pb))
+        return AVERROR_EOF;
+    av_log(s, AV_LOG_TRACE, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb));
+
+    return 1;
+}
+
 static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     MOVContext *mov = s->priv_data;
@@ -4595,19 +4728,11 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
     mov->fc = s;
  retry:
     sample = mov_find_next_sample(s, &st);
-    if (!sample) {
-        mov->found_mdat = 0;
+    if (!sample || (mov->next_root_atom && sample->pos > mov->next_root_atom)) {
         if (!mov->next_root_atom)
             return AVERROR_EOF;
-        if (avio_seek(s->pb, mov->next_root_atom, SEEK_SET) != mov->next_root_atom) {
-            av_log(mov->fc, AV_LOG_ERROR, "next root atom offset 0x%"PRIx64": partial file\n", mov->next_root_atom);
-            return AVERROR_INVALIDDATA;
-        }
-        mov->next_root_atom = 0;
-        if (mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX }) < 0 ||
-            avio_feof(s->pb))
-            return AVERROR_EOF;
-        av_log(s, AV_LOG_TRACE, "read fragments, offset 0x%"PRIx64"\n", avio_tell(s->pb));
+        if ((ret = mov_switch_root(s, mov->next_root_atom)) < 0)
+            return ret;
         goto retry;
     }
     sc = st->priv_data;
@@ -4685,12 +4810,51 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
     return 0;
 }
 
+static int mov_seek_fragment(AVFormatContext *s, AVStream *st, int64_t timestamp)
+{
+    MOVContext *mov = s->priv_data;
+    int64_t offset = -1;
+    MOVFragmentIndex *index = NULL;
+    int i;
+
+    if (!mov->fragment_index_count)
+        return 0;
+
+    for (i = 0; i < mov->fragment_index_count; i++) {
+        if (mov->fragment_index_data[i]->track_id == st->id) {
+            index = mov->fragment_index_data[i];
+            break;
+        }
+    }
+    if (!index)
+        return 0;
+
+    for (i = index->item_count - 1; i >= 0; i--) {
+        if (index->items[i].time <= timestamp) {
+            if (index->items[i].headers_read)
+                return 0;
+
+            offset = index->items[i].moof_offset;
+            break;
+        }
+    }
+
+    if (offset == -1)
+        return 0;
+
+    return mov_switch_root(s, offset);
+}
+
 static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, int flags)
 {
     MOVStreamContext *sc = st->priv_data;
     int sample, time_sample;
     int i;
 
+    int ret = mov_seek_fragment(s, st, timestamp);
+    if (ret < 0)
+        return ret;
+
     sample = av_index_search_timestamp(st, timestamp, flags);
     av_log(s, AV_LOG_TRACE, "stream %d, timestamp %"PRId64", sample %d\n", st->index, timestamp, sample);
     if (sample < 0 && st->nb_index_entries && timestamp < st->index_entries[0].timestamp)
-- 
2.6.0



More information about the ffmpeg-devel mailing list