mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-14 23:52:53 +01:00
126 lines
4.8 KiB
C
126 lines
4.8 KiB
C
|
#include "meta.h"
|
||
|
#include "../coding/coding.h"
|
||
|
#include "../util.h"
|
||
|
|
||
|
#ifdef VGM_USE_FFMPEG
|
||
|
|
||
|
static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample);
|
||
|
|
||
|
/* BIK 1/2 - RAD Game Tools movies (audio/video format) */
|
||
|
VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
|
||
|
VGMSTREAM * vgmstream = NULL;
|
||
|
ffmpeg_codec_data *data = NULL;
|
||
|
|
||
|
/* check extension, case insensitive (bika = manually demuxed audio) */
|
||
|
if (!check_extensions(streamFile,"bik,bika,bik2,bik2a,bk2,bk2a")) goto fail;
|
||
|
|
||
|
/* check header "BIK" (bik1) or "KB2" (bik2) (followed by version-char) */
|
||
|
if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x42494B00 &&
|
||
|
(read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail;
|
||
|
|
||
|
/* FFmpeg can parse BIK audio, but can't get the number of samples, which vgmstream needs.
|
||
|
* The only way to get them is to read all frame headers */
|
||
|
data = init_ffmpeg_offset(streamFile, 0x0, get_streamfile_size(streamFile));
|
||
|
if (!data) goto fail;
|
||
|
|
||
|
vgmstream = allocate_vgmstream(data->channels, 0); /* alloc FFmpeg first to get channel count */
|
||
|
if (!vgmstream) goto fail;
|
||
|
vgmstream->codec_data = data;
|
||
|
vgmstream->coding_type = coding_FFmpeg;
|
||
|
vgmstream->layout_type = layout_none;
|
||
|
vgmstream->meta_type = meta_FFmpeg;
|
||
|
vgmstream->sample_rate = data->sampleRate;
|
||
|
|
||
|
/* manually get num_samples since data->totalSamples is always 0 */
|
||
|
vgmstream->num_samples = bik_get_num_samples(streamFile, data->bitsPerSample);
|
||
|
if (vgmstream->num_samples == 0)
|
||
|
goto fail;
|
||
|
|
||
|
return vgmstream;
|
||
|
|
||
|
fail:
|
||
|
free_ffmpeg(data);
|
||
|
if (vgmstream) {
|
||
|
vgmstream->codec_data = NULL;
|
||
|
close_vgmstream(vgmstream);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the number of samples in a BIK file by reading all frames' headers,
|
||
|
* as they are not in the main header. The header for BIK1 and 2 is the same.
|
||
|
* (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough)
|
||
|
*
|
||
|
* Needs bits per sample to calculate PCM samples, since most bink audio seems to use 32, actually.
|
||
|
*/
|
||
|
static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample) {
|
||
|
uint32_t *offsets = NULL;
|
||
|
uint32_t num_samples_b = 0;
|
||
|
off_t cur_offset;
|
||
|
size_t filesize;
|
||
|
int i, j, num_frames, num_tracks;
|
||
|
int target_stream = 0;
|
||
|
|
||
|
filesize = get_streamfile_size(streamFile);
|
||
|
|
||
|
num_frames = read_32bitLE(0x08,streamFile);
|
||
|
if (num_frames<=0) goto fail;
|
||
|
|
||
|
/* multichannel audio is usually N tracks of stereo/mono, no way to know channel layout */
|
||
|
num_tracks = read_32bitLE(0x28,streamFile);
|
||
|
if (num_tracks<=0 || num_tracks > 255) goto fail;
|
||
|
|
||
|
/* find the frame index table, which is after 3 audio headers of size 4 for each track */
|
||
|
cur_offset = 0x2c + num_tracks*4 * 3;
|
||
|
|
||
|
/* read offsets in a buffer, to avoid fseeking to the table back and forth
|
||
|
* the number of frames can be highly variable so we'll alloc */
|
||
|
offsets = malloc(sizeof(uint32_t) * num_frames);
|
||
|
for (i=0; i < num_frames; i++) {
|
||
|
offsets[i] = read_32bitLE(cur_offset,streamFile) & 0xFFFFFFFE; /* mask first bit (= keyframe) */
|
||
|
cur_offset += 0x4;
|
||
|
|
||
|
if (offsets[i] > filesize) goto fail;
|
||
|
}
|
||
|
/* after the last index is the file size, validate just in case */
|
||
|
if (read_32bitLE(cur_offset,streamFile)!=filesize) goto fail;
|
||
|
|
||
|
/* multistream support just for fun (FFmpeg should select the same target stream)
|
||
|
* (num_samples for other streams seem erratic though) */
|
||
|
if (target_stream > num_tracks) goto fail;
|
||
|
if (target_stream == 0) target_stream = 1;
|
||
|
VGM_ASSERT(num_tracks > 1, "BIK: multiple streams found (%i entries)\n", num_tracks);
|
||
|
|
||
|
/* read each frame header and sum all samples
|
||
|
* a frame has N audio packets with header (one per track) + video packet */
|
||
|
for (i=0; i < num_frames; i++) {
|
||
|
cur_offset = offsets[i];
|
||
|
|
||
|
/* read audio packet headers */
|
||
|
for (j=0; j < num_tracks; j++) {
|
||
|
uint32_t ap_size, samples_b;
|
||
|
ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */
|
||
|
samples_b = read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */
|
||
|
if (ap_size==0) break; /* no audio in this frame */
|
||
|
|
||
|
if (j == target_stream-1) { /* target samples found, read next frame */
|
||
|
num_samples_b += samples_b;
|
||
|
break;
|
||
|
} else { /* check next audio packet */
|
||
|
cur_offset += 4 + ap_size; /* todo sometimes ap_size doesn't include itself (+4), others it does? */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
free(offsets);
|
||
|
return num_samples_b / (bits_per_sample / 8);
|
||
|
|
||
|
fail:
|
||
|
free(offsets);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif
|