faad.c.nocompile 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. /*
  2. * Squeezelite - lightweight headless squeezebox emulator
  3. *
  4. * (c) Adrian Smith 2012-2015, triode1@btinternet.com
  5. * Ralph Irving 2015-2017, ralph_irving@hotmail.com
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program 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
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. #include "squeezelite.h"
  22. #include <neaacdec.h>
  23. #if BYTES_PER_FRAME == 4
  24. #define ALIGN(n) (n)
  25. #else
  26. #define ALIGN(n) (n << 8)
  27. #endif
  28. #define WRAPBUF_LEN 2048
  29. struct chunk_table {
  30. u32_t sample, offset;
  31. };
  32. struct faad {
  33. NeAACDecHandle hAac;
  34. u8_t type;
  35. // following used for mp4 only
  36. u32_t consume;
  37. u32_t pos;
  38. u32_t sample;
  39. u32_t nextchunk;
  40. void *stsc;
  41. u32_t skip;
  42. u64_t samples;
  43. u64_t sttssamples;
  44. bool empty;
  45. struct chunk_table *chunkinfo;
  46. // faad symbols to be dynamically loaded
  47. #if !LINKALL
  48. NeAACDecConfigurationPtr (* NeAACDecGetCurrentConfiguration)(NeAACDecHandle);
  49. unsigned char (* NeAACDecSetConfiguration)(NeAACDecHandle, NeAACDecConfigurationPtr);
  50. NeAACDecHandle (* NeAACDecOpen)(void);
  51. void (* NeAACDecClose)(NeAACDecHandle);
  52. long (* NeAACDecInit)(NeAACDecHandle, unsigned char *, unsigned long, unsigned long *, unsigned char *);
  53. char (* NeAACDecInit2)(NeAACDecHandle, unsigned char *pBuffer, unsigned long, unsigned long *, unsigned char *);
  54. void *(* NeAACDecDecode)(NeAACDecHandle, NeAACDecFrameInfo *, unsigned char *, unsigned long);
  55. char *(* NeAACDecGetErrorMessage)(unsigned char);
  56. #endif
  57. };
  58. static struct faad *a;
  59. extern log_level loglevel;
  60. extern struct buffer *streambuf;
  61. extern struct buffer *outputbuf;
  62. extern struct streamstate stream;
  63. extern struct outputstate output;
  64. extern struct decodestate decode;
  65. extern struct processstate process;
  66. #define LOCK_S mutex_lock(streambuf->mutex)
  67. #define UNLOCK_S mutex_unlock(streambuf->mutex)
  68. #define LOCK_O mutex_lock(outputbuf->mutex)
  69. #define UNLOCK_O mutex_unlock(outputbuf->mutex)
  70. #if PROCESS
  71. #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex)
  72. #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex)
  73. #define IF_DIRECT(x) if (decode.direct) { x }
  74. #define IF_PROCESS(x) if (!decode.direct) { x }
  75. #else
  76. #define LOCK_O_direct mutex_lock(outputbuf->mutex)
  77. #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex)
  78. #define IF_DIRECT(x) { x }
  79. #define IF_PROCESS(x)
  80. #endif
  81. #if LINKALL
  82. #define NEAAC(h, fn, ...) (NeAACDec ## fn)(__VA_ARGS__)
  83. #else
  84. #define NEAAC(h, fn, ...) (h)->NeAACDec##fn(__VA_ARGS__)
  85. #endif
  86. // minimal code for mp4 file parsing to extract audio config and find media data
  87. // adapted from faad2/common/mp4ff
  88. u32_t mp4_desc_length(u8_t **buf) {
  89. u8_t b;
  90. u8_t num_bytes = 0;
  91. u32_t length = 0;
  92. do {
  93. b = **buf;
  94. *buf += 1;
  95. num_bytes++;
  96. length = (length << 7) | (b & 0x7f);
  97. } while ((b & 0x80) && num_bytes < 4);
  98. return length;
  99. }
  100. // read mp4 header to extract config data
  101. static int read_mp4_header(unsigned long *samplerate_p, unsigned char *channels_p) {
  102. size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
  103. char type[5];
  104. u32_t len;
  105. while (bytes >= 8) {
  106. // count trak to find the first playable one
  107. static unsigned trak, play;
  108. u32_t consume;
  109. len = unpackN((u32_t *)streambuf->readp);
  110. memcpy(type, streambuf->readp + 4, 4);
  111. type[4] = '\0';
  112. if (!strcmp(type, "moov")) {
  113. trak = 0;
  114. play = 0;
  115. }
  116. if (!strcmp(type, "trak")) {
  117. trak++;
  118. }
  119. // extract audio config from within esds and pass to DecInit2
  120. if (!strcmp(type, "esds") && bytes > len) {
  121. unsigned config_len;
  122. u8_t *ptr = streambuf->readp + 12;
  123. if (*ptr++ == 0x03) {
  124. mp4_desc_length(&ptr);
  125. ptr += 4;
  126. } else {
  127. ptr += 3;
  128. }
  129. mp4_desc_length(&ptr);
  130. ptr += 13;
  131. if (*ptr++ != 0x05) {
  132. LOG_WARN("error parsing esds");
  133. return -1;
  134. }
  135. config_len = mp4_desc_length(&ptr);
  136. if (NEAAC(a, Init2, a->hAac, ptr, config_len, samplerate_p, channels_p) == 0) {
  137. LOG_DEBUG("playable aac track: %u", trak);
  138. play = trak;
  139. }
  140. }
  141. // extract the total number of samples from stts
  142. if (!strcmp(type, "stts") && bytes > len) {
  143. u32_t i;
  144. u8_t *ptr = streambuf->readp + 12;
  145. u32_t entries = unpackN((u32_t *)ptr);
  146. ptr += 4;
  147. for (i = 0; i < entries; ++i) {
  148. u32_t count = unpackN((u32_t *)ptr);
  149. u32_t size = unpackN((u32_t *)(ptr + 4));
  150. a->sttssamples += count * size;
  151. ptr += 8;
  152. }
  153. LOG_DEBUG("total number of samples contained in stts: " FMT_u64, a->sttssamples);
  154. }
  155. // stash sample to chunk info, assume it comes before stco
  156. if (!strcmp(type, "stsc") && bytes > len && !a->chunkinfo) {
  157. a->stsc = malloc(len - 12);
  158. if (a->stsc == NULL) {
  159. LOG_WARN("malloc fail");
  160. return -1;
  161. }
  162. memcpy(a->stsc, streambuf->readp + 12, len - 12);
  163. }
  164. // build offsets table from stco and stored stsc
  165. if (!strcmp(type, "stco") && bytes > len && play == trak) {
  166. u32_t i;
  167. // extract chunk offsets
  168. u8_t *ptr = streambuf->readp + 12;
  169. u32_t entries = unpackN((u32_t *)ptr);
  170. ptr += 4;
  171. a->chunkinfo = malloc(sizeof(struct chunk_table) * (entries + 1));
  172. if (a->chunkinfo == NULL) {
  173. LOG_WARN("malloc fail");
  174. return -1;
  175. }
  176. for (i = 0; i < entries; ++i) {
  177. a->chunkinfo[i].offset = unpackN((u32_t *)ptr);
  178. a->chunkinfo[i].sample = 0;
  179. ptr += 4;
  180. }
  181. a->chunkinfo[i].sample = 0;
  182. a->chunkinfo[i].offset = 0;
  183. // fill in first sample id for each chunk from stored stsc
  184. if (a->stsc) {
  185. u32_t stsc_entries = unpackN((u32_t *)a->stsc);
  186. u32_t sample = 0;
  187. u32_t last = 0, last_samples = 0;
  188. u8_t *ptr = (u8_t *)a->stsc + 4;
  189. while (stsc_entries--) {
  190. u32_t first = unpackN((u32_t *)ptr);
  191. u32_t samples = unpackN((u32_t *)(ptr + 4));
  192. if (last) {
  193. for (i = last - 1; i < first - 1; ++i) {
  194. a->chunkinfo[i].sample = sample;
  195. sample += last_samples;
  196. }
  197. }
  198. if (stsc_entries == 0) {
  199. for (i = first - 1; i < entries; ++i) {
  200. a->chunkinfo[i].sample = sample;
  201. sample += samples;
  202. }
  203. }
  204. last = first;
  205. last_samples = samples;
  206. ptr += 12;
  207. }
  208. free(a->stsc);
  209. a->stsc = NULL;
  210. }
  211. }
  212. // found media data, advance to start of first chunk and return
  213. if (!strcmp(type, "mdat")) {
  214. _buf_inc_readp(streambuf, 8);
  215. a->pos += 8;
  216. bytes -= 8;
  217. if (play) {
  218. LOG_DEBUG("type: mdat len: %u pos: %u", len, a->pos);
  219. if (a->chunkinfo && a->chunkinfo[0].offset > a->pos) {
  220. u32_t skip = a->chunkinfo[0].offset - a->pos;
  221. LOG_DEBUG("skipping: %u", skip);
  222. if (skip <= bytes) {
  223. _buf_inc_readp(streambuf, skip);
  224. a->pos += skip;
  225. } else {
  226. a->consume = skip;
  227. }
  228. }
  229. a->sample = a->nextchunk = 1;
  230. return 1;
  231. } else {
  232. LOG_DEBUG("type: mdat len: %u, no playable track found", len);
  233. return -1;
  234. }
  235. }
  236. // parse key-value atoms within ilst ---- entries to get encoder padding within iTunSMPB entry for gapless
  237. if (!strcmp(type, "----") && bytes > len) {
  238. u8_t *ptr = streambuf->readp + 8;
  239. u32_t remain = len - 8, size;
  240. if (!memcmp(ptr + 4, "mean", 4) && (size = unpackN((u32_t *)ptr)) < remain) {
  241. ptr += size; remain -= size;
  242. }
  243. if (!memcmp(ptr + 4, "name", 4) && (size = unpackN((u32_t *)ptr)) < remain && !memcmp(ptr + 12, "iTunSMPB", 8)) {
  244. ptr += size; remain -= size;
  245. }
  246. if (!memcmp(ptr + 4, "data", 4) && remain > 16 + 48) {
  247. // data is stored as hex strings: 0 start end samples
  248. u32_t b, c; u64_t d;
  249. if (sscanf((const char *)(ptr + 16), "%x %x %x " FMT_x64, &b, &b, &c, &d) == 4) {
  250. LOG_DEBUG("iTunSMPB start: %u end: %u samples: " FMT_u64, b, c, d);
  251. if (a->sttssamples && a->sttssamples < b + c + d) {
  252. LOG_DEBUG("reducing samples as stts count is less");
  253. d = a->sttssamples - (b + c);
  254. }
  255. a->skip = b;
  256. a->samples = d;
  257. }
  258. }
  259. }
  260. // default to consuming entire box
  261. consume = len;
  262. // read into these boxes so reduce consume
  263. if (!strcmp(type, "moov") || !strcmp(type, "trak") || !strcmp(type, "mdia") || !strcmp(type, "minf") || !strcmp(type, "stbl") ||
  264. !strcmp(type, "udta") || !strcmp(type, "ilst")) {
  265. consume = 8;
  266. }
  267. // special cases which mix mix data in the enclosing box which we want to read into
  268. if (!strcmp(type, "stsd")) consume = 16;
  269. if (!strcmp(type, "mp4a")) consume = 36;
  270. if (!strcmp(type, "meta")) consume = 12;
  271. // consume rest of box if it has been parsed (all in the buffer) or is not one we want to parse
  272. if (bytes >= consume) {
  273. LOG_DEBUG("type: %s len: %u consume: %u", type, len, consume);
  274. _buf_inc_readp(streambuf, consume);
  275. a->pos += consume;
  276. bytes -= consume;
  277. } else if ( !(!strcmp(type, "esds") || !strcmp(type, "stts") || !strcmp(type, "stsc") ||
  278. !strcmp(type, "stco") || !strcmp(type, "----")) ) {
  279. LOG_DEBUG("type: %s len: %u consume: %u - partial consume: %u", type, len, consume, bytes);
  280. _buf_inc_readp(streambuf, bytes);
  281. a->pos += bytes;
  282. a->consume = consume - bytes;
  283. break;
  284. } else {
  285. break;
  286. }
  287. }
  288. return 0;
  289. }
  290. static decode_state faad_decode(void) {
  291. size_t bytes_total;
  292. size_t bytes_wrap;
  293. static NeAACDecFrameInfo info;
  294. ISAMPLE_T *iptr;
  295. bool endstream;
  296. frames_t frames;
  297. LOCK_S;
  298. bytes_total = _buf_used(streambuf);
  299. bytes_wrap = min(bytes_total, _buf_cont_read(streambuf));
  300. if (stream.state <= DISCONNECT && !bytes_total) {
  301. UNLOCK_S;
  302. return DECODE_COMPLETE;
  303. }
  304. if (a->consume) {
  305. u32_t consume = min(a->consume, bytes_wrap);
  306. LOG_DEBUG("consume: %u of %u", consume, a->consume);
  307. _buf_inc_readp(streambuf, consume);
  308. a->pos += consume;
  309. a->consume -= consume;
  310. UNLOCK_S;
  311. return DECODE_RUNNING;
  312. }
  313. if (decode.new_stream) {
  314. int found = 0;
  315. static unsigned char channels;
  316. static unsigned long samplerate;
  317. if (a->type == '2') {
  318. // adts stream - seek for header
  319. while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) {
  320. _buf_inc_readp(streambuf, 1);
  321. bytes_total--;
  322. bytes_wrap--;
  323. }
  324. if (bytes_wrap >= 2) {
  325. long n = NEAAC(a, Init, a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels);
  326. if (n < 0) {
  327. found = -1;
  328. } else {
  329. _buf_inc_readp(streambuf, n);
  330. found = 1;
  331. }
  332. }
  333. } else {
  334. // mp4 - read header
  335. found = read_mp4_header(&samplerate, &channels);
  336. }
  337. if (found == 1) {
  338. LOG_INFO("samplerate: %u channels: %u", samplerate, channels);
  339. bytes_total = _buf_used(streambuf);
  340. bytes_wrap = min(bytes_total, _buf_cont_read(streambuf));
  341. LOCK_O;
  342. LOG_INFO("setting track_start");
  343. output.next_sample_rate = decode_newstream(samplerate, output.supported_rates);
  344. IF_DSD( output.next_fmt = PCM; )
  345. output.track_start = outputbuf->writep;
  346. if (output.fade_mode) _checkfade(true);
  347. decode.new_stream = false;
  348. UNLOCK_O;
  349. } else if (found == -1) {
  350. LOG_WARN("error reading stream header");
  351. UNLOCK_S;
  352. return DECODE_ERROR;
  353. } else {
  354. // not finished header parsing come back next time
  355. UNLOCK_S;
  356. return DECODE_RUNNING;
  357. }
  358. }
  359. if (bytes_wrap < WRAPBUF_LEN && bytes_total > WRAPBUF_LEN) {
  360. // make a local copy of frames which may have wrapped round the end of streambuf
  361. static u8_t buf[WRAPBUF_LEN];
  362. memcpy(buf, streambuf->readp, bytes_wrap);
  363. memcpy(buf + bytes_wrap, streambuf->buf, WRAPBUF_LEN - bytes_wrap);
  364. iptr = NEAAC(a, Decode, a->hAac, &info, buf, WRAPBUF_LEN);
  365. } else {
  366. iptr = NEAAC(a, Decode, a->hAac, &info, streambuf->readp, bytes_wrap);
  367. }
  368. if (info.error) {
  369. LOG_WARN("error: %u %s", info.error, NEAAC(a, GetErrorMessage, info.error));
  370. }
  371. endstream = false;
  372. // mp4 end of chunk - skip to next offset
  373. if (a->chunkinfo && a->chunkinfo[a->nextchunk].offset && a->sample++ == a->chunkinfo[a->nextchunk].sample) {
  374. if (a->chunkinfo[a->nextchunk].offset > a->pos) {
  375. u32_t skip = a->chunkinfo[a->nextchunk].offset - a->pos;
  376. if (skip != info.bytesconsumed) {
  377. LOG_DEBUG("skipping to next chunk pos: %u consumed: %u != skip: %u", a->pos, info.bytesconsumed, skip);
  378. }
  379. if (bytes_total >= skip) {
  380. _buf_inc_readp(streambuf, skip);
  381. a->pos += skip;
  382. } else {
  383. a->consume = skip;
  384. }
  385. a->nextchunk++;
  386. } else {
  387. LOG_ERROR("error: need to skip backwards!");
  388. endstream = true;
  389. }
  390. // adts and mp4 when not at end of chunk
  391. } else if (info.bytesconsumed != 0) {
  392. _buf_inc_readp(streambuf, info.bytesconsumed);
  393. a->pos += info.bytesconsumed;
  394. // error which doesn't advance streambuf - end
  395. } else {
  396. endstream = true;
  397. }
  398. UNLOCK_S;
  399. if (endstream) {
  400. LOG_WARN("unable to decode further");
  401. return DECODE_ERROR;
  402. }
  403. if (!info.samples) {
  404. a->empty = true;
  405. return DECODE_RUNNING;
  406. }
  407. frames = info.samples / info.channels;
  408. if (a->skip) {
  409. u32_t skip;
  410. if (a->empty) {
  411. a->empty = false;
  412. a->skip -= frames;
  413. LOG_DEBUG("gapless: first frame empty, skipped %u frames at start", frames);
  414. }
  415. skip = min(frames, a->skip);
  416. LOG_DEBUG("gapless: skipping %u frames at start", skip);
  417. frames -= skip;
  418. a->skip -= skip;
  419. iptr += skip * info.channels;
  420. }
  421. if (a->samples) {
  422. if (a->samples < frames) {
  423. LOG_DEBUG("gapless: trimming %u frames from end", frames - a->samples);
  424. frames = (frames_t)a->samples;
  425. }
  426. a->samples -= frames;
  427. }
  428. LOG_SDEBUG("write %u frames", frames);
  429. LOCK_O_direct;
  430. while (frames > 0) {
  431. frames_t f;
  432. frames_t count;
  433. ISAMPLE_T *optr;
  434. IF_DIRECT(
  435. f = _buf_cont_write(outputbuf) / BYTES_PER_FRAME;
  436. optr = (ISAMPLE_T *)outputbuf->writep;
  437. );
  438. IF_PROCESS(
  439. f = process.max_in_frames;
  440. optr = (ISAMPLE_T *)process.inbuf;
  441. );
  442. f = min(f, frames);
  443. count = f;
  444. if (info.channels == 2) {
  445. #if BYTES_PER_FRAME == 4
  446. memcpy(optr, iptr, count * BYTES_PER_FRAME);
  447. iptr += count * 2;
  448. #else
  449. while (count--) {
  450. *optr++ = *iptr++ << 8;
  451. *optr++ = *iptr++ << 8;
  452. }
  453. #endif
  454. } else if (info.channels == 1) {
  455. while (count--) {
  456. *optr++ = ALIGN(*iptr);
  457. *optr++ = ALIGN(*iptr++);
  458. }
  459. } else {
  460. LOG_WARN("unsupported number of channels");
  461. }
  462. frames -= f;
  463. IF_DIRECT(
  464. _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME);
  465. );
  466. IF_PROCESS(
  467. process.in_frames = f;
  468. if (frames) LOG_ERROR("unhandled case");
  469. );
  470. }
  471. UNLOCK_O_direct;
  472. return DECODE_RUNNING;
  473. }
  474. static void faad_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
  475. NeAACDecConfigurationPtr conf;
  476. LOG_INFO("opening %s stream", size == '2' ? "adts" : "mp4");
  477. a->type = size;
  478. a->pos = a->consume = a->sample = a->nextchunk = 0;
  479. if (a->chunkinfo) {
  480. free(a->chunkinfo);
  481. }
  482. if (a->stsc) {
  483. free(a->stsc);
  484. }
  485. a->chunkinfo = NULL;
  486. a->stsc = NULL;
  487. a->skip = 0;
  488. a->samples = 0;
  489. a->sttssamples = 0;
  490. a->empty = false;
  491. if (a->hAac) {
  492. NEAAC(a, Close, a->hAac);
  493. }
  494. a->hAac = NEAAC(a, Open);
  495. conf = NEAAC(a, GetCurrentConfiguration, a->hAac);
  496. #if BYTES_PER_FRAME == 4
  497. conf->outputFormat = FAAD_FMT_16BIT;
  498. #else
  499. conf->outputFormat = FAAD_FMT_24BIT;
  500. #endif
  501. conf->defSampleRate = 44100;
  502. conf->downMatrix = 1;
  503. if (!NEAAC(a, SetConfiguration, a->hAac, conf)) {
  504. LOG_WARN("error setting config");
  505. };
  506. }
  507. static void faad_close(void) {
  508. NEAAC(a, Close, a->hAac);
  509. a->hAac = NULL;
  510. if (a->chunkinfo) {
  511. free(a->chunkinfo);
  512. a->chunkinfo = NULL;
  513. }
  514. if (a->stsc) {
  515. free(a->stsc);
  516. a->stsc = NULL;
  517. }
  518. }
  519. static bool load_faad() {
  520. #if !LINKALL
  521. void *handle = dlopen(LIBFAAD, RTLD_NOW);
  522. char *err;
  523. if (!handle) {
  524. LOG_INFO("dlerror: %s", dlerror());
  525. return false;
  526. }
  527. a->NeAACDecGetCurrentConfiguration = dlsym(handle, "NeAACDecGetCurrentConfiguration");
  528. a->NeAACDecSetConfiguration = dlsym(handle, "NeAACDecSetConfiguration");
  529. a->NeAACDecOpen = dlsym(handle, "NeAACDecOpen");
  530. a->NeAACDecClose = dlsym(handle, "NeAACDecClose");
  531. a->NeAACDecInit = dlsym(handle, "NeAACDecInit");
  532. a->NeAACDecInit2 = dlsym(handle, "NeAACDecInit2");
  533. a->NeAACDecDecode = dlsym(handle, "NeAACDecDecode");
  534. a->NeAACDecGetErrorMessage = dlsym(handle, "NeAACDecGetErrorMessage");
  535. if ((err = dlerror()) != NULL) {
  536. LOG_INFO("dlerror: %s", err);
  537. return false;
  538. }
  539. LOG_INFO("loaded "LIBFAAD"");
  540. #endif
  541. return true;
  542. }
  543. struct codec *register_faad(void) {
  544. static struct codec ret = {
  545. 'a', // id
  546. "aac", // types
  547. WRAPBUF_LEN, // min read
  548. 20480, // min space
  549. faad_open, // open
  550. faad_close, // close
  551. faad_decode, // decode
  552. };
  553. a = malloc(sizeof(struct faad));
  554. if (!a) {
  555. return NULL;
  556. }
  557. a->hAac = NULL;
  558. a->chunkinfo = NULL;
  559. a->stsc = NULL;
  560. if (!load_faad()) {
  561. return NULL;
  562. }
  563. LOG_INFO("using faad to decode aac");
  564. return &ret;
  565. }