From 69b6c97e583544f490481b32951e4c0c47656986 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Tue, 19 May 2026 10:49:18 +0300 Subject: [PATCH] Codecs: mp4: Improve support for long files Load stts (time to sample) table on demand if it doesn't fit in RAM. Fixes FS#13889 (in most cases I've seen files with single element in this table and problematic one has 111200 elements) Change-Id: I719f92a4512a45472739587e81861b9bc545f349 --- lib/rbcodec/codecs/aac.c | 3 +- lib/rbcodec/codecs/libm4a/demux.c | 24 +++++++++-- lib/rbcodec/codecs/libm4a/m4a.c | 66 +++++++++++++++++++++++++------ lib/rbcodec/codecs/libm4a/m4a.h | 1 + 4 files changed, 76 insertions(+), 18 deletions(-) diff --git a/lib/rbcodec/codecs/aac.c b/lib/rbcodec/codecs/aac.c index 67cfd09ded..236987ec8d 100644 --- a/lib/rbcodec/codecs/aac.c +++ b/lib/rbcodec/codecs/aac.c @@ -249,7 +249,8 @@ enum codec_status codec_run(void) if (i == demux_res.num_sample_byte_sizes - 1) { // Size of the last frame - const uint32_t sample_duration = (demux_res.num_time_to_samples > 0) ? + // Currently ignored if time_to_sample table fails to fit. + const uint32_t sample_duration = (demux_res.num_time_to_samples > 0 && demux_res.time_to_sample) ? demux_res.time_to_sample[demux_res.num_time_to_samples - 1].sample_duration : frame_samples; diff --git a/lib/rbcodec/codecs/libm4a/demux.c b/lib/rbcodec/codecs/libm4a/demux.c index 948b686f4d..f9203a0893 100644 --- a/lib/rbcodec/codecs/libm4a/demux.c +++ b/lib/rbcodec/codecs/libm4a/demux.c @@ -317,13 +317,29 @@ static bool read_chunk_stts(qtmovie_t *qtmovie, size_t chunk_len) qtmovie->res->num_time_to_samples = numentries; qtmovie->res->time_to_sample = malloc(numentries * sizeof(*qtmovie->res->time_to_sample)); - + if (qtmovie->res->time_to_sample && numentries > 1) + { + //Make sure we leave space for at least 100 records in lookup table + void* enough_mem = malloc(sizeof(*qtmovie->res->lookup_table)*100); + if (!enough_mem) + { + free(qtmovie->res->time_to_sample); + qtmovie->res->time_to_sample = NULL; + } + else + { + free(enough_mem); + } + } if (!qtmovie->res->time_to_sample) { - DEBUGF("stts too large\n"); - return false; + qtmovie->res->time_to_sample_offset = stream_tell(qtmovie->stream); + stream_skip(qtmovie->stream, size_remaining); + DEBUGF("stts too large %ld, save time_to_sample_offset\n", size_remaining); + return true; } + DEBUGF("stts numentires %d, size: %ld\n", numentries, numentries * sizeof(*qtmovie->res->time_to_sample)); for (i = 0; i < numentries; i++) { qtmovie->res->time_to_sample[i].sample_count = stream_read_uint32(qtmovie->stream); @@ -465,7 +481,7 @@ static bool read_chunk_stco(qtmovie_t *qtmovie, size_t chunk_len) fit_numentries = numentries / accuracy_divider; } } - DEBUGF("lookup_table numentries %d, fit_numentries %d\n", numentries, fit_numentries); + DEBUGF("lookup_table numentries %d, fit_numentries %ld\n", numentries, fit_numentries); qtmovie->res->num_lookup_table = fit_numentries; if (!qtmovie->res->lookup_table) diff --git a/lib/rbcodec/codecs/libm4a/m4a.c b/lib/rbcodec/codecs/libm4a/m4a.c index 6c943b408f..1695d21bb3 100644 --- a/lib/rbcodec/codecs/libm4a/m4a.c +++ b/lib/rbcodec/codecs/libm4a/m4a.c @@ -137,6 +137,12 @@ int m4a_check_sample_offset(demux_res_t *demux_res, uint32_t frame, uint32_t *st return -1; } +static inline void read_time_to_sample_entry(stream_t* stream, uint32_t* time_cnt, uint32_t* time_dur) +{ + *time_cnt = stream_read_uint32(stream); + *time_dur = stream_read_uint32(stream); +} + /* Seek to desired sound sample location. Return 1 on success (and modify * sound_samples_done and current_sample), 0 if failed. */ unsigned int m4a_seek(demux_res_t* demux_res, stream_t* stream, @@ -161,10 +167,21 @@ unsigned int m4a_seek(demux_res_t* demux_res, stream_t* stream, /* The 'sound_sample_loc' we have is PCM-based and not directly usable. * We need to convert it to an MP4 sample number 'sample_i' first. */ sample_i = sound_sample_i = 0; + if (tts_tab == NULL) + { + stream_seek(stream, demux_res->time_to_sample_offset); + } for (time = 0; time < demux_res->num_time_to_samples; ++time) { - time_cnt = tts_tab[time].sample_count; - time_dur = tts_tab[time].sample_duration; + if (tts_tab) + { + time_dur = tts_tab[time].sample_duration; + time_cnt = tts_tab[time].sample_count; + } + else + { + read_time_to_sample_entry(stream, &time_cnt, &time_dur); + } uint32_t time_var = time_cnt * time_dur; if (sound_sample_loc < sound_sample_i + time_var) @@ -194,10 +211,22 @@ unsigned int m4a_seek(demux_res_t* demux_res, stream_t* stream, /* Compute the PCM sample number of the chunk's first sample * to get an accurate base for sound_sample_i. */ i = sound_sample_i = 0; + if (tts_tab == NULL) + { + stream_seek(stream, demux_res->time_to_sample_offset); + } + for (time = 0; time < demux_res->num_time_to_samples; ++time) { - time_cnt = tts_tab[time].sample_count; - time_dur = tts_tab[time].sample_duration; + if (tts_tab) + { + time_dur = tts_tab[time].sample_duration; + time_cnt = tts_tab[time].sample_count; + } + else + { + read_time_to_sample_entry(stream, &time_cnt, &time_dur); + } if (chunk_first_sample < i + time_cnt) { @@ -223,7 +252,7 @@ unsigned int m4a_seek(demux_res_t* demux_res, stream_t* stream, i < sample_i && i < demux_res->num_sample_byte_sizes; ++i) { /* this could be unnecessary */ - if (time_cnt == 0 && ++time < demux_res->num_time_to_samples) + if (time_cnt == 0 && tts_tab && ++time < demux_res->num_time_to_samples) { time_cnt = tts_tab[time].sample_count; time_dur = tts_tab[time].sample_duration; @@ -279,8 +308,8 @@ unsigned int m4a_seek_raw(demux_res_t* demux_res, stream_t* stream, uint32_t chunk_sample = 0; uint32_t total_samples = 0; uint64_t new_sound_sample = 0; - uint32_t tmp_dur; - uint32_t tmp_cnt; + uint32_t time_dur; + uint32_t time_cnt; uint32_t new_pos; /* We know the desired byte offset, search for the chunk right before. @@ -297,16 +326,27 @@ unsigned int m4a_seek_raw(demux_res_t* demux_res, stream_t* stream, /* Get sound sample offset. */ i = 0; - time_to_sample_t *tab2 = demux_res->time_to_sample; + time_to_sample_t *tts_tab = demux_res->time_to_sample; + if (tts_tab == NULL) + { + stream_seek(stream, demux_res->time_to_sample_offset); + } while (i < demux_res->num_time_to_samples) { - tmp_dur = tab2[i].sample_duration; - tmp_cnt = tab2[i].sample_count; - total_samples += tmp_cnt; - new_sound_sample += tmp_cnt * tmp_dur; + if (tts_tab) + { + time_dur = tts_tab[i].sample_duration; + time_cnt = tts_tab[i].sample_count; + } + else + { + read_time_to_sample_entry(stream, &time_cnt, &time_dur); + } + total_samples += time_cnt; + new_sound_sample += time_cnt * time_dur; if (chunk_sample <= total_samples) { - new_sound_sample -= (total_samples - chunk_sample) * tmp_dur; + new_sound_sample -= (total_samples - chunk_sample) * time_dur; break; } ++i; diff --git a/lib/rbcodec/codecs/libm4a/m4a.h b/lib/rbcodec/codecs/libm4a/m4a.h index 14b22f5dbf..a0cdf3ac1f 100644 --- a/lib/rbcodec/codecs/libm4a/m4a.h +++ b/lib/rbcodec/codecs/libm4a/m4a.h @@ -79,6 +79,7 @@ typedef struct time_to_sample_t *time_to_sample; uint32_t num_time_to_samples; + uint32_t time_to_sample_offset; uint32_t *sample_byte_sizes; uint32_t num_sample_byte_sizes;