3 * Copyright (c) 2020 Paul B Mahol
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "libavutil/avassert.h"
23 #include "libavutil/intreadwrite.h"
27 typedef struct AAXColumn {
35 typedef struct AAXSegment {
40 typedef struct AAXContext {
44 int64_t strings_offset;
50 int64_t schema_offset;
54 uint32_t current_segment;
60 static int aax_probe(const AVProbeData *p)
62 if (AV_RB32(p->buf) != MKBETAG('@','U','T','F'))
64 if (AV_RB32(p->buf + 4) == 0)
66 if (AV_RB16(p->buf + 8) > 1)
68 if (AV_RB32(p->buf + 28) < 1)
71 return AVPROBE_SCORE_MAX;
75 COLUMN_FLAG_NAME = 0x1,
76 COLUMN_FLAG_DEFAULT = 0x2,
77 COLUMN_FLAG_ROW = 0x4,
78 COLUMN_FLAG_UNDEFINED = 0x8 /* shouldn't exist */
82 COLUMN_TYPE_UINT8 = 0x00,
83 COLUMN_TYPE_SINT8 = 0x01,
84 COLUMN_TYPE_UINT16 = 0x02,
85 COLUMN_TYPE_SINT16 = 0x03,
86 COLUMN_TYPE_UINT32 = 0x04,
87 COLUMN_TYPE_SINT32 = 0x05,
88 COLUMN_TYPE_UINT64 = 0x06,
89 COLUMN_TYPE_SINT64 = 0x07,
90 COLUMN_TYPE_FLOAT = 0x08,
91 COLUMN_TYPE_DOUBLE = 0x09,
92 COLUMN_TYPE_STRING = 0x0a,
93 COLUMN_TYPE_VLDATA = 0x0b,
94 COLUMN_TYPE_UINT128 = 0x0c, /* for GUIDs */
95 COLUMN_TYPE_UNDEFINED = -1
98 static int64_t get_pts(AVFormatContext *s, int64_t pos, int size)
100 AAXContext *a = s->priv_data;
103 for (int seg = 0; seg < a->current_segment; seg++)
104 pts += (a->segments[seg].end - a->segments[seg].start) / size;
106 pts += ((pos - a->segments[a->current_segment].start) / size);
111 static int aax_read_header(AVFormatContext *s)
113 AAXContext *a = s->priv_data;
114 AVIOContext *pb = s->pb;
115 AVCodecParameters *par;
117 int64_t column_offset = 0;
118 int ret, extradata_size;
122 a->table_size = avio_rb32(pb) + 8LL;
123 a->version = avio_rb16(pb);
124 a->rows_offset = avio_rb16(pb) + 8LL;
125 a->strings_offset = avio_rb32(pb) + 8LL;
126 a->data_offset = avio_rb32(pb) + 8LL;
127 a->name_offset = avio_rb32(pb);
128 a->columns = avio_rb16(pb);
129 a->row_width = avio_rb16(pb);
130 a->nb_segments = avio_rb32(pb);
132 if (a->nb_segments < 1)
133 return AVERROR_INVALIDDATA;
135 a->schema_offset = 0x20;
136 a->strings_size = a->data_offset - a->strings_offset;
138 if (a->rows_offset > a->table_size ||
139 a->strings_offset > a->table_size ||
140 a->data_offset > a->table_size)
141 return AVERROR_INVALIDDATA;
142 if (a->strings_size <= 0 || a->name_offset >= a->strings_size ||
143 a->strings_size > UINT16_MAX)
144 return AVERROR_INVALIDDATA;
146 return AVERROR_INVALIDDATA;
148 a->segments = av_calloc(a->nb_segments, sizeof(*a->segments));
150 return AVERROR(ENOMEM);
152 a->xcolumns = av_calloc(a->columns, sizeof(*a->xcolumns));
154 ret = AVERROR(ENOMEM);
158 a->string_table = av_calloc(a->strings_size + 1, sizeof(*a->string_table));
159 if (!a->string_table) {
160 ret = AVERROR(ENOMEM);
164 for (int c = 0; c < a->columns; c++) {
165 uint8_t info = avio_r8(pb);
166 uint32_t offset = avio_rb32(pb);
169 if (offset >= a->strings_size) {
170 ret = AVERROR_INVALIDDATA;
174 a->xcolumns[c].flag = info >> 4;
175 a->xcolumns[c].type = info & 0x0F;
177 switch (a->xcolumns[c].type) {
178 case COLUMN_TYPE_UINT8:
179 case COLUMN_TYPE_SINT8:
182 case COLUMN_TYPE_UINT16:
183 case COLUMN_TYPE_SINT16:
186 case COLUMN_TYPE_UINT32:
187 case COLUMN_TYPE_SINT32:
188 case COLUMN_TYPE_FLOAT:
189 case COLUMN_TYPE_STRING:
192 case COLUMN_TYPE_VLDATA:
195 case COLUMN_TYPE_UINT128:
199 ret = AVERROR_INVALIDDATA;
203 a->xcolumns[c].size = value_size;
205 if (a->xcolumns[c].flag & COLUMN_FLAG_NAME)
206 a->xcolumns[c].name = a->string_table + offset;
208 if (a->xcolumns[c].flag & COLUMN_FLAG_DEFAULT) {
209 /* data is found relative to columns start */
210 a->xcolumns[c].offset = avio_tell(pb) - a->schema_offset;
211 avio_skip(pb, value_size);
214 if (a->xcolumns[c].flag & COLUMN_FLAG_ROW) {
215 /* data is found relative to row start */
216 a->xcolumns[c].offset = column_offset;
217 column_offset += value_size;
221 avio_seek(pb, a->strings_offset, SEEK_SET);
222 ret = avio_read(pb, a->string_table, a->strings_size);
223 if (ret != a->strings_size) {
230 for (int c = 0; c < a->columns; c++) {
231 int64_t data_offset = 0;
235 if (!a->xcolumns[c].name || strcmp(a->xcolumns[c].name, "data"))
238 type = a->xcolumns[c].type;
239 flag = a->xcolumns[c].flag;
240 col_offset = a->xcolumns[c].offset;
242 for (uint64_t r = 0; r < a->nb_segments; r++) {
243 if (flag & COLUMN_FLAG_DEFAULT) {
244 data_offset = a->schema_offset + col_offset;
245 } else if (flag & COLUMN_FLAG_ROW) {
246 data_offset = a->rows_offset + r * a->row_width + col_offset;
248 ret = AVERROR_INVALIDDATA;
252 avio_seek(pb, data_offset, SEEK_SET);
253 if (type == COLUMN_TYPE_VLDATA) {
256 start = avio_rb32(pb);
257 size = avio_rb32(pb);
258 a->segments[r].start = start + a->data_offset;
259 a->segments[r].end = a->segments[r].start + size;
261 ret = AVERROR_INVALIDDATA;
267 if (!a->segments[0].end) {
268 ret = AVERROR_INVALIDDATA;
272 st = avformat_new_stream(s, NULL);
274 ret = AVERROR(ENOMEM);
278 par = s->streams[0]->codecpar;
279 par->codec_type = AVMEDIA_TYPE_AUDIO;
281 codec = a->string_table + a->name_offset;
282 if (!strcmp(codec, "AAX")) {
283 par->codec_id = AV_CODEC_ID_ADPCM_ADX;
284 avio_seek(pb, a->segments[0].start, SEEK_SET);
285 if (avio_rb16(pb) != 0x8000) {
286 ret = AVERROR_INVALIDDATA;
289 extradata_size = avio_rb16(pb) + 4;
290 if (extradata_size < 12) {
291 ret = AVERROR_INVALIDDATA;
294 avio_seek(pb, -4, SEEK_CUR);
295 ret = ff_get_extradata(s, par, pb, extradata_size);
299 par->channels = AV_RB8 (par->extradata + 7);
300 par->sample_rate = AV_RB32(par->extradata + 8);
301 if (!par->channels || !par->sample_rate) {
302 ret = AVERROR_INVALIDDATA;
306 avpriv_set_pts_info(st, 64, 32, par->sample_rate);
307 /*} else if (!strcmp(codec, "HCA") ){
308 par->codec_id = AV_CODEC_ID_HCA;*/
310 ret = AVERROR_INVALIDDATA;
316 av_freep(&a->string_table);
317 av_freep(&a->xcolumns);
318 av_freep(&a->segments);
323 static int aax_read_packet(AVFormatContext *s, AVPacket *pkt)
325 AAXContext *a = s->priv_data;
326 AVCodecParameters *par = s->streams[0]->codecpar;
327 AVIOContext *pb = s->pb;
328 const int size = 18 * par->channels;
329 int ret, extradata_size = 0;
330 uint8_t *extradata = NULL;
336 pkt->pos = avio_tell(pb);
338 for (uint32_t seg = 0; seg < a->nb_segments; seg++) {
339 int64_t start = a->segments[seg].start;
340 int64_t end = a->segments[seg].end;
342 if (pkt->pos >= start && pkt->pos <= end) {
343 a->current_segment = seg;
344 if (par->codec_id == AV_CODEC_ID_ADPCM_ADX)
345 skip = (end - start) - ((end - start) / size) * size;
350 if (pkt->pos >= a->segments[a->current_segment].end - skip) {
351 if (a->current_segment + 1 == a->nb_segments)
353 a->current_segment++;
354 avio_seek(pb, a->segments[a->current_segment].start, SEEK_SET);
356 if (par->codec_id == AV_CODEC_ID_ADPCM_ADX) {
357 if (avio_rb16(pb) != 0x8000)
358 return AVERROR_INVALIDDATA;
359 extradata_size = avio_rb16(pb) + 4;
360 avio_seek(pb, -4, SEEK_CUR);
361 if (extradata_size < 12)
362 return AVERROR_INVALIDDATA;
363 extradata = av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
365 return AVERROR(ENOMEM);
366 if (avio_read(pb, extradata, extradata_size) != extradata_size) {
370 memset(extradata + extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
374 ret = av_get_packet(pb, pkt, size);
377 return ret < 0 ? ret : AVERROR(EIO);
380 pkt->stream_index = 0;
381 pkt->pts = get_pts(s, pkt->pos, size);
384 ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, extradata, extradata_size);
394 static int aax_read_close(AVFormatContext *s)
396 AAXContext *a = s->priv_data;
398 av_freep(&a->segments);
399 av_freep(&a->xcolumns);
400 av_freep(&a->string_table);
405 AVInputFormat ff_aax_demuxer = {
407 .long_name = NULL_IF_CONFIG_SMALL("CRI AAX"),
408 .priv_data_size = sizeof(AAXContext),
409 .read_probe = aax_probe,
410 .read_header = aax_read_header,
411 .read_packet = aax_read_packet,
412 .read_close = aax_read_close,
414 .flags = AVFMT_GENERIC_INDEX,