From patchwork Mon Apr 29 22:50:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: fumoboy007 X-Patchwork-Id: 12943 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id F2683447DE4 for ; Tue, 30 Apr 2019 01:50:37 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CE42E68A7EE; Tue, 30 Apr 2019 01:50:37 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mr85p00im-zteg06012001.me.com (mr85p00im-zteg06012001.me.com [17.58.23.197]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5770968A6E6 for ; Tue, 30 Apr 2019 01:50:31 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=04042017; t=1556578229; bh=Ftm7r01gGyRg7WV634Rl8mv1HZH1Wae7k9TZuR3sA7Y=; h=From:To:Subject:Date:Message-Id:MIME-Version:Content-Type; b=QGAACV24USWwK7DV5ZP6Qlz1ftxes4UZc5Pwy1hfz5JPwWtjuxxc16MGPlIJQkbLP HCyOlyDg/D7h1kskRdr841d3MnEVzCHunYGadx5Wu1Xm/9RjszRHCfJV67rVnvKbqh ohJe+xALil5rzF+5ZRpEjs4itL2vrlXQQgbAtGInmuaThtFtBf/MyUeSparf6xKGma 8sTcvYPIQ9IFvg+diGZdIVf55MvO15fN5Zsvs4O0HeXH2npq3jtslSXU82EODEMfV6 IM00JVUieTnHrL82trD+famDY9FlNit1EMhMf3D/5yDjmSltjaouHQRAcaYB5j7in0 EVbJsbMTjzeHg== Received: from MacBook-Pro.localdomain (adsl-76-236-28-223.dsl.chi2ca.sbcglobal.net [76.236.28.223]) by mr85p00im-zteg06012001.me.com (Postfix) with ESMTPSA id 7A50FA0017C; Mon, 29 Apr 2019 22:50:29 +0000 (UTC) From: fumoboy007 To: ffmpeg-devel@ffmpeg.org Date: Mon, 29 Apr 2019 15:50:27 -0700 Message-Id: <20190429225027.81295-1-fumoboy007@me.com> X-Mailer: git-send-email 2.21.0 MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-04-29_14:, , signatures=0 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=3 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1812120000 definitions=main-1904290148 Subject: [FFmpeg-devel] [PATCH] avformat/mov: Populate packet duration using `stts` atom instead of guessing. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: fumoboy007 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Fixes #7855 (“Last subtitle in MP4 is displayed forever”). --- libavformat/isom.h | 3 + libavformat/mov.c | 158 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 135 insertions(+), 26 deletions(-) diff --git a/libavformat/isom.h b/libavformat/isom.h index 69452cae8e..b83744ba09 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -162,6 +162,7 @@ typedef struct MOVStreamContext { unsigned int chunk_count; int64_t *chunk_offsets; unsigned int stts_count; + unsigned int stts_allocated_size; MOVStts *stts_data; unsigned int ctts_count; unsigned int ctts_allocated_size; @@ -174,6 +175,8 @@ typedef struct MOVStreamContext { unsigned *stps_data; ///< partial sync sample for mpeg-2 open gop MOVElst *elst_data; unsigned int elst_count; + int stts_index; + int stts_sample; 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 d0347b2970..3d5f5f7ab0 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -2886,7 +2886,7 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; MOVStreamContext *sc; - unsigned int i, entries, alloc_size = 0; + unsigned int i, entries = 0; int64_t duration=0; int64_t total_sample_count=0; @@ -2913,7 +2913,7 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) int sample_duration; unsigned int sample_count; unsigned int min_entries = FFMIN(FFMAX(i + 1, 1024 * 1024), entries); - MOVStts *stts_data = av_fast_realloc(sc->stts_data, &alloc_size, + MOVStts *stts_data = av_fast_realloc(sc->stts_data, &sc->stts_allocated_size, min_entries * sizeof(*sc->stts_data)); if (!stts_data) { av_freep(&sc->stts_data); @@ -3130,11 +3130,15 @@ static int get_edit_list_entry(MOVContext *mov, static int find_prev_closest_index(AVStream *st, AVIndexEntry *e_old, int nb_old, + MOVStts* stts_data, + int64_t stts_count, MOVStts* ctts_data, int64_t ctts_count, int64_t timestamp_pts, int flag, int64_t* index, + int64_t* stts_index, + int64_t* stts_sample, int64_t* ctts_index, int64_t* ctts_sample) { @@ -3176,6 +3180,15 @@ static int find_prev_closest_index(AVStream *st, // Find out the ctts_index for the found frame. *ctts_index = 0; *ctts_sample = 0; + + if (stts_data) { + av_assert0(stts_index); + av_assert0(stts_sample); + + *stts_index = 0; + *stts_sample = 0; + } + for (index_ctts_count = 0; index_ctts_count < *index; index_ctts_count++) { if (*ctts_index < ctts_count) { (*ctts_sample)++; @@ -3184,6 +3197,13 @@ static int find_prev_closest_index(AVStream *st, *ctts_sample = 0; } } + if (stts_data && *stts_index < stts_count) { + (*stts_sample)++; + if (*stts_sample == stts_data[*stts_index].count) { + (*stts_index)++; + *stts_sample = 0; + } + } } while (*index >= 0 && (*ctts_index) >= 0 && (*ctts_index) < ctts_count) { @@ -3203,6 +3223,16 @@ static int find_prev_closest_index(AVStream *st, } else { (*ctts_sample)--; } + if (stts_data) { + if (*stts_sample == 0) { + (*stts_index)--; + if (*stts_index >= 0) { + *stts_sample = stts_data[*stts_index].count - 1; + } + } else { + (*stts_sample)--; + } + } } } @@ -3275,34 +3305,44 @@ static void fix_index_entry_timestamps(AVStream* st, int end_index, int64_t end_ } /** - * Append a new ctts entry to ctts_data. - * Returns the new ctts_count if successful, else returns -1. + * Append a new stts entry to stts_data. + * Returns the new stts_count if successful, else returns -1. */ -static int64_t add_ctts_entry(MOVStts** ctts_data, unsigned int* ctts_count, unsigned int* allocated_size, +static int64_t add_stts_entry(MOVStts** stts_data, unsigned int* stts_count, unsigned int* allocated_size, int count, int duration) { - MOVStts *ctts_buf_new; - const size_t min_size_needed = (*ctts_count + 1) * sizeof(MOVStts); + MOVStts *stts_buf_new; + const size_t min_size_needed = (*stts_count + 1) * sizeof(MOVStts); const size_t requested_size = min_size_needed > *allocated_size ? FFMAX(min_size_needed, 2 * (*allocated_size)) : min_size_needed; - if((unsigned)(*ctts_count) >= UINT_MAX / sizeof(MOVStts) - 1) + if((unsigned)(*stts_count) >= UINT_MAX / sizeof(MOVStts) - 1) return -1; - ctts_buf_new = av_fast_realloc(*ctts_data, allocated_size, requested_size); + stts_buf_new = av_fast_realloc(*stts_data, allocated_size, requested_size); - if(!ctts_buf_new) + if(!stts_buf_new) return -1; - *ctts_data = ctts_buf_new; + *stts_data = stts_buf_new; - ctts_buf_new[*ctts_count].count = count; - ctts_buf_new[*ctts_count].duration = duration; + stts_buf_new[*stts_count].count = count; + stts_buf_new[*stts_count].duration = duration; - *ctts_count = (*ctts_count) + 1; - return *ctts_count; + *stts_count = (*stts_count) + 1; + return *stts_count; +} + +/** + * Append a new ctts entry to ctts_data. + * Returns the new ctts_count if successful, else returns -1. + */ +static int64_t add_ctts_entry(MOVStts** ctts_data, unsigned int* ctts_count, unsigned int* allocated_size, + int count, int duration) +{ + return add_stts_entry(ctts_data, ctts_count, allocated_size, count, duration); } #define MAX_REORDER_DELAY 16 @@ -3420,6 +3460,10 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) int nb_old = st->nb_index_entries; const AVIndexEntry *e_old_end = e_old + nb_old; const AVIndexEntry *current = NULL; + MOVStts *stts_data_old = msc->stts_data; + int64_t stts_index_old = 0; + int64_t stts_sample_old = 0; + int64_t stts_count_old = msc->stts_count; MOVStts *ctts_data_old = msc->ctts_data; int64_t ctts_index_old = 0; int64_t ctts_sample_old = 0; @@ -3429,6 +3473,7 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) int64_t frame_duration = 0; int64_t edit_list_dts_counter = 0; int64_t edit_list_dts_entry_end = 0; + int64_t edit_list_start_stts_sample = 0; int64_t edit_list_start_ctts_sample = 0; int64_t curr_cts; int64_t curr_ctts = 0; @@ -3466,6 +3511,13 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) st->index_entries_allocated_size = 0; st->nb_index_entries = 0; + // Clean stts fields of MOVStreamContext + msc->stts_data = NULL; + msc->stts_count = 0; + msc->stts_index = 0; + msc->stts_sample = 0; + msc->stts_allocated_size = 0; + // Clean ctts fields of MOVStreamContext msc->ctts_data = NULL; msc->ctts_count = 0; @@ -3524,22 +3576,25 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) search_timestamp = FFMAX(search_timestamp - msc->time_scale, e_old[0].timestamp); } - 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) { + if (find_prev_closest_index(st, e_old, nb_old, stts_data_old, stts_count_old, ctts_data_old, ctts_count_old, search_timestamp, 0, + &index, &stts_index_old, &stts_sample_old, &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); - 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) { + if (find_prev_closest_index(st, e_old, nb_old, stts_data_old, stts_count_old, ctts_data_old, ctts_count_old, search_timestamp, AVSEEK_FLAG_ANY, + &index, &stts_index_old, &stts_sample_old, &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", st->index, edit_list_index, search_timestamp); index = 0; + stts_index_old = 0; + stts_sample_old = 0; ctts_index_old = 0; ctts_sample_old = 0; } } current = e_old + index; + edit_list_start_stts_sample = stts_sample_old; edit_list_start_ctts_sample = ctts_sample_old; // Iterate over index and arrange it according to edit list @@ -3556,6 +3611,24 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) curr_cts = current->timestamp + msc->dts_shift; curr_ctts = 0; + if (stts_data_old && stts_index_old < stts_count_old) { + stts_sample_old++; + if (stts_sample_old == stts_data_old[stts_index_old].count) { + if (add_stts_entry(&msc->stts_data, &msc->stts_count, + &msc->stts_allocated_size, + stts_data_old[stts_index_old].count - edit_list_start_stts_sample, + stts_data_old[stts_index_old].duration) == -1) { + av_log(mov->fc, AV_LOG_ERROR, "Cannot add STTS entry %"PRId64" - {%"PRId64", %d}\n", + stts_index_old, + stts_data_old[stts_index_old].count - edit_list_start_stts_sample, + stts_data_old[stts_index_old].duration); + break; + } + stts_index_old++; + stts_sample_old = 0; + edit_list_start_stts_sample = 0; + } + } if (ctts_data_old && ctts_index_old < ctts_count_old) { curr_ctts = ctts_data_old[ctts_index_old].duration; av_log(mov->fc, AV_LOG_DEBUG, "stts: %"PRId64" ctts: %"PRId64", ctts_index: %"PRId64", ctts_count: %"PRId64"\n", @@ -3669,6 +3742,16 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) continue; } if (ctts_sample_old != 0) { + if (stts_data_old && + add_stts_entry(&msc->stts_data, &msc->stts_count, + &msc->stts_allocated_size, + stts_sample_old - edit_list_start_stts_sample, + stts_data_old[stts_index_old].duration) == -1) { + av_log(mov->fc, AV_LOG_ERROR, "Cannot add STTS entry %"PRId64" - {%"PRId64", %d}\n", + stts_index_old, stts_sample_old - edit_list_start_stts_sample, + stts_data_old[stts_index_old].duration); + break; + } if (add_ctts_entry(&msc->ctts_data, &msc->ctts_count, &msc->ctts_allocated_size, ctts_sample_old - edit_list_start_ctts_sample, @@ -3705,8 +3788,9 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) st->duration = FFMIN(st->duration, edit_list_dts_entry_end - start_dts); msc->start_pad = st->skip_samples; - // Free the old index and the old CTTS structures + // Free the old index and the old STTS/CTTS structures av_free(e_old); + av_free(stts_data_old); av_free(ctts_data_old); av_freep(&frame_duration_buffer); @@ -4277,7 +4361,6 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_freep(&sc->chunk_offsets); av_freep(&sc->sample_sizes); av_freep(&sc->keyframes); - av_freep(&sc->stts_data); av_freep(&sc->stps_data); av_freep(&sc->elst_data); av_freep(&sc->rap_group); @@ -7765,6 +7848,15 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) if (sample->flags & AVINDEX_DISCARD_FRAME) { pkt->flags |= AV_PKT_FLAG_DISCARD; } + if (sc->stts_data && sc->stts_index < sc->stts_count) { + pkt->duration = sc->stts_data[sc->stts_index].duration; + /* update stts context */ + sc->stts_sample++; + if (sc->stts_sample == sc->stts_data[sc->stts_index].count) { + sc->stts_index++; + sc->stts_sample = 0; + } + } if (sc->ctts_data && sc->ctts_index < sc->ctts_count) { pkt->pts = pkt->dts + sc->dts_shift + sc->ctts_data[sc->ctts_index].duration; /* update ctts context */ @@ -7775,11 +7867,12 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) sc->ctts_sample = 0; } } else { - int64_t next_dts = (sc->current_sample < st->nb_index_entries) ? - st->index_entries[sc->current_sample].timestamp : st->duration; - - if (next_dts >= pkt->dts) - pkt->duration = next_dts - pkt->dts; + if (pkt->duration == 0) { + int64_t next_dts = (sc->current_sample < st->nb_index_entries) ? + st->index_entries[sc->current_sample].timestamp : st->duration; + if (next_dts >= pkt->dts) + pkt->duration = next_dts - pkt->dts; + } pkt->pts = pkt->dts; } if (st->discard == AVDISCARD_ALL) @@ -7857,6 +7950,19 @@ static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, return AVERROR_INVALIDDATA; mov_current_sample_set(sc, sample); av_log(s, AV_LOG_TRACE, "stream %d, found sample %d\n", st->index, sc->current_sample); + /* adjust stts index */ + if (sc->stts_data) { + time_sample = 0; + for (i = 0; i < sc->stts_count; i++) { + int next = time_sample + sc->stts_data[i].count; + if (next > sc->current_sample) { + sc->stts_index = i; + sc->stts_sample = sc->current_sample - time_sample; + break; + } + time_sample = next; + } + } /* adjust ctts index */ if (sc->ctts_data) { time_sample = 0;