ffmpeg: internals cleanup

This commit is contained in:
bnnm 2021-07-23 18:55:03 +02:00
parent 805412e129
commit 039541f881
16 changed files with 375 additions and 334 deletions

View File

@ -552,6 +552,8 @@ void free_speex(speex_codec_data* data);
#ifdef VGM_USE_FFMPEG
/* ffmpeg_decoder */
typedef struct ffmpeg_codec_data ffmpeg_codec_data;
ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t size);
ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size);
ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong);
@ -566,7 +568,14 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data);
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channels_remap);
const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data);
void ffmpeg_set_force_seek(ffmpeg_codec_data* data);
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data);
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key);
int32_t ffmpeg_get_samples(ffmpeg_codec_data* data);
int ffmpeg_get_sample_rate(ffmpeg_codec_data* data);
int ffmpeg_get_channels(ffmpeg_codec_data* data);
int ffmpeg_get_subsong_count(ffmpeg_codec_data* data);
STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data);
/* ffmpeg_decoder_utils.c (helper-things) */

View File

@ -2,10 +2,61 @@
#include "coding.h"
#ifdef VGM_USE_FFMPEG
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
/* opaque struct */
struct ffmpeg_codec_data {
/*** IO internals ***/
STREAMFILE* sf;
uint64_t start; // absolute start within the streamfile
uint64_t offset; // absolute offset within the streamfile
uint64_t size; // max size within the streamfile
uint64_t logical_offset; // computed offset FFmpeg sees (including fake header)
uint64_t logical_size; // computed size FFmpeg sees (including fake header)
uint64_t header_size; // fake header (parseable by FFmpeg) prepended on reads
uint8_t* header_block; // fake header data (ie. RIFF)
/*** internal state ***/
// config
int stream_count; /* FFmpeg audio streams (ignores video/etc) */
int stream_index;
int64_t total_samples; /* may be 0 and innacurate */
int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */
int channel_remap_set;
int channel_remap[32]; /* map of channel > new position */
int invert_floats_set;
int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
int force_seek; /* flags for special seeking in faulty formats */
int bad_init;
// FFmpeg context used for metadata
AVCodec* codec;
/* FFmpeg decoder state */
unsigned char* buffer;
AVIOContext* ioCtx;
AVFormatContext* formatCtx;
AVCodecContext* codecCtx;
AVFrame* frame; /* last decoded frame */
AVPacket* packet; /* last read data packet */
int read_packet;
int end_of_stream;
int end_of_audio;
/* sample state */
int32_t samples_discard;
int32_t samples_consumed;
int32_t samples_filled;
};
#define FFMPEG_DEFAULT_IO_BUFFER_SIZE 128 * 1024
static volatile int g_ffmpeg_initialized = 0;
static void free_ffmpeg_config(ffmpeg_codec_data* data);
@ -69,8 +120,8 @@ static int init_seek(ffmpeg_codec_data* data) {
int size = 0; /* data size (block align) */
int distance = 0; /* always 0 ("duration") */
AVStream * stream = data->formatCtx->streams[data->streamIndex];
AVPacket * pkt = data->packet;
AVStream* stream = data->formatCtx->streams[data->stream_index];
AVPacket* pkt = data->packet;
/* read_seek shouldn't need this index, but direct access to FFmpeg's internals is no good */
@ -95,7 +146,7 @@ static int init_seek(ffmpeg_codec_data* data) {
ret = av_read_frame(data->formatCtx, pkt);
if (ret < 0)
break;
if (pkt->stream_index != data->streamIndex)
if (pkt->stream_index != data->stream_index)
continue; /* ignore non-selected streams */
//;VGM_LOG("FFMPEG: packet %i, ret=%i, pos=%i, dts=%i\n", packet_count, ret, (int32_t)pkt->pos, (int32_t)pkt->dts);
@ -138,7 +189,7 @@ static int init_seek(ffmpeg_codec_data* data) {
test_seek:
/* seek to 0 test + move back to beginning, since we just consumed packets */
ret = avformat_seek_file(data->formatCtx, data->streamIndex, ts, ts, ts, AVSEEK_FLAG_ANY);
ret = avformat_seek_file(data->formatCtx, data->stream_index, ts, ts, ts, AVSEEK_FLAG_ANY);
if ( ret < 0 ) {
//char test[1000] = {0}; av_strerror(ret, test, 1000); VGM_LOG("FFMPEG: ret=%i %s\n", ret, test);
return ret; /* we can't even reset_vgmstream the file */
@ -186,7 +237,7 @@ static int ffmpeg_read(void* opaque, uint8_t* buf, int read_size) {
}
/* main read */
bytes = read_streamfile(buf, data->offset, read_size, data->streamfile);
bytes = read_streamfile(buf, data->offset, read_size, data->sf);
data->logical_offset += bytes;
data->offset += bytes;
return bytes + max_to_copy;
@ -244,7 +295,7 @@ ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t s
return init_ffmpeg_header_offset(sf, NULL,0, start,size);
}
ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) {
ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size) {
return init_ffmpeg_header_offset_subsong(sf, header, header_size, start, size, 0);
}
@ -281,8 +332,8 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
data = calloc(1, sizeof(ffmpeg_codec_data));
if (!data) return NULL;
data->streamfile = reopen_streamfile(sf, 0);
if (!data->streamfile) goto fail;
data->sf = reopen_streamfile(sf, 0);
if (!data->sf) goto fail;
/* fake header to trick FFmpeg into demuxing/decoding the stream */
if (header_size > 0) {
@ -307,16 +358,16 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
/* setup other values */
{
AVStream *stream = data->formatCtx->streams[data->streamIndex];
AVStream* stream = data->formatCtx->streams[data->stream_index];
AVRational tb = {0};
tb.num = 1; tb.den = data->codecCtx->sample_rate;
#if 0
/* derive info */
data->sampleRate = data->codecCtx->sample_rate;
data->channels = data->codecCtx->channels;
data->bitrate = (int)(data->codecCtx->bit_rate);
#if 0
data->blockAlign = data->codecCtx->block_align;
data->frameSize = data->codecCtx->frame_size;
if(data->frameSize == 0) /* some formats don't set frame_size but can get on request, and vice versa */
@ -324,14 +375,16 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
#endif
/* try to guess frames/samples (duration isn't always set) */
data->totalSamples = av_rescale_q(stream->duration, stream->time_base, tb);
if (data->totalSamples < 0)
data->totalSamples = 0; /* caller must consider this */
data->total_samples = av_rescale_q(stream->duration, stream->time_base, tb);
if (data->total_samples < 0)
data->total_samples = 0;
/* read start samples to be skipped (encoder delay), info only.
* Not too reliable though, see ffmpeg_set_skip_samples */
if (stream->start_time > 0 && stream->start_time != AV_NOPTS_VALUE)
if (stream->start_time && stream->start_time != AV_NOPTS_VALUE)
data->skip_samples = av_rescale_q(stream->start_time, stream->time_base, tb);
if (data->skip_samples < 0)
data->skip_samples = 0;
#if 0 //LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
/* exposed before but not too reliable either */
@ -398,44 +451,46 @@ static int init_ffmpeg_config(ffmpeg_codec_data* data, int target_subsong, int r
/* find valid audio stream and set other streams to discard */
{
int i, streamIndex, streamCount;
int i, stream_index, stream_count;
streamIndex = -1;
streamCount = 0;
stream_index = -1;
stream_count = 0;
if (reset)
streamIndex = data->streamIndex;
stream_index = data->stream_index;
for (i = 0; i < data->formatCtx->nb_streams; ++i) {
AVStream *stream = data->formatCtx->streams[i];
AVStream* stream = data->formatCtx->streams[i];
if (stream->codecpar && stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
streamCount++;
stream_count++;
/* select Nth audio stream if specified, or first one */
if (streamIndex < 0 || (target_subsong > 0 && streamCount == target_subsong)) {
streamIndex = i;
if (stream_index < 0 || (target_subsong > 0 && stream_count == target_subsong)) {
stream_index = i;
}
}
if (i != streamIndex)
if (i != stream_index)
stream->discard = AVDISCARD_ALL; /* disable demuxing for other streams */
}
if (streamCount < target_subsong) goto fail;
if (streamIndex < 0) goto fail;
if (stream_count < target_subsong) goto fail;
if (stream_index < 0) goto fail;
data->streamIndex = streamIndex;
data->streamCount = streamCount;
data->stream_index = stream_index;
data->stream_count = stream_count;
}
/* setup codec with stream info */
data->codecCtx = avcodec_alloc_context3(NULL);
if (!data->codecCtx) goto fail;
errcode = avcodec_parameters_to_context(data->codecCtx, ((AVStream*)data->formatCtx->streams[data->streamIndex])->codecpar);
errcode = avcodec_parameters_to_context(data->codecCtx, data->formatCtx->streams[data->stream_index]->codecpar);
if (errcode < 0) goto fail;
//av_codec_set_pkt_timebase(data->codecCtx, stream->time_base); /* deprecated and seemingly not needed */
/* deprecated and seemingly not needed */
//av_codec_set_pkt_timebase(data->codecCtx, stream->time_base);
/* not useddeprecated and seemingly not needed */
data->codec = avcodec_find_decoder(data->codecCtx->codec_id);
if (!data->codec) goto fail;
@ -501,7 +556,7 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
}
/* ignore non-selected streams */
if (data->packet->stream_index != data->streamIndex)
if (data->packet->stream_index != data->stream_index)
continue;
}
@ -788,7 +843,7 @@ void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
if (errcode < 0) goto fail;
}
else {
avformat_seek_file(data->formatCtx, data->streamIndex, 0, 0, 0, AVSEEK_FLAG_ANY);
avformat_seek_file(data->formatCtx, data->stream_index, 0, 0, 0, AVSEEK_FLAG_ANY);
avcodec_flush_buffers(data->codecCtx);
}
@ -864,7 +919,7 @@ void free_ffmpeg(ffmpeg_codec_data* data) {
data->header_block = NULL;
}
close_streamfile(data->streamfile);
close_streamfile(data->sf);
free(data);
}
@ -897,7 +952,7 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) {
#if 0
{
AVStream* stream = data->formatCtx->streams[data->streamIndex];
AVStream* stream = data->formatCtx->streams[data->stream_index];
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 64, 100)
stream->start_skip_samples = 0;
stream->skip_samples = 0;
@ -925,10 +980,10 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data) {
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) {
int i;
if (data->channels > 32)
if (data->codecCtx->channels > 32)
return;
for (i = 0; i < data->channels; i++) {
for (i = 0; i < data->codecCtx->channels; i++) {
data->channel_remap[i] = channel_remap[i];
}
data->channel_remap_set = 1;
@ -945,12 +1000,20 @@ const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) {
}
void ffmpeg_set_force_seek(ffmpeg_codec_data* data) {
if (!data)
return;
/* some formats like Smacker are so buggy that any seeking is impossible (even on video players),
* or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset).
* whatever, we'll just kill and reconstruct FFmpeg's config every time */
data->force_seek = 1;
reset_ffmpeg(data); /* reset state from trying to seek */
//stream = data->formatCtx->streams[data->streamIndex];
//stream = data->formatCtx->streams[data->stream_index];
}
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data) {
if (!data)
return;
data->invert_floats_set = 1;
}
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) {
@ -960,7 +1023,7 @@ const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key)
if (!data || !data->codec)
return NULL;
avd = data->formatCtx->streams[data->streamIndex]->metadata; /* per stream (like Ogg) */
avd = data->formatCtx->streams[data->stream_index]->metadata; /* per stream (like Ogg) */
if (!avd)
avd = data->formatCtx->metadata; /* per format (like Flac) */
if (!avd)
@ -973,8 +1036,33 @@ const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key)
return avde->value;
}
int32_t ffmpeg_get_samples(ffmpeg_codec_data* data) {
if (!data)
return 0;
return (int32_t)data->total_samples;
}
int ffmpeg_get_sample_rate(ffmpeg_codec_data* data) {
if (!data || !data->codecCtx)
return 0;
return data->codecCtx->sample_rate;
}
int ffmpeg_get_channels(ffmpeg_codec_data* data) {
if (!data || !data->codecCtx)
return 0;
return data->codecCtx->channels;
}
int ffmpeg_get_subsong_count(ffmpeg_codec_data* data) {
if (!data)
return 0;
return data->stream_count;
}
STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data) {
if (!data) return NULL;
return data->streamfile;
return data->sf;
}
#endif

View File

@ -2,7 +2,7 @@
#ifdef VGM_USE_FFMPEG
static int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) {
static int ffmpeg_make_riff_atrac3(uint8_t* buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) {
uint16_t codec_ATRAC3 = 0x0270;
size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4;
@ -41,7 +41,7 @@ static int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample
return riff_size;
}
ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay) {
ffmpeg_codec_data* init_ffmpeg_atrac3_raw(STREAMFILE* sf, off_t offset, size_t data_size, int sample_count, int channels, int sample_rate, int block_align, int encoder_delay) {
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[0x100];
int bytes;
@ -57,16 +57,14 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_raw(STREAMFILE *sf, off_t offset, size_t
* in offsets, so calcs are expected to be handled externally (presumably the game would call raw decoding API
* and any skips would be handled manually) */
/* FFmpeg reads this but just in case they fiddle with it in the future */
ffmpeg_data->totalSamples = sample_count;
/* encoder delay: encoder introduces some garbage (not always silent) samples to skip at the beginning (at least 1 frame)
* FFmpeg doesn't set this, and even if it ever does it's probably better to force it for the implicit skip. */
ffmpeg_set_skip_samples(ffmpeg_data, encoder_delay);
//ffmpeg_set_samples(sample_count); /* useful? */
/* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */
if (is_at3) {
ffmpeg_data->invert_floats_set = 1;
ffmpeg_set_invert_floats(ffmpeg_data);
}
return ffmpeg_data;
@ -76,7 +74,7 @@ fail:
}
/* init ATRAC3/plus while adding some fixes */
ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* out_samples) {
ffmpeg_codec_data* init_ffmpeg_atrac3_riff(STREAMFILE* sf, off_t offset, int* p_samples) {
ffmpeg_codec_data *ffmpeg_data = NULL;
int is_at3 = 0, is_at3p = 0, codec;
size_t riff_size;
@ -151,35 +149,33 @@ ffmpeg_codec_data * init_ffmpeg_atrac3_riff(STREAMFILE *sf, off_t offset, int* o
implicit_skip = 0;
}
/* FFmpeg reads this but just in case they fiddle with it in the future */
ffmpeg_data->totalSamples = fact_samples;
/* encoder delay: encoder introduces some garbage (not always silent) samples to skip at the beginning (at least 1 frame)
* FFmpeg doesn't set this, and even if it ever does it's probably better to force it for the implicit skip. */
ffmpeg_set_skip_samples(ffmpeg_data, skip_samples + implicit_skip);
//ffmpeg_set_samples(sample_count); /* useful? */
/* invert ATRAC3: waveform is inverted vs official tools (not noticeable but for accuracy) */
if (is_at3) {
ffmpeg_data->invert_floats_set = 1;
ffmpeg_set_invert_floats(ffmpeg_data);
}
/* multichannel fix: LFE channel should be reordered on decode (ATRAC3Plus only, only 1/2/6/8ch exist):
* - 6ch: FL FR FC BL BR LFE > FL FR FC LFE BL BR
* - 8ch: FL FR FC BL BR SL SR LFE > FL FR FC LFE BL BR SL SR */
if (is_at3p && ffmpeg_data->channels == 6) {
if (is_at3p && ffmpeg_get_channels(ffmpeg_data) == 6) {
/* LFE BR BL > LFE BL BR > same */
int channel_remap[] = { 0, 1, 2, 5, 5, 5, };
ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap);
}
else if (is_at3p && ffmpeg_data->channels == 8) {
else if (is_at3p && ffmpeg_get_channels(ffmpeg_data) == 8) {
/* LFE BR SL SR BL > LFE BL SL SR BR > LFE BL BR SR SL > LFE BL BR SL SR > same */
int channel_remap[] = { 0, 1, 2, 7, 7, 7, 7, 7};
ffmpeg_set_channel_remapping(ffmpeg_data, channel_remap);
}
if (out_samples)
*out_samples = fact_samples;
if (p_samples)
*p_samples = fact_samples;
return ffmpeg_data;
fail:

View File

@ -23,7 +23,7 @@ static int find_meta_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32
VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
ffmpeg_codec_data* data = NULL;
int loop_flag = 0;
int loop_flag = 0, channels, sample_rate;
int32_t loop_start = 0, loop_end = 0, num_samples = 0, encoder_delay = 0;
int total_subsongs, target_subsong = sf->stream_index;
@ -45,18 +45,18 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
data = init_ffmpeg_header_offset_subsong(sf, NULL, 0, 0, get_streamfile_size(sf), target_subsong);
if (!data) return NULL;
total_subsongs = data->streamCount; /* uncommon, ex. wmv [Lost Odyssey (X360)] */
total_subsongs = ffmpeg_get_subsong_count(data); /* uncommon, ex. wmv [Lost Odyssey (X360)] */
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* try to get .pos data */
{
uint8_t posbuf[4+4+4];
uint8_t posbuf[0x04*3];
if (read_pos_file(posbuf, 4+4+4, sf)) {
loop_start = get_s32le(posbuf+0);
loop_end = get_s32le(posbuf+4);
if (read_pos_file(posbuf, sizeof(posbuf), sf)) {
loop_start = get_s32le(posbuf+0x00);
loop_end = get_s32le(posbuf+0x04);
loop_flag = 1; /* incorrect looping will be validated outside */
/* FFmpeg can't always determine totalSamples correctly so optionally load it (can be 0/NULL)
/* FFmpeg can't always determine samples correctly so optionally load it (can be 0/NULL)
* won't crash and will output silence if no loop points and bigger than actual stream's samples */
num_samples = get_s32le(posbuf+8);
}
@ -103,18 +103,22 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
/* default but often inaccurate when calculated using bitrate (wrong for VBR) */
if (!num_samples) {
num_samples = data->totalSamples; /* may be 0 if FFmpeg can't precalculate it */
num_samples = ffmpeg_get_samples(data); /* may be 0 if FFmpeg can't precalculate it */
}
channels = ffmpeg_get_channels(data);
sample_rate = ffmpeg_get_sample_rate(data);
/* build VGMSTREAM */
vgmstream = allocate_vgmstream(data->channels, loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = data->sampleRate;
vgmstream->meta_type = meta_FFMPEG;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->sample_rate = sample_rate;
vgmstream->codec_data = data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = num_samples;
@ -214,7 +218,7 @@ static int find_meta_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32
if (loop_end <= 0) {
// Looks a calculation was not possible, or tag value is wrongly set. Use the end of track as end value
loop_end = data->totalSamples;
loop_end = ffmpeg_get_samples(data);
}
if (loop_start <= 0) {

View File

@ -307,7 +307,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
if ( !ffmpeg_data ) goto fail;
//if (vgmstream->num_samples == 0)
// vgmstream->num_samples = ffmpeg_data->totalSamples; /* sometimes works */
// vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); /* sometimes works */
}
else {
/* fake header FFmpeg */

View File

@ -163,11 +163,14 @@ fail:
#ifdef VGM_USE_FFMPEG
typedef struct {
int channels;
int sample_rate;
int32_t num_samples;
int loop_flag;
int32_t loop_start;
int32_t loop_end;
int32_t encoder_delay;
int subsongs;
} mp4_header;
static void parse_mp4(STREAMFILE* sf, mp4_header* mp4);
@ -199,25 +202,33 @@ VGMSTREAM* init_vgmstream_mp4_aac_ffmpeg(STREAMFILE* sf) {
parse_mp4(sf, &mp4);
/* most values aren't read directly and use FFmpeg b/c MP4 makes things hard */
if (!mp4.num_samples)
mp4.num_samples = ffmpeg_get_samples(ffmpeg_data); /* does this take into account encoder delay? see FFV */
if (!mp4.channels)
mp4.channels = ffmpeg_get_channels(ffmpeg_data);
if (!mp4.sample_rate)
mp4.sample_rate = ffmpeg_get_sample_rate(ffmpeg_data);
if (!mp4.subsongs)
mp4.subsongs = ffmpeg_get_subsong_count(ffmpeg_data); /* may contain N tracks */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ffmpeg_data->channels, mp4.loop_flag);
vgmstream = allocate_vgmstream(mp4.channels, mp4.loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_MP4;
vgmstream->sample_rate = ffmpeg_data->sampleRate;
vgmstream->sample_rate = mp4.sample_rate;
vgmstream->num_samples = mp4.num_samples;
if (vgmstream->num_samples == 0) /* does this take into account encoder delay? see FFV */
vgmstream->num_samples = ffmpeg_data->totalSamples;
vgmstream->loop_start_sample = mp4.loop_start;
vgmstream->loop_end_sample = mp4.loop_end;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_streams = ffmpeg_data->streamCount; /* may contain N tracks */
vgmstream->num_streams = mp4.subsongs;
vgmstream->channel_layout = ffmpeg_get_channel_layout(vgmstream->codec_data);
vgmstream->channel_layout = ffmpeg_get_channel_layout(ffmpeg_data);
/* needed for CRI MP4, otherwise FFmpeg usually reads standard delay */
ffmpeg_set_skip_samples(vgmstream->codec_data, mp4.encoder_delay);
@ -225,11 +236,11 @@ VGMSTREAM* init_vgmstream_mp4_aac_ffmpeg(STREAMFILE* sf) {
return vgmstream;
fail:
if (ffmpeg_data) {
free_ffmpeg(ffmpeg_data);
if (vgmstream) vgmstream->codec_data = NULL;
free_ffmpeg(ffmpeg_data);
if (vgmstream) {
vgmstream->codec_data = NULL;
close_vgmstream(vgmstream);
}
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -144,20 +144,16 @@ VGMSTREAM* init_vgmstream_msf(STREAMFILE* sf) {
}
#elif defined(VGM_USE_FFMPEG)
case 0x07: { /* MPEG (LAME MP3) [Dengeki Bunko Fighting Climax (PS3), Asura's Wrath (PS3)-vbr] */
ffmpeg_codec_data *ffmpeg_data = NULL;
int is_vbr = (flags & 0x20); /* must calc samples/loop offsets manually */
ffmpeg_data = init_ffmpeg_offset(sf, start_offset, sf->get_size(sf));
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, 0);;
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
//todo use same calcs as above
vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate;
if (loop_flag) {
vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate;
vgmstream->loop_end_sample = (int64_t)loop_end * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate;
}
vgmstream->num_samples = mpeg_get_samples_clean(sf, start_offset, data_size, &loop_start, &loop_end, is_vbr);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
break;
}
#endif

View File

@ -151,7 +151,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *sf) {
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); /* an estimation, sometimes cuts files a bit early */
//vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf) / channel_count / 2; /* may be PCM data size, but not exact */
vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, sf);
break;

View File

@ -150,8 +150,8 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) {
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = ffmpeg_data->totalSamples;
if (vgmstream->channels != ffmpeg_data->channels)
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data);
if (vgmstream->channels != ffmpeg_get_channels(ffmpeg_data))
goto fail;
break;

View File

@ -483,7 +483,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
if (!ffmpeg_data) goto fail;
if (vgmstream->num_samples == 0)
vgmstream->num_samples = ffmpeg_data->totalSamples; /* sometimes works */
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); /* sometimes works */
}
else if (txth.codec == AAC) {
ffmpeg_data = init_ffmpeg_aac(txth.sf_body, txth.start_offset, txth.data_size, 0);

View File

@ -412,7 +412,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
vgmstream->num_samples = msd.num_samples;
if (!vgmstream->num_samples)
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* very wrong, from avg-br */
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); /* very wrong, from avg-br */
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
}
@ -433,7 +433,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples;
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data); //todo correct?
break;
}
@ -518,7 +518,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
vgmstream->layout_type = layout_none;
/* FFmpeg's samples seem correct, otherwise see ogg_opus.c for getting samples. */
vgmstream->num_samples = (int32_t)((ffmpeg_codec_data*)vgmstream->codec_data)->totalSamples;
vgmstream->num_samples = ffmpeg_get_samples(vgmstream->codec_data);
break;
}

View File

@ -530,7 +530,7 @@ VGMSTREAM* init_vgmstream_xwb(STREAMFILE* sf) {
/* no wma_bytes_to_samples, this should be ok */
if (!vgmstream->num_samples)
vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples;
vgmstream->num_samples = ffmpeg_get_samples(ffmpeg_data);
break;
}

View File

@ -1,87 +1,87 @@
#include "meta.h"
#include "../coding/coding.h"
/* XWMA - Microsoft WMA container [The Elder Scrolls: Skyrim (PC/X360), Hydrophobia (PC)] */
VGMSTREAM * init_vgmstream_xwma(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t fmt_offset, data_offset, first_offset = 0xc;
size_t fmt_size, data_size;
int loop_flag, channel_count;
/* checks */
/* .xwma: standard
* .xwm: The Elder Scrolls: Skyrim (PC), Blade Arcus from Shining (PC) */
if (!check_extensions(streamFile, "xwma,xwm"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x58574D41) /* "XWMA" */
goto fail;
if ( !find_chunk_le(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size) ) /* "fmt "*/
goto fail;
if ( !find_chunk_le(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size) ) /* "data"*/
goto fail;
channel_count = read_16bitLE(fmt_offset+0x02,streamFile);
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(fmt_offset+0x04, streamFile);
vgmstream->meta_type = meta_XWMA;
/* the main purpose of this meta is redoing the XWMA header to:
* - redo header to fix XWMA with buggy bit rates so FFmpeg can play them ok
* - skip seek table to fix FFmpeg buggy XWMA seeking (see init_seek)
* - read num_samples correctly
*/
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
int bytes, avg_bps, block_align, wma_codec;
avg_bps = read_32bitLE(fmt_offset+0x08, streamFile);
block_align = (uint16_t)read_16bitLE(fmt_offset+0x0c, streamFile);
wma_codec = (uint16_t)read_16bitLE(fmt_offset+0x00, streamFile);
bytes = ffmpeg_make_riff_xwma(buf,0x100, wma_codec, data_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, data_offset,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* manually find total samples, why don't they put this in the header is beyond me */
{
ms_sample_data msd = {0};
msd.channels = vgmstream->channels;
msd.data_offset = data_offset;
msd.data_size = data_size;
if (wma_codec == 0x0162)
wmapro_get_samples(&msd, streamFile, block_align, vgmstream->sample_rate,0x00E0);
else
wma_get_samples(&msd, streamFile, block_align, vgmstream->sample_rate,0x001F);
vgmstream->num_samples = msd.num_samples;
if (vgmstream->num_samples == 0)
vgmstream->num_samples = (int32_t)((ffmpeg_codec_data*)vgmstream->codec_data)->totalSamples; /* from avg-br */
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
}
}
#else
goto fail;
#endif
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
/* XWMA - Microsoft WMA container [The Elder Scrolls: Skyrim (PC/X360), Hydrophobia (PC)] */
VGMSTREAM * init_vgmstream_xwma(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t fmt_offset, data_offset, first_offset = 0xc;
size_t fmt_size, data_size;
int loop_flag, channel_count;
/* checks */
/* .xwma: standard
* .xwm: The Elder Scrolls: Skyrim (PC), Blade Arcus from Shining (PC) */
if (!check_extensions(streamFile, "xwma,xwm"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x58574D41) /* "XWMA" */
goto fail;
if ( !find_chunk_le(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size) ) /* "fmt "*/
goto fail;
if ( !find_chunk_le(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size) ) /* "data"*/
goto fail;
channel_count = read_16bitLE(fmt_offset+0x02,streamFile);
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(fmt_offset+0x04, streamFile);
vgmstream->meta_type = meta_XWMA;
/* the main purpose of this meta is redoing the XWMA header to:
* - redo header to fix XWMA with buggy bit rates so FFmpeg can play them ok
* - skip seek table to fix FFmpeg buggy XWMA seeking (see init_seek)
* - read num_samples correctly
*/
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
int bytes, avg_bps, block_align, wma_codec;
avg_bps = read_32bitLE(fmt_offset+0x08, streamFile);
block_align = (uint16_t)read_16bitLE(fmt_offset+0x0c, streamFile);
wma_codec = (uint16_t)read_16bitLE(fmt_offset+0x00, streamFile);
bytes = ffmpeg_make_riff_xwma(buf,0x100, wma_codec, data_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, data_offset,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* manually find total samples, why don't they put this in the header is beyond me */
{
ms_sample_data msd = {0};
msd.channels = vgmstream->channels;
msd.data_offset = data_offset;
msd.data_size = data_size;
if (wma_codec == 0x0162)
wmapro_get_samples(&msd, streamFile, block_align, vgmstream->sample_rate,0x00E0);
else
wma_get_samples(&msd, streamFile, block_align, vgmstream->sample_rate,0x001F);
vgmstream->num_samples = msd.num_samples;
if (vgmstream->num_samples == 0)
vgmstream->num_samples = ffmpeg_get_samples(vgmstream->codec_data); /* from avg-br */
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
}
}
#else
goto fail;
#endif
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,86 +1,86 @@
#include "meta.h"
#include "../coding/coding.h"
#include "xwma_konami_streamfile.h"
/* MSFC - Konami (Armature?) variation [Metal Gear Solid 2 HD (X360), Metal Gear Solid 3 HD (X360)] */
VGMSTREAM * init_vgmstream_xwma_konami(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, codec, sample_rate;
size_t data_size;
STREAMFILE *temp_streamFile = NULL;
/* checks */
if (!check_extensions(streamFile,"xwma"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x58574D41) /* "XWMA" */
goto fail;
codec = read_32bitBE(0x04,streamFile);
channel_count = read_32bitBE(0x08,streamFile);
sample_rate = read_32bitBE(0x0c,streamFile);
data_size = read_32bitBE(0x10,streamFile); /* data size without padding */
loop_flag = 0;
start_offset = 0x20;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->meta_type = meta_XWMA_KONAMI;
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
int bytes, avg_bps, block_align;
/* 0x10: related to size? */
avg_bps = read_32bitBE(0x14, streamFile);
block_align = read_32bitBE(0x18, streamFile);
/* data has padding (unrelated to KCEJ blocks) */
temp_streamFile = setup_xwma_konami_streamfile(streamFile, start_offset, block_align);
if (!temp_streamFile) goto fail;
bytes = ffmpeg_make_riff_xwma(buf,0x100, codec, data_size, channel_count, sample_rate, avg_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* manually find total samples */
{
ms_sample_data msd = {0};
msd.channels = vgmstream->channels;
msd.data_offset = 0x00;
msd.data_size = data_size;
if (codec == 0x0161)
wma_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x001F);
//else //todo not correct
// wmapro_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x00E0);
vgmstream->num_samples = msd.num_samples;
if (vgmstream->num_samples == 0)
vgmstream->num_samples = (int32_t)((ffmpeg_codec_data*)vgmstream->codec_data)->totalSamples; /* from avg-br */
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
}
}
#else
goto fail;
#endif
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
#include "xwma_konami_streamfile.h"
/* MSFC - Konami (Armature?) variation [Metal Gear Solid 2 HD (X360), Metal Gear Solid 3 HD (X360)] */
VGMSTREAM * init_vgmstream_xwma_konami(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, codec, sample_rate;
size_t data_size;
STREAMFILE *temp_streamFile = NULL;
/* checks */
if (!check_extensions(streamFile,"xwma"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x58574D41) /* "XWMA" */
goto fail;
codec = read_32bitBE(0x04,streamFile);
channel_count = read_32bitBE(0x08,streamFile);
sample_rate = read_32bitBE(0x0c,streamFile);
data_size = read_32bitBE(0x10,streamFile); /* data size without padding */
loop_flag = 0;
start_offset = 0x20;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->meta_type = meta_XWMA_KONAMI;
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
int bytes, avg_bps, block_align;
/* 0x10: related to size? */
avg_bps = read_32bitBE(0x14, streamFile);
block_align = read_32bitBE(0x18, streamFile);
/* data has padding (unrelated to KCEJ blocks) */
temp_streamFile = setup_xwma_konami_streamfile(streamFile, start_offset, block_align);
if (!temp_streamFile) goto fail;
bytes = ffmpeg_make_riff_xwma(buf,0x100, codec, data_size, channel_count, sample_rate, avg_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* manually find total samples */
{
ms_sample_data msd = {0};
msd.channels = vgmstream->channels;
msd.data_offset = 0x00;
msd.data_size = data_size;
if (codec == 0x0161)
wma_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x001F);
//else //todo not correct
// wmapro_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x00E0);
vgmstream->num_samples = msd.num_samples;
if (vgmstream->num_samples == 0)
vgmstream->num_samples = ffmpeg_get_samples(vgmstream->codec_data); /* from avg-br */
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
}
}
#else
goto fail;
#endif
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -606,9 +606,9 @@ static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) {
#ifdef VGM_USE_FFMPEG
/* check FFmpeg streams here, for lack of a better place */
if (vgmstream->coding_type == coding_FFmpeg) {
ffmpeg_codec_data *data = vgmstream->codec_data;
if (data && data->streamCount && !vgmstream->num_streams) {
vgmstream->num_streams = data->streamCount;
int ffmpeg_subsongs = ffmpeg_get_subsong_count(vgmstream->codec_data);
if (ffmpeg_subsongs && !vgmstream->num_streams) {
vgmstream->num_streams = ffmpeg_subsongs;
}
}
#endif

View File

@ -52,12 +52,6 @@ enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */
#include <maiatrac3plus.h>
#endif
#ifdef VGM_USE_FFMPEG
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#endif
#include <clHCA.h>
#include "coding/g72x_state.h"
@ -1042,63 +1036,6 @@ typedef struct {
} acm_codec_data;
#ifdef VGM_USE_FFMPEG
typedef struct {
/*** IO internals ***/
STREAMFILE* streamfile;
uint64_t start; // absolute start within the streamfile
uint64_t offset; // absolute offset within the streamfile
uint64_t size; // max size within the streamfile
uint64_t logical_offset; // computed offset FFmpeg sees (including fake header)
uint64_t logical_size; // computed size FFmpeg sees (including fake header)
uint64_t header_size; // fake header (parseable by FFmpeg) prepended on reads
uint8_t* header_block; // fake header data (ie. RIFF)
/*** "public" API (read-only) ***/
// stream info
int channels;
int sampleRate;
int bitrate;
// extra info: 0 if unknown or not fixed
int64_t totalSamples; // estimated count (may not be accurate for some demuxers)
int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */
int streamCount; // number of FFmpeg audio streams
/*** internal state ***/
// config
int channel_remap_set;
int channel_remap[32]; /* map of channel > new position */
int invert_floats_set;
int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
int force_seek; /* flags for special seeking in faulty formats */
int bad_init;
// FFmpeg context used for metadata
AVCodec *codec;
// FFmpeg decoder state
unsigned char *buffer;
AVIOContext *ioCtx;
int streamIndex;
AVFormatContext *formatCtx;
AVCodecContext *codecCtx;
AVFrame *frame; /* last decoded frame */
AVPacket *packet; /* last read data packet */
int read_packet;
int end_of_stream;
int end_of_audio;
/* sample state */
int32_t samples_discard;
int32_t samples_consumed;
int32_t samples_filled;
} ffmpeg_codec_data;
#endif
#ifdef VGM_USE_MP4V2
typedef struct {
STREAMFILE* streamfile;