diff mbox

[FFmpeg-devel] avformat/westwood_aud: Adds PCM format demux.

Message ID AM4PR1001MB13464BFC515E1574FE35FC7CC5740@AM4PR1001MB1346.EURPRD10.PROD.OUTLOOK.COM
State Superseded
Headers show

Commit Message

Aidan Richmond Feb. 27, 2019, 11:12 p.m. UTC
PCM format AUD files are found in Westwood's Blade Runner game.
---
 libavformat/westwood_aud.c | 80 ++++++++++++++++++++++++++++++++++++----------
 1 file changed, 63 insertions(+), 17 deletions(-)

Comments

Tomas Härdin Feb. 28, 2019, 9:06 p.m. UTC | #1
ons 2019-02-27 klockan 23:12 +0000 skrev Aidan R:
> PCM format AUD files are found in Westwood's Blade Runner game.

Do we have samples for this?

> @@ -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) {

what about u8?

/Tomas
Michael Niedermayer March 1, 2019, 12:02 a.m. UTC | #2
On Wed, Feb 27, 2019 at 11:12:08PM +0000, Aidan R wrote:
> 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;
>  }

this seems to break probetest
tools/probetest 256 1024
testing size=1
testing size=2
testing size=4
testing size=8
testing size=16
testing size=32
Failure of wsaud probing code with score=50 type=0 p=EBC size=32
testing size=64
testing size=128
testing size=256
testing size=512

[...]
diff mbox

Patch

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,