[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