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.
This commit is contained in:
Tomas Härdin 2024-09-15 22:04:13 +02:00
parent e3fa469a55
commit 649ac17efd
1 changed files with 53 additions and 14 deletions

View File

@ -262,6 +262,7 @@ typedef struct MXFIndexTableSegment {
int *flag_entries; int *flag_entries;
uint64_t *stream_offset_entries; uint64_t *stream_offset_entries;
int nb_index_entries; int nb_index_entries;
int64_t offset;
} MXFIndexTableSegment; } MXFIndexTableSegment;
typedef struct MXFPackage { typedef struct MXFPackage {
@ -1899,18 +1900,44 @@ static int64_t mxf_essence_container_end(MXFContext *mxf, int body_sid)
/* EditUnit -> absolute offset */ /* 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) 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; int i = 0;
int64_t offset_temp = 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); 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]; MXFIndexTableSegment *s = index_table->segments[i];
edit_unit = FFMAX(edit_unit, s->index_start_position); /* clamp if trying to seek before start */ if (s->index_start_position <= edit_unit && edit_unit < s->index_start_position + s->index_duration) {
if (edit_unit < s->index_start_position + s->index_duration) {
int64_t index = edit_unit - s->index_start_position; int64_t index = edit_unit - s->index_start_position;
int64_t offset_temp = s->offset;
if (s->edit_unit_byte_count) { if (s->edit_unit_byte_count) {
if (index > INT64_MAX / 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); *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); 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 { } else {
/* EditUnitByteCount == 0 for VBR indexes, which is fine since they use explicit StreamOffsets */ // edit_unit >= s->index_start_position + s->index_duration
if (s->edit_unit_byte_count && (s->index_duration > INT64_MAX / s->edit_unit_byte_count || i++;
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;
} }
} }
@ -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) { for (int i = 0, j = 0; j < mxf->nb_index_tables; i += mxf->index_tables[j++].nb_segments) {
MXFIndexTable *t = &mxf->index_tables[j]; MXFIndexTable *t = &mxf->index_tables[j];
MXFTrack *mxf_track = NULL; MXFTrack *mxf_track = NULL;
int64_t offset_temp = 0;
t->segments = av_calloc(t->nb_segments, sizeof(*t->segments)); t->segments = av_calloc(t->nb_segments, sizeof(*t->segments));
if (!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++) { 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) { 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", av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment %i has invalid IndexEditRate\n",
t->index_sid, k); 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; 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) if (t->segments[k]->index_duration)
continue; continue;