[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