Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* |
2 | * Westwood Studios VQA Format Demuxer |
||
3 | * Copyright (c) 2003 The ffmpeg Project |
||
4 | * |
||
5 | * This file is part of FFmpeg. |
||
6 | * |
||
7 | * FFmpeg is free software; you can redistribute it and/or |
||
8 | * modify it under the terms of the GNU Lesser General Public |
||
9 | * License as published by the Free Software Foundation; either |
||
10 | * version 2.1 of the License, or (at your option) any later version. |
||
11 | * |
||
12 | * FFmpeg is distributed in the hope that it will be useful, |
||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
15 | * Lesser General Public License for more details. |
||
16 | * |
||
17 | * You should have received a copy of the GNU Lesser General Public |
||
18 | * License along with FFmpeg; if not, write to the Free Software |
||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||
20 | */ |
||
21 | |||
22 | /** |
||
23 | * @file |
||
24 | * Westwood Studios VQA file demuxer |
||
25 | * by Mike Melanson (melanson@pcisys.net) |
||
26 | * for more information on the Westwood file formats, visit: |
||
27 | * http://www.pcisys.net/~melanson/codecs/ |
||
28 | * http://www.geocities.com/SiliconValley/8682/aud3.txt |
||
29 | */ |
||
30 | |||
31 | #include "libavutil/intreadwrite.h" |
||
32 | #include "avformat.h" |
||
33 | #include "internal.h" |
||
34 | |||
35 | #define FORM_TAG MKBETAG('F', 'O', 'R', 'M') |
||
36 | #define WVQA_TAG MKBETAG('W', 'V', 'Q', 'A') |
||
37 | #define VQHD_TAG MKBETAG('V', 'Q', 'H', 'D') |
||
38 | #define FINF_TAG MKBETAG('F', 'I', 'N', 'F') |
||
39 | #define SND0_TAG MKBETAG('S', 'N', 'D', '0') |
||
40 | #define SND1_TAG MKBETAG('S', 'N', 'D', '1') |
||
41 | #define SND2_TAG MKBETAG('S', 'N', 'D', '2') |
||
42 | #define VQFR_TAG MKBETAG('V', 'Q', 'F', 'R') |
||
43 | |||
44 | /* don't know what these tags are for, but acknowledge their existence */ |
||
45 | #define CINF_TAG MKBETAG('C', 'I', 'N', 'F') |
||
46 | #define CINH_TAG MKBETAG('C', 'I', 'N', 'H') |
||
47 | #define CIND_TAG MKBETAG('C', 'I', 'N', 'D') |
||
48 | #define PINF_TAG MKBETAG('P', 'I', 'N', 'F') |
||
49 | #define PINH_TAG MKBETAG('P', 'I', 'N', 'H') |
||
50 | #define PIND_TAG MKBETAG('P', 'I', 'N', 'D') |
||
51 | #define CMDS_TAG MKBETAG('C', 'M', 'D', 'S') |
||
52 | |||
53 | #define VQA_HEADER_SIZE 0x2A |
||
54 | #define VQA_PREAMBLE_SIZE 8 |
||
55 | |||
56 | typedef struct WsVqaDemuxContext { |
||
57 | int version; |
||
58 | int bps; |
||
59 | int channels; |
||
60 | int sample_rate; |
||
61 | int audio_stream_index; |
||
62 | int video_stream_index; |
||
63 | } WsVqaDemuxContext; |
||
64 | |||
65 | static int wsvqa_probe(AVProbeData *p) |
||
66 | { |
||
67 | /* need 12 bytes to qualify */ |
||
68 | if (p->buf_size < 12) |
||
69 | return 0; |
||
70 | |||
71 | /* check for the VQA signatures */ |
||
72 | if ((AV_RB32(&p->buf[0]) != FORM_TAG) || |
||
73 | (AV_RB32(&p->buf[8]) != WVQA_TAG)) |
||
74 | return 0; |
||
75 | |||
76 | return AVPROBE_SCORE_MAX; |
||
77 | } |
||
78 | |||
79 | static int wsvqa_read_header(AVFormatContext *s) |
||
80 | { |
||
81 | WsVqaDemuxContext *wsvqa = s->priv_data; |
||
82 | AVIOContext *pb = s->pb; |
||
83 | AVStream *st; |
||
84 | uint8_t *header; |
||
85 | uint8_t scratch[VQA_PREAMBLE_SIZE]; |
||
86 | uint32_t chunk_tag; |
||
87 | uint32_t chunk_size; |
||
88 | int fps; |
||
89 | |||
90 | /* initialize the video decoder stream */ |
||
91 | st = avformat_new_stream(s, NULL); |
||
92 | if (!st) |
||
93 | return AVERROR(ENOMEM); |
||
94 | st->start_time = 0; |
||
95 | wsvqa->video_stream_index = st->index; |
||
96 | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
||
97 | st->codec->codec_id = AV_CODEC_ID_WS_VQA; |
||
98 | st->codec->codec_tag = 0; /* no fourcc */ |
||
99 | |||
100 | /* skip to the start of the VQA header */ |
||
101 | avio_seek(pb, 20, SEEK_SET); |
||
102 | |||
103 | /* the VQA header needs to go to the decoder */ |
||
104 | if (ff_alloc_extradata(st->codec, VQA_HEADER_SIZE)) |
||
105 | return AVERROR(ENOMEM); |
||
106 | header = (uint8_t *)st->codec->extradata; |
||
107 | if (avio_read(pb, st->codec->extradata, VQA_HEADER_SIZE) != |
||
108 | VQA_HEADER_SIZE) { |
||
109 | return AVERROR(EIO); |
||
110 | } |
||
111 | st->codec->width = AV_RL16(&header[6]); |
||
112 | st->codec->height = AV_RL16(&header[8]); |
||
113 | fps = header[12]; |
||
114 | st->nb_frames = |
||
115 | st->duration = AV_RL16(&header[4]); |
||
116 | if (fps < 1 || fps > 30) { |
||
117 | av_log(s, AV_LOG_ERROR, "invalid fps: %d\n", fps); |
||
118 | return AVERROR_INVALIDDATA; |
||
119 | } |
||
120 | avpriv_set_pts_info(st, 64, 1, fps); |
||
121 | |||
122 | wsvqa->version = AV_RL16(&header[ 0]); |
||
123 | wsvqa->sample_rate = AV_RL16(&header[24]); |
||
124 | wsvqa->channels = header[26]; |
||
125 | wsvqa->bps = header[27]; |
||
126 | wsvqa->audio_stream_index = -1; |
||
127 | |||
128 | s->ctx_flags |= AVFMTCTX_NOHEADER; |
||
129 | |||
130 | /* there are 0 or more chunks before the FINF chunk; iterate until |
||
131 | * FINF has been skipped and the file will be ready to be demuxed */ |
||
132 | do { |
||
133 | if (avio_read(pb, scratch, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE) |
||
134 | return AVERROR(EIO); |
||
135 | chunk_tag = AV_RB32(&scratch[0]); |
||
136 | chunk_size = AV_RB32(&scratch[4]); |
||
137 | |||
138 | /* catch any unknown header tags, for curiousity */ |
||
139 | switch (chunk_tag) { |
||
140 | case CINF_TAG: |
||
141 | case CINH_TAG: |
||
142 | case CIND_TAG: |
||
143 | case PINF_TAG: |
||
144 | case PINH_TAG: |
||
145 | case PIND_TAG: |
||
146 | case FINF_TAG: |
||
147 | case CMDS_TAG: |
||
148 | break; |
||
149 | |||
150 | default: |
||
151 | av_log (s, AV_LOG_ERROR, " note: unknown chunk seen (%c%c%c%c)\n", |
||
152 | scratch[0], scratch[1], |
||
153 | scratch[2], scratch[3]); |
||
154 | break; |
||
155 | } |
||
156 | |||
157 | avio_skip(pb, chunk_size); |
||
158 | } while (chunk_tag != FINF_TAG); |
||
159 | |||
160 | return 0; |
||
161 | } |
||
162 | |||
163 | static int wsvqa_read_packet(AVFormatContext *s, |
||
164 | AVPacket *pkt) |
||
165 | { |
||
166 | WsVqaDemuxContext *wsvqa = s->priv_data; |
||
167 | AVIOContext *pb = s->pb; |
||
168 | int ret = -1; |
||
169 | uint8_t preamble[VQA_PREAMBLE_SIZE]; |
||
170 | uint32_t chunk_type; |
||
171 | uint32_t chunk_size; |
||
172 | int skip_byte; |
||
173 | |||
174 | while (avio_read(pb, preamble, VQA_PREAMBLE_SIZE) == VQA_PREAMBLE_SIZE) { |
||
175 | chunk_type = AV_RB32(&preamble[0]); |
||
176 | chunk_size = AV_RB32(&preamble[4]); |
||
177 | |||
178 | skip_byte = chunk_size & 0x01; |
||
179 | |||
180 | if ((chunk_type == SND0_TAG) || (chunk_type == SND1_TAG) || |
||
181 | (chunk_type == SND2_TAG) || (chunk_type == VQFR_TAG)) { |
||
182 | |||
183 | ret= av_get_packet(pb, pkt, chunk_size); |
||
184 | if (ret<0) |
||
185 | return AVERROR(EIO); |
||
186 | |||
187 | switch (chunk_type) { |
||
188 | case SND0_TAG: |
||
189 | case SND1_TAG: |
||
190 | case SND2_TAG: |
||
191 | if (wsvqa->audio_stream_index == -1) { |
||
192 | AVStream *st = avformat_new_stream(s, NULL); |
||
193 | if (!st) |
||
194 | return AVERROR(ENOMEM); |
||
195 | |||
196 | wsvqa->audio_stream_index = st->index; |
||
197 | if (!wsvqa->sample_rate) |
||
198 | wsvqa->sample_rate = 22050; |
||
199 | if (!wsvqa->channels) |
||
200 | wsvqa->channels = 1; |
||
201 | if (!wsvqa->bps) |
||
202 | wsvqa->bps = 8; |
||
203 | st->codec->sample_rate = wsvqa->sample_rate; |
||
204 | st->codec->bits_per_coded_sample = wsvqa->bps; |
||
205 | st->codec->channels = wsvqa->channels; |
||
206 | st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
||
207 | |||
208 | avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); |
||
209 | |||
210 | switch (chunk_type) { |
||
211 | case SND0_TAG: |
||
212 | if (wsvqa->bps == 16) |
||
213 | st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; |
||
214 | else |
||
215 | st->codec->codec_id = AV_CODEC_ID_PCM_U8; |
||
216 | break; |
||
217 | case SND1_TAG: |
||
218 | st->codec->codec_id = AV_CODEC_ID_WESTWOOD_SND1; |
||
219 | break; |
||
220 | case SND2_TAG: |
||
221 | st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WS; |
||
222 | if (ff_alloc_extradata(st->codec, 2)) |
||
223 | return AVERROR(ENOMEM); |
||
224 | AV_WL16(st->codec->extradata, wsvqa->version); |
||
225 | break; |
||
226 | } |
||
227 | } |
||
228 | |||
229 | pkt->stream_index = wsvqa->audio_stream_index; |
||
230 | switch (chunk_type) { |
||
231 | case SND1_TAG: |
||
232 | /* unpacked size is stored in header */ |
||
233 | if(pkt->data) |
||
234 | pkt->duration = AV_RL16(pkt->data) / wsvqa->channels; |
||
235 | break; |
||
236 | case SND2_TAG: |
||
237 | /* 2 samples/byte, 1 or 2 samples per frame depending on stereo */ |
||
238 | pkt->duration = (chunk_size * 2) / wsvqa->channels; |
||
239 | break; |
||
240 | } |
||
241 | break; |
||
242 | case VQFR_TAG: |
||
243 | pkt->stream_index = wsvqa->video_stream_index; |
||
244 | pkt->duration = 1; |
||
245 | break; |
||
246 | } |
||
247 | |||
248 | /* stay on 16-bit alignment */ |
||
249 | if (skip_byte) |
||
250 | avio_skip(pb, 1); |
||
251 | |||
252 | return ret; |
||
253 | } else { |
||
254 | switch(chunk_type){ |
||
255 | case CMDS_TAG: |
||
256 | break; |
||
257 | default: |
||
258 | av_log(s, AV_LOG_INFO, "Skipping unknown chunk 0x%08X\n", chunk_type); |
||
259 | } |
||
260 | avio_skip(pb, chunk_size + skip_byte); |
||
261 | } |
||
262 | } |
||
263 | |||
264 | return ret; |
||
265 | } |
||
266 | |||
267 | AVInputFormat ff_wsvqa_demuxer = { |
||
268 | .name = "wsvqa", |
||
269 | .long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA"), |
||
270 | .priv_data_size = sizeof(WsVqaDemuxContext), |
||
271 | .read_probe = wsvqa_probe, |
||
272 | .read_header = wsvqa_read_header, |
||
273 | .read_packet = wsvqa_read_packet, |
||
274 | };0) |