From c24d914127635cf57389e32b25eb2e92062e6b29 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 23 Mar 2025 20:02:58 -0700 Subject: [PATCH] minimp3: Numerous buffering fixes, consolidation Move minimp3 packet decoder state into the decoder_ex state structure, instead of using the redundant duplicate structure. Also reduce input buffer size for streams to 16KiB, and actually use the defined macro in the header file to declare the streaming buffer size. Signed-off-by: Christopher Snowhill --- Plugins/minimp3/MP3Decoder.h | 5 ++--- Plugins/minimp3/MP3Decoder.m | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Plugins/minimp3/MP3Decoder.h b/Plugins/minimp3/MP3Decoder.h index 686120b3c..b6051f6a6 100644 --- a/Plugins/minimp3/MP3Decoder.h +++ b/Plugins/minimp3/MP3Decoder.h @@ -15,12 +15,11 @@ #import "Plugin.h" -#define INPUT_BUFFER_SIZE 5 * 8192 +#define INPUT_BUFFER_SIZE 16 * 1024 @interface MP3Decoder : NSObject { BOOL seekable; - mp3dec_t _decoder; - unsigned char _decoder_buffer[32768]; + unsigned char _decoder_buffer[INPUT_BUFFER_SIZE]; size_t _decoder_buffer_filled; mp3dec_ex_t _decoder_ex; diff --git a/Plugins/minimp3/MP3Decoder.m b/Plugins/minimp3/MP3Decoder.m index d2f245833..5c82fc42d 100644 --- a/Plugins/minimp3/MP3Decoder.m +++ b/Plugins/minimp3/MP3Decoder.m @@ -38,6 +38,8 @@ static int mp3_seek_callback(uint64_t position, void *user_data) { size_t id3_length = 0; if(seekable) { + // minimp3 already skips ID3v2, but we need to supplement it with support for + // iTunes gapless info, which is stored in the ID3v2 tag uint8_t buffer[10]; size_t buflen = [source read:&buffer[0] amount:10]; @@ -137,12 +139,14 @@ static int mp3_seek_callback(uint64_t position, void *user_data) { if(error) return NO; if(_foundiTunSMPB) { + // start_delay is used for seeking, to_skip must be filled for the first packet decoded + // and detected_samples will truncate the end padding _decoder_ex.start_delay = _decoder_ex.to_skip = _startPadding * _decoder_ex.info.channels; _decoder_ex.detected_samples = totalFrames * _decoder_ex.info.channels; _decoder_ex.samples = (totalFrames + _startPadding + _endPadding) * _decoder_ex.info.channels; } mp3d_sample_t *sample_ptr = NULL; - size_t samples = mp3dec_ex_read_frame(&_decoder_ex, &sample_ptr, &_decoder_info, 1152 * 2); + size_t samples = mp3dec_ex_read_frame(&_decoder_ex, &sample_ptr, &_decoder_info, MINIMP3_MAX_SAMPLES_PER_FRAME); if(samples && sample_ptr) { samples_filled = samples / _decoder_info.channels; memcpy(&_decoder_buffer_output[0], sample_ptr, sizeof(mp3d_sample_t) * samples); @@ -157,10 +161,10 @@ static int mp3_seek_callback(uint64_t position, void *user_data) { } bitrate = (double)((_fileSize - id3_length) * 8) / ((double)totalFrames / (double)_decoder_info.hz) / 1000.0; } else { - _decoder_buffer_filled = [source read:_decoder_buffer amount:32768]; - inputEOF = _decoder_buffer_filled < 32768; - mp3dec_init(&_decoder); - int samples = mp3dec_decode_frame(&_decoder, _decoder_buffer, (int)_decoder_buffer_filled, &_decoder_buffer_output[0], &_decoder_info); + _decoder_buffer_filled = [source read:_decoder_buffer amount:INPUT_BUFFER_SIZE]; + inputEOF = _decoder_buffer_filled < INPUT_BUFFER_SIZE; + mp3dec_init(&_decoder_ex.mp3d); + int samples = mp3dec_decode_frame(&_decoder_ex.mp3d, _decoder_buffer, (int)_decoder_buffer_filled, &_decoder_buffer_output[0], &_decoder_info); if(!samples) return NO; size_t bytes_read = _decoder_info.frame_bytes; @@ -231,7 +235,7 @@ static int mp3_seek_callback(uint64_t position, void *user_data) { if(seekable) { mp3d_sample_t *sample_ptr = NULL; - size_t samples = mp3dec_ex_read_frame(&_decoder_ex, &sample_ptr, &_decoder_info, 1152 * 2); + size_t samples = mp3dec_ex_read_frame(&_decoder_ex, &sample_ptr, &_decoder_info, MINIMP3_MAX_SAMPLES_PER_FRAME); if(samples && sample_ptr) { samples_filled = samples / _decoder_info.channels; memcpy(&_decoder_buffer_output[0], sample_ptr, sizeof(mp3d_sample_t) * samples); @@ -239,7 +243,7 @@ static int mp3_seek_callback(uint64_t position, void *user_data) { inputEOF = YES; } } else { - size_t bytesRemain = 32768 - _decoder_buffer_filled; + size_t bytesRemain = INPUT_BUFFER_SIZE - _decoder_buffer_filled; ssize_t bytesRead = [_source read:&_decoder_buffer[_decoder_buffer_filled] amount:bytesRemain]; if(bytesRead < 0 || bytesRead < bytesRemain) { inputEOF = YES; @@ -247,7 +251,7 @@ static int mp3_seek_callback(uint64_t position, void *user_data) { if(bytesRead > 0) { _decoder_buffer_filled += bytesRead; } - int samples = mp3dec_decode_frame(&_decoder, &_decoder_buffer[0], (int)_decoder_buffer_filled, &_decoder_buffer_output[0], &_decoder_info); + int samples = mp3dec_decode_frame(&_decoder_ex.mp3d, &_decoder_buffer[0], (int)_decoder_buffer_filled, &_decoder_buffer_output[0], &_decoder_info); if(samples > 0) { samples_filled = samples; } else {