[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