vgmstream/src/coding/mpeg_decoder.c
bnnm 0e10b75beb Fix interleaved FSB MPEG decoding
Padding doesn't apply to Layer II FSBs, and it needed to manually get
info from MPEG frames since mpg123 functions weren't always working.
2017-03-13 20:04:09 +01:00

808 lines
29 KiB
C

#include "coding.h"
#include "../util.h"
#include "../vgmstream.h"
#ifdef VGM_USE_MPEG
#include <mpg123.h>
#define AHX_EXPECTED_FRAME_SIZE 0x414
#define MPEG_DEFAULT_BUFFER_SIZE 0x1000 /* should be >= AHX_EXPECTED_FRAME_SIZE */
typedef struct {
int version;
int layer;
int bit_rate;
int sample_rate;
int frame_samples;
int frame_size; /* bytes */
int channels;
coding_t coding_type;
} mpeg_frame_info;
static mpg123_handle * init_mpg123_handle();
static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream, size_t block_size);
static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, off_t offset);
static int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info);
/**
* Inits regular MPEG.
*/
mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) {
mpeg_codec_data *data = NULL;
/* init codec */
data = calloc(1,sizeof(mpeg_codec_data));
if (!data) goto fail;
data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE;
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
if (!data->buffer) goto fail;
data->m = init_mpg123_handle();
if (!data->m) goto fail;
/* check format */
{
mpg123_handle *main_m = data->m;
off_t read_offset = 0;
int rc;
long sample_rate_per_frame;
int channels_per_frame, encoding;
size_t samples_per_frame;
struct mpg123_frameinfo mi;
/* read first frame(s) */
do {
size_t bytes_done;
if (read_streamfile(data->buffer, start_offset+read_offset, data->buffer_size, streamfile) != data->buffer_size)
goto fail;
read_offset+=1;
rc = mpg123_decode(main_m, data->buffer,data->buffer_size, NULL,0, &bytes_done);
if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) {
VGM_LOG("MPEG: unable to set up mpp123 @ 0x%08lx to 0x%08lx\n", start_offset, read_offset);
goto fail; //todo handle MPG123_DONE?
}
if (read_offset > 0x5000) { /* don't hang in some incorrectly detected formats */
VGM_LOG("MPEG: unable to find mpeg data @ 0x%08lx to 0x%08lx\n", start_offset, read_offset);
goto fail;
}
} while (rc != MPG123_NEW_FORMAT);
/* check first frame header and validate */
rc = mpg123_getformat(main_m,&sample_rate_per_frame,&channels_per_frame,&encoding);
if (rc != MPG123_OK) goto fail;
mpg123_info(main_m,&mi);
if (encoding != MPG123_ENC_SIGNED_16)
goto fail;
if (sample_rate_per_frame != mi.rate)
goto fail;
if ((channels != -1 && channels_per_frame != channels))
goto fail;
if (mi.version == MPG123_1_0 && mi.layer == 1)
*coding_type = coding_MPEG1_L1;
else if (mi.version == MPG123_1_0 && mi.layer == 2)
*coding_type = coding_MPEG1_L2;
else if (mi.version == MPG123_1_0 && mi.layer == 3)
*coding_type = coding_MPEG1_L3;
else if (mi.version == MPG123_2_0 && mi.layer == 1)
*coding_type = coding_MPEG2_L1;
else if (mi.version == MPG123_2_0 && mi.layer == 2)
*coding_type = coding_MPEG2_L2;
else if (mi.version == MPG123_2_0 && mi.layer == 3)
*coding_type = coding_MPEG2_L3;
else if (mi.version == MPG123_2_5 && mi.layer == 1)
*coding_type = coding_MPEG25_L1;
else if (mi.version == MPG123_2_5 && mi.layer == 2)
*coding_type = coding_MPEG25_L2;
else if (mi.version == MPG123_2_5 && mi.layer == 3)
*coding_type = coding_MPEG25_L3;
else goto fail;
if (mi.layer == 1)
samples_per_frame = 384;
else if (mi.layer == 2)
samples_per_frame = 1152;
else if (mi.layer == 3 && mi.version == MPG123_1_0) //MP3
samples_per_frame = 1152;
else if (mi.layer == 3)
samples_per_frame = 576;
else goto fail;
data->sample_rate_per_frame = sample_rate_per_frame;
data->channels_per_frame = channels_per_frame;
data->samples_per_frame = samples_per_frame;
/* reinit, to ignore the reading we've done so far */
mpg123_open_feed(main_m);
}
return data;
fail:
free_mpeg(data);
return NULL;
}
/**
* Init interleaved MPEG (also accepts normal MPEGs, but it's less error tolerant than normal MPEG init).
*/
mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, int fixed_frame_size, int fsb_padding) {
mpeg_codec_data *data = NULL;
/* init codec */
data = calloc(1,sizeof(mpeg_codec_data));
if (!data) goto fail;
data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE;
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
if (!data->buffer) goto fail;
data->m = init_mpg123_handle();
if (!data->m) goto fail;
/* interleaved setup */
{
mpeg_frame_info info;
int i;
data->interleaved = 1;
/* get frame info at offset */
if ( !mpeg_get_frame_info(streamfile, start_offset, &info) )
goto fail;
*coding_type = info.coding_type;
data->channels_per_frame = info.channels;
data->sample_rate_per_frame = info.sample_rate;
data->samples_per_frame = info.frame_samples;
data->fixed_frame_size = fixed_frame_size;
data->current_frame_size = fixed_frame_size;
data->fsb_padding = fsb_padding;
/* get frame size from the first frame as it can be used externally to calc interleave */
if ( !update_frame_sizes(data, streamfile, start_offset) )
goto fail;
/* unlikely, can fixed with bigger buffer or a feed loop */
if (info.frame_size > data->buffer_size)
goto fail;
if (channels < 1 || channels > 32) goto fail; /* arbitrary max */
if (channels < data->channels_per_frame) goto fail;
//todo improve (less buffers / simplify data->ms)
/* Init interleaved audio, which needs separate decoders per stream and frame size stuff.
* We still leave data->m as a "base" info/format to simplify some stuff */
data->ms_size = channels / data->channels_per_frame;
data->ms = calloc(sizeof(mpg123_handle *), data->ms_size);
for (i=0; i < data->ms_size; i++) {
data->ms[i] = init_mpg123_handle();
if (!data->ms[i]) goto fail;
}
data->frame_buffer_size = sizeof(sample) * data->samples_per_frame * data->channels_per_frame;
data->frame_buffer = calloc(sizeof(uint8_t), data->frame_buffer_size);
if (!data->frame_buffer) goto fail;
data->interleave_buffer_size = sizeof(sample) * data->samples_per_frame * channels;
data->interleave_buffer = calloc(sizeof(uint8_t), data->interleave_buffer_size);
if (!data->interleave_buffer) goto fail;
}
return data;
fail:
free_mpeg(data);
return NULL;
}
/**
* Inits MPEG for AHX, which ignores frame headers.
*/
mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count) {
mpeg_codec_data *data = NULL;
/* init codec */
data = calloc(1,sizeof(mpeg_codec_data));
if (!data) goto fail;
data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE;
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
if (!data->buffer) goto fail;
data->m = init_mpg123_handle();
if (!data->m) goto fail;
return data;
fail:
free_mpeg(data);
return NULL;
}
static mpg123_handle * init_mpg123_handle() {
mpg123_handle *m = NULL;
int rc;
/* inits a new mpg123 handle */
m = mpg123_new(NULL,&rc);
if (rc == MPG123_NOT_INITIALIZED) {
/* inits the library if needed */
if (mpg123_init() != MPG123_OK)
goto fail;
m = mpg123_new(NULL,&rc);
if (rc != MPG123_OK) goto fail;
} else if (rc != MPG123_OK) {
goto fail;
}
mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); //todo fix gapless
mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x10000); /* should be enough */
if (mpg123_open_feed(m) != MPG123_OK) {
goto fail;
}
return m;
fail:
mpg123_delete(m);
return NULL;
}
/************/
/* DECODERS */
/************/
void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
mpeg_codec_data * data = (mpeg_codec_data *) vgmstream->codec_data;
/* MPEGs streams contain one or two channels, so we may only need half VGMSTREAMCHANNELs for offsets */
if (data->interleaved) {
decode_mpeg_interleave(vgmstream, data, outbuf, samples_to_do, channels);
} else {
decode_mpeg_default(&vgmstream->ch[0], data, outbuf, samples_to_do, channels);
}
}
/**
* Decode anything mpg123 can.
* Feeds raw data and extracts decoded samples as needed.
*/
static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
int samples_done = 0;
mpg123_handle *m = data->m;
while (samples_done < samples_to_do) {
size_t bytes_done;
int rc;
/* read more raw data */
if (!data->buffer_full) {
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->buffer_size,stream->streamfile);
/* end of stream, fill rest with 0s */
if (!data->bytes_in_buffer) {
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample));
break;
}
data->buffer_full = 1;
data->buffer_used = 0;
stream->offset += data->bytes_in_buffer;
}
/* feed new raw data to the decoder if needed, copy decoded results to output */
if (!data->buffer_used) {
rc = mpg123_decode(m,
data->buffer,data->bytes_in_buffer,
(unsigned char *)(outbuf+samples_done*channels),
(samples_to_do-samples_done)*sizeof(sample)*channels,
&bytes_done);
data->buffer_used = 1;
} else {
rc = mpg123_decode(m,
NULL,0,
(unsigned char *)(outbuf+samples_done*channels),
(samples_to_do-samples_done)*sizeof(sample)*channels,
&bytes_done);
}
/* not enough raw data, request more */
if (rc == MPG123_NEED_MORE) {
data->buffer_full = 0;
}
/* update copied samples */
samples_done += bytes_done/sizeof(sample)/channels;
}
}
/**
* Decode interleaved (multichannel) MPEG. Works with mono/stereo too.
* Channels (1 or 2), samples and frame size per stream should be constant.
*
* Reads frame 'streams' (ex. 4ch = 1+1+1+1 = 4 streams or 2+2 = 2 streams), decodes
* samples per stream and muxes them into a single internal buffer before copying to outbuf
* (to make sure channel samples are orderly copied between decode_mpeg calls).
*
* Interleave variations:
* - blocks of frames: fixed block_size per stream (unknown number of samples) [XVAG]
* (ex. b1 = N samples of ch1, b2 = N samples of ch2, b3 = M samples of ch1, etc)
* - partial frames: single frames per stream with padding (block_size is frame_size+padding) [FSB]
* (ex. f1+f3+f5 = 1152*2 samples of ch1+2, f2+f4 = 1152*2 samples of ch3+4, etc)
*/
static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
int samples_done = 0, bytes_max, bytes_to_copy;
while (samples_done < samples_to_do) {
if (data->bytes_used_in_interleave_buffer < data->bytes_in_interleave_buffer) {
/* copy remaining samples */
bytes_to_copy = data->bytes_in_interleave_buffer - data->bytes_used_in_interleave_buffer;
bytes_max = (samples_to_do - samples_done) * sizeof(sample) * channels;
if (bytes_to_copy > bytes_max)
bytes_to_copy = bytes_max;
memcpy((uint8_t*)(outbuf+samples_done*channels), data->interleave_buffer + data->bytes_used_in_interleave_buffer, bytes_to_copy);
/* update samples copied */
data->bytes_used_in_interleave_buffer += bytes_to_copy;
samples_done += bytes_to_copy / sizeof(sample) / channels;
}
else {
/* fill the internal sample buffer */
int i;
data->bytes_in_interleave_buffer = 0;
data->bytes_used_in_interleave_buffer = 0;
for (i=0; i < data->ms_size; i++) {
decode_mpeg_interleave_samples(&vgmstream->ch[i], data, data->ms[i], channels, i, vgmstream->interleave_block_size);
}
/* discard (for looping): 'remove' decoded samples from the buffer */
if (data->samples_to_discard) {
size_t bytes_to_discard = data->samples_to_discard * sizeof(sample) * channels;
/* 'remove' all buffer at most */
if (bytes_to_discard > data->bytes_in_interleave_buffer)
bytes_to_discard = data->bytes_in_interleave_buffer;
/* pretend the samples were used up */
data->bytes_used_in_interleave_buffer = bytes_to_discard;
/* and readjust discard */
data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;;
}
}
}
}
/**
* Decodes frames from a stream and muxes the samples into a intermediate buffer.
* Skips to the next interleaved block once reaching the stream's block end.
*/
static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream, size_t block_size) {
size_t bytes_done;
size_t stream_size = get_streamfile_size(stream->streamfile);
/* decode samples from 1 full frame */
do {
int rc;
/* padded frame stuff */
rc = update_frame_sizes(data, stream->streamfile, stream->offset);
/* ignore any errors and continue; mpg123 will probably sync */
VGM_ASSERT(rc==0, "MPEG: frame error @ 0x%08lx (prev size=0x%x / padding=0x%x)\n", stream->offset, data->current_frame_size, data->current_padding);
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
if (stream->offset > stream_size) {
memset(data->frame_buffer, 0, data->frame_buffer_size);
bytes_done = data->frame_buffer_size;
break;
}
/* read more raw data (only 1 frame, to check interleave block end) */
if (!data->buffer_full) {
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->current_frame_size,stream->streamfile);
/* end of stream, fill frame buffer with 0s but continue normally with other streams */
if (!data->bytes_in_buffer) {
memset(data->frame_buffer, 0, data->frame_buffer_size);
bytes_done = data->frame_buffer_size;
break;
}
data->buffer_full = 1;
data->buffer_used = 0;
stream->offset += data->current_frame_size + data->current_padding; /* skip FSB frame+garbage */
if (block_size && ((stream->offset - stream->channel_start_offset) % block_size==0)) {
stream->offset += block_size * (data->ms_size-1); /* skip a block per stream if block done */
}
}
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
if (!data->buffer_used) {
rc = mpg123_decode(m,
data->buffer, data->bytes_in_buffer,
(unsigned char *)data->frame_buffer, data->frame_buffer_size,
&bytes_done);
data->buffer_used = 1;
} else {
rc = mpg123_decode(m,
NULL,0,
(unsigned char *)data->frame_buffer, data->frame_buffer_size,
&bytes_done);
}
/* samples per frame should be constant... */
if (bytes_done > 0 && bytes_done < data->frame_buffer_size) {
VGM_LOG("MPEG: borked frame @ 0x%08lx (%i bytes done, expected %i, rc=%i)\n", stream->offset, bytes_done, data->frame_buffer_size, rc);
memset(data->frame_buffer + bytes_done, 0, data->frame_buffer_size - bytes_done);
bytes_done = data->frame_buffer_size;
}
/* not enough raw data, request more */
if (rc == MPG123_NEED_MORE) {
data->buffer_full = 0;
continue;
}
break;
} while (1);
/* copy decoded frame to intermediate sample buffer, muxing channels
* (ex stream1: ch1s1 ch1s2, stream2: ch2s1 ch2s2 > ch1s1 ch2s1 ch1s2 ch2s2) */
{
size_t samples_done;
size_t sz = sizeof(sample);
int channels_f = data->channels_per_frame;
int fch, i;
samples_done = bytes_done / sz / channels_f;
for (fch = 0; fch < channels_f; fch++) { /* channels inside the frame */
for (i = 0; i < samples_done; i++) { /* decoded samples */
off_t in_offset = sz*i*channels_f + sz*fch;
off_t out_offset = sz*i*channels + sz*(num_stream*channels_f + fch);
memcpy(data->interleave_buffer + out_offset, data->frame_buffer + in_offset, sz);
}
}
data->bytes_in_interleave_buffer += bytes_done;
}
}
/**
* Get frame size info for the current frame, needed by FSBs of varying padding.
* Padding sometimes contains next frame header so we can't feed it to mpg123 or it gets confused.
* Expected to be called at the beginning of a new frame.
*/
static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, off_t offset) {
if (!data->fixed_frame_size) {
mpeg_frame_info info;
/* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */
if ( !mpeg_get_frame_info(streamfile, offset, &info) )
goto fail;
/* could mess some calcs */
VGM_ASSERT(data->sample_rate_per_frame != info.sample_rate || data->samples_per_frame != info.frame_samples,
"MPEG: variable frame info found @ 0x%08lx", offset);
/* new frame */
data->current_frame_size = info.frame_size;
/* get FSB padding for MPEG1/2 Layer III (MPEG1 Layer II doesn't use it, and Layer I doesn't seem to be supported) */
if (data->fsb_padding && info.layer == 3) {
data->current_padding = (data->current_frame_size % data->fsb_padding) ?
data->fsb_padding - (data->current_frame_size % data->fsb_padding) : 0;
}
}
return 1;
fail:
return 0;
}
/**
* Decode AHX mono frames.
* mpg123 expects frames of 0x414 (160kbps, 22050Hz) but they actually vary and are much shorter
*/
void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do) {
int samples_done = 0;
const int frame_size = AHX_EXPECTED_FRAME_SIZE;
mpg123_handle *m = data->m;
while (samples_done < samples_to_do) {
size_t bytes_done;
int rc;
/* read more raw data */
if (!data->buffer_full) {
/* fill buffer up to next frame ending (or file ending) */
int bytes_into_header = 0;
const uint8_t header[4] = {0xff,0xf5,0xe0,0xc0};
off_t frame_offset = 0;
/* assume that we are starting at a header, skip it and look for the next one */
read_streamfile(data->buffer, stream->offset+frame_offset, 4, stream->streamfile);
frame_offset += 4;
do {
uint8_t byte;
byte = read_8bit(stream->offset+frame_offset,stream->streamfile);
data->buffer[frame_offset] = byte;
frame_offset++;
if (byte == header[bytes_into_header]) {
bytes_into_header++;
} else {
/* This might have been the first byte of the header, so
* we need to check again.
* No need to get more complicated than this, though, since
* there are no repeated characters in the search string. */
if (bytes_into_header>0) {
frame_offset--;
}
bytes_into_header=0;
}
if (bytes_into_header==4) {
break;
}
} while (frame_offset < frame_size);
if (bytes_into_header==4)
frame_offset-=4;
memset(data->buffer+frame_offset,0,frame_size-frame_offset);
data->buffer_full = 1;
data->buffer_used = 0;
stream->offset += frame_offset;
}
/* feed new raw data to the decoder if needed, copy decodec results to output */
if (!data->buffer_used) {
rc = mpg123_decode(m,
data->buffer,frame_size,
(unsigned char *)(outbuf+samples_done),
(samples_to_do-samples_done)*sizeof(sample),
&bytes_done);
data->buffer_used = 1;
} else {
rc = mpg123_decode(m,
NULL,0,
(unsigned char *)(outbuf+samples_done),
(samples_to_do-samples_done)*sizeof(sample),
&bytes_done);
}
/* not enough raw data, request more */
if (rc == MPG123_NEED_MORE) {
data->buffer_full = 0;
}
/* update copied samples */
samples_done += bytes_done/sizeof(sample);/* mono */
}
}
/*********/
/* UTILS */
/*********/
void free_mpeg(mpeg_codec_data *data) {
if (!data)
return;
mpg123_delete(data->m);
if (data->interleaved) {
int i;
for (i=0; i < data->ms_size; i++) {
mpg123_delete(data->ms[i]);
}
free(data->ms);
free(data->interleave_buffer);
free(data->frame_buffer);
}
free(data->buffer);
free(data);
/* The astute reader will note that a call to mpg123_exit is never
* made. While is is evilly breaking our contract with mpg123, it
* doesn't actually do anything except set the "initialized" flag
* to 0. And if we exit we run the risk of turning it off when
* someone else in another thread is using it. */
}
void reset_mpeg(VGMSTREAM *vgmstream) {
off_t input_offset;
mpeg_codec_data *data = vgmstream->codec_data;
/* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */
mpg123_feedseek(data->m,0,SEEK_SET,&input_offset);
/* reset multistream */ //todo check if stream offsets are properly reset
if (data->interleaved) {
int i;
for (i=0; i < data->ms_size; i++) {
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset + input_offset;
}
data->bytes_in_interleave_buffer = 0;
data->bytes_used_in_interleave_buffer = 0;
}
}
void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
/* won't work for fake AHX MPEG */
off_t input_offset;
mpeg_codec_data *data = vgmstream->codec_data;
/* seek multistream */
if (!data->interleaved) {
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
} else {
int i;
/* re-start from 0 */
for (i=0; i < data->ms_size; i++) {
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset;
}
/* manually add skip samples, since we don't really know the correct offset */
data->samples_to_discard = num_sample;
data->bytes_in_interleave_buffer = 0;
data->bytes_used_in_interleave_buffer = 0;
}
data->buffer_full = 0;
data->buffer_used = 0;
}
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) {
struct mpg123_frameinfo mi;
mpg123_handle *m = data->m;
if (MPG123_OK != mpg123_info(m, &mi))
return 0;
/* In this case just return 0 and expect to fail (if used for num_samples)
* We would need to read the number of frames in some frame header or count them to get samples */
if (mi.vbr != MPG123_CBR) //maybe abr_rate could be used
return 0;
return (int64_t)bytes * mi.rate * 8 / (mi.bitrate * 1000);
}
/**
* disables/enables stderr output, useful for MPEG known to contain recoverable errors
*/
void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
if (data->interleaved) {
int i;
for (i=0; i < data->ms_size; i++) {
mpg123_param(data->ms[i], MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
}
}
}
/*****************/
/* FRAME HELPERS */
/*****************/
/**
* Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow
* it's wrong at times (maybe because we use an ancient version) so here we do our thing.
*/
static int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info) {
/* index tables */
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
static const int layers[4] = { -1,3,2,1 };
static const int bit_rates[5][16] = { /* [version index ][bit rate index] (0=free, -1=bad) */
{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, /* MPEG1 Layer I */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, /* MPEG1 Layer II */
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, /* MPEG1 Layer III */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, /* MPEG2/2.5 Layer I */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, /* MPEG2/2.5 Layer II/III */
};
static const int sample_rates[4][4] = { /* [version][sample rate index] */
{ 44100, 48000, 32000, -1}, /* MPEG1 */
{ 22050, 24000, 16000, -1}, /* MPEG2 */
{ 11025, 12000, 8000, -1}, /* MPEG2.5 */
};
static const int channels[4] = { 2,2,2, 1 }; /* [channel] */
static const int frame_samples[3][3] = { /* [version][layer] */
{ 384, 1152, 1152 }, /* MPEG1 */
{ 384, 1152, 576 }, /* MPEG2 */
{ 384, 1152, 576 } /* MPEG2.5 */
};
static const coding_t coding_types[3][3] = { /* [version][layer] */
{ coding_MPEG1_L1, coding_MPEG1_L2, coding_MPEG1_L3 },
{ coding_MPEG2_L1, coding_MPEG2_L2, coding_MPEG2_L3 },
{ coding_MPEG25_L1, coding_MPEG25_L2, coding_MPEG25_L3 },
};
uint32_t header;
int idx, padding;
memset(info, 0, sizeof(*info));
header = read_32bitBE(offset, streamfile);
if ((header >> 21) != 0x7FF) /* 31-21: sync */
goto fail;
info->version = versions[(header >> 19) & 0x3]; /* 20,19: version */
if (info->version <= 0) goto fail;
info->layer = layers[(header >> 17) & 0x3]; /* 18,17: layer */
if (info->layer <= 0) goto fail;
//crc = (header >> 16) & 0x1; /* 16: protected by crc? */
idx = (info->version==1 ? info->layer-1 : (3 + (info->layer==1 ? 0 : 1)));
info->bit_rate = bit_rates[idx][(header >> 12) & 0xf]; /* 15-12: bit rate */
if (info->bit_rate <= 0) goto fail;
info->sample_rate = sample_rates[info->version-1][(header >> 10) & 0x3]; /* 11-10: sampling rate */
if (info->sample_rate <= 0) goto fail;
padding = (header >> 9) & 0x1; /* 9: padding? */
//private = (header >> 8) & 0x1; /* 8: private bit */
info->channels = channels[(header >> 6) & 0x3]; /* 7,6: channel mode */
//js_mode = (header >> 4) & 0x3; /* 5,4: mode extension for joint stereo */
//copyright = (header >> 3) & 0x1; /* 3: copyrighted */
//original = (header >> 2) & 0x1; /* 2: original */
//emphasis = (header >> 0) & 0x3; /* 1,0: emphasis */
info->frame_samples = frame_samples[info->version-1][info->layer-1];
/* calculate frame length (from hcs's fsb_mpeg) */
switch (info->frame_samples) {
case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break;
case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break;
case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break;
}
info->coding_type = coding_types[info->version-1][info->layer-1];
return 1;
fail:
return 0;
}
#endif