From 649ac17efd96ddc3967958c6bba07bed3e779bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Sun, 15 Sep 2024 22:04:13 +0200 Subject: [PATCH] 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. --- 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++) { + 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)); + } + + for (; i >= 0 && i < index_table->nb_segments;) { MXFIndexTableSegment *s = index_table->segments[i]; - edit_unit = FFMAX(edit_unit, s->index_start_position); /* clamp if trying to seek before start */ - - 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;