[FFmpeg-cvslog] lavf/mxfdec: Speed up mxf_edit_unit_absolute_offset()

Tomas Härdin git at videolan.org
Tue Oct 1 20:19:52 EEST 2024


ffmpeg | branch: master | Tomas Härdin <git at haerdin.se> | Sun Sep 15 22:04:13 2024 +0200| [649ac17efd96ddc3967958c6bba07bed3e779bb0] | committer: Tomas Härdin

lavf/mxfdec: Speed up mxf_edit_unit_absolute_offset()

This involves computing the approximate location of the desired index table segment and linearly searching from there.

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=649ac17efd96ddc3967958c6bba07bed3e779bb0
---

 libavformat/mxfdec.c | 67 +++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 53 insertions(+), 14 deletions(-)

diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index 99bf352e00..0b03550e33 100644
--- a/libavformat/mxfdec.c
+++ b/libavformat/mxfdec.c
@@ -262,6 +262,7 @@ typedef struct MXFIndexTableSegment {
     int *flag_entries;
     uint64_t *stream_offset_entries;
     int nb_index_entries;
+    int64_t offset;
 } MXFIndexTableSegment;
 
 typedef struct MXFPackage {
@@ -1899,18 +1900,44 @@ static int64_t mxf_essence_container_end(MXFContext *mxf, int body_sid)
 /* EditUnit -> absolute offset */
 static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_table, int64_t edit_unit, AVRational edit_rate, int64_t *edit_unit_out, int64_t *offset_out, MXFPartition **partition_out, int nag)
 {
-    int i;
-    int64_t offset_temp = 0;
+    int i = 0;
+    int64_t index_duration, index_end;
+    MXFIndexTableSegment *first_segment, *last_segment;
+
+    if (!index_table->nb_segments) {
+        av_log(mxf->fc, AV_LOG_ERROR, "no index table segments\n");
+        return AVERROR_INVALIDDATA;
+    }
 
     edit_unit = av_rescale_q(edit_unit, index_table->segments[0]->index_edit_rate, edit_rate);
 
-    for (i = 0; i < index_table->nb_segments; i++) {
-        MXFIndexTableSegment *s = index_table->segments[i];
+    first_segment = index_table->segments[0];
+    last_segment  = index_table->segments[index_table->nb_segments - 1];
+
+    // clamp to actual range of index
+    index_end = av_sat_add64(last_segment->index_start_position, last_segment->index_duration);
+    edit_unit = FFMAX(FFMIN(edit_unit, index_end), first_segment->index_start_position);
+
+    // guess which table segment this edit unit is in
+    // saturation is fine since it's just a guess
+    // if the guess is wrong we revert to a linear search
+    index_duration = av_sat_sub64(index_end, first_segment->index_start_position);
+
+    // compute the guess, taking care not to cause overflow or division by zero
+    if (index_duration > 0 && edit_unit <= INT64_MAX / index_table->nb_segments) {
+        // a simple linear guesstimate
+        // this is accurate to within +-1 when partitions are generated at a constant rate like mxfenc does
+        int64_t i64 = index_table->nb_segments * edit_unit / index_duration;
+        // clamp and downcast to 32-bit
+        i = FFMAX(0, FFMIN(index_table->nb_segments - 1, i64));
+    }
 
-        edit_unit = FFMAX(edit_unit, s->index_start_position);  /* clamp if trying to seek before start */
+    for (; i >= 0 && i < index_table->nb_segments;) {
+        MXFIndexTableSegment *s = index_table->segments[i];
 
-        if (edit_unit < s->index_start_position + s->index_duration) {
+        if (s->index_start_position <= edit_unit && edit_unit < s->index_start_position + s->index_duration) {
             int64_t index = edit_unit - s->index_start_position;
+            int64_t offset_temp = s->offset;
 
             if (s->edit_unit_byte_count) {
                 if (index > INT64_MAX / s->edit_unit_byte_count ||
@@ -1935,14 +1962,12 @@ static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_t
                 *edit_unit_out = av_rescale_q(edit_unit, edit_rate, s->index_edit_rate);
 
             return mxf_absolute_bodysid_offset(mxf, index_table->body_sid, offset_temp, offset_out, partition_out);
+        } else if (edit_unit < s->index_start_position) {
+            // the segments are sorted by IndexStartPosition, so this is guaranteed to terminate
+            i--;
         } else {
-            /* EditUnitByteCount == 0 for VBR indexes, which is fine since they use explicit StreamOffsets */
-            if (s->edit_unit_byte_count && (s->index_duration > INT64_MAX / s->edit_unit_byte_count ||
-                s->edit_unit_byte_count * s->index_duration > INT64_MAX - offset_temp)
-            )
-                return AVERROR_INVALIDDATA;
-
-            offset_temp += s->edit_unit_byte_count * s->index_duration;
+            // edit_unit >= s->index_start_position + s->index_duration
+            i++;
         }
     }
 
@@ -2127,6 +2152,7 @@ static int mxf_compute_index_tables(MXFContext *mxf)
     for (int i = 0, j = 0; j < mxf->nb_index_tables; i += mxf->index_tables[j++].nb_segments) {
         MXFIndexTable *t = &mxf->index_tables[j];
         MXFTrack *mxf_track = NULL;
+        int64_t offset_temp = 0;
 
         t->segments = av_calloc(t->nb_segments, sizeof(*t->segments));
         if (!t->segments) {
@@ -2155,8 +2181,10 @@ static int mxf_compute_index_tables(MXFContext *mxf)
             }
         }
 
-        /* fix zero IndexDurations */
+        /* fix zero IndexDurations and compute segment offsets */
         for (int k = 0; k < t->nb_segments; k++) {
+            MXFIndexTableSegment *s = t->segments[k];
+
             if (!t->segments[k]->index_edit_rate.num || !t->segments[k]->index_edit_rate.den) {
                 av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment %i has invalid IndexEditRate\n",
                        t->index_sid, k);
@@ -2164,6 +2192,17 @@ static int mxf_compute_index_tables(MXFContext *mxf)
                     t->segments[k]->index_edit_rate = mxf_track->edit_rate;
             }
 
+            s->offset = offset_temp;
+
+            /* EditUnitByteCount == 0 for VBR indexes, which is fine since they use explicit StreamOffsets */
+            if (s->edit_unit_byte_count && (s->index_duration > INT64_MAX / s->edit_unit_byte_count ||
+                s->edit_unit_byte_count * s->index_duration > INT64_MAX - offset_temp)) {
+                ret = AVERROR_INVALIDDATA;
+                goto finish_decoding_index;
+            }
+
+            offset_temp += t->segments[k]->edit_unit_byte_count * t->segments[k]->index_duration;
+
             if (t->segments[k]->index_duration)
                 continue;
 



More information about the ffmpeg-cvslog mailing list