[FFmpeg-devel] [PATCH] avformat/westwood_aud: Adds PCM format demux.
Aidan R
aidan.is at hotmail.co.uk
Thu Feb 28 01:12:08 EET 2019
PCM format AUD files are found in Westwood's Blade Runner game.
---
libavformat/westwood_aud.c | 80 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 63 insertions(+), 17 deletions(-)
diff --git a/libavformat/westwood_aud.c b/libavformat/westwood_aud.c
index 9c2d35cb8a..5d7e827bc1 100644
--- a/libavformat/westwood_aud.c
+++ b/libavformat/westwood_aud.c
@@ -41,6 +41,12 @@
#define AUD_HEADER_SIZE 12
#define AUD_CHUNK_PREAMBLE_SIZE 8
#define AUD_CHUNK_SIGNATURE 0x0000DEAF
+#define AUD_PCM_CHUNK_SIZE 2048 /* arbitrary size to pull out of PCM data*/
+
+typedef struct AUDDemuxContext {
+ int size;
+ int pos;
+} AUDDemuxContext;
static int wsaud_probe(AVProbeData *p)
{
@@ -50,10 +56,10 @@ static int wsaud_probe(AVProbeData *p)
* so perform sanity checks on various header parameters:
* 8000 <= sample rate (16 bits) <= 48000 ==> 40001 acceptable numbers
* flags <= 0x03 (2 LSBs are used) ==> 4 acceptable numbers
- * compression type (8 bits) = 1 or 99 ==> 2 acceptable numbers
+ * compression type (8 bits) = 0, 1 or 99 ==> 3 acceptable numbers
* first audio chunk signature (32 bits) ==> 1 acceptable number
- * The number space contains 2^64 numbers. There are 40001 * 4 * 2 * 1 =
- * 320008 acceptable number combinations.
+ * The number space contains 2^64 numbers. There are 40001 * 4 * 3 * 1 =
+ * 480012 acceptable number combinations.
*/
if (p->buf_size < AUD_HEADER_SIZE + AUD_CHUNK_PREAMBLE_SIZE)
@@ -69,13 +75,22 @@ static int wsaud_probe(AVProbeData *p)
if (p->buf[10] & 0xFC)
return 0;
- if (p->buf[11] != 99 && p->buf[11] != 1)
+ /* valid format values are 99 == adpcm, 1 == snd1 and 0 == pcm */
+ if (p->buf[11] != 99 && p->buf[11] != 1 && p->buf[11] != 0)
return 0;
- /* read ahead to the first audio chunk and validate the first header signature */
- if (AV_RL32(&p->buf[16]) != AUD_CHUNK_SIGNATURE)
+ /* read ahead to the first audio chunk and validate the first header
+ * signature pcm format does not use a chunk format, so don't check
+ * for that codec */
+ if (p->buf[11] != 0 && AV_RL32(&p->buf[16]) != AUD_CHUNK_SIGNATURE)
return 0;
+ /* if we have pcm format then compressed size should equal
+ * uncompressed size */
+ if (p->buf[11] == 0 && AV_RL32(&p->buf[2]) != AV_RL32(&p->buf[6])) {
+ return 0;
+ }
+
/* return 1/2 certainty since this file check is a little sketchy */
return AVPROBE_SCORE_EXTENSION;
}
@@ -83,16 +98,20 @@ static int wsaud_probe(AVProbeData *p)
static int wsaud_read_header(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
+ AUDDemuxContext *aud = s->priv_data;
AVStream *st;
unsigned char header[AUD_HEADER_SIZE];
- int sample_rate, channels, codec;
+ int sample_rate, channels, codec, bits_per_sample;
if (avio_read(pb, header, AUD_HEADER_SIZE) != AUD_HEADER_SIZE)
return AVERROR(EIO);
sample_rate = AV_RL16(&header[0]);
channels = (header[10] & 0x1) + 1;
+ bits_per_sample = (header[10] & 0x2) ? 16 : 8;
codec = header[11];
+ aud->size = AV_RL32(&header[2]);
+ aud->pos = 0; /* used to track position in a PCM stream */
/* initialize the audio decoder stream */
st = avformat_new_stream(s, NULL);
@@ -100,6 +119,15 @@ static int wsaud_read_header(AVFormatContext *s)
return AVERROR(ENOMEM);
switch (codec) {
+ case 0:
+ if (bits_per_sample == 8) {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
+ } else {
+ st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
+ }
+ st->codecpar->bits_per_coded_sample = bits_per_sample;
+ st->codecpar->bit_rate = channels * sample_rate * bits_per_sample;
+ break;
case 1:
if (channels != 1) {
avpriv_request_sample(s, "Stereo WS-SND1");
@@ -130,20 +158,24 @@ static int wsaud_read_packet(AVFormatContext *s,
AVPacket *pkt)
{
AVIOContext *pb = s->pb;
+ AUDDemuxContext *aud = s->priv_data;
unsigned char preamble[AUD_CHUNK_PREAMBLE_SIZE];
- unsigned int chunk_size;
+ unsigned int chunk_size, bytes_per_sample;
int ret = 0;
AVStream *st = s->streams[0];
- if (avio_read(pb, preamble, AUD_CHUNK_PREAMBLE_SIZE) !=
- AUD_CHUNK_PREAMBLE_SIZE)
- return AVERROR(EIO);
+ /* AUD files don't store PCM audio in chunks */
+ if (st->codecpar->codec_id != AV_CODEC_ID_PCM_S16LE) {
+ if (avio_read(pb, preamble, AUD_CHUNK_PREAMBLE_SIZE) !=
+ AUD_CHUNK_PREAMBLE_SIZE)
+ return AVERROR(EIO);
- /* validate the chunk */
- if (AV_RL32(&preamble[4]) != AUD_CHUNK_SIGNATURE)
- return AVERROR_INVALIDDATA;
+ /* validate the chunk */
+ if (AV_RL32(&preamble[4]) != AUD_CHUNK_SIGNATURE)
+ return AVERROR_INVALIDDATA;
- chunk_size = AV_RL16(&preamble[0]);
+ chunk_size = AV_RL16(&preamble[0]);
+ }
if (st->codecpar->codec_id == AV_CODEC_ID_WESTWOOD_SND1) {
/* For Westwood SND1 audio we need to add the output size and input
@@ -159,7 +191,7 @@ static int wsaud_read_packet(AVFormatContext *s,
AV_WL16(&pkt->data[2], chunk_size);
pkt->duration = out_size;
- } else {
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_ADPCM_IMA_WS) {
ret = av_get_packet(pb, pkt, chunk_size);
if (ret != chunk_size)
return AVERROR(EIO);
@@ -172,7 +204,20 @@ static int wsaud_read_packet(AVFormatContext *s,
/* 2 samples/byte, 1 or 2 samples per frame depending on stereo */
pkt->duration = (chunk_size * 2) / st->codecpar->channels;
- }
+ } else {
+ chunk_size = FFMIN(aud->size - aud->pos, AUD_PCM_CHUNK_SIZE);
+ if(chunk_size <= 0)
+ return AVERROR_EOF;
+
+ aud->pos += chunk_size;
+ ret = av_get_packet(pb, pkt, chunk_size);
+ if (ret != chunk_size)
+ return AVERROR(EIO);
+
+ bytes_per_sample = st->codecpar->bits_per_coded_sample / 8;
+ pkt->duration = (chunk_size / bytes_per_sample) /
+ st->codecpar->channels;
+ }
pkt->stream_index = st->index;
return ret;
@@ -181,6 +226,7 @@ static int wsaud_read_packet(AVFormatContext *s,
AVInputFormat ff_wsaud_demuxer = {
.name = "wsaud",
.long_name = NULL_IF_CONFIG_SMALL("Westwood Studios audio"),
+ .priv_data_size = sizeof(AUDDemuxContext),
.read_probe = wsaud_probe,
.read_header = wsaud_read_header,
.read_packet = wsaud_read_packet,
--
2.16.1.windows.1
More information about the ffmpeg-devel
mailing list