3 * Copyright (c) 2015 Vesselin Bontchev
5 * Header parsing is borrowed from https://github.com/jteeuwen/audible project.
6 * Copyright (c) 2001-2014, Jim Teeuwen
8 * Redistribution and use in source and binary forms, with or without modification,
9 * are permitted provided that the following conditions are met:
11 * 1. Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "libavutil/avstring.h"
30 #include "libavutil/dict.h"
31 #include "libavutil/intreadwrite.h"
32 #include "libavutil/mem.h"
33 #include "libavutil/tea.h"
34 #include "libavutil/opt.h"
36 #define AA_MAGIC 1469084982 /* this identifies an audible .aa file */
37 #define MAX_TOC_ENTRIES 16
38 #define MAX_DICTIONARY_ENTRIES 128
39 #define TEA_BLOCK_SIZE 8
40 #define CHAPTER_HEADER_SIZE 8
42 #define MP3_FRAME_SIZE 104
44 typedef struct AADemuxContext
{
46 uint8_t *aa_fixed_key
;
48 int codec_second_size
;
49 int current_codec_second_size
;
51 struct AVTEA
*tea_ctx
;
53 int64_t current_chapter_size
;
54 int64_t content_start
;
59 static int get_second_size(char *codec_name
)
63 if (!strcmp(codec_name
, "mp332")) {
65 } else if (!strcmp(codec_name
, "acelp16")) {
67 } else if (!strcmp(codec_name
, "acelp85")) {
74 static int aa_read_header(AVFormatContext
*s
)
77 uint32_t toc_size
, npairs
, header_seed
= 0, start
;
78 char codec_name
[64] = {0};
80 int64_t largest_size
= -1, current_size
= -1, chapter_pos
;
84 } TOC
[MAX_TOC_ENTRIES
];
85 uint8_t header_key
[16] = {0};
86 AADemuxContext
*c
= s
->priv_data
;
87 char file_key
[2 * sizeof(c
->file_key
) + 1];
88 AVIOContext
*pb
= s
->pb
;
93 /* parse .aa header */
94 avio_skip(pb
, 4); // file size
95 avio_skip(pb
, 4); // magic string
96 toc_size
= avio_rb32(pb
); // TOC size
97 avio_skip(pb
, 4); // unidentified integer
98 if (toc_size
> MAX_TOC_ENTRIES
|| toc_size
< 2)
99 return AVERROR_INVALIDDATA
;
100 for (uint32_t i
= 0; i
< toc_size
; i
++) { // read TOC
101 avio_skip(pb
, 4); // TOC entry index
102 TOC
[i
].offset
= avio_rb32(pb
); // block offset
103 TOC
[i
].size
= avio_rb32(pb
); // block size
105 avio_skip(pb
, 24); // header termination block (ignored)
106 npairs
= avio_rb32(pb
); // read dictionary entries
107 if (npairs
> MAX_DICTIONARY_ENTRIES
)
108 return AVERROR_INVALIDDATA
;
109 for (uint32_t i
= 0; i
< npairs
; i
++) {
110 char key
[128], val
[128];
113 avio_skip(pb
, 1); // unidentified integer
114 nkey
= avio_rb32(pb
); // key string length
115 nval
= avio_rb32(pb
); // value string length
116 avio_get_str(pb
, nkey
, key
, sizeof(key
));
117 avio_get_str(pb
, nval
, val
, sizeof(val
));
118 if (!strcmp(key
, "codec")) {
119 av_log(s
, AV_LOG_DEBUG
, "Codec is <%s>\n", val
);
120 av_strlcpy(codec_name
, val
, sizeof(codec_name
));
121 } else if (!strcmp(key
, "HeaderSeed")) {
122 av_log(s
, AV_LOG_DEBUG
, "HeaderSeed is <%s>\n", val
);
123 header_seed
= atoi(val
);
124 } else if (!strcmp(key
, "HeaderKey")) { // this looks like "1234567890 1234567890 1234567890 1234567890"
125 uint32_t header_key_part
[4];
126 av_log(s
, AV_LOG_DEBUG
, "HeaderKey is <%s>\n", val
);
128 ret
= sscanf(val
, "%"SCNu32
"%"SCNu32
"%"SCNu32
"%"SCNu32
,
129 &header_key_part
[0], &header_key_part
[1], &header_key_part
[2], &header_key_part
[3]);
131 return AVERROR_INVALIDDATA
;
133 for (int idx
= 0; idx
< 4; idx
++)
134 AV_WB32(&header_key
[idx
* 4], header_key_part
[idx
]); // convert each part to BE!
135 ff_data_to_hex(key
, header_key
, sizeof(header_key
), 1);
136 av_log(s
, AV_LOG_DEBUG
, "Processed HeaderKey is %s\n", key
);
138 av_dict_set(&s
->metadata
, key
, val
, 0);
142 /* verify fixed key */
143 if (c
->aa_fixed_key_len
!= 16) {
144 av_log(s
, AV_LOG_ERROR
, "aa_fixed_key value needs to be 16 bytes!\n");
145 return AVERROR(EINVAL
);
149 if ((c
->codec_second_size
= get_second_size(codec_name
)) == -1) {
150 av_log(s
, AV_LOG_ERROR
, "unknown codec <%s>!\n", codec_name
);
151 return AVERROR(EINVAL
);
154 /* decryption key derivation */
155 c
->tea_ctx
= av_tea_alloc();
157 return AVERROR(ENOMEM
);
158 av_tea_init(c
->tea_ctx
, c
->aa_fixed_key
, 16);
159 for (int i
= 0; i
< 6; i
++)
160 AV_WB32(buf
+ 4 * i
, header_seed
+ i
);
161 av_tea_crypt(c
->tea_ctx
, buf
, buf
, 3, NULL
, 0);
162 AV_WN64(c
->file_key
, AV_RN64(buf
+ 2) ^ AV_RN64(header_key
));
163 AV_WN64(c
->file_key
+ 8, AV_RN64(buf
+ 10) ^ AV_RN64(header_key
+ 8));
164 ff_data_to_hex(file_key
, c
->file_key
, sizeof(c
->file_key
), 1);
165 av_log(s
, AV_LOG_DEBUG
, "File key is %s\n", file_key
);
166 av_tea_init(c
->tea_ctx
, c
->file_key
, 16);
169 st
= avformat_new_stream(s
, NULL
);
171 return AVERROR(ENOMEM
);
173 st
->codecpar
->codec_type
= AVMEDIA_TYPE_AUDIO
;
174 if (!strcmp(codec_name
, "mp332")) {
175 st
->codecpar
->codec_id
= AV_CODEC_ID_MP3
;
176 st
->codecpar
->sample_rate
= 22050;
177 sti
->need_parsing
= AVSTREAM_PARSE_FULL_RAW
;
178 avpriv_set_pts_info(st
, 64, 8, 32000 * TIMEPREC
);
179 // encoded audio frame is MP3_FRAME_SIZE bytes (+1 with padding, unlikely)
180 } else if (!strcmp(codec_name
, "acelp85")) {
181 st
->codecpar
->codec_id
= AV_CODEC_ID_SIPR
;
182 st
->codecpar
->block_align
= 19;
183 st
->codecpar
->ch_layout
.nb_channels
= 1;
184 st
->codecpar
->sample_rate
= 8500;
185 st
->codecpar
->bit_rate
= 8500;
186 sti
->need_parsing
= AVSTREAM_PARSE_FULL_RAW
;
187 avpriv_set_pts_info(st
, 64, 8, 8500 * TIMEPREC
);
188 } else if (!strcmp(codec_name
, "acelp16")) {
189 st
->codecpar
->codec_id
= AV_CODEC_ID_SIPR
;
190 st
->codecpar
->block_align
= 20;
191 st
->codecpar
->ch_layout
.nb_channels
= 1;
192 st
->codecpar
->sample_rate
= 16000;
193 st
->codecpar
->bit_rate
= 16000;
194 sti
->need_parsing
= AVSTREAM_PARSE_FULL_RAW
;
195 avpriv_set_pts_info(st
, 64, 8, 16000 * TIMEPREC
);
198 /* determine, and jump to audio start offset */
199 for (uint32_t i
= 1; i
< toc_size
; i
++) { // skip the first entry!
200 current_size
= TOC
[i
].size
;
201 if (current_size
> largest_size
) {
203 largest_size
= current_size
;
206 start
= TOC
[largest_idx
].offset
;
207 avio_seek(pb
, start
, SEEK_SET
);
209 // extract chapter positions. since all formats have constant bit rate, use it
210 // as time base in bytes/s, for easy stream position <-> timestamp conversion
212 c
->content_start
= start
;
213 c
->content_end
= start
+ largest_size
;
215 while ((chapter_pos
= avio_tell(pb
)) >= 0 && chapter_pos
< c
->content_end
) {
216 unsigned chapter_idx
= s
->nb_chapters
;
217 uint32_t chapter_size
= avio_rb32(pb
);
218 if (chapter_size
== 0 || avio_feof(pb
))
220 chapter_pos
-= start
+ CHAPTER_HEADER_SIZE
* chapter_idx
;
221 avio_skip(pb
, 4 + chapter_size
);
222 if (!avpriv_new_chapter(s
, chapter_idx
, st
->time_base
,
223 chapter_pos
* TIMEPREC
,
224 (chapter_pos
+ chapter_size
) * TIMEPREC
, NULL
))
225 return AVERROR(ENOMEM
);
228 st
->duration
= (largest_size
- CHAPTER_HEADER_SIZE
* s
->nb_chapters
) * TIMEPREC
;
230 avpriv_update_cur_dts(s
, st
, 0);
231 avio_seek(pb
, start
, SEEK_SET
);
232 c
->current_chapter_size
= 0;
238 static int aa_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
241 AADemuxContext
*c
= s
->priv_data
;
242 uint64_t pos
= avio_tell(s
->pb
);
244 // are we at the end of the audio content?
245 if (pos
>= c
->content_end
) {
249 // are we at the start of a chapter?
250 if (c
->current_chapter_size
== 0) {
251 c
->current_chapter_size
= avio_rb32(s
->pb
);
252 if (c
->current_chapter_size
== 0) {
255 av_log(s
, AV_LOG_DEBUG
, "Chapter %d (%" PRId64
" bytes)\n", c
->chapter_idx
, c
->current_chapter_size
);
256 c
->chapter_idx
= c
->chapter_idx
+ 1;
257 avio_skip(s
->pb
, 4); // data start offset
258 c
->current_codec_second_size
= c
->codec_second_size
;
261 // is this the last block in this chapter?
262 if (c
->current_chapter_size
/ c
->current_codec_second_size
== 0) {
263 c
->current_codec_second_size
= c
->current_chapter_size
% c
->current_codec_second_size
;
266 ret
= av_get_packet(s
->pb
, pkt
, c
->current_codec_second_size
);
267 if (ret
!= c
->current_codec_second_size
)
270 // decrypt c->current_codec_second_size bytes in blocks of TEA_BLOCK_SIZE
271 // trailing bytes are left unencrypted!
272 av_tea_crypt(c
->tea_ctx
, pkt
->data
, pkt
->data
,
273 c
->current_codec_second_size
/ TEA_BLOCK_SIZE
, NULL
, 1);
276 c
->current_chapter_size
= c
->current_chapter_size
- c
->current_codec_second_size
;
277 if (c
->current_chapter_size
<= 0)
278 c
->current_chapter_size
= 0;
280 if (c
->seek_offset
> c
->current_codec_second_size
)
281 c
->seek_offset
= 0; // ignore wrong estimate
282 pkt
->data
+= c
->seek_offset
;
283 pkt
->size
-= c
->seek_offset
;
289 static int aa_read_seek(AVFormatContext
*s
,
290 int stream_index
, int64_t timestamp
, int flags
)
292 AADemuxContext
*c
= s
->priv_data
;
294 int64_t chapter_pos
, chapter_start
, chapter_size
;
297 // find chapter containing seek timestamp
301 while (chapter_idx
< s
->nb_chapters
&& timestamp
>= s
->chapters
[chapter_idx
]->end
) {
305 if (chapter_idx
>= s
->nb_chapters
) {
306 chapter_idx
= s
->nb_chapters
- 1;
307 if (chapter_idx
< 0) return -1; // there is no chapter.
308 timestamp
= s
->chapters
[chapter_idx
]->end
;
311 ch
= s
->chapters
[chapter_idx
];
313 // sync by clamping timestamp to nearest valid block position in its chapter
314 chapter_size
= ch
->end
/ TIMEPREC
- ch
->start
/ TIMEPREC
;
315 chapter_pos
= av_rescale_rnd((timestamp
- ch
->start
) / TIMEPREC
,
316 1, c
->codec_second_size
,
317 (flags
& AVSEEK_FLAG_BACKWARD
) ? AV_ROUND_DOWN
: AV_ROUND_UP
)
318 * c
->codec_second_size
;
319 if (chapter_pos
>= chapter_size
)
320 chapter_pos
= chapter_size
;
321 chapter_start
= c
->content_start
+ (ch
->start
/ TIMEPREC
) + CHAPTER_HEADER_SIZE
* (1 + chapter_idx
);
324 avio_seek(s
->pb
, chapter_start
+ chapter_pos
, SEEK_SET
);
325 c
->current_codec_second_size
= c
->codec_second_size
;
326 c
->current_chapter_size
= chapter_size
- chapter_pos
;
327 c
->chapter_idx
= 1 + chapter_idx
;
329 // for unaligned frames, estimate offset of first frame in block (assume no padding)
330 if (s
->streams
[0]->codecpar
->codec_id
== AV_CODEC_ID_MP3
) {
331 c
->seek_offset
= (MP3_FRAME_SIZE
- chapter_pos
% MP3_FRAME_SIZE
) % MP3_FRAME_SIZE
;
334 avpriv_update_cur_dts(s
, s
->streams
[0], ch
->start
+ (chapter_pos
+ c
->seek_offset
) * TIMEPREC
);
339 static int aa_probe(const AVProbeData
*p
)
341 uint8_t *buf
= p
->buf
;
343 // first 4 bytes are file size, next 4 bytes are the magic
344 if (AV_RB32(buf
+4) != AA_MAGIC
)
347 return AVPROBE_SCORE_MAX
/ 2;
350 static int aa_read_close(AVFormatContext
*s
)
352 AADemuxContext
*c
= s
->priv_data
;
354 av_freep(&c
->tea_ctx
);
359 #define OFFSET(x) offsetof(AADemuxContext, x)
360 static const AVOption aa_options
[] = {
361 { "aa_fixed_key", // extracted from libAAX_SDK.so and AAXSDKWin.dll files!
362 "Fixed key used for handling Audible AA files", OFFSET(aa_fixed_key
),
363 AV_OPT_TYPE_BINARY
, {.str
="77214d4b196a87cd520045fd2a51d673"},
364 .flags
= AV_OPT_FLAG_DECODING_PARAM
},
368 static const AVClass aa_class
= {
370 .item_name
= av_default_item_name
,
371 .option
= aa_options
,
372 .version
= LIBAVUTIL_VERSION_INT
,
375 const FFInputFormat ff_aa_demuxer
= {
377 .p
.long_name
= NULL_IF_CONFIG_SMALL("Audible AA format files"),
378 .p
.priv_class
= &aa_class
,
379 .p
.extensions
= "aa",
380 .p
.flags
= AVFMT_NO_BYTE_SEEK
| AVFMT_NOGENSEARCH
,
381 .priv_data_size
= sizeof(AADemuxContext
),
382 .read_probe
= aa_probe
,
383 .read_header
= aa_read_header
,
384 .read_packet
= aa_read_packet
,
385 .read_seek
= aa_read_seek
,
386 .read_close
= aa_read_close
,
387 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,