[FFmpeg-devel] [PATCH] lavf/mov.c: Refine edit list start seek, based on PTS computed from CTTS.

Sasi Inguva isasi at google.com
Wed Nov 1 01:42:56 EET 2017


Partially fixes t/6699.
---
 libavformat/mov.c  | 125 +++++++++++++++++++++++++++++++++++------------------
 tests/fate/mov.mak |   8 ++++
 2 files changed, 90 insertions(+), 43 deletions(-)

diff --git a/libavformat/mov.c b/libavformat/mov.c
index 60f0228e2d..a295445651 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -3014,34 +3014,95 @@ static int get_edit_list_entry(MOVContext *mov,
 }
 
 /**
- * Find the closest previous frame to the timestamp, in e_old index
+ * Find the closest previous frame to the timestamp_pts, in e_old index
  * entries. Searching for just any frame / just key frames can be controlled by
  * last argument 'flag'.
- * Returns the index of the entry in st->index_entries if successful,
- * else returns -1.
+ * Here the timestamp_pts is considered to be a presentation timestamp and
+ * the timestamp of index entries are considered to be decoding timestamps.
+ *
+ * Returns 0 if successful in finding a frame, else returns -1.
+ * Places the found index corresponding output arg.
+ *
+ * If ctts_old is not NULL, then refines the searched entry by searching
+ * backwards from the found timestamp, to find the frame with correct PTS.
+ *
+ * Places the found ctts_index and ctts_sample in corresponding output args.
  */
-static int64_t find_prev_closest_index(AVStream *st,
-                                       AVIndexEntry *e_old,
-                                       int nb_old,
-                                       int64_t timestamp,
-                                       int flag)
+static int find_prev_closest_index(AVStream *st,
+                                   AVIndexEntry *e_old,
+                                   int nb_old,
+                                   MOVStts* ctts_data,
+                                   int64_t ctts_count,
+                                   int64_t timestamp_pts,
+                                   int flag,
+                                   int64_t* index,
+                                   int64_t* ctts_index,
+                                   int64_t* ctts_sample)
 {
+    MOVStreamContext *msc = st->priv_data;
     AVIndexEntry *e_keep = st->index_entries;
     int nb_keep = st->nb_index_entries;
-    int64_t found = -1;
     int64_t i = 0;
+    int64_t index_ctts_count;
+
+    av_assert0(index);
+
+    // If dts_shift > 0, then all the index timestamps will have to be offset by
+    // at least dts_shift amount to obtain PTS.
+    // Hence we decrement the searched timestamp_pts by dts_shift to find the closest index element.
+    if (msc->dts_shift > 0) {
+        timestamp_pts -= msc->dts_shift;
+    }
 
     st->index_entries = e_old;
     st->nb_index_entries = nb_old;
-    found = av_index_search_timestamp(st, timestamp, flag | AVSEEK_FLAG_BACKWARD);
+    *index = av_index_search_timestamp(st, timestamp_pts, flag | AVSEEK_FLAG_BACKWARD);
 
     // Keep going backwards in the index entries until the timestamp is the same.
-    if (found >= 0) {
-        for (i = found; i > 0 && e_old[i].timestamp == e_old[i - 1].timestamp;
+    if (*index >= 0) {
+        for (i = *index; i > 0 && e_old[i].timestamp == e_old[i - 1].timestamp;
              i--) {
             if ((flag & AVSEEK_FLAG_ANY) ||
                 (e_old[i - 1].flags & AVINDEX_KEYFRAME)) {
-                found = i - 1;
+                *index = i - 1;
+            }
+        }
+    }
+
+    // If we have CTTS then refine the search, by searching backwards over PTS
+    // computed by adding corresponding CTTS durations to index timestamps.
+    if (ctts_data && *index >= 0) {
+        av_assert0(ctts_index);
+        av_assert0(ctts_sample);
+        // Find out the ctts_index for the found frame.
+        *ctts_index = 0;
+        *ctts_sample = 0;
+        for (index_ctts_count = 0; index_ctts_count < *index; index_ctts_count++) {
+            if (*ctts_index < ctts_count) {
+                (*ctts_sample)++;
+                if (ctts_data[*ctts_index].count == *ctts_sample) {
+                    (*ctts_index)++;
+                    *ctts_sample = 0;
+                }
+            }
+        }
+
+        while (*index >= 0 && (*ctts_index) >= 0) {
+            // Find a "key frame" with PTS <= timestamp_pts (So that we can decode B-frames correctly).
+            // No need to add dts_shift to the timestamp here becase timestamp_pts has already been
+            // compensated by dts_shift above.
+            if ((e_old[*index].timestamp + ctts_data[*ctts_index].duration) <= timestamp_pts &&
+                (e_old[*index].flags & AVINDEX_KEYFRAME)) {
+                break;
+            }
+
+            (*index)--;
+            if (*ctts_sample == 0) {
+                (*ctts_index)--;
+                if (*ctts_index >= 0)
+                  *ctts_sample = ctts_data[*ctts_index].count - 1;
+            } else {
+                (*ctts_sample)--;
             }
         }
     }
@@ -3049,7 +3110,7 @@ static int64_t find_prev_closest_index(AVStream *st,
     /* restore AVStream state*/
     st->index_entries = e_keep;
     st->nb_index_entries = nb_keep;
-    return found;
+    return *index >= 0 ? 0 : -1;
 }
 
 /**
@@ -3220,10 +3281,8 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
     int64_t empty_edits_sum_duration = 0;
     int64_t edit_list_index = 0;
     int64_t index;
-    int64_t index_ctts_count;
     int flags;
     int64_t start_dts = 0;
-    int64_t edit_list_media_time_dts = 0;
     int64_t edit_list_start_encountered = 0;
     int64_t search_timestamp = 0;
     int64_t* frame_duration_buffer = NULL;
@@ -3293,17 +3352,11 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
                 st->skip_samples = msc->start_pad = 0;
         }
 
-        //find closest previous key frame
-        edit_list_media_time_dts = edit_list_media_time;
-        if (msc->dts_shift > 0) {
-            edit_list_media_time_dts -= msc->dts_shift;
-        }
-
         // While reordering frame index according to edit list we must handle properly
         // the scenario when edit list entry starts from none key frame.
         // We find closest previous key frame and preserve it and consequent frames in index.
         // All frames which are outside edit list entry time boundaries will be dropped after decoding.
-        search_timestamp = edit_list_media_time_dts;
+        search_timestamp = edit_list_media_time;
         if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
             // Audio decoders like AAC need need a decoder delay samples previous to the current sample,
             // to correctly decode this frame. Hence for audio we seek to a frame 1 sec. before the
@@ -3311,38 +3364,24 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
             search_timestamp = FFMAX(search_timestamp - msc->time_scale, e_old[0].timestamp);
         }
 
-        index = find_prev_closest_index(st, e_old, nb_old, search_timestamp, 0);
-        if (index == -1) {
+        if (find_prev_closest_index(st, e_old, nb_old, ctts_data_old, ctts_count_old, search_timestamp, 0,
+                                    &index, &ctts_index_old, &ctts_sample_old) < 0) {
             av_log(mov->fc, AV_LOG_WARNING,
                    "st: %d edit list: %"PRId64" Missing key frame while searching for timestamp: %"PRId64"\n",
                    st->index, edit_list_index, search_timestamp);
-            index = find_prev_closest_index(st, e_old, nb_old, search_timestamp, AVSEEK_FLAG_ANY);
-
-            if (index == -1) {
+            if (find_prev_closest_index(st, e_old, nb_old, ctts_data_old, ctts_count_old, search_timestamp, AVSEEK_FLAG_ANY,
+                                        &index, &ctts_index_old, &ctts_sample_old) < 0) {
                 av_log(mov->fc, AV_LOG_WARNING,
                        "st: %d edit list %"PRId64" Cannot find an index entry before timestamp: %"PRId64".\n"
                        "Rounding edit list media time to zero.\n",
                        st->index, edit_list_index, search_timestamp);
                 index = 0;
+                ctts_index_old = 0;
+                ctts_sample_old = 0;
                 edit_list_media_time = 0;
             }
         }
         current = e_old + index;
-
-        ctts_index_old = 0;
-        ctts_sample_old = 0;
-
-        // set ctts_index properly for the found key frame
-        for (index_ctts_count = 0; index_ctts_count < index; index_ctts_count++) {
-            if (ctts_data_old && ctts_index_old < ctts_count_old) {
-                ctts_sample_old++;
-                if (ctts_data_old[ctts_index_old].count == ctts_sample_old) {
-                    ctts_index_old++;
-                    ctts_sample_old = 0;
-                }
-            }
-        }
-
         edit_list_start_ctts_sample = ctts_sample_old;
 
         // Iterate over index and arrange it according to edit list
diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
index 6815e4feca..01893a0767 100644
--- a/tests/fate/mov.mak
+++ b/tests/fate/mov.mak
@@ -9,6 +9,7 @@ FATE_MOV = fate-mov-3elist \
            fate-mov-invalid-elst-entry-count \
            fate-mov-gpmf-remux \
            fate-mov-440hz-10ms \
+           fate-mov-ibi-elst-starts-b \
 
 FATE_MOV_FFPROBE = fate-mov-aac-2048-priming \
                    fate-mov-zombie \
@@ -47,6 +48,13 @@ fate-mov-440hz-10ms: CMD = framemd5 -i $(TARGET_SAMPLES)/mov/440hz-10ms.m4a
 # Makes sure that we handle invalid edit list entry count correctly.
 fate-mov-invalid-elst-entry-count: CMD = framemd5 -flags +bitexact -i $(TARGET_SAMPLES)/mov/invalid_elst_entry_count.mov
 
+# Makes sure that 1st key-frame is picked when,
+#    i) One B-frame between 2 key-frames
+#   ii) Edit list starts on B-frame.
+#  iii) Both key-frames have their DTS < edit list start
+# i.e.  Pts Order: I-B-I
+fate-mov-ibi-elst-starts-b: CMD = framemd5 -flags +bitexact -i $(TARGET_SAMPLES)/mov/mov_ibi_elst_starts_b.mov
+
 fate-mov-aac-2048-priming: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_packets -print_format compact $(TARGET_SAMPLES)/mov/aac-2048-priming.mov
 
 fate-mov-zombie: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_streams -show_packets -show_frames -bitexact -print_format compact $(TARGET_SAMPLES)/mov/white_zombie_scrunch-part.mov
-- 
2.15.0.403.gc27cc4dac6-goog



More information about the ffmpeg-devel mailing list