diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 194547ee..2b5d23fb 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -260,6 +260,10 @@ RelativePath=".\meta\ea_eaac_streamfile.h" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 4809abd5..a301fc29 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -107,6 +107,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 9ce72cf5..1bf4f290 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -92,6 +92,9 @@ meta\Header Files + + meta\Header Files + meta\Header Files diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index 027809c2..82e20256 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -1339,19 +1339,18 @@ static layered_layout_data* build_layered_eaaudiocore(STREAMFILE *sf_data, eaac_ size_t data_size; /* We'll remove EA blocks and pass raw data to FFmpeg Opus decoder */ - temp_sf = setup_eaac_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,0,0, eaac->stream_offset); + temp_sf = setup_eaac_streamfile(sf_data, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset); if (!temp_sf) goto fail; - //TODO: setup opus frame deinterleave (1 frame per 2ch stream, only seen 4ch and 6ch tho) - skip = ea_opus_get_encoder_delay(0x00, temp_sf); data_size = get_streamfile_size(temp_sf); - data->layers[i]->codec_data = init_ffmpeg_ea_opus(temp_sf, 0x00,data_size, eaac->channels, skip, eaac->sample_rate); + data->layers[i]->codec_data = init_ffmpeg_ea_opus(temp_sf, 0x00,data_size, layer_channels, skip, eaac->sample_rate); if (!data->layers[i]->codec_data) goto fail; data->layers[i]->coding_type = coding_FFmpeg; data->layers[i]->layout_type = layout_none; + //TODO: 6ch channel layout seems L C R BL BR LFE, not sure about other EAAC break; } diff --git a/src/meta/ea_eaac_opus_streamfile.h b/src/meta/ea_eaac_opus_streamfile.h new file mode 100644 index 00000000..a71b810e --- /dev/null +++ b/src/meta/ea_eaac_opus_streamfile.h @@ -0,0 +1,242 @@ +#ifndef _EA_EAAC_OPUS_STREAMFILE_H_ +#define _EA_EAAC_OPUS_STREAMFILE_H_ +#include "../streamfile.h" + +typedef struct deblock_config_t deblock_config_t; +typedef struct deblock_io_data deblock_io_data; + + struct deblock_config_t { + /* config (all optional) */ + size_t logical_size; /* pre-calculated size for performance (otherwise has to read the whole thing) */ + off_t stream_start; /* data start */ + size_t stream_size; /* data max */ + + size_t chunk_size; /* some size like a constant interleave */ + size_t skip_size; /* same */ + + int codec; /* codec or type variations */ + int channels; + int big_endian; + uint32_t config; /* some non-standard config value */ + + /* read=blocks from out stream to read) and "steps" (blocks from other streams to skip) */ + int step_start; /* initial step_count at stream start (often 0) */ + int step_count; /* number of blocks to step over from other streams */ + int read_count; /* number of blocks to read from this stream, after steps */ + + size_t track_size; + int track_number; + int track_count; + size_t interleave_count; + size_t interleave_last_count; + + /* callback that setups deblock_io_data state, normally block_size and data_size */ + void (*block_callback)(STREAMFILE *sf, off_t offset, deblock_io_data *data); +} ; + + +struct deblock_io_data{ + /* initial config */ + deblock_config_t cfg; + + /* state */ + off_t logical_offset; /* fake deblocked offset */ + off_t physical_offset; /* actual file offset */ + off_t block_size; /* current block (added to physical offset) */ + off_t skip_size; /* data to skip from block start to reach data (like a header) */ + off_t data_size; /* usable data in a block (added to logical offset) */ +//todo head/foot? + int step_count; /* number of blocks to step over */ + int read_count; /* number of blocks to read */ + + size_t logical_size; + size_t physical_size; + off_t physical_end; +} ; + + +static void block_callback_default(STREAMFILE *sf, off_t offset, deblock_io_data *data) { + data->block_size = data->cfg.chunk_size; + data->skip_size = data->cfg.skip_size; + data->data_size = data->block_size - data->skip_size; +} + +static size_t deblock_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, deblock_io_data* data) { + size_t total_read = 0; + + + /* re-start when previous offset (can't map logical<>physical offsets) */ + if (data->logical_offset < 0 || offset < data->logical_offset) { + ;VGM_LOG("DEBLOCK: restart offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset); + data->physical_offset = data->cfg.stream_start; + data->logical_offset = 0x00; + data->block_size = 0; + data->data_size = 0; + data->skip_size = 0; + + data->step_count = data->cfg.step_start; + data->read_count = data->cfg.read_count; + } + + /* read blocks */ + while (length > 0) { + + /* ignore EOF */ + if (offset < 0 || + (data->physical_offset >= data->cfg.stream_start + data->physical_size) || + (data->logical_size > 0 && offset > data->logical_size)) { + break; + } + + /* process new block */ + if (data->data_size <= 0) { + data->cfg.block_callback(sf, offset, data); + + if (data->block_size <= 0) { + VGM_LOG("DEBLOCK: block size not set at %lx\n", data->physical_offset); + break; + } + } + +#if 1 + if (data->step_count > 0) { + data->step_count--; + data->physical_offset += data->block_size; + data->data_size = 0; + continue; + } +#else + /* handle blocks from multiple streams */ + { + if (data->step_count > 0) { + data->step_count--; + data->data_size = 0; /* step over this block */ + } + else if (data->read_count) {//must detect when blocks has been read + data->read_count--; /* read this block */ + + /* reset */ + if (data->step_count == 0 && data->read_count == 0) { + data->step_count = data->cfg.step_count; + data->read_count = data->cfg.read_count; + } + } + } +#endif + /* move to next block */ + if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->block_size; + data->logical_offset += data->data_size; + data->data_size = 0; + + data->step_count = data->cfg.step_count; + //VGM_LOG("ignore at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count); + continue; + } + + //VGM_LOG("accept at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count); + + /* read block data */ + { + size_t bytes_consumed, bytes_done, to_read; + + bytes_consumed = offset - data->logical_offset; + to_read = data->data_size - bytes_consumed; + if (to_read > length) + to_read = length; + bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, sf); + + total_read += bytes_done; + dest += bytes_done; + offset += bytes_done; + length -= bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } + } + } + + return total_read; +} + +static size_t deblock_io_size(STREAMFILE *streamfile, deblock_io_data* data) { + uint8_t buf[0x04]; + + if (data->logical_size) + return data->logical_size; + + if (data->cfg.logical_size) { + data->logical_size = data->cfg.logical_size; + return data->logical_size; + } + + /* force a fake read at max offset, to get max logical_offset (will be reset next read) */ + deblock_io_read(streamfile, buf, 0x7FFFFFFF, 1, data); + data->logical_size = data->logical_offset; + return data->logical_size; +} + +/* generic "de-blocker" helper for streams divided in blocks that have weird interleaves, their + * decoder can't easily use blocked layout, or some other weird feature. Must pass a + * deblock_config_t with setup and a callback that sets sizes of a single block. */ +static STREAMFILE* open_io_deblocker_streamfile_f(STREAMFILE *sf, deblock_config_t *cfg) { + STREAMFILE *new_sf = NULL; + deblock_io_data io_data = {0}; + + /* prepare data */ + io_data.cfg = *cfg; /* memcpy */ + + if (io_data.cfg.block_callback == NULL) + io_data.cfg.block_callback = block_callback_default; + + if (io_data.cfg.stream_start < 0) + goto fail; + if (io_data.cfg.step_start < 0 || io_data.cfg.step_count < 0) + goto fail; + + if (io_data.cfg.read_count == 0) + io_data.cfg.read_count = 1; + + io_data.physical_size = io_data.cfg.stream_size; + if (io_data.physical_size > get_streamfile_size(sf) + io_data.cfg.stream_start || io_data.physical_size == 0) + io_data.physical_size = get_streamfile_size(sf) - io_data.cfg.stream_start; + io_data.physical_end = io_data.cfg.stream_start + io_data.physical_size; +VGM_LOG("ps=%x, pe=%lx\n", io_data.physical_size, io_data.physical_end); + io_data.logical_offset = -1; /* read reset */ + + //TODO: other validations + + /* setup subfile */ + new_sf = open_io_streamfile_f(sf, &io_data, sizeof(deblock_io_data), deblock_io_read, deblock_io_size); + return new_sf; +fail: + VGM_LOG("DEBLOCK: bad init\n"); + close_streamfile(sf); + return NULL; +} + +/*****************************************************/ + +static void block_callback(STREAMFILE *sf, off_t offset, deblock_io_data *data) { + /* read the whole block, will be skipped for unwanted sub-streams */ + data->block_size = 0x02 + read_u16be(data->physical_offset, sf); + data->data_size = data->block_size; + //VGM_LOG("read at %lx + %lx, skips=%i, reads=%i\n", data->physical_offset, data->block_size, data->step_count, data->read_count); +} + +static STREAMFILE* open_io_eaac_opus_streamfile_f(STREAMFILE *new_sf, int stream_number, int stream_count) { + deblock_config_t cfg = {0}; + + cfg.step_start = stream_number; + cfg.step_count = stream_count - 1; + cfg.block_callback = block_callback; + /* starts from 0 since new_sf is pre-deblocked */ + + /* setup subfile */ + //new_sf = open_wrap_streamfile(sf); /* to be used with others */ + new_sf = open_io_deblocker_streamfile_f(new_sf, &cfg); + return new_sf; +} + +#endif /* _EA_EAAC_OPUS_STREAMFILE_H_ */ diff --git a/src/meta/ea_eaac_streamfile.h b/src/meta/ea_eaac_streamfile.h index 0d8ac8d0..9e43540e 100644 --- a/src/meta/ea_eaac_streamfile.h +++ b/src/meta/ea_eaac_streamfile.h @@ -1,6 +1,7 @@ #ifndef _EA_EAAC_STREAMFILE_H_ #define _EA_EAAC_STREAMFILE_H_ #include "../streamfile.h" +#include "ea_eaac_opus_streamfile.h" #define XMA_FRAME_SIZE 0x800 @@ -261,6 +262,8 @@ static STREAMFILE* setup_eaac_streamfile(STREAMFILE *sf, int version, int codec, new_sf = open_wrap_streamfile(sf); new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(eaac_io_data), eaac_io_read, eaac_io_size); new_sf = open_buffer_streamfile_f(new_sf, 0); /* EA-XMA and multichannel EALayer3 benefit from this */ + if (codec == 0x0c && stream_count > 1) /* multichannel opus */ + new_sf = open_io_eaac_opus_streamfile_f(new_sf, stream_number, stream_count); return new_sf; }