#ifndef MINIMP3_EXT_H
#define MINIMP3_EXT_H
#include <stddef.h>
#include "minimp3.h"
#define MP3D_SEEK_TO_BYTE …
#define MP3D_SEEK_TO_SAMPLE …
#define MP3D_DO_NOT_SCAN …
#ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION
#define MP3D_ALLOW_MONO_STEREO_TRANSITION …
#define MP3D_FLAGS_MASK …
#else
#define MP3D_FLAGS_MASK …
#endif
#define MINIMP3_PREDECODE_FRAMES …
#define MINIMP3_IO_SIZE …
#define MINIMP3_BUF_SIZE …
#define MINIMP3_ENABLE_RING …
#define MP3D_E_PARAM …
#define MP3D_E_MEMORY …
#define MP3D_E_IOERROR …
#define MP3D_E_USER …
#define MP3D_E_DECODE …
mp3dec_file_info_t;
mp3dec_map_info_t;
mp3dec_frame_t;
mp3dec_index_t;
MP3D_READ_CB;
MP3D_SEEK_CB;
mp3dec_io_t;
mp3dec_ex_t;
MP3D_ITERATE_CB;
MP3D_PROGRESS_CB;
#ifdef __cplusplus
extern "C" {
#endif
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);
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);
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);
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
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
#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)
{ … }
static size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size)
{ … }
static void mp3dec_skip_id3(const uint8_t **pbuf, size_t *pbuf_size)
{ … }
static int mp3dec_check_vbrtag(const uint8_t *frame, int frame_size, uint32_t *frames, int *delay, int *padding)
{ … }
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)
{ … }
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)
{ … }
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)
{ … }
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)
{ … }
int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int flags)
{ … }
#ifndef MINIMP3_SEEK_IDX_LINEAR_SEARCH
static size_t mp3dec_idx_binary_search(mp3dec_index_t *idx, uint64_t position)
{ … }
#endif
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)
{ … }
int mp3dec_ex_open_cb(mp3dec_ex_t *dec, mp3dec_io_t *io, int flags)
{ … }
#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 …
#elif !defined(MAP_POPULATE)
#define MAP_POPULATE …
#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
#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
void mp3dec_ex_close(mp3dec_ex_t *dec)
{ … }
#endif
#endif