From: Jiejun Zhang <zhangjiejun1...@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 <zhangjiejun1...@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) _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel