[FFmpeg-devel] [PATCH] avformat/mov: Populate packet duration using `stts` atom instead of guessing.

fumoboy007 fumoboy007 at me.com
Tue Apr 30 01:50:27 EEST 2019


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;
-- 
2.21.0



More information about the ffmpeg-devel mailing list