2020-11-16 14:47:43 +08:00
# ifndef MINIMP3_EXT_H
# define MINIMP3_EXT_H
/*
https : //github.com/lieff/minimp3
To the extent possible under law , the author ( s ) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide .
This software is distributed without any warranty .
See < http : //creativecommons.org/publicdomain/zero/1.0/>.
*/
# include "minimp3.h"
/* flags for mp3dec_ex_open_* functions */
# define MP3D_SEEK_TO_BYTE 0 /* mp3dec_ex_seek seeks to byte in stream */
# define MP3D_SEEK_TO_SAMPLE 1 /* mp3dec_ex_seek precisely seeks to sample using index (created during duration calculation scan or when mp3dec_ex_seek called) */
# define MP3D_DO_NOT_SCAN 2 /* do not scan whole stream for duration if vbrtag not found, mp3dec_ex_t::samples will be filled only if mp3dec_ex_t::vbr_tag_found == 1 */
# ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION
# define MP3D_ALLOW_MONO_STEREO_TRANSITION 4
# define MP3D_FLAGS_MASK 7
# else
# define MP3D_FLAGS_MASK 3
# endif
/* compile-time config */
# define MINIMP3_PREDECODE_FRAMES 2 /* frames to pre-decode and skip after seek (to fill internal structures) */
/*#define MINIMP3_SEEK_IDX_LINEAR_SEARCH*/ /* define to use linear index search instead of binary search on seek */
# define MINIMP3_IO_SIZE (128*1024) /* io buffer size for streaming functions, must be greater than MINIMP3_BUF_SIZE */
# define MINIMP3_BUF_SIZE (16*1024) /* buffer which can hold minimum 10 consecutive mp3 frames (~16KB) worst case */
/*#define MINIMP3_SCAN_LIMIT (256*1024)*/ /* how many bytes will be scanned to search first valid mp3 frame, to prevent stall on large non-mp3 files */
# define MINIMP3_ENABLE_RING 0 /* WIP enable hardware magic ring buffer if available, to make less input buffer memmove(s) in callback IO mode */
/* return error codes */
# define MP3D_E_PARAM -1
# define MP3D_E_MEMORY -2
# define MP3D_E_IOERROR -3
# define MP3D_E_USER -4 /* can be used to stop processing from callbacks without indicating specific error */
# define MP3D_E_DECODE -5 /* decode error which can't be safely skipped, such as sample rate, layer and channels change */
typedef struct
{
mp3d_sample_t * buffer ;
size_t samples ; /* channels included, byte size = samples*sizeof(mp3d_sample_t) */
int channels , hz , layer , avg_bitrate_kbps ;
} mp3dec_file_info_t ;
typedef struct
{
const uint8_t * buffer ;
size_t size ;
} mp3dec_map_info_t ;
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 size_t ( * MP3D_READ_CB ) ( void * buf , size_t size , void * user_data ) ;
typedef int ( * MP3D_SEEK_CB ) ( uint64_t position , void * user_data ) ;
typedef struct
{
MP3D_READ_CB read ;
void * read_data ;
MP3D_SEEK_CB seek ;
void * seek_data ;
} mp3dec_io_t ;
typedef struct
{
mp3dec_t mp3d ;
mp3dec_map_info_t file ;
mp3dec_io_t * io ;
mp3dec_index_t index ;
uint64_t offset , samples , detected_samples , cur_sample , start_offset , end_offset ;
mp3dec_frame_info_t info ;
mp3d_sample_t buffer [ MINIMP3_MAX_SAMPLES_PER_FRAME ] ;
size_t input_consumed , input_filled ;
int is_file , flags , vbr_tag_found , indexes_built ;
int free_format_bytes ;
int buffer_samples , buffer_consumed , to_skip , start_delay ;
int last_error ;
} mp3dec_ex_t ;
typedef int ( * MP3D_ITERATE_CB ) ( void * user_data , const uint8_t * frame , int frame_size , int free_format_bytes , size_t buf_size , uint64_t offset , mp3dec_frame_info_t * info ) ;
typedef int ( * MP3D_PROGRESS_CB ) ( void * user_data , size_t file_size , uint64_t offset , mp3dec_frame_info_t * info ) ;
# ifdef __cplusplus
extern " C " {
# endif
/* detect mp3/mpa format */
int mp3dec_detect_buf ( const uint8_t * buf , size_t buf_size ) ;
int mp3dec_detect_cb ( mp3dec_io_t * io , uint8_t * buf , size_t buf_size ) ;
/* decode whole buffer block */
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 */
int mp3dec_iterate_buf ( const uint8_t * buf , size_t buf_size , MP3D_ITERATE_CB callback , void * user_data ) ;
int mp3dec_iterate_cb ( mp3dec_io_t * io , 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 flags ) ;
int mp3dec_ex_open_cb ( mp3dec_ex_t * dec , mp3dec_io_t * io , int flags ) ;
void mp3dec_ex_close ( mp3dec_ex_t * dec ) ;
int mp3dec_ex_seek ( mp3dec_ex_t * dec , uint64_t position ) ;
size_t mp3dec_ex_read_frame ( mp3dec_ex_t * dec , mp3d_sample_t * * buf , mp3dec_frame_info_t * frame_info , size_t max_samples ) ;
size_t mp3dec_ex_read ( mp3dec_ex_t * dec , mp3d_sample_t * buf , size_t samples ) ;
# ifndef MINIMP3_NO_STDIO
/* stdio versions of file detect, load, iterate and stream */
int mp3dec_detect ( const char * file_name ) ;
int mp3dec_load ( mp3dec_t * dec , const char * file_name , mp3dec_file_info_t * info , MP3D_PROGRESS_CB progress_cb , void * user_data ) ;
int mp3dec_iterate ( const char * file_name , MP3D_ITERATE_CB callback , void * user_data ) ;
int mp3dec_ex_open ( mp3dec_ex_t * dec , const char * file_name , int flags ) ;
# ifdef _WIN32
int mp3dec_detect_w ( const wchar_t * file_name ) ;
int mp3dec_load_w ( mp3dec_t * dec , const wchar_t * file_name , mp3dec_file_info_t * info , MP3D_PROGRESS_CB progress_cb , void * user_data ) ;
int mp3dec_iterate_w ( const wchar_t * file_name , MP3D_ITERATE_CB callback , void * user_data ) ;
int mp3dec_ex_open_w ( mp3dec_ex_t * dec , const wchar_t * file_name , int flags ) ;
# endif
# endif
# ifdef __cplusplus
}
# endif
# endif /*MINIMP3_EXT_H*/
# ifdef MINIMP3_IMPLEMENTATION
# include <limits.h>
static void mp3dec_skip_id3v1 ( const uint8_t * buf , size_t * pbuf_size )
{
size_t buf_size = * pbuf_size ;
# ifndef MINIMP3_NOSKIP_ID3V1
if ( buf_size > = 128 & & ! memcmp ( buf + buf_size - 128 , " TAG " , 3 ) )
{
buf_size - = 128 ;
if ( buf_size > = 227 & & ! memcmp ( buf + buf_size - 227 , " TAG+ " , 4 ) )
buf_size - = 227 ;
}
# endif
# ifndef MINIMP3_NOSKIP_APEV2
if ( buf_size > 32 & & ! memcmp ( buf + buf_size - 32 , " APETAGEX " , 8 ) )
{
buf_size - = 32 ;
const uint8_t * tag = buf + buf_size + 8 + 4 ;
uint32_t tag_size = ( uint32_t ) ( tag [ 3 ] < < 24 ) | ( tag [ 2 ] < < 16 ) | ( tag [ 1 ] < < 8 ) | tag [ 0 ] ;
if ( buf_size > = tag_size )
buf_size - = tag_size ;
}
# endif
* pbuf_size = buf_size ;
}
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 > = 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 ) )
id3v2size + = 10 ; /* footer */
return id3v2size ;
}
# endif
return 0 ;
}
static void mp3dec_skip_id3 ( const uint8_t * * pbuf , size_t * pbuf_size )
{
uint8_t * buf = ( uint8_t * ) ( * pbuf ) ;
size_t buf_size = * pbuf_size ;
size_t id3v2size = mp3dec_skip_id3v2 ( buf , buf_size ) ;
if ( id3v2size )
{
if ( id3v2size > = buf_size )
id3v2size = buf_size ;
buf + = id3v2size ;
buf_size - = id3v2size ;
}
mp3dec_skip_id3v1 ( buf , & buf_size ) ;
* pbuf = ( const uint8_t * ) buf ;
* pbuf_size = buf_size ;
}
static int mp3dec_check_vbrtag ( const uint8_t * frame , int frame_size , uint32_t * frames , int * delay , int * padding )
{
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
/* Side info offsets after header:
/ Mono Stereo
/ MPEG1 17 32
/ MPEG2 & 2.5 9 17 */
bs_t bs [ 1 ] ;
L3_gr_info_t gr_info [ 4 ] ;
bs_init ( bs , frame + HDR_SIZE , frame_size - HDR_SIZE ) ;
if ( HDR_IS_CRC ( frame ) )
get_bits ( bs , 16 ) ;
if ( L3_read_side_info ( bs , gr_info , frame ) < 0 )
return 0 ; /* side info corrupted */
const uint8_t * tag = frame + HDR_SIZE + bs - > pos / 8 ;
if ( memcmp ( g_xing_tag , tag , 4 ) & & memcmp ( g_info_tag , tag , 4 ) )
return 0 ;
int flags = tag [ 7 ] ;
if ( ! ( ( flags & FRAMES_FLAG ) ) )
return - 1 ;
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 ;
* delay = * padding = 0 ;
if ( * tag )
{ /* extension, LAME, Lavc, etc. Should be the same structure. */
tag + = 21 ;
if ( tag - frame + 14 > = frame_size )
return 0 ;
* delay = ( ( tag [ 0 ] < < 4 ) | ( tag [ 1 ] > > 4 ) ) + ( 528 + 1 ) ;
* padding = ( ( ( tag [ 1 ] & 0xF ) < < 8 ) | tag [ 2 ] ) - ( 528 + 1 ) ;
}
return 1 ;
}
int mp3dec_detect_buf ( const uint8_t * buf , size_t buf_size )
{
return mp3dec_detect_cb ( 0 , ( uint8_t * ) buf , buf_size ) ;
}
int mp3dec_detect_cb ( mp3dec_io_t * io , uint8_t * buf , size_t buf_size )
{
if ( ! buf | | ( size_t ) - 1 = = buf_size | | ( io & & buf_size < MINIMP3_BUF_SIZE ) )
return MP3D_E_PARAM ;
size_t filled = buf_size ;
if ( io )
{
if ( io - > seek ( 0 , io - > seek_data ) )
return MP3D_E_IOERROR ;
filled = io - > read ( buf , MINIMP3_ID3_DETECT_SIZE , io - > read_data ) ;
if ( filled > MINIMP3_ID3_DETECT_SIZE )
return MP3D_E_IOERROR ;
}
if ( filled < MINIMP3_ID3_DETECT_SIZE )
return MP3D_E_USER ; /* too small, can't be mp3/mpa */
if ( mp3dec_skip_id3v2 ( buf , filled ) )
return 0 ; /* id3v2 tag is enough evidence */
if ( io )
{
size_t readed = io - > read ( buf + MINIMP3_ID3_DETECT_SIZE , buf_size - MINIMP3_ID3_DETECT_SIZE , io - > read_data ) ;
if ( readed > ( buf_size - MINIMP3_ID3_DETECT_SIZE ) )
return MP3D_E_IOERROR ;
filled + = readed ;
if ( filled < MINIMP3_BUF_SIZE )
mp3dec_skip_id3v1 ( buf , & filled ) ;
} else
{
mp3dec_skip_id3v1 ( buf , & filled ) ;
if ( filled > MINIMP3_BUF_SIZE )
filled = MINIMP3_BUF_SIZE ;
}
int free_format_bytes , frame_size ;
mp3d_find_frame ( buf , filled , & free_format_bytes , & frame_size ) ;
if ( frame_size )
return 0 ; /* MAX_FRAME_SYNC_MATCHES consecutive frames found */
return MP3D_E_USER ;
}
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 )
{
if ( ! dec | | ! buf | | ! info | | ( size_t ) - 1 = = buf_size | | ( io & & buf_size < MINIMP3_BUF_SIZE ) )
return MP3D_E_PARAM ;
uint64_t detected_samples = 0 ;
size_t orig_buf_size = buf_size ;
int to_skip = 0 ;
mp3dec_frame_info_t frame_info ;
memset ( info , 0 , sizeof ( * info ) ) ;
memset ( & frame_info , 0 , sizeof ( frame_info ) ) ;
/* skip id3 */
size_t filled = 0 , consumed = 0 ;
int eof = 0 , ret = 0 ;
if ( io )
{
if ( io - > seek ( 0 , io - > seek_data ) )
return MP3D_E_IOERROR ;
filled = io - > read ( buf , MINIMP3_ID3_DETECT_SIZE , io - > read_data ) ;
if ( filled > MINIMP3_ID3_DETECT_SIZE )
return MP3D_E_IOERROR ;
if ( MINIMP3_ID3_DETECT_SIZE ! = filled )
return 0 ;
size_t id3v2size = mp3dec_skip_id3v2 ( buf , filled ) ;
if ( id3v2size )
{
if ( io - > seek ( id3v2size , io - > seek_data ) )
return MP3D_E_IOERROR ;
filled = io - > read ( buf , buf_size , io - > read_data ) ;
if ( filled > buf_size )
return MP3D_E_IOERROR ;
} else
{
size_t readed = io - > read ( buf + MINIMP3_ID3_DETECT_SIZE , buf_size - MINIMP3_ID3_DETECT_SIZE , io - > read_data ) ;
if ( readed > ( buf_size - MINIMP3_ID3_DETECT_SIZE ) )
return MP3D_E_IOERROR ;
filled + = readed ;
}
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 ;
do
{
uint32_t frames ;
int i , delay , padding , free_format_bytes = 0 , frame_size = 0 ;
const uint8_t * hdr ;
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 ) )
return MP3D_E_IOERROR ;
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 ;
hdr = buf + consumed ;
} else
{
i = mp3d_find_frame ( buf , buf_size , & free_format_bytes , & frame_size ) ;
buf + = i ;
buf_size - = i ;
hdr = buf ;
}
if ( i & & ! frame_size )
continue ;
if ( ! frame_size )
return 0 ;
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 ( 3 ! = frame_info . layer )
break ;
int ret = mp3dec_check_vbrtag ( hdr , frame_size , & frames , & delay , & padding ) ;
if ( ret > 0 )
{
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 0 ;
}
if ( ret )
{
if ( io )
{
consumed + = frame_size ;
} else
{
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 MP3D_E_MEMORY ;
/* save info */
info - > channels = frame_info . channels ;
info - > hz = frame_info . hz ;
info - > layer = frame_info . layer ;
/* decode all frames */
size_t avg_bitrate_kbps = 0 , frames = 0 ;
do
{
if ( ( allocated - info - > samples * sizeof ( mp3d_sample_t ) ) < MINIMP3_MAX_SAMPLES_PER_FRAME * sizeof ( mp3d_sample_t ) )
{
allocated * = 2 ;
mp3d_sample_t * alloc_buf = ( mp3d_sample_t * ) realloc ( info - > buffer , allocated ) ;
if ( ! alloc_buf )
return MP3D_E_MEMORY ;
info - > buffer = alloc_buf ;
}
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 )
{
ret = MP3D_E_DECODE ;
break ;
}
if ( info - > channels & & info - > channels ! = frame_info . channels )
{
# ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION
info - > channels = 0 ; /* mark file with mono-stereo transition */
# else
ret = MP3D_E_DECODE ;
break ;
# endif
}
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 * sizeof ( mp3d_sample_t ) ) ;
}
info - > samples + = samples ;
avg_bitrate_kbps + = frame_info . bitrate_kbps ;
frames + + ;
if ( progress_cb )
{
ret = progress_cb ( user_data , orig_buf_size , orig_buf_size - buf_size , & frame_info ) ;
if ( ret )
break ;
}
}
} while ( frame_info . frame_bytes ) ;
if ( detected_samples & & info - > samples > detected_samples )
info - > samples = detected_samples ; /* cut padding */
/* reallocate to normal buffer size */
if ( allocated ! = info - > samples * sizeof ( mp3d_sample_t ) )
{
mp3d_sample_t * alloc_buf = ( mp3d_sample_t * ) realloc ( info - > buffer , info - > samples * sizeof ( mp3d_sample_t ) ) ;
if ( ! alloc_buf & & info - > samples )
return MP3D_E_MEMORY ;
info - > buffer = alloc_buf ;
}
if ( frames )
info - > avg_bitrate_kbps = avg_bitrate_kbps / frames ;
return ret ;
}
int mp3dec_iterate_buf ( const uint8_t * buf , size_t buf_size , MP3D_ITERATE_CB callback , void * user_data )
{
const uint8_t * orig_buf = buf ;
if ( ! buf | | ( size_t ) - 1 = = buf_size | | ! callback )
return MP3D_E_PARAM ;
/* skip id3 */
mp3dec_skip_id3 ( & buf , & buf_size ) ;
if ( ! buf_size )
return 0 ;
mp3dec_frame_info_t frame_info ;
memset ( & frame_info , 0 , sizeof ( frame_info ) ) ;
do
{
int free_format_bytes = 0 , frame_size = 0 , ret ;
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 )
break ;
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 ;
if ( callback )
{
if ( ( ret = callback ( user_data , hdr , frame_size , free_format_bytes , buf_size , hdr - orig_buf , & frame_info ) ) )
return ret ;
}
buf + = frame_size ;
buf_size - = frame_size ;
} while ( 1 ) ;
return 0 ;
}
int mp3dec_iterate_cb ( mp3dec_io_t * io , uint8_t * buf , size_t buf_size , MP3D_ITERATE_CB callback , void * user_data )
{
if ( ! io | | ! buf | | ( size_t ) - 1 = = buf_size | | buf_size < MINIMP3_BUF_SIZE | | ! callback )
return MP3D_E_PARAM ;
size_t filled = io - > read ( buf , MINIMP3_ID3_DETECT_SIZE , io - > read_data ) , consumed = 0 ;
uint64_t readed = 0 ;
mp3dec_frame_info_t frame_info ;
int eof = 0 ;
memset ( & frame_info , 0 , sizeof ( frame_info ) ) ;
if ( filled > MINIMP3_ID3_DETECT_SIZE )
return MP3D_E_IOERROR ;
if ( MINIMP3_ID3_DETECT_SIZE ! = filled )
return 0 ;
size_t id3v2size = mp3dec_skip_id3v2 ( buf , filled ) ;
if ( id3v2size )
{
if ( io - > seek ( id3v2size , io - > seek_data ) )
return MP3D_E_IOERROR ;
filled = io - > read ( buf , buf_size , io - > read_data ) ;
if ( filled > buf_size )
return MP3D_E_IOERROR ;
readed + = id3v2size ;
} else
{
size_t readed = io - > read ( buf + MINIMP3_ID3_DETECT_SIZE , buf_size - MINIMP3_ID3_DETECT_SIZE , io - > read_data ) ;
if ( readed > ( buf_size - MINIMP3_ID3_DETECT_SIZE ) )
return MP3D_E_IOERROR ;
filled + = readed ;
}
if ( filled < MINIMP3_BUF_SIZE )
mp3dec_skip_id3v1 ( buf , & filled ) ;
do
{
int free_format_bytes = 0 , frame_size = 0 , ret ;
int i = mp3d_find_frame ( buf + consumed , filled - consumed , & free_format_bytes , & frame_size ) ;
if ( i & & ! frame_size )
{
consumed + = i ;
continue ;
}
if ( ! frame_size )
break ;
const uint8_t * hdr = buf + consumed + i ;
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 ;
readed + = i ;
if ( callback )
{
if ( ( ret = callback ( user_data , hdr , frame_size , free_format_bytes , filled - consumed , readed , & frame_info ) ) )
return ret ;
}
readed + = frame_size ;
consumed + = i + frame_size ;
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 ) )
return MP3D_E_IOERROR ;
if ( readed ! = ( buf_size - filled ) )
eof = 1 ;
filled + = readed ;
if ( eof )
mp3dec_skip_id3v1 ( buf , & filled ) ;
}
} while ( 1 ) ;
return 0 ;
}
static int mp3dec_load_index ( void * user_data , const uint8_t * frame , int frame_size , int free_format_bytes , size_t buf_size , uint64_t offset , mp3dec_frame_info_t * info )
{
mp3dec_frame_t * idx_frame ;
mp3dec_ex_t * dec = ( mp3dec_ex_t * ) user_data ;
if ( ! dec - > index . frames & & ! dec - > start_offset )
{ /* detect VBR tag and try to avoid full scan */
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 */
if ( 3 = = dec - > info . layer )
{
int ret = mp3dec_check_vbrtag ( frame , frame_size , & frames , & delay , & padding ) ;
if ( ret )
dec - > start_offset = dec - > offset = offset + frame_size ;
if ( ret > 0 )
{
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 - > vbr_tag_found = 1 ;
return MP3D_E_USER ;
} else if ( ret < 0 )
return 0 ;
}
}
if ( dec - > flags & MP3D_DO_NOT_SCAN )
return MP3D_E_USER ;
if ( dec - > index . num_frames + 1 > dec - > index . capacity )
{
if ( ! dec - > index . capacity )
dec - > index . capacity = 4096 ;
else
dec - > index . capacity * = 2 ;
mp3dec_frame_t * alloc_buf = ( mp3dec_frame_t * ) realloc ( ( void * ) dec - > index . frames , sizeof ( mp3dec_frame_t ) * dec - > index . capacity ) ;
if ( ! alloc_buf )
return MP3D_E_MEMORY ;
dec - > index . frames = alloc_buf ;
}
idx_frame = & dec - > index . frames [ dec - > index . num_frames + + ] ;
idx_frame - > offset = offset ;
idx_frame - > sample = dec - > samples ;
if ( ! dec - > buffer_samples & & dec - > index . num_frames < 256 )
{ /* for some cutted mp3 frames, bit-reservoir not filled and decoding can't be started from first frames */
/* try to decode up to 255 first frames till samples starts to decode */
dec - > buffer_samples = mp3dec_decode_frame ( & dec - > mp3d , frame , MINIMP3_MIN ( buf_size , ( size_t ) INT_MAX ) , dec - > buffer , info ) ;
dec - > samples + = dec - > buffer_samples * info - > channels ;
} else
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 flags )
{
if ( ! dec | | ! buf | | ( size_t ) - 1 = = buf_size | | ( flags & ( ~ MP3D_FLAGS_MASK ) ) )
return MP3D_E_PARAM ;
memset ( dec , 0 , sizeof ( * dec ) ) ;
dec - > file . buffer = buf ;
dec - > file . size = buf_size ;
dec - > flags = flags ;
mp3dec_init ( & dec - > mp3d ) ;
int ret = mp3dec_iterate_buf ( dec - > file . buffer , dec - > file . size , mp3dec_load_index , dec ) ;
if ( ret & & MP3D_E_USER ! = ret )
return ret ;
mp3dec_init ( & dec - > mp3d ) ;
dec - > buffer_samples = 0 ;
dec - > indexes_built = ! ( dec - > vbr_tag_found | | ( flags & MP3D_DO_NOT_SCAN ) ) ;
dec - > flags & = ( ~ MP3D_DO_NOT_SCAN ) ;
return 0 ;
}
# ifndef MINIMP3_SEEK_IDX_LINEAR_SEARCH
static size_t mp3dec_idx_binary_search ( mp3dec_index_t * idx , uint64_t position )
{
size_t end = idx - > num_frames , start = 0 , index = 0 ;
while ( start < = end )
{
size_t mid = ( start + end ) / 2 ;
if ( idx - > frames [ mid ] . sample > = position )
{ /* move left side. */
if ( idx - > frames [ mid ] . sample = = position )
return mid ;
end = mid - 1 ;
} else
{ /* move to right side */
index = mid ;
start = mid + 1 ;
if ( start = = idx - > num_frames )
break ;
}
}
return index ;
}
# endif
int mp3dec_ex_seek ( mp3dec_ex_t * dec , uint64_t position )
{
size_t i ;
if ( ! dec )
return MP3D_E_PARAM ;
if ( ! ( dec - > flags & MP3D_SEEK_TO_SAMPLE ) )
{
if ( dec - > io )
{
dec - > offset = position ;
} else
{
dec - > offset = MINIMP3_MIN ( position , dec - > file . size ) ;
}
dec - > cur_sample = 0 ;
goto do_exit ;
}
dec - > cur_sample = position ;
position + = dec - > start_delay ;
if ( 0 = = position )
{ /* optimize seek to zero, no index needed */
seek_zero :
dec - > offset = dec - > start_offset ;
dec - > to_skip = 0 ;
goto do_exit ;
}
if ( ! dec - > indexes_built )
{ /* no index created yet (vbr tag used to calculate track length or MP3D_DO_NOT_SCAN open flag used) */
dec - > indexes_built = 1 ;
dec - > samples = 0 ;
dec - > buffer_samples = 0 ;
if ( dec - > io )
{
if ( dec - > io - > seek ( dec - > start_offset , dec - > io - > seek_data ) )
return MP3D_E_IOERROR ;
int ret = mp3dec_iterate_cb ( dec - > io , ( uint8_t * ) dec - > file . buffer , dec - > file . size , mp3dec_load_index , dec ) ;
if ( ret & & MP3D_E_USER ! = ret )
return ret ;
} else
{
int ret = mp3dec_iterate_buf ( dec - > file . buffer + dec - > start_offset , dec - > file . size - dec - > start_offset , mp3dec_load_index , dec ) ;
if ( ret & & MP3D_E_USER ! = ret )
return ret ;
}
for ( i = 0 ; i < dec - > index . num_frames ; i + + )
dec - > index . frames [ i ] . offset + = dec - > start_offset ;
dec - > samples = dec - > detected_samples ;
}
if ( ! dec - > index . frames )
goto seek_zero ; /* no frames in file - seek to zero */
# ifdef MINIMP3_SEEK_IDX_LINEAR_SEARCH
for ( i = 0 ; i < dec - > index . num_frames ; i + + )
{
if ( dec - > index . frames [ i ] . sample > = position )
break ;
}
# else
i = mp3dec_idx_binary_search ( & dec - > index , position ) ;
# endif
if ( i )
{
int to_fill_bytes = 511 ;
int skip_frames = MINIMP3_PREDECODE_FRAMES
# ifdef MINIMP3_SEEK_IDX_LINEAR_SEARCH
+ ( ( dec - > index . frames [ i ] . sample = = position ) ? 0 : 1 )
# endif
;
i - = MINIMP3_MIN ( i , ( size_t ) skip_frames ) ;
if ( 3 = = dec - > info . layer )
{
while ( i & & to_fill_bytes )
{ /* make sure bit-reservoir is filled when we start decoding */
bs_t bs [ 1 ] ;
L3_gr_info_t gr_info [ 4 ] ;
int frame_bytes , frame_size ;
const uint8_t * hdr ;
if ( dec - > io )
{
hdr = dec - > file . buffer ;
if ( dec - > io - > seek ( dec - > index . frames [ i - 1 ] . offset , dec - > io - > seek_data ) )
return MP3D_E_IOERROR ;
size_t readed = dec - > io - > read ( ( uint8_t * ) hdr , HDR_SIZE , dec - > io - > read_data ) ;
if ( readed ! = HDR_SIZE )
return MP3D_E_IOERROR ;
frame_size = hdr_frame_bytes ( hdr , dec - > free_format_bytes ) + hdr_padding ( hdr ) ;
readed = dec - > io - > read ( ( uint8_t * ) hdr + HDR_SIZE , frame_size - HDR_SIZE , dec - > io - > read_data ) ;
if ( readed ! = ( size_t ) ( frame_size - HDR_SIZE ) )
return MP3D_E_IOERROR ;
bs_init ( bs , hdr + HDR_SIZE , frame_size - HDR_SIZE ) ;
} else
{
hdr = dec - > file . buffer + dec - > index . frames [ i - 1 ] . offset ;
frame_size = hdr_frame_bytes ( hdr , dec - > free_format_bytes ) + hdr_padding ( hdr ) ;
bs_init ( bs , hdr + HDR_SIZE , frame_size - HDR_SIZE ) ;
}
if ( HDR_IS_CRC ( hdr ) )
get_bits ( bs , 16 ) ;
i - - ;
if ( L3_read_side_info ( bs , gr_info , hdr ) < 0 )
break ; /* frame not decodable, we can start from here */
frame_bytes = ( bs - > limit - bs - > pos ) / 8 ;
to_fill_bytes - = MINIMP3_MIN ( to_fill_bytes , frame_bytes ) ;
}
}
}
dec - > offset = dec - > index . frames [ i ] . offset ;
dec - > to_skip = position - dec - > index . frames [ i ] . sample ;
while ( ( i + 1 ) < dec - > index . num_frames & & ! dec - > index . frames [ i ] . sample & & ! dec - > index . frames [ i + 1 ] . sample )
{ /* skip not decodable first frames */
const uint8_t * hdr ;
if ( dec - > io )
{
hdr = dec - > file . buffer ;
if ( dec - > io - > seek ( dec - > index . frames [ i ] . offset , dec - > io - > seek_data ) )
return MP3D_E_IOERROR ;
size_t readed = dec - > io - > read ( ( uint8_t * ) hdr , HDR_SIZE , dec - > io - > read_data ) ;
if ( readed ! = HDR_SIZE )
return MP3D_E_IOERROR ;
} else
hdr = dec - > file . buffer + dec - > index . frames [ i ] . offset ;
dec - > to_skip + = hdr_frame_samples ( hdr ) * dec - > info . channels ;
i + + ;
}
do_exit :
if ( dec - > io )
{
if ( dec - > io - > seek ( dec - > offset , dec - > io - > seek_data ) )
return MP3D_E_IOERROR ;
}
dec - > buffer_samples = 0 ;
dec - > buffer_consumed = 0 ;
dec - > input_consumed = 0 ;
dec - > input_filled = 0 ;
dec - > last_error = 0 ;
mp3dec_init ( & dec - > mp3d ) ;
return 0 ;
}
size_t mp3dec_ex_read_frame ( mp3dec_ex_t * dec , mp3d_sample_t * * buf , mp3dec_frame_info_t * frame_info , size_t max_samples )
{
if ( ! dec | | ! buf | | ! frame_info )
{
if ( dec )
dec - > last_error = MP3D_E_PARAM ;
return 0 ;
}
if ( dec - > detected_samples & & dec - > cur_sample > = dec - > detected_samples )
return 0 ; /* at end of stream */
if ( dec - > last_error )
return 0 ; /* error eof state, seek can reset it */
* buf = NULL ;
uint64_t end_offset = dec - > end_offset ? dec - > end_offset : dec - > file . size ;
int eof = 0 ;
while ( dec - > buffer_consumed = = dec - > buffer_samples )
{
const uint8_t * dec_buf ;
if ( dec - > io )
{
if ( ! eof & & ( dec - > input_filled - dec - > input_consumed ) < MINIMP3_BUF_SIZE )
{ /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */
memmove ( ( uint8_t * ) dec - > file . buffer , ( uint8_t * ) dec - > file . buffer + dec - > input_consumed , dec - > input_filled - dec - > input_consumed ) ;
dec - > input_filled - = dec - > input_consumed ;
dec - > input_consumed = 0 ;
size_t readed = dec - > io - > read ( ( uint8_t * ) dec - > file . buffer + dec - > input_filled , dec - > file . size - dec - > input_filled , dec - > io - > read_data ) ;
if ( readed > ( dec - > file . size - dec - > input_filled ) )
{
dec - > last_error = MP3D_E_IOERROR ;
readed = 0 ;
}
if ( readed ! = ( dec - > file . size - dec - > input_filled ) )
eof = 1 ;
dec - > input_filled + = readed ;
if ( eof )
mp3dec_skip_id3v1 ( ( uint8_t * ) dec - > file . buffer , & dec - > input_filled ) ;
}
dec_buf = dec - > file . buffer + dec - > input_consumed ;
if ( ! ( dec - > input_filled - dec - > input_consumed ) )
return 0 ;
dec - > buffer_samples = mp3dec_decode_frame ( & dec - > mp3d , dec_buf , dec - > input_filled - dec - > input_consumed , dec - > buffer , frame_info ) ;
dec - > input_consumed + = frame_info - > frame_bytes ;
} else
{
dec_buf = dec - > file . buffer + dec - > offset ;
uint64_t buf_size = end_offset - dec - > offset ;
if ( ! buf_size )
return 0 ;
dec - > buffer_samples = mp3dec_decode_frame ( & dec - > mp3d , dec_buf , MINIMP3_MIN ( buf_size , ( uint64_t ) INT_MAX ) , dec - > buffer , frame_info ) ;
}
dec - > buffer_consumed = 0 ;
if ( dec - > info . hz ! = frame_info - > hz | | dec - > info . layer ! = frame_info - > layer )
{
return_e_decode :
dec - > last_error = MP3D_E_DECODE ;
return 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 ;
}
if (
# ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION
! ( dec - > flags & MP3D_ALLOW_MONO_STEREO_TRANSITION ) & &
# endif
dec - > buffer_consumed ! = dec - > buffer_samples & & dec - > info . channels ! = frame_info - > channels )
{
goto return_e_decode ;
}
} 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 ;
}
size_t out_samples = MINIMP3_MIN ( ( size_t ) ( dec - > buffer_samples - dec - > buffer_consumed ) , max_samples ) ;
if ( dec - > detected_samples )
{ /* count decoded samples to properly cut padding */
if ( dec - > cur_sample + out_samples > = dec - > detected_samples )
out_samples = dec - > detected_samples - dec - > cur_sample ;
}
dec - > cur_sample + = out_samples ;
* buf = dec - > buffer + dec - > buffer_consumed ;
dec - > buffer_consumed + = out_samples ;
return out_samples ;
}
size_t mp3dec_ex_read ( mp3dec_ex_t * dec , mp3d_sample_t * buf , size_t samples )
{
if ( ! dec | | ! buf )
{
if ( dec )
dec - > last_error = MP3D_E_PARAM ;
return 0 ;
}
mp3dec_frame_info_t frame_info ;
memset ( & frame_info , 0 , sizeof ( frame_info ) ) ;
size_t samples_requested = samples ;
while ( samples )
{
mp3d_sample_t * buf_frame = NULL ;
size_t read_samples = mp3dec_ex_read_frame ( dec , & buf_frame , & frame_info , samples ) ;
if ( ! read_samples )
{
break ;
}
memcpy ( buf , buf_frame , read_samples * sizeof ( mp3d_sample_t ) ) ;
buf + = read_samples ;
samples - = read_samples ;
}
return samples_requested - samples ;
}
int mp3dec_ex_open_cb ( mp3dec_ex_t * dec , mp3dec_io_t * io , int flags )
{
if ( ! dec | | ! io | | ( flags & ( ~ MP3D_FLAGS_MASK ) ) )
return MP3D_E_PARAM ;
memset ( dec , 0 , sizeof ( * dec ) ) ;
# ifdef MINIMP3_HAVE_RING
int ret ;
if ( ret = mp3dec_open_ring ( & dec - > file , MINIMP3_IO_SIZE ) )
return ret ;
# else
dec - > file . size = MINIMP3_IO_SIZE ;
dec - > file . buffer = ( const uint8_t * ) malloc ( dec - > file . size ) ;
if ( ! dec - > file . buffer )
return MP3D_E_MEMORY ;
# endif
dec - > flags = flags ;
dec - > io = io ;
mp3dec_init ( & dec - > mp3d ) ;
if ( io - > seek ( 0 , io - > seek_data ) )
return MP3D_E_IOERROR ;
int ret = mp3dec_iterate_cb ( io , ( uint8_t * ) dec - > file . buffer , dec - > file . size , mp3dec_load_index , dec ) ;
if ( ret & & MP3D_E_USER ! = ret )
return ret ;
if ( dec - > io - > seek ( dec - > start_offset , dec - > io - > seek_data ) )
return MP3D_E_IOERROR ;
mp3dec_init ( & dec - > mp3d ) ;
dec - > buffer_samples = 0 ;
dec - > indexes_built = ! ( dec - > vbr_tag_found | | ( flags & MP3D_DO_NOT_SCAN ) ) ;
dec - > flags & = ( ~ MP3D_DO_NOT_SCAN ) ;
return 0 ;
}
# ifndef MINIMP3_NO_STDIO
# if defined(__linux__) || defined(__FreeBSD__)
# include <errno.h>
# include <sys/mman.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <fcntl.h>
# if !defined(_GNU_SOURCE)
# include <sys/ipc.h>
# include <sys/shm.h>
# endif
# if !defined(MAP_POPULATE) && defined(__linux__)
# define MAP_POPULATE 0x08000
# elif !defined(MAP_POPULATE)
# define MAP_POPULATE 0
# endif
static void mp3dec_close_file ( mp3dec_map_info_t * map_info )
{
if ( map_info - > buffer & & MAP_FAILED ! = map_info - > buffer )
munmap ( ( void * ) map_info - > buffer , map_info - > size ) ;
map_info - > buffer = 0 ;
map_info - > size = 0 ;
}
static int mp3dec_open_file ( const char * file_name , mp3dec_map_info_t * map_info )
{
if ( ! file_name )
return MP3D_E_PARAM ;
int file ;
struct stat st ;
memset ( map_info , 0 , sizeof ( * map_info ) ) ;
retry_open :
file = open ( file_name , O_RDONLY ) ;
if ( file < 0 & & ( errno = = EAGAIN | | errno = = EINTR ) )
goto retry_open ;
if ( file < 0 | | fstat ( file , & st ) < 0 )
{
close ( file ) ;
return MP3D_E_IOERROR ;
}
map_info - > size = st . st_size ;
retry_mmap :
map_info - > buffer = ( const uint8_t * ) mmap ( NULL , st . st_size , PROT_READ , MAP_PRIVATE | MAP_POPULATE , file , 0 ) ;
if ( MAP_FAILED = = map_info - > buffer & & ( errno = = EAGAIN | | errno = = EINTR ) )
goto retry_mmap ;
close ( file ) ;
if ( MAP_FAILED = = map_info - > buffer )
return MP3D_E_IOERROR ;
return 0 ;
}
# if MINIMP3_ENABLE_RING && defined(__linux__) && defined(_GNU_SOURCE)
# define MINIMP3_HAVE_RING
static void mp3dec_close_ring ( mp3dec_map_info_t * map_info )
{
# if defined(__linux__) && defined(_GNU_SOURCE)
if ( map_info - > buffer & & MAP_FAILED ! = map_info - > buffer )
munmap ( ( void * ) map_info - > buffer , map_info - > size * 2 ) ;
# else
if ( map_info - > buffer )
{
shmdt ( map_info - > buffer ) ;
shmdt ( map_info - > buffer + map_info - > size ) ;
}
# endif
map_info - > buffer = 0 ;
map_info - > size = 0 ;
}
static int mp3dec_open_ring ( mp3dec_map_info_t * map_info , size_t size )
{
int memfd , page_size ;
# if defined(__linux__) && defined(_GNU_SOURCE)
void * buffer ;
int res ;
# endif
memset ( map_info , 0 , sizeof ( * map_info ) ) ;
# ifdef _SC_PAGESIZE
page_size = sysconf ( _SC_PAGESIZE ) ;
# else
page_size = getpagesize ( ) ;
# endif
map_info - > size = ( size + page_size - 1 ) / page_size * page_size ;
# if defined(__linux__) && defined(_GNU_SOURCE)
memfd = memfd_create ( " mp3_ring " , 0 ) ;
if ( memfd < 0 )
return MP3D_E_MEMORY ;
retry_ftruncate :
res = ftruncate ( memfd , map_info - > size ) ;
if ( res & & ( errno = = EAGAIN | | errno = = EINTR ) )
goto retry_ftruncate ;
if ( res )
goto error ;
retry_mmap :
map_info - > buffer = ( const uint8_t * ) mmap ( NULL , map_info - > size * 2 , PROT_NONE , MAP_ANONYMOUS | MAP_PRIVATE , - 1 , 0 ) ;
if ( MAP_FAILED = = map_info - > buffer & & ( errno = = EAGAIN | | errno = = EINTR ) )
goto retry_mmap ;
if ( MAP_FAILED = = map_info - > buffer | | ! map_info - > buffer )
goto error ;
retry_mmap2 :
buffer = mmap ( ( void * ) map_info - > buffer , map_info - > size , PROT_READ | PROT_WRITE , MAP_FIXED | MAP_SHARED , memfd , 0 ) ;
if ( MAP_FAILED = = map_info - > buffer & & ( errno = = EAGAIN | | errno = = EINTR ) )
goto retry_mmap2 ;
if ( MAP_FAILED = = map_info - > buffer | | buffer ! = ( void * ) map_info - > buffer )
goto error ;
retry_mmap3 :
buffer = mmap ( ( void * ) map_info - > buffer + map_info - > size , map_info - > size , PROT_READ | PROT_WRITE , MAP_FIXED | MAP_SHARED , memfd , 0 ) ;
if ( MAP_FAILED = = map_info - > buffer & & ( errno = = EAGAIN | | errno = = EINTR ) )
goto retry_mmap3 ;
if ( MAP_FAILED = = map_info - > buffer | | buffer ! = ( void * ) ( map_info - > buffer + map_info - > size ) )
goto error ;
close ( memfd ) ;
return 0 ;
error :
close ( memfd ) ;
mp3dec_close_ring ( map_info ) ;
return MP3D_E_MEMORY ;
# else
memfd = shmget ( IPC_PRIVATE , map_info - > size , IPC_CREAT | 0700 ) ;
if ( memfd < 0 )
return MP3D_E_MEMORY ;
retry_mmap :
map_info - > buffer = ( const uint8_t * ) mmap ( NULL , map_info - > size * 2 , PROT_NONE , MAP_PRIVATE , - 1 , 0 ) ;
if ( MAP_FAILED = = map_info - > buffer & & ( errno = = EAGAIN | | errno = = EINTR ) )
goto retry_mmap ;
if ( MAP_FAILED = = map_info - > buffer )
goto error ;
if ( map_info - > buffer ! = shmat ( memfd , map_info - > buffer , 0 ) )
goto error ;
if ( ( map_info - > buffer + map_info - > size ) ! = shmat ( memfd , map_info - > buffer + map_info - > size , 0 ) )
goto error ;
if ( shmctl ( memfd , IPC_RMID , NULL ) < 0 )
return MP3D_E_MEMORY ;
return 0 ;
error :
shmctl ( memfd , IPC_RMID , NULL ) ;
mp3dec_close_ring ( map_info ) ;
return MP3D_E_MEMORY ;
# endif
}
# endif /*MINIMP3_ENABLE_RING*/
# elif defined(_WIN32)
# include <windows.h>
static void mp3dec_close_file ( mp3dec_map_info_t * map_info )
{
if ( map_info - > buffer )
UnmapViewOfFile ( map_info - > buffer ) ;
map_info - > buffer = 0 ;
map_info - > size = 0 ;
}
static int mp3dec_open_file_h ( HANDLE file , mp3dec_map_info_t * map_info )
{
memset ( map_info , 0 , sizeof ( * map_info ) ) ;
HANDLE mapping = NULL ;
LARGE_INTEGER s ;
2023-03-11 22:10:18 +08:00
s . LowPart = GetFileSizeEx ( file , & s ) ;
2020-11-16 14:47:43 +08:00
if ( s . LowPart = = INVALID_FILE_SIZE & & GetLastError ( ) ! = NO_ERROR )
goto error ;
map_info - > size = s . QuadPart ;
mapping = CreateFileMapping ( file , NULL , PAGE_READONLY , 0 , 0 , NULL ) ;
if ( ! mapping )
goto error ;
map_info - > buffer = ( const uint8_t * ) MapViewOfFile ( mapping , FILE_MAP_READ , 0 , 0 , s . QuadPart ) ;
CloseHandle ( mapping ) ;
if ( ! map_info - > buffer )
goto error ;
CloseHandle ( file ) ;
return 0 ;
error :
mp3dec_close_file ( map_info ) ;
CloseHandle ( file ) ;
return MP3D_E_IOERROR ;
}
2023-03-11 22:10:18 +08:00
# if defined(WINAPI_FAMILY)
# if (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
2020-11-16 14:47:43 +08:00
static int mp3dec_open_file ( const char * file_name , mp3dec_map_info_t * map_info )
{
if ( ! file_name )
return MP3D_E_PARAM ;
HANDLE file = CreateFileA ( file_name , GENERIC_READ , FILE_SHARE_READ , 0 , OPEN_EXISTING , 0 , 0 ) ;
if ( INVALID_HANDLE_VALUE = = file )
return MP3D_E_IOERROR ;
return mp3dec_open_file_h ( file , map_info ) ;
}
static int mp3dec_open_file_w ( const wchar_t * file_name , mp3dec_map_info_t * map_info )
{
if ( ! file_name )
return MP3D_E_PARAM ;
HANDLE file = CreateFileW ( file_name , GENERIC_READ , FILE_SHARE_READ , 0 , OPEN_EXISTING , 0 , 0 ) ;
if ( INVALID_HANDLE_VALUE = = file )
return MP3D_E_IOERROR ;
return mp3dec_open_file_h ( file , map_info ) ;
}
# else
2023-03-11 22:10:18 +08:00
static int mp3dec_open_file ( const char * file_name , mp3dec_map_info_t * map_info )
{
if ( ! file_name )
return MP3D_E_PARAM ;
int cch = : : MultiByteToWideChar ( CP_UTF8 , 0 , file_name , - 1 , NULL , 0 ) ;
if ( cch < 2 )
return MP3D_E_PARAM ;
wchar_t * file_name_w = ( wchar_t * ) malloc ( cch * sizeof ( wchar_t * ) ) ;
if ( ! file_name_w )
return MP3D_E_MEMORY ;
if ( : : MultiByteToWideChar ( CP_UTF8 , 0 , file_name , cch , file_name_w , cch ) ! = cch )
{
free ( file_name_w ) ;
return MP3D_E_PARAM ;
}
HANDLE file = CreateFile2 ( file_name_w , GENERIC_READ , FILE_SHARE_READ , OPEN_EXISTING , 0 ) ;
free ( file_name_w ) ;
if ( INVALID_HANDLE_VALUE = = file )
return MP3D_E_IOERROR ;
return mp3dec_open_file_h ( file , map_info ) ;
}
static int mp3dec_open_file_w ( const wchar_t * file_name , mp3dec_map_info_t * map_info )
{
if ( ! file_name )
return MP3D_E_PARAM ;
HANDLE file = CreateFile2 ( file_name , GENERIC_READ , FILE_SHARE_READ , OPEN_EXISTING , 0 ) ;
if ( INVALID_HANDLE_VALUE = = file )
return MP3D_E_IOERROR ;
return mp3dec_open_file_h ( file , map_info ) ;
}
# endif
# endif
# else
2020-11-16 14:47:43 +08:00
# include <stdio.h>
static void mp3dec_close_file ( mp3dec_map_info_t * map_info )
{
if ( map_info - > buffer )
free ( ( void * ) map_info - > buffer ) ;
map_info - > buffer = 0 ;
map_info - > size = 0 ;
}
static int mp3dec_open_file ( const char * file_name , mp3dec_map_info_t * map_info )
{
if ( ! file_name )
return MP3D_E_PARAM ;
memset ( map_info , 0 , sizeof ( * map_info ) ) ;
FILE * file = fopen ( file_name , " rb " ) ;
if ( ! file )
return MP3D_E_IOERROR ;
int res = MP3D_E_IOERROR ;
long size = - 1 ;
if ( fseek ( file , 0 , SEEK_END ) )
goto error ;
size = ftell ( file ) ;
if ( size < 0 )
goto error ;
map_info - > size = ( size_t ) size ;
if ( fseek ( file , 0 , SEEK_SET ) )
goto error ;
map_info - > buffer = ( uint8_t * ) malloc ( map_info - > size ) ;
if ( ! map_info - > buffer )
{
res = MP3D_E_MEMORY ;
goto error ;
}
if ( fread ( ( void * ) map_info - > buffer , 1 , map_info - > size , file ) ! = map_info - > size )
goto error ;
fclose ( file ) ;
return 0 ;
error :
mp3dec_close_file ( map_info ) ;
fclose ( file ) ;
return res ;
}
# endif
static int mp3dec_detect_mapinfo ( mp3dec_map_info_t * map_info )
{
int ret = mp3dec_detect_buf ( map_info - > buffer , map_info - > size ) ;
mp3dec_close_file ( map_info ) ;
return ret ;
}
static int mp3dec_load_mapinfo ( mp3dec_t * dec , mp3dec_map_info_t * map_info , mp3dec_file_info_t * info , MP3D_PROGRESS_CB progress_cb , void * user_data )
{
int ret = mp3dec_load_buf ( dec , map_info - > buffer , map_info - > size , info , progress_cb , user_data ) ;
mp3dec_close_file ( map_info ) ;
return ret ;
}
static int mp3dec_iterate_mapinfo ( mp3dec_map_info_t * map_info , MP3D_ITERATE_CB callback , void * user_data )
{
int ret = mp3dec_iterate_buf ( map_info - > buffer , map_info - > size , callback , user_data ) ;
mp3dec_close_file ( map_info ) ;
return ret ;
}
static int mp3dec_ex_open_mapinfo ( mp3dec_ex_t * dec , int flags )
{
int ret = mp3dec_ex_open_buf ( dec , dec - > file . buffer , dec - > file . size , flags ) ;
dec - > is_file = 1 ;
if ( ret )
mp3dec_ex_close ( dec ) ;
return ret ;
}
int mp3dec_detect ( const char * file_name )
{
int ret ;
mp3dec_map_info_t map_info ;
if ( ( ret = mp3dec_open_file ( file_name , & map_info ) ) )
return ret ;
return mp3dec_detect_mapinfo ( & map_info ) ;
}
int mp3dec_load ( mp3dec_t * dec , const char * file_name , mp3dec_file_info_t * info , MP3D_PROGRESS_CB progress_cb , void * user_data )
{
int ret ;
mp3dec_map_info_t map_info ;
if ( ( ret = mp3dec_open_file ( file_name , & map_info ) ) )
return ret ;
return mp3dec_load_mapinfo ( dec , & map_info , info , progress_cb , user_data ) ;
}
int mp3dec_iterate ( const char * file_name , MP3D_ITERATE_CB callback , void * user_data )
{
int ret ;
mp3dec_map_info_t map_info ;
if ( ( ret = mp3dec_open_file ( file_name , & map_info ) ) )
return ret ;
return mp3dec_iterate_mapinfo ( & map_info , callback , user_data ) ;
}
int mp3dec_ex_open ( mp3dec_ex_t * dec , const char * file_name , int flags )
{
int ret ;
if ( ! dec )
return MP3D_E_PARAM ;
if ( ( ret = mp3dec_open_file ( file_name , & dec - > file ) ) )
return ret ;
return mp3dec_ex_open_mapinfo ( dec , flags ) ;
}
void mp3dec_ex_close ( mp3dec_ex_t * dec )
{
# ifdef MINIMP3_HAVE_RING
if ( dec - > io )
mp3dec_close_ring ( & dec - > file ) ;
# else
if ( dec - > io & & dec - > file . buffer )
free ( ( void * ) dec - > file . buffer ) ;
# endif
if ( dec - > is_file )
mp3dec_close_file ( & dec - > file ) ;
if ( dec - > index . frames )
free ( dec - > index . frames ) ;
memset ( dec , 0 , sizeof ( * dec ) ) ;
}
# ifdef _WIN32
int mp3dec_detect_w ( const wchar_t * file_name )
{
int ret ;
mp3dec_map_info_t map_info ;
if ( ( ret = mp3dec_open_file_w ( file_name , & map_info ) ) )
return ret ;
return mp3dec_detect_mapinfo ( & map_info ) ;
}
int mp3dec_load_w ( mp3dec_t * dec , const wchar_t * file_name , mp3dec_file_info_t * info , MP3D_PROGRESS_CB progress_cb , void * user_data )
{
int ret ;
mp3dec_map_info_t map_info ;
if ( ( ret = mp3dec_open_file_w ( file_name , & map_info ) ) )
return ret ;
return mp3dec_load_mapinfo ( dec , & map_info , info , progress_cb , user_data ) ;
}
int mp3dec_iterate_w ( const wchar_t * file_name , MP3D_ITERATE_CB callback , void * user_data )
{
int ret ;
mp3dec_map_info_t map_info ;
if ( ( ret = mp3dec_open_file_w ( file_name , & map_info ) ) )
return ret ;
return mp3dec_iterate_mapinfo ( & map_info , callback , user_data ) ;
}
int mp3dec_ex_open_w ( mp3dec_ex_t * dec , const wchar_t * file_name , int flags )
{
int ret ;
if ( ( ret = mp3dec_open_file_w ( file_name , & dec - > file ) ) )
return ret ;
return mp3dec_ex_open_mapinfo ( dec , flags ) ;
}
# endif
# else /* MINIMP3_NO_STDIO */
void mp3dec_ex_close ( mp3dec_ex_t * dec )
{
2021-10-06 11:26:47 +08:00
# ifdef MINIMP3_HAVE_RING
if ( dec - > io )
mp3dec_close_ring ( & dec - > file ) ;
# else
if ( dec - > io & & dec - > file . buffer )
free ( ( void * ) dec - > file . buffer ) ;
# endif
2020-11-16 14:47:43 +08:00
if ( dec - > index . frames )
free ( dec - > index . frames ) ;
memset ( dec , 0 , sizeof ( * dec ) ) ;
}
# endif
# endif /*MINIMP3_IMPLEMENTATION*/