libavformat/westwood_vqa.c
Go to the documentation of this file.
00001 /*
00002  * Westwood Studios VQA Format Demuxer
00003  * Copyright (c) 2003 The ffmpeg Project
00004  *
00005  * This file is part of FFmpeg.
00006  *
00007  * FFmpeg is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * FFmpeg is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with FFmpeg; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00031 #include "libavutil/intreadwrite.h"
00032 #include "avformat.h"
00033 #include "internal.h"
00034 
00035 #define FORM_TAG MKBETAG('F', 'O', 'R', 'M')
00036 #define WVQA_TAG MKBETAG('W', 'V', 'Q', 'A')
00037 #define VQHD_TAG MKBETAG('V', 'Q', 'H', 'D')
00038 #define FINF_TAG MKBETAG('F', 'I', 'N', 'F')
00039 #define SND0_TAG MKBETAG('S', 'N', 'D', '0')
00040 #define SND1_TAG MKBETAG('S', 'N', 'D', '1')
00041 #define SND2_TAG MKBETAG('S', 'N', 'D', '2')
00042 #define VQFR_TAG MKBETAG('V', 'Q', 'F', 'R')
00043 
00044 /* don't know what these tags are for, but acknowledge their existence */
00045 #define CINF_TAG MKBETAG('C', 'I', 'N', 'F')
00046 #define CINH_TAG MKBETAG('C', 'I', 'N', 'H')
00047 #define CIND_TAG MKBETAG('C', 'I', 'N', 'D')
00048 #define PINF_TAG MKBETAG('P', 'I', 'N', 'F')
00049 #define PINH_TAG MKBETAG('P', 'I', 'N', 'H')
00050 #define PIND_TAG MKBETAG('P', 'I', 'N', 'D')
00051 #define CMDS_TAG MKBETAG('C', 'M', 'D', 'S')
00052 
00053 #define VQA_HEADER_SIZE 0x2A
00054 #define VQA_PREAMBLE_SIZE 8
00055 
00056 typedef struct WsVqaDemuxContext {
00057     int audio_channels;
00058     int audio_stream_index;
00059     int video_stream_index;
00060 } WsVqaDemuxContext;
00061 
00062 static int wsvqa_probe(AVProbeData *p)
00063 {
00064     /* need 12 bytes to qualify */
00065     if (p->buf_size < 12)
00066         return 0;
00067 
00068     /* check for the VQA signatures */
00069     if ((AV_RB32(&p->buf[0]) != FORM_TAG) ||
00070         (AV_RB32(&p->buf[8]) != WVQA_TAG))
00071         return 0;
00072 
00073     return AVPROBE_SCORE_MAX;
00074 }
00075 
00076 static int wsvqa_read_header(AVFormatContext *s,
00077                              AVFormatParameters *ap)
00078 {
00079     WsVqaDemuxContext *wsvqa = s->priv_data;
00080     AVIOContext *pb = s->pb;
00081     AVStream *st;
00082     unsigned char *header;
00083     unsigned char scratch[VQA_PREAMBLE_SIZE];
00084     unsigned int chunk_tag;
00085     unsigned int chunk_size;
00086     int fps, version, flags, sample_rate, channels;
00087 
00088     /* initialize the video decoder stream */
00089     st = avformat_new_stream(s, NULL);
00090     if (!st)
00091         return AVERROR(ENOMEM);
00092     st->start_time = 0;
00093     wsvqa->video_stream_index = st->index;
00094     st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00095     st->codec->codec_id = CODEC_ID_WS_VQA;
00096     st->codec->codec_tag = 0;  /* no fourcc */
00097 
00098     /* skip to the start of the VQA header */
00099     avio_seek(pb, 20, SEEK_SET);
00100 
00101     /* the VQA header needs to go to the decoder */
00102     st->codec->extradata_size = VQA_HEADER_SIZE;
00103     st->codec->extradata = av_mallocz(VQA_HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
00104     header = (unsigned char *)st->codec->extradata;
00105     if (avio_read(pb, st->codec->extradata, VQA_HEADER_SIZE) !=
00106         VQA_HEADER_SIZE) {
00107         av_free(st->codec->extradata);
00108         return AVERROR(EIO);
00109     }
00110     st->codec->width = AV_RL16(&header[6]);
00111     st->codec->height = AV_RL16(&header[8]);
00112     fps = header[12];
00113     if (fps < 1 || fps > 30) {
00114         av_log(s, AV_LOG_ERROR, "invalid fps: %d\n", fps);
00115         return AVERROR_INVALIDDATA;
00116     }
00117     avpriv_set_pts_info(st, 64, 1, fps);
00118 
00119     /* initialize the audio decoder stream for VQA v1 or nonzero samplerate */
00120     version     = AV_RL16(&header[ 0]);
00121     flags       = AV_RL16(&header[ 2]);
00122     sample_rate = AV_RL16(&header[24]);
00123     channels    =          header[26];
00124     if (sample_rate || (version == 1 && flags == 1)) {
00125         st = avformat_new_stream(s, NULL);
00126         if (!st)
00127             return AVERROR(ENOMEM);
00128         st->start_time = 0;
00129         st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
00130 
00131         st->codec->extradata_size = VQA_HEADER_SIZE;
00132         st->codec->extradata = av_mallocz(VQA_HEADER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE);
00133         if (!st->codec->extradata)
00134             return AVERROR(ENOMEM);
00135         memcpy(st->codec->extradata, header, VQA_HEADER_SIZE);
00136 
00137         if (!sample_rate)
00138             sample_rate = 22050;
00139         st->codec->sample_rate = sample_rate;
00140         avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
00141 
00142         if (!channels)
00143             channels = 1;
00144         st->codec->channels = channels;
00145 
00146         switch (version) {
00147         case 1:
00148             st->codec->codec_id = CODEC_ID_WESTWOOD_SND1;
00149             break;
00150         case 2:
00151         case 3:
00152             st->codec->codec_id = CODEC_ID_ADPCM_IMA_WS;
00153             st->codec->bits_per_coded_sample = 4;
00154             st->codec->bit_rate = channels * sample_rate * 4;
00155             break;
00156         default:
00157             /* NOTE: version 0 is supposedly raw pcm_u8 or pcm_s16le, but we do
00158                      not have any samples to validate this */
00159             av_log_ask_for_sample(s, "VQA version %d audio\n", version);
00160             return AVERROR_PATCHWELCOME;
00161         }
00162 
00163         wsvqa->audio_stream_index = st->index;
00164         wsvqa->audio_channels = st->codec->channels;
00165     }
00166 
00167     /* there are 0 or more chunks before the FINF chunk; iterate until
00168      * FINF has been skipped and the file will be ready to be demuxed */
00169     do {
00170         if (avio_read(pb, scratch, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE)
00171             return AVERROR(EIO);
00172         chunk_tag = AV_RB32(&scratch[0]);
00173         chunk_size = AV_RB32(&scratch[4]);
00174 
00175         /* catch any unknown header tags, for curiousity */
00176         switch (chunk_tag) {
00177         case CINF_TAG:
00178         case CINH_TAG:
00179         case CIND_TAG:
00180         case PINF_TAG:
00181         case PINH_TAG:
00182         case PIND_TAG:
00183         case FINF_TAG:
00184         case CMDS_TAG:
00185             break;
00186 
00187         default:
00188             av_log (s, AV_LOG_ERROR, " note: unknown chunk seen (%c%c%c%c)\n",
00189                 scratch[0], scratch[1],
00190                 scratch[2], scratch[3]);
00191             break;
00192         }
00193 
00194         avio_skip(pb, chunk_size);
00195     } while (chunk_tag != FINF_TAG);
00196 
00197     return 0;
00198 }
00199 
00200 static int wsvqa_read_packet(AVFormatContext *s,
00201                              AVPacket *pkt)
00202 {
00203     WsVqaDemuxContext *wsvqa = s->priv_data;
00204     AVIOContext *pb = s->pb;
00205     int ret = -1;
00206     unsigned char preamble[VQA_PREAMBLE_SIZE];
00207     unsigned int chunk_type;
00208     unsigned int chunk_size;
00209     int skip_byte;
00210 
00211     while (avio_read(pb, preamble, VQA_PREAMBLE_SIZE) == VQA_PREAMBLE_SIZE) {
00212         chunk_type = AV_RB32(&preamble[0]);
00213         chunk_size = AV_RB32(&preamble[4]);
00214 
00215         skip_byte = chunk_size & 0x01;
00216 
00217         if ((chunk_type == SND2_TAG || chunk_type == SND1_TAG) && wsvqa->audio_channels == 0) {
00218             av_log(s, AV_LOG_ERROR, "audio chunk without any audio header information found\n");
00219             return AVERROR_INVALIDDATA;
00220         }
00221 
00222         if ((chunk_type == SND1_TAG) || (chunk_type == SND2_TAG) || (chunk_type == VQFR_TAG)) {
00223 
00224             ret= av_get_packet(pb, pkt, chunk_size);
00225             if (ret<0)
00226                 return AVERROR(EIO);
00227 
00228             if (chunk_type == SND2_TAG) {
00229                 pkt->stream_index = wsvqa->audio_stream_index;
00230                 /* 2 samples/byte, 1 or 2 samples per frame depending on stereo */
00231                 pkt->duration = (chunk_size * 2) / wsvqa->audio_channels;
00232             } else if(chunk_type == SND1_TAG) {
00233                 pkt->stream_index = wsvqa->audio_stream_index;
00234                 /* unpacked size is stored in header */
00235                 pkt->duration = AV_RL16(pkt->data) / wsvqa->audio_channels;
00236             } else {
00237                 pkt->stream_index = wsvqa->video_stream_index;
00238                 pkt->duration = 1;
00239             }
00240             /* stay on 16-bit alignment */
00241             if (skip_byte)
00242                 avio_skip(pb, 1);
00243 
00244             return ret;
00245         } else {
00246             switch(chunk_type){
00247             case CMDS_TAG:
00248             case SND0_TAG:
00249                 break;
00250             default:
00251                 av_log(s, AV_LOG_INFO, "Skipping unknown chunk 0x%08X\n", chunk_type);
00252             }
00253             avio_skip(pb, chunk_size + skip_byte);
00254         }
00255     }
00256 
00257     return ret;
00258 }
00259 
00260 AVInputFormat ff_wsvqa_demuxer = {
00261     .name           = "wsvqa",
00262     .long_name      = NULL_IF_CONFIG_SMALL("Westwood Studios VQA format"),
00263     .priv_data_size = sizeof(WsVqaDemuxContext),
00264     .read_probe     = wsvqa_probe,
00265     .read_header    = wsvqa_read_header,
00266     .read_packet    = wsvqa_read_packet,
00267 };