shithub: mp3dec

Download patch

ref: fa4c2ef5d00c4a136e6b59873151dea46e8d8023
parent: 32a3ae45de25edbee8c232a63b1c0528bc3dbd96
author: lieff <lieff@users.noreply.github.com>
date: Wed Feb 5 14:48:54 EST 2020

mp3dec_ex: support vbr tag for mp3dec_load_buf

diff: cannot open a/vectors/vbrtag//null: file does not exist: 'a/vectors/vbrtag//null'
--- a/minimp3_ex.h
+++ b/minimp3_ex.h
@@ -127,10 +127,52 @@
     *pbuf_size = buf_size;
 }
 
+static int mp3dec_check_vbrtag(const uint8_t *frame, int frame_size, uint32_t *frames, int *delay, int *padding)
+{
+    int i;
+    static const char g_xing_tag[4] = { 'X', 'i', 'n', 'g' };
+    static const char g_info_tag[4] = { 'I', 'n', 'f', 'o' };
+#define FRAMES_FLAG     1
+#define BYTES_FLAG      2
+#define TOC_FLAG        4
+#define VBR_SCALE_FLAG  8
+    /* TODO: skip side-info?
+    /  Side info offsets afrer header:
+    /                Mono  Stereo
+    /  MPEG1          17     32
+    /  MPEG2 & 2.5     9     17*/
+    for (i = 5; i < frame_size - 36; i++)
+    {
+        const uint8_t *tag = frame + i;
+        if (memcmp(g_xing_tag, tag, 4) && memcmp(g_info_tag, tag, 4))
+            continue;
+        int flags = tag[7];
+        if (!((flags & FRAMES_FLAG)))
+            break;
+        tag += 8;
+        (*frames) = (uint32_t)(tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8) | tag[3];
+        tag += 4;
+        if (flags & BYTES_FLAG)
+            tag += 4;
+        if (flags & TOC_FLAG)
+            tag += 100;
+        if (flags & VBR_SCALE_FLAG)
+            tag += 4;
+        tag += 21;
+        if (tag - frame + 2 >= frame_size)
+            break;
+        *delay   = ((tag[0] << 4) | (tag[1] >> 4)) + (528 + 1);
+        *padding = (((tag[1] & 0xF) << 8) | tag[2]) - (528 + 1);
+        return 1;
+    }
+    return 0;
+}
+
 void mp3dec_load_buf(mp3dec_t *dec, const uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data)
 {
+    uint64_t detected_samples = 0;
     size_t orig_buf_size = buf_size;
-    mp3d_sample_t pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
+    int to_skip = 0;
     mp3dec_frame_info_t frame_info;
     memset(info, 0, sizeof(*info));
     memset(&frame_info, 0, sizeof(frame_info));
@@ -138,33 +180,57 @@
     mp3dec_skip_id3(&buf, &buf_size);
     if (!buf_size)
         return;
-    /* try to make allocation size assumption by first frame */
+    /* try to make allocation size assumption by first frame or vbr tag */
     mp3dec_init(dec);
     int samples;
     do
     {
-        samples = mp3dec_decode_frame(dec, buf, MINIMP3_MIN(buf_size, (size_t)INT_MAX), pcm, &frame_info);
-        buf      += frame_info.frame_bytes;
-        buf_size -= frame_info.frame_bytes;
-        if (samples)
-            break;
-    } while (frame_info.frame_bytes);
-    if (!samples)
-        return;
-    samples *= frame_info.channels;
-    size_t allocated = (buf_size/frame_info.frame_bytes)*samples*sizeof(mp3d_sample_t) + MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t);
+        uint32_t frames;
+        int delay, padding, free_format_bytes = 0, frame_size = 0;
+        int i = mp3d_find_frame(buf, buf_size, &free_format_bytes, &frame_size);
+        buf      += i;
+        buf_size -= i;
+        if (i && !frame_size)
+            continue;
+        if (!frame_size)
+            return;
+        const uint8_t *hdr = buf;
+        frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2;
+        frame_info.hz = hdr_sample_rate_hz(hdr);
+        frame_info.layer = 4 - HDR_GET_LAYER(hdr);
+        frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr);
+        frame_info.frame_bytes = frame_size;
+        samples = hdr_frame_samples(hdr)*frame_info.channels;
+        if (mp3dec_check_vbrtag(hdr, frame_size, &frames, &delay, &padding))
+        {
+            padding *= frame_info.channels;
+            to_skip = delay*frame_info.channels;
+            detected_samples = samples*(uint64_t)frames;
+            if (detected_samples >= (uint64_t)to_skip)
+                detected_samples -= to_skip;
+            if (padding > 0 && detected_samples >= (uint64_t)padding)
+                detected_samples -= padding;
+            if (!detected_samples)
+                return;
+            buf      += frame_size;
+            buf_size -= frame_size;
+        }
+        break;
+    } while(1);
+    size_t allocated = MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t);
+    if (detected_samples)
+        allocated += detected_samples*sizeof(mp3d_sample_t);
+    else
+        allocated += (buf_size/frame_info.frame_bytes)*samples*sizeof(mp3d_sample_t);
     info->buffer = (mp3d_sample_t*)malloc(allocated);
     if (!info->buffer)
         return;
-    info->samples = samples;
-    memcpy(info->buffer, pcm, info->samples*sizeof(mp3d_sample_t));
     /* save info */
     info->channels = frame_info.channels;
     info->hz       = frame_info.hz;
     info->layer    = frame_info.layer;
-    size_t avg_bitrate_kbps = frame_info.bitrate_kbps;
-    size_t frames = 1;
-    /* decode rest frames */
+    /* decode all frames */
+    size_t avg_bitrate_kbps = 0, frames = 0;
     int frame_bytes;
     do
     {
@@ -187,7 +253,15 @@
 #else
                 break;
 #endif
-            info->samples += samples*frame_info.channels;
+            samples *= frame_info.channels;
+            if (to_skip)
+            {
+                size_t skip = MINIMP3_MIN(samples, to_skip);
+                to_skip -= skip;
+                samples -= skip;
+                memmove(info->buffer, info->buffer + skip, samples);
+            }
+            info->samples += samples;
             avg_bitrate_kbps += frame_info.bitrate_kbps;
             frames++;
             if (progress_cb)
@@ -194,10 +268,13 @@
                 progress_cb(user_data, orig_buf_size, orig_buf_size - buf_size, &frame_info);
         }
     } while (frame_bytes);
+    if (detected_samples)
+        info->samples = detected_samples;
     /* reallocate to normal buffer size */
     if (allocated != info->samples*sizeof(mp3d_sample_t))
         info->buffer = (mp3d_sample_t*)realloc(info->buffer, info->samples*sizeof(mp3d_sample_t));
-    info->avg_bitrate_kbps = avg_bitrate_kbps/frames;
+    if (frames)
+        info->avg_bitrate_kbps = avg_bitrate_kbps/frames;
 }
 
 size_t mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data)
@@ -241,61 +318,29 @@
 
 static int mp3dec_load_index(void *user_data, const uint8_t *frame, int frame_size, int free_format_bytes, size_t buf_size, size_t offset, mp3dec_frame_info_t *info)
 {
-    static const char g_xing_tag[4] = { 'X', 'i', 'n', 'g' };
-    static const char g_info_tag[4] = { 'I', 'n', 'f', 'o' };
     mp3dec_frame_t *idx_frame;
     mp3dec_ex_t *dec = (mp3dec_ex_t *)user_data;
     if (!dec->index.frames && !dec->vbr_tag_found)
     {   /* detect VBR tag and try to avoid full scan */
-        int i;
+        uint32_t frames;
+        int delay, padding;
         dec->info = *info;
         dec->start_offset = dec->offset = offset;
         dec->end_offset   = offset + buf_size;
         dec->free_format_bytes = free_format_bytes; /* should not change */
-        /* TODO: skip side-info?
-        /  Side info offsets afrer header:
-        /                Mono  Stereo
-        /  MPEG1          17     32
-        /  MPEG2 & 2.5     9     17*/
-        for (i = 5; i < frame_size - 36; i++)
+        if (mp3dec_check_vbrtag(frame, frame_size, &frames, &delay, &padding))
         {
-            const uint8_t *tag = frame + i;
-            if (!memcmp(g_xing_tag, tag, 4) || !memcmp(g_info_tag, tag, 4))
-            {
-#define FRAMES_FLAG     1
-#define BYTES_FLAG      2
-#define TOC_FLAG        4
-#define VBR_SCALE_FLAG  8
-                int flags = tag[7], delay, padding;
-                uint32_t frames;
-                if (!((flags & FRAMES_FLAG)))
-                    break;
-                tag += 8;
-                frames = (uint32_t)(tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8) | tag[3];
-                tag += 4;
-                if (flags & BYTES_FLAG)
-                    tag += 4;
-                if (flags & TOC_FLAG)
-                    tag += 100;
-                if (flags & VBR_SCALE_FLAG)
-                    tag += 4;
-                tag += 21;
-                if (tag - frame + 2 >= frame_size)
-                    break;
-                delay   = (((tag[0] << 4) | (tag[1] >> 4)) + (528 + 1))*info->channels;
-                padding = ((((tag[1] & 0xF) << 8) | tag[2]) - (528 + 1))*info->channels;
-
-                dec->start_delay = dec->to_skip = delay;
-                dec->samples = hdr_frame_samples(frame)*info->channels*(uint64_t)frames;
-                if (dec->samples >= (uint64_t)dec->start_delay)
-                    dec->samples -= dec->start_delay;
-                if (padding > 0 && dec->samples >= (uint64_t)padding)
-                    dec->samples -= padding;
-                dec->detected_samples = dec->samples;
-                dec->start_offset = dec->offset = offset + frame_size;
-                dec->vbr_tag_found = 1;
-                return 1;
-            }
+            padding *= info->channels;
+            dec->start_delay = dec->to_skip = delay*info->channels;
+            dec->samples = hdr_frame_samples(frame)*info->channels*(uint64_t)frames;
+            if (dec->samples >= (uint64_t)dec->start_delay)
+                dec->samples -= dec->start_delay;
+            if (padding > 0 && dec->samples >= (uint64_t)padding)
+                dec->samples -= padding;
+            dec->detected_samples = dec->samples;
+            dec->start_offset = dec->offset = offset + frame_size;
+            dec->vbr_tag_found = 1;
+            return 1;
         }
     }
     if (dec->index.num_frames + 1 > dec->index.capacity)
--- a/scripts/test_mode.sh
+++ b/scripts/test_mode.sh
@@ -13,7 +13,7 @@
 vectors/M2L3_bitrate_22_all.bit vectors/M2L3_bitrate_24_all.bit vectors/M2L3_compl24.bit vectors/M2L3_noise.bit \
 vectors/l3-nonstandard-id3v1.bit vectors/l3-nonstandard-id3v2.bit vectors/l1-fl1.bit vectors/l2-fl10.bit"
 if [ "$MODE" = "2" ]; then
-VECTORS+=" vectors/vbrtag/l3-nonstandard-sin1k0db_lame_tag.bit"
+VECTORS+=" vectors/l3-nonstandard-sin1k0db_lame_vbrtag.bit"
 fi
 
 set +e
binary files /dev/null b/vectors/l3-nonstandard-sin1k0db_lame_vbrtag.bit differ
binary files /dev/null b/vectors/l3-nonstandard-sin1k0db_lame_vbrtag.pcm differ
binary files a/vectors/vbrtag/l3-nonstandard-sin1k0db_lame_tag.bit /dev/null differ
binary files a/vectors/vbrtag/l3-nonstandard-sin1k0db_lame_tag.pcm /dev/null differ