shithub: mp3dec

Download patch

ref: ea70d0b775b2d589d1697d1e1dfbc744ecab814e
parent: 682006f91a95058ad3927b46c7d99df1d674d3e1
author: lieff <lieff@users.noreply.github.com>
date: Wed Feb 12 20:59:36 EST 2020

mp3dec_ex: implement mp3dec_load_cb, improve test

--- a/minimp3_ex.h
+++ b/minimp3_ex.h
@@ -81,7 +81,7 @@
 #endif
 
 /* decode whole buffer block */
-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);
+int 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);
 int mp3dec_load_cb(mp3dec_t *dec, mp3dec_io_t *io, uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);
 /* iterate through frames */
 size_t mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data);
@@ -134,8 +134,9 @@
 
 static size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size)
 {
+#define MINIMP3_ID3_DETECT_SIZE 10
 #ifndef MINIMP3_NOSKIP_ID3V2
-    if (buf_size >= 10 && !memcmp(buf, "ID3", 3) && !((buf[5] & 15) || (buf[6] & 0x80) || (buf[7] & 0x80) || (buf[8] & 0x80) || (buf[9] & 0x80)))
+    if (buf_size >= MINIMP3_ID3_DETECT_SIZE && !memcmp(buf, "ID3", 3) && !((buf[5] & 15) || (buf[6] & 0x80) || (buf[7] & 0x80) || (buf[8] & 0x80) || (buf[9] & 0x80)))
     {
         size_t id3v2size = (((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f)) + 10;
         if ((buf[5] & 16))
@@ -210,8 +211,13 @@
     return 1;
 }
 
-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)
+int 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)
 {
+    return mp3dec_load_cb(dec, 0, (uint8_t *)buf, buf_size, info, progress_cb, user_data);
+}
+
+int mp3dec_load_cb(mp3dec_t *dec, mp3dec_io_t *io, 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;
     int to_skip = 0;
@@ -218,10 +224,31 @@
     mp3dec_frame_info_t frame_info;
     memset(info, 0, sizeof(*info));
     memset(&frame_info, 0, sizeof(frame_info));
+
     /* skip id3 */
-    mp3dec_skip_id3(&buf, &buf_size);
-    if (!buf_size)
-        return;
+    size_t filled, consumed;
+    int eof;
+    if (io)
+    {
+        io->seek(0, io->seek_data);
+        filled = io->read(buf, MINIMP3_ID3_DETECT_SIZE, io->read_data), consumed = 0, eof = 0;
+        if (MINIMP3_ID3_DETECT_SIZE != filled)
+            return 0;
+        size_t id3v2size = mp3dec_skip_id3v2(buf, filled);
+        if (id3v2size)
+        {
+            io->seek(id3v2size, io->seek_data);
+            filled = io->read(buf, buf_size, io->read_data);
+        } else
+            filled += io->read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io->read_data);
+        if (filled < MINIMP3_BUF_SIZE)
+            mp3dec_skip_id3v1(buf, &filled);
+    } else
+    {
+        mp3dec_skip_id3((const uint8_t **)&buf, &buf_size);
+        if (!buf_size)
+            return 0;
+    }
     /* try to make allocation size assumption by first frame or vbr tag */
     mp3dec_init(dec);
     int samples;
@@ -228,14 +255,33 @@
     do
     {
         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;
+        int i, delay, padding, free_format_bytes = 0, frame_size = 0;
+        if (io)
+        {
+            if (!eof && filled - consumed < MINIMP3_BUF_SIZE)
+            {   /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */
+                memmove(buf, buf + consumed, filled - consumed);
+                filled -= consumed;
+                consumed = 0;
+                size_t readed = io->read(buf + filled, buf_size - filled, io->read_data);
+                if (readed != (buf_size - filled))
+                    eof = 1;
+                filled += readed;
+                if (eof)
+                    mp3dec_skip_id3v1(buf, &filled);
+            }
+            i = mp3d_find_frame(buf + consumed, filled - consumed, &free_format_bytes, &frame_size);
+            consumed += i;
+        } else
+        {
+            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;
+            return 0;
         const uint8_t *hdr = buf;
         frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2;
         frame_info.hz = hdr_sample_rate_hz(hdr);
@@ -253,9 +299,15 @@
             if (padding > 0 && detected_samples >= (uint64_t)padding)
                 detected_samples -= padding;
             if (!detected_samples)
-                return;
-            buf      += frame_size;
-            buf_size -= frame_size;
+                return 0;
+            if (io)
+            {
+                consumed += frame_size;
+            } else
+            {
+                buf      += frame_size;
+                buf_size -= frame_size;
+            }
         }
         break;
     } while(1);
@@ -266,7 +318,7 @@
         allocated += (buf_size/frame_info.frame_bytes)*samples*sizeof(mp3d_sample_t);
     info->buffer = (mp3d_sample_t*)malloc(allocated);
     if (!info->buffer)
-        return;
+        return MP3D_E_MEMORY;
     /* save info */
     info->channels = frame_info.channels;
     info->hz       = frame_info.hz;
@@ -273,7 +325,6 @@
     info->layer    = frame_info.layer;
     /* decode all frames */
     size_t avg_bitrate_kbps = 0, frames = 0;
-    int frame_bytes;
     do
     {
         if ((allocated - info->samples*sizeof(mp3d_sample_t)) < MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t))
@@ -281,10 +332,28 @@
             allocated *= 2;
             info->buffer = (mp3d_sample_t*)realloc(info->buffer, allocated);
         }
-        samples = mp3dec_decode_frame(dec, buf, MINIMP3_MIN(buf_size, (size_t)INT_MAX), info->buffer + info->samples, &frame_info);
-        frame_bytes = frame_info.frame_bytes;
-        buf      += frame_bytes;
-        buf_size -= frame_bytes;
+        if (io)
+        {
+            if (!eof && filled - consumed < MINIMP3_BUF_SIZE)
+            {   /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */
+                memmove(buf, buf + consumed, filled - consumed);
+                filled -= consumed;
+                consumed = 0;
+                size_t readed = io->read(buf + filled, buf_size - filled, io->read_data);
+                if (readed != (buf_size - filled))
+                    eof = 1;
+                filled += readed;
+                if (eof)
+                    mp3dec_skip_id3v1(buf, &filled);
+            }
+            samples = mp3dec_decode_frame(dec, buf + consumed, filled - consumed, info->buffer + info->samples, &frame_info);
+            consumed += frame_info.frame_bytes;
+        } else
+        {
+            samples = mp3dec_decode_frame(dec, buf, MINIMP3_MIN(buf_size, (size_t)INT_MAX), info->buffer + info->samples, &frame_info);
+            buf      += frame_info.frame_bytes;
+            buf_size -= frame_info.frame_bytes;
+        }
         if (samples)
         {
             if (info->hz != frame_info.hz || info->layer != frame_info.layer)
@@ -309,7 +378,7 @@
             if (progress_cb)
                 progress_cb(user_data, orig_buf_size, orig_buf_size - buf_size, &frame_info);
         }
-    } while (frame_bytes);
+    } while (frame_info.frame_bytes);
     if (detected_samples && info->samples > detected_samples)
         info->samples = detected_samples; /* cut padding */
     /* reallocate to normal buffer size */
@@ -317,6 +386,7 @@
         info->buffer = (mp3d_sample_t*)realloc(info->buffer, info->samples*sizeof(mp3d_sample_t));
     if (frames)
         info->avg_bitrate_kbps = avg_bitrate_kbps/frames;
+    return 0;
 }
 
 size_t mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data)
@@ -363,11 +433,13 @@
     if (buf_size < MINIMP3_BUF_SIZE)
         return 0;
     io->seek(0, io->seek_data);
-    size_t filled = io->read(buf, 10, io->read_data), consumed = 0;
+    size_t filled = io->read(buf, MINIMP3_ID3_DETECT_SIZE, io->read_data), consumed = 0;
     uint64_t readed = 0, frames = 0;
     mp3dec_frame_info_t frame_info;
     int eof = 0;
     memset(&frame_info, 0, sizeof(frame_info));
+    if (MINIMP3_ID3_DETECT_SIZE != filled)
+        return 0;
     size_t id3v2size = mp3dec_skip_id3v2(buf, filled);
     if (id3v2size)
     {
@@ -375,7 +447,7 @@
         filled = io->read(buf, buf_size, io->read_data);
         readed += id3v2size;
     } else
-        filled += io->read(buf + 10, buf_size - 10, io->read_data);
+        filled += io->read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io->read_data);
     if (filled < MINIMP3_BUF_SIZE)
         mp3dec_skip_id3v1(buf, &filled);
     do
--- a/minimp3_test.c
+++ b/minimp3_test.c
@@ -14,10 +14,15 @@
     #include <strings.h>
 #endif
 
-#define MODE_LOAD    0
-#define MODE_ITERATE 1
-#define MODE_STREAM  2
-#define MODE_STREAM_CB 3
+#define MODE_LOAD     0
+#define MODE_LOAD_BUF 1
+#define MODE_LOAD_CB  2
+#define MODE_ITERATE  3
+#define MODE_ITERATE_BUF 4
+#define MODE_ITERATE_CB  5
+#define MODE_STREAM     6
+#define MODE_STREAM_BUF 7
+#define MODE_STREAM_CB  8
 
 static int16_t read16le(const void *p)
 {
@@ -115,31 +120,75 @@
     int no_std_vec = strstr(input_file_name, "nonstandard") || strstr(input_file_name, "ILL");
     double MSE = 0.0, psnr;
 
+    mp3dec_io_t io;
     mp3dec_file_info_t info;
     memset(&info, 0, sizeof(info));
+    io.read = read_cb;
+    io.seek = seek_cb;
     if (MODE_LOAD == mode)
     {
         res = mp3dec_load(&mp3d, input_file_name, &info, 0, 0);
+    } else if (MODE_LOAD_BUF == mode)
+    {
+        int size = 0;
+        FILE *file = fopen(input_file_name, "rb");
+        uint8_t *buf = preload(file, &size);
+        fclose(file);
+        res = buf ? mp3dec_load_buf(&mp3d, buf, size, &info, 0, 0) : -1;
+        free(buf);
+    } else if (MODE_LOAD_CB == mode)
+    {
+        uint8_t *io_buf = malloc(MINIMP3_IO_SIZE);
+        FILE *file = fopen(input_file_name, "rb");
+        io.read_data = io.seek_data = file;
+        res = file ? mp3dec_load_cb(&mp3d, &io, io_buf, MINIMP3_IO_SIZE, &info, 0, 0) : -1;
+        fclose((FILE*)io.read_data);
+        free(io_buf);
     } else if (MODE_ITERATE == mode)
     {
         frames_iterate_data d = { &mp3d, &info, 0 };
         mp3dec_init(&mp3d);
         res = mp3dec_iterate(input_file_name, frames_iterate_cb, &d) > 0 ? 0 : -1;
-    } else if (MODE_STREAM == mode || MODE_STREAM_CB == mode)
+    } else if (MODE_ITERATE_BUF == mode)
     {
+        int size = 0;
+        FILE *file = fopen(input_file_name, "rb");
+        uint8_t *buf = preload(file, &size);
+        fclose(file);
+        frames_iterate_data d = { &mp3d, &info, 0 };
+        mp3dec_init(&mp3d);
+        res = mp3dec_iterate_buf(buf, size, frames_iterate_cb, &d) > 0 ? 0 : -1;
+        free(buf);
+    } else if (MODE_ITERATE_CB == mode)
+    {
+        uint8_t *io_buf = malloc(MINIMP3_IO_SIZE);
+        FILE *file = fopen(input_file_name, "rb");
+        io.read_data = io.seek_data = file;
+        frames_iterate_data d = { &mp3d, &info, 0 };
+        mp3dec_init(&mp3d);
+        res = mp3dec_iterate_cb(&io, io_buf, MINIMP3_IO_SIZE, frames_iterate_cb, &d) > 0 ? 0 : -1;
+        fclose((FILE*)io.read_data);
+        free(io_buf);
+    } else if (MODE_STREAM == mode || MODE_STREAM_BUF == mode || MODE_STREAM_CB == mode)
+    {
         mp3dec_ex_t dec;
-        mp3dec_io_t io;
         size_t readed;
-        if (MODE_STREAM_CB == mode)
+        uint8_t *buf;
+        if (MODE_STREAM == mode)
         {
+            res = mp3dec_ex_open(&dec, input_file_name, MP3D_SEEK_TO_SAMPLE);
+        } else if (MODE_STREAM_BUF == mode)
+        {
+            int size = 0;
             FILE *file = fopen(input_file_name, "rb");
-            io.read = read_cb;
-            io.seek = seek_cb;
+            buf = preload(file, &size);
+            fclose(file);
+            res = mp3dec_ex_open_buf(&dec, buf, size, MP3D_SEEK_TO_SAMPLE);
+        } else if (MODE_STREAM_CB == mode)
+        {
+            FILE *file = fopen(input_file_name, "rb");
             io.read_data = io.seek_data = file;
             res = file ? mp3dec_ex_open_cb(&dec, &io, MP3D_SEEK_TO_SAMPLE) : -1;
-        } else
-        {
-            res = mp3dec_ex_open(&dec, input_file_name, MP3D_SEEK_TO_SAMPLE);
         }
         if (res)
         {
@@ -184,6 +233,8 @@
             exit(1);
         }
         mp3dec_ex_close(&dec);
+        if (MODE_STREAM_BUF == mode)
+            free(buf);
         if (MODE_STREAM_CB == mode)
             fclose((FILE*)io.read_data);
     } else
@@ -217,7 +268,7 @@
         int len_match = ref_samples == info.samples;
         int relaxed_len_match = len_match || (ref_samples + 1152) == info.samples || (ref_samples + 2304) == info.samples;
         int seek_len_match = (ref_samples <= info.samples) || (ref_samples + 2304) >= info.samples;
-        if ((((!relaxed_len_match && 2 != mode && 3 != mode) || !seek_len_match) && 3 == info.layer && !no_std_vec) || (no_std_vec && !len_match))
+        if ((((!relaxed_len_match && MODE_STREAM != mode && MODE_STREAM_BUF != mode && MODE_STREAM_CB != mode) || !seek_len_match) && 3 == info.layer && !no_std_vec) || (no_std_vec && !len_match))
         {   /* some standard vectors are for some reason a little shorter */
             printf("error: reference and produced number of samples do not match (%d/%d)\n", (int)ref_samples, (int)info.samples);
             exit(1);
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -11,17 +11,22 @@
 
 echo testing mp4 mode...
 gcc $CFLAGS -DMP4_MODE -o minimp3 minimp3_test.c -lm
-scripts/test_mode.sh 1 0
+scripts/test_mode.sh 3 0
 
 echo testing stream mode...
-scripts/test_mode.sh 2 -1
+scripts/test_mode.sh 6 -1
 
 echo testing coverage x86 w sse...
 gcc -coverage -O0 -m32 -std=c89 -msse2 -DMINIMP3_TEST -DMINIMP3_NO_WAV -o minimp3 minimp3_test.c -lm
 scripts/test.sh
 scripts/test_mode.sh 1 0
-scripts/test_mode.sh 2 -1
-scripts/test_mode.sh 3 -1
+scripts/test_mode.sh 2 0
+scripts/test_mode.sh 3 0
+scripts/test_mode.sh 4 0
+scripts/test_mode.sh 5 0
+scripts/test_mode.sh 6 -1
+scripts/test_mode.sh 7 -1
+scripts/test_mode.sh 8 -1
 set +e
 ./minimp3
 ./minimp3 do_not_exist
@@ -40,8 +45,13 @@
 gcc $CFLAGS -DMINIMP3_FLOAT_OUTPUT -o minimp3 minimp3_test.c -lm
 scripts/test.sh
 scripts/test_mode.sh 1 0
-scripts/test_mode.sh 2 -1
-scripts/test_mode.sh 3 -1
+scripts/test_mode.sh 2 0
+scripts/test_mode.sh 3 0
+scripts/test_mode.sh 4 0
+scripts/test_mode.sh 5 0
+scripts/test_mode.sh 6 -1
+scripts/test_mode.sh 7 -1
+scripts/test_mode.sh 8 -1
 
 echo testing arm w/o neon...
 arm-none-eabi-gcc $CFLAGS -mthumb -mcpu=arm9e -o minimp3_arm minimp3_test.c --specs=rdimon.specs -lm