FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/aaxdec.c
Date: 2024-02-16 17:37:06
Exec Total Coverage
Lines: 3 206 1.5%
Functions: 1 5 20.0%
Branches: 1 126 0.8%

Line Branch Exec Source
1 /*
2 * AAX demuxer
3 * Copyright (c) 2020 Paul B Mahol
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 #include "libavutil/intreadwrite.h"
23 #include "avformat.h"
24 #include "avio_internal.h"
25 #include "demux.h"
26 #include "internal.h"
27
28 typedef struct AAXColumn {
29 uint8_t flag;
30 uint8_t type;
31 const char *name;
32 uint32_t offset;
33 int size;
34 } AAXColumn;
35
36 typedef struct AAXSegment {
37 int64_t start;
38 int64_t end;
39 } AAXSegment;
40
41 typedef struct AAXContext {
42 int64_t table_size;
43 uint16_t version;
44 int64_t rows_offset;
45 int64_t strings_offset;
46 int64_t data_offset;
47 int64_t name_offset;
48 uint16_t columns;
49 uint16_t row_width;
50 uint32_t nb_segments;
51 int64_t schema_offset;
52 int64_t strings_size;
53 char *string_table;
54
55 uint32_t current_segment;
56
57 AAXColumn *xcolumns;
58 AAXSegment *segments;
59 } AAXContext;
60
61 7023 static int aax_probe(const AVProbeData *p)
62 {
63
1/2
✓ Branch 0 taken 7023 times.
✗ Branch 1 not taken.
7023 if (AV_RB32(p->buf) != MKBETAG('@','U','T','F'))
64 7023 return 0;
65 if (AV_RB32(p->buf + 4) == 0)
66 return 0;
67 if (AV_RB16(p->buf + 8) > 1)
68 return 0;
69 if (AV_RB32(p->buf + 28) < 1)
70 return 0;
71
72 return AVPROBE_SCORE_MAX;
73 }
74
75 enum ColumnFlag {
76 COLUMN_FLAG_NAME = 0x1,
77 COLUMN_FLAG_DEFAULT = 0x2,
78 COLUMN_FLAG_ROW = 0x4,
79 COLUMN_FLAG_UNDEFINED = 0x8 /* shouldn't exist */
80 };
81
82 enum ColumnType {
83 COLUMN_TYPE_UINT8 = 0x00,
84 COLUMN_TYPE_SINT8 = 0x01,
85 COLUMN_TYPE_UINT16 = 0x02,
86 COLUMN_TYPE_SINT16 = 0x03,
87 COLUMN_TYPE_UINT32 = 0x04,
88 COLUMN_TYPE_SINT32 = 0x05,
89 COLUMN_TYPE_UINT64 = 0x06,
90 COLUMN_TYPE_SINT64 = 0x07,
91 COLUMN_TYPE_FLOAT = 0x08,
92 COLUMN_TYPE_DOUBLE = 0x09,
93 COLUMN_TYPE_STRING = 0x0a,
94 COLUMN_TYPE_VLDATA = 0x0b,
95 COLUMN_TYPE_UINT128 = 0x0c, /* for GUIDs */
96 COLUMN_TYPE_UNDEFINED = -1
97 };
98
99 static int64_t get_pts(AVFormatContext *s, int64_t pos, int size)
100 {
101 AAXContext *a = s->priv_data;
102 int64_t pts = 0;
103
104 for (int seg = 0; seg < a->current_segment; seg++)
105 pts += (a->segments[seg].end - a->segments[seg].start) / size;
106
107 pts += ((pos - a->segments[a->current_segment].start) / size);
108
109 return pts;
110 }
111
112 static int aax_read_header(AVFormatContext *s)
113 {
114 AAXContext *a = s->priv_data;
115 AVIOContext *pb = s->pb;
116 AVCodecParameters *par;
117 AVStream *st;
118 int64_t column_offset = 0;
119 int ret, extradata_size;
120 char *codec;
121 int64_t ret64;
122
123 avio_skip(pb, 4);
124 a->table_size = avio_rb32(pb) + 8LL;
125 a->version = avio_rb16(pb);
126 a->rows_offset = avio_rb16(pb) + 8LL;
127 a->strings_offset = avio_rb32(pb) + 8LL;
128 a->data_offset = avio_rb32(pb) + 8LL;
129 a->name_offset = avio_rb32(pb);
130 a->columns = avio_rb16(pb);
131 a->row_width = avio_rb16(pb);
132 a->nb_segments = avio_rb32(pb);
133
134 if (a->nb_segments < 1)
135 return AVERROR_INVALIDDATA;
136
137 a->schema_offset = 0x20;
138 a->strings_size = a->data_offset - a->strings_offset;
139
140 if (a->rows_offset > a->table_size ||
141 a->strings_offset > a->table_size ||
142 a->data_offset > a->table_size)
143 return AVERROR_INVALIDDATA;
144 if (a->strings_size <= 0 || a->name_offset >= a->strings_size ||
145 a->strings_size > UINT16_MAX)
146 return AVERROR_INVALIDDATA;
147 if (a->columns <= 0)
148 return AVERROR_INVALIDDATA;
149
150 a->segments = av_calloc(a->nb_segments, sizeof(*a->segments));
151 if (!a->segments)
152 return AVERROR(ENOMEM);
153
154 a->xcolumns = av_calloc(a->columns, sizeof(*a->xcolumns));
155 if (!a->xcolumns)
156 return AVERROR(ENOMEM);
157
158 a->string_table = av_calloc(a->strings_size + 1, sizeof(*a->string_table));
159 if (!a->string_table)
160 return AVERROR(ENOMEM);
161
162 for (int c = 0; c < a->columns; c++) {
163 uint8_t info = avio_r8(pb);
164 uint32_t offset = avio_rb32(pb);
165 int value_size;
166
167 if (offset >= a->strings_size)
168 return AVERROR_INVALIDDATA;
169
170 a->xcolumns[c].flag = info >> 4;
171 a->xcolumns[c].type = info & 0x0F;
172
173 switch (a->xcolumns[c].type) {
174 case COLUMN_TYPE_UINT8:
175 case COLUMN_TYPE_SINT8:
176 value_size = 0x01;
177 break;
178 case COLUMN_TYPE_UINT16:
179 case COLUMN_TYPE_SINT16:
180 value_size = 0x02;
181 break;
182 case COLUMN_TYPE_UINT32:
183 case COLUMN_TYPE_SINT32:
184 case COLUMN_TYPE_FLOAT:
185 case COLUMN_TYPE_STRING:
186 value_size = 0x04;
187 break;
188 case COLUMN_TYPE_VLDATA:
189 value_size = 0x08;
190 break;
191 case COLUMN_TYPE_UINT128:
192 value_size = 0x10;
193 break;
194 default:
195 return AVERROR_INVALIDDATA;
196 }
197
198 a->xcolumns[c].size = value_size;
199
200 if (a->xcolumns[c].flag & COLUMN_FLAG_NAME)
201 a->xcolumns[c].name = a->string_table + offset;
202
203 if (a->xcolumns[c].flag & COLUMN_FLAG_DEFAULT) {
204 /* data is found relative to columns start */
205 a->xcolumns[c].offset = avio_tell(pb) - a->schema_offset;
206 avio_skip(pb, value_size);
207 }
208
209 if (a->xcolumns[c].flag & COLUMN_FLAG_ROW) {
210 /* data is found relative to row start */
211 a->xcolumns[c].offset = column_offset;
212 column_offset += value_size;
213 }
214 }
215
216 ret = ret64 = avio_seek(pb, a->strings_offset, SEEK_SET);
217 if (ret64 < 0)
218 return ret;
219
220 ret = ffio_read_size(pb, a->string_table, a->strings_size);
221 if (ret < 0)
222 return ret;
223
224 for (int c = 0; c < a->columns; c++) {
225 int64_t data_offset = 0;
226 int64_t col_offset;
227 int flag, type;
228
229 if (!a->xcolumns[c].name || strcmp(a->xcolumns[c].name, "data"))
230 continue;
231
232 type = a->xcolumns[c].type;
233 flag = a->xcolumns[c].flag;
234 col_offset = a->xcolumns[c].offset;
235
236 for (uint64_t r = 0; r < a->nb_segments; r++) {
237 if (flag & COLUMN_FLAG_DEFAULT) {
238 data_offset = a->schema_offset + col_offset;
239 } else if (flag & COLUMN_FLAG_ROW) {
240 data_offset = a->rows_offset + r * a->row_width + col_offset;
241 } else
242 return AVERROR_INVALIDDATA;
243
244 ret = ret64 = avio_seek(pb, data_offset, SEEK_SET);
245 if (ret64 < 0)
246 return ret;
247
248 if (type == COLUMN_TYPE_VLDATA) {
249 int64_t start, size;
250
251 start = avio_rb32(pb);
252 size = avio_rb32(pb);
253 if (!size)
254 return AVERROR_INVALIDDATA;
255 a->segments[r].start = start + a->data_offset;
256 a->segments[r].end = a->segments[r].start + size;
257 if (r &&
258 a->segments[r].start < a->segments[r-1].end &&
259 a->segments[r].end > a->segments[r-1].start)
260 return AVERROR_INVALIDDATA;
261 } else
262 return AVERROR_INVALIDDATA;
263 }
264 }
265
266 if (!a->segments[0].end)
267 return AVERROR_INVALIDDATA;
268
269 st = avformat_new_stream(s, NULL);
270 if (!st)
271 return AVERROR(ENOMEM);
272 st->start_time = 0;
273 par = s->streams[0]->codecpar;
274 par->codec_type = AVMEDIA_TYPE_AUDIO;
275
276 codec = a->string_table + a->name_offset;
277 if (!strcmp(codec, "AAX")) {
278 par->codec_id = AV_CODEC_ID_ADPCM_ADX;
279 ret64 = avio_seek(pb, a->segments[0].start, SEEK_SET);
280 if (ret64 < 0 || avio_rb16(pb) != 0x8000)
281 return AVERROR_INVALIDDATA;
282 extradata_size = avio_rb16(pb) + 4;
283 if (extradata_size < 12)
284 return AVERROR_INVALIDDATA;
285 avio_seek(pb, -4, SEEK_CUR);
286 ret = ff_get_extradata(s, par, pb, extradata_size);
287 if (ret < 0)
288 return ret;
289 par->ch_layout.nb_channels = AV_RB8 (par->extradata + 7);
290 par->sample_rate = AV_RB32(par->extradata + 8);
291 if (!par->ch_layout.nb_channels || !par->sample_rate)
292 return AVERROR_INVALIDDATA;
293
294 avpriv_set_pts_info(st, 64, 32, par->sample_rate);
295 /*} else if (!strcmp(codec, "HCA") ){
296 par->codec_id = AV_CODEC_ID_HCA;*/
297 } else {
298 return AVERROR_INVALIDDATA;
299 }
300
301 return 0;
302 }
303
304 static int aax_read_packet(AVFormatContext *s, AVPacket *pkt)
305 {
306 AAXContext *a = s->priv_data;
307 AVCodecParameters *par = s->streams[0]->codecpar;
308 AVIOContext *pb = s->pb;
309 const int size = 18 * par->ch_layout.nb_channels;
310 int ret, extradata_size = 0;
311 uint8_t *extradata = NULL;
312 int skip = 0;
313
314 if (avio_feof(pb))
315 return AVERROR_EOF;
316
317 pkt->pos = avio_tell(pb);
318
319 for (uint32_t seg = 0; seg < a->nb_segments; seg++) {
320 int64_t start = a->segments[seg].start;
321 int64_t end = a->segments[seg].end;
322
323 if (pkt->pos >= start && pkt->pos <= end) {
324 a->current_segment = seg;
325 if (par->codec_id == AV_CODEC_ID_ADPCM_ADX)
326 skip = (end - start) - ((end - start) / size) * size;
327 break;
328 }
329 }
330
331 if (pkt->pos >= a->segments[a->current_segment].end - skip) {
332 if (a->current_segment + 1 == a->nb_segments)
333 return AVERROR_EOF;
334 a->current_segment++;
335 avio_seek(pb, a->segments[a->current_segment].start, SEEK_SET);
336
337 if (par->codec_id == AV_CODEC_ID_ADPCM_ADX) {
338 if (avio_rb16(pb) != 0x8000)
339 return AVERROR_INVALIDDATA;
340 extradata_size = avio_rb16(pb) + 4;
341 avio_seek(pb, -4, SEEK_CUR);
342 if (extradata_size < 12)
343 return AVERROR_INVALIDDATA;
344 extradata = av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
345 if (!extradata)
346 return AVERROR(ENOMEM);
347 if (avio_read(pb, extradata, extradata_size) != extradata_size) {
348 av_free(extradata);
349 return AVERROR(EIO);
350 }
351 memset(extradata + extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
352 }
353 }
354
355 ret = av_get_packet(pb, pkt, size);
356 if (ret != size) {
357 av_free(extradata);
358 return ret < 0 ? ret : AVERROR(EIO);
359 }
360 pkt->duration = 1;
361 pkt->stream_index = 0;
362 pkt->pts = get_pts(s, pkt->pos, size);
363
364 if (extradata) {
365 ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, extradata, extradata_size);
366 if (ret < 0) {
367 av_free(extradata);
368 return ret;
369 }
370 }
371
372 return ret;
373 }
374
375 static int aax_read_close(AVFormatContext *s)
376 {
377 AAXContext *a = s->priv_data;
378
379 av_freep(&a->segments);
380 av_freep(&a->xcolumns);
381 av_freep(&a->string_table);
382
383 return 0;
384 }
385
386 const AVInputFormat ff_aax_demuxer = {
387 .name = "aax",
388 .long_name = NULL_IF_CONFIG_SMALL("CRI AAX"),
389 .priv_data_size = sizeof(AAXContext),
390 .flags_internal = FF_FMT_INIT_CLEANUP,
391 .read_probe = aax_probe,
392 .read_header = aax_read_header,
393 .read_packet = aax_read_packet,
394 .read_close = aax_read_close,
395 .extensions = "aax",
396 .flags = AVFMT_GENERIC_INDEX,
397 };
398