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-07-29 23:14:04 +02:00
|
|
|
#include "mpeg_decoder.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO list for custom decoder
|
|
|
|
* - don't force channel param and get them from frame headers for some types (for MPEG_STANDARD)
|
|
|
|
* - use one stream per channel and advance streams as channels are done in case of streams like 2ch+1ch+1ch+2ch (not seen)
|
|
|
|
* (would also need to change sample_buffer copy)
|
|
|
|
* - improve validation of channels/samples_per_frame between streams
|
|
|
|
* - improve decoded samples to sample buffer copying (very picky with sizes)
|
|
|
|
* - use mpg123 stream_buffer, with flags per stream, and call copy to sample_buffer when all streams have some samples
|
|
|
|
* (so it could handle interleaved VBR frames).
|
|
|
|
* - AHX type 8 encryption
|
|
|
|
* - test encoder delays
|
|
|
|
* - improve error handling
|
|
|
|
*/
|
2017-02-18 00:17:38 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* mostly arbitrary max values */
|
|
|
|
#define MPEG_DATA_BUFFER_SIZE 0x1000
|
|
|
|
#define MPEG_MAX_CHANNELS 16
|
|
|
|
#define MPEG_MAX_STREAM_FRAMES 10
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-03-13 20:04:09 +01:00
|
|
|
static mpg123_handle * init_mpg123_handle();
|
2017-07-29 23:14:04 +02:00
|
|
|
static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
|
|
|
|
static void decode_mpeg_custom(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
|
|
|
|
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream);
|
2008-07-05 13:49:29 +02:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* Inits regular MPEG */
|
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;
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
data->buffer_size = MPEG_DATA_BUFFER_SIZE;
|
2017-02-18 00:17:38 +01:00
|
|
|
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-07-29 23:14:04 +02:00
|
|
|
goto fail; //handle MPG123_DONE?
|
2017-03-13 20:04:09 +01:00
|
|
|
}
|
|
|
|
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->channels_per_frame = channels_per_frame;
|
|
|
|
data->samples_per_frame = samples_per_frame;
|
2017-07-29 23:14:04 +02:00
|
|
|
if (channels_per_frame != channels)
|
|
|
|
goto fail;
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
|
|
|
|
/* Init custom MPEG, with given type and config. */
|
|
|
|
mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t type, mpeg_custom_config *config) {
|
2017-03-13 20:04:09 +01:00
|
|
|
mpeg_codec_data *data = NULL;
|
2017-07-29 23:14:04 +02:00
|
|
|
int stream_frames = 1;
|
|
|
|
int i, ok;
|
2017-03-13 20:04:09 +01:00
|
|
|
|
|
|
|
/* init codec */
|
|
|
|
data = calloc(1,sizeof(mpeg_codec_data));
|
|
|
|
if (!data) goto fail;
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
data->buffer_size = MPEG_DATA_BUFFER_SIZE;
|
2017-03-13 20:04:09 +01:00
|
|
|
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
|
|
|
|
if (!data->buffer) goto fail;
|
|
|
|
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* keep around to decode */
|
|
|
|
data->custom = 1;
|
|
|
|
data->type = type;
|
|
|
|
memcpy(&data->config, config, sizeof(mpeg_custom_config));
|
|
|
|
data->config.channels = channels;
|
2017-03-13 20:04:09 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* init per subtype */
|
|
|
|
switch(data->type) {
|
|
|
|
case MPEG_EAL31:
|
|
|
|
case MPEG_EAL32P:
|
|
|
|
case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break;
|
|
|
|
default: ok = mpeg_custom_setup_init_default(streamFile, start_offset, data, coding_type); break;
|
|
|
|
}
|
|
|
|
if (!ok)
|
|
|
|
goto fail;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
if (channels <= 0 || channels > MPEG_MAX_CHANNELS) goto fail;
|
|
|
|
if (channels < data->channels_per_frame) goto fail;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* init stream decoders (separate as MPEG frames may need N previous frames from their stream to decode) */
|
|
|
|
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;
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
if (stream_frames > MPEG_MAX_STREAM_FRAMES) goto fail;
|
2010-09-10 23:49:56 +02:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* init stream buffer, big enough for one stream and N frames at a time (will be copied to sample buffer) */
|
|
|
|
data->stream_buffer_size = sizeof(sample) * data->channels_per_frame * stream_frames * data->samples_per_frame;
|
|
|
|
data->stream_buffer = calloc(sizeof(uint8_t), data->stream_buffer_size);
|
|
|
|
if (!data->stream_buffer) goto fail;
|
2010-09-10 23:49:56 +02:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* init sample buffer, big enough for all streams/channels and N frames at a time */
|
|
|
|
data->sample_buffer_size = sizeof(sample) * channels * stream_frames * data->samples_per_frame;
|
|
|
|
data->sample_buffer = calloc(sizeof(uint8_t), data->sample_buffer_size);
|
|
|
|
if (!data->sample_buffer) goto fail;
|
2017-02-17 18:35:58 +01:00
|
|
|
|
2017-03-13 20:04:09 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* write output */
|
|
|
|
config->interleave = data->config.interleave; /* for FSB */
|
2017-02-18 18:27:21 +01:00
|
|
|
|
|
|
|
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-07-29 23:14:04 +02:00
|
|
|
mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); /* wonky support */
|
2017-02-19 20:20:13 +01:00
|
|
|
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-07-29 23:14:04 +02:00
|
|
|
if (!data->custom) {
|
|
|
|
decode_mpeg_standard(&vgmstream->ch[0], data, outbuf, samples_to_do, channels);
|
2017-02-19 20:20:13 +01:00
|
|
|
} else {
|
2017-07-29 23:14:04 +02:00
|
|
|
decode_mpeg_custom(vgmstream, data, outbuf, samples_to_do, channels);
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
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-07-29 23:14:04 +02:00
|
|
|
static void decode_mpeg_standard(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;
|
2017-07-29 23:14:04 +02:00
|
|
|
}
|
|
|
|
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-07-29 23:14:04 +02:00
|
|
|
* Decode custom MPEG, allowing support for: single frames, interleave, mutant frames, multiple streams
|
|
|
|
* (1 frame = 1/2ch so Nch = 2ch*N/2 or 1ch*N or 2ch+1ch+2ch+...), etc.
|
2017-02-19 20:20:13 +01:00
|
|
|
*
|
2017-07-29 23:14:04 +02:00
|
|
|
* Decodes samples per each stream and muxes them into a single internal buffer before copying to outbuf
|
2017-02-19 20:20:13 +01:00
|
|
|
* (to make sure channel samples are orderly copied between decode_mpeg calls).
|
2017-07-29 23:14:04 +02:00
|
|
|
* decode_mpeg_custom_stream does the main decoding, while this handles layout and copying samples to output.
|
2017-02-19 20:20:13 +01:00
|
|
|
*/
|
2017-07-29 23:14:04 +02:00
|
|
|
static void decode_mpeg_custom(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
|
2017-02-19 20:20:13 +01:00
|
|
|
int samples_done = 0, bytes_max, bytes_to_copy;
|
|
|
|
|
|
|
|
while (samples_done < samples_to_do) {
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
if (data->bytes_used_in_sample_buffer < data->bytes_in_sample_buffer) {
|
2017-02-19 20:20:13 +01:00
|
|
|
/* copy remaining samples */
|
2017-07-29 23:14:04 +02:00
|
|
|
bytes_to_copy = data->bytes_in_sample_buffer - data->bytes_used_in_sample_buffer;
|
2017-02-19 20:20:13 +01:00
|
|
|
bytes_max = (samples_to_do - samples_done) * sizeof(sample) * channels;
|
|
|
|
if (bytes_to_copy > bytes_max)
|
|
|
|
bytes_to_copy = bytes_max;
|
2017-07-29 23:14:04 +02:00
|
|
|
memcpy((uint8_t*)(outbuf+samples_done*channels), data->sample_buffer + data->bytes_used_in_sample_buffer, bytes_to_copy);
|
2017-02-19 20:20:13 +01:00
|
|
|
|
|
|
|
/* update samples copied */
|
2017-07-29 23:14:04 +02:00
|
|
|
data->bytes_used_in_sample_buffer += bytes_to_copy;
|
2017-02-19 20:20:13 +01:00
|
|
|
samples_done += bytes_to_copy / sizeof(sample) / channels;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* fill the internal sample buffer */
|
|
|
|
int i;
|
2017-07-29 23:14:04 +02:00
|
|
|
data->bytes_in_sample_buffer = 0;
|
|
|
|
data->bytes_used_in_sample_buffer = 0;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams)
|
|
|
|
* With multiple offsets it's expected offsets are set up pointing to the first frame of each stream. */
|
2017-02-19 20:20:13 +01:00
|
|
|
for (i=0; i < data->ms_size; i++) {
|
2017-07-29 23:14:04 +02:00
|
|
|
switch(data->type) {
|
|
|
|
//case MPEG_LYN:
|
|
|
|
case MPEG_FSB:
|
|
|
|
case MPEG_XVAG:
|
|
|
|
/* multiple offsets, decodes 1 frame per stream until reaching interleave/block_size and skips it */
|
|
|
|
decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i);
|
|
|
|
break;
|
|
|
|
|
|
|
|
//case MPEG_EA: //?
|
|
|
|
case MPEG_AWC:
|
|
|
|
/* consecutive streams: multiple offsets, decodes 1 frame per stream */
|
|
|
|
decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* N frames: single offset, decodes all N frames per stream (sample buffer must be big enough for N) */
|
|
|
|
decode_mpeg_custom_stream(&vgmstream->ch[0], data, data->ms[i], channels, i);
|
|
|
|
break;
|
|
|
|
}
|
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 */
|
2017-07-29 23:14:04 +02:00
|
|
|
if (bytes_to_discard > data->bytes_in_sample_buffer)
|
|
|
|
bytes_to_discard = data->bytes_in_sample_buffer;
|
2017-03-13 20:04:09 +01:00
|
|
|
|
2017-06-25 02:09:12 +02:00
|
|
|
/* pretend the samples were used up and readjust discard */
|
2017-07-29 23:14:04 +02:00
|
|
|
data->bytes_used_in_sample_buffer = bytes_to_discard;
|
2017-03-13 20:04:09 +01:00
|
|
|
data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;;
|
|
|
|
}
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* Decodes frames from a stream and muxes samples into a intermediate buffer and moves the stream offsets. */
|
|
|
|
static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream) {
|
|
|
|
size_t bytes_done = 0;
|
|
|
|
size_t stream_size = get_streamfile_size(stream->streamfile);
|
|
|
|
int rc, ok;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-04-13 15:04:46 +02:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* decode samples from one full-frame (as N data-frames = 1 full-frame) before exiting (to orderly copy to sample buffer) */
|
|
|
|
do {
|
|
|
|
VGM_LOG("MPEG: new step of stream %i @ 0x%08lx\n", num_stream, stream->offset);
|
|
|
|
getchar();
|
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 */
|
2017-07-29 23:14:04 +02:00
|
|
|
if (stream->offset >= stream_size) {
|
|
|
|
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
|
|
|
bytes_done = data->stream_buffer_size;
|
|
|
|
break; /* continue with other streams */
|
2017-03-13 20:04:09 +01:00
|
|
|
}
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* read more raw data */
|
2017-02-19 20:20:13 +01:00
|
|
|
if (!data->buffer_full) {
|
2017-06-25 02:09:12 +02:00
|
|
|
//VGM_LOG("MPEG: reading more raw data\n");
|
2017-07-29 23:14:04 +02:00
|
|
|
switch(data->type) {
|
|
|
|
case MPEG_EAL31:
|
|
|
|
case MPEG_EAL32P:
|
|
|
|
case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_parse_frame_ealayer3(stream, data); break;
|
|
|
|
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data); break;
|
|
|
|
default: ok = mpeg_custom_parse_frame_default(stream, data); break;
|
|
|
|
}
|
|
|
|
/* error/EOF, mpg123 can resync in some cases but custom MPEGs wouldn't need that */
|
|
|
|
if (!ok || !data->bytes_in_buffer) {
|
|
|
|
VGM_LOG("MPEG: cannot parse frame @ around %lx\n",stream->offset);
|
|
|
|
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
|
|
|
bytes_done = data->stream_buffer_size;
|
|
|
|
break; /* continue with other streams */
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
2017-07-29 23:14:04 +02:00
|
|
|
VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset off=%lx\n", data->bytes_in_buffer, stream->offset);
|
2017-02-19 20:20:13 +01:00
|
|
|
|
|
|
|
data->buffer_full = 1;
|
|
|
|
data->buffer_used = 0;
|
2017-07-29 23:14:04 +02:00
|
|
|
}
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
#if 0
|
|
|
|
//TODO: in case of EALayer3 with "PCM flag" must put them in buffer
|
|
|
|
if (ea_pcm_flag) {
|
|
|
|
/* write some samples to data->stream_buffer *before* decoding this frame */
|
|
|
|
bytes_done += read_streamfile(data->stream_buffer,offset,num_pcm_samples,stream->streamfile);
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
2017-07-29 23:14:04 +02:00
|
|
|
#endif
|
2017-06-25 02:09:12 +02:00
|
|
|
#if 0
|
2017-07-29 23:14:04 +02:00
|
|
|
//TODO: FSB sometimes has garbage in the first frames, not sure why/when, no apparent patern
|
|
|
|
if (data->custom_type == MPEG_FSB && stream->offset == stream->channel_start_offset) { /* first frame */
|
|
|
|
VGM_LOG("MPEG: skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset);
|
2017-04-13 15:04:46 +02:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
data->buffer_full = 0;
|
|
|
|
memset(data->stream_buffer, 0, data->stream_buffer_size);
|
|
|
|
bytes_done = data->stream_buffer_size;
|
2017-04-13 15:04:46 +02:00
|
|
|
break;
|
|
|
|
}
|
2017-06-25 02:09:12 +02:00
|
|
|
#endif
|
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-07-29 23:14:04 +02:00
|
|
|
//VGM_LOG("MPEG: feed new data and get samples \n");
|
2017-02-19 20:20:13 +01:00
|
|
|
rc = mpg123_decode(m,
|
|
|
|
data->buffer, data->bytes_in_buffer,
|
2017-07-29 23:14:04 +02:00
|
|
|
(unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size,
|
2017-02-19 20:20:13 +01:00
|
|
|
&bytes_done);
|
|
|
|
data->buffer_used = 1;
|
2017-07-29 23:14:04 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
//VGM_LOG("MPEG: get samples from old data\n");
|
2017-02-19 20:20:13 +01:00
|
|
|
rc = mpg123_decode(m,
|
|
|
|
NULL,0,
|
2017-07-29 23:14:04 +02:00
|
|
|
(unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size,
|
2017-02-19 20:20:13 +01:00
|
|
|
&bytes_done);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not enough raw data, request more */
|
|
|
|
if (rc == MPG123_NEED_MORE) {
|
2017-07-29 23:14:04 +02:00
|
|
|
//VGM_LOG("MPEG: need more raw data to get samples\n");
|
|
|
|
/* (apparently mpg123 can give bytes and request more at the same time and may mess up some calcs, when/how? */
|
|
|
|
VGM_ASSERT(bytes_done > 0, "MPEG: bytes done but decoder requests more data\n");
|
2017-02-19 20:20:13 +01:00
|
|
|
data->buffer_full = 0;
|
|
|
|
continue;
|
|
|
|
}
|
2017-07-29 23:14:04 +02:00
|
|
|
//VGM_LOG("MPEG: got samples, bytes_done=0x%x (fsbs=0x%x)\n", bytes_done, data->stream_buffer_size);
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-06-25 02:09:12 +02:00
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
break;
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* copy decoded full-frame to intermediate sample buffer, muxing channels
|
2017-02-19 20:20:13 +01:00
|
|
|
* (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);
|
2017-07-29 23:14:04 +02:00
|
|
|
memcpy(data->sample_buffer + out_offset, data->stream_buffer + in_offset, sz);
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
data->bytes_in_sample_buffer += bytes_done;
|
2017-07-01 23:55:10 +02:00
|
|
|
}
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
if (!data->custom) {
|
|
|
|
mpg123_delete(data->m);
|
|
|
|
}
|
|
|
|
else {
|
2017-02-19 20:20:13 +01:00
|
|
|
int i;
|
|
|
|
for (i=0; i < data->ms_size; i++) {
|
|
|
|
mpg123_delete(data->ms[i]);
|
|
|
|
}
|
|
|
|
free(data->ms);
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
free(data->stream_buffer);
|
|
|
|
free(data->sample_buffer);
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
/* reset multistream */ //todo check if stream offsets are properly reset
|
2017-07-29 23:14:04 +02:00
|
|
|
|
|
|
|
if (!data->custom) {
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
else {
|
2017-02-19 20:20:13 +01:00
|
|
|
int i;
|
|
|
|
for (i=0; i < data->ms_size; i++) {
|
|
|
|
mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset);
|
|
|
|
}
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
data->bytes_in_sample_buffer = 0;
|
|
|
|
data->bytes_used_in_sample_buffer = 0;
|
|
|
|
|
|
|
|
/* initial delay */
|
|
|
|
data->samples_to_discard = data->skip_samples;
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
2017-02-17 17:20:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
|
|
|
|
off_t input_offset;
|
|
|
|
mpeg_codec_data *data = vgmstream->codec_data;
|
|
|
|
|
2017-02-19 20:20:13 +01:00
|
|
|
/* seek multistream */
|
2017-07-29 23:14:04 +02:00
|
|
|
if (!data->custom) {
|
|
|
|
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
|
|
|
|
if (vgmstream->loop_ch)
|
|
|
|
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
|
|
|
|
}
|
|
|
|
else {
|
2017-02-19 20:20:13 +01:00
|
|
|
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
|
|
|
}
|
2017-07-29 23:14:04 +02:00
|
|
|
data->bytes_in_sample_buffer = 0;
|
|
|
|
data->bytes_used_in_sample_buffer = 0;
|
2017-02-19 20:20:13 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* manually discard samples, since we don't really know the correct offset */
|
|
|
|
data->samples_to_discard = num_sample;
|
|
|
|
data->samples_to_discard += data->skip_samples;
|
2017-02-19 20:20:13 +01:00
|
|
|
}
|
|
|
|
|
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) {
|
2017-07-29 23:14:04 +02:00
|
|
|
/* if not found just return 0 and expect to fail (if used for num_samples) */
|
|
|
|
if (!data->custom) {
|
|
|
|
struct mpg123_frameinfo mi;
|
|
|
|
mpg123_handle *m = data->m;
|
2017-02-18 00:17:38 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
if (m == NULL || MPG123_OK != mpg123_info(m, &mi))
|
|
|
|
return 0;
|
2017-02-18 00:17:38 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* We would need to read all VBR frames headers to count samples */
|
|
|
|
if (mi.vbr != MPG123_CBR) //maybe abr_rate could be used to get an approx
|
|
|
|
return 0;
|
2017-02-18 00:17:38 +01:00
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
return (int64_t)bytes * mi.rate * 8 / (mi.bitrate * 1000);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0; /* a bit too complex for what is worth */
|
|
|
|
}
|
2010-09-11 01:56:39 +02:00
|
|
|
}
|
|
|
|
|
2017-07-29 23:14:04 +02:00
|
|
|
/* disables/enables stderr output, useful for MPEG known to contain recoverable errors */
|
2016-12-28 01:38:56 +01:00
|
|
|
void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
|
2017-07-29 23:14:04 +02:00
|
|
|
if (!data->custom) {
|
|
|
|
mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable);
|
|
|
|
}
|
|
|
|
else {
|
2017-02-19 20:20:13 +01:00
|
|
|
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
|
|
|
|
2008-07-05 13:49:29 +02:00
|
|
|
#endif
|