Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Westwood Studios VQA Format Demuxer | ||
3 | * Copyright (c) 2003 Mike Melanson <melanson@pcisys.net> | ||
4 | * Copyright (c) 2021 Pekka Väänänen <pekka.vaananen@iki.fi> | ||
5 | * | ||
6 | * This file is part of FFmpeg. | ||
7 | * | ||
8 | * FFmpeg is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU Lesser General Public | ||
10 | * License as published by the Free Software Foundation; either | ||
11 | * version 2.1 of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * FFmpeg is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * Lesser General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU Lesser General Public | ||
19 | * License along with FFmpeg; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
21 | */ | ||
22 | |||
23 | /** | ||
24 | * @file | ||
25 | * Westwood Studios VQA file demuxer | ||
26 | * by Mike Melanson (melanson@pcisys.net) | ||
27 | * for more information on the Westwood file formats, visit: | ||
28 | * http://www.pcisys.net/~melanson/codecs/ | ||
29 | * http://www.geocities.com/SiliconValley/8682/aud3.txt | ||
30 | */ | ||
31 | |||
32 | #include "libavutil/intreadwrite.h" | ||
33 | #include "avformat.h" | ||
34 | #include "avio_internal.h" | ||
35 | #include "demux.h" | ||
36 | #include "internal.h" | ||
37 | |||
38 | #define FORM_TAG MKBETAG('F', 'O', 'R', 'M') | ||
39 | #define WVQA_TAG MKBETAG('W', 'V', 'Q', 'A') | ||
40 | #define VQHD_TAG MKBETAG('V', 'Q', 'H', 'D') | ||
41 | #define FINF_TAG MKBETAG('F', 'I', 'N', 'F') | ||
42 | #define SND0_TAG MKBETAG('S', 'N', 'D', '0') | ||
43 | #define SND1_TAG MKBETAG('S', 'N', 'D', '1') | ||
44 | #define SND2_TAG MKBETAG('S', 'N', 'D', '2') | ||
45 | #define VQFR_TAG MKBETAG('V', 'Q', 'F', 'R') | ||
46 | #define VQFL_TAG MKBETAG('V', 'Q', 'F', 'L') | ||
47 | |||
48 | /* don't know what these tags are for, but acknowledge their existence */ | ||
49 | #define CINF_TAG MKBETAG('C', 'I', 'N', 'F') | ||
50 | #define CINH_TAG MKBETAG('C', 'I', 'N', 'H') | ||
51 | #define CIND_TAG MKBETAG('C', 'I', 'N', 'D') | ||
52 | #define LINF_TAG MKBETAG('L', 'I', 'N', 'F') | ||
53 | #define PINF_TAG MKBETAG('P', 'I', 'N', 'F') | ||
54 | #define PINH_TAG MKBETAG('P', 'I', 'N', 'H') | ||
55 | #define PIND_TAG MKBETAG('P', 'I', 'N', 'D') | ||
56 | #define CMDS_TAG MKBETAG('C', 'M', 'D', 'S') | ||
57 | #define SN2J_TAG MKBETAG('S', 'N', '2', 'J') | ||
58 | #define VIEW_TAG MKBETAG('V', 'I', 'E', 'W') | ||
59 | #define ZBUF_TAG MKBETAG('Z', 'B', 'U', 'F') | ||
60 | |||
61 | #define VQA_HEADER_SIZE 0x2A | ||
62 | #define VQA_PREAMBLE_SIZE 8 | ||
63 | |||
64 | typedef struct WsVqaDemuxContext { | ||
65 | int version; | ||
66 | int bps; | ||
67 | int channels; | ||
68 | int sample_rate; | ||
69 | int audio_stream_index; | ||
70 | int video_stream_index; | ||
71 | int64_t vqfl_chunk_pos; | ||
72 | int vqfl_chunk_size; | ||
73 | } WsVqaDemuxContext; | ||
74 | |||
75 | 7186 | static int wsvqa_probe(const AVProbeData *p) | |
76 | { | ||
77 | /* need 12 bytes to qualify */ | ||
78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7186 times.
|
7186 | if (p->buf_size < 12) |
79 | ✗ | return 0; | |
80 | |||
81 | /* check for the VQA signatures */ | ||
82 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 7165 times.
|
7186 | if ((AV_RB32(&p->buf[0]) != FORM_TAG) || |
83 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 4 times.
|
21 | (AV_RB32(&p->buf[8]) != WVQA_TAG)) |
84 | 7182 | return 0; | |
85 | |||
86 | 4 | return AVPROBE_SCORE_MAX; | |
87 | } | ||
88 | |||
89 | 4 | static int wsvqa_read_header(AVFormatContext *s) | |
90 | { | ||
91 | 4 | WsVqaDemuxContext *wsvqa = s->priv_data; | |
92 | 4 | AVIOContext *pb = s->pb; | |
93 | AVStream *st; | ||
94 | uint8_t *header; | ||
95 | uint8_t scratch[VQA_PREAMBLE_SIZE]; | ||
96 | uint32_t chunk_tag; | ||
97 | uint32_t chunk_size; | ||
98 | int fps, ret; | ||
99 | |||
100 | /* initialize the video decoder stream */ | ||
101 | 4 | st = avformat_new_stream(s, NULL); | |
102 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!st) |
103 | ✗ | return AVERROR(ENOMEM); | |
104 | 4 | st->start_time = 0; | |
105 | 4 | wsvqa->video_stream_index = st->index; | |
106 | 4 | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; | |
107 | 4 | st->codecpar->codec_id = AV_CODEC_ID_WS_VQA; | |
108 | 4 | st->codecpar->codec_tag = 0; /* no fourcc */ | |
109 | |||
110 | /* skip to the start of the VQA header */ | ||
111 | 4 | avio_seek(pb, 20, SEEK_SET); | |
112 | |||
113 | /* the VQA header needs to go to the decoder */ | ||
114 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if ((ret = ff_get_extradata(s, st->codecpar, pb, VQA_HEADER_SIZE)) < 0) |
115 | ✗ | return ret; | |
116 | 4 | header = st->codecpar->extradata; | |
117 | 4 | st->codecpar->width = AV_RL16(&header[6]); | |
118 | 4 | st->codecpar->height = AV_RL16(&header[8]); | |
119 | 4 | fps = header[12]; | |
120 | 4 | st->nb_frames = | |
121 | 4 | st->duration = AV_RL16(&header[4]); | |
122 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
4 | if (fps < 1 || fps > 30) { |
123 | ✗ | av_log(s, AV_LOG_ERROR, "invalid fps: %d\n", fps); | |
124 | ✗ | return AVERROR_INVALIDDATA; | |
125 | } | ||
126 | 4 | avpriv_set_pts_info(st, 64, 1, fps); | |
127 | |||
128 | 4 | wsvqa->version = AV_RL16(&header[ 0]); | |
129 | 4 | wsvqa->sample_rate = AV_RL16(&header[24]); | |
130 | 4 | wsvqa->channels = header[26]; | |
131 | 4 | wsvqa->bps = header[27]; | |
132 | 4 | wsvqa->audio_stream_index = -1; | |
133 | 4 | wsvqa->vqfl_chunk_pos = 0; | |
134 | 4 | wsvqa->vqfl_chunk_size = 0; | |
135 | |||
136 | 4 | s->ctx_flags |= AVFMTCTX_NOHEADER; | |
137 | |||
138 | /* there are 0 or more chunks before the FINF chunk; iterate until | ||
139 | * FINF has been skipped and the file will be ready to be demuxed */ | ||
140 | do { | ||
141 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if (avio_read(pb, scratch, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE) |
142 | ✗ | return AVERROR(EIO); | |
143 | 6 | chunk_tag = AV_RB32(&scratch[0]); | |
144 | 6 | chunk_size = AV_RB32(&scratch[4]); | |
145 | |||
146 | /* catch any unknown header tags, for curiosity */ | ||
147 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | switch (chunk_tag) { |
148 | 6 | case CINF_TAG: | |
149 | case CINH_TAG: | ||
150 | case CIND_TAG: | ||
151 | case LINF_TAG: | ||
152 | case PINF_TAG: | ||
153 | case PINH_TAG: | ||
154 | case PIND_TAG: | ||
155 | case FINF_TAG: | ||
156 | case CMDS_TAG: | ||
157 | case VIEW_TAG: | ||
158 | case ZBUF_TAG: | ||
159 | 6 | break; | |
160 | |||
161 | ✗ | default: | |
162 | ✗ | av_log(s, AV_LOG_ERROR, " note: unknown chunk seen (%s)\n", | |
163 | ✗ | av_fourcc2str(chunk_tag)); | |
164 | ✗ | break; | |
165 | } | ||
166 | |||
167 | 6 | avio_skip(pb, chunk_size); | |
168 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | } while (chunk_tag != FINF_TAG); |
169 | |||
170 | 4 | return 0; | |
171 | } | ||
172 | |||
173 | 212 | static int wsvqa_read_packet(AVFormatContext *s, | |
174 | AVPacket *pkt) | ||
175 | { | ||
176 | 212 | WsVqaDemuxContext *wsvqa = s->priv_data; | |
177 | 212 | AVIOContext *pb = s->pb; | |
178 | 212 | int ret = -1; | |
179 | uint8_t preamble[VQA_PREAMBLE_SIZE]; | ||
180 | uint32_t chunk_type; | ||
181 | int chunk_size; | ||
182 | unsigned skip_byte; | ||
183 | |||
184 |
2/2✓ Branch 1 taken 217 times.
✓ Branch 2 taken 8 times.
|
225 | while (avio_read(pb, preamble, VQA_PREAMBLE_SIZE) == VQA_PREAMBLE_SIZE) { |
185 | 217 | chunk_type = AV_RB32(&preamble[0]); | |
186 | 217 | chunk_size = AV_RB32(&preamble[4]); | |
187 | |||
188 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
|
217 | if (chunk_size < 0) |
189 | ✗ | return AVERROR_INVALIDDATA; | |
190 | 217 | skip_byte = chunk_size & 0x01; | |
191 | |||
192 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
|
217 | if (chunk_type == VQFL_TAG) { |
193 | /* Each VQFL chunk carries only a codebook update inside which must be applied | ||
194 | * before the next VQFR is rendered. That's why we stash the VQFL offset here | ||
195 | * so it can be combined with the next VQFR packet. This way each packet | ||
196 | * includes a whole frame as expected. */ | ||
197 | ✗ | wsvqa->vqfl_chunk_pos = avio_tell(pb); | |
198 | ✗ | if (chunk_size > 3 * (1 << 20)) | |
199 | ✗ | return AVERROR_INVALIDDATA; | |
200 | ✗ | wsvqa->vqfl_chunk_size = chunk_size; | |
201 | /* We need a big seekback buffer because there can be SNxx, VIEW and ZBUF | ||
202 | * chunks (<512 KiB total) in the stream before we read VQFR (<256 KiB) and | ||
203 | * seek back here. */ | ||
204 | ✗ | ret = ffio_ensure_seekback(pb, wsvqa->vqfl_chunk_size + (512 + 256) * 1024); | |
205 | ✗ | avio_skip(pb, chunk_size + skip_byte); | |
206 | ✗ | if (ret < 0) | |
207 | ✗ | return ret; | |
208 | ✗ | continue; | |
209 |
5/6✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 206 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 114 times.
✓ Branch 5 taken 92 times.
|
217 | } else if ((chunk_type == SND0_TAG) || (chunk_type == SND1_TAG) || |
210 |
2/2✓ Branch 0 taken 101 times.
✓ Branch 1 taken 13 times.
|
114 | (chunk_type == SND2_TAG) || (chunk_type == VQFR_TAG)) { |
211 | |||
212 | 204 | ret= av_get_packet(pb, pkt, chunk_size); | |
213 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 204 times.
|
204 | if (ret<0) |
214 | ✗ | return AVERROR(EIO); | |
215 | |||
216 |
2/3✓ Branch 0 taken 103 times.
✓ Branch 1 taken 101 times.
✗ Branch 2 not taken.
|
204 | switch (chunk_type) { |
217 | 103 | case SND0_TAG: | |
218 | case SND1_TAG: | ||
219 | case SND2_TAG: | ||
220 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 99 times.
|
103 | if (wsvqa->audio_stream_index == -1) { |
221 | 4 | AVStream *st = avformat_new_stream(s, NULL); | |
222 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!st) |
223 | ✗ | return AVERROR(ENOMEM); | |
224 | |||
225 | 4 | wsvqa->audio_stream_index = st->index; | |
226 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | if (!wsvqa->sample_rate) |
227 | 1 | wsvqa->sample_rate = 22050; | |
228 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | if (!wsvqa->channels) |
229 | 1 | wsvqa->channels = 1; | |
230 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | if (!wsvqa->bps) |
231 | 1 | wsvqa->bps = 8; | |
232 | 4 | st->codecpar->sample_rate = wsvqa->sample_rate; | |
233 | 4 | st->codecpar->bits_per_coded_sample = wsvqa->bps; | |
234 | 4 | av_channel_layout_default(&st->codecpar->ch_layout, wsvqa->channels); | |
235 | 4 | st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; | |
236 | |||
237 | 4 | avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); | |
238 | |||
239 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
4 | switch (chunk_type) { |
240 | ✗ | case SND0_TAG: | |
241 | ✗ | if (wsvqa->bps == 16) | |
242 | ✗ | st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; | |
243 | else | ||
244 | ✗ | st->codecpar->codec_id = AV_CODEC_ID_PCM_U8; | |
245 | ✗ | break; | |
246 | 1 | case SND1_TAG: | |
247 | 1 | st->codecpar->codec_id = AV_CODEC_ID_WESTWOOD_SND1; | |
248 | 1 | break; | |
249 | 3 | case SND2_TAG: | |
250 | 3 | st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_WS; | |
251 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if ((ret = ff_alloc_extradata(st->codecpar, 2)) < 0) |
252 | ✗ | return ret; | |
253 | 3 | AV_WL16(st->codecpar->extradata, wsvqa->version); | |
254 | 3 | break; | |
255 | } | ||
256 | } | ||
257 | |||
258 | 103 | pkt->stream_index = wsvqa->audio_stream_index; | |
259 |
2/3✓ Branch 0 taken 11 times.
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
|
103 | switch (chunk_type) { |
260 | 11 | case SND1_TAG: | |
261 | /* unpacked size is stored in header */ | ||
262 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if(pkt->data) |
263 | 11 | pkt->duration = AV_RL16(pkt->data) / wsvqa->channels; | |
264 | 11 | break; | |
265 | 92 | case SND2_TAG: | |
266 | /* 2 samples/byte, 1 or 2 samples per frame depending on stereo */ | ||
267 | 92 | pkt->duration = (chunk_size * 2LL) / wsvqa->channels; | |
268 | 92 | break; | |
269 | } | ||
270 | 103 | break; | |
271 | 101 | case VQFR_TAG: | |
272 | /* if a new codebook is available inside an earlier a VQFL chunk then | ||
273 | * append it to 'pkt' */ | ||
274 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 101 times.
|
101 | if (wsvqa->vqfl_chunk_size > 0) { |
275 | ✗ | int64_t current_pos = pkt->pos; | |
276 | |||
277 | ✗ | if (avio_seek(pb, wsvqa->vqfl_chunk_pos, SEEK_SET) < 0) | |
278 | ✗ | return AVERROR(EIO); | |
279 | |||
280 | /* the decoder expects chunks to be 16-bit aligned */ | ||
281 | ✗ | if (wsvqa->vqfl_chunk_size % 2 == 1) | |
282 | ✗ | wsvqa->vqfl_chunk_size++; | |
283 | |||
284 | ✗ | if (av_append_packet(pb, pkt, wsvqa->vqfl_chunk_size) < 0) | |
285 | ✗ | return AVERROR(EIO); | |
286 | |||
287 | ✗ | if (avio_seek(pb, current_pos, SEEK_SET) < 0) | |
288 | ✗ | return AVERROR(EIO); | |
289 | |||
290 | ✗ | wsvqa->vqfl_chunk_pos = 0; | |
291 | ✗ | wsvqa->vqfl_chunk_size = 0; | |
292 | } | ||
293 | |||
294 | 101 | pkt->stream_index = wsvqa->video_stream_index; | |
295 | 101 | pkt->duration = 1; | |
296 | 101 | break; | |
297 | } | ||
298 | |||
299 | /* stay on 16-bit alignment */ | ||
300 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 107 times.
|
204 | if (skip_byte) |
301 | 97 | avio_skip(pb, 1); | |
302 | |||
303 | 204 | return ret; | |
304 | } else { | ||
305 |
1/2✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
|
13 | switch(chunk_type){ |
306 | 13 | case CMDS_TAG: | |
307 | case SN2J_TAG: | ||
308 | case VIEW_TAG: | ||
309 | case ZBUF_TAG: | ||
310 | 13 | break; | |
311 | ✗ | default: | |
312 | ✗ | av_log(s, AV_LOG_INFO, "Skipping unknown chunk %s\n", | |
313 | ✗ | av_fourcc2str(av_bswap32(chunk_type))); | |
314 | } | ||
315 | 13 | avio_skip(pb, chunk_size + skip_byte); | |
316 | } | ||
317 | } | ||
318 | |||
319 | 8 | return ret; | |
320 | } | ||
321 | |||
322 | const FFInputFormat ff_wsvqa_demuxer = { | ||
323 | .p.name = "wsvqa", | ||
324 | .p.long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA"), | ||
325 | .priv_data_size = sizeof(WsVqaDemuxContext), | ||
326 | .read_probe = wsvqa_probe, | ||
327 | .read_header = wsvqa_read_header, | ||
328 | .read_packet = wsvqa_read_packet, | ||
329 | }; | ||
330 |