CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/ext/minimp3/minimp3_ex.h
Views: 1401
#ifndef MINIMP3_EXT_H1#define MINIMP3_EXT_H2/*3https://github.com/lieff/minimp34To 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.5This software is distributed without any warranty.6See <http://creativecommons.org/publicdomain/zero/1.0/>.7*/8#include <stddef.h>9#include "minimp3.h"1011/* flags for mp3dec_ex_open_* functions */12#define MP3D_SEEK_TO_BYTE 0 /* mp3dec_ex_seek seeks to byte in stream */13#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) */14#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 */15#ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION16#define MP3D_ALLOW_MONO_STEREO_TRANSITION 417#define MP3D_FLAGS_MASK 718#else19#define MP3D_FLAGS_MASK 320#endif2122/* compile-time config */23#define MINIMP3_PREDECODE_FRAMES 2 /* frames to pre-decode and skip after seek (to fill internal structures) */24/*#define MINIMP3_SEEK_IDX_LINEAR_SEARCH*/ /* define to use linear index search instead of binary search on seek */25#define MINIMP3_IO_SIZE (128*1024) /* io buffer size for streaming functions, must be greater than MINIMP3_BUF_SIZE */26#define MINIMP3_BUF_SIZE (16*1024) /* buffer which can hold minimum 10 consecutive mp3 frames (~16KB) worst case */27/*#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 */28#define MINIMP3_ENABLE_RING 0 /* WIP enable hardware magic ring buffer if available, to make less input buffer memmove(s) in callback IO mode */2930/* return error codes */31#define MP3D_E_PARAM -132#define MP3D_E_MEMORY -233#define MP3D_E_IOERROR -334#define MP3D_E_USER -4 /* can be used to stop processing from callbacks without indicating specific error */35#define MP3D_E_DECODE -5 /* decode error which can't be safely skipped, such as sample rate, layer and channels change */3637typedef struct38{39mp3d_sample_t *buffer;40size_t samples; /* channels included, byte size = samples*sizeof(mp3d_sample_t) */41int channels, hz, layer, avg_bitrate_kbps;42} mp3dec_file_info_t;4344typedef struct45{46const uint8_t *buffer;47size_t size;48} mp3dec_map_info_t;4950typedef struct51{52uint64_t sample;53uint64_t offset;54} mp3dec_frame_t;5556typedef struct57{58mp3dec_frame_t *frames;59size_t num_frames, capacity;60} mp3dec_index_t;6162typedef size_t (*MP3D_READ_CB)(void *buf, size_t size, void *user_data);63typedef int (*MP3D_SEEK_CB)(uint64_t position, void *user_data);6465typedef struct66{67MP3D_READ_CB read;68void *read_data;69MP3D_SEEK_CB seek;70void *seek_data;71} mp3dec_io_t;7273typedef struct74{75mp3dec_t mp3d;76mp3dec_map_info_t file;77mp3dec_io_t *io;78mp3dec_index_t index;79uint64_t offset, samples, detected_samples, cur_sample, start_offset, end_offset;80mp3dec_frame_info_t info;81mp3d_sample_t buffer[MINIMP3_MAX_SAMPLES_PER_FRAME];82size_t input_consumed, input_filled;83int is_file, flags, vbr_tag_found, indexes_built;84int free_format_bytes;85int buffer_samples, buffer_consumed, to_skip, start_delay;86int last_error;87} mp3dec_ex_t;8889typedef 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);90typedef int (*MP3D_PROGRESS_CB)(void *user_data, size_t file_size, uint64_t offset, mp3dec_frame_info_t *info);9192#ifdef __cplusplus93extern "C" {94#endif9596/* detect mp3/mpa format */97int mp3dec_detect_buf(const uint8_t *buf, size_t buf_size);98int mp3dec_detect_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size);99/* decode whole buffer block */100int 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);101int 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);102/* iterate through frames */103int mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data);104int mp3dec_iterate_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data);105/* streaming decoder with seeking capability */106int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int flags);107int mp3dec_ex_open_cb(mp3dec_ex_t *dec, mp3dec_io_t *io, int flags);108void mp3dec_ex_close(mp3dec_ex_t *dec);109int mp3dec_ex_seek(mp3dec_ex_t *dec, uint64_t position);110size_t mp3dec_ex_read_frame(mp3dec_ex_t *dec, mp3d_sample_t **buf, mp3dec_frame_info_t *frame_info, size_t max_samples);111size_t mp3dec_ex_read(mp3dec_ex_t *dec, mp3d_sample_t *buf, size_t samples);112#ifndef MINIMP3_NO_STDIO113/* stdio versions of file detect, load, iterate and stream */114int mp3dec_detect(const char *file_name);115int mp3dec_load(mp3dec_t *dec, const char *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);116int mp3dec_iterate(const char *file_name, MP3D_ITERATE_CB callback, void *user_data);117int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int flags);118#ifdef _WIN32119int mp3dec_detect_w(const wchar_t *file_name);120int mp3dec_load_w(mp3dec_t *dec, const wchar_t *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);121int mp3dec_iterate_w(const wchar_t *file_name, MP3D_ITERATE_CB callback, void *user_data);122int mp3dec_ex_open_w(mp3dec_ex_t *dec, const wchar_t *file_name, int flags);123#endif124#endif125126#ifdef __cplusplus127}128#endif129#endif /*MINIMP3_EXT_H*/130131#if defined(MINIMP3_IMPLEMENTATION) && !defined(_MINIMP3_EX_IMPLEMENTATION_GUARD)132#define _MINIMP3_EX_IMPLEMENTATION_GUARD133#include <limits.h>134#include "minimp3.h"135136static void mp3dec_skip_id3v1(const uint8_t *buf, size_t *pbuf_size)137{138size_t buf_size = *pbuf_size;139#ifndef MINIMP3_NOSKIP_ID3V1140if (buf_size >= 128 && !memcmp(buf + buf_size - 128, "TAG", 3))141{142buf_size -= 128;143if (buf_size >= 227 && !memcmp(buf + buf_size - 227, "TAG+", 4))144buf_size -= 227;145}146#endif147#ifndef MINIMP3_NOSKIP_APEV2148if (buf_size > 32 && !memcmp(buf + buf_size - 32, "APETAGEX", 8))149{150buf_size -= 32;151const uint8_t *tag = buf + buf_size + 8 + 4;152uint32_t tag_size = (uint32_t)(tag[3] << 24) | (tag[2] << 16) | (tag[1] << 8) | tag[0];153if (buf_size >= tag_size)154buf_size -= tag_size;155}156#endif157*pbuf_size = buf_size;158}159160static size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size)161{162#define MINIMP3_ID3_DETECT_SIZE 10163#ifndef MINIMP3_NOSKIP_ID3V2164if (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)))165{166size_t id3v2size = (((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f)) + 10;167if ((buf[5] & 16))168id3v2size += 10; /* footer */169return id3v2size;170}171#endif172return 0;173}174175static void mp3dec_skip_id3(const uint8_t **pbuf, size_t *pbuf_size)176{177uint8_t *buf = (uint8_t *)(*pbuf);178size_t buf_size = *pbuf_size;179size_t id3v2size = mp3dec_skip_id3v2(buf, buf_size);180if (id3v2size)181{182if (id3v2size >= buf_size)183id3v2size = buf_size;184buf += id3v2size;185buf_size -= id3v2size;186}187mp3dec_skip_id3v1(buf, &buf_size);188*pbuf = (const uint8_t *)buf;189*pbuf_size = buf_size;190}191192static int mp3dec_check_vbrtag(const uint8_t *frame, int frame_size, uint32_t *frames, int *delay, int *padding)193{194static const char g_xing_tag[4] = { 'X', 'i', 'n', 'g' };195static const char g_info_tag[4] = { 'I', 'n', 'f', 'o' };196#define FRAMES_FLAG 1197#define BYTES_FLAG 2198#define TOC_FLAG 4199#define VBR_SCALE_FLAG 8200/* Side info offsets after header:201/ Mono Stereo202/ MPEG1 17 32203/ MPEG2 & 2.5 9 17*/204bs_t bs[1];205L3_gr_info_t gr_info[4];206bs_init(bs, frame + HDR_SIZE, frame_size - HDR_SIZE);207if (HDR_IS_CRC(frame))208get_bits(bs, 16);209if (L3_read_side_info(bs, gr_info, frame) < 0)210return 0; /* side info corrupted */211212const uint8_t *tag = frame + HDR_SIZE + bs->pos/8;213if (memcmp(g_xing_tag, tag, 4) && memcmp(g_info_tag, tag, 4))214return 0;215int flags = tag[7];216if (!((flags & FRAMES_FLAG)))217return -1;218tag += 8;219*frames = (uint32_t)(tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8) | tag[3];220tag += 4;221if (flags & BYTES_FLAG)222tag += 4;223if (flags & TOC_FLAG)224tag += 100;225if (flags & VBR_SCALE_FLAG)226tag += 4;227*delay = *padding = 0;228if (*tag)229{ /* extension, LAME, Lavc, etc. Should be the same structure. */230tag += 21;231if (tag - frame + 14 >= frame_size)232return 0;233*delay = ((tag[0] << 4) | (tag[1] >> 4)) + (528 + 1);234*padding = (((tag[1] & 0xF) << 8) | tag[2]) - (528 + 1);235}236return 1;237}238239int mp3dec_detect_buf(const uint8_t *buf, size_t buf_size)240{241return mp3dec_detect_cb(0, (uint8_t *)buf, buf_size);242}243244int mp3dec_detect_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size)245{246if (!buf || (size_t)-1 == buf_size || (io && buf_size < MINIMP3_BUF_SIZE))247return MP3D_E_PARAM;248size_t filled = buf_size;249if (io)250{251if (io->seek(0, io->seek_data))252return MP3D_E_IOERROR;253filled = io->read(buf, MINIMP3_ID3_DETECT_SIZE, io->read_data);254if (filled > MINIMP3_ID3_DETECT_SIZE)255return MP3D_E_IOERROR;256}257if (filled < MINIMP3_ID3_DETECT_SIZE)258return MP3D_E_USER; /* too small, can't be mp3/mpa */259if (mp3dec_skip_id3v2(buf, filled))260return 0; /* id3v2 tag is enough evidence */261if (io)262{263size_t readed = io->read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io->read_data);264if (readed > (buf_size - MINIMP3_ID3_DETECT_SIZE))265return MP3D_E_IOERROR;266filled += readed;267if (filled < MINIMP3_BUF_SIZE)268mp3dec_skip_id3v1(buf, &filled);269} else270{271mp3dec_skip_id3v1(buf, &filled);272if (filled > MINIMP3_BUF_SIZE)273filled = MINIMP3_BUF_SIZE;274}275int free_format_bytes, frame_size;276mp3d_find_frame(buf, filled, &free_format_bytes, &frame_size);277if (frame_size)278return 0; /* MAX_FRAME_SYNC_MATCHES consecutive frames found */279return MP3D_E_USER;280}281282int 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)283{284return mp3dec_load_cb(dec, 0, (uint8_t *)buf, buf_size, info, progress_cb, user_data);285}286287int 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)288{289if (!dec || !buf || !info || (size_t)-1 == buf_size || (io && buf_size < MINIMP3_BUF_SIZE))290return MP3D_E_PARAM;291uint64_t detected_samples = 0;292size_t orig_buf_size = buf_size;293int to_skip = 0;294mp3dec_frame_info_t frame_info;295memset(info, 0, sizeof(*info));296memset(&frame_info, 0, sizeof(frame_info));297298/* skip id3 */299size_t filled = 0, consumed = 0;300int eof = 0, ret = 0;301if (io)302{303if (io->seek(0, io->seek_data))304return MP3D_E_IOERROR;305filled = io->read(buf, MINIMP3_ID3_DETECT_SIZE, io->read_data);306if (filled > MINIMP3_ID3_DETECT_SIZE)307return MP3D_E_IOERROR;308if (MINIMP3_ID3_DETECT_SIZE != filled)309return 0;310size_t id3v2size = mp3dec_skip_id3v2(buf, filled);311if (id3v2size)312{313if (io->seek(id3v2size, io->seek_data))314return MP3D_E_IOERROR;315filled = io->read(buf, buf_size, io->read_data);316if (filled > buf_size)317return MP3D_E_IOERROR;318} else319{320size_t readed = io->read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io->read_data);321if (readed > (buf_size - MINIMP3_ID3_DETECT_SIZE))322return MP3D_E_IOERROR;323filled += readed;324}325if (filled < MINIMP3_BUF_SIZE)326mp3dec_skip_id3v1(buf, &filled);327} else328{329mp3dec_skip_id3((const uint8_t **)&buf, &buf_size);330if (!buf_size)331return 0;332}333/* try to make allocation size assumption by first frame or vbr tag */334mp3dec_init(dec);335int samples;336do337{338uint32_t frames;339int i, delay, padding, free_format_bytes = 0, frame_size = 0;340const uint8_t *hdr;341if (io)342{343if (!eof && filled - consumed < MINIMP3_BUF_SIZE)344{ /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */345memmove(buf, buf + consumed, filled - consumed);346filled -= consumed;347consumed = 0;348size_t readed = io->read(buf + filled, buf_size - filled, io->read_data);349if (readed > (buf_size - filled))350return MP3D_E_IOERROR;351if (readed != (buf_size - filled))352eof = 1;353filled += readed;354if (eof)355mp3dec_skip_id3v1(buf, &filled);356}357i = mp3d_find_frame(buf + consumed, filled - consumed, &free_format_bytes, &frame_size);358consumed += i;359hdr = buf + consumed;360} else361{362i = mp3d_find_frame(buf, buf_size, &free_format_bytes, &frame_size);363buf += i;364buf_size -= i;365hdr = buf;366}367if (i && !frame_size)368continue;369if (!frame_size)370return 0;371frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2;372frame_info.hz = hdr_sample_rate_hz(hdr);373frame_info.layer = 4 - HDR_GET_LAYER(hdr);374frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr);375frame_info.frame_bytes = frame_size;376samples = hdr_frame_samples(hdr)*frame_info.channels;377if (3 != frame_info.layer)378break;379int ret = mp3dec_check_vbrtag(hdr, frame_size, &frames, &delay, &padding);380if (ret > 0)381{382padding *= frame_info.channels;383to_skip = delay*frame_info.channels;384detected_samples = samples*(uint64_t)frames;385if (detected_samples >= (uint64_t)to_skip)386detected_samples -= to_skip;387if (padding > 0 && detected_samples >= (uint64_t)padding)388detected_samples -= padding;389if (!detected_samples)390return 0;391}392if (ret)393{394if (io)395{396consumed += frame_size;397} else398{399buf += frame_size;400buf_size -= frame_size;401}402}403break;404} while(1);405size_t allocated = MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t);406if (detected_samples)407allocated += detected_samples*sizeof(mp3d_sample_t);408else409allocated += (buf_size/frame_info.frame_bytes)*samples*sizeof(mp3d_sample_t);410info->buffer = (mp3d_sample_t*)malloc(allocated);411if (!info->buffer)412return MP3D_E_MEMORY;413/* save info */414info->channels = frame_info.channels;415info->hz = frame_info.hz;416info->layer = frame_info.layer;417/* decode all frames */418size_t avg_bitrate_kbps = 0, frames = 0;419do420{421if ((allocated - info->samples*sizeof(mp3d_sample_t)) < MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t))422{423allocated *= 2;424mp3d_sample_t *alloc_buf = (mp3d_sample_t*)realloc(info->buffer, allocated);425if (!alloc_buf)426return MP3D_E_MEMORY;427info->buffer = alloc_buf;428}429if (io)430{431if (!eof && filled - consumed < MINIMP3_BUF_SIZE)432{ /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */433memmove(buf, buf + consumed, filled - consumed);434filled -= consumed;435consumed = 0;436size_t readed = io->read(buf + filled, buf_size - filled, io->read_data);437if (readed != (buf_size - filled))438eof = 1;439filled += readed;440if (eof)441mp3dec_skip_id3v1(buf, &filled);442}443samples = mp3dec_decode_frame(dec, buf + consumed, filled - consumed, info->buffer + info->samples, &frame_info);444consumed += frame_info.frame_bytes;445} else446{447samples = mp3dec_decode_frame(dec, buf, MINIMP3_MIN(buf_size, (size_t)INT_MAX), info->buffer + info->samples, &frame_info);448buf += frame_info.frame_bytes;449buf_size -= frame_info.frame_bytes;450}451if (samples)452{453if (info->hz != frame_info.hz || info->layer != frame_info.layer)454{455ret = MP3D_E_DECODE;456break;457}458if (info->channels && info->channels != frame_info.channels)459{460#ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION461info->channels = 0; /* mark file with mono-stereo transition */462#else463ret = MP3D_E_DECODE;464break;465#endif466}467samples *= frame_info.channels;468if (to_skip)469{470size_t skip = MINIMP3_MIN(samples, to_skip);471to_skip -= skip;472samples -= skip;473memmove(info->buffer, info->buffer + skip, samples*sizeof(mp3d_sample_t));474}475info->samples += samples;476avg_bitrate_kbps += frame_info.bitrate_kbps;477frames++;478if (progress_cb)479{480ret = progress_cb(user_data, orig_buf_size, orig_buf_size - buf_size, &frame_info);481if (ret)482break;483}484}485} while (frame_info.frame_bytes);486if (detected_samples && info->samples > detected_samples)487info->samples = detected_samples; /* cut padding */488/* reallocate to normal buffer size */489if (allocated != info->samples*sizeof(mp3d_sample_t))490{491mp3d_sample_t *alloc_buf = (mp3d_sample_t*)realloc(info->buffer, info->samples*sizeof(mp3d_sample_t));492if (!alloc_buf && info->samples)493return MP3D_E_MEMORY;494info->buffer = alloc_buf;495}496if (frames)497info->avg_bitrate_kbps = avg_bitrate_kbps/frames;498return ret;499}500501int mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data)502{503const uint8_t *orig_buf = buf;504if (!buf || (size_t)-1 == buf_size || !callback)505return MP3D_E_PARAM;506/* skip id3 */507mp3dec_skip_id3(&buf, &buf_size);508if (!buf_size)509return 0;510mp3dec_frame_info_t frame_info;511memset(&frame_info, 0, sizeof(frame_info));512do513{514int free_format_bytes = 0, frame_size = 0, ret;515int i = mp3d_find_frame(buf, buf_size, &free_format_bytes, &frame_size);516buf += i;517buf_size -= i;518if (i && !frame_size)519continue;520if (!frame_size)521break;522const uint8_t *hdr = buf;523frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2;524frame_info.hz = hdr_sample_rate_hz(hdr);525frame_info.layer = 4 - HDR_GET_LAYER(hdr);526frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr);527frame_info.frame_bytes = frame_size;528529if (callback)530{531if ((ret = callback(user_data, hdr, frame_size, free_format_bytes, buf_size, hdr - orig_buf, &frame_info)))532return ret;533}534buf += frame_size;535buf_size -= frame_size;536} while (1);537return 0;538}539540int mp3dec_iterate_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data)541{542if (!io || !buf || (size_t)-1 == buf_size || buf_size < MINIMP3_BUF_SIZE || !callback)543return MP3D_E_PARAM;544size_t filled = io->read(buf, MINIMP3_ID3_DETECT_SIZE, io->read_data), consumed = 0;545uint64_t readed = 0;546mp3dec_frame_info_t frame_info;547int eof = 0;548memset(&frame_info, 0, sizeof(frame_info));549if (filled > MINIMP3_ID3_DETECT_SIZE)550return MP3D_E_IOERROR;551if (MINIMP3_ID3_DETECT_SIZE != filled)552return 0;553size_t id3v2size = mp3dec_skip_id3v2(buf, filled);554if (id3v2size)555{556if (io->seek(id3v2size, io->seek_data))557return MP3D_E_IOERROR;558filled = io->read(buf, buf_size, io->read_data);559if (filled > buf_size)560return MP3D_E_IOERROR;561readed += id3v2size;562} else563{564size_t readed = io->read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io->read_data);565if (readed > (buf_size - MINIMP3_ID3_DETECT_SIZE))566return MP3D_E_IOERROR;567filled += readed;568}569if (filled < MINIMP3_BUF_SIZE)570mp3dec_skip_id3v1(buf, &filled);571do572{573int free_format_bytes = 0, frame_size = 0, ret;574int i = mp3d_find_frame(buf + consumed, filled - consumed, &free_format_bytes, &frame_size);575if (i && !frame_size)576{577consumed += i;578continue;579}580if (!frame_size)581break;582const uint8_t *hdr = buf + consumed + i;583frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2;584frame_info.hz = hdr_sample_rate_hz(hdr);585frame_info.layer = 4 - HDR_GET_LAYER(hdr);586frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr);587frame_info.frame_bytes = frame_size;588589readed += i;590if (callback)591{592if ((ret = callback(user_data, hdr, frame_size, free_format_bytes, filled - consumed, readed, &frame_info)))593return ret;594}595readed += frame_size;596consumed += i + frame_size;597if (!eof && filled - consumed < MINIMP3_BUF_SIZE)598{ /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */599memmove(buf, buf + consumed, filled - consumed);600filled -= consumed;601consumed = 0;602size_t readed = io->read(buf + filled, buf_size - filled, io->read_data);603if (readed > (buf_size - filled))604return MP3D_E_IOERROR;605if (readed != (buf_size - filled))606eof = 1;607filled += readed;608if (eof)609mp3dec_skip_id3v1(buf, &filled);610}611} while (1);612return 0;613}614615static 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)616{617mp3dec_frame_t *idx_frame;618mp3dec_ex_t *dec = (mp3dec_ex_t *)user_data;619if (!dec->index.frames && !dec->start_offset)620{ /* detect VBR tag and try to avoid full scan */621uint32_t frames;622int delay, padding;623dec->info = *info;624dec->start_offset = dec->offset = offset;625dec->end_offset = offset + buf_size;626dec->free_format_bytes = free_format_bytes; /* should not change */627if (3 == dec->info.layer)628{629int ret = mp3dec_check_vbrtag(frame, frame_size, &frames, &delay, &padding);630if (ret)631dec->start_offset = dec->offset = offset + frame_size;632if (ret > 0)633{634padding *= info->channels;635dec->start_delay = dec->to_skip = delay*info->channels;636dec->samples = hdr_frame_samples(frame)*info->channels*(uint64_t)frames;637if (dec->samples >= (uint64_t)dec->start_delay)638dec->samples -= dec->start_delay;639if (padding > 0 && dec->samples >= (uint64_t)padding)640dec->samples -= padding;641dec->detected_samples = dec->samples;642dec->vbr_tag_found = 1;643return MP3D_E_USER;644} else if (ret < 0)645return 0;646}647}648if (dec->flags & MP3D_DO_NOT_SCAN)649return MP3D_E_USER;650if (dec->index.num_frames + 1 > dec->index.capacity)651{652if (!dec->index.capacity)653dec->index.capacity = 4096;654else655dec->index.capacity *= 2;656mp3dec_frame_t *alloc_buf = (mp3dec_frame_t *)realloc((void*)dec->index.frames, sizeof(mp3dec_frame_t)*dec->index.capacity);657if (!alloc_buf)658return MP3D_E_MEMORY;659dec->index.frames = alloc_buf;660}661idx_frame = &dec->index.frames[dec->index.num_frames++];662idx_frame->offset = offset;663idx_frame->sample = dec->samples;664if (!dec->buffer_samples && dec->index.num_frames < 256)665{ /* for some cutted mp3 frames, bit-reservoir not filled and decoding can't be started from first frames */666/* try to decode up to 255 first frames till samples starts to decode */667dec->buffer_samples = mp3dec_decode_frame(&dec->mp3d, frame, MINIMP3_MIN(buf_size, (size_t)INT_MAX), dec->buffer, info);668dec->samples += dec->buffer_samples*info->channels;669} else670dec->samples += hdr_frame_samples(frame)*info->channels;671return 0;672}673674int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int flags)675{676if (!dec || !buf || (size_t)-1 == buf_size || (flags & (~MP3D_FLAGS_MASK)))677return MP3D_E_PARAM;678memset(dec, 0, sizeof(*dec));679dec->file.buffer = buf;680dec->file.size = buf_size;681dec->flags = flags;682mp3dec_init(&dec->mp3d);683int ret = mp3dec_iterate_buf(dec->file.buffer, dec->file.size, mp3dec_load_index, dec);684if (ret && MP3D_E_USER != ret)685return ret;686mp3dec_init(&dec->mp3d);687dec->buffer_samples = 0;688dec->indexes_built = !(dec->vbr_tag_found || (flags & MP3D_DO_NOT_SCAN));689dec->flags &= (~MP3D_DO_NOT_SCAN);690return 0;691}692693#ifndef MINIMP3_SEEK_IDX_LINEAR_SEARCH694static size_t mp3dec_idx_binary_search(mp3dec_index_t *idx, uint64_t position)695{696size_t end = idx->num_frames, start = 0, index = 0;697while (start <= end)698{699size_t mid = (start + end) / 2;700if (idx->frames[mid].sample >= position)701{ /* move left side. */702if (idx->frames[mid].sample == position)703return mid;704end = mid - 1;705} else706{ /* move to right side */707index = mid;708start = mid + 1;709if (start == idx->num_frames)710break;711}712}713return index;714}715#endif716717int mp3dec_ex_seek(mp3dec_ex_t *dec, uint64_t position)718{719size_t i;720if (!dec)721return MP3D_E_PARAM;722if (!(dec->flags & MP3D_SEEK_TO_SAMPLE))723{724if (dec->io)725{726dec->offset = position;727} else728{729dec->offset = MINIMP3_MIN(position, dec->file.size);730}731dec->cur_sample = 0;732goto do_exit;733}734dec->cur_sample = position;735position += dec->start_delay;736if (0 == position)737{ /* optimize seek to zero, no index needed */738seek_zero:739dec->offset = dec->start_offset;740dec->to_skip = 0;741goto do_exit;742}743if (!dec->indexes_built)744{ /* no index created yet (vbr tag used to calculate track length or MP3D_DO_NOT_SCAN open flag used) */745dec->indexes_built = 1;746dec->samples = 0;747dec->buffer_samples = 0;748if (dec->io)749{750if (dec->io->seek(dec->start_offset, dec->io->seek_data))751return MP3D_E_IOERROR;752int ret = mp3dec_iterate_cb(dec->io, (uint8_t *)dec->file.buffer, dec->file.size, mp3dec_load_index, dec);753if (ret && MP3D_E_USER != ret)754return ret;755} else756{757int ret = mp3dec_iterate_buf(dec->file.buffer + dec->start_offset, dec->file.size - dec->start_offset, mp3dec_load_index, dec);758if (ret && MP3D_E_USER != ret)759return ret;760}761for (i = 0; i < dec->index.num_frames; i++)762dec->index.frames[i].offset += dec->start_offset;763dec->samples = dec->detected_samples;764}765if (!dec->index.frames)766goto seek_zero; /* no frames in file - seek to zero */767#ifdef MINIMP3_SEEK_IDX_LINEAR_SEARCH768for (i = 0; i < dec->index.num_frames; i++)769{770if (dec->index.frames[i].sample >= position)771break;772}773#else774i = mp3dec_idx_binary_search(&dec->index, position);775#endif776if (i)777{778int to_fill_bytes = 511;779int skip_frames = MINIMP3_PREDECODE_FRAMES780#ifdef MINIMP3_SEEK_IDX_LINEAR_SEARCH781+ ((dec->index.frames[i].sample == position) ? 0 : 1)782#endif783;784i -= MINIMP3_MIN(i, (size_t)skip_frames);785if (3 == dec->info.layer)786{787while (i && to_fill_bytes)788{ /* make sure bit-reservoir is filled when we start decoding */789bs_t bs[1];790L3_gr_info_t gr_info[4];791int frame_bytes, frame_size;792const uint8_t *hdr;793if (dec->io)794{795hdr = dec->file.buffer;796if (dec->io->seek(dec->index.frames[i - 1].offset, dec->io->seek_data))797return MP3D_E_IOERROR;798size_t readed = dec->io->read((uint8_t *)hdr, HDR_SIZE, dec->io->read_data);799if (readed != HDR_SIZE)800return MP3D_E_IOERROR;801frame_size = hdr_frame_bytes(hdr, dec->free_format_bytes) + hdr_padding(hdr);802readed = dec->io->read((uint8_t *)hdr + HDR_SIZE, frame_size - HDR_SIZE, dec->io->read_data);803if (readed != (size_t)(frame_size - HDR_SIZE))804return MP3D_E_IOERROR;805bs_init(bs, hdr + HDR_SIZE, frame_size - HDR_SIZE);806} else807{808hdr = dec->file.buffer + dec->index.frames[i - 1].offset;809frame_size = hdr_frame_bytes(hdr, dec->free_format_bytes) + hdr_padding(hdr);810bs_init(bs, hdr + HDR_SIZE, frame_size - HDR_SIZE);811}812if (HDR_IS_CRC(hdr))813get_bits(bs, 16);814i--;815if (L3_read_side_info(bs, gr_info, hdr) < 0)816break; /* frame not decodable, we can start from here */817frame_bytes = (bs->limit - bs->pos)/8;818to_fill_bytes -= MINIMP3_MIN(to_fill_bytes, frame_bytes);819}820}821}822dec->offset = dec->index.frames[i].offset;823dec->to_skip = position - dec->index.frames[i].sample;824while ((i + 1) < dec->index.num_frames && !dec->index.frames[i].sample && !dec->index.frames[i + 1].sample)825{ /* skip not decodable first frames */826const uint8_t *hdr;827if (dec->io)828{829hdr = dec->file.buffer;830if (dec->io->seek(dec->index.frames[i].offset, dec->io->seek_data))831return MP3D_E_IOERROR;832size_t readed = dec->io->read((uint8_t *)hdr, HDR_SIZE, dec->io->read_data);833if (readed != HDR_SIZE)834return MP3D_E_IOERROR;835} else836hdr = dec->file.buffer + dec->index.frames[i].offset;837dec->to_skip += hdr_frame_samples(hdr)*dec->info.channels;838i++;839}840do_exit:841if (dec->io)842{843if (dec->io->seek(dec->offset, dec->io->seek_data))844return MP3D_E_IOERROR;845}846dec->buffer_samples = 0;847dec->buffer_consumed = 0;848dec->input_consumed = 0;849dec->input_filled = 0;850dec->last_error = 0;851mp3dec_init(&dec->mp3d);852return 0;853}854855size_t mp3dec_ex_read_frame(mp3dec_ex_t *dec, mp3d_sample_t **buf, mp3dec_frame_info_t *frame_info, size_t max_samples)856{857if (!dec || !buf || !frame_info)858{859if (dec)860dec->last_error = MP3D_E_PARAM;861return 0;862}863if (dec->detected_samples && dec->cur_sample >= dec->detected_samples)864return 0; /* at end of stream */865if (dec->last_error)866return 0; /* error eof state, seek can reset it */867*buf = NULL;868uint64_t end_offset = dec->end_offset ? dec->end_offset : dec->file.size;869int eof = 0;870while (dec->buffer_consumed == dec->buffer_samples)871{872const uint8_t *dec_buf;873if (dec->io)874{875if (!eof && (dec->input_filled - dec->input_consumed) < MINIMP3_BUF_SIZE)876{ /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */877memmove((uint8_t*)dec->file.buffer, (uint8_t*)dec->file.buffer + dec->input_consumed, dec->input_filled - dec->input_consumed);878dec->input_filled -= dec->input_consumed;879dec->input_consumed = 0;880size_t readed = dec->io->read((uint8_t*)dec->file.buffer + dec->input_filled, dec->file.size - dec->input_filled, dec->io->read_data);881if (readed > (dec->file.size - dec->input_filled))882{883dec->last_error = MP3D_E_IOERROR;884readed = 0;885}886if (readed != (dec->file.size - dec->input_filled))887eof = 1;888dec->input_filled += readed;889if (eof)890mp3dec_skip_id3v1((uint8_t*)dec->file.buffer, &dec->input_filled);891}892dec_buf = dec->file.buffer + dec->input_consumed;893if (!(dec->input_filled - dec->input_consumed))894return 0;895dec->buffer_samples = mp3dec_decode_frame(&dec->mp3d, dec_buf, dec->input_filled - dec->input_consumed, dec->buffer, frame_info);896dec->input_consumed += frame_info->frame_bytes;897} else898{899dec_buf = dec->file.buffer + dec->offset;900uint64_t buf_size = end_offset - dec->offset;901if (!buf_size)902return 0;903dec->buffer_samples = mp3dec_decode_frame(&dec->mp3d, dec_buf, MINIMP3_MIN(buf_size, (uint64_t)INT_MAX), dec->buffer, frame_info);904}905dec->buffer_consumed = 0;906if (dec->info.hz != frame_info->hz || dec->info.layer != frame_info->layer)907{908return_e_decode:909dec->last_error = MP3D_E_DECODE;910return 0;911}912if (dec->buffer_samples)913{914dec->buffer_samples *= frame_info->channels;915if (dec->to_skip)916{917size_t skip = MINIMP3_MIN(dec->buffer_samples, dec->to_skip);918dec->buffer_consumed += skip;919dec->to_skip -= skip;920}921if (922#ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION923!(dec->flags & MP3D_ALLOW_MONO_STEREO_TRANSITION) &&924#endif925dec->buffer_consumed != dec->buffer_samples && dec->info.channels != frame_info->channels)926{927goto return_e_decode;928}929} else if (dec->to_skip)930{ /* In mp3 decoding not always can start decode from any frame because of bit reservoir,931count skip samples for such frames */932int frame_samples = hdr_frame_samples(dec_buf)*frame_info->channels;933dec->to_skip -= MINIMP3_MIN(frame_samples, dec->to_skip);934}935dec->offset += frame_info->frame_bytes;936}937size_t out_samples = MINIMP3_MIN((size_t)(dec->buffer_samples - dec->buffer_consumed), max_samples);938if (dec->detected_samples)939{ /* count decoded samples to properly cut padding */940if (dec->cur_sample + out_samples >= dec->detected_samples)941out_samples = dec->detected_samples - dec->cur_sample;942}943dec->cur_sample += out_samples;944*buf = dec->buffer + dec->buffer_consumed;945dec->buffer_consumed += out_samples;946return out_samples;947}948949size_t mp3dec_ex_read(mp3dec_ex_t *dec, mp3d_sample_t *buf, size_t samples)950{951if (!dec || !buf)952{953if (dec)954dec->last_error = MP3D_E_PARAM;955return 0;956}957mp3dec_frame_info_t frame_info;958memset(&frame_info, 0, sizeof(frame_info));959size_t samples_requested = samples;960while (samples)961{962mp3d_sample_t *buf_frame = NULL;963size_t read_samples = mp3dec_ex_read_frame(dec, &buf_frame, &frame_info, samples);964if (!read_samples)965{966break;967}968memcpy(buf, buf_frame, read_samples * sizeof(mp3d_sample_t));969buf += read_samples;970samples -= read_samples;971}972return samples_requested - samples;973}974975int mp3dec_ex_open_cb(mp3dec_ex_t *dec, mp3dec_io_t *io, int flags)976{977if (!dec || !io || (flags & (~MP3D_FLAGS_MASK)))978return MP3D_E_PARAM;979memset(dec, 0, sizeof(*dec));980#ifdef MINIMP3_HAVE_RING981int ret;982if (ret = mp3dec_open_ring(&dec->file, MINIMP3_IO_SIZE))983return ret;984#else985dec->file.size = MINIMP3_IO_SIZE;986dec->file.buffer = (const uint8_t*)malloc(dec->file.size);987if (!dec->file.buffer)988return MP3D_E_MEMORY;989#endif990dec->flags = flags;991dec->io = io;992mp3dec_init(&dec->mp3d);993if (io->seek(0, io->seek_data))994return MP3D_E_IOERROR;995int ret = mp3dec_iterate_cb(io, (uint8_t *)dec->file.buffer, dec->file.size, mp3dec_load_index, dec);996if (ret && MP3D_E_USER != ret)997return ret;998if (dec->io->seek(dec->start_offset, dec->io->seek_data))999return MP3D_E_IOERROR;1000mp3dec_init(&dec->mp3d);1001dec->buffer_samples = 0;1002dec->indexes_built = !(dec->vbr_tag_found || (flags & MP3D_DO_NOT_SCAN));1003dec->flags &= (~MP3D_DO_NOT_SCAN);1004return 0;1005}100610071008#ifndef MINIMP3_NO_STDIO10091010#if defined(__linux__) || defined(__FreeBSD__)1011#include <errno.h>1012#include <sys/mman.h>1013#include <sys/types.h>1014#include <sys/stat.h>1015#include <unistd.h>1016#include <fcntl.h>1017#if !defined(_GNU_SOURCE)1018#include <sys/ipc.h>1019#include <sys/shm.h>1020#endif1021#if !defined(MAP_POPULATE) && defined(__linux__)1022#define MAP_POPULATE 0x080001023#elif !defined(MAP_POPULATE)1024#define MAP_POPULATE 01025#endif10261027static void mp3dec_close_file(mp3dec_map_info_t *map_info)1028{1029if (map_info->buffer && MAP_FAILED != map_info->buffer)1030munmap((void *)map_info->buffer, map_info->size);1031map_info->buffer = 0;1032map_info->size = 0;1033}10341035static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)1036{1037if (!file_name)1038return MP3D_E_PARAM;1039int file;1040struct stat st;1041memset(map_info, 0, sizeof(*map_info));1042retry_open:1043file = open(file_name, O_RDONLY);1044if (file < 0 && (errno == EAGAIN || errno == EINTR))1045goto retry_open;1046if (file < 0 || fstat(file, &st) < 0)1047{1048close(file);1049return MP3D_E_IOERROR;1050}10511052map_info->size = st.st_size;1053retry_mmap:1054map_info->buffer = (const uint8_t *)mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, file, 0);1055if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR))1056goto retry_mmap;1057close(file);1058if (MAP_FAILED == map_info->buffer)1059return MP3D_E_IOERROR;1060return 0;1061}10621063#if MINIMP3_ENABLE_RING && defined(__linux__) && defined(_GNU_SOURCE)1064#define MINIMP3_HAVE_RING1065static void mp3dec_close_ring(mp3dec_map_info_t *map_info)1066{1067#if defined(__linux__) && defined(_GNU_SOURCE)1068if (map_info->buffer && MAP_FAILED != map_info->buffer)1069munmap((void *)map_info->buffer, map_info->size*2);1070#else1071if (map_info->buffer)1072{1073shmdt(map_info->buffer);1074shmdt(map_info->buffer + map_info->size);1075}1076#endif1077map_info->buffer = 0;1078map_info->size = 0;1079}10801081static int mp3dec_open_ring(mp3dec_map_info_t *map_info, size_t size)1082{1083int memfd, page_size;1084#if defined(__linux__) && defined(_GNU_SOURCE)1085void *buffer;1086int res;1087#endif1088memset(map_info, 0, sizeof(*map_info));10891090#ifdef _SC_PAGESIZE1091page_size = sysconf(_SC_PAGESIZE);1092#else1093page_size = getpagesize();1094#endif1095map_info->size = (size + page_size - 1)/page_size*page_size;10961097#if defined(__linux__) && defined(_GNU_SOURCE)1098memfd = memfd_create("mp3_ring", 0);1099if (memfd < 0)1100return MP3D_E_MEMORY;11011102retry_ftruncate:1103res = ftruncate(memfd, map_info->size);1104if (res && (errno == EAGAIN || errno == EINTR))1105goto retry_ftruncate;1106if (res)1107goto error;11081109retry_mmap:1110map_info->buffer = (const uint8_t *)mmap(NULL, map_info->size*2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);1111if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR))1112goto retry_mmap;1113if (MAP_FAILED == map_info->buffer || !map_info->buffer)1114goto error;1115retry_mmap2:1116buffer = mmap((void *)map_info->buffer, map_info->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, memfd, 0);1117if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR))1118goto retry_mmap2;1119if (MAP_FAILED == map_info->buffer || buffer != (void *)map_info->buffer)1120goto error;1121retry_mmap3:1122buffer = mmap((void *)map_info->buffer + map_info->size, map_info->size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, memfd, 0);1123if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR))1124goto retry_mmap3;1125if (MAP_FAILED == map_info->buffer || buffer != (void *)(map_info->buffer + map_info->size))1126goto error;11271128close(memfd);1129return 0;1130error:1131close(memfd);1132mp3dec_close_ring(map_info);1133return MP3D_E_MEMORY;1134#else1135memfd = shmget(IPC_PRIVATE, map_info->size, IPC_CREAT | 0700);1136if (memfd < 0)1137return MP3D_E_MEMORY;1138retry_mmap:1139map_info->buffer = (const uint8_t *)mmap(NULL, map_info->size*2, PROT_NONE, MAP_PRIVATE, -1, 0);1140if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR))1141goto retry_mmap;1142if (MAP_FAILED == map_info->buffer)1143goto error;1144if (map_info->buffer != shmat(memfd, map_info->buffer, 0))1145goto error;1146if ((map_info->buffer + map_info->size) != shmat(memfd, map_info->buffer + map_info->size, 0))1147goto error;1148if (shmctl(memfd, IPC_RMID, NULL) < 0)1149return MP3D_E_MEMORY;1150return 0;1151error:1152shmctl(memfd, IPC_RMID, NULL);1153mp3dec_close_ring(map_info);1154return MP3D_E_MEMORY;1155#endif1156}1157#endif /*MINIMP3_ENABLE_RING*/1158#elif defined(_WIN32)1159#include <windows.h>11601161static void mp3dec_close_file(mp3dec_map_info_t *map_info)1162{1163if (map_info->buffer)1164UnmapViewOfFile(map_info->buffer);1165map_info->buffer = 0;1166map_info->size = 0;1167}11681169static int mp3dec_open_file_h(HANDLE file, mp3dec_map_info_t *map_info)1170{1171memset(map_info, 0, sizeof(*map_info));11721173HANDLE mapping = NULL;1174LARGE_INTEGER s;1175s.LowPart = GetFileSize(file, (DWORD*)&s.HighPart);1176if (s.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)1177goto error;1178map_info->size = s.QuadPart;11791180mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);1181if (!mapping)1182goto error;1183map_info->buffer = (const uint8_t*)MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, s.QuadPart);1184CloseHandle(mapping);1185if (!map_info->buffer)1186goto error;11871188CloseHandle(file);1189return 0;1190error:1191mp3dec_close_file(map_info);1192CloseHandle(file);1193return MP3D_E_IOERROR;1194}11951196static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)1197{1198if (!file_name)1199return MP3D_E_PARAM;1200HANDLE file = CreateFileA(file_name, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);1201if (INVALID_HANDLE_VALUE == file)1202return MP3D_E_IOERROR;1203return mp3dec_open_file_h(file, map_info);1204}12051206static int mp3dec_open_file_w(const wchar_t *file_name, mp3dec_map_info_t *map_info)1207{1208if (!file_name)1209return MP3D_E_PARAM;1210HANDLE file = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);1211if (INVALID_HANDLE_VALUE == file)1212return MP3D_E_IOERROR;1213return mp3dec_open_file_h(file, map_info);1214}1215#else1216#include <stdio.h>12171218static void mp3dec_close_file(mp3dec_map_info_t *map_info)1219{1220if (map_info->buffer)1221free((void *)map_info->buffer);1222map_info->buffer = 0;1223map_info->size = 0;1224}12251226static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)1227{1228if (!file_name)1229return MP3D_E_PARAM;1230memset(map_info, 0, sizeof(*map_info));1231FILE *file = fopen(file_name, "rb");1232if (!file)1233return MP3D_E_IOERROR;1234int res = MP3D_E_IOERROR;1235long size = -1;1236if (fseek(file, 0, SEEK_END))1237goto error;1238size = ftell(file);1239if (size < 0)1240goto error;1241map_info->size = (size_t)size;1242if (fseek(file, 0, SEEK_SET))1243goto error;1244map_info->buffer = (uint8_t *)malloc(map_info->size);1245if (!map_info->buffer)1246{1247res = MP3D_E_MEMORY;1248goto error;1249}1250if (fread((void *)map_info->buffer, 1, map_info->size, file) != map_info->size)1251goto error;1252fclose(file);1253return 0;1254error:1255mp3dec_close_file(map_info);1256fclose(file);1257return res;1258}1259#endif12601261static int mp3dec_detect_mapinfo(mp3dec_map_info_t *map_info)1262{1263int ret = mp3dec_detect_buf(map_info->buffer, map_info->size);1264mp3dec_close_file(map_info);1265return ret;1266}12671268static 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)1269{1270int ret = mp3dec_load_buf(dec, map_info->buffer, map_info->size, info, progress_cb, user_data);1271mp3dec_close_file(map_info);1272return ret;1273}12741275static int mp3dec_iterate_mapinfo(mp3dec_map_info_t *map_info, MP3D_ITERATE_CB callback, void *user_data)1276{1277int ret = mp3dec_iterate_buf(map_info->buffer, map_info->size, callback, user_data);1278mp3dec_close_file(map_info);1279return ret;1280}12811282static int mp3dec_ex_open_mapinfo(mp3dec_ex_t *dec, int flags)1283{1284int ret = mp3dec_ex_open_buf(dec, dec->file.buffer, dec->file.size, flags);1285dec->is_file = 1;1286if (ret)1287mp3dec_ex_close(dec);1288return ret;1289}12901291int mp3dec_detect(const char *file_name)1292{1293int ret;1294mp3dec_map_info_t map_info;1295if ((ret = mp3dec_open_file(file_name, &map_info)))1296return ret;1297return mp3dec_detect_mapinfo(&map_info);1298}12991300int mp3dec_load(mp3dec_t *dec, const char *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data)1301{1302int ret;1303mp3dec_map_info_t map_info;1304if ((ret = mp3dec_open_file(file_name, &map_info)))1305return ret;1306return mp3dec_load_mapinfo(dec, &map_info, info, progress_cb, user_data);1307}13081309int mp3dec_iterate(const char *file_name, MP3D_ITERATE_CB callback, void *user_data)1310{1311int ret;1312mp3dec_map_info_t map_info;1313if ((ret = mp3dec_open_file(file_name, &map_info)))1314return ret;1315return mp3dec_iterate_mapinfo(&map_info, callback, user_data);1316}13171318int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int flags)1319{1320int ret;1321if (!dec)1322return MP3D_E_PARAM;1323if ((ret = mp3dec_open_file(file_name, &dec->file)))1324return ret;1325return mp3dec_ex_open_mapinfo(dec, flags);1326}13271328void mp3dec_ex_close(mp3dec_ex_t *dec)1329{1330#ifdef MINIMP3_HAVE_RING1331if (dec->io)1332mp3dec_close_ring(&dec->file);1333#else1334if (dec->io && dec->file.buffer)1335free((void*)dec->file.buffer);1336#endif1337if (dec->is_file)1338mp3dec_close_file(&dec->file);1339if (dec->index.frames)1340free(dec->index.frames);1341memset(dec, 0, sizeof(*dec));1342}13431344#ifdef _WIN321345int mp3dec_detect_w(const wchar_t *file_name)1346{1347int ret;1348mp3dec_map_info_t map_info;1349if ((ret = mp3dec_open_file_w(file_name, &map_info)))1350return ret;1351return mp3dec_detect_mapinfo(&map_info);1352}13531354int mp3dec_load_w(mp3dec_t *dec, const wchar_t *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data)1355{1356int ret;1357mp3dec_map_info_t map_info;1358if ((ret = mp3dec_open_file_w(file_name, &map_info)))1359return ret;1360return mp3dec_load_mapinfo(dec, &map_info, info, progress_cb, user_data);1361}13621363int mp3dec_iterate_w(const wchar_t *file_name, MP3D_ITERATE_CB callback, void *user_data)1364{1365int ret;1366mp3dec_map_info_t map_info;1367if ((ret = mp3dec_open_file_w(file_name, &map_info)))1368return ret;1369return mp3dec_iterate_mapinfo(&map_info, callback, user_data);1370}13711372int mp3dec_ex_open_w(mp3dec_ex_t *dec, const wchar_t *file_name, int flags)1373{1374int ret;1375if ((ret = mp3dec_open_file_w(file_name, &dec->file)))1376return ret;1377return mp3dec_ex_open_mapinfo(dec, flags);1378}1379#endif1380#else /* MINIMP3_NO_STDIO */1381void mp3dec_ex_close(mp3dec_ex_t *dec)1382{1383#ifdef MINIMP3_HAVE_RING1384if (dec->io)1385mp3dec_close_ring(&dec->file);1386#else1387if (dec->io && dec->file.buffer)1388free((void*)dec->file.buffer);1389#endif1390if (dec->index.frames)1391free(dec->index.frames);1392memset(dec, 0, sizeof(*dec));1393}1394#endif13951396#endif /* MINIMP3_IMPLEMENTATION && !_MINIMP3_EX_IMPLEMENTATION_GUARD */139713981399