FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/westwood_vqa.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 106 148 71.6%
Functions: 3 3 100.0%
Branches: 46 80 57.5%

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