matroskadec: defer parsing of cues element until we seek.
authorAaron Colwell <acolwell@chromium.org>
Sat, 9 Jul 2011 05:48:43 +0000 (07:48 +0200)
committerAnton Khirnov <anton@khirnov.net>
Sun, 10 Jul 2011 16:51:30 +0000 (18:51 +0200)
This decreases startup latency.

Signed-off-by: Ronald S. Bultje <rsbultje@gmail.com>
Signed-off-by: Anton Khirnov <anton@khirnov.net>
libavformat/matroskadec.c

index a1e827f..37ce334 100644 (file)
@@ -244,6 +244,9 @@ typedef struct {
     /* What to skip before effectively reading a packet. */
     int skip_to_keyframe;
     uint64_t skip_to_timecode;
+
+    /* File has a CUES element, but we defer parsing until it is needed. */
+    int cues_parsing_deferred;
 } MatroskaDemuxContext;
 
 typedef struct {
@@ -1110,7 +1113,7 @@ static void matroska_convert_tags(AVFormatContext *s)
     }
 }
 
-static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
+static int matroska_parse_seekhead_entry(MatroskaDemuxContext *matroska, int idx)
 {
     EbmlList *seekhead_list = &matroska->seekhead;
     MatroskaSeekhead *seekhead = seekhead_list->elem;
@@ -1118,34 +1121,25 @@ static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
     int64_t before_pos = avio_tell(matroska->ctx->pb);
     uint32_t saved_id = matroska->current_id;
     MatroskaLevel level;
-    int i;
-
-    // we should not do any seeking in the streaming case
-    if (!matroska->ctx->pb->seekable ||
-        (matroska->ctx->flags & AVFMT_FLAG_IGNIDX))
-        return;
-
-    for (i=0; i<seekhead_list->nb_elem; i++) {
-        int64_t offset = seekhead[i].pos + matroska->segment_start;
+    int64_t offset;
+    int ret = 0;
 
-        if (seekhead[i].pos <= before_pos
-            || seekhead[i].id == MATROSKA_ID_SEEKHEAD
-            || seekhead[i].id == MATROSKA_ID_CLUSTER)
-            continue;
+    if (idx >= seekhead_list->nb_elem
+            || seekhead[idx].id == MATROSKA_ID_SEEKHEAD
+            || seekhead[idx].id == MATROSKA_ID_CLUSTER)
+        return 0;
 
         /* seek */
-        if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) != offset)
-            continue;
-
+    offset = seekhead[idx].pos + matroska->segment_start;
+    if (avio_seek(matroska->ctx->pb, offset, SEEK_SET) == offset) {
         /* We don't want to lose our seekhead level, so we add
          * a dummy. This is a crude hack. */
         if (matroska->num_levels == EBML_MAX_DEPTH) {
             av_log(matroska->ctx, AV_LOG_INFO,
                    "Max EBML element depth (%d) reached, "
                    "cannot parse further.\n", EBML_MAX_DEPTH);
-            break;
-        }
-
+            ret = AVERROR_INVALIDDATA;
+        } else {
         level.start = 0;
         level.length = (uint64_t)-1;
         matroska->levels[matroska->num_levels] = level;
@@ -1161,11 +1155,76 @@ static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
                 break;
         }
     }
-
+    }
     /* seek back */
     avio_seek(matroska->ctx->pb, before_pos, SEEK_SET);
     matroska->level_up = level_up;
     matroska->current_id = saved_id;
+
+    return ret;
+}
+
+static void matroska_execute_seekhead(MatroskaDemuxContext *matroska)
+{
+    EbmlList *seekhead_list = &matroska->seekhead;
+    MatroskaSeekhead *seekhead = seekhead_list->elem;
+    int64_t before_pos = avio_tell(matroska->ctx->pb);
+    int i;
+
+    // we should not do any seeking in the streaming case
+    if (!matroska->ctx->pb->seekable ||
+        (matroska->ctx->flags & AVFMT_FLAG_IGNIDX))
+        return;
+
+    for (i = 0; i < seekhead_list->nb_elem; i++) {
+        if (seekhead[i].pos <= before_pos)
+            continue;
+
+        // defer cues parsing until we actually need cue data.
+        if (seekhead[i].id == MATROSKA_ID_CUES) {
+            matroska->cues_parsing_deferred = 1;
+            continue;
+        }
+
+        if (matroska_parse_seekhead_entry(matroska, i) < 0)
+            break;
+    }
+}
+
+static void matroska_parse_cues(MatroskaDemuxContext *matroska) {
+    EbmlList *seekhead_list = &matroska->seekhead;
+    MatroskaSeekhead *seekhead = seekhead_list->elem;
+    EbmlList *index_list;
+    MatroskaIndex *index;
+    int index_scale = 1;
+    int i, j;
+
+    for (i = 0; i < seekhead_list->nb_elem; i++)
+        if (seekhead[i].id != MATROSKA_ID_CUES)
+            break;
+    assert(i <= seekhead_list->nb_elem);
+
+    matroska_parse_seekhead_entry(matroska, i);
+
+    index_list = &matroska->index;
+    index = index_list->elem;
+    if (index_list->nb_elem
+        && index[0].time > 1E14/matroska->time_scale) {
+        av_log(matroska->ctx, AV_LOG_WARNING, "Working around broken index.\n");
+        index_scale = matroska->time_scale;
+    }
+    for (i = 0; i < index_list->nb_elem; i++) {
+        EbmlList *pos_list = &index[i].pos;
+        MatroskaIndexPos *pos = pos_list->elem;
+        for (j = 0; j < pos_list->nb_elem; j++) {
+            MatroskaTrack *track = matroska_find_track_by_num(matroska, pos[j].track);
+            if (track && track->stream)
+                av_add_index_entry(track->stream,
+                                   pos[j].pos + matroska->segment_start,
+                                   index[i].time/index_scale, 0, 0,
+                                   AVINDEX_KEYFRAME);
+        }
+    }
 }
 
 static int matroska_aac_profile(char *codec_id)
@@ -1197,9 +1256,6 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap)
     EbmlList *chapters_list = &matroska->chapters;
     MatroskaChapter *chapters;
     MatroskaTrack *tracks;
-    EbmlList *index_list;
-    MatroskaIndex *index;
-    int index_scale = 1;
     uint64_t max_start = 0;
     Ebml ebml = { 0 };
     AVStream *st;
@@ -1529,27 +1585,6 @@ static int matroska_read_header(AVFormatContext *s, AVFormatParameters *ap)
             max_start = chapters[i].start;
         }
 
-    index_list = &matroska->index;
-    index = index_list->elem;
-    if (index_list->nb_elem
-        && index[0].time > 100000000000000/matroska->time_scale) {
-        av_log(matroska->ctx, AV_LOG_WARNING, "Working around broken index.\n");
-        index_scale = matroska->time_scale;
-    }
-    for (i=0; i<index_list->nb_elem; i++) {
-        EbmlList *pos_list = &index[i].pos;
-        MatroskaIndexPos *pos = pos_list->elem;
-        for (j=0; j<pos_list->nb_elem; j++) {
-            MatroskaTrack *track = matroska_find_track_by_num(matroska,
-                                                              pos[j].track);
-            if (track && track->stream)
-                av_add_index_entry(track->stream,
-                                   pos[j].pos + matroska->segment_start,
-                                   index[i].time/index_scale, 0, 0,
-                                   AVINDEX_KEYFRAME);
-        }
-    }
-
     matroska_convert_tags(s);
 
     return 0;
@@ -1898,6 +1933,12 @@ static int matroska_read_seek(AVFormatContext *s, int stream_index,
     AVStream *st = s->streams[stream_index];
     int i, index, index_sub, index_min;
 
+    /* Parse the CUES now since we need the index data to seek. */
+    if (matroska->cues_parsing_deferred) {
+        matroska_parse_cues(matroska);
+        matroska->cues_parsing_deferred = 0;
+    }
+
     if (!st->nb_index_entries)
         return 0;
     timestamp = FFMAX(timestamp, st->index_entries[0].timestamp);