ref: 8b79f06f1b08e0a93cdc23f95e84cf272c442567
parent: cf038fa3dfdb0f469fd23795939009bf3c354d4a
author: lieff <lieff@users.noreply.github.com>
date: Mon Jan 20 18:39:29 EST 2020
finish mp3dec_ex_* streaming API.
--- a/minimp3_ex.h
+++ b/minimp3_ex.h
@@ -10,8 +10,9 @@
#define MP3D_SEEK_TO_BYTE 0
#define MP3D_SEEK_TO_SAMPLE 1
-#define MP3D_SEEK_TO_SAMPLE_INDEXED 2
+#define MP3D_E_MEMORY -1
+
typedef struct
{
mp3d_sample_t *buffer;
@@ -27,12 +28,31 @@
typedef struct
{
+ uint64_t sample;
+ uint64_t offset;
+} mp3dec_frame_t;
+
+typedef struct
+{
+ mp3dec_frame_t *frames;
+ size_t num_frames, capacity;
+} mp3dec_index_t;
+
+/*typedef int (*MP3D_READ_CB)(void *buf, size_t size, void *user_data);*/
+
+typedef struct
+{
mp3dec_t mp3d;
mp3dec_map_info_t file;
- int seek_method;
+ mp3dec_index_t index;
+ uint64_t offset, samples;
+ mp3dec_frame_info_t info;
+ mp3d_sample_t buffer[MINIMP3_MAX_SAMPLES_PER_FRAME];
#ifndef MINIMP3_NO_STDIO
int is_file;
#endif
+ int seek_method;
+ int buffer_samples, buffer_consumed, to_skip;
} mp3dec_ex_t;
typedef int (*MP3D_ITERATE_CB)(void *user_data, const uint8_t *frame, int frame_size, size_t offset, mp3dec_frame_info_t *info);
@@ -44,13 +64,14 @@
/* 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);
-/* iterate through frames with optional decoding */
-void mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data);
-/* decoder with seeking capability */
+/* iterate through frames */
+size_t mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data);
+/* streaming decoder with seeking capability */
int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int seek_method);
+/*int mp3dec_ex_open_cb(mp3dec_ex_t *dec, MP3D_READ_CB cb, void *user_data, uint64_t file_size, int seek_method);*/
void mp3dec_ex_close(mp3dec_ex_t *dec);
-void mp3dec_ex_seek(mp3dec_ex_t *dec, size_t position);
-int mp3dec_ex_read(mp3dec_ex_t *dec, int16_t *buf, int samples);
+void mp3dec_ex_seek(mp3dec_ex_t *dec, uint64_t position);
+size_t mp3dec_ex_read(mp3dec_ex_t *dec, int16_t *buf, size_t samples);
#ifndef MINIMP3_NO_STDIO
/* stdio versions with file pre-load */
int mp3dec_load(mp3dec_t *dec, const char *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);
@@ -163,15 +184,14 @@
info->avg_bitrate_kbps = avg_bitrate_kbps/frames;
}
-void mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data)
+size_t mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data)
{
- if (!callback)
- return;
+ size_t frames = 0;
const uint8_t *orig_buf = buf;
/* skip id3 */
mp3dec_skip_id3(&buf, &buf_size);
if (!buf_size)
- return;
+ return 0;
mp3dec_frame_info_t frame_info;
memset(&frame_info, 0, sizeof(frame_info));
do
@@ -191,13 +211,42 @@
frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr);
frame_info.frame_bytes = frame_size;
- if (callback(user_data, hdr, frame_size, hdr - orig_buf, &frame_info))
- break;
+ frames++;
+ if (callback)
+ {
+ if (callback(user_data, hdr, frame_size, hdr - orig_buf, &frame_info))
+ break;
+ }
buf += frame_size;
buf_size -= frame_size;
} while (1);
+ return frames;
}
+static int mp3dec_load_index(void *user_data, const uint8_t *frame, int frame_size, size_t offset, mp3dec_frame_info_t *info)
+{
+ (void)frame_size;
+ mp3dec_frame_t *idx_frame;
+ mp3dec_ex_t *dec = (mp3dec_ex_t *)user_data;
+ if (dec->index.num_frames + 1 > dec->index.capacity)
+ {
+ if (!dec->index.capacity)
+ dec->index.capacity = 4096;
+ else
+ dec->index.capacity *= 2;
+ dec->index.frames = (mp3dec_frame_t *)realloc((void*)dec->index.frames, sizeof(mp3dec_frame_t)*dec->index.capacity);
+ if (!dec->index.frames)
+ return -1;
+ }
+ if (!dec->samples)
+ dec->info = *info;
+ idx_frame = &dec->index.frames[dec->index.num_frames++];
+ idx_frame->offset = offset;
+ idx_frame->sample = dec->samples;
+ dec->samples += hdr_frame_samples(frame)*info->channels;
+ return 0;
+}
+
int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int seek_method)
{
memset(dec, 0, sizeof(*dec));
@@ -205,17 +254,78 @@
dec->file.size = buf_size;
dec->seek_method = seek_method;
mp3dec_init(&dec->mp3d);
+ if (mp3dec_iterate_buf(dec->file.buffer, dec->file.size, mp3dec_load_index, dec) > 0 && !dec->index.frames)
+ return MP3D_E_MEMORY;
return 0;
}
-/*void mp3dec_ex_seek(mp3dec_ex_t *dec, size_t position)
+void mp3dec_ex_seek(mp3dec_ex_t *dec, uint64_t position)
{
+ size_t i;
+ if (MP3D_SEEK_TO_BYTE == dec->seek_method)
+ {
+ dec->offset = position;
+ return;
+ }
+ for (i = 0; i < dec->index.num_frames; i++)
+ {
+ if (dec->index.frames[i].sample > position)
+ break;
+ }
+ if (i)
+ i--;
+ dec->offset = dec->index.frames[i].offset;
+ dec->to_skip = position - dec->index.frames[i].sample;
+ dec->buffer_samples = 0;
+ dec->buffer_consumed = 0;
+ mp3dec_init(&dec->mp3d);
}
-int mp3dec_ex_read(mp3dec_ex_t *dec, int16_t *buf, int samples)
+size_t mp3dec_ex_read(mp3dec_ex_t *dec, int16_t *buf, size_t samples)
{
- return 0;
-}*/
+ size_t samples_requested = samples;
+ mp3dec_frame_info_t frame_info;
+ memset(&frame_info, 0, sizeof(frame_info));
+ if (dec->buffer_consumed < dec->buffer_samples)
+ {
+ size_t to_copy = MINIMP3_MIN((size_t)(dec->buffer_samples - dec->buffer_consumed), samples);
+ memcpy(buf, dec->buffer + dec->buffer_consumed, to_copy*sizeof(int16_t));
+ buf += to_copy;
+ dec->buffer_consumed += to_copy;
+ samples -= to_copy;
+ }
+ while (samples)
+ {
+ const uint8_t *dec_buf = dec->file.buffer + dec->offset;
+ size_t buf_size = dec->file.size - dec->offset;
+ if (!buf_size)
+ break;
+ dec->buffer_samples = mp3dec_decode_frame(&dec->mp3d, dec_buf, buf_size, dec->buffer, &frame_info);
+ dec->buffer_consumed = 0;
+ if (dec->buffer_samples)
+ {
+ dec->buffer_samples *= frame_info.channels;
+ if (dec->to_skip)
+ {
+ size_t skip = MINIMP3_MIN(dec->buffer_samples, dec->to_skip);
+ dec->buffer_consumed += skip;
+ dec->to_skip -= skip;
+ }
+ size_t to_copy = MINIMP3_MIN((size_t)(dec->buffer_samples - dec->buffer_consumed), samples);
+ memcpy(buf, dec->buffer + dec->buffer_consumed, to_copy*sizeof(int16_t));
+ buf += to_copy;
+ dec->buffer_consumed += to_copy;
+ samples -= to_copy;
+ } else if (dec->to_skip)
+ { /* In mp3 decoding not always can start decode from any frame because of bit reservoir,
+ count skip samples for such frames */
+ int frame_samples = hdr_frame_samples(dec_buf)*frame_info.channels;
+ dec->to_skip -= MINIMP3_MIN(frame_samples, dec->to_skip);
+ }
+ dec->offset += frame_info.frame_bytes;
+ }
+ return samples_requested - samples;
+}
#ifndef MINIMP3_NO_STDIO
@@ -371,8 +481,8 @@
{
if (dec->is_file)
mp3dec_close_file(&dec->file);
- else
- free((void *)dec->file.buffer);
+ if (dec->index.frames)
+ free(dec->index.frames);
memset(dec, 0, sizeof(*dec));
}
@@ -379,18 +489,17 @@
int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int seek_method)
{
int ret;
- memset(dec, 0, sizeof(*dec));
if ((ret = mp3dec_open_file(file_name, &dec->file)))
return ret;
- dec->seek_method = seek_method;
+ ret = mp3dec_ex_open_buf(dec, dec->file.buffer, dec->file.size, seek_method);
dec->is_file = 1;
- mp3dec_init(&dec->mp3d);
- return 0;
+ return ret;
}
-#else
+#else /* MINIMP3_NO_STDIO */
void mp3dec_ex_close(mp3dec_ex_t *dec)
{
- free((void*)dec->file.buffer);
+ if (dec->index.frames)
+ free(dec->index.frames);
memset(dec, 0, sizeof(*dec));
}
#endif