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/intreadwrite.h"
23 #include "libavutil/mem.h"
25 #include "avio_internal.h"
29 typedef struct AAXColumn
{
37 typedef struct AAXSegment
{
42 typedef struct AAXContext
{
46 int64_t strings_offset
;
52 int64_t schema_offset
;
56 uint32_t current_segment
;
62 static int aax_probe(const AVProbeData
*p
)
64 if (AV_RB32(p
->buf
) != MKBETAG('@','U','T','F'))
66 if (AV_RB32(p
->buf
+ 4) == 0)
68 if (AV_RB16(p
->buf
+ 8) > 1)
70 if (AV_RB32(p
->buf
+ 28) < 1)
73 return AVPROBE_SCORE_MAX
;
77 COLUMN_FLAG_NAME
= 0x1,
78 COLUMN_FLAG_DEFAULT
= 0x2,
79 COLUMN_FLAG_ROW
= 0x4,
80 COLUMN_FLAG_UNDEFINED
= 0x8 /* shouldn't exist */
84 COLUMN_TYPE_UINT8
= 0x00,
85 COLUMN_TYPE_SINT8
= 0x01,
86 COLUMN_TYPE_UINT16
= 0x02,
87 COLUMN_TYPE_SINT16
= 0x03,
88 COLUMN_TYPE_UINT32
= 0x04,
89 COLUMN_TYPE_SINT32
= 0x05,
90 COLUMN_TYPE_UINT64
= 0x06,
91 COLUMN_TYPE_SINT64
= 0x07,
92 COLUMN_TYPE_FLOAT
= 0x08,
93 COLUMN_TYPE_DOUBLE
= 0x09,
94 COLUMN_TYPE_STRING
= 0x0a,
95 COLUMN_TYPE_VLDATA
= 0x0b,
96 COLUMN_TYPE_UINT128
= 0x0c, /* for GUIDs */
97 COLUMN_TYPE_UNDEFINED
= -1
100 static int64_t get_pts(AVFormatContext
*s
, int64_t pos
, int size
)
102 AAXContext
*a
= s
->priv_data
;
105 for (int seg
= 0; seg
< a
->current_segment
; seg
++)
106 pts
+= (a
->segments
[seg
].end
- a
->segments
[seg
].start
) / size
;
108 pts
+= ((pos
- a
->segments
[a
->current_segment
].start
) / size
);
113 static int aax_read_header(AVFormatContext
*s
)
115 AAXContext
*a
= s
->priv_data
;
116 AVIOContext
*pb
= s
->pb
;
117 AVCodecParameters
*par
;
119 int64_t column_offset
= 0;
120 int ret
, extradata_size
;
125 a
->table_size
= avio_rb32(pb
) + 8LL;
126 a
->version
= avio_rb16(pb
);
127 a
->rows_offset
= avio_rb16(pb
) + 8LL;
128 a
->strings_offset
= avio_rb32(pb
) + 8LL;
129 a
->data_offset
= avio_rb32(pb
) + 8LL;
130 a
->name_offset
= avio_rb32(pb
);
131 a
->columns
= avio_rb16(pb
);
132 a
->row_width
= avio_rb16(pb
);
133 a
->nb_segments
= avio_rb32(pb
);
135 if (a
->nb_segments
< 1)
136 return AVERROR_INVALIDDATA
;
138 a
->schema_offset
= 0x20;
139 a
->strings_size
= a
->data_offset
- a
->strings_offset
;
141 if (a
->rows_offset
> a
->table_size
||
142 a
->strings_offset
> a
->table_size
||
143 a
->data_offset
> a
->table_size
)
144 return AVERROR_INVALIDDATA
;
145 if (a
->strings_size
<= 0 || a
->name_offset
>= a
->strings_size
||
146 a
->strings_size
> UINT16_MAX
)
147 return AVERROR_INVALIDDATA
;
149 return AVERROR_INVALIDDATA
;
151 a
->segments
= av_calloc(a
->nb_segments
, sizeof(*a
->segments
));
153 return AVERROR(ENOMEM
);
155 a
->xcolumns
= av_calloc(a
->columns
, sizeof(*a
->xcolumns
));
157 return AVERROR(ENOMEM
);
159 a
->string_table
= av_calloc(a
->strings_size
+ 1, sizeof(*a
->string_table
));
160 if (!a
->string_table
)
161 return AVERROR(ENOMEM
);
163 for (int c
= 0; c
< a
->columns
; c
++) {
164 uint8_t info
= avio_r8(pb
);
165 uint32_t offset
= avio_rb32(pb
);
168 if (offset
>= a
->strings_size
)
169 return AVERROR_INVALIDDATA
;
171 a
->xcolumns
[c
].flag
= info
>> 4;
172 a
->xcolumns
[c
].type
= info
& 0x0F;
174 switch (a
->xcolumns
[c
].type
) {
175 case COLUMN_TYPE_UINT8
:
176 case COLUMN_TYPE_SINT8
:
179 case COLUMN_TYPE_UINT16
:
180 case COLUMN_TYPE_SINT16
:
183 case COLUMN_TYPE_UINT32
:
184 case COLUMN_TYPE_SINT32
:
185 case COLUMN_TYPE_FLOAT
:
186 case COLUMN_TYPE_STRING
:
189 case COLUMN_TYPE_VLDATA
:
192 case COLUMN_TYPE_UINT128
:
196 return AVERROR_INVALIDDATA
;
199 a
->xcolumns
[c
].size
= value_size
;
201 if (a
->xcolumns
[c
].flag
& COLUMN_FLAG_NAME
)
202 a
->xcolumns
[c
].name
= a
->string_table
+ offset
;
204 if (a
->xcolumns
[c
].flag
& COLUMN_FLAG_DEFAULT
) {
205 /* data is found relative to columns start */
206 a
->xcolumns
[c
].offset
= avio_tell(pb
) - a
->schema_offset
;
207 avio_skip(pb
, value_size
);
210 if (a
->xcolumns
[c
].flag
& COLUMN_FLAG_ROW
) {
211 /* data is found relative to row start */
212 a
->xcolumns
[c
].offset
= column_offset
;
213 column_offset
+= value_size
;
217 ret
= ret64
= avio_seek(pb
, a
->strings_offset
, SEEK_SET
);
221 ret
= ffio_read_size(pb
, a
->string_table
, a
->strings_size
);
225 for (int c
= 0; c
< a
->columns
; c
++) {
226 int64_t data_offset
= 0;
230 if (!a
->xcolumns
[c
].name
|| strcmp(a
->xcolumns
[c
].name
, "data"))
233 type
= a
->xcolumns
[c
].type
;
234 flag
= a
->xcolumns
[c
].flag
;
235 col_offset
= a
->xcolumns
[c
].offset
;
237 for (uint64_t r
= 0; r
< a
->nb_segments
; r
++) {
238 if (flag
& COLUMN_FLAG_DEFAULT
) {
239 data_offset
= a
->schema_offset
+ col_offset
;
240 } else if (flag
& COLUMN_FLAG_ROW
) {
241 data_offset
= a
->rows_offset
+ r
* a
->row_width
+ col_offset
;
243 return AVERROR_INVALIDDATA
;
245 ret
= ret64
= avio_seek(pb
, data_offset
, SEEK_SET
);
249 if (type
== COLUMN_TYPE_VLDATA
) {
252 start
= avio_rb32(pb
);
253 size
= avio_rb32(pb
);
255 return AVERROR_INVALIDDATA
;
256 a
->segments
[r
].start
= start
+ a
->data_offset
;
257 a
->segments
[r
].end
= a
->segments
[r
].start
+ size
;
259 a
->segments
[r
].start
< a
->segments
[r
-1].end
&&
260 a
->segments
[r
].end
> a
->segments
[r
-1].start
)
261 return AVERROR_INVALIDDATA
;
263 return AVERROR_INVALIDDATA
;
267 if (!a
->segments
[0].end
)
268 return AVERROR_INVALIDDATA
;
270 st
= avformat_new_stream(s
, NULL
);
272 return AVERROR(ENOMEM
);
274 par
= s
->streams
[0]->codecpar
;
275 par
->codec_type
= AVMEDIA_TYPE_AUDIO
;
277 codec
= a
->string_table
+ a
->name_offset
;
278 if (!strcmp(codec
, "AAX")) {
279 par
->codec_id
= AV_CODEC_ID_ADPCM_ADX
;
280 ret64
= avio_seek(pb
, a
->segments
[0].start
, SEEK_SET
);
281 if (ret64
< 0 || avio_rb16(pb
) != 0x8000)
282 return AVERROR_INVALIDDATA
;
283 extradata_size
= avio_rb16(pb
) + 4;
284 if (extradata_size
< 12)
285 return AVERROR_INVALIDDATA
;
286 avio_seek(pb
, -4, SEEK_CUR
);
287 ret
= ff_get_extradata(s
, par
, pb
, extradata_size
);
290 par
->ch_layout
.nb_channels
= AV_RB8 (par
->extradata
+ 7);
291 par
->sample_rate
= AV_RB32(par
->extradata
+ 8);
292 if (!par
->ch_layout
.nb_channels
|| !par
->sample_rate
)
293 return AVERROR_INVALIDDATA
;
295 avpriv_set_pts_info(st
, 64, 32, par
->sample_rate
);
296 /*} else if (!strcmp(codec, "HCA") ){
297 par->codec_id = AV_CODEC_ID_HCA;*/
299 return AVERROR_INVALIDDATA
;
305 static int aax_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
307 AAXContext
*a
= s
->priv_data
;
308 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
309 AVIOContext
*pb
= s
->pb
;
310 const int size
= 18 * par
->ch_layout
.nb_channels
;
311 int ret
, extradata_size
= 0;
312 uint8_t *extradata
= NULL
;
318 pkt
->pos
= avio_tell(pb
);
320 for (uint32_t seg
= 0; seg
< a
->nb_segments
; seg
++) {
321 int64_t start
= a
->segments
[seg
].start
;
322 int64_t end
= a
->segments
[seg
].end
;
324 if (pkt
->pos
>= start
&& pkt
->pos
<= end
) {
325 a
->current_segment
= seg
;
326 if (par
->codec_id
== AV_CODEC_ID_ADPCM_ADX
)
327 skip
= (end
- start
) - ((end
- start
) / size
) * size
;
332 if (pkt
->pos
>= a
->segments
[a
->current_segment
].end
- skip
) {
333 if (a
->current_segment
+ 1 == a
->nb_segments
)
335 a
->current_segment
++;
336 avio_seek(pb
, a
->segments
[a
->current_segment
].start
, SEEK_SET
);
338 if (par
->codec_id
== AV_CODEC_ID_ADPCM_ADX
) {
339 if (avio_rb16(pb
) != 0x8000)
340 return AVERROR_INVALIDDATA
;
341 extradata_size
= avio_rb16(pb
) + 4;
342 avio_seek(pb
, -4, SEEK_CUR
);
343 if (extradata_size
< 12)
344 return AVERROR_INVALIDDATA
;
345 extradata
= av_malloc(extradata_size
+ AV_INPUT_BUFFER_PADDING_SIZE
);
347 return AVERROR(ENOMEM
);
348 ret
= ffio_read_size(pb
, extradata
, extradata_size
);
353 memset(extradata
+ extradata_size
, 0, AV_INPUT_BUFFER_PADDING_SIZE
);
357 ret
= av_get_packet(pb
, pkt
, size
);
360 return ret
< 0 ? ret
: AVERROR_INVALIDDATA
;
363 pkt
->stream_index
= 0;
364 pkt
->pts
= get_pts(s
, pkt
->pos
, size
);
367 ret
= av_packet_add_side_data(pkt
, AV_PKT_DATA_NEW_EXTRADATA
, extradata
, extradata_size
);
377 static int aax_read_close(AVFormatContext
*s
)
379 AAXContext
*a
= s
->priv_data
;
381 av_freep(&a
->segments
);
382 av_freep(&a
->xcolumns
);
383 av_freep(&a
->string_table
);
388 const FFInputFormat ff_aax_demuxer
= {
390 .p
.long_name
= NULL_IF_CONFIG_SMALL("CRI AAX"),
391 .p
.extensions
= "aax",
392 .p
.flags
= AVFMT_GENERIC_INDEX
,
393 .priv_data_size
= sizeof(AAXContext
),
394 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,
395 .read_probe
= aax_probe
,
396 .read_header
= aax_read_header
,
397 .read_packet
= aax_read_packet
,
398 .read_close
= aax_read_close
,