changeset 266:8bb470d85249 libavformat

New demuxers: Sega FILM/CPK, Westwood VQA & AUD; new decoders: MS RLE & Video-1, Apple RPZA, Cinepak, Westwood IMA ADPCM
author tmmm
date Wed, 01 Oct 2003 04:39:38 +0000
parents 786e8286ea4a
children a96788f40561
files Makefile allformats.c avformat.h avienc.c mov.c segafilm.c westwood.c
diffstat 7 files changed, 718 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Sep 30 17:54:30 2003 +0000
+++ b/Makefile	Wed Oct 01 04:39:38 2003 +0000
@@ -15,7 +15,7 @@
 OBJS+=mpeg.o mpegts.o mpegtsenc.o ffm.o crc.o img.o raw.o rm.o \
       avienc.o avidec.o wav.o swf.o au.o gif.o mov.o mpjpeg.o dv.o \
       yuv4mpeg.o 4xm.o flvenc.o flvdec.o movenc.o psxstr.o idroq.o ipmovie.o \
-      nut.o wc3movie.o mp3.o
+      nut.o wc3movie.o mp3.o westwood.o segafilm.o
 
 ifeq ($(CONFIG_RISKY),yes)
 OBJS+= asf.o
--- a/allformats.c	Tue Sep 30 17:54:30 2003 +0000
+++ b/allformats.c	Wed Oct 01 04:39:38 2003 +0000
@@ -56,6 +56,8 @@
     roq_init();
     ipmovie_init();
     wc3_init();
+    westwood_init();
+    film_init();
 
 #if defined(AMR_NB) || defined(AMR_NB_FIXED) || defined(AMR_WB)
     amr_init();
--- a/avformat.h	Tue Sep 30 17:54:30 2003 +0000
+++ b/avformat.h	Wed Oct 01 04:39:38 2003 +0000
@@ -406,6 +406,12 @@
 /* wc3movie.c */
 int wc3_init(void);
 
+/* westwood.c */
+int westwood_init(void);
+
+/* segafilm.c */
+int film_init(void);
+
 #include "rtp.h"
 
 #include "rtsp.h"
--- a/avienc.c	Tue Sep 30 17:54:30 2003 +0000
+++ b/avienc.c	Wed Oct 01 04:39:38 2003 +0000
@@ -141,6 +141,15 @@
     { CODEC_ID_VCR1, MKTAG('V', 'C', 'R', '1') },
     { CODEC_ID_FFV1, MKTAG('F', 'F', 'V', '1') },
     { CODEC_ID_XAN_WC4, MKTAG('X', 'x', 'a', 'n') },
+    { CODEC_ID_MSRLE, MKTAG('m', 'r', 'l', 'e') },
+    { CODEC_ID_MSRLE, MKTAG(0x1, 0x0, 0x0, 0x0) },
+    { CODEC_ID_MSVIDEO1, MKTAG('M', 'S', 'V', 'C') },
+    { CODEC_ID_MSVIDEO1, MKTAG('m', 's', 'v', 'c') },
+    { CODEC_ID_MSVIDEO1, MKTAG('C', 'R', 'A', 'M') },
+    { CODEC_ID_MSVIDEO1, MKTAG('c', 'r', 'a', 'm') },
+    { CODEC_ID_MSVIDEO1, MKTAG('W', 'H', 'A', 'M') },
+    { CODEC_ID_MSVIDEO1, MKTAG('w', 'h', 'a', 'm') },
+    { CODEC_ID_CINEPAK, MKTAG('c', 'v', 'i', 'd') },
     { 0, 0 },
 };
 
--- a/mov.c	Tue Sep 30 17:54:30 2003 +0000
+++ b/mov.c	Wed Oct 01 04:39:38 2003 +0000
@@ -98,6 +98,8 @@
     { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 'p') }, /* DV PAL */
 /*    { CODEC_ID_DVVIDEO, MKTAG('A', 'V', 'd', 'v') }, *//* AVID dv */
     { CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') }, /* On2 VP3 */
+    { CODEC_ID_RPZA, MKTAG('r', 'p', 'z', 'a') }, /* Apple Video (RPZA) */
+    { CODEC_ID_CINEPAK, MKTAG('c', 'v', 'i', 'd') }, /* Cinepak */
     { CODEC_ID_NONE, 0 },
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/segafilm.c	Wed Oct 01 04:39:38 2003 +0000
@@ -0,0 +1,323 @@
+/*
+ * Sega FILM Format (CPK) Demuxer
+ * Copyright (c) 2003 The ffmpeg Project
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * @file segafilm.c
+ * Sega FILM (.cpk) file demuxer
+ * by Mike Melanson (melanson@pcisys.net)
+ * For more information regarding the Sega FILM file format, visit:
+ *   http://www.pcisys.net/~melanson/codecs/
+ */
+
+#include "avformat.h"
+
+#define BE_16(x)  ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1])
+#define BE_32(x)  ((((uint8_t*)(x))[0] << 24) | \
+                   (((uint8_t*)(x))[1] << 16) | \
+                   (((uint8_t*)(x))[2] << 8) | \
+                    ((uint8_t*)(x))[3])
+
+#define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
+        ( (long)(unsigned char)(ch3) | \
+        ( (long)(unsigned char)(ch2) << 8 ) | \
+        ( (long)(unsigned char)(ch1) << 16 ) | \
+        ( (long)(unsigned char)(ch0) << 24 ) )
+
+#define FILM_TAG FOURCC_TAG('F', 'I', 'L', 'M')
+#define FDSC_TAG FOURCC_TAG('F', 'D', 'S', 'C')
+#define STAB_TAG FOURCC_TAG('S', 'T', 'A', 'B')
+#define CVID_TAG FOURCC_TAG('c', 'v', 'i', 'd')
+
+typedef struct {
+  int stream;
+  off_t sample_offset;
+  unsigned int sample_size;
+  int64_t pts;
+  int keyframe;
+} film_sample_t;
+
+typedef struct FilmDemuxContext {
+    int video_stream_index;
+    int audio_stream_index;
+
+    unsigned int audio_type;
+    unsigned int audio_samplerate;
+    unsigned int audio_bits;
+    unsigned int audio_channels;
+
+    unsigned int video_type;
+    unsigned int sample_count;
+    film_sample_t *sample_table;
+    unsigned int current_sample;
+
+    unsigned int base_clock;
+    unsigned int version;
+    int cvid_extra_bytes;  /* the number of bytes thrown into the Cinepak
+                            * chunk header to throw off decoders */
+
+    /* buffer used for interleaving stereo PCM data */
+    unsigned char *stereo_buffer;
+    int stereo_buffer_size;
+} FilmDemuxContext;
+
+static int film_probe(AVProbeData *p)
+{
+    if (p->buf_size < 4)
+        return 0;
+
+    if (BE_32(&p->buf[0]) != FILM_TAG)
+        return 0;
+
+    return AVPROBE_SCORE_MAX;
+}
+
+static int film_read_header(AVFormatContext *s,
+                            AVFormatParameters *ap)
+{
+    FilmDemuxContext *film = (FilmDemuxContext *)s->priv_data;
+    ByteIOContext *pb = &s->pb;
+    AVStream *st;
+    unsigned char scratch[256];
+    int i;
+    unsigned int data_offset;
+    unsigned int audio_frame_counter;
+
+    film->sample_table = NULL;
+    film->stereo_buffer = NULL;
+    film->stereo_buffer_size = 0;
+
+    /* load the main FILM header */
+    if (get_buffer(pb, scratch, 16) != 16)
+        return -EIO;
+    data_offset = BE_32(&scratch[4]);
+    film->version = BE_32(&scratch[8]);
+
+    /* load the FDSC chunk */
+    if (film->version == 0) {
+        /* special case for Lemmings .film files; 20-byte header */
+        if (get_buffer(pb, scratch, 20) != 20)
+            return -EIO;
+        /* make some assumptions about the audio parameters */
+        film->audio_type = CODEC_ID_PCM_S8;
+        film->audio_samplerate = 22050;
+        film->audio_channels = 1;
+        film->audio_bits = 8;
+    } else {
+        /* normal Saturn .cpk files; 32-byte header */
+        if (get_buffer(pb, scratch, 32) != 32)
+            return -EIO;
+        film->audio_samplerate = BE_16(&scratch[24]);;
+        film->audio_channels = scratch[21];
+        film->audio_bits = scratch[22];
+        if (film->audio_bits == 8)
+            film->audio_type = CODEC_ID_PCM_S8;
+        else if (film->audio_bits == 16)
+            film->audio_type = CODEC_ID_PCM_S16BE;
+        else
+            film->audio_type = 0;
+    }
+
+    if (BE_32(&scratch[0]) != FDSC_TAG)
+        return AVERROR_INVALIDDATA;
+
+    film->cvid_extra_bytes = 0;
+    if (BE_32(&scratch[8]) == CVID_TAG) {
+        film->video_type = CODEC_ID_CINEPAK;
+        if (film->version)
+            film->cvid_extra_bytes = 2;
+        else
+            film->cvid_extra_bytes = 6;  /* Lemmings 3DO case */
+    } else
+        film->video_type = 0;
+
+    /* initialize the decoder streams */
+    if (film->video_type) {
+        st = av_new_stream(s, 0);
+        if (!st)
+            return AVERROR_NOMEM;
+        film->video_stream_index = st->index;
+        st->codec.codec_type = CODEC_TYPE_VIDEO;
+        st->codec.codec_id = film->video_type;
+        st->codec.codec_tag = 0;  /* no fourcc */
+        st->codec.width = BE_32(&scratch[16]);
+        st->codec.height = BE_32(&scratch[12]);
+    }
+
+    if (film->audio_type) {
+        st = av_new_stream(s, 0);
+        if (!st)
+            return AVERROR_NOMEM;
+        film->audio_stream_index = st->index;
+        st->codec.codec_type = CODEC_TYPE_AUDIO;
+        st->codec.codec_id = film->audio_type;
+        st->codec.codec_tag = 1;
+        st->codec.channels = film->audio_channels;
+        st->codec.bits_per_sample = film->audio_bits;
+        st->codec.sample_rate = film->audio_samplerate;
+        st->codec.bit_rate = st->codec.channels * st->codec.sample_rate *
+            st->codec.bits_per_sample;
+        st->codec.block_align = st->codec.channels * 
+            st->codec.bits_per_sample / 8;
+    }
+
+    /* load the sample table */
+    if (get_buffer(pb, scratch, 16) != 16)
+        return -EIO;
+    if (BE_32(&scratch[0]) != STAB_TAG)
+        return AVERROR_INVALIDDATA;
+    film->base_clock = BE_32(&scratch[8]);
+    film->sample_count = BE_32(&scratch[12]);
+    film->sample_table = av_malloc(film->sample_count * sizeof(film_sample_t));
+    
+    audio_frame_counter = 0;
+    for (i = 0; i < film->sample_count; i++) {
+        /* load the next sample record and transfer it to an internal struct */
+        if (get_buffer(pb, scratch, 16) != 16) {
+            av_free(film->sample_table);
+            return -EIO;
+        }
+        film->sample_table[i].sample_offset = 
+            data_offset + BE_32(&scratch[0]);
+        film->sample_table[i].sample_size = BE_32(&scratch[4]);
+        if (BE_32(&scratch[8]) == 0xFFFFFFFF) {
+            film->sample_table[i].stream = film->audio_stream_index;
+            film->sample_table[i].pts = audio_frame_counter;
+            film->sample_table[i].pts *= film->base_clock;
+            film->sample_table[i].pts /= film->audio_samplerate;
+
+            audio_frame_counter += (film->sample_table[i].sample_size /
+                (film->audio_channels * film->audio_bits / 8));
+        } else {
+            film->sample_table[i].stream = film->video_stream_index;
+            film->sample_table[i].pts = BE_32(&scratch[8]) & 0x7FFFFFFF;
+            film->sample_table[i].keyframe = (scratch[8] & 0x80) ? 0 : 1;
+        }
+    }
+
+    film->current_sample = 0;
+
+    /* set the pts reference to match the tick rate of the file */
+    s->pts_num = 1;
+    s->pts_den = film->base_clock;
+
+    return 0;
+}
+
+static int film_read_packet(AVFormatContext *s,
+                            AVPacket *pkt)
+{
+    FilmDemuxContext *film = (FilmDemuxContext *)s->priv_data;
+    ByteIOContext *pb = &s->pb;
+    film_sample_t *sample;
+    int ret = 0;
+    int i;
+    int left, right;
+
+    if (film->current_sample >= film->sample_count)
+        return -EIO;
+
+    sample = &film->sample_table[film->current_sample];
+
+    /* position the stream (will probably be there anyway) */
+    url_fseek(pb, sample->sample_offset, SEEK_SET);
+
+    /* do a special song and dance when loading FILM Cinepak chunks */
+    if ((sample->stream == film->video_stream_index) && 
+        (film->video_type == CODEC_ID_CINEPAK)) {
+        if (av_new_packet(pkt, sample->sample_size - film->cvid_extra_bytes))
+            return AVERROR_NOMEM;
+        ret = get_buffer(pb, pkt->data, 10);
+        /* skip the non-spec CVID bytes */
+        url_fseek(pb, film->cvid_extra_bytes, SEEK_CUR);
+        ret += get_buffer(pb, pkt->data + 10, 
+            sample->sample_size - 10 - film->cvid_extra_bytes);
+        if (ret != sample->sample_size - film->cvid_extra_bytes)
+            ret = -EIO;
+    } else if ((sample->stream == film->audio_stream_index) &&
+        (film->audio_channels == 2)) {
+        /* stereo PCM needs to be interleaved */
+
+        if (av_new_packet(pkt, sample->sample_size))
+            return AVERROR_NOMEM;
+
+        /* make sure the interleave buffer is large enough */
+        if (sample->sample_size > film->stereo_buffer_size) {
+            av_free(film->stereo_buffer);
+            film->stereo_buffer_size = sample->sample_size;
+            film->stereo_buffer = av_malloc(film->stereo_buffer_size);
+        }
+
+        ret = get_buffer(pb, film->stereo_buffer, sample->sample_size);
+        if (ret != sample->sample_size)
+            ret = -EIO;
+
+        left = 0;
+        right = sample->sample_size / 2;
+        for (i = 0; i < sample->sample_size; ) {
+            if (film->audio_bits == 8) {
+                pkt->data[i++] = film->stereo_buffer[left++];
+                pkt->data[i++] = film->stereo_buffer[right++];
+            } else {
+                pkt->data[i++] = film->stereo_buffer[left++];
+                pkt->data[i++] = film->stereo_buffer[left++];
+                pkt->data[i++] = film->stereo_buffer[right++];
+                pkt->data[i++] = film->stereo_buffer[right++];
+            }
+        }
+    } else {
+        if (av_new_packet(pkt, sample->sample_size))
+            return AVERROR_NOMEM;
+        ret = get_buffer(pb, pkt->data, sample->sample_size);
+        if (ret != sample->sample_size)
+            ret = -EIO;
+    }
+
+    pkt->stream_index = sample->stream;
+    pkt->pts = sample->pts;
+
+    film->current_sample++;
+
+    return ret;
+}
+
+static int film_read_close(AVFormatContext *s)
+{
+    FilmDemuxContext *film = (FilmDemuxContext *)s->priv_data;
+
+    av_free(film->sample_table);
+    av_free(film->stereo_buffer);
+
+    return 0;
+}
+
+static AVInputFormat film_iformat = {
+    "film_cpk",
+    "Sega FILM/CPK format",
+    sizeof(FilmDemuxContext),
+    film_probe,
+    film_read_header,
+    film_read_packet,
+    film_read_close,
+};
+
+int film_init(void)
+{
+    av_register_input_format(&film_iformat);
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/westwood.c	Wed Oct 01 04:39:38 2003 +0000
@@ -0,0 +1,375 @@
+/*
+ * Westwood Studios Multimedia Formats Demuxer (VQA, AUD)
+ * Copyright (c) 2003 The ffmpeg Project
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * @file westwood.c
+ * Westwood Studios VQA & AUD file demuxers
+ * by Mike Melanson (melanson@pcisys.net)
+ * for more information on the Westwood file formats, visit:
+ *   http://www.pcisys.net/~melanson/codecs/
+ *   http://www.geocities.com/SiliconValley/8682/aud3.txt
+ *
+ * Implementation note: There is no definite file signature for AUD files.
+ * The demuxer uses a probabilistic strategy for content detection. This
+ * entails performing sanity checks on certain header values in order to
+ * qualify a file. Refer to wsaud_probe() for the precise parameters.
+ */
+
+#include "avformat.h"
+
+#define LE_16(x)  ((((uint8_t*)(x))[1] << 8) | ((uint8_t*)(x))[0])
+#define LE_32(x)  ((((uint8_t*)(x))[3] << 24) | \
+                   (((uint8_t*)(x))[2] << 16) | \
+                   (((uint8_t*)(x))[1] << 8) | \
+                    ((uint8_t*)(x))[0])
+#define BE_32(x)  ((((uint8_t*)(x))[0] << 24) | \
+                   (((uint8_t*)(x))[1] << 16) | \
+                   (((uint8_t*)(x))[2] << 8) | \
+                    ((uint8_t*)(x))[3])
+
+#define AUD_HEADER_SIZE 12
+#define AUD_CHUNK_PREAMBLE_SIZE 8
+#define AUD_CHUNK_SIGNATURE 0x0000DEAF
+
+#define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
+        ( (long)(unsigned char)(ch3) | \
+        ( (long)(unsigned char)(ch2) << 8 ) | \
+        ( (long)(unsigned char)(ch1) << 16 ) | \
+        ( (long)(unsigned char)(ch0) << 24 ) )
+
+#define FORM_TAG FOURCC_TAG('F', 'O', 'R', 'M')
+#define WVQA_TAG FOURCC_TAG('W', 'V', 'Q', 'A')
+#define VQHD_TAG FOURCC_TAG('V', 'Q', 'H', 'D')
+#define FINF_TAG FOURCC_TAG('F', 'I', 'N', 'F')
+#define SND0_TAG FOURCC_TAG('S', 'N', 'D', '0')
+#define SND2_TAG FOURCC_TAG('S', 'N', 'D', '2')
+#define VQFR_TAG FOURCC_TAG('V', 'Q', 'F', 'R')
+
+#define VQA_HEADER_SIZE 0x2A
+#define VQA_FRAMERATE 15
+#define VQA_VIDEO_PTS_INC (90000 / VQA_FRAMERATE)
+#define VQA_PREAMBLE_SIZE 8
+
+typedef struct WsAudDemuxContext {
+    int audio_samplerate;
+    int audio_channels;
+    int audio_bits;
+    int audio_type;
+    int audio_stream_index;
+    int64_t audio_frame_counter;
+} WsAudDemuxContext;
+
+typedef struct WsVqaDemuxContext {
+    int audio_samplerate;
+    int audio_channels;
+    int audio_bits;
+
+    int audio_stream_index;
+    int video_stream_index;
+
+    int64_t audio_frame_counter;
+    int64_t video_pts;
+} WsVqaDemuxContext;
+
+static int wsaud_probe(AVProbeData *p)
+{
+    int field;
+
+    /* Probabilistic content detection strategy: There is no file signature
+     * so perform sanity checks on various header parameters:
+     *   8000 <= sample rate (16 bits) <= 48000  ==> 40001 acceptable numbers
+     *   compression type (8 bits) = 1 or 99     ==> 2 acceptable numbers
+     * There is a total of 24 bits. The number space contains 2^24 =
+     * 16777216 numbers. There are 40001 * 2 = 80002 acceptable combinations
+     * of numbers. There is a 80002/16777216 = 0.48% chance of a false
+     * positive.
+     */
+
+    if (p->buf_size < AUD_HEADER_SIZE)
+        return 0;
+
+    /* check sample rate */
+    field = LE_16(&p->buf[0]);
+    if ((field < 8000) || (field > 48000))
+        return 0;
+
+    /* note: only check for WS IMA (type 99) right now since there is no 
+     * support for type 1 */
+    if (p->buf[11] != 99)
+        return 0;
+
+    /* return 1/2 certainty since this file check is a little sketchy */
+    return AVPROBE_SCORE_MAX / 2;
+}
+
+static int wsaud_read_header(AVFormatContext *s,
+                             AVFormatParameters *ap)
+{
+    WsAudDemuxContext *wsaud = (WsAudDemuxContext *)s->priv_data;
+    ByteIOContext *pb = &s->pb;
+    AVStream *st;
+    unsigned char header[AUD_HEADER_SIZE];
+
+    if (get_buffer(pb, header, AUD_HEADER_SIZE) != AUD_HEADER_SIZE)
+        return -EIO;
+    wsaud->audio_samplerate = LE_16(&header[0]);
+    if (header[11] == 99)
+        wsaud->audio_type = CODEC_ID_ADPCM_IMA_WS;
+    else
+        return AVERROR_INVALIDDATA;
+
+    /* flag 0 indicates stereo */
+    wsaud->audio_channels = (header[10] & 0x1) + 1;
+    /* flag 1 indicates 16 bit audio */
+    wsaud->audio_bits = (((header[10] & 0x2) >> 1) + 1) * 8;
+
+    /* set the pts reference the same as the sample rate */
+    s->pts_num = 1;
+    s->pts_den = wsaud->audio_samplerate;
+
+    /* initialize the audio decoder stream */
+    st = av_new_stream(s, 0);
+    if (!st)
+        return AVERROR_NOMEM;
+    st->codec.codec_type = CODEC_TYPE_AUDIO;
+    st->codec.codec_id = wsaud->audio_type;
+    st->codec.codec_tag = 0;  /* no tag */
+    st->codec.channels = wsaud->audio_channels;
+    st->codec.sample_rate = wsaud->audio_samplerate;
+    st->codec.bits_per_sample = wsaud->audio_bits;
+    st->codec.bit_rate = st->codec.channels * st->codec.sample_rate *
+        st->codec.bits_per_sample / 4;
+    st->codec.block_align = st->codec.channels * st->codec.bits_per_sample;
+
+    wsaud->audio_stream_index = st->index;
+    wsaud->audio_frame_counter = 0;
+
+    return 0;
+}
+
+static int wsaud_read_packet(AVFormatContext *s,
+                             AVPacket *pkt)
+{
+    WsAudDemuxContext *wsaud = (WsAudDemuxContext *)s->priv_data;
+    ByteIOContext *pb = &s->pb;
+    unsigned char preamble[AUD_CHUNK_PREAMBLE_SIZE];
+    unsigned int chunk_size;
+    int ret = 0;
+
+    if (get_buffer(pb, preamble, AUD_CHUNK_PREAMBLE_SIZE) !=
+        AUD_CHUNK_PREAMBLE_SIZE)
+        return -EIO;
+
+    /* validate the chunk */
+    if (LE_32(&preamble[4]) != AUD_CHUNK_SIGNATURE)
+        return AVERROR_INVALIDDATA;
+
+    chunk_size = LE_16(&preamble[0]);
+    if (av_new_packet(pkt, chunk_size))
+        return -EIO;
+    pkt->stream_index = wsaud->audio_stream_index;
+    pkt->pts = wsaud->audio_frame_counter;
+    pkt->pts /= wsaud->audio_samplerate;
+    if ((ret = get_buffer(pb, pkt->data, chunk_size)) != chunk_size) {
+        av_free_packet(pkt);
+        ret = -EIO;
+    }
+
+    /* 2 samples/byte, 1 or 2 samples per frame depending on stereo */
+    wsaud->audio_frame_counter += (chunk_size * 2) / wsaud->audio_channels;
+
+    return ret;
+}
+
+static int wsaud_read_close(AVFormatContext *s)
+{
+//    WsAudDemuxContext *wsaud = (WsAudDemuxContext *)s->priv_data;
+
+    return 0;
+}
+
+
+static int wsvqa_probe(AVProbeData *p)
+{
+    /* need 12 bytes to qualify */
+    if (p->buf_size < 12)
+        return 0;
+
+    /* check for the VQA signatures */
+    if ((BE_32(&p->buf[0]) != FORM_TAG) ||
+        (BE_32(&p->buf[8]) != WVQA_TAG))
+        return 0;
+
+    return AVPROBE_SCORE_MAX;
+}
+
+static int wsvqa_read_header(AVFormatContext *s,
+                             AVFormatParameters *ap)
+{
+    WsVqaDemuxContext *wsvqa = (WsVqaDemuxContext *)s->priv_data;
+    ByteIOContext *pb = &s->pb;
+    AVStream *st;
+    unsigned char *header;
+    unsigned char scratch[VQA_PREAMBLE_SIZE];
+
+    /* set the pts reference (1 pts = 1/90000) */
+    s->pts_num = 1;
+    s->pts_den = 90000;
+
+    /* initialize the video decoder stream */
+    st = av_new_stream(s, 0);
+    if (!st)
+        return AVERROR_NOMEM;
+    wsvqa->video_stream_index = st->index;
+    st->codec.codec_type = CODEC_TYPE_VIDEO;
+    st->codec.codec_id = CODEC_ID_WS_VQA;
+    st->codec.codec_tag = 0;  /* no fourcc */
+
+    /* skip to the start of the VQA header */
+    url_fseek(pb, 20, SEEK_SET);
+
+    /* the VQA header needs to go to the decoder */
+    st->codec.extradata_size = VQA_HEADER_SIZE;
+    st->codec.extradata = av_malloc(VQA_HEADER_SIZE);
+    header = (unsigned char *)st->codec.extradata;
+    if (get_buffer(pb, st->codec.extradata, VQA_HEADER_SIZE) !=
+        VQA_HEADER_SIZE) {
+        av_free(st->codec.extradata);
+        return -EIO;
+    }
+    st->codec.width = LE_16(&header[6]);
+    st->codec.height = LE_16(&header[8]);
+
+    /* initialize the audio decoder stream */
+    st = av_new_stream(s, 0);
+    if (!st)
+        return AVERROR_NOMEM;
+    st->codec.codec_type = CODEC_TYPE_AUDIO;
+    st->codec.codec_id = CODEC_ID_ADPCM_IMA_WS;
+    st->codec.codec_tag = 0;  /* no tag */
+    st->codec.sample_rate = LE_16(&header[24]);
+    st->codec.channels = header[26];
+    st->codec.bits_per_sample = 16;
+    st->codec.bit_rate = st->codec.channels * st->codec.sample_rate *
+        st->codec.bits_per_sample / 4;
+    st->codec.block_align = st->codec.channels * st->codec.bits_per_sample;
+
+    wsvqa->audio_stream_index = st->index;
+    wsvqa->audio_samplerate = st->codec.sample_rate;
+    wsvqa->audio_channels = st->codec.channels;
+    wsvqa->audio_frame_counter = 0;
+
+    /* skip the useless FINF chunk index */
+    if (get_buffer(pb, scratch, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE) {
+        av_free(st->codec.extradata);
+        return -EIO;
+    }
+    url_fseek(pb, BE_32(&scratch[4]), SEEK_CUR);
+    wsvqa->video_pts = wsvqa->audio_frame_counter = 0;
+
+    return 0;
+}
+
+static int wsvqa_read_packet(AVFormatContext *s,
+                             AVPacket *pkt)
+{
+    WsVqaDemuxContext *wsvqa = (WsVqaDemuxContext *)s->priv_data;
+    ByteIOContext *pb = &s->pb;
+    int ret = 0;
+    unsigned char preamble[VQA_PREAMBLE_SIZE];
+    unsigned int chunk_type;
+    unsigned int chunk_size;
+    int skip_byte;
+
+    if (get_buffer(pb, preamble, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE)
+        return -EIO;
+
+    chunk_type = BE_32(&preamble[0]);
+    chunk_size = BE_32(&preamble[4]);
+    skip_byte = chunk_size & 0x01;
+
+    if ((chunk_type == SND2_TAG) || (chunk_type == VQFR_TAG)) {
+
+        if (av_new_packet(pkt, chunk_size))
+            return -EIO;
+        ret = get_buffer(pb, pkt->data, chunk_size);
+        if (ret != chunk_size) {
+            av_free_packet(pkt);
+            ret = -EIO;
+        }
+
+        if (chunk_type == SND2_TAG) {
+            pkt->stream_index = wsvqa->audio_stream_index;
+
+            pkt->pts = 90000;
+            pkt->pts *= wsvqa->audio_frame_counter;
+            pkt->pts /= wsvqa->audio_samplerate;
+
+            /* 2 samples/byte, 1 or 2 samples per frame depending on stereo */
+            wsvqa->audio_frame_counter += (chunk_size * 2) / 
+                wsvqa->audio_channels;
+        } else {
+            pkt->stream_index = wsvqa->video_stream_index;
+            pkt->pts = wsvqa->video_pts;
+            wsvqa->video_pts += VQA_VIDEO_PTS_INC;
+        }
+
+    } else
+        return AVERROR_INVALIDDATA;
+
+    /* stay on 16-bit alignment */
+    if (skip_byte)
+        url_fseek(pb, 1, SEEK_CUR);
+
+    return ret;
+}
+
+static int wsvqa_read_close(AVFormatContext *s)
+{
+//    WsVqaDemuxContext *wsvqa = (WsVqaDemuxContext *)s->priv_data;
+
+    return 0;
+}
+
+static AVInputFormat wsaud_iformat = {
+    "wsaud",
+    "Westwood Studios audio format",
+    sizeof(WsAudDemuxContext),
+    wsaud_probe,
+    wsaud_read_header,
+    wsaud_read_packet,
+    wsaud_read_close,
+};
+
+static AVInputFormat wsvqa_iformat = {
+    "wsvqa",
+    "Westwood Studios VQA format",
+    sizeof(WsVqaDemuxContext),
+    wsvqa_probe,
+    wsvqa_read_header,
+    wsvqa_read_packet,
+    wsvqa_read_close,
+};
+
+int westwood_init(void)
+{
+    av_register_input_format(&wsaud_iformat);
+    av_register_input_format(&wsvqa_iformat);
+    return 0;
+}