Added FFmpeg support, and fixed a long standing issue with broken loops locking up the decoder.

This commit is contained in:
Chris Moeller 2016-07-16 23:02:27 -07:00
parent b31ee39a95
commit cbe9bc3d6b
9 changed files with 828 additions and 2 deletions

View File

@ -108,6 +108,11 @@ void decode_at3plus(VGMSTREAM *vgmstream,
sample * outbuf, int channelspacing, int32_t samples_to_do, int channel);
#endif
#ifdef VGM_USE_FFMPEG
void decode_ffmpeg(VGMSTREAM *stream,
sample * outbuf, int32_t samples_to_do, int channels);
#endif
void decode_acm(ACMStream * acm, sample * outbuf,
int32_t samples_to_do, int channelspacing);

220
src/coding/ffmpeg_decoder.c Normal file
View File

@ -0,0 +1,220 @@
#include "../vgmstream.h"
#ifdef VGM_USE_FFMPEG
static void convert_audio(sample *outbuf, const uint8_t *inbuf, int sampleCount, int bitsPerSample, int floatingPoint) {
int s;
switch (bitsPerSample) {
case 8:
{
for (s = 0; s < sampleCount; ++s) {
*outbuf++ = ((int)(*(inbuf++))-0x80) << 8;
}
}
break;
case 16:
{
int16_t *s16 = (int16_t *)inbuf;
for (s = 0; s < sampleCount; ++s) {
*outbuf++ = *(s16++);
}
}
break;
case 32:
{
if (!floatingPoint) {
int32_t *s32 = (int32_t *)inbuf;
for (s = 0; s < sampleCount; ++s) {
*outbuf++ = (*(s32++)) >> 16;
}
}
else {
float *s32 = (float *)inbuf;
for (s = 0; s < sampleCount; ++s) {
float sample = *s32++;
int s16 = (int)(sample * 32768.0f);
if ((unsigned)(s16 + 0x8000) & 0xFFFF0000) {
s16 = (s16 >> 31) ^ 0x7FFF;
}
*outbuf++ = s16;
}
}
}
break;
case 64:
{
if (floatingPoint) {
double *s64 = (double *)inbuf;
for (s = 0; s < sampleCount; ++s) {
double sample = *s64++;
int s16 = (int)(sample * 32768.0f);
if ((unsigned)(s16 + 0x8000) & 0xFFFF0000) {
s16 = (s16 >> 31) ^ 0x7FFF;
}
*outbuf++ = s16;
}
}
}
break;
}
}
void decode_ffmpeg(VGMSTREAM *vgmstream,
sample * outbuf, int32_t samples_to_do, int channels) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
int frameSize;
int dataSize;
int bytesToRead;
int bytesRead;
int errcode;
uint8_t *targetBuf;
AVFormatContext *formatCtx;
AVCodecContext *codecCtx;
AVPacket *lastReadPacket;
AVFrame *lastDecodedFrame;
int streamIndex;
int bytesConsumedFromDecodedFrame;
int readNextPacket;
int endOfStream;
int endOfAudio;
int toConsume;
int framesReadNow;
if (data->totalFrames && data->framesRead >= data->totalFrames) {
memset(outbuf, 0, samples_to_do * channels * sizeof(sample));
return;
}
frameSize = data->channels * (data->bitsPerSample / 8);
dataSize = 0;
bytesToRead = samples_to_do * frameSize;
bytesRead = 0;
targetBuf = data->sampleBuffer;
memset(targetBuf, 0, bytesToRead);
formatCtx = data->formatCtx;
codecCtx = data->codecCtx;
lastReadPacket = data->lastReadPacket;
lastDecodedFrame = data->lastDecodedFrame;
streamIndex = data->streamIndex;
bytesConsumedFromDecodedFrame = data->bytesConsumedFromDecodedFrame;
readNextPacket = data->readNextPacket;
endOfStream = data->endOfStream;
endOfAudio = data->endOfAudio;
while (bytesRead < bytesToRead) {
int planeSize;
int planar = av_sample_fmt_is_planar(codecCtx->sample_fmt);
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels,
lastDecodedFrame->nb_samples,
codecCtx->sample_fmt, 1);
if (dataSize < 0)
dataSize = 0;
while (readNextPacket && !endOfAudio) {
if (!endOfStream) {
av_packet_unref(lastReadPacket);
if ((errcode = av_read_frame(formatCtx, lastReadPacket)) < 0) {
if (errcode == AVERROR_EOF) {
endOfStream = 1;
}
if (formatCtx->pb && formatCtx->pb->error)
break;
}
if (lastReadPacket->stream_index != streamIndex)
continue;
}
if ((errcode = avcodec_send_packet(codecCtx, endOfStream ? NULL : lastReadPacket)) < 0) {
if (errcode != AVERROR(EAGAIN)) {
goto end;
}
}
readNextPacket = 0;
}
if (dataSize <= bytesConsumedFromDecodedFrame) {
if (endOfStream && endOfAudio)
break;
bytesConsumedFromDecodedFrame = 0;
if ((errcode = avcodec_receive_frame(codecCtx, lastDecodedFrame)) < 0) {
if (errcode == AVERROR_EOF) {
endOfAudio = 1;
break;
}
else if (errcode == AVERROR(EAGAIN)) {
readNextPacket = 1;
continue;
}
else {
goto end;
}
}
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels, lastDecodedFrame->nb_samples, codecCtx->sample_fmt, 1);
if (dataSize < 0)
dataSize = 0;
}
toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead));
if (!planar || channels == 1) {
memmove(targetBuf + bytesRead, (lastDecodedFrame->data[0] + bytesConsumedFromDecodedFrame), toConsume);
}
else {
uint8_t * out = (uint8_t *) targetBuf + bytesRead;
int bytesPerSample = data->bitsPerSample / 8;
int bytesConsumedPerPlane = bytesConsumedFromDecodedFrame / channels;
int toConsumePerPlane = toConsume / channels;
int s, ch;
for (s = 0; s < toConsumePerPlane; s += bytesPerSample) {
for (ch = 0; ch < channels; ++ch) {
memcpy(out, lastDecodedFrame->extended_data[ch] + bytesConsumedPerPlane + s, bytesPerSample);
out += bytesPerSample;
}
}
}
bytesConsumedFromDecodedFrame += toConsume;
bytesRead += toConsume;
}
end:
framesReadNow = bytesRead / frameSize;
if (data->totalFrames && (data->framesRead + framesReadNow > data->totalFrames)) {
framesReadNow = (int)(data->totalFrames - data->framesRead);
}
data->framesRead += framesReadNow;
// Convert the audio
convert_audio(outbuf, data->sampleBuffer, framesReadNow * channels, data->bitsPerSample, data->floatingPoint);
// Output the state back to the structure
data->bytesConsumedFromDecodedFrame = bytesConsumedFromDecodedFrame;
data->readNextPacket = readNextPacket;
data->endOfStream = endOfStream;
data->endOfAudio = endOfAudio;
}
#endif

View File

@ -18,6 +18,11 @@ void render_vgmstream_nolayout(sample * buffer, int32_t sample_count, VGMSTREAM
if (samples_written+samples_to_do > sample_count)
samples_to_do=sample_count-samples_written;
if (!samples_to_do) {
memset(buffer + samples_written * vgmstream->channels, 0, sizeof(sample) * vgmstream->channels * (sample_count - samples_written));
return;
}
decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer);

344
src/meta/ffmpeg.c Normal file
View File

@ -0,0 +1,344 @@
#include "../vgmstream.h"
#include "meta.h"
#include "../util.h"
#ifdef VGM_USE_FFMPEG
static volatile int g_ffmpeg_initialized = 0;
static void g_init_ffmpeg()
{
if (g_ffmpeg_initialized == 1)
{
while (g_ffmpeg_initialized < 2);
}
else if (g_ffmpeg_initialized == 0)
{
g_ffmpeg_initialized = 1;
av_log_set_flags(AV_LOG_SKIP_REPEATED);
av_log_set_level(AV_LOG_ERROR);
av_register_all();
g_ffmpeg_initialized = 2;
}
}
ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_offset, uint64_t stream_offset, uint64_t stream_size, int fmt_big_endian);
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
return init_ffmpeg_faux_riff(streamFile, -1, start, size, 0);
}
VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile) {
return init_vgmstream_ffmpeg_offset( streamFile, 0, streamFile->get_size(streamFile) );
}
void free_ffmpeg(ffmpeg_codec_data *data);
VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
VGMSTREAM *vgmstream = NULL;
ffmpeg_codec_data *data = init_ffmpeg_offset(streamFile, start, size);
if (!data) return NULL;
vgmstream = allocate_vgmstream(data->channels, 0);
if (!vgmstream) goto fail;
vgmstream->loop_flag = 0;
vgmstream->codec_data = data;
vgmstream->channels = data->channels;
vgmstream->sample_rate = data->sampleRate;
vgmstream->num_samples = data->totalFrames;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_FFmpeg;
return vgmstream;
fail:
free_ffmpeg(data);
return NULL;
}
static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size)
{
ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque;
uint64_t offset = data->offset;
int max_to_copy;
int ret;
if (data->header_insert_block) {
max_to_copy = 0;
if (offset < data->header_size) {
max_to_copy = (int)(data->header_size - offset);
if (max_to_copy > buf_size) {
max_to_copy = buf_size;
}
memcpy(buf, data->header_insert_block + offset, max_to_copy);
buf += max_to_copy;
buf_size -= max_to_copy;
offset += max_to_copy;
if (!buf_size) {
data->offset = offset;
return max_to_copy;
}
}
offset -= data->header_size;
}
ret = read_streamfile(buf, offset + data->start, buf_size, data->streamfile);
if (ret > 0) {
offset += ret;
if (data->header_insert_block) {
ret += max_to_copy;
}
}
if (data->header_insert_block) {
offset += data->header_size;
}
data->offset = offset;
return ret;
}
static int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size)
{
return -1;
}
static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence)
{
ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque;
if (whence & AVSEEK_SIZE) {
return data->size + data->header_size;
}
whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE);
switch (whence) {
case SEEK_CUR:
offset += data->offset;
break;
case SEEK_END:
offset += data->size;
if (data->header_insert_block)
offset += data->header_size;
break;
}
if (offset > data->size + data->header_size)
offset = data->size + data->header_size;
return data->offset = offset;
}
ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_offset, uint64_t start, uint64_t size, int big_endian) {
char filename[PATH_LIMIT];
ffmpeg_codec_data * data;
int errcode, i;
int streamIndex;
AVCodecParameters *codecPar;
AVRational tb;
g_init_ffmpeg();
data = ( ffmpeg_codec_data * ) calloc(1, sizeof(ffmpeg_codec_data));
if (!data) return 0;
streamFile->get_name( streamFile, filename, sizeof(filename) );
data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!data->streamfile) goto fail;
data->start = start;
data->size = size;
if (fmt_offset > 0) {
int max_header_size = (int)(start - fmt_offset);
uint8_t *p;
if (max_header_size < 18) goto fail;
data->header_insert_block = p = av_malloc(max_header_size + 8 + 4 + 8 + 8);
if (!data->header_insert_block) goto fail;
if (read_streamfile(p + 8 + 4 + 8, fmt_offset, max_header_size, streamFile) != max_header_size) goto fail;
if (big_endian) {
p += 8 + 4 + 8;
put_16bitLE(p, get_16bitBE(p));
put_16bitLE(p + 2, get_16bitBE(p + 2));
put_32bitLE(p + 4, get_32bitBE(p + 4));
put_32bitLE(p + 8, get_32bitBE(p + 8));
put_16bitLE(p + 12, get_16bitBE(p + 12));
put_16bitLE(p + 14, get_16bitBE(p + 14));
put_16bitLE(p + 16, get_16bitBE(p + 16));
p -= 8 + 4 + 8;
}
data->header_size = 8 + 4 + 8 + 8 + 18 + get_16bitLE(p + 8 + 4 + 8 + 16);
// Meh, dunno how to handle swapping the extra data
// FFmpeg doesn't need most of this data anyway
if ((unsigned)(get_16bitLE(p + 8 + 4 + 8) - 0x165) < 2)
memset(p + 8 + 4 + 8 + 18, 0, 34);
// Fill out the RIFF structure
memcpy(p, "RIFF", 4);
put_32bitLE(p + 4, data->header_size + size - 8);
memcpy(p + 8, "WAVE", 4);
memcpy(p + 12, "fmt ", 4);
put_32bitLE(p + 16, 18 + get_16bitLE(p + 8 + 4 + 8 + 16));
memcpy(p + data->header_size - 8, "data", 4);
put_32bitLE(p + data->header_size - 4, size);
}
data->buffer = av_malloc(128 * 1024);
if (!data->buffer) goto fail;
data->ioCtx = avio_alloc_context(data->buffer, 128 * 1024, 0, data, ffmpeg_read, ffmpeg_write, ffmpeg_seek);
if (!data->ioCtx) goto fail;
data->formatCtx = avformat_alloc_context();
if (!data->formatCtx) goto fail;
data->formatCtx->pb = data->ioCtx;
if ((errcode = avformat_open_input(&data->formatCtx, "", NULL, NULL)) < 0) goto fail;
if ((errcode = avformat_find_stream_info(data->formatCtx, NULL)) < 0) goto fail;
streamIndex = -1;
for (i = 0; i < data->formatCtx->nb_streams; ++i) {
codecPar = data->formatCtx->streams[i]->codecpar;
if (codecPar->codec_type == AVMEDIA_TYPE_AUDIO) {
streamIndex = i;
break;
}
}
if (streamIndex < 0) goto fail;
data->streamIndex = streamIndex;
data->codecCtx = avcodec_alloc_context3(NULL);
if (!data->codecCtx) goto fail;
if ((errcode = avcodec_parameters_to_context(data->codecCtx, codecPar)) < 0) goto fail;
av_codec_set_pkt_timebase(data->codecCtx, data->formatCtx->streams[streamIndex]->time_base);
data->codec = avcodec_find_decoder(data->codecCtx->codec_id);
if (!data->codec) goto fail;
if ((errcode = avcodec_open2(data->codecCtx, data->codec, NULL)) < 0) goto fail;
data->lastDecodedFrame = av_frame_alloc();
if (!data->lastDecodedFrame) goto fail;
av_frame_unref(data->lastDecodedFrame);
data->lastReadPacket = malloc(sizeof(AVPacket));
if (!data->lastReadPacket) goto fail;
av_new_packet(data->lastReadPacket, 0);
data->readNextPacket = 1;
data->bytesConsumedFromDecodedFrame = INT_MAX;
data->sampleRate = data->codecCtx->sample_rate;
data->channels = data->codecCtx->channels;
data->floatingPoint = 0;
switch (data->codecCtx->sample_fmt) {
case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P:
data->bitsPerSample = 8;
break;
case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S16P:
data->bitsPerSample = 16;
break;
case AV_SAMPLE_FMT_S32:
case AV_SAMPLE_FMT_S32P:
data->bitsPerSample = 32;
break;
case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP:
data->bitsPerSample = 32;
data->floatingPoint = 1;
break;
case AV_SAMPLE_FMT_DBL:
case AV_SAMPLE_FMT_DBLP:
data->bitsPerSample = 64;
data->floatingPoint = 1;
break;
default:
goto fail;
}
tb.num = 1; tb.den = data->codecCtx->sample_rate;
data->totalFrames = av_rescale_q(data->formatCtx->streams[streamIndex]->duration, data->formatCtx->streams[streamIndex]->time_base, tb);
data->bitrate = (int)((data->codecCtx->bit_rate) / 1000);
data->framesRead = 0;
data->endOfStream = 0;
data->endOfAudio = 0;
if (data->totalFrames < 0)
data->totalFrames = 0;
data->sampleBuffer = av_malloc( 2048 * (data->bitsPerSample / 8) * data->channels );
if (!data->sampleBuffer)
goto fail;
return data;
fail:
free_ffmpeg(data);
return NULL;
}
void free_ffmpeg(ffmpeg_codec_data *data) {
if (data->lastReadPacket) {
av_packet_unref(data->lastReadPacket);
free(data->lastReadPacket);
data->lastReadPacket = NULL;
}
if (data->lastDecodedFrame) {
av_free(data->lastDecodedFrame);
data->lastDecodedFrame = NULL;
}
if (data->codecCtx) {
avcodec_close(data->codecCtx);
avcodec_free_context(&(data->codecCtx));
data->codecCtx = NULL;
}
if (data->formatCtx) {
avformat_close_input(&(data->formatCtx));
data->formatCtx = NULL;
}
if (data->ioCtx) {
// buffer passed in is occasionally freed and replaced.
// the replacement must be freed as well.
data->buffer = data->ioCtx->buffer;
av_free(data->ioCtx);
data->ioCtx = NULL;
}
if (data->buffer) {
av_free(data->buffer);
data->buffer = NULL;
}
if (data->sampleBuffer) {
av_free(data->sampleBuffer);
data->sampleBuffer = NULL;
}
if (data->header_insert_block) {
av_free(data->header_insert_block);
data->header_insert_block = NULL;
}
if (data->streamfile) {
close_streamfile(data->streamfile);
data->streamfile = NULL;
}
free(data);
}
#endif

View File

@ -115,6 +115,17 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
#ifdef VGM_USE_FFMPEG
ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_offset, uint64_t stream_offset, uint64_t stream_size, int fmt_big_endian);
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
void free_ffmpeg(ffmpeg_codec_data *);
VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
VGMSTREAM * init_vgmstream_ffmpeg(STREAMFILE *streamFile);
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE * streamFile);

View File

@ -172,6 +172,14 @@ int read_fmt(int big_endian,
fmt->coding_type = coding_NGC_DSP;
fmt->interleave = 8;
break;
#ifdef VGM_USE_FFMPEG
case 0x270:
case 0xFFFE:
fmt->coding_type = coding_FFmpeg;
fmt->block_size = 2048;
fmt->interleave = 0;
break;
#endif
#ifdef VGM_USE_MAIATRAC3PLUS
case 0xFFFE: /* WAVEFORMATEXTENSIBLE */
if (read_32bit(current_chunk+0x20,streamFile) == 0xE923AABF &&
@ -203,6 +211,10 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
struct riff_fmt_chunk fmt;
#ifdef VGM_USE_FFMPEG
ffmpeg_codec_data *ffmpeg_data = NULL;
#endif
off_t file_size = -1;
int sample_count = 0;
int fact_sample_count = -1;
@ -231,7 +243,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("wav",filename_extension(filename)) &&
strcasecmp("lwav",filename_extension(filename))
#ifdef VGM_USE_MAIATRAC3PLUS
#if defined(VGM_USE_MAIATRAC3PLUS) || defined(VGM_USE_FFMPEG)
&& strcasecmp("at3",filename_extension(filename))
#endif
)
@ -367,6 +379,16 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
break;
case coding_NGC_DSP:
break;
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
{
ffmpeg_data = init_ffmpeg_offset(streamFile, 0, streamFile->get_size(streamFile) );
if ( !ffmpeg_data ) goto fail;
sample_count = ffmpeg_data->totalFrames;
}
break;
#endif
#ifdef VGM_USE_MAIATRAC3PLUS
case coding_AT3plus:
sample_count = (data_size / fmt.block_size) * 2048 * fmt.channel_count;
@ -400,6 +422,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
case coding_PCM8_U_int:
case coding_MS_IMA:
case coding_MSADPCM:
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
#endif
#ifdef VGM_USE_MAIATRAC3PLUS
case coding_AT3plus:
#endif
@ -415,6 +440,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
switch (fmt.coding_type) {
case coding_MSADPCM:
case coding_MS_IMA:
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
#endif
#ifdef VGM_USE_MAIATRAC3PLUS
case coding_AT3plus:
#endif
@ -426,6 +454,12 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
break;
}
#ifdef VGM_USE_FFMPEG
if (fmt.coding_type == coding_FFmpeg) {
vgmstream->codec_data = ffmpeg_data;
}
#endif
#ifdef VGM_USE_MAIATRAC3PLUS
if (fmt.coding_type == coding_AT3plus) {
maiatrac3plus_codec_data *data = malloc(sizeof(maiatrac3plus_codec_data));
@ -527,6 +561,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
/* clean up anything we may have opened */
fail:
#ifdef VGM_USE_FFMPEG
if (ffmpeg_data) free_ffmpeg(ffmpeg_data);
#endif
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -280,6 +280,32 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
}
break;
#ifdef VGM_USE_FFMPEG
case 0xB:
/* XMA1/XMA2 */
{
uint16_t codec_id = read_16bit(post_meta_offset, streamFile);
if (codec_id == 0x165 || codec_id == 0x166)
{
ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_faux_riff(streamFile, post_meta_offset, start_offset, streamFile->get_size(streamFile) - start_offset, read_32bit == read_32bitBE);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = ffmpeg_data->totalFrames;
if (loop_flag) {
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
}
}
else goto fail;
}
break;
#endif
default:
goto fail;
}
@ -287,7 +313,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_SQEX_SCD;
/* open the file for reading */
if (vgmstream->layout_type != layout_scd_int)
if (vgmstream->layout_type != layout_scd_int && vgmstream->coding_type != coding_FFmpeg)
{
int i;
STREAMFILE * file;

View File

@ -339,6 +339,9 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_3ds_idsp,
init_vgmstream_g1l,
init_vgmstream_hca,
#ifdef VGM_USE_FFMPEG
init_vgmstream_ffmpeg,
#endif
};
#define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0]))
@ -362,6 +365,13 @@ VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) {
close_vgmstream(vgmstream);
continue;
}
/* Sanify loops! */
if (vgmstream->loop_flag) {
if ((vgmstream->loop_end_sample <= vgmstream->loop_start_sample) ||
(vgmstream->loop_end_sample > vgmstream->num_samples))
vgmstream->loop_flag = 0;
}
/* dual file stereo */
if (do_dfs && (
@ -494,6 +504,24 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
data->samples_discard = 0;
}
#endif
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type==coding_FFmpeg) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
if (data->formatCtx) {
avformat_seek_file(data->formatCtx, -1, 0, 0, 0, AVSEEK_FLAG_ANY);
}
if (data->codecCtx) {
avcodec_flush_buffers(data->codecCtx);
}
data->readNextPacket = 1;
data->bytesConsumedFromDecodedFrame = INT_MAX;
data->framesRead = 0;
data->endOfStream = 0;
data->endOfAudio = 0;
}
#endif
if (vgmstream->coding_type==coding_ACM) {
mus_acm_codec_data *data = vgmstream->codec_data;
@ -642,6 +670,16 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
vgmstream->codec_data = NULL;
}
}
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type==coding_FFmpeg) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
if (vgmstream->codec_data) {
free_ffmpeg(data);
vgmstream->codec_data = NULL;
}
}
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
@ -1032,6 +1070,18 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
#ifdef VGM_USE_G719
case coding_G719:
return 48000/50;
#endif
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
{
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
if (vgmstream->codec_data) {
int64_t samplesRemain = data->totalFrames - data->framesRead;
return samplesRemain > 2048 ? 2048 : samplesRemain;
}
return 0;
}
break;
#endif
case coding_LSF:
return 54;
@ -1147,6 +1197,9 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
#endif
#ifdef VGM_USE_MAIATRAC3PLUS
case coding_AT3plus:
#endif
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
#endif
case coding_MSADPCM:
case coding_MTAF:
@ -1425,6 +1478,14 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
buffer+samples_written*vgmstream->channels,samples_to_do,
vgmstream->channels);
break;
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
decode_ffmpeg(vgmstream,
buffer+samples_written*vgmstream->channels,
samples_to_do,
vgmstream->channels);
break;
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
case coding_MP4_AAC:
decode_mp4_aac(vgmstream->codec_data,
@ -1717,6 +1778,20 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
data->sample_ptr = clHCA_samplesPerBlock;
data->samples_discard = 0;
}
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type==coding_FFmpeg) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *)(vgmstream->codec_data);
int64_t ts;
data->framesRead = vgmstream->loop_start_sample;
ts = data->framesRead * (data->formatCtx->duration) / data->totalFrames;
avformat_seek_file(data->formatCtx, -1, ts - 1000, ts, ts, AVSEEK_FLAG_ANY);
avcodec_flush_buffers(data->codecCtx);
data->readNextPacket = 1;
data->bytesConsumedFromDecodedFrame = INT_MAX;
data->endOfStream = 0;
data->endOfAudio = 0;
}
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
mp4_aac_codec_data *data = (mp4_aac_codec_data *)(vgmstream->codec_data);
@ -2015,6 +2090,25 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
snprintf(temp,TEMPSIZE,"ATRAC3plus");
break;
#endif
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
{
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
if (vgmstream->codec_data) {
if (data->codec) {
snprintf(temp,TEMPSIZE,data->codec->long_name);
}
else {
snprintf(temp,TEMPSIZE,"FFmpeg");
}
}
else {
snprintf(temp,TEMPSIZE,"FFmpeg");
}
}
break;
#endif
case coding_ACM:
snprintf(temp,TEMPSIZE,"InterPlay ACM");
break;
@ -3186,6 +3280,14 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
case meta_XB3D_ADX:
snprintf(temp, TEMPSIZE,"Xenoblade 3D ADX Header");
break;
case meta_HCA:
snprintf(temp, TEMPSIZE,"CRI MiddleWare HCA Header");
break;
#ifdef VGM_USE_FFMPEG
case meta_FFmpeg:
snprintf(temp, TEMPSIZE,"FFmpeg supported file format");
break;
#endif
default:
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");
}
@ -3379,6 +3481,18 @@ static int get_vgmstream_channel_count(VGMSTREAM * vgmstream)
return 0;
}
}
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type==coding_FFmpeg) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
if (data) {
return 1;
}
else {
return 0;
}
}
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
mp4_aac_codec_data *data = (mp4_aac_codec_data *) vgmstream->codec_data;
@ -3411,6 +3525,13 @@ static STREAMFILE * get_vgmstream_streamfile(VGMSTREAM * vgmstream, int channel)
return data->streamfile;
}
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type==coding_FFmpeg) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
return data->streamfile;
}
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
mp4_aac_codec_data *data = (mp4_aac_codec_data *) vgmstream->codec_data;

View File

@ -48,6 +48,11 @@ enum { PATH_LIMIT = 32768 };
#include "clHCA.h"
#ifdef VGM_USE_FFMPEG
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#endif
#include "coding/acm_decoder.h"
#include "coding/nwa_decoder.h"
@ -153,6 +158,10 @@ typedef enum {
coding_MTAF, /* Konami IMA-derived MTAF ADPCM */
coding_CRI_HCA, /* CRI High Compression Audio */
#ifdef VGM_USE_FFMPEG
coding_FFmpeg,
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
coding_MP4_AAC,
@ -586,6 +595,9 @@ typedef enum {
meta_MCA, // Capcom MCA "MADP"
meta_XB3D_ADX, // Xenoblade Chronicles 3D ADX
meta_HCA,
#ifdef VGM_USE_FFMPEG
meta_FFmpeg,
#endif
#ifdef VGM_USE_MP4V2
meta_MP4,
#endif
@ -830,6 +842,51 @@ typedef struct {
// clHCA exists here
} hca_codec_data;
#ifdef VGM_USE_FFMPEG
typedef struct {
STREAMFILE *streamfile;
// offset and total size of raw stream data
uint64_t start;
uint64_t size;
// offset into stream, includes header_size if header exists
uint64_t offset;
// inserted header, ie. fake RIFF header
uint8_t *header_insert_block;
uint64_t header_size;
// stream info
int channels;
int bitsPerSample;
int floatingPoint;
int sampleRate;
int64_t totalFrames;
int64_t framesRead;
int bitrate;
// Intermediate buffer
uint8_t *sampleBuffer;
// FFmpeg context used for metadata
AVCodec *codec;
// FFmpeg decoder state
unsigned char *buffer;
AVIOContext *ioCtx;
int streamIndex;
AVFormatContext *formatCtx;
AVCodecContext *codecCtx;
AVFrame *lastDecodedFrame;
AVPacket *lastReadPacket;
int bytesConsumedFromDecodedFrame;
int readNextPacket;
int endOfStream;
int endOfAudio;
} ffmpeg_codec_data;
#endif
#ifdef VGM_USE_MP4V2
typedef struct {
STREAMFILE *streamfile;