Codecs: mp4: Improve support for long files

rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]> Tue, 19 May 2026 16:28:50 -0400
Newsgroups gmane.comp.systems.archos.rockbox.cvs
Message-ID <[email protected]>
commit 69b6c97e583544f490481b32951e4c0c47656986
Author: Roman Artiukhin <[email protected]>
Date:   Tue May 19 10:49:18 2026 +0300

    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

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;
-- 
rockbox-cvs mailing list
[email protected]
https://lists.haxx.se/mailman/listinfo/rockbox-cvs