2017-02-18 00:17:38 +01:00
|
|
|
#include "coding.h"
|
|
|
|
#include "../util.h"
|
2008-07-05 13:49:29 +02:00
|
|
|
#include "../vgmstream.h"
|
|
|
|
|
|
|
|
#ifdef VGM_USE_MPEG
|
|
|
|
#include <mpg123.h>
|
2017-02-18 00:17:38 +01:00
|
|
|
|
|
|
|
#define AHX_EXPECTED_FRAME_SIZE 0x414
|
|
|
|
#define MPEG_DEFAULT_BUFFER_SIZE 0x1000 /* should be >= AHX_EXPECTED_FRAME_SIZE */
|
2008-07-05 13:49:29 +02:00
|
|
|
|
2017-03-13 20:04:09 +01:00
|
|
|
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;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-03-13 20:04:09 +01:00
|
|
|
static mpg123_handle * init_mpg123_handle();
|
2017-02-19 20:20:13 +01:00
|
|
|
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);
|
2017-03-13 20:04:09 +01:00
|
|
|
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);
|
2008-07-05 13:49:29 +02:00
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/**
|
2017-02-19 20:20:13 +01:00
|
|
|
* Inits regular MPEG.
|
2017-02-18 00:17:38 +01:00
|
|
|
*/
|
2017-02-19 21:16:09 +01:00
|
|
|
mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) {
|
2017-02-18 00:17:38 +01:00
|
|
|
mpeg_codec_data *data = NULL;
|
|
|
|
|
|
|
|
/* init codec */
|
2010-09-10 23:49:56 +02:00
|
|
|
data = calloc(1,sizeof(mpeg_codec_data));
|
2017-02-18 00:17:38 +01:00
|
|
|
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;
|
2010-09-10 23:49:56 +02:00
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
data->m = init_mpg123_handle();
|
|
|
|
if (!data->m) goto fail;
|
2010-09-10 23:49:56 +02:00
|
|
|
|
|
|
|
/* check format */
|
|
|
|
{
|
2017-02-18 18:27:21 +01:00
|
|
|
mpg123_handle *main_m = data->m;
|
|
|
|
off_t read_offset = 0;
|
|
|
|
int rc;
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
long sample_rate_per_frame;
|
|
|
|
int channels_per_frame, encoding;
|
|
|
|
size_t samples_per_frame;
|
2010-09-10 23:49:56 +02:00
|
|
|
struct mpg123_frameinfo mi;
|
2017-02-18 00:17:38 +01:00
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
/* 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);
|
2017-03-13 20:04:09 +01:00
|
|
|
if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) {
|
2017-04-13 15:04:46 +02:00
|
|
|
VGM_LOG("MPEG: unable to set up mpg123 @ 0x%08lx to 0x%08lx\n", start_offset, read_offset);
|
2017-03-13 20:04:09 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
} while (rc != MPG123_NEW_FORMAT);
|
|
|
|
|
|
|
|
/* check first frame header and validate */
|
2017-02-19 20:20:13 +01:00
|
|
|
rc = mpg123_getformat(main_m,&sample_rate_per_frame,&channels_per_frame,&encoding);
|
2017-02-18 00:17:38 +01:00
|
|
|
if (rc != MPG123_OK) goto fail;
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
mpg123_info(main_m,&mi);
|
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
if (encoding != MPG123_ENC_SIGNED_16)
|
|
|
|
goto fail;
|
2017-02-19 20:20:13 +01:00
|
|
|
if (sample_rate_per_frame != mi.rate)
|
2017-02-18 00:17:38 +01:00
|
|
|
goto fail;
|
2017-03-13 20:04:09 +01:00
|
|
|
if ((channels != -1 && channels_per_frame != channels))
|
2017-02-18 00:17:38 +01:00
|
|
|
goto fail;
|
2010-09-10 23:49:56 +02:00
|
|
|
|
2017-07-29 13:05:23 +02:00
|
|
|
switch(mi.layer) {
|
|
|
|
case 1: *coding_type = coding_MPEG_layer1; break;
|
|
|
|
case 2: *coding_type = coding_MPEG_layer2; break;
|
|
|
|
case 3: *coding_type = coding_MPEG_layer3; break;
|
|
|
|
default: goto fail;
|
|
|
|
}
|
2011-01-19 14:54:59 +01:00
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
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;
|
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
/* reinit, to ignore the reading we've done so far */
|
|
|
|
mpg123_open_feed(main_m);
|
2010-09-10 23:49:56 +02:00
|
|
|
}
|
|
|
|
|
2017-03-13 20:04:09 +01:00
|
|
|
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).
|
|
|
|
*/
|
2017-06-25 02:09:12 +02:00
|
|
|
mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, mpeg_interleave_type interleave_type, uint32_t interleave_value) {
|
2017-03-13 20:04:09 +01:00
|
|
|
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;
|
2017-02-19 20:20:13 +01:00
|
|
|
int i;
|
|
|
|
|
2017-03-13 20:04:09 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2017-06-25 02:09:12 +02:00
|
|
|
data->interleave_type = interleave_type;
|
|
|
|
data->interleave_value = interleave_value;
|
2017-03-13 20:04:09 +01:00
|
|
|
|
|
|
|
/* 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;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-02-19 21:16:09 +01:00
|
|
|
if (channels < 1 || channels > 32) goto fail; /* arbitrary max */
|
|
|
|
if (channels < data->channels_per_frame) goto fail;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-03-13 20:04:09 +01:00
|
|
|
//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 */
|
2017-02-19 21:16:09 +01:00
|
|
|
data->ms_size = channels / data->channels_per_frame;
|
2017-02-19 20:20:13 +01:00
|
|
|
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;
|
|
|
|
|
2017-02-19 21:16:09 +01:00
|
|
|
data->interleave_buffer_size = sizeof(sample) * data->samples_per_frame * channels;
|
2017-02-19 20:20:13 +01:00
|
|
|
data->interleave_buffer = calloc(sizeof(uint8_t), data->interleave_buffer_size);
|
|
|
|
if (!data->interleave_buffer) goto fail;
|
|
|
|
}
|
|
|
|
|
2010-09-10 23:49:56 +02:00
|
|
|
|
|
|
|
return data;
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
fail:
|
2017-02-17 18:35:58 +01:00
|
|
|
free_mpeg(data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-03-13 20:04:09 +01:00
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/**
|
2017-02-19 20:20:13 +01:00
|
|
|
* Inits MPEG for AHX, which ignores frame headers.
|
2017-02-18 00:17:38 +01:00
|
|
|
*/
|
2017-02-17 18:35:58 +01:00
|
|
|
mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count) {
|
|
|
|
mpeg_codec_data *data = NULL;
|
2017-02-18 00:17:38 +01:00
|
|
|
|
|
|
|
/* init codec */
|
2017-02-17 18:35:58 +01:00
|
|
|
data = calloc(1,sizeof(mpeg_codec_data));
|
|
|
|
if (!data) goto fail;
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE;
|
|
|
|
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
|
|
|
if (!data->buffer) goto fail;
|
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
data->m = init_mpg123_handle();
|
|
|
|
if (!data->m) goto fail;
|
|
|
|
|
|
|
|
|
|
|
|
return data;
|
2017-02-17 18:35:58 +01:00
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
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);
|
2017-02-17 18:35:58 +01:00
|
|
|
if (rc != MPG123_OK) goto fail;
|
2017-02-18 18:27:21 +01:00
|
|
|
} else if (rc != MPG123_OK) {
|
2017-02-17 18:35:58 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); //todo fix gapless
|
|
|
|
mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x10000); /* should be enough */
|
2017-02-18 18:27:21 +01:00
|
|
|
|
|
|
|
if (mpg123_open_feed(m) != MPG123_OK) {
|
2017-02-17 18:35:58 +01:00
|
|
|
goto fail;
|
2010-09-10 23:49:56 +02:00
|
|
|
}
|
2017-02-17 18:35:58 +01:00
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
return m;
|
2017-02-17 18:35:58 +01:00
|
|
|
|
|
|
|
fail:
|
2017-02-18 18:27:21 +01:00
|
|
|
mpg123_delete(m);
|
2010-09-10 23:49:56 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-02-17 18:35:58 +01:00
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
/************/
|
|
|
|
/* 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;
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
/* 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);
|
|
|
|
}
|
2017-02-18 18:27:21 +01:00
|
|
|
}
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/**
|
|
|
|
* Decode anything mpg123 can.
|
|
|
|
* Feeds raw data and extracts decoded samples as needed.
|
|
|
|
*/
|
2017-02-19 20:20:13 +01:00
|
|
|
static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
|
2008-07-06 17:33:38 +02:00
|
|
|
int samples_done = 0;
|
2017-02-19 20:20:13 +01:00
|
|
|
mpg123_handle *m = data->m;
|
2008-07-06 17:33:38 +02:00
|
|
|
|
|
|
|
while (samples_done < samples_to_do) {
|
|
|
|
size_t bytes_done;
|
|
|
|
int rc;
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/* read more raw data */
|
2008-07-06 17:33:38 +02:00
|
|
|
if (!data->buffer_full) {
|
2017-02-18 00:17:38 +01:00
|
|
|
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->buffer_size,stream->streamfile);
|
2008-07-06 17:33:38 +02:00
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/* end of stream, fill rest with 0s */
|
2011-01-19 14:54:59 +01:00
|
|
|
if (!data->bytes_in_buffer) {
|
|
|
|
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-07-06 17:33:38 +02:00
|
|
|
data->buffer_full = 1;
|
|
|
|
data->buffer_used = 0;
|
|
|
|
|
|
|
|
stream->offset += data->bytes_in_buffer;
|
|
|
|
}
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
/* feed new raw data to the decoder if needed, copy decoded results to output */
|
2008-07-06 17:33:38 +02:00
|
|
|
if (!data->buffer_used) {
|
2017-02-18 18:27:21 +01:00
|
|
|
rc = mpg123_decode(m,
|
2008-07-06 17:33:38 +02:00
|
|
|
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 {
|
2017-02-18 18:27:21 +01:00
|
|
|
rc = mpg123_decode(m,
|
2008-07-06 17:33:38 +02:00
|
|
|
NULL,0,
|
|
|
|
(unsigned char *)(outbuf+samples_done*channels),
|
|
|
|
(samples_to_do-samples_done)*sizeof(sample)*channels,
|
|
|
|
&bytes_done);
|
|
|
|
}
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/* not enough raw data, request more */
|
|
|
|
if (rc == MPG123_NEED_MORE) {
|
|
|
|
data->buffer_full = 0;
|
|
|
|
}
|
2008-07-06 17:33:38 +02:00
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/* update copied samples */
|
2008-07-06 17:33:38 +02:00
|
|
|
samples_done += bytes_done/sizeof(sample)/channels;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/**
|
2017-02-19 20:20:13 +01:00
|
|
|
* Decode interleaved (multichannel) MPEG. Works with mono/stereo too.
|
2017-03-13 20:04:09 +01:00
|
|
|
* Channels (1 or 2), samples and frame size per stream should be constant.
|
2017-02-19 20:20:13 +01:00
|
|
|
*
|
|
|
|
* 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).
|
2017-02-18 00:17:38 +01:00
|
|
|
*
|
2017-06-25 02:09:12 +02:00
|
|
|
* Interleave modes:
|
|
|
|
* - FIXED [XVAG]: fixed block_size per stream (unknown number of samples)
|
2017-02-19 20:20:13 +01:00
|
|
|
* (ex. b1 = N samples of ch1, b2 = N samples of ch2, b3 = M samples of ch1, etc)
|
2017-06-25 02:09:12 +02:00
|
|
|
* - FSB: single frames per stream with padding (block_size is frame_size+padding) [FSB]
|
2017-02-19 20:20:13 +01:00
|
|
|
* (ex. f1+f3+f5 = 1152*2 samples of ch1+2, f2+f4 = 1152*2 samples of ch3+4, etc)
|
2017-06-25 02:09:12 +02:00
|
|
|
* - P3D: unknown layout at the moment (possibly N
|
2017-02-19 20:20:13 +01:00
|
|
|
*/
|
|
|
|
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++) {
|
2017-07-01 23:55:10 +02:00
|
|
|
if (data->interleave_type == MPEG_P3D /* P3D have a strange way to interleave so just use first offset */
|
|
|
|
|| data->interleave_type == MPEG_EA) /* EA MPEG is simply frame by frame normal MPEG */
|
2017-06-25 02:09:12 +02:00
|
|
|
decode_mpeg_interleave_samples(&vgmstream->ch[0], data, data->ms[i], channels, i, vgmstream->interleave_block_size);
|
|
|
|
else
|
|
|
|
decode_mpeg_interleave_samples(&vgmstream->ch[i], data, data->ms[i], channels, i, vgmstream->interleave_block_size);
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
2017-03-13 20:04:09 +01:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2017-06-25 02:09:12 +02:00
|
|
|
/* pretend the samples were used up and readjust discard */
|
2017-03-13 20:04:09 +01:00
|
|
|
data->bytes_used_in_interleave_buffer = bytes_to_discard;
|
|
|
|
data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;;
|
|
|
|
}
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2017-03-13 20:04:09 +01:00
|
|
|
size_t stream_size = get_streamfile_size(stream->streamfile);
|
2017-02-19 20:20:13 +01:00
|
|
|
|
|
|
|
/* decode samples from 1 full frame */
|
|
|
|
do {
|
|
|
|
int rc;
|
2017-04-13 15:04:46 +02:00
|
|
|
//int first_frame = stream->offset == stream->channel_start_offset;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
|
|
|
/* padded frame stuff */
|
2017-03-13 20:04:09 +01:00
|
|
|
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);
|
2017-06-25 02:09:12 +02:00
|
|
|
//VGM_LOG("off=%lx, st=%i, fs=%x, pd=%x\n", stream->offset, num_stream, data->current_frame_size, data->current_padding);
|
2017-04-13 15:04:46 +02:00
|
|
|
|
|
|
|
|
2017-03-13 20:04:09 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
/* read more raw data (only 1 frame, to check interleave block end) */
|
|
|
|
if (!data->buffer_full) {
|
2017-06-25 02:09:12 +02:00
|
|
|
//VGM_LOG("MPEG: reading more raw data\n");
|
2017-02-19 20:20:13 +01:00
|
|
|
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;
|
|
|
|
|
2017-06-25 02:09:12 +02:00
|
|
|
stream->offset += data->current_frame_size + data->current_padding; /* skip frame + FSB garbage */
|
2017-02-19 20:20:13 +01:00
|
|
|
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 */
|
|
|
|
}
|
2017-04-13 15:04:46 +02:00
|
|
|
|
2017-06-25 02:09:12 +02:00
|
|
|
#if 0
|
|
|
|
//TODO: skip very first frame but add fake silence? only if first frame is fully decodable?
|
|
|
|
if (data->interleave_type == MPEG_FSB && first_frame) {
|
2017-04-13 15:04:46 +02:00
|
|
|
data->buffer_full = 0;
|
|
|
|
|
|
|
|
VGM_LOG("skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset);
|
|
|
|
memset(data->frame_buffer, 0, data->frame_buffer_size);
|
|
|
|
bytes_done = data->frame_buffer_size;
|
|
|
|
break;
|
|
|
|
}
|
2017-06-25 02:09:12 +02:00
|
|
|
#endif
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
|
2017-04-13 15:04:46 +02:00
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
|
|
|
|
if (!data->buffer_used) {
|
2017-06-25 02:09:12 +02:00
|
|
|
//VGM_LOG("MPEG: get samples from new raw data\n");
|
2017-02-19 20:20:13 +01:00
|
|
|
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 {
|
2017-06-25 02:09:12 +02:00
|
|
|
//VGM_LOG("MPEG: get samples from old raw data\n");
|
2017-02-19 20:20:13 +01:00
|
|
|
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) {
|
2017-03-13 20:04:09 +01:00
|
|
|
VGM_LOG("MPEG: borked frame @ 0x%08lx (%i bytes done, expected %i, rc=%i)\n", stream->offset, bytes_done, data->frame_buffer_size, rc);
|
2017-02-19 20:20:13 +01:00
|
|
|
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) {
|
2017-06-25 02:09:12 +02:00
|
|
|
//VGM_LOG("MPEG: need more raw data\n");
|
2017-02-19 20:20:13 +01:00
|
|
|
data->buffer_full = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-06-25 02:09:12 +02:00
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-03-13 20:04:09 +01:00
|
|
|
* Get frame size info for the current frame, needed by FSBs of varying padding.
|
2017-02-19 20:20:13 +01:00
|
|
|
* 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.
|
|
|
|
*/
|
2017-03-13 20:04:09 +01:00
|
|
|
static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, off_t offset) {
|
2017-06-25 02:09:12 +02:00
|
|
|
|
|
|
|
if (data->interleave_type == MPEG_FIXED) { /* frames of fixed size */
|
|
|
|
data->current_frame_size = data->interleave_value;
|
|
|
|
}
|
|
|
|
else if (data->interleave_type == MPEG_FSB) { /* padding between frames */
|
2017-03-13 20:04:09 +01:00
|
|
|
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;
|
|
|
|
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) */
|
2017-06-25 02:09:12 +02:00
|
|
|
if (data->interleave_value && info.layer == 3) {
|
|
|
|
data->current_padding = (data->current_frame_size % data->interleave_value)
|
|
|
|
? data->interleave_value - (data->current_frame_size % data->interleave_value)
|
|
|
|
: 0;
|
|
|
|
}
|
2017-07-01 23:55:10 +02:00
|
|
|
|
|
|
|
/* 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);
|
2017-06-25 02:09:12 +02:00
|
|
|
}
|
|
|
|
else if (data->interleave_type == MPEG_P3D) { /* varying frames size, even though the frame header says 0x120 */
|
|
|
|
uint32_t header = read_32bitBE(offset,streamfile);
|
|
|
|
|
|
|
|
if (read_32bitBE(offset+0x120,streamfile) == header) {
|
|
|
|
data->current_frame_size = 0x120;
|
|
|
|
} else if (read_32bitBE(offset+0xA0,streamfile) == header) {
|
|
|
|
data->current_frame_size = 0xA0;
|
|
|
|
} else if (read_32bitBE(offset+0x1C0,streamfile) == header) {
|
|
|
|
data->current_frame_size = 0x1C0;
|
|
|
|
} else if (read_32bitBE(offset+0x180,streamfile) == header) {
|
|
|
|
data->current_frame_size = 0x180;
|
|
|
|
//} else if (read_32bitBE(offset+0x120,streamfile) == -1) { /* at EOF */
|
|
|
|
// data->current_frame_size = 0x120;
|
|
|
|
} else {
|
|
|
|
VGM_LOG("MPEG: unknown frame size @ %lx, %x\n", offset, read_32bitBE(offset+0x120,streamfile));
|
|
|
|
goto fail;
|
2017-03-13 20:04:09 +01:00
|
|
|
}
|
2017-07-01 23:55:10 +02:00
|
|
|
}
|
|
|
|
else if (data->interleave_type == MPEG_EA) { /* straight frame by frame */
|
|
|
|
mpeg_frame_info info;
|
2017-04-13 15:04:46 +02:00
|
|
|
|
2017-07-01 23:55:10 +02:00
|
|
|
/* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */
|
|
|
|
if ( !mpeg_get_frame_info(streamfile, offset, &info) )
|
|
|
|
goto fail;
|
|
|
|
data->current_frame_size = info.frame_size;
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
2017-03-13 20:04:09 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return 0;
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decode AHX mono frames.
|
2017-02-18 00:17:38 +01:00
|
|
|
* 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) {
|
2017-02-17 18:35:58 +01:00
|
|
|
int samples_done = 0;
|
2017-02-18 00:17:38 +01:00
|
|
|
const int frame_size = AHX_EXPECTED_FRAME_SIZE;
|
2017-02-18 18:27:21 +01:00
|
|
|
mpg123_handle *m = data->m;
|
2017-02-17 18:35:58 +01:00
|
|
|
|
|
|
|
while (samples_done < samples_to_do) {
|
|
|
|
size_t bytes_done;
|
|
|
|
int rc;
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/* read more raw data */
|
2017-02-17 18:35:58 +01:00
|
|
|
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;
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/* 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);
|
2017-02-17 18:35:58 +01:00
|
|
|
frame_offset += 4;
|
|
|
|
|
|
|
|
do {
|
|
|
|
uint8_t byte;
|
2017-02-18 00:17:38 +01:00
|
|
|
byte = read_8bit(stream->offset+frame_offset,stream->streamfile);
|
2017-02-17 18:35:58 +01:00
|
|
|
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;
|
|
|
|
}
|
2017-02-18 00:17:38 +01:00
|
|
|
} while (frame_offset < frame_size);
|
2017-02-17 18:35:58 +01:00
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
if (bytes_into_header==4)
|
|
|
|
frame_offset-=4;
|
|
|
|
memset(data->buffer+frame_offset,0,frame_size-frame_offset);
|
2017-02-17 18:35:58 +01:00
|
|
|
|
|
|
|
data->buffer_full = 1;
|
|
|
|
data->buffer_used = 0;
|
|
|
|
|
|
|
|
stream->offset += frame_offset;
|
|
|
|
}
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/* feed new raw data to the decoder if needed, copy decodec results to output */
|
2017-02-17 18:35:58 +01:00
|
|
|
if (!data->buffer_used) {
|
2017-02-18 18:27:21 +01:00
|
|
|
rc = mpg123_decode(m,
|
2017-02-18 00:17:38 +01:00
|
|
|
data->buffer,frame_size,
|
2017-02-17 18:35:58 +01:00
|
|
|
(unsigned char *)(outbuf+samples_done),
|
|
|
|
(samples_to_do-samples_done)*sizeof(sample),
|
|
|
|
&bytes_done);
|
|
|
|
data->buffer_used = 1;
|
|
|
|
} else {
|
2017-02-18 18:27:21 +01:00
|
|
|
rc = mpg123_decode(m,
|
2017-02-17 18:35:58 +01:00
|
|
|
NULL,0,
|
|
|
|
(unsigned char *)(outbuf+samples_done),
|
|
|
|
(samples_to_do-samples_done)*sizeof(sample),
|
|
|
|
&bytes_done);
|
|
|
|
}
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/* not enough raw data, request more */
|
|
|
|
if (rc == MPG123_NEED_MORE) {
|
|
|
|
data->buffer_full = 0;
|
|
|
|
}
|
2017-02-17 18:35:58 +01:00
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
/* update copied samples */
|
|
|
|
samples_done += bytes_done/sizeof(sample);/* mono */
|
2017-02-17 18:35:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
/*********/
|
|
|
|
/* UTILS */
|
|
|
|
/*********/
|
2017-02-17 17:20:40 +01:00
|
|
|
|
|
|
|
void free_mpeg(mpeg_codec_data *data) {
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mpg123_delete(data->m);
|
2017-02-19 20:20:13 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
free(data->buffer);
|
2017-02-17 17:20:40 +01:00
|
|
|
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);
|
2017-02-19 20:20:13 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
data->bytes_in_interleave_buffer = 0;
|
|
|
|
data->bytes_used_in_interleave_buffer = 0;
|
|
|
|
}
|
2017-02-17 17:20:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
2017-03-13 20:04:09 +01:00
|
|
|
/* won't work for fake AHX MPEG */
|
2017-02-17 17:20:40 +01:00
|
|
|
off_t input_offset;
|
|
|
|
mpeg_codec_data *data = vgmstream->codec_data;
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
/* seek multistream */
|
|
|
|
if (!data->interleaved) {
|
|
|
|
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
|
2017-04-14 01:22:53 +02:00
|
|
|
if (vgmstream->loop_ch)
|
|
|
|
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
|
2017-02-19 20:20:13 +01:00
|
|
|
} 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);
|
2017-04-14 01:22:53 +02:00
|
|
|
if (vgmstream->loop_ch)
|
|
|
|
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset;
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
/* manually add skip samples, since we don't really know the correct offset */
|
2017-03-13 20:04:09 +01:00
|
|
|
data->samples_to_discard = num_sample;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
|
|
|
data->bytes_in_interleave_buffer = 0;
|
|
|
|
data->bytes_used_in_interleave_buffer = 0;
|
|
|
|
}
|
|
|
|
|
2017-02-17 17:20:40 +01:00
|
|
|
data->buffer_full = 0;
|
|
|
|
data->buffer_used = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-18 00:17:38 +01:00
|
|
|
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) {
|
|
|
|
struct mpg123_frameinfo mi;
|
2017-02-18 18:27:21 +01:00
|
|
|
mpg123_handle *m = data->m;
|
2017-02-18 00:17:38 +01:00
|
|
|
|
2017-02-18 18:27:21 +01:00
|
|
|
if (MPG123_OK != mpg123_info(m, &mi))
|
2017-02-18 00:17:38 +01:00
|
|
|
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);
|
2010-09-11 01:56:39 +02:00
|
|
|
}
|
|
|
|
|
2016-12-28 01:38:56 +01:00
|
|
|
/**
|
|
|
|
* disables/enables stderr output, useful for MPEG known to contain recoverable errors
|
|
|
|
*/
|
|
|
|
void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
|
2017-02-19 20:20:13 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2016-12-28 01:38:56 +01:00
|
|
|
}
|
2017-03-13 20:04:09 +01:00
|
|
|
|
|
|
|
/*****************/
|
|
|
|
/* 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 */
|
|
|
|
};
|
2017-07-29 13:05:23 +02:00
|
|
|
static const coding_t coding_types[3] = { coding_MPEG_layer1, coding_MPEG_layer2, coding_MPEG_layer3 }; /* [layer] */
|
2017-03-13 20:04:09 +01:00
|
|
|
|
|
|
|
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 */
|
2017-07-29 13:05:23 +02:00
|
|
|
if (info->layer <= 0 || info->layer > 3) goto fail;
|
2017-03-13 20:04:09 +01:00
|
|
|
|
|
|
|
//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;
|
|
|
|
}
|
|
|
|
|
2017-07-29 13:05:23 +02:00
|
|
|
info->coding_type = coding_types[info->layer-1];
|
2017-03-13 20:04:09 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-07-05 13:49:29 +02:00
|
|
|
#endif
|