1 /*******************************************************************************
2 * Goggles Audio Player Library *
3 ********************************************************************************
4 * Copyright (C) 2010-2021 by Sander Jansen. All Rights Reserved *
5 * --- *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see http://www.gnu.org/licenses. *
18 ********************************************************************************/
19 #include "ap_defs.h"
20 #include "ap_event_private.h"
21 #include "ap_packet.h"
22 #include "ap_reader_plugin.h"
23 #include "ap_input_plugin.h"
24
25 #include <cstdint>
26
27 namespace ap {
28
29 #ifdef HAVE_FAAD
30 extern FXbool ap_parse_aac_specific_config(const FXuchar * data, FXuint length, FXushort & samples_per_frame, FXbool & upsampled, AudioFormat & af);
31 #endif
32
33 class Track {
34 struct stts_entry {
35 FXuint nsamples;
36 FXuint delta;
37 };
38
39 struct stsc_entry {
40 FXint first;
41 FXint nsamples;
42 FXint index;
43 };
44
45 struct ctts_entry {
46 FXint nsamples;
47 FXint offset;
48 };
49
50 public:
51 AudioFormat af; // Audio Format
52 FXuchar codec = Codec::Invalid; // Audio Codec
53 FXuint fixed_sample_size = 0; // used if all samples have the same size
54 FXushort samples_per_frame = 0; // number of pcm samples in a frame (used by AAC)
55 FXbool upsampled = false;
56 DecoderSpecificConfig * dc = nullptr;
57 FXArray<FXuint> stsz; // samples size lookup table (in bytes)
58 FXArray<FXuint> stco; // chunk offset table
59 FXArray<stts_entry> stts; // time to sample number lookup table
60 FXArray<stsc_entry> stsc; // chunk-to-sample table
61 FXArray<ctts_entry> ctts;
62 public:
Track()63 Track() {}
~Track()64 ~Track() { delete dc; }
65 public:
getChunkOffset(FXuint chunk,FXuint chunk_nsamples,FXuint sample) const66 FXlong getChunkOffset(FXuint chunk,FXuint chunk_nsamples,FXuint sample) const {
67 FXlong offset;
68 if (stco.no())
69 offset = stco[FXMIN(chunk,stco.no()-1)];
70 else
71 offset = 8;
72
73 if (fixed_sample_size) {
74 offset += (sample-chunk_nsamples)*fixed_sample_size;
75 }
76 else {
77 for (FXuint i=chunk_nsamples;i<sample;i++) {
78 offset+=stsz[i];
79 }
80 }
81 return offset;
82 }
83
84 // Find the chunk that contains sample s. Also return nsamples at start of chunk
getChunk(FXuint s,FXuint & chunk,FXuint & chunk_nsamples) const85 void getChunk(FXuint s,FXuint & chunk,FXuint & chunk_nsamples) const{
86 FXuint nchunks,nsamples,ntotal=0;
87 for (FXint i=0;i<stsc.no()-1;i++) {
88 nchunks = (stsc[i+1].first - stsc[i].first);
89 nsamples = nchunks * stsc[i].nsamples;
90 if (s<ntotal+nsamples) {
91 chunk = stsc[i].first + ((s-ntotal)/stsc[i].nsamples) - 1;
92 chunk_nsamples = ntotal + ((chunk+1)-stsc[i].first) * stsc[i].nsamples;
93 return;
94 }
95 ntotal+=nsamples;
96 }
97
98 chunk = stsc.tail().first + ((s-ntotal) / stsc.tail().nsamples) - 1;
99 chunk_nsamples = ntotal + ((chunk+1) - stsc.tail().first) * stsc.tail().nsamples;
100 }
101
102 // Sample Offset
getCompositionOffset(FXlong position) const103 FXint getCompositionOffset(FXlong position) const {
104 FXint s = 0;
105 for (int i=0;i<ctts.no();i++){
106 if (position < s + ctts[i].nsamples){
107 if (upsampled)
108 return ctts[i].offset << 1;
109 else
110 return ctts[i].offset;
111 }
112 s+=ctts[i].nsamples;
113 }
114 return 0;
115 }
116
getSample(FXlong position) const117 FXint getSample(FXlong position) const {
118 FXlong n,ntotal = 0;
119 FXint nsamples = 0;
120
121 if (upsampled) {
122 position>>=1;
123 }
124
125 for (int i=0;i<stts.no();i++){
126 n = stts[i].nsamples*stts[i].delta;
127 if (position<ntotal+n) {
128 return nsamples+((position-ntotal)/stts[i].delta);
129 }
130 ntotal+=n;
131 nsamples+=stts[i].nsamples;
132 }
133 return -1;
134 }
135
getSamplePosition(FXuint s) const136 FXlong getSamplePosition(FXuint s) const {
137 FXlong pos=0;
138 FXuint nsamples=0;
139 for (int i=0;i<stts.no();i++){
140 if (s<(stts[i].nsamples+nsamples)){
141 pos+=stts[i].delta*(s-nsamples);
142 if (upsampled)
143 return pos << 1;
144 else
145 return pos;
146 }
147 else {
148 pos+=stts[i].delta*stts[i].nsamples;
149 }
150 nsamples+=stts[i].nsamples;
151 }
152 return 0;
153 }
154
getLength() const155 FXlong getLength() const {
156 FXlong length=0;
157 for (int i=0;i<stts.no();i++){
158 length+=static_cast<FXlong>(stts[i].delta)*static_cast<FXlong>(stts[i].nsamples);
159 }
160
161 if (upsampled)
162 return length << 1;
163 else
164 return length;
165 }
166
getSampleOffset(FXuint s) const167 FXlong getSampleOffset(FXuint s) const {
168 FXuint chunk,nsamples;
169 getChunk(s,chunk,nsamples);
170 return getChunkOffset(chunk,nsamples,s);
171 }
172
getSampleSize(FXuint s) const173 FXlong getSampleSize(FXuint s) const {
174 if (fixed_sample_size)
175 return fixed_sample_size;
176 else
177 return stsz[s];
178 }
179
getNumSamples() const180 FXuint getNumSamples() const {
181 FXint nsamples = 0;
182 for (FXint i=0;i<stts.no();i++) {
183 nsamples += stts[i].nsamples;
184 }
185 return nsamples;
186 }
187 };
188
189
190
191
192 class MP4Reader : public ReaderPlugin {
193 protected:
194 FXPtrListOf<Track> tracks;
195 Track* track = nullptr;
196 MetaInfo* meta = nullptr;
197 FXushort padstart = 0;
198 FXushort padend = 0;
199 FXlong framesize = 0;
200 protected:
201 FXuint read_descriptor_length(FXuint&);
202 FXbool atom_parse_asc(const FXuchar*,FXuint size);
203 FXbool atom_parse_esds(FXlong size);
204 FXbool atom_parse_mp4a(FXlong size);
205 FXbool atom_parse_alac(FXlong size);
206 FXbool atom_parse_stsd(FXlong size);
207 FXbool atom_parse_stco(FXlong size);
208 FXbool atom_parse_stsc(FXlong size);
209 FXbool atom_parse_stts(FXlong size);
210 FXbool atom_parse_stsz(FXlong size);
211 FXbool atom_parse_ctts(FXlong size);
212 FXbool atom_parse_trak(FXlong size);
213 //FXbool atom_parse_freeform(FXlong size);
214 //FXbool atom_parse_text(FXlong size,FXString & value);
215 FXbool atom_parse_meta(FXlong size);
216 FXbool atom_parse_meta_text(FXlong size,FXString &);
217 FXbool atom_parse_meta_free(FXlong size);
218 FXbool atom_parse_header(FXuint & atom_type,FXlong & atom_size,FXlong & container);
219 FXbool atom_parse(FXlong size);
220 protected:
221 FXuint sample = 0; // current sample
222 FXuint nsamples = 0; // number of samples
223 protected:
224 ReadStatus parse();
225 FXbool select_track();
226 void clear_tracks();
227 public:
228 MP4Reader(InputContext*);
229
230 // Format
format() const231 FXuchar format() const override { return Format::MP4; };
232
233 // Init
234 FXbool init(InputPlugin*) override;
235
236 // Seekable
237 FXbool can_seek() const override;
238
239 // Seek
240 FXbool seek(FXlong) override;
241
242 // Process Packet
243 ReadStatus process(Packet*) override;
244
245 // Destroy
246 ~MP4Reader();
247 };
248
249
ap_mp4_reader(InputContext * ctx)250 ReaderPlugin * ap_mp4_reader(InputContext * ctx) {
251 return new MP4Reader(ctx);
252 }
253
254
255
MP4Reader(InputContext * ctx)256 MP4Reader::MP4Reader(InputContext * ctx) : ReaderPlugin(ctx), track(nullptr), meta(nullptr) {
257 }
258
~MP4Reader()259 MP4Reader::~MP4Reader(){
260 clear_tracks();
261 }
262
init(InputPlugin * plugin)263 FXbool MP4Reader::init(InputPlugin*plugin) {
264 ReaderPlugin::init(plugin);
265 flags&=~FLAG_PARSED;
266 clear_tracks();
267 nsamples=0;
268 sample=0;
269 framesize=0;
270 if (meta) {
271 meta->unref();
272 meta=nullptr;
273 }
274 padstart=0;
275 padend=0;
276 return true;
277 }
278
can_seek() const279 FXbool MP4Reader::can_seek() const {
280 return true;
281 }
282
seek(FXlong offset)283 FXbool MP4Reader::seek(FXlong offset){
284 if (!input->serial()){
285 FXint s = track->getSample(offset);
286 if (s>=0) {
287 sample = s;
288 framesize = 0;
289 return true;
290 }
291 }
292 return false;
293 }
294
process(Packet * packet)295 ReadStatus MP4Reader::process(Packet*packet) {
296 packet->stream_position=-1;
297 packet->stream_length=stream_length;
298
299 if (!(flags&FLAG_PARSED) && parse()==ReadError) {
300 packet->unref();
301 return ReadError;
302 }
303
304 // Remaining data from sample
305 if (framesize) {
306 FXlong n = FXMIN(framesize,packet->space());
307 if (input->read(packet->ptr(),n)!=n)
308 return ReadError;
309 packet->wroteBytes(n);
310 framesize-=n;
311 if (framesize) {
312 context->post_packet(packet);
313 packet=NULL;
314 return ReadOk;
315 }
316 sample++;
317 }
318 else {
319 packet->stream_position = track->getSamplePosition(sample);
320 }
321
322 for (;sample<nsamples;sample++) {
323
324 // Framesize for this sample
325 framesize = track->getSampleSize(sample);
326
327 // Gracefully handle unknown framesizes
328 if (__unlikely(framesize<0))
329 return ReadError;
330
331 // Check if packet is full and we should continue at the next packet
332 if (packet->space()<8){
333 framesize = 0; // no remaining data to be read next time
334 context->post_packet(packet);
335 packet=nullptr;
336 return ReadOk;
337 }
338
339 // Locate sample
340 FXlong offset = track->getSampleOffset(sample);
341 input->position(offset,FXIO::Begin);
342
343 // Send framesize to AAC and ALAC decoder
344 if (track->codec==Codec::ALAC || track->codec==Codec::AAC) {
345 if (__unlikely(framesize > UINT32_MAX)) return ReadError;
346 FXuint size32=framesize; // perhaps bounce check?
347 memcpy(packet->ptr(),&size32,4);
348 packet->wroteBytes(4);
349 }
350
351 FXlong n = FXMIN(framesize,packet->space());
352 if (input->read(packet->ptr(),n)!=n){
353 packet->unref();
354 return ReadError;
355 }
356 packet->wroteBytes(n);
357 framesize-=n;
358
359 /// If framesize remaining, assume packet is full
360 if(framesize) {
361 context->post_packet(packet);
362 packet=nullptr;
363 return ReadOk;
364 }
365 }
366
367 if (packet) {
368 FXASSERT(sample>=nsamples-1);
369 packet->flags|=FLAG_EOS;
370 context->post_packet(packet);
371 packet=nullptr;
372 return ReadDone;
373 }
374
375 return ReadOk;
376 }
377
378
clear_tracks()379 void MP4Reader::clear_tracks(){
380 for (int i=0;i<tracks.no();i++)
381 if (tracks[i]!=track)
382 delete tracks[i];
383 tracks.clear();
384 delete track;
385 track = nullptr;
386 }
387
388
select_track()389 FXbool MP4Reader::select_track() {
390 Track* selected = nullptr;
391 for (FXint i=0;i<tracks.no();i++) {
392 if (tracks[i]->codec!=Codec::Invalid) {
393 selected = tracks[i];
394 }
395 }
396 for (FXint i=0;i<tracks.no();i++){
397 if (tracks[i]!=selected)
398 delete tracks[i];
399 }
400 tracks.clear();
401 track = selected;
402 #ifdef DEBUG
403 if (track==nullptr)
404 GM_DEBUG_PRINT("[mp4] no suitable track found\n");
405 #endif
406 return (track!=nullptr);
407 }
408
409
parse()410 ReadStatus MP4Reader::parse() {
411 meta = new MetaInfo();
412
413 if (atom_parse(input->size()) && select_track()) {
414
415 FXASSERT(track);
416
417 stream_length = track->getLength();
418 nsamples = track->getNumSamples();
419 sample = 0;
420
421 af = track->af;
422
423 ConfigureEvent * cfg = new ConfigureEvent(af,track->codec);
424 cfg->dc = track->dc;
425 track->dc = nullptr;
426
427 GM_DEBUG_PRINT("[mp4] total samples %lld\n",stream_length);
428 GM_DEBUG_PRINT("[mp4] padding %hu %hu\n",padstart,padend);
429 GM_DEBUG_PRINT("[mp4] composition offset %d\n",track->getCompositionOffset(0));
430
431 if (track->codec == Codec::AAC) {
432
433 if (track->upsampled) {
434 padstart <<= 1;
435 padend <<= 1;
436 }
437
438 // FAAD has a fixed decoder delay of one frame
439 if (padstart || padend) {
440 stream_length -= (track->samples_per_frame + padend);
441 }
442 else if (stream_length && track->stts.no() && track->ctts.no()) {
443 padstart = track->getCompositionOffset(0); // usually 1024
444 stream_length -= track->samples_per_frame;
445 }
446 cfg->stream_offset_start = FXMAX(0, padstart - track->samples_per_frame);
447 GM_DEBUG_PRINT("[mp4] stream_offset_start %hu\n",cfg->stream_offset_start);
448 }
449
450 GM_DEBUG_STREAM_LENGTH("mp4",stream_length-cfg->stream_offset_start,track->af.rate);
451 GM_DEBUG_PRINT("[mp4] codec %s\n",Codec::name(track->codec));
452 context->post_configuration(cfg);
453
454 if (meta->title.length()) {
455 context->post_meta(meta);
456 meta = nullptr;
457 }
458 else {
459 meta->unref();
460 meta = nullptr;
461 }
462
463 flags|=FLAG_PARSED;
464 return ReadOk;
465 }
466 meta->unref();
467 meta=nullptr;
468 return ReadError;
469 }
470
471
472
473 // Defined in reverse so we don't have to byteswap while reading on LE.
474 #define DEFINE_ATOM(b1,b2,b3,b4) ((b4<<24) | (b3<<16) | (b2<<8) | (b1))
475
476
477 enum Atom {
478
479 ESDS = DEFINE_ATOM('e','s','d','s'),
480
481 FREE = DEFINE_ATOM('f','r','e','e'),
482
483 FTYP = DEFINE_ATOM('f','t','y','p'),
484
485 ILST = DEFINE_ATOM('i','l','s','t'),
486
487 MDAT = DEFINE_ATOM('m','d','a','t'),
488
489 MOOV = DEFINE_ATOM('m','o','o','v'),
490
491 MVHD = DEFINE_ATOM('m','v','h','d'),
492 MDIA = DEFINE_ATOM('m','d','i','a'),
493 MDHD = DEFINE_ATOM('m','d','h','d'),
494 MINF = DEFINE_ATOM('m','i','n','f'),
495
496 STBL = DEFINE_ATOM('s','t','b','l'),
497 STSD = DEFINE_ATOM('s','t','s','d'),
498 STSC = DEFINE_ATOM('s','t','s','c'),
499 STSZ = DEFINE_ATOM('s','t','s','z'),
500 STCO = DEFINE_ATOM('s','t','c','o'),
501 STTS = DEFINE_ATOM('s','t','t','s'),
502 CTTS = DEFINE_ATOM('c','t','t','s'),
503
504 TRAK = DEFINE_ATOM('t','r','a','k'),
505 UDTA = DEFINE_ATOM('u','d','t','a'),
506 MP4A = DEFINE_ATOM('m','p','4','a'),
507 ALAC = DEFINE_ATOM('a','l','a','c'),
508 META = DEFINE_ATOM('m','e','t','a'),
509
510 CART = DEFINE_ATOM(169,'A','R','T'),
511 CALB = DEFINE_ATOM(169,'a','l','b'),
512 CNAM = DEFINE_ATOM(169,'n','a','m'),
513 CTOO = DEFINE_ATOM(169,'t','o','o'),
514 CCMT = DEFINE_ATOM(169,'c','m','t'),
515
516 MEAN = DEFINE_ATOM('m','e','a','n'),
517 NAME = DEFINE_ATOM('n','a','m','e'),
518 DATA = DEFINE_ATOM('d','a','t','a'),
519 DDDD = DEFINE_ATOM('-','-','-','-')
520 };
521
522
atom_parse_trak(FXlong)523 FXbool MP4Reader::atom_parse_trak(FXlong /*size*/) {
524 track = new Track();
525 tracks.append(track);
526 return true;
527 }
528
529
atom_parse_alac(FXlong size)530 FXbool MP4Reader::atom_parse_alac(FXlong size) {
531 FXuchar alac_reserved[16];
532 FXushort index;
533 FXushort version;
534 FXushort channels;
535 FXushort samplesize;
536 FXuint samplerate;
537
538 if (track==NULL)
539 return false;
540
541 if (input->read(&alac_reserved,6)!=6)
542 return false;
543
544 if (!input->read_uint16_be(index)) // data reference index
545 return false;
546
547 if (!input->read_uint16_be(version))
548 return false;
549
550 if (input->read(&alac_reserved,6)!=6)
551 return false;
552
553 if (!input->read_uint16_be(channels))
554 return false;
555
556 if (!input->read_uint16_be(samplesize))
557 return false;
558
559 if (input->read(&alac_reserved,4)!=4)
560 return false;
561
562 if (!input->read_uint32_be(samplerate))
563 return false;
564
565 // samplerate comes in as a fixed point number 16.16
566 samplerate = samplerate >> 16;
567
568 // ALAC specifc info (size + "alac" + version)
569 if (input->read(&alac_reserved,12)!=12)
570 return false;
571
572 // Check size of AlacSpecificConfig
573 if (size-40!=24 && size-40!=48)
574 return false;
575
576 // Check for duplicate entry
577 if (track->dc) {
578 GM_DEBUG_PRINT("[mp4] decoder_specific_info already set?");
579 return false;
580 }
581
582 // Read AlacSpecificConfig
583 DecoderSpecificConfig * dc = new DecoderSpecificConfig();
584 dc->config_bytes = size - 40;
585 allocElms(dc->config,dc->config_bytes);
586 if (input->read(dc->config,dc->config_bytes)!=dc->config_bytes) {
587 delete dc;
588 return false;
589 }
590
591 // As it turns out, samplesize from the AudioSampleEntry box is not always accurate
592 // 24 bit files I've gotten from bandcamp.org had it set to 16 which is the default value
593 // for audio sample entry boxes. Since decoder itself relies on the DecoderSpecificConfig,
594 // we may as well use the bitdepth information from there instead.
595 FXuchar bitdepth = *(dc->config + 5);
596
597 // Record what we found
598 track->codec = Codec::ALAC;
599 track->af.set(Format::Signed,bitdepth,bitdepth>>3,samplerate,channels);
600 track->dc = dc;
601
602 // Some extra debugging
603 if (samplesize!=bitdepth)
604 GM_DEBUG_PRINT("[mp4] alac samplesize %hu doesn't match bitdepth %hhu\n",samplesize,bitdepth);
605
606 return true;
607 }
608
609
610
atom_parse_mp4a(FXlong size)611 FXbool MP4Reader::atom_parse_mp4a(FXlong size) {
612 FXuchar mp4a_reserved[16];
613 FXushort index;
614 FXushort version;
615 FXushort channels;
616 FXushort samplesize;
617 FXuint samplerate;
618
619 FXlong nbytes = size;
620
621 if (track==nullptr)
622 return false;
623
624 if (input->read(&mp4a_reserved,6)!=6)
625 return false;
626
627 if (!input->read_uint16_be(index))
628 return false;
629
630 if (!input->read_uint16_be(version))
631 return false;
632
633 if (input->read(&mp4a_reserved,6)!=6)
634 return false;
635
636 if (!input->read_uint16_be(channels))
637 return false;
638
639 if (!input->read_uint16_be(samplesize))
640 return false;
641
642 if (input->read(&mp4a_reserved,4)!=4)
643 return false;
644
645 if (!input->read_uint32_be(samplerate))
646 return false;
647
648 samplerate = samplerate >> 16;
649
650 GM_DEBUG_PRINT("[mp4] samplerate %d\n",samplerate);
651 GM_DEBUG_PRINT("[mp4] channels %d\n",channels);
652
653 track->af.set(AP_FORMAT_S16,samplerate,channels);
654
655 if (version==1) {
656 if (input->read(&mp4a_reserved,16)!=16)
657 return false;
658 nbytes -= 16;
659 }
660 else if (version==2) {
661 input->position(36,FXIO::Current);
662 nbytes -= 36;
663 }
664
665 if (nbytes-28>0) {
666 return atom_parse(nbytes-28);
667 }
668 return true;
669 }
670
671
read_descriptor_length(FXuint & length)672 FXuint MP4Reader::read_descriptor_length(FXuint & length) {
673 FXuchar b,nbytes=0;
674 length=0;
675 do {
676 if (input->read(&b,1)!=1)
677 return 0;
678 nbytes++;
679 length = (length<<7) | (b&0x7f);
680 }
681 while(b&0x80);
682 return nbytes;
683 }
684
685
686 #define ESDescriptorTag 0x3
687 #define DecoderConfigDescriptorTag 0x4
688 #define DecoderSpecificInfoTag 0x5
689
atom_parse_esds(FXlong size)690 FXbool MP4Reader::atom_parse_esds(FXlong size) {
691 FXlong nbytes = size;
692 FXuint version;
693 FXushort esid;
694 FXuchar esflags;
695 FXuint length;
696 FXuint l;
697
698 FXlong start = input->position();
699
700 if (track==nullptr)
701 return false;
702
703 if (!input->read_uint32_be(version))
704 return false;
705
706 FXuchar tag;
707
708 if (input->read(&tag,1)!=1)
709 return false;
710
711 if (tag==ESDescriptorTag) {
712
713 length = read_descriptor_length(l);
714
715 if (!input->read_uint16_be(esid))
716 return false;
717
718 if (!input->read(&esflags,1))
719 return false;
720
721 nbytes-=(length);
722 }
723 else {
724 // fixme
725 return false;
726 }
727
728 if (input->read(&tag,1)!=1)
729 return false;
730
731 if (tag!=DecoderConfigDescriptorTag)
732 return false;
733
734 nbytes -= read_descriptor_length(l);
735
736 if (input->read(&tag,1)!=1)
737 return false;
738
739 if (tag==0x40 || tag==0x67)
740 track->codec = Codec::AAC;
741
742 if (!input->read_uint32_be(version))
743 return false;
744
745 FXuint avgbitrate;
746 FXuint maxbitrate;
747
748 if (!input->read_uint32_be(maxbitrate))
749 return false;
750
751 if (!input->read_uint32_be(avgbitrate))
752 return false;
753
754 if (input->read(&tag,1)!=1)
755 return false;
756
757 if (tag!=DecoderSpecificInfoTag)
758 return false;
759
760
761 DecoderSpecificConfig * dc = new DecoderSpecificConfig();
762
763 nbytes -= read_descriptor_length(dc->config_bytes);
764
765 if (dc->config_bytes) {
766
767 allocElms(dc->config,dc->config_bytes);
768
769 if (input->read(dc->config,dc->config_bytes)!=dc->config_bytes) {
770 delete dc;
771 return false;
772 }
773
774 track->dc = dc;
775
776 #ifdef HAVE_FAAD
777 if (!ap_parse_aac_specific_config(dc->config,dc->config_bytes,track->samples_per_frame,track->upsampled,track->af))
778 return false;
779 #endif
780
781 }
782
783 FXlong end = input->position();
784
785 if (end-start>0)
786 input->position(size-(end-start),FXIO::Current);
787
788 return true;
789 }
790
791
792
atom_parse_meta(FXlong size)793 FXbool MP4Reader::atom_parse_meta(FXlong size) {
794 FXuint version;
795
796 if (track==nullptr)
797 return false;
798
799 if (!input->read_uint32_be(version))
800 return false;
801
802 if (!atom_parse(size-4))
803 return false;
804
805 return true;
806 }
807
808
809
atom_parse_meta_free(FXlong size)810 FXbool MP4Reader::atom_parse_meta_free(FXlong size) {
811 FXuint atom_type;
812 FXlong atom_size;
813 FXint length;
814
815 FXint type;
816 FXshort county;
817 FXshort language;
818
819 FXString mean;
820 FXString name;
821 FXString data;
822
823 while(size>=8 && atom_parse_header(atom_type,atom_size,size)){
824 switch(atom_type){
825
826 case MEAN:
827 if (atom_size <= 4)
828 return false;
829
830 if (!input->read_int32_be(length))
831 return false;
832
833 mean.length(atom_size-4);
834 if (input->read(mean.text(),atom_size-4)!=atom_size-4)
835 return false;
836
837 break;
838
839 case NAME:
840
841 if (atom_size <= 4)
842 return false;
843
844 if (!input->read_int32_be(length))
845 return false;
846
847 name.length(atom_size-4);
848 if (input->read(name.text(),atom_size-4)!=atom_size-4)
849 return false;
850
851 break;
852
853 case DATA:
854
855 if (!input->read_int32_be(type))
856 return false;
857
858 if (type==1) { // UTF-8
859
860 if (!input->read_int16_be(county))
861 return false;
862
863 if (!input->read_int16_be(language))
864 return false;
865
866 data.length(atom_size-8);
867 if (input->read(data.text(),(atom_size-8))!=(atom_size-8))
868 return false;
869
870 }
871 else {
872 input->position(atom_size-4,FXIO::Current);
873 }
874 break;
875
876 default : input->position(atom_size,FXIO::Current);
877 break;
878 }
879 size-=atom_size;
880 }
881
882 GM_DEBUG_PRINT("[mp4] %s.%s = %s\n",mean.text(),name.text(),data.text());
883 if (name=="iTunSMPB") {
884 FXlong duration;
885 data.simplify().scan("%*x %hx %hx %lx",&padstart,&padend,&duration);
886 GM_DEBUG_PRINT("[mp4] parsed iTunSMPB %hu %hu %lld\n",padstart,padend,duration);
887 }
888 return true;
889 }
890
891
892
atom_parse_meta_text(FXlong size,FXString & field)893 FXbool MP4Reader::atom_parse_meta_text(FXlong size,FXString & field) {
894 FXint length;
895 FXchar id[4];
896 FXint type;
897 FXshort county;
898 FXshort language;
899
900 if (!input->read_int32_be(length))
901 return false;
902
903 if (size!=length)
904 return false;
905
906 if (input->read(&id,4)!=4)
907 return false;
908
909 if (!input->read_int32_be(type))
910 return false;
911
912 if (type==1) {
913
914 if (!input->read_int16_be(county))
915 return false;
916
917 if (!input->read_int16_be(language))
918 return false;
919
920 field.length(length-16);
921 if (input->read(&field[0],(length-16))!=(length-16))
922 return false;
923
924 GM_DEBUG_PRINT("[mp4] meta text: \"%s\"\n",field.text());
925 }
926 else {
927 input->position(length-12,FXIO::Current);
928 }
929 return true;
930 }
931
atom_parse_stsd(FXlong size)932 FXbool MP4Reader::atom_parse_stsd(FXlong size) {
933 FXuint version;
934 FXuint nentries;
935
936 if (track==nullptr)
937 return false;
938
939 if (!input->read_uint32_be(version))
940 return false;
941
942 if (!input->read_uint32_be(nentries))
943 return false;
944
945 FXASSERT(nentries==1);
946
947 if (!atom_parse(size-8))
948 return false;
949
950 return true;
951 }
952
953
atom_parse_stsc(FXlong)954 FXbool MP4Reader::atom_parse_stsc(FXlong /*size*/) {
955 FXuint version;
956 FXuint nentries;
957
958 if (track==nullptr)
959 return false;
960
961 if (!input->read_uint32_be(version))
962 return false;
963
964 if (!input->read_uint32_be(nentries))
965 return false;
966
967 if (nentries) {
968 track->stsc.no(nentries);
969 for (FXuint i=0;i<nentries;i++) {
970 if (!input->read_int32_be(track->stsc[i].first))
971 return false;
972 if (!input->read_int32_be(track->stsc[i].nsamples))
973 return false;
974 if (!input->read_int32_be(track->stsc[i].index))
975 return false;
976 }
977 }
978 //FXASSERT(size==((nentries*12)+8));
979 return true;
980 }
981
982
983
atom_parse_stco(FXlong)984 FXbool MP4Reader::atom_parse_stco(FXlong/*size*/) {
985 FXuint version;
986 FXuint nchunks;
987
988 if (track==nullptr)
989 return false;
990
991 if (input->read(&version,4)!=4)
992 return false;
993
994 if (!input->read_uint32_be(nchunks))
995 return false;
996
997 if (nchunks>0) {
998 track->stco.no(nchunks);
999 for (FXuint i=0;i<nchunks;i++) {
1000 if (!input->read_uint32_be(track->stco[i]))
1001 return false;
1002 }
1003 }
1004 //FXASSERT(size==((nchunks*4)+8));
1005 return true;
1006 }
1007
atom_parse_stts(FXlong)1008 FXbool MP4Reader::atom_parse_stts(FXlong /*size*/) {
1009 FXuint version;
1010 FXuint nsize;
1011
1012 if (track==nullptr)
1013 return false;
1014
1015 if (input->read(&version,4)!=4)
1016 return false;
1017
1018 if (!input->read_uint32_be(nsize))
1019 return false;
1020
1021 if (nsize>0) {
1022 track->stts.no(nsize);
1023 for (FXuint i=0;i<nsize;i++) {
1024 if (!input->read_uint32_be(track->stts[i].nsamples)) return false;
1025 if (!input->read_uint32_be(track->stts[i].delta)) return false;
1026 //GM_DEBUG_PRINT("stts %d: %d %d\n",i,track->stts[i].nsamples,track->stts[i].delta);
1027 }
1028
1029 }
1030 //FXASSERT(size==((nsize*8)+8));
1031 return true;
1032 }
1033
1034
atom_parse_ctts(FXlong)1035 FXbool MP4Reader::atom_parse_ctts(FXlong /*size*/) {
1036 FXuint version;
1037 FXuint nentries;
1038
1039 if (track==NULL)
1040 return false;
1041
1042 if (!input->read_uint32_be(version))
1043 return false;
1044
1045 if (!input->read_uint32_be(nentries))
1046 return false;
1047
1048 if (nentries) {
1049 track->ctts.no(nentries);
1050 for (FXuint i=0;i<nentries;i++) {
1051 if (!input->read_int32_be(track->ctts[i].nsamples))
1052 return false;
1053 if (!input->read_int32_be(track->ctts[i].offset))
1054 return false;
1055 //GM_DEBUG_PRINT("ctts %d: %d %d\n",i,track->ctts[i].nsamples,track->ctts[i].offset);
1056 }
1057 }
1058 //FXASSERT(size==((nentries*12)+8));
1059 return true;
1060 }
1061
1062
1063
atom_parse_stsz(FXlong)1064 FXbool MP4Reader::atom_parse_stsz(FXlong /*size*/) {
1065 FXuint version;
1066 FXuint samplecount;
1067
1068 if (track==nullptr)
1069 return false;
1070
1071 if (input->read(&version,4)!=4)
1072 return false;
1073
1074 if (!input->read_uint32_be(track->fixed_sample_size))
1075 return false;
1076
1077 if (!input->read_uint32_be(samplecount))
1078 return false;
1079
1080 if (track->fixed_sample_size==0 && samplecount>0) {
1081 track->stsz.no(samplecount);
1082 for (FXuint i=0;i<samplecount;i++) {
1083 if (!input->read_uint32_be(track->stsz[i]))
1084 return false;
1085 }
1086 }
1087 //FXASSERT(size==((nsamples*4)+12));
1088 return true;
1089 }
1090
atom_parse_header(FXuint & type,FXlong & size,FXlong & container)1091 FXbool MP4Reader::atom_parse_header(FXuint & type,FXlong & size,FXlong & container) {
1092 FXuint sz;
1093
1094 if (!input->read_uint32_be(sz))
1095 return false;
1096
1097 if (input->read(&type,4)!=4)
1098 return false;
1099
1100 if (sz==1) {
1101 if (!input->read_int64_be(size))
1102 return false;
1103 size -= 16;
1104 container -= 16;
1105 }
1106 else {
1107 size = sz - 8;
1108 container -= 8;
1109 }
1110 return true;
1111 }
1112
1113
1114
atom_parse(FXlong size)1115 FXbool MP4Reader::atom_parse(FXlong size) {
1116 static int indent = 0;
1117 FXuint atom_type;
1118 FXlong atom_size;
1119 FXbool ok;
1120 indent++;
1121 while(size>=8 && atom_parse_header(atom_type,atom_size,size)){
1122 GM_DEBUG_PRINT("[mp4] %*d atom %c%c%c%c size %lld left %lld\n",indent,indent,(atom_type)&0xFF,(atom_type>>8)&0xFF,(atom_type>>16)&0xFF,(atom_type>>24),atom_size,size);
1123 switch(atom_type){
1124
1125 // Don't go any further than the mdat atom in serial streams.
1126 case MDAT: if (input->serial())
1127 return true;
1128 input->position(atom_size,FXIO::Current); ok=true;
1129 break;
1130
1131 case TRAK: ok=atom_parse_trak(atom_size);
1132 if(!ok) return false;
1133 // fallthrough - intentionally no break
1134 case MDIA:
1135 case MINF:
1136 case STBL:
1137 case UDTA:
1138 case ILST:
1139 case MOOV: ok=atom_parse(atom_size);
1140 break;
1141 case META: ok=atom_parse_meta(atom_size); break;
1142 case STSZ: ok=atom_parse_stsz(atom_size); break;
1143 case STCO: ok=atom_parse_stco(atom_size); break;
1144 case STSD: ok=atom_parse_stsd(atom_size); break;
1145 case STSC: ok=atom_parse_stsc(atom_size); break;
1146 case STTS: ok=atom_parse_stts(atom_size); break;
1147 case CTTS: ok=atom_parse_ctts(atom_size); break;
1148 case MP4A: ok=atom_parse_mp4a(atom_size); break;
1149 case ALAC: ok=atom_parse_alac(atom_size); break;
1150 case ESDS: ok=atom_parse_esds(atom_size); break;
1151 case DDDD: ok=atom_parse_meta_free(atom_size); break;
1152 case CART: ok=atom_parse_meta_text(atom_size,meta->artist); break;
1153 case CALB: ok=atom_parse_meta_text(atom_size,meta->album); break;
1154 case CNAM: ok=atom_parse_meta_text(atom_size,meta->title); break;
1155 #ifdef DEBUG
1156 case CTOO:
1157 case CCMT:
1158 {
1159 FXString comment;
1160 ok=atom_parse_meta_text(atom_size,comment);
1161 break;
1162 }
1163 #endif
1164 default : input->position(atom_size,FXIO::Current); ok=true;
1165 break;
1166 }
1167 if (!ok) return false;
1168 size-=atom_size;
1169 }
1170 indent--;
1171 return true;
1172 }
1173
1174
1175
1176
1177 }
1178
1179
1180
1181