123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398 |
- #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 <stddef.h>
- #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*/
- #if defined(MINIMP3_IMPLEMENTATION) && !defined(_MINIMP3_EX_IMPLEMENTATION_GUARD)
- #define _MINIMP3_EX_IMPLEMENTATION_GUARD
- #include <limits.h>
- #include "minimp3.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;
- s.LowPart = GetFileSize(file, (DWORD*)&s.HighPart);
- 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;
- }
- 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
- #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)
- {
- #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->index.frames)
- free(dec->index.frames);
- memset(dec, 0, sizeof(*dec));
- }
- #endif
- #endif /* MINIMP3_IMPLEMENTATION && !_MINIMP3_EX_IMPLEMENTATION_GUARD */
|