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