init_ffmpeg accepts any generic header; init_seek edge cases (for XMA)

This commit is contained in:
bnnm 2016-12-18 13:10:08 +01:00
parent ce040bbb36
commit c1c1cd1ba6
2 changed files with 77 additions and 41 deletions

View File

@ -186,18 +186,73 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence)
/** /**
* Manually init FFmpeg only, from an offset. * Manually init FFmpeg, from an offset.
* Can be used if the stream has an extra header over data recognized by FFmpeg. * Can be used if the stream has an extra header over data recognized by FFmpeg.
*/ */
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
return init_ffmpeg_faux_riff(streamFile, -1, start, size, 0); return init_ffmpeg_header_offset(streamFile, NULL, 0, start, size);
}
/**
* Manually init FFmpeg, from an offset and creating a fake RIFF from a streamfile.
*/
ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_offset, uint64_t start, uint64_t size, int big_endian) {
if (fmt_offset > 0) {
size_t header_size = 0;
int max_header_size = (int)(start - fmt_offset);
uint8_t p[100];
if (max_header_size < 18 || max_header_size > 100)
goto fail;
//p = av_malloc(max_header_size + 8 + 4 + 8 + 8);
//if (!p) goto fail;
if (read_streamfile(p + 8 + 4 + 8, fmt_offset, max_header_size, streamFile) != max_header_size)
goto fail;
if (big_endian) {
int shift = 8 + 4 + 8;
put_16bitLE(p+shift, get_16bitBE(p));
put_16bitLE(p+shift + 2, get_16bitBE(p + 2));
put_32bitLE(p+shift + 4, get_32bitBE(p + 4));
put_32bitLE(p+shift + 8, get_32bitBE(p + 8));
put_16bitLE(p+shift + 12, get_16bitBE(p + 12));
put_16bitLE(p+shift + 14, get_16bitBE(p + 14));
put_16bitLE(p+shift + 16, get_16bitBE(p + 16));
}
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, 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 + header_size - 8, "data", 4);
put_32bitLE(p + header_size - 4, size);
return init_ffmpeg_header_offset(streamFile, p, header_size, start, size);
}
else {
return init_ffmpeg_header_offset(streamFile, NULL, 0, start, size);
}
fail:
return NULL;
} }
/** /**
* Manually init FFmpeg only, from an offset / fake RIFF. * Manually init FFmpeg, from a fake header / offset.
* Can insert a fake RIFF header, to trick FFmpeg into demuxing/decoding the stream. *
* Can take a fake header, to trick FFmpeg into demuxing/decoding the stream.
* This header will be seamlessly inserted before 'start' offset, and total filesize will be 'header_size' + 'size'.
* The header buffer will be copied and memory-managed internally.
*/ */
ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_offset, uint64_t start, uint64_t size, int big_endian) { ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size) {
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
ffmpeg_codec_data * data; ffmpeg_codec_data * data;
@ -226,41 +281,12 @@ ffmpeg_codec_data * init_ffmpeg_faux_riff(STREAMFILE *streamFile, int64_t fmt_of
data->size = size; data->size = size;
/* insert fake RIFF header to trick FFmpeg into demuxing/decoding the stream */ /* insert fake header to trick FFmpeg into demuxing/decoding the stream */
if (fmt_offset > 0) { if (header_size > 0) {
int max_header_size = (int)(start - fmt_offset); data->header_size = header_size;
uint8_t *p; data->header_insert_block = av_memdup(header, header_size);
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 (!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);
}
/* setup IO, attempt to autodetect format and gather some info */ /* setup IO, attempt to autodetect format and gather some info */
data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE); data->buffer = av_malloc(FFMPEG_DEFAULT_IO_BUFFER_SIZE);
@ -411,8 +437,8 @@ fail:
static int init_seek(ffmpeg_codec_data * data) { static int init_seek(ffmpeg_codec_data * data) {
int ret, ts_index, found_first = 0; int ret, ts_index, found_first = 0;
int64_t ts = 0; int64_t ts = 0;
int64_t pos; /* offset */ int64_t pos = 0; /* offset */
int size; /* coded size */ int size = 0; /* coded size */
int distance = 0; /* always? */ int distance = 0; /* always? */
AVStream * stream; AVStream * stream;
@ -436,7 +462,7 @@ static int init_seek(ffmpeg_codec_data * data) {
av_packet_unref(pkt); av_packet_unref(pkt);
ret = av_read_frame(data->formatCtx, pkt); ret = av_read_frame(data->formatCtx, pkt);
if (ret < 0) if (ret < 0)
goto fail; break;
if (pkt->stream_index != data->streamIndex) if (pkt->stream_index != data->streamIndex)
continue; /* ignore non-selected streams */ continue; /* ignore non-selected streams */
@ -450,6 +476,15 @@ static int init_seek(ffmpeg_codec_data * data) {
break; break;
} }
} }
if (!found_first)
goto fail;
/* in rare cases there is only one packet */
/* if (size == 0) { size = data_end - pos; } */ /* no easy way to know, ignore (most formats don's need size) */
/* some formats (XMA1) don't seem to have packet.dts, pretend it's 0 */
if (ts == INT64_MIN)
ts = 0;
/* apparently some (non-audio?) streams start with a DTS before 0, but some read_seeks expect 0, which would disrupt the index /* apparently some (non-audio?) streams start with a DTS before 0, but some read_seeks expect 0, which would disrupt the index
* we may need to keep start_ts around, since avstream/codec/format isn't always set */ * we may need to keep start_ts around, since avstream/codec/format isn't always set */

View File

@ -119,6 +119,7 @@ VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, ui
#ifdef VGM_USE_FFMPEG #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_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); ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size);
void free_ffmpeg(ffmpeg_codec_data *); void free_ffmpeg(ffmpeg_codec_data *);