[FFmpeg-devel] [PATCH] libavformat/mov: fix multiple stsd handling of MOV files with edit list
zhangjiejun1992 at gmail.com
zhangjiejun1992 at gmail.com
Fri Dec 2 08:25:07 EET 2016
From: Jiejun Zhang <zhangjiejun1992 at gmail.com>
When an edit list exists in a MOV file, counting by stscs no longer works because stscs' order is different from the actual timeline. This commit adds stsd-change markers to the actual timeline and changes stsd according to these markers.
Test sample: https://www.dropbox.com/s/qz0ort4znqq9jdy/attachment_video.mov?dl=0
Run `ffmpeg -i attachment_video.mov -f image2 frames%03d.png`. The git-master version cannot decode frame 327 to 330 because a wrong stsd is used. After applying this patch they are decoded correctly.
Signed-off-by: Jiejun Zhang <zhangjiejun1992 at gmail.com>
---
libavformat/isom.h | 13 +++++-
libavformat/mov.c | 125 +++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 109 insertions(+), 29 deletions(-)
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 02bfedd..141237a 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -115,6 +115,14 @@ typedef struct MOVFragmentIndex {
MOVFragmentIndexItem *items;
} MOVFragmentIndex;
+/**
+ * An entry for: stsd changes to `stsd_id` at sample `sample_index` (inclusive)
+ */
+typedef struct MOVStsdChangeEntry {
+ int64_t sample_index;
+ int stsd_id;
+} MOVStsdChangeEntry;
+
typedef struct MOVStreamContext {
AVIOContext *pb;
int pb_is_copied;
@@ -128,12 +136,13 @@ typedef struct MOVStreamContext {
MOVStts *ctts_data;
unsigned int stsc_count;
MOVStsc *stsc_data;
- int stsc_index;
- int stsc_sample;
unsigned int stps_count;
unsigned *stps_data; ///< partial sync sample for mpeg-2 open gop
MOVElst *elst_data;
unsigned int elst_count;
+ MOVStsdChangeEntry *stsd_change_data;
+ int stsd_change_count;
+ int stsd_change_index;
int ctts_index;
int ctts_sample;
unsigned int sample_size; ///< may contain value calculated from stsd or value from stsz atom
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 88a79da..e5f7f72 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -2901,6 +2901,35 @@ static int64_t add_index_entry(AVStream *st, int64_t pos, int64_t timestamp,
return index;
}
+static int add_stsd_change_entry(MOVStreamContext *msc, int64_t sample_index, int stsd_id,
+ unsigned int* allocated_size)
+{
+ MOVStsdChangeEntry *entries;
+ const size_t min_size_needed = (msc->stsd_change_count + 1) * sizeof(MOVStsdChangeEntry);
+
+ const size_t requested_size =
+ min_size_needed > *allocated_size ?
+ FFMAX(min_size_needed, 2 * (*allocated_size)) :
+ min_size_needed;
+
+ if ((unsigned)msc->stsd_change_count + 1 >= UINT_MAX / sizeof(MOVStsdChangeEntry))
+ return -1;
+
+ entries = av_fast_realloc(msc->stsd_change_data,
+ allocated_size,
+ requested_size);
+
+ if (!entries)
+ return -1;
+
+ msc->stsd_change_data = entries;
+ entries[msc->stsd_change_count].sample_index = sample_index;
+ entries[msc->stsd_change_count].stsd_id = stsd_id;
+ msc->stsd_change_count++;
+
+ return 0;
+}
+
/**
* Rewrite timestamps of index entries in the range [end_index - frame_duration_buffer_size, end_index)
* by subtracting end_ts successively by the amounts given in frame_duration_buffer.
@@ -2984,6 +3013,8 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
int num_discarded_begin = 0;
int first_non_zero_audio_edit = -1;
int packet_skip_samples = 0;
+ int stsc_index = 0;
+ int stsc_sample = 0;
if (!msc->elst_data || msc->elst_count <= 0 || nb_old <= 0) {
return;
@@ -3012,6 +3043,11 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
start_dts = edit_list_dts_entry_end;
+ msc->last_stsd_index = -1;
+ unsigned int stsd_change_data_allocated_size = 0;
+ msc->stsd_change_data = NULL;
+ msc->stsd_change_count = 0;
+
while (get_edit_list_entry(mov, msc, edit_list_index, &edit_list_media_time,
&edit_list_duration, mov->time_scale)) {
av_log(mov->fc, AV_LOG_DEBUG, "Processing st: %d, edit list %"PRId64" - media time: %"PRId64", duration: %"PRId64"\n",
@@ -3073,6 +3109,20 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
}
current = e_old + index;
+ // adjust stsd index
+ int time_sample = 0;
+ for (int i = 0; i < msc->stsc_count; i++) {
+ int next = time_sample + mov_get_stsc_samples(msc, i);
+ if (next > index) {
+ stsc_index = i;
+ stsc_sample = index - time_sample;
+ break;
+ }
+ time_sample = next;
+ }
+
+ av_log(mov->fc, AV_LOG_INFO, "stream %d, adjusted stsc_index: %d, stsc_sample: %d\n", st->index, stsc_index, stsc_sample);
+
ctts_index_old = 0;
ctts_sample_old = 0;
@@ -3176,12 +3226,36 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
}
}
- if (add_index_entry(st, current->pos, edit_list_dts_counter, current->size,
- current->min_distance, flags) == -1) {
+ int index_entry_index = add_index_entry(st, current->pos, edit_list_dts_counter, current->size,
+ current->min_distance, flags);
+ if (index_entry_index == -1) {
av_log(mov->fc, AV_LOG_ERROR, "Cannot add index entry\n");
break;
}
+ if (msc->stsc_data) {
+ if (msc->stsc_data[stsc_index].id > 0 &&
+ msc->stsc_data[stsc_index].id - 1 < msc->stsd_count &&
+ msc->stsc_data[stsc_index].id - 1 != msc->last_stsd_index) {
+ msc->last_stsd_index = msc->stsc_data[stsc_index].id - 1;
+ int ret = add_stsd_change_entry(msc, index_entry_index, msc->last_stsd_index, &stsd_change_data_allocated_size);
+ if (ret < 0) {
+ av_log(mov->fc, AV_LOG_ERROR, "Cannot add stsd change entry\n");
+ break;
+ }
+ av_log(mov->fc, AV_LOG_INFO, "added a stsd change entry sample_index: %d, stsd_index:%d\n",
+ index_entry_index,
+ msc->last_stsd_index);
+ }
+
+ stsc_sample++;
+ if (stsc_index < msc->stsc_count - 1 &&
+ mov_get_stsc_samples(msc, stsc_index) == stsc_sample) {
+ stsc_index++;
+ stsc_sample = 0;
+ }
+ }
+
// Only start incrementing DTS in frame_duration amounts, when we encounter a frame in edit list.
if (edit_list_start_encountered > 0) {
edit_list_dts_counter = edit_list_dts_counter + frame_duration;
@@ -3206,6 +3280,7 @@ static void mov_fix_index(MOVContext *mov, AVStream *st)
}
}
}
+ msc->last_stsd_index = 0;
// Update av stream length
st->duration = edit_list_dts_entry_end - start_dts;
@@ -5385,6 +5460,7 @@ static int mov_read_close(AVFormatContext *s)
av_freep(&sc->stts_data);
av_freep(&sc->stps_data);
av_freep(&sc->elst_data);
+ av_freep(&sc->stsd_change_data);
av_freep(&sc->rap_group);
av_freep(&sc->display_matrix);
@@ -5807,9 +5883,6 @@ static int mov_change_extradata(MOVStreamContext *sc, AVPacket *pkt)
uint8_t *side, *extradata;
int extradata_size;
- /* Save the current index. */
- sc->last_stsd_index = sc->stsc_data[sc->stsc_index].id - 1;
-
/* Notify the decoder that extradata changed. */
extradata_size = sc->extradata_size[sc->last_stsd_index];
extradata = sc->extradata[sc->last_stsd_index];
@@ -5923,21 +5996,22 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
pkt->pos = sample->pos;
/* Multiple stsd handling. */
- if (sc->stsc_data) {
- /* Keep track of the stsc index for the given sample, then check
- * if the stsd index is different from the last used one. */
- sc->stsc_sample++;
- if (sc->stsc_index < sc->stsc_count - 1 &&
- mov_get_stsc_samples(sc, sc->stsc_index) == sc->stsc_sample) {
- sc->stsc_index++;
- sc->stsc_sample = 0;
- /* Do not check indexes after a switch. */
- } else if (sc->stsc_data[sc->stsc_index].id > 0 &&
- sc->stsc_data[sc->stsc_index].id - 1 < sc->stsd_count &&
- sc->stsc_data[sc->stsc_index].id - 1 != sc->last_stsd_index) {
+ if (sc->stsd_change_data) {
+ if (sc->stsd_change_index + 1 < sc->stsd_change_count &&
+ // sc->current_sample is already the next sample, so subtract 1
+ sc->stsd_change_data[sc->stsd_change_index + 1].sample_index == sc->current_sample - 1) {
+ ++sc->stsd_change_index;
+ }
+ if (sc->last_stsd_index != sc->stsd_change_data[sc->stsd_change_index].stsd_id) {
+ av_log(mov->fc, AV_LOG_INFO, "stsd id changed from %d to %d, at sample %d\n",
+ sc->last_stsd_index,
+ sc->stsd_change_data[sc->stsd_change_index].stsd_id,
+ sc->current_sample - 1);
+ sc->last_stsd_index = sc->stsd_change_data[sc->stsd_change_index].stsd_id;
ret = mov_change_extradata(sc, pkt);
- if (ret < 0)
+ if (ret < 0) {
return ret;
+ }
}
}
@@ -6012,16 +6086,13 @@ static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp,
}
}
- /* adjust stsd index */
- time_sample = 0;
- for (i = 0; i < sc->stsc_count; i++) {
- int next = time_sample + mov_get_stsc_samples(sc, i);
- if (next > sc->current_sample) {
- sc->stsc_index = i;
- sc->stsc_sample = sc->current_sample - time_sample;
- break;
+ /* adjust stsd change index */
+ for (i = 0; i < sc->stsd_change_count; i++) {
+ if (sc->current_sample >= sc->stsd_change_data[i].sample_index &&
+ (i + 1 == sc->stsd_change_count ||
+ sc->current_sample < sc->stsd_change_data[i + 1].sample_index)) {
+ sc->stsd_change_index = i;
}
- time_sample = next;
}
ret = mov_seek_auxiliary_info(s, sc);
--
2.9.3 (Apple Git-75)
More information about the ffmpeg-devel
mailing list