Define MPEG interleave type to improve and simplify some internals

This commit is contained in:
bnnm 2017-06-25 02:09:12 +02:00
parent b56ab1fa5b
commit 86cb660f76
6 changed files with 60 additions and 51 deletions

View File

@ -161,7 +161,7 @@ void free_ogl_vorbis(vorbis_codec_data *data);
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
/* mpeg_decoder */ /* mpeg_decoder */
mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels); mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels);
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 *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);
mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count); mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count);
void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels); void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels);

View File

@ -134,7 +134,7 @@ fail:
/** /**
* Init interleaved MPEG (also accepts normal MPEGs, but it's less error tolerant than normal MPEG init). * 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 *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) {
mpeg_codec_data *data = NULL; mpeg_codec_data *data = NULL;
/* init codec */ /* init codec */
@ -165,19 +165,13 @@ mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t
data->sample_rate_per_frame = info.sample_rate; data->sample_rate_per_frame = info.sample_rate;
data->samples_per_frame = info.frame_samples; data->samples_per_frame = info.frame_samples;
data->fixed_frame_size = fixed_frame_size; data->interleave_type = interleave_type;
data->current_frame_size = fixed_frame_size; data->interleave_value = interleave_value;
data->fsb_padding = fsb_padding;
/* get frame size from the first frame as it can be used externally to calc interleave */ /* get frame size from the first frame as it can be used externally to calc interleave */
if ( !update_frame_sizes(data, streamfile, start_offset) ) if ( !update_frame_sizes(data, streamfile, start_offset) )
goto fail; goto fail;
//VGM_LOG("ch=%i, sr=%i, spf=%i, v=%i, l=%i\n", data->channels_per_frame, data->sample_rate_per_frame, data->samples_per_frame, info.version, info.layer);
//mpg123_handle *main_m = data->m;
//struct mpg123_frameinfo mi;
//mpg123_info(main_m,&mi);
//VGM_LOG("mi.framesize=%x\n", mi.framesize);
/* unlikely, can fixed with bigger buffer or a feed loop */ /* unlikely, can fixed with bigger buffer or a feed loop */
if (info.frame_size > data->buffer_size) if (info.frame_size > data->buffer_size)
goto fail; goto fail;
@ -348,11 +342,12 @@ static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data
* samples per stream and muxes them into a single internal buffer before copying to outbuf * 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). * (to make sure channel samples are orderly copied between decode_mpeg calls).
* *
* Interleave variations: * Interleave modes:
* - blocks of frames: fixed block_size per stream (unknown number of samples) [XVAG] * - FIXED [XVAG]: fixed block_size per stream (unknown number of samples)
* (ex. b1 = N samples of ch1, b2 = N samples of ch2, b3 = M samples of ch1, etc) * (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] * - FSB: 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) * (ex. f1+f3+f5 = 1152*2 samples of ch1+2, f2+f4 = 1152*2 samples of ch3+4, etc)
* - P3D: unknown layout at the moment (possibly N
*/ */
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(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
int samples_done = 0, bytes_max, bytes_to_copy; int samples_done = 0, bytes_max, bytes_to_copy;
@ -378,7 +373,10 @@ static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data
data->bytes_used_in_interleave_buffer = 0; data->bytes_used_in_interleave_buffer = 0;
for (i=0; i < data->ms_size; i++) { 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); if (data->interleave_type == MPEG_P3D) /* P3D have a strange way to interleave so just use first offset */
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);
} }
/* discard (for looping): 'remove' decoded samples from the buffer */ /* discard (for looping): 'remove' decoded samples from the buffer */
@ -389,10 +387,8 @@ static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data
if (bytes_to_discard > data->bytes_in_interleave_buffer) if (bytes_to_discard > data->bytes_in_interleave_buffer)
bytes_to_discard = data->bytes_in_interleave_buffer; bytes_to_discard = data->bytes_in_interleave_buffer;
/* pretend the samples were used up */ /* pretend the samples were used up and readjust discard */
data->bytes_used_in_interleave_buffer = bytes_to_discard; data->bytes_used_in_interleave_buffer = bytes_to_discard;
/* and readjust discard */
data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;; data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;;
} }
} }
@ -417,8 +413,7 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
rc = update_frame_sizes(data, stream->streamfile, stream->offset); rc = update_frame_sizes(data, stream->streamfile, stream->offset);
/* ignore any errors and continue; mpg123 will probably sync */ /* 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); 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);
//VGM_LOG("off=%lx, st=%i, fs=%x, pd=%x\n", stream->offset, num_stream, data->current_frame_size, data->current_padding);
/* extra EOF check for edge cases when the caller tries to read more samples than possible */ /* extra EOF check for edge cases when the caller tries to read more samples than possible */
@ -428,11 +423,10 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
break; break;
} }
/* read more raw data (only 1 frame, to check interleave block end) */ /* read more raw data (only 1 frame, to check interleave block end) */
if (!data->buffer_full) { if (!data->buffer_full) {
//VGM_LOG("MPEG: reading more raw data\n");
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->current_frame_size,stream->streamfile); data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->current_frame_size,stream->streamfile);
//VGM_LOG("read raw: cfs=%x, cp=%x, tot=%x, off=%x\n", data->current_frame_size, data->current_padding, data->current_frame_size + data->current_padding, stream->offset);
/* end of stream, fill frame buffer with 0s but continue normally with other streams */ /* end of stream, fill frame buffer with 0s but continue normally with other streams */
if (!data->bytes_in_buffer) { if (!data->bytes_in_buffer) {
@ -444,16 +438,14 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
data->buffer_full = 1; data->buffer_full = 1;
data->buffer_used = 0; data->buffer_used = 0;
stream->offset += data->current_frame_size + data->current_padding; /* skip FSB frame+garbage */ stream->offset += data->current_frame_size + data->current_padding; /* skip frame + FSB garbage */
if (block_size && ((stream->offset - stream->channel_start_offset) % block_size==0)) { 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 */ stream->offset += block_size * (data->ms_size-1); /* skip a block per stream if block done */
} }
/* #if 0
/// TODO: skip very first frame but add fake silence? //TODO: skip very first frame but add fake silence? only if first frame is fully decodable?
//todo skip only if first frame is fully decodable? (ie.- blank below) if (data->interleave_type == MPEG_FSB && first_frame) {
//todo for multich files idem
if (first_frame) {
data->buffer_full = 0; data->buffer_full = 0;
VGM_LOG("skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset); VGM_LOG("skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset);
@ -461,24 +453,20 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
bytes_done = data->frame_buffer_size; bytes_done = data->frame_buffer_size;
break; break;
} }
*/ #endif
} }
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */ /* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
if (!data->buffer_used) { if (!data->buffer_used) {
//VGM_LOG("feed unused: cfs=%x, cp=%x, tot=%x, off=%x\n", data->current_frame_size, data->current_padding, data->current_frame_size + data->current_padding, stream->offset); //VGM_LOG("MPEG: get samples from new raw data\n");
//getchar();
rc = mpg123_decode(m, rc = mpg123_decode(m,
data->buffer, data->bytes_in_buffer, data->buffer, data->bytes_in_buffer,
(unsigned char *)data->frame_buffer, data->frame_buffer_size, (unsigned char *)data->frame_buffer, data->frame_buffer_size,
&bytes_done); &bytes_done);
data->buffer_used = 1; data->buffer_used = 1;
} else { } else {
//VGM_LOG("feed used: cfs=%x, cp=%x, tot=%x, off=%x\n", data->current_frame_size, data->current_padding, data->current_frame_size + data->current_padding, stream->offset); //VGM_LOG("MPEG: get samples from old raw data\n");
//getchar();
rc = mpg123_decode(m, rc = mpg123_decode(m,
NULL,0, NULL,0,
(unsigned char *)data->frame_buffer, data->frame_buffer_size, (unsigned char *)data->frame_buffer, data->frame_buffer_size,
@ -494,10 +482,12 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
/* not enough raw data, request more */ /* not enough raw data, request more */
if (rc == MPG123_NEED_MORE) { if (rc == MPG123_NEED_MORE) {
//VGM_LOG("MPEG: need more raw data\n");
data->buffer_full = 0; data->buffer_full = 0;
continue; continue;
} }
break; break;
} while (1); } while (1);
@ -529,7 +519,11 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_
* Expected to be called at the beginning of a new frame. * 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) { static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, off_t offset) {
if (!data->fixed_frame_size) {
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 */
mpeg_frame_info info; mpeg_frame_info info;
/* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */ /* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */
@ -544,13 +538,30 @@ static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, of
data->current_frame_size = info.frame_size; 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) */ /* 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) { if (data->interleave_value && info.layer == 3) {
data->current_padding = (data->current_frame_size % data->fsb_padding) ? data->current_padding = (data->current_frame_size % data->interleave_value)
data->fsb_padding - (data->current_frame_size % data->fsb_padding) : 0; ? data->interleave_value - (data->current_frame_size % data->interleave_value)
: 0;
}
}
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;
} }
//VGM_LOG("off=%lx, ch=%i, sr=%i, spf=%i, fs=%x, v=%i, l=%i\n", offset, info.channels, info.sample_rate, info.frame_samples, info.frame_size, info.version, info.layer);
//getchar();
} }
return 1; return 1;
@ -830,9 +841,6 @@ static int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_
case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); 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; case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break;
} }
//int fs2 = (info->frame_samples / 8 * info->bit_rate * 1000l) / info->sample_rate + padding;
//VGM_LOG("fs=%x, fs2=%x, fsp=%i, br=%i\n", info->frame_size, fs2, info->frame_samples, info->bit_rate);
//getchar();
info->coding_type = coding_types[info->version-1][info->layer-1]; info->coding_type = coding_types[info->version-1][info->layer-1];

View File

@ -292,7 +292,7 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
else /* needed by multichannel with no flags */ else /* needed by multichannel with no flags */
fsb_padding = fsbh.numchannels > 2 ? 16 : 0; fsb_padding = fsbh.numchannels > 2 ? 16 : 0;
mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, 0, fsb_padding); mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, MPEG_FSB, fsb_padding);
if (!mpeg_data) goto fail; if (!mpeg_data) goto fail;
vgmstream->codec_data = mpeg_data; vgmstream->codec_data = mpeg_data;
vgmstream->coding_type = mpeg_coding_type; vgmstream->coding_type = mpeg_coding_type;

View File

@ -264,7 +264,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
fsb_padding = vgmstream->channels > 2 ? 16 : 4; /* observed default */ fsb_padding = vgmstream->channels > 2 ? 16 : 4; /* observed default */
mpeg_data = init_mpeg_codec_data_interleaved(streamFile, StartOffset, &mpeg_coding_type, vgmstream->channels, 0, fsb_padding); mpeg_data = init_mpeg_codec_data_interleaved(streamFile, StartOffset, &mpeg_coding_type, vgmstream->channels, MPEG_FSB, fsb_padding);
if (!mpeg_data) goto fail; if (!mpeg_data) goto fail;
vgmstream->codec_data = mpeg_data; vgmstream->codec_data = mpeg_data;
vgmstream->coding_type = mpeg_coding_type; vgmstream->coding_type = mpeg_coding_type;

View File

@ -91,7 +91,7 @@ VGMSTREAM * init_vgmstream_ps3_xvag(STREAMFILE *streamFile) {
if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) goto fail; /*"mpin"*/ if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) goto fail; /*"mpin"*/
fixed_frame_size = read_32bit(chunk_offset+0x1c,streamFile); fixed_frame_size = read_32bit(chunk_offset+0x1c,streamFile);
mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, fixed_frame_size, 0); mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, MPEG_FIXED, fixed_frame_size);
if (!mpeg_data) goto fail; if (!mpeg_data) goto fail;
vgmstream->codec_data = mpeg_data; vgmstream->codec_data = mpeg_data;
vgmstream->layout_type = layout_mpeg; vgmstream->layout_type = layout_mpeg;

View File

@ -830,6 +830,7 @@ typedef struct {
#endif #endif
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
typedef enum { /*MPEG_NONE,*/ MPEG_FIXED, MPEG_FSB, MPEG_P3D } mpeg_interleave_type;
typedef struct { typedef struct {
uint8_t *buffer; /* raw (coded) data buffer */ uint8_t *buffer; /* raw (coded) data buffer */
size_t buffer_size; size_t buffer_size;
@ -847,7 +848,10 @@ typedef struct {
size_t samples_to_discard; /* for interleaved looping */ size_t samples_to_discard; /* for interleaved looping */
/* interleaved MPEG internals */ /* interleaved MPEG internals */
int interleaved; /* flag */ int interleaved;
mpeg_interleave_type interleave_type; /* flag */
uint32_t interleave_value; /* varies with type */
mpg123_handle **ms; /* array of MPEG streams */ mpg123_handle **ms; /* array of MPEG streams */
size_t ms_size; size_t ms_size;
uint8_t *frame_buffer; /* temp buffer with samples from a single decoded frame */ uint8_t *frame_buffer; /* temp buffer with samples from a single decoded frame */
@ -857,11 +861,8 @@ typedef struct {
size_t bytes_in_interleave_buffer; size_t bytes_in_interleave_buffer;
size_t bytes_used_in_interleave_buffer; size_t bytes_used_in_interleave_buffer;
/* messy stuff for padded FSB frames */
size_t fixed_frame_size; /* when given a fixed size (XVAG) */
size_t current_frame_size; size_t current_frame_size;
int fsb_padding; /* for FSBs that have extra garbage between frames */ size_t current_padding; /* FSB padding between frames */
size_t current_padding; /* padding needed for current frame size */
} mpeg_codec_data; } mpeg_codec_data;
#endif #endif