arduino-audio-tools
Loading...
Searching...
No Matches
ContainerMP4.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cstring>
5
13
14namespace audio_tools {
15
52class DemuxerMP4 : public Demuxer {
53 public:
55
68
69 DemuxerMP4(Print& video_out, Print& audio_out) {
71 setOutputAudio(audio_out);
72 setOutputVideo(video_out);
73 }
74
75 DemuxerMP4(VideoOutput& video_out, Print& audio_out) {
77 setOutputAudio(audio_out);
78 setOutputVideo(video_out);
79 }
80
82
83 const char* mime() override { return "video/mp4"; }
84
85 bool begin() override {
86 freeTracks();
87 schedule.clear();
88 current_track = nullptr;
89 p_video_track = nullptr;
90 p_audio_track = nullptr;
93 mdat_seen = false;
94 box_accum_active = false;
100 current_sample_track = nullptr;
101 is_wav_header_sent = false;
103 playback_start_set = false;
104 parser.begin();
105 is_active = true;
106 return true;
107 }
108
109 void end() override { is_active = false; }
110
111 operator bool() override { return is_active; }
112
116 void setOutputAudio(Print& out) override { p_output_audio = &out; }
117
122 void setOutput(Print& out) override { setOutputAudio(out); }
123
124 void setOutputVideo(Print& out) override { p_video_out = &out; }
126
139 VideoInfo result;
141 // width==0 means 'avc1'/'hev1' hasn't been parsed yet (p_video_track can
142 // already be set from 'hdlr' alone) - keep the all-UNKNOWN default then
143 if (p_video_track == nullptr || p_video_track->width == 0) return result;
144 result.width = p_video_track->width;
145 result.height = p_video_track->height;
148 result.frame_size = (uint32_t)videoFrameSizeBytes(
149 result.format, result.width, result.height);
150 return result;
151 }
162 AudioInfoFormat result(info);
163 result.format = audio_format;
164 return result;
165 }
173
178 size_t write(const uint8_t* data, size_t len) override {
179 if (!is_active) return 0;
180 size_t written = parser.write(data, len);
181 total_bytes_received += written;
182 return written;
183 }
184
185 protected:
187 struct StscEntry {
188 uint32_t first_chunk;
190 };
191
195 struct SttsEntry {
196 uint32_t sample_count;
197 uint32_t sample_delta;
198 };
199
201 struct Track {
203
204 // audio
205 Codec audio_codec = Codec::Unknown;
208
209 // video (H.264 only in v1)
210 uint16_t width = 0, height = 0;
211 uint8_t nal_length_size = 4;
213 bool avc_config_sent = false;
215
216 // sample tables, populated while parsing 'moov'
218 uint32_t fixed_sample_size = 0;
219 uint32_t fixed_sample_count = 0;
222
223 // playback timing: this track's own 'mdhd' timescale (ticks/second)
224 // and its 'stts' time-to-sample table - both needed to turn a sample
225 // index into a scheduled presentation time.
226 uint32_t timescale = 0;
228
229 // runtime cursor, used while walking the merge schedule
230 uint32_t next_sample_index = 0;
231
232 // runtime cursor for nextPtsTicks(), advanced once per dispatched
233 // sample - kept separate from next_sample_index so it stays valid
234 // even if next_sample_index is ever used for random access
235 uint32_t stts_entry_idx = 0;
237 uint64_t next_pts_ticks = 0;
238
239 uint32_t sampleSize(uint32_t idx) {
240 if (fixed_sample_size > 0) return fixed_sample_size;
241 if (idx >= sample_sizes.size()) return 0;
242 return sample_sizes[idx];
243 }
244
245 uint32_t sampleCount() {
247 : (uint32_t)sample_sizes.size();
248 }
249
255 uint64_t nextPtsTicks() {
256 uint64_t pts = next_pts_ticks;
257 if (stts_entry_idx < stts.size()) {
258 if (stts_entry_remaining == 0)
260 next_pts_ticks += stts[stts_entry_idx].sample_delta;
261 if (stts_entry_remaining > 0) {
264 }
265 }
266 return pts;
267 }
268 };
269
274 uint32_t sample_count;
275 };
276
278 // heap-allocated (not Vector<Track>): current_track/p_video_track/
279 // p_audio_track are captured *during* moov parsing, while more trak
280 // boxes (and thus more push_back calls) may still arrive - Vector<T>
281 // reallocates its backing array on growth, which would dangle any
282 // pointer taken directly into it. Pointers to heap objects stay valid
283 // regardless of how the Vector<Track*> itself grows.
286 nullptr;
287
288 void freeTracks() {
289 for (size_t i = 0; i < tracks.size(); i++) delete tracks[i];
290 tracks.clear();
291 }
292 Track* p_video_track = nullptr;
293 Track* p_audio_track = nullptr;
294
296 size_t schedule_index = 0;
298 bool mdat_seen = false;
299
301 Print* p_video_out = nullptr;
307 bool send_wav_header = false;
309 bool is_wav_header_sent = false;
313 bool is_active = false;
314
315 // video playback pacing: wall-clock reference captured at the first
316 // dispatched video sample, against which each subsequent sample's
317 // scheduled presentation time (from 'mdhd'+'stts') is compared - see
318 // dispatchVideo().
319 bool playback_start_set = false;
320 uint32_t playback_start_ms = 0;
321
322 // sample accumulation (mdat streaming)
328
329 // scratch accumulation buffer, reused sequentially for stsz/stsc/stco/co64
330 // (only one of these is ever "in progress" at a time in a linear parse)
332 // Explicit "are we mid-accumulation" state for box_accum, decoupled from
333 // its fill level: box_accum.available()==0 is NOT a reliable "start of a
334 // new box" signal, because some handlers (onStsz) legitimately drain it
335 // to empty via clearArray() *while still mid-box* (e.g. right after
336 // consuming a fixed-size sub-header, with more payload still to come) -
337 // available()==0 in that case would be misread as a fresh box starting.
338 bool box_accum_active = false;
340
345 bool beginBoxAccum(const MP4Parser::Box& box) {
346 if (box_accum_active) return false;
347 box_accum.resize(box.size);
349 box_accum_active = true;
350 return true;
351 }
352
354 void endBoxAccum() {
356 box_accum_active = false;
357 }
358
360
361 static uint32_t readU32(const uint8_t* p) {
362 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
363 }
364 static uint64_t readU64(const uint8_t* p) {
365 return ((uint64_t)readU32(p) << 32) | readU32(p + 4);
366 }
367 static uint16_t readU16(const uint8_t* p) { return (p[0] << 8) | p[1]; }
368
369 void setupParser() {
370 parser.setReference(this);
371 // suppress MP4Parser's default behavior of printing every box we don't
372 // otherwise handle (mvhd, tkhd, stts, ...) to Serial/stdout
373 parser.setCallback([](MP4Parser::Box&, void*) {});
374
376 "trak",
377 [](MP4Parser::Box& box, void* ref) {
378 static_cast<DemuxerMP4*>(ref)->onTrak();
379 },
380 false);
381
383 "hdlr",
384 [](MP4Parser::Box& box, void* ref) {
385 static_cast<DemuxerMP4*>(ref)->onHdlr(box);
386 },
387 false);
388
390 "stsd",
391 [](MP4Parser::Box& box, void* ref) {
392 static_cast<DemuxerMP4*>(ref)->onStsd(box);
393 },
394 false);
395
397 "mp4a",
398 [](MP4Parser::Box& box, void* ref) {
399 static_cast<DemuxerMP4*>(ref)->onMp4a(box);
400 },
401 false);
403 "alac",
404 [](MP4Parser::Box& box, void* ref) {
405 static_cast<DemuxerMP4*>(ref)->onAlac(box);
406 },
407 false);
409 "esds",
410 [](MP4Parser::Box& box, void* ref) {
411 static_cast<DemuxerMP4*>(ref)->onEsds(box);
412 },
413 false);
414
416 "avc1",
417 [](MP4Parser::Box& box, void* ref) {
418 static_cast<DemuxerMP4*>(ref)->onAvc1(box);
419 },
420 false);
422 "hev1",
423 [](MP4Parser::Box& box, void* ref) {
424 static_cast<DemuxerMP4*>(ref)->onHevc(box);
425 },
426 false);
428 "hvc1",
429 [](MP4Parser::Box& box, void* ref) {
430 static_cast<DemuxerMP4*>(ref)->onHevc(box);
431 },
432 false);
434 "avcC",
435 [](MP4Parser::Box& box, void* ref) {
436 static_cast<DemuxerMP4*>(ref)->onAvcC(box);
437 },
438 false);
439
441 "mdhd",
442 [](MP4Parser::Box& box, void* ref) {
443 static_cast<DemuxerMP4*>(ref)->onMdhd(box);
444 },
445 false);
447 "stts",
448 [](MP4Parser::Box& box, void* ref) {
449 static_cast<DemuxerMP4*>(ref)->onStts(box);
450 },
451 false);
452
454 "stsz",
455 [](MP4Parser::Box& box, void* ref) {
456 static_cast<DemuxerMP4*>(ref)->onStsz(box);
457 },
458 false);
460 "stsc",
461 [](MP4Parser::Box& box, void* ref) {
462 static_cast<DemuxerMP4*>(ref)->onStsc(box);
463 },
464 false);
466 "stco",
467 [](MP4Parser::Box& box, void* ref) {
468 static_cast<DemuxerMP4*>(ref)->onStco(box, false);
469 },
470 false);
472 "co64",
473 [](MP4Parser::Box& box, void* ref) {
474 static_cast<DemuxerMP4*>(ref)->onStco(box, true);
475 },
476 false);
477
479 "mdat",
480 [](MP4Parser::Box& box, void* ref) {
481 static_cast<DemuxerMP4*>(ref)->onMdat(box);
482 },
483 false);
484 }
485
486 // ---- moov / track parsing ----
487
488 void onTrak() {
489 Track* t = new Track();
490 tracks.push_back(t);
491 current_track = t;
492 }
493
495 if (current_track == nullptr) return;
496 beginBoxAccum(box);
498 if (!box.is_complete || box_accum.available() < 12) return;
499 const uint8_t* handler = box_accum.data() + 8;
500 if (memcmp(handler, "vide", 4) == 0) {
502 if (p_video_track == nullptr) p_video_track = current_track;
503 } else if (memcmp(handler, "soun", 4) == 0) {
505 if (p_audio_track == nullptr) p_audio_track = current_track;
506 }
507 endBoxAccum();
508 }
509
511 beginBoxAccum(box);
513 if (box.is_complete && box_accum.available() >= 8) {
514 // sample entries (mp4a/alac/avc1/hev1/...) parsed generically, same
515 // mechanism as M4ACommonDemuxer::onStsd
517 box.file_offset + 8 + 8, box.level + 1);
518 endBoxAccum();
519 }
520 }
521
522 void onMp4a(const MP4Parser::Box& box) {
523 if (current_track == nullptr || !box.is_complete) return;
524 current_track->aacProfile = 2; // default AAC LC
525 current_track->sampleRateIdx = 4; // default 44100 Hz
526 current_track->channelCfg = 2; // default stereo
527 current_track->audio_codec = Codec::AAC;
529 // AudioSampleEntry fixed header is 28 bytes (incl. the 8 bytes already
530 // stripped as the box header) - esds (child box) follows
531 int pos = 36 - 8;
532 if (box.data_size > (size_t)pos)
533 parser.parseString(box.data + pos, box.data_size - pos,
534 box.file_offset + 8 + pos, box.level + 1);
535 }
536
542 void setupAudioInfo(AudioFormat format, const uint8_t* entryData,
543 size_t entrySize) {
544 if (entrySize < 28) return;
545 info.channels = readU16(entryData + 16);
546 info.sample_rate = readU16(entryData + 24);
548 audio_format = format;
549 info.logInfo();
553 }
554
561 void setSendWavHeader(bool flag) override {
562 send_wav_header = flag;
564 }
565
575 if (is_wav_header_sent || p_output_audio == nullptr) return;
576 WAVAudioInfo winfo(info);
577 winfo.format = audio_format;
578 winfo.is_streamed = true;
580 winfo.byte_rate = info.sample_rate * winfo.block_align;
581 WAVHeader wav_header;
582 wav_header.setAudioInfo(winfo);
583 wav_header.writeHeader(p_output_audio);
584 is_wav_header_sent = true;
585 }
586
587 void onEsds(const MP4Parser::Box& box) {
588 if (current_track == nullptr) return;
590 if (!esdsParser.parse(box.data, box.data_size)) {
591 LOGE("Failed to parse esds box");
592 return;
593 }
597 }
598
599 void onAlac(const MP4Parser::Box& box) {
600 if (current_track == nullptr || !box.is_complete) return;
601 current_track->audio_codec = Codec::ALAC;
603 MP4Parser::Box alac;
604 if (parser.findBox("alac", box.data, box.data_size, alac)) {
606 memcpy(current_track->alacMagicCookie.data(), alac.data + 4,
607 alac.data_size - 4);
608 }
609 }
610
611 void onAvc1(const MP4Parser::Box& box) {
612 if (current_track == nullptr || !box.is_complete) return;
614 if (p_video_track == nullptr) p_video_track = current_track;
615 if (box.data_size < 28) return;
616 current_track->width = readU16(box.data + 24);
617 current_track->height = readU16(box.data + 26);
618 // VisualSampleEntry fixed header is 78 bytes (already excl. the 8 byte
619 // box header) - avcC (child box) follows
620 const int pos = 78;
621 if (box.data_size > (size_t)pos)
622 parser.parseString(box.data + pos, box.data_size - pos,
623 box.file_offset + 8 + pos, box.level + 1);
624 }
625
626 void onHevc(const MP4Parser::Box& box) {
627 if (current_track == nullptr) return;
629 if (p_video_track == nullptr) p_video_track = current_track;
631 LOGE("HEVC (hev1/hvc1) video is not supported - only H.264 (avc1)");
633 }
634 }
635
636 void onAvcC(const MP4Parser::Box& box) {
637 if (current_track == nullptr || !box.is_complete || box.data_size < 6)
638 return;
639 const uint8_t* d = box.data;
640 current_track->nal_length_size = (d[4] & 0x03) + 1;
641 int numSps = d[5] & 0x1F;
642 size_t pos = 6;
643 auto appendUnit = [&](const uint8_t* unit, size_t len) {
644 static const uint8_t startCode[4] = {0, 0, 0, 1};
645 auto& v = current_track->sps_pps_annexb;
646 size_t off = v.size();
647 v.resize(off + 4 + len);
648 memcpy(v.data() + off, startCode, 4);
649 memcpy(v.data() + off + 4, unit, len);
650 };
651 for (int i = 0; i < numSps && pos + 2 <= box.data_size; i++) {
652 uint16_t len = readU16(d + pos);
653 pos += 2;
654 if (pos + len > box.data_size) break;
655 appendUnit(d + pos, len);
656 pos += len;
657 }
658 if (pos >= box.data_size) return;
659 int numPps = d[pos++];
660 for (int i = 0; i < numPps && pos + 2 <= box.data_size; i++) {
661 uint16_t len = readU16(d + pos);
662 pos += 2;
663 if (pos + len > box.data_size) break;
664 appendUnit(d + pos, len);
665 pos += len;
666 }
667 }
668
673 if (current_track == nullptr) return;
674 beginBoxAccum(box);
676 if (!box.is_complete) return;
677 if (box_accum.available() < 4) {
678 endBoxAccum();
679 return;
680 }
681 uint8_t version = box_accum.data()[0];
682 // v0: creation(4)+modification(4)+timescale(4)+duration(4)
683 // v1: creation(8)+modification(8)+timescale(4)+duration(8)
684 size_t timescaleOffset = version == 1 ? 20 : 12;
685 if (box_accum.available() >= timescaleOffset + 4) {
686 current_track->timescale = readU32(box_accum.data() + timescaleOffset);
687 }
688 endBoxAccum();
689 }
690
694 if (current_track == nullptr) return;
695 beginBoxAccum(box);
697 if (!box.is_complete) return;
698 if (box_accum.available() < 8) {
699 endBoxAccum();
700 return;
701 }
702 uint32_t entryCount = readU32(box_accum.data() + 4);
703 const uint8_t* p = box_accum.data() + 8;
704 size_t avail = box_accum.available() - 8;
705 for (uint32_t i = 0; i < entryCount && (i + 1) * 8 <= avail; i++) {
706 SttsEntry e;
707 e.sample_count = readU32(p + i * 8);
708 e.sample_delta = readU32(p + i * 8 + 4);
709 current_track->stts.push_back(e);
710 }
711 endBoxAccum();
712 }
713
715 if (current_track == nullptr) return;
716 if (beginBoxAccum(box))
717 stsz_header_pending = true; // starting a fresh stsz box
719
720 // The 12-byte header (version+flags, sampleSize, sampleCount) must be
721 // fully consumed *once* before any bytes are interpreted as per-sample
722 // sizes. Using "no sizes pushed yet" as a stand-in for "header not
723 // parsed yet" (as tried previously) is wrong: in the variable-size
724 // case, both are simultaneously true right after the header IS parsed,
725 // so under fine-grained incremental delivery the per-sample loop below
726 // could start consuming still-unparsed header bytes as if they were
727 // sample sizes. An explicit flag avoids that ambiguity.
729 if (box_accum.available() < 12)
730 return; // wait for the rest of the header
731 Track* t = current_track;
732 uint32_t sampleSize = readU32(box_accum.data() + 4);
733 uint32_t sampleCount = readU32(box_accum.data() + 8);
735 stsz_header_pending = false;
736 if (sampleSize != 0) {
737 t->fixed_sample_size = sampleSize;
738 t->fixed_sample_count = sampleCount;
739 }
740 // else: sample_sizes is filled below via push_back, one entry at a
741 // time as bytes arrive - do NOT pre-resize it (Vector::push_back
742 // always appends, unlike BaseBuffer::write() which fills pre-sized
743 // capacity in place)
744 }
745 // incrementally consume per-sample sizes (fixed-size case has none left
746 // to read; the table is only present in the box when sampleSize==0)
748 while (box_accum.available() >= 4) {
751 }
752 }
753 if (box.is_complete) endBoxAccum();
754 }
755
757 if (current_track == nullptr) return;
758 beginBoxAccum(box);
760 if (!box.is_complete) return;
761 if (box_accum.available() < 8) {
762 endBoxAccum();
763 return;
764 }
765 uint32_t entryCount = readU32(box_accum.data() + 4);
766 const uint8_t* p = box_accum.data() + 8;
767 size_t avail = box_accum.available() - 8;
768 for (uint32_t i = 0; i < entryCount && (i + 1) * 12 <= avail; i++) {
769 StscEntry e;
770 e.first_chunk = readU32(p + i * 12);
771 e.samples_per_chunk = readU32(p + i * 12 + 4);
772 current_track->stsc.push_back(e);
773 }
774 endBoxAccum();
775 }
776
777 void onStco(MP4Parser::Box& box, bool is64) {
778 if (current_track == nullptr) return;
779 beginBoxAccum(box);
781 if (!box.is_complete) return;
782 if (box_accum.available() < 8) {
783 endBoxAccum();
784 return;
785 }
786 uint32_t entryCount = readU32(box_accum.data() + 4);
787 const uint8_t* p = box_accum.data() + 8;
788 size_t avail = box_accum.available() - 8;
789 size_t entrySize = is64 ? 8 : 4;
790 for (uint32_t i = 0; i < entryCount && (i + 1) * entrySize <= avail; i++) {
791 uint64_t off = is64 ? readU64(p + i * 8) : readU32(p + i * 4);
793 }
794 endBoxAccum();
795 }
796
797 // ---- mdat / schedule / sample dispatch ----
798
800 if (!mdat_seen) {
801 mdat_seen = true;
803 }
804 feed(box.data, box.available);
805 }
806
810 // (track*, chunk_index, offset) working list
811 struct Item {
812 Track* track;
813 uint32_t chunk_index;
814 uint64_t offset;
815 };
816 Vector<Item> items;
817 for (size_t ti = 0; ti < tracks.size(); ti++) {
818 Track* t = tracks[ti];
819 if (t->kind == Track::Kind::Unknown) continue;
820 if (t->kind == Track::Kind::Video && t != p_video_track) continue;
821 if (t->kind == Track::Kind::Audio && t != p_audio_track) continue;
822 for (size_t ci = 0; ci < t->chunk_offsets.size(); ci++) {
823 items.push_back({t, (uint32_t)ci, t->chunk_offsets[ci]});
824 }
825 }
826 // simple insertion sort by offset (small N: chunk count is typically
827 // well under a few thousand for embedded-relevant clip lengths)
828 for (size_t i = 1; i < items.size(); i++) {
829 Item key = items[i];
830 long j = (long)i - 1;
831 while (j >= 0 && items[j].offset > key.offset) {
832 items[j + 1] = items[j];
833 j--;
834 }
835 items[j + 1] = key;
836 }
837 for (size_t i = 0; i < items.size(); i++) {
838 Track* t = items[i].track;
839 uint32_t samples = samplesInChunk(*t, items[i].chunk_index);
840 if (samples > 0) schedule.push_back({t, samples});
841 }
842 schedule_index = 0;
843 samples_left_in_entry = schedule.size() > 0 ? schedule[0].sample_count : 0;
844 }
845
847 uint32_t samplesInChunk(Track& t, uint32_t chunkIndex0) {
848 uint32_t chunk1 = chunkIndex0 + 1; // stsc uses 1-based chunk numbers
849 uint32_t result = 0;
850 for (size_t i = 0; i < t.stsc.size(); i++) {
851 if (t.stsc[i].first_chunk <= chunk1) {
852 result = t.stsc[i].samples_per_chunk;
853 } else {
854 break;
855 }
856 }
857 return result;
858 }
859
862 while (samples_left_in_entry == 0 && schedule_index < schedule.size()) {
865 ? schedule[schedule_index].sample_count
866 : 0;
867 }
868 }
869
873 void feed(const uint8_t* data, size_t len) {
874 size_t pos = 0;
875 while (pos < len) {
876 if (current_sample_track == nullptr) {
878 if (schedule_index >= schedule.size()) return; // nothing more expected
879 Track* t = schedule[schedule_index].track;
880 uint32_t size = t->sampleSize(t->next_sample_index);
881 if (size == 0) {
882 // no more sizes for this track - stop
884 continue;
885 }
887 current_sample_size = size;
889 // Vector::resize() sets the logical size immediately (unlike a
890 // capacity reserve), so track how much of it is actually filled
891 // separately rather than relying on sample_buffer.size().
892 sample_buffer.resize(size);
894 }
895
897 size_t take = std::min(need, len - pos);
898 memcpy(sample_buffer.data() + sample_buffer_filled, data + pos, take);
899 sample_buffer_filled += take;
900 pos += take;
901
907 current_sample_track = nullptr;
908 }
909 }
910 }
911
912 void dispatchSample(Track& t, const uint8_t* data, size_t size,
913 bool isFirst) {
914 if (&t == p_video_track) {
915 dispatchVideo(t, data, size, isFirst);
916 } else if (&t == p_audio_track) {
917 dispatchAudio(t, data, size, isFirst);
918 }
919 }
920
921 void dispatchVideo(Track& t, const uint8_t* data, size_t size, bool isFirst) {
922 // nextPtsTicks() must run unconditionally, in sample order, to keep
923 // the track's timing cursor correct regardless of what we do below -
924 // call it before any early return.
925 uint64_t ptsTicks = t.nextPtsTicks();
926 if ((p_video_out == nullptr && p_video_out_video == nullptr) ||
928 return;
929
930 if (t.timescale > 0 && !t.stts.empty()) {
931 uint32_t scheduledMs = (uint32_t)((ptsTicks * 1000) / t.timescale);
932 uint32_t nowMs = millis();
933 if (!playback_start_set) {
934 playback_start_ms = nowMs;
935 playback_start_set = true;
936 }
937 uint32_t scheduledWallMs = playback_start_ms + scheduledMs;
938 if (nowMs > scheduledWallMs) {
939 LOGW("DemuxerMP4: video frame %u ms late - skipping",
940 (unsigned)(nowMs - scheduledWallMs));
941 return;
942 } else if (nowMs < scheduledWallMs) {
943 LOGI("DemuxerMP4: video frame %u ms early",
944 (unsigned)(scheduledWallMs - nowMs));
945 }
946 }
947
948 // convert AVCC length-prefixed NAL units to Annex-B start codes
949 nal_tmp.clear();
950 size_t pos = 0;
951 size_t lenSize = t.nal_length_size;
952 while (pos + lenSize <= size) {
953 uint32_t nalLen = 0;
954 for (size_t i = 0; i < lenSize; i++)
955 nalLen = (nalLen << 8) | data[pos + i];
956 pos += lenSize;
957 if (pos + nalLen > size) break;
958 size_t off = nal_tmp.size();
959 static const uint8_t startCode[4] = {0, 0, 0, 1};
960 nal_tmp.resize(off + 4 + nalLen);
961 memcpy(nal_tmp.data() + off, startCode, 4);
962 memcpy(nal_tmp.data() + off + 4, data + pos, nalLen);
963 pos += nalLen;
964 }
965
966 bool prependConfig =
967 isFirst && !t.avc_config_sent && t.sps_pps_annexb.size() > 0;
968 if (prependConfig) {
970 t.avc_config_sent = true;
971 }
972 if (nal_tmp.size() > 0) writeVideo(nal_tmp.data(), nal_tmp.size());
973 flushVideo();
974 }
975
976 void writeVideo(uint8_t* data, size_t size) {
977 if (p_video_out == nullptr && p_video_out_video == nullptr) return;
978 if (size > 0) {
979 if (p_video_out != nullptr) p_video_out->write(data, size);
980 if (p_video_out_video != nullptr) p_video_out_video->write(data, size);
981 }
982 }
983
984 void flushVideo() {
985 if (p_video_out != nullptr) p_video_out->flush();
986 if (p_video_out_video != nullptr) p_video_out_video->flush();
987 }
988
989 void dispatchAudio(Track& t, const uint8_t* data, size_t size, bool isFirst) {
990 if (p_output_audio == nullptr) return;
991 if (t.audio_codec == Codec::AAC) {
992 uint8_t adts[7];
994 (int)size);
995 p_output_audio->write(adts, sizeof(adts));
996 p_output_audio->write(data, size);
997 } else {
998 // ALAC (magic cookie is exposed via audioALACMagicCookie() for the
999 // caller to configure their own decoder with) and any other codec:
1000 // raw payload as-is.
1001 p_output_audio->write(data, size);
1002 }
1003 }
1004
1005 static void writeAdtsHeader(uint8_t* adts, int aacProfile, int sampleRateIdx,
1006 int channelCfg, int frameLen) {
1007 adts[0] = 0xFF;
1008 adts[1] = 0xF1;
1009 adts[2] = ((aacProfile - 1) << 6) | (sampleRateIdx << 2) |
1010 ((channelCfg >> 2) & 0x1);
1011 adts[3] = ((channelCfg & 0x3) << 6) | ((frameLen + 7) >> 11);
1012 adts[4] = ((frameLen + 7) >> 3) & 0xFF;
1013 adts[5] = (((frameLen + 7) & 0x7) << 5) | 0x1F;
1014 adts[6] = 0xFC;
1015 }
1016};
1017
1023 public:
1025
1026 void clear() { buffer.clear(); }
1027 size_t size() { return buffer.size(); }
1028 const uint8_t* data() { return buffer.data(); }
1029
1030 void u8(uint8_t v) { buffer.push_back(v); }
1031 void u16(uint16_t v) {
1032 u8((uint8_t)(v >> 8));
1033 u8((uint8_t)v);
1034 }
1035 void u24(uint32_t v) {
1036 u8((uint8_t)(v >> 16));
1037 u8((uint8_t)(v >> 8));
1038 u8((uint8_t)v);
1039 }
1040 void u32(uint32_t v) {
1041 u8((uint8_t)(v >> 24));
1042 u8((uint8_t)(v >> 16));
1043 u8((uint8_t)(v >> 8));
1044 u8((uint8_t)v);
1045 }
1046 void fourcc(const char* type) { bytes((const uint8_t*)type, 4); }
1047 void bytes(const uint8_t* data, size_t len) {
1048 size_t off = buffer.size();
1049 buffer.resize(off + len);
1050 memcpy(buffer.data() + off, data, len);
1051 }
1052 void zeros(size_t len) {
1053 size_t off = buffer.size();
1054 buffer.resize(off + len);
1055 memset(buffer.data() + off, 0, len);
1056 }
1057 void cstr(const char* s) {
1058 while (*s) u8((uint8_t)*s++);
1059 u8(0);
1060 }
1061
1064 size_t beginBox(const char* type) {
1065 size_t pos = buffer.size();
1066 u32(0);
1067 fourcc(type);
1068 return pos;
1069 }
1072 void endBox(size_t pos) {
1073 uint32_t sz = (uint32_t)(buffer.size() - pos);
1074 buffer[pos] = (uint8_t)(sz >> 24);
1075 buffer[pos + 1] = (uint8_t)(sz >> 16);
1076 buffer[pos + 2] = (uint8_t)(sz >> 8);
1077 buffer[pos + 3] = (uint8_t)sz;
1078 }
1079};
1080
1083
1163class MuxerMP4 : public Muxer {
1164 public:
1166 MuxerMP4(Print& out) : MuxerMP4() { setOutput(out); }
1167
1168 const char* mime() override { return "video/mp4"; }
1169
1171 void setOutput(Print& out) override { p_out = &out; }
1172
1174 void setVideoInfo(MuxerVideoConfig config) override { video_cfg = config; }
1175
1178
1187 void setAudioInfo(AudioInfoFormat info) override {
1189 audio_info = info;
1190 has_audio = true;
1191 }
1193 AudioInfoFormat& audioInfo() override { return audio_info; }
1197 void setAudioProfile(int aacProfile) { audio_profile = aacProfile; }
1198
1206 bool begin() override {
1207 if (p_out == nullptr) {
1208 LOGE("output not defined");
1209 return false;
1210 }
1211 if (video_cfg.width == 0 || video_cfg.height == 0) {
1212 LOGE("invalid video size: %d x %d", (int)video_cfg.width,
1213 (int)video_cfg.height);
1214 return false;
1215 }
1221 LOGE(
1222 "unsupported video format: %d - MuxerMP4 writes H264, MJPEG, "
1223 "YUV422, RGB565 or I420",
1224 (int)video_cfg.format);
1225 return false;
1226 }
1227 video_timescale = 90000;
1229 video_cfg.fps > 0 ? (uint32_t)(video_timescale / video_cfg.fps) : 0;
1231 // AAC: fixed 1024 samples/frame (LC). PCM: duration varies per call
1232 // (addAudioFrame() computes it from the actual byte length instead),
1233 // so this default is not used for PCM - trun always states it
1234 // explicitly per fragment regardless of codec.
1236
1237 sps_data.clear();
1238 pps_data.clear();
1239 moov_written = false;
1240 video_seq = 0;
1241 video_base_time = 0;
1242 audio_seq = 0;
1243 audio_base_time = 0;
1244 fragment_seq = 0;
1247 is_open = true;
1248 // MJPEG needs no stream-derived config (unlike H264's SPS/PPS), so
1249 // this writes 'moov' immediately; for H264 it's a no-op here and
1250 // happens lazily once SPS/PPS have been captured (see addVideoFrame()).
1251 tryWriteMoov();
1252 return true;
1253 }
1254
1256 void end() override { is_open = false; }
1257
1258 operator bool() override { return is_open; }
1259
1264 void setStreamType(StreamContentType type) override {
1265 write_stream_type = type;
1266 }
1269
1277 size_t write(const uint8_t* data, size_t len) override {
1279 return addAudioFrame(data, len);
1280 }
1281
1282 switch (video_cfg.format) {
1283 case VideoFormat::MJPEG:
1284 return addJpegFrame(data, len);
1286 return addYUV422Frame(data, len);
1288 return addRGB565Frame(data, len);
1289 case VideoFormat::I420:
1290 return addI420Frame(data, len);
1291 default:
1292 return addVideoFrame(data, len);
1293 }
1294 }
1295
1307 size_t addVideoFrame(const uint8_t* data, size_t len,
1308 bool isKeyFrame = true) override {
1309 if (!is_open || video_cfg.format != VideoFormat::H264) return 0;
1310 setVideoConfigData(data, len);
1311 if (!tryWriteMoov()) {
1312 LOGW("dropping video frame: SPS/PPS not seen yet");
1313 return 0;
1314 }
1315 nal_tmp.clear();
1316 forEachAnnexBNal(data, len, [this](const uint8_t* nal, size_t nalLen) {
1317 if (nalLen == 0) return;
1318 uint8_t nalType = nal[0] & 0x1F;
1319 if (nalType == 7 || nalType == 8) return; // SPS/PPS: already in avcC
1320 size_t off = nal_tmp.size();
1321 nal_tmp.resize(off + 4 + nalLen);
1322 uint8_t* p = nal_tmp.data() + off;
1323 p[0] = (uint8_t)(nalLen >> 24);
1324 p[1] = (uint8_t)(nalLen >> 16);
1325 p[2] = (uint8_t)(nalLen >> 8);
1326 p[3] = (uint8_t)nalLen;
1327 memcpy(p + 4, nal, nalLen);
1328 });
1329 if (nal_tmp.size() == 0) return 0;
1334 return len;
1335 }
1336
1341 size_t addJpegFrame(const uint8_t* data, size_t len) override {
1342 if (video_cfg.format != VideoFormat::MJPEG) return 0;
1343 return addRawFrame(data, len);
1344 }
1345
1354 size_t addYUV422Frame(const uint8_t* data, size_t len) override {
1356 return addRawFrame(data, len);
1357 }
1358
1365 size_t addRGB565Frame(const uint8_t* data, size_t len) override {
1367 return addRawFrame(data, len);
1368 }
1369
1381 size_t addI420Frame(const uint8_t* data, size_t len) override {
1383 return addRawFrame(data, len);
1384 }
1385
1395 size_t addAudioFrame(const uint8_t* data, size_t len) override {
1396 if (!is_open || !has_audio || len == 0) return 0;
1397 if (!moov_written) {
1398 LOGW(
1399 "dropping audio frame: moov not written yet (video SPS/PPS not "
1400 "seen)");
1401 return 0;
1402 }
1403 uint32_t duration = audio_sample_duration; // AAC: fixed 1024
1405 int bytesPerFrame =
1407 duration = bytesPerFrame > 0 ? (uint32_t)(len / bytesPerFrame) : 0;
1408 }
1409 writeMoofMdat(kAudioTrackId, data, len, duration,
1410 /*isKeyFrame*/ true, audio_base_time);
1411 audio_base_time += duration;
1413 return len;
1414 }
1415
1417 uint32_t videoFrameCount() { return video_frame_count; }
1419 uint32_t audioFrameCount() { return audio_frame_count; }
1420
1421 protected:
1422 static const uint32_t kVideoTrackId = 1;
1423 static const uint32_t kAudioTrackId = 2;
1424
1425 Print* p_out = nullptr;
1429
1431 bool has_audio = false;
1432 int audio_profile = 2; // AAC LC
1433
1434 bool is_open = false;
1437 bool moov_written = false;
1439 uint32_t video_timescale = 90000;
1441 uint32_t audio_timescale = 0;
1442 uint32_t audio_sample_duration = 1024;
1443
1444 uint32_t video_seq = 0;
1445 uint32_t video_base_time = 0;
1446 uint32_t audio_seq = 0;
1447 uint32_t audio_base_time = 0;
1448 uint32_t fragment_seq = 0;
1449 uint32_t video_frame_count = 0;
1450 uint32_t audio_frame_count = 0;
1451
1454
1460 void setVideoConfigData(const uint8_t* spsPpsAnnexB, size_t len) {
1461 forEachAnnexBNal(spsPpsAnnexB, len,
1462 [this](const uint8_t* nal, size_t nalLen) {
1463 if (nalLen == 0) return;
1464 uint8_t nalType = nal[0] & 0x1F;
1465 if (nalType == 7) {
1466 sps_data.resize(nalLen);
1467 memcpy(sps_data.data(), nal, nalLen);
1468 } else if (nalType == 8) {
1469 pps_data.resize(nalLen);
1470 memcpy(pps_data.data(), nal, nalLen);
1471 }
1472 });
1473 }
1474
1480 if (moov_written) return true;
1482 (sps_data.size() == 0 || pps_data.size() == 0)) {
1483 return false;
1484 }
1485 writeFtypMoov();
1486 moov_written = true;
1487 return true;
1488 }
1489
1491 if (video_cfg.format != expected) {
1492 LOGW("getVideoInfo().format does not match the addXxxFrame() called");
1493 }
1494 }
1495
1498 void checkRawFrame(VideoFormat expected, size_t len) {
1499 checkVideoFormat(expected);
1500 size_t expectedSize =
1502 if (expectedSize > 0 && len != expectedSize) {
1503 LOGW("frame size %d does not match the expected %d bytes for %d x %d",
1504 (int)len, (int)expectedSize, (int)video_cfg.width,
1505 (int)video_cfg.height);
1506 }
1507 }
1508
1516 size_t addRawFrame(const uint8_t* data, size_t len) {
1517 if (!is_open || len == 0) return 0;
1518 if (!tryWriteMoov()) return 0;
1520 /*isKeyFrame*/ true, video_base_time);
1523 return len;
1524 }
1525
1529 template <typename F>
1530 static void forEachAnnexBNal(const uint8_t* data, size_t len, F callback) {
1531 size_t i = 0;
1532 while (i + 3 <= len) {
1533 size_t scLen = 0;
1534 if (i + 4 <= len && data[i] == 0 && data[i + 1] == 0 &&
1535 data[i + 2] == 0 && data[i + 3] == 1) {
1536 scLen = 4;
1537 } else if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
1538 scLen = 3;
1539 }
1540 if (scLen == 0) {
1541 i++;
1542 continue;
1543 }
1544 size_t nalStart = i + scLen;
1545 size_t j = nalStart;
1546 size_t nextStart = len;
1547 while (j + 3 <= len) {
1548 if (data[j] == 0 && data[j + 1] == 0 &&
1549 (data[j + 2] == 1 ||
1550 (j + 3 < len && data[j + 2] == 0 && data[j + 3] == 1))) {
1551 nextStart = j;
1552 break;
1553 }
1554 j++;
1555 }
1556 if (nextStart > nalStart) callback(data + nalStart, nextStart - nalStart);
1557 i = nextStart;
1558 }
1559 }
1560
1561 static int aacSampleRateIndex(uint32_t sampleRate) {
1562 static const uint32_t rates[13] = {96000, 88200, 64000, 48000, 44100,
1563 32000, 24000, 22050, 16000, 12000,
1564 11025, 8000, 7350};
1565 for (int i = 0; i < 13; i++)
1566 if (rates[i] == sampleRate) return i;
1567 return 4; // default: 44100
1568 }
1569
1571 size_t pos = b.beginBox("avcC");
1572 b.u8(1); // configurationVersion
1573 b.u8(sps_data[1]); // AVCProfileIndication
1574 b.u8(sps_data[2]); // profile_compatibility
1575 b.u8(sps_data[3]); // AVCLevelIndication
1576 b.u8(0xFF); // reserved(6) + lengthSizeMinusOne(2) = 4-byte lengths
1577 b.u8(0xE1); // reserved(3) + numOfSPS(5) = 1
1578 b.u16((uint16_t)sps_data.size());
1580 b.u8(1); // numOfPPS
1581 b.u16((uint16_t)pps_data.size());
1583 b.endBox(pos);
1584 }
1585
1592 size_t pos = b.beginBox("esds");
1593 b.u32(0); // version + flags
1594 b.u8(0x03);
1595 // size: ES_ID(2)+flags(1)+DecoderConfigDescr(15 incl. its own tag+size)
1596 // +SLConfigDescr(3 incl. its own tag+size) = 21
1597 b.u8(21);
1598 b.u16(kVideoTrackId); // ES_ID
1599 b.u8(0); // flags
1600 b.u8(0x04);
1601 b.u8(13); // size: objType(1)+streamType(1)+bufSize(3)+maxBr(4)+avgBr(4)
1602 b.u8(0x6C); // objectTypeIndication: JPEG (ISO/IEC 10918-1)
1603 b.u8((4 << 2) | 1); // streamType: visual(4), reserved bit set
1604 b.u24(0); // bufferSizeDB
1605 b.u32(0); // maxBitrate (0 = unspecified)
1606 b.u32(0); // avgBitrate
1607 b.u8(0x06);
1608 b.u8(1);
1609 b.u8(0x02);
1610 b.endBox(pos);
1611 }
1612
1614 int sampleRateIdx = aacSampleRateIndex(audio_info.sample_rate);
1615 uint8_t asc[2];
1616 asc[0] = (uint8_t)((audio_profile << 3) | (sampleRateIdx >> 1));
1617 asc[1] = (uint8_t)(((sampleRateIdx & 1) << 7) | (audio_info.channels << 3));
1618
1619 size_t pos = b.beginBox("esds");
1620 b.u32(0); // version + flags
1621 // ES_Descriptor
1622 b.u8(0x03);
1623 // size: ES_ID(2)+flags(1)+DecoderConfigDescr(19 incl. its own tag+size)
1624 // +SLConfigDescr(3 incl. its own tag+size) = 25
1625 b.u8(25);
1626 b.u16(kAudioTrackId); // ES_ID
1627 b.u8(0); // flags
1628 // DecoderConfigDescr
1629 b.u8(0x04);
1630 b.u8(
1631 17); // size:
1632 // objType(1)+streamType(1)+bufSize(3)+maxBr(4)+avgBr(4)+DecSpecificInfo(4)
1633 b.u8(0x40); // objectTypeIndication: AAC
1634 b.u8((5 << 2) | 1); // streamType: audio(5), upstream=0, reserved=1
1635 b.u24(0); // bufferSizeDB
1636 b.u32(128000); // maxBitrate
1637 b.u32(128000); // avgBitrate
1638 // DecSpecificInfo (the 2-byte AudioSpecificConfig)
1639 b.u8(0x05);
1640 b.u8(2);
1641 b.bytes(asc, 2);
1642 // SLConfigDescr
1643 b.u8(0x06);
1644 b.u8(1);
1645 b.u8(0x02);
1646 b.endBox(pos);
1647 }
1648
1650 size_t pos = b.beginBox("mvhd");
1651 b.u32(0); // version + flags
1652 b.u32(0); // creation_time
1653 b.u32(0); // modification_time
1654 b.u32(1000); // timescale
1655 b.u32(0); // duration (unknown - fragmented)
1656 b.u32(0x00010000); // rate 1.0
1657 b.u16(0x0100); // volume 1.0
1658 b.u16(0); // reserved
1659 b.u32(0);
1660 b.u32(0); // reserved[2]
1661 // unity matrix
1662 static const uint32_t identity[9] = {
1663 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
1664 for (int i = 0; i < 9; i++) b.u32(identity[i]);
1665 b.zeros(24); // pre_defined[6]
1666 b.u32(has_audio ? kAudioTrackId + 1 : kVideoTrackId + 1); // next_track_ID
1667 b.endBox(pos);
1668 }
1669
1670 void writeTkhd(MP4BoxWriter& b, uint32_t trackId, bool isVideo) {
1671 size_t pos = b.beginBox("tkhd");
1672 b.u32(0x000007); // version 0 + flags: enabled|in_movie|in_preview
1673 b.u32(0); // creation_time
1674 b.u32(0); // modification_time
1675 b.u32(trackId);
1676 b.u32(0); // reserved
1677 b.u32(0); // duration (unknown)
1678 b.u32(0);
1679 b.u32(0); // reserved[2]
1680 b.u16(0); // layer
1681 b.u16(0); // alternate_group
1682 b.u16(isVideo ? 0 : 0x0100); // volume
1683 b.u16(0); // reserved
1684 static const uint32_t identity[9] = {
1685 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
1686 for (int i = 0; i < 9; i++) b.u32(identity[i]);
1687 b.u32(isVideo ? ((uint32_t)video_cfg.width << 16) : 0);
1688 b.u32(isVideo ? ((uint32_t)video_cfg.height << 16) : 0);
1689 b.endBox(pos);
1690 }
1691
1692 void writeMdhd(MP4BoxWriter& b, uint32_t timescale) {
1693 size_t pos = b.beginBox("mdhd");
1694 b.u32(0); // version + flags
1695 b.u32(0); // creation_time
1696 b.u32(0); // modification_time
1697 b.u32(timescale);
1698 b.u32(0); // duration (unknown)
1699 b.u16(0x55C4); // language: und
1700 b.u16(0);
1701 b.endBox(pos);
1702 }
1703
1704 void writeHdlr(MP4BoxWriter& b, const char* handlerType, const char* name) {
1705 size_t pos = b.beginBox("hdlr");
1706 b.u32(0); // version + flags
1707 b.u32(0); // pre_defined
1708 b.fourcc(handlerType);
1709 b.zeros(12); // reserved
1710 b.cstr(name);
1711 b.endBox(pos);
1712 }
1713
1715 size_t pos = b.beginBox("dinf");
1716 size_t drefPos = b.beginBox("dref");
1717 b.u32(0); // version + flags
1718 b.u32(1); // entry_count
1719 size_t urlPos = b.beginBox("url ");
1720 b.u32(1); // version + flags: self-contained (data in this file)
1721 b.endBox(urlPos);
1722 b.endBox(drefPos);
1723 b.endBox(pos);
1724 }
1725
1727 size_t pos = b.beginBox("stbl");
1728 size_t stsdPos = b.beginBox("stsd");
1729 b.u32(0); // version + flags
1730 b.u32(1); // entry_count
1731 // VisualSampleEntry: same fixed 78-byte header for every fourCC below
1732 const char* fourcc = "avc1";
1733 uint16_t depth = 0x0018; // 24, nominal - not critical for playback
1734 switch (video_cfg.format) {
1735 case VideoFormat::MJPEG:
1736 fourcc = "mp4v";
1737 break;
1739 fourcc = "yuvs"; // matches what ffmpeg itself writes for YUY2-in-MOV
1740 break;
1742 fourcc = "L565"; // matches what ffmpeg itself writes for rgb565le
1743 depth = 16; // actual bit depth, unlike the nominal 24 above
1744 break;
1745 case VideoFormat::I420:
1746 fourcc = "I420"; // see addI420Frame()'s note: unverified in MP4
1747 break;
1748 default:
1749 break; // H264: 'avc1'
1750 }
1751 size_t entryPos = b.beginBox(fourcc);
1752 b.zeros(6); // reserved
1753 b.u16(1); // data_reference_index
1754 b.u16(0); // pre_defined
1755 b.u16(0); // reserved
1756 b.zeros(12); // pre_defined[3]
1757 b.u16(video_cfg.width);
1758 b.u16(video_cfg.height);
1759 b.u32(0x00480000); // horizresolution 72dpi
1760 b.u32(0x00480000); // vertresolution 72dpi
1761 b.u32(0); // reserved
1762 b.u16(1); // frame_count
1763 b.zeros(32); // compressorname
1764 b.u16(depth);
1765 b.u16(0xFFFF); // pre_defined
1766 switch (video_cfg.format) {
1767 case VideoFormat::MJPEG:
1768 writeEsdsMjpeg(b);
1769 break;
1772 case VideoFormat::I420:
1773 break; // raw formats: no child config box needed
1774 default:
1775 writeAvcC(b); // H264
1776 break;
1777 }
1778 b.endBox(entryPos);
1779 b.endBox(stsdPos);
1780 // empty sample tables - samples are described per-fragment instead
1781 size_t sttsPos = b.beginBox("stts");
1782 b.u32(0);
1783 b.u32(0);
1784 b.endBox(sttsPos);
1785 size_t stscPos = b.beginBox("stsc");
1786 b.u32(0);
1787 b.u32(0);
1788 b.endBox(stscPos);
1789 size_t stszPos = b.beginBox("stsz");
1790 b.u32(0);
1791 b.u32(0);
1792 b.u32(0);
1793 b.endBox(stszPos);
1794 size_t stcoPos = b.beginBox("stco");
1795 b.u32(0);
1796 b.u32(0);
1797 b.endBox(stcoPos);
1798 b.endBox(pos);
1799 }
1800
1806 b.zeros(6); // reserved
1807 b.u16(1); // data_reference_index
1808 b.zeros(8); // reserved[2]
1809 b.u16((uint16_t)audio_info.channels);
1810 b.u16((uint16_t)audio_info.bits_per_sample);
1811 b.u16(0); // pre_defined
1812 b.u16(0); // reserved
1813 b.u32((uint32_t)audio_info.sample_rate << 16);
1814 }
1815
1817 size_t pos = b.beginBox("stbl");
1818 size_t stsdPos = b.beginBox("stsd");
1819 b.u32(0); // version + flags
1820 b.u32(1); // entry_count
1822 // 'sowt': little-endian signed PCM - the fixed AudioSampleEntry
1823 // header alone is a complete sample description, no child box
1824 // needed (unlike 'mp4a', which needs 'esds' for the AAC config).
1825 size_t sowtPos = b.beginBox("sowt");
1827 b.endBox(sowtPos);
1828 } else {
1829 size_t mp4aPos = b.beginBox("mp4a");
1831 writeEsds(b);
1832 b.endBox(mp4aPos);
1833 }
1834 b.endBox(stsdPos);
1835 size_t sttsPos = b.beginBox("stts");
1836 b.u32(0);
1837 b.u32(0);
1838 b.endBox(sttsPos);
1839 size_t stscPos = b.beginBox("stsc");
1840 b.u32(0);
1841 b.u32(0);
1842 b.endBox(stscPos);
1843 size_t stszPos = b.beginBox("stsz");
1844 b.u32(0);
1845 b.u32(0);
1846 b.u32(0);
1847 b.endBox(stszPos);
1848 size_t stcoPos = b.beginBox("stco");
1849 b.u32(0);
1850 b.u32(0);
1851 b.endBox(stcoPos);
1852 b.endBox(pos);
1853 }
1854
1855 void writeTrak(MP4BoxWriter& b, uint32_t trackId, bool isVideo) {
1856 size_t pos = b.beginBox("trak");
1857 writeTkhd(b, trackId, isVideo);
1858 size_t mdiaPos = b.beginBox("mdia");
1860 writeHdlr(b, isVideo ? "vide" : "soun",
1861 isVideo ? "VideoHandler" : "SoundHandler");
1862 size_t minfPos = b.beginBox("minf");
1863 if (isVideo) {
1864 size_t vmhdPos = b.beginBox("vmhd");
1865 b.u32(1); // version + flags
1866 b.zeros(8); // graphicsmode + opcolor
1867 b.endBox(vmhdPos);
1868 } else {
1869 size_t smhdPos = b.beginBox("smhd");
1870 b.u32(0); // version + flags
1871 b.u16(0); // balance
1872 b.u16(0); // reserved
1873 b.endBox(smhdPos);
1874 }
1875 writeDinf(b);
1876 if (isVideo) {
1877 writeVideoStbl(b);
1878 } else {
1879 writeAudioStbl(b);
1880 }
1881 b.endBox(minfPos);
1882 b.endBox(mdiaPos);
1883 b.endBox(pos);
1884 }
1885
1887 size_t pos = b.beginBox("mvex");
1888 size_t trexVideoPos = b.beginBox("trex");
1889 b.u32(0); // version + flags
1890 b.u32(kVideoTrackId);
1891 b.u32(1); // default_sample_description_index
1893 b.u32(0); // default_sample_size
1894 b.u32(0); // default_sample_flags
1895 b.endBox(trexVideoPos);
1896 if (has_audio) {
1897 size_t trexAudioPos = b.beginBox("trex");
1898 b.u32(0);
1899 b.u32(kAudioTrackId);
1900 b.u32(1);
1902 b.u32(0);
1903 b.u32(0);
1904 b.endBox(trexAudioPos);
1905 }
1906 b.endBox(pos);
1907 }
1908
1910 box.clear();
1911 size_t ftypPos = box.beginBox("ftyp");
1912 box.fourcc("isom");
1913 box.u32(0x200);
1914 box.fourcc("isom");
1915 box.fourcc("iso5");
1916 box.fourcc("iso6");
1917 box.fourcc("mp41");
1918 box.endBox(ftypPos);
1919
1920 size_t moovPos = box.beginBox("moov");
1921 writeMvhd(box);
1922 writeTrak(box, kVideoTrackId, true);
1923 if (has_audio) writeTrak(box, kAudioTrackId, false);
1924 writeMvex(box);
1925 box.endBox(moovPos);
1926
1927 p_out->write(box.data(), box.size());
1928 }
1929
1935 void writeMoofMdat(uint32_t trackId, const uint8_t* data, size_t len,
1936 uint32_t sampleDuration, bool isKeyFrame,
1937 uint32_t baseTime) {
1938 if (p_out == nullptr) return;
1939 box.clear();
1940 size_t moofPos = box.beginBox("moof");
1941 size_t mfhdPos = box.beginBox("mfhd");
1942 box.u32(0); // version + flags
1944 box.endBox(mfhdPos);
1945
1946 size_t trafPos = box.beginBox("traf");
1947 size_t tfhdPos = box.beginBox("tfhd");
1948 box.u32(0x020000); // version 0 + flags: default-base-is-moof
1949 box.u32(trackId);
1950 box.endBox(tfhdPos);
1951
1952 size_t tfdtPos = box.beginBox("tfdt");
1953 box.u32(0); // version 0 + flags
1954 box.u32(baseTime);
1955 box.endBox(tfdtPos);
1956
1957 bool isVideo = (trackId == kVideoTrackId);
1958 // data-offset | sample-duration | sample-size (always explicit per
1959 // fragment - relying on trex's constant default doesn't hold once a
1960 // track's per-sample duration can vary, as with PCM audio)
1961 uint32_t trunFlags = 0x000001 | 0x000100 | 0x000200;
1962 if (isVideo) trunFlags |= 0x000400; // + sample-flags
1963
1964 size_t trunPos = box.beginBox("trun");
1965 box.u32(trunFlags);
1966 box.u32(1); // sample_count
1967 size_t dataOffsetFieldPos = box.size();
1968 box.u32(0); // data_offset placeholder
1969 // per-sample fields, in the fixed order the spec mandates: duration,
1970 // size, flags
1971 box.u32(sampleDuration);
1972 box.u32((uint32_t)len);
1973 if (isVideo) {
1974 // sample_depends_on / sample_is_non_sync_sample
1975 box.u32(isKeyFrame ? 0x02000000 : 0x01010000);
1976 }
1977 box.endBox(trunPos);
1978 box.endBox(trafPos);
1979 box.endBox(moofPos);
1980
1981 uint32_t dataOffset = (uint32_t)(box.size() + 8); // + mdat header
1982 box.buffer[dataOffsetFieldPos] = (uint8_t)(dataOffset >> 24);
1983 box.buffer[dataOffsetFieldPos + 1] = (uint8_t)(dataOffset >> 16);
1984 box.buffer[dataOffsetFieldPos + 2] = (uint8_t)(dataOffset >> 8);
1985 box.buffer[dataOffsetFieldPos + 3] = (uint8_t)dataOffset;
1986
1987 p_out->write(box.data(), box.size());
1988
1989 uint8_t mdatHeader[8];
1990 uint32_t mdatSize = (uint32_t)(len + 8);
1991 mdatHeader[0] = (uint8_t)(mdatSize >> 24);
1992 mdatHeader[1] = (uint8_t)(mdatSize >> 16);
1993 mdatHeader[2] = (uint8_t)(mdatSize >> 8);
1994 mdatHeader[3] = (uint8_t)mdatSize;
1995 mdatHeader[4] = 'm';
1996 mdatHeader[5] = 'd';
1997 mdatHeader[6] = 'a';
1998 mdatHeader[7] = 't';
1999 p_out->write(mdatHeader, 8);
2000 p_out->write(data, len);
2001
2002 fragment_seq++;
2003 }
2004};
2005
2006} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGE(...)
Definition AudioLoggerIDF.h:30
Definition Arduino.h:56
virtual size_t write(const uint8_t *data, size_t len)
Definition Arduino.h:120
virtual void flush()
Definition Arduino.h:130
AudioInfo info
Definition AudioCodecsBase.h:77
void notifyAudioChange(AudioInfo info)
Definition AudioTypes.h:174
void clear()
same as reset
Definition Buffers.h:96
Common interface for demuxers (DemuxerAVI, DemuxerMP4) that split a container's video and (optional) ...
Definition ContainerCommon.h:164
DemuxerMP4 extracts both the audio and (H.264) video track from a general, interleaved MP4/ISO-BMFF s...
Definition ContainerMP4.h:52
MP4Parser parser
Definition ContainerMP4.h:277
void setOutput(Print &out) override
Definition ContainerMP4.h:122
Vector< ScheduleEntry > schedule
Definition ContainerMP4.h:295
bool mdat_seen
Definition ContainerMP4.h:298
void onTrak()
Definition ContainerMP4.h:488
void onEsds(const MP4Parser::Box &box)
Definition ContainerMP4.h:587
Vector< uint8_t > & audioALACMagicCookie()
Definition ContainerMP4.h:169
void dispatchVideo(Track &t, const uint8_t *data, size_t size, bool isFirst)
Definition ContainerMP4.h:921
void setupParser()
Definition ContainerMP4.h:369
void onStsd(MP4Parser::Box &box)
Definition ContainerMP4.h:510
size_t schedule_index
index into 'schedule'
Definition ContainerMP4.h:296
bool playback_start_set
Definition ContainerMP4.h:319
void buildSchedule()
Definition ContainerMP4.h:809
uint32_t samples_left_in_entry
Definition ContainerMP4.h:297
bool beginBoxAccum(const MP4Parser::Box &box)
Definition ContainerMP4.h:345
void setOutputVideo(VideoOutput &out)
Definition ContainerMP4.h:125
size_t sample_buffer_filled
Definition ContainerMP4.h:324
Vector< uint8_t > sample_buffer
Definition ContainerMP4.h:323
DemuxerMP4(VideoOutput &video_out, Print &audio_out)
Definition ContainerMP4.h:75
uint32_t samplesInChunk(Track &t, uint32_t chunkIndex0)
Number of samples in the given (0-based) chunk index, per 'stsc'.
Definition ContainerMP4.h:847
void flushVideo()
Definition ContainerMP4.h:984
~DemuxerMP4()
Definition ContainerMP4.h:81
bool is_active
Definition ContainerMP4.h:313
Print * p_output_audio
Definition ContainerMP4.h:300
void dispatchSample(Track &t, const uint8_t *data, size_t size, bool isFirst)
Definition ContainerMP4.h:912
static void writeAdtsHeader(uint8_t *adts, int aacProfile, int sampleRateIdx, int channelCfg, int frameLen)
Definition ContainerMP4.h:1005
VideoInfo getVideoInfo() override
Definition ContainerMP4.h:138
bool send_wav_header
Definition ContainerMP4.h:307
void onHdlr(MP4Parser::Box &box)
Definition ContainerMP4.h:494
AudioInfoFormat getAudioInfo() override
Definition ContainerMP4.h:161
Vector< Track * > tracks
Definition ContainerMP4.h:284
void setOutputAudio(Print &out) override
Definition ContainerMP4.h:116
uint32_t total_bytes_received
Definition ContainerMP4.h:312
bool box_accum_active
Definition ContainerMP4.h:338
void onAvc1(const MP4Parser::Box &box)
Definition ContainerMP4.h:611
void end() override
Definition ContainerMP4.h:109
AudioFormat audio_format
Definition ContainerMP4.h:306
Track * p_video_track
first video track found
Definition ContainerMP4.h:292
VideoOutput * p_video_out_video
Definition ContainerMP4.h:302
void onAlac(const MP4Parser::Box &box)
Definition ContainerMP4.h:599
void onStsz(MP4Parser::Box &box)
Definition ContainerMP4.h:714
void onMp4a(const MP4Parser::Box &box)
Definition ContainerMP4.h:522
size_t write(const uint8_t *data, size_t len) override
Definition ContainerMP4.h:178
void sendWavHeader()
Definition ContainerMP4.h:574
void advanceScheduleIfNeeded()
Advances to the next schedule entry once the current one is exhausted.
Definition ContainerMP4.h:861
void onStsc(MP4Parser::Box &box)
Definition ContainerMP4.h:756
static uint32_t readU32(const uint8_t *p)
Definition ContainerMP4.h:361
bool stsz_header_pending
Definition ContainerMP4.h:339
bool is_wav_header_sent
Definition ContainerMP4.h:309
void onHevc(const MP4Parser::Box &box)
Definition ContainerMP4.h:626
void onMdhd(MP4Parser::Box &box)
Definition ContainerMP4.h:672
Track * current_sample_track
Definition ContainerMP4.h:326
void dispatchAudio(Track &t, const uint8_t *data, size_t size, bool isFirst)
Definition ContainerMP4.h:989
Vector< uint8_t > nal_tmp
scratch buffer for Annex-B conversion
Definition ContainerMP4.h:359
void onMdat(MP4Parser::Box &box)
Definition ContainerMP4.h:799
void onStts(MP4Parser::Box &box)
Definition ContainerMP4.h:693
static uint64_t readU64(const uint8_t *p)
Definition ContainerMP4.h:364
DemuxerMP4()
Definition ContainerMP4.h:67
Print * p_video_out
Definition ContainerMP4.h:301
const char * mime() override
The container's MIME type (e.g. "video/avi", "video/mp4").
Definition ContainerMP4.h:83
void writeVideo(uint8_t *data, size_t size)
Definition ContainerMP4.h:976
Track * p_audio_track
first audio track found
Definition ContainerMP4.h:293
void freeTracks()
Definition ContainerMP4.h:288
void feed(const uint8_t *data, size_t len)
Definition ContainerMP4.h:873
uint32_t playback_start_ms
Definition ContainerMP4.h:320
bool current_sample_is_first_for_track
Definition ContainerMP4.h:327
Track * current_track
track currently being parsed (scoped by 'trak')
Definition ContainerMP4.h:285
void setSendWavHeader(bool flag) override
Definition ContainerMP4.h:561
SingleBuffer< uint8_t > box_accum
Definition ContainerMP4.h:331
bool begin() override
Definition ContainerMP4.h:85
static uint16_t readU16(const uint8_t *p)
Definition ContainerMP4.h:367
void setupAudioInfo(AudioFormat format, const uint8_t *entryData, size_t entrySize)
Definition ContainerMP4.h:542
uint32_t current_sample_size
Definition ContainerMP4.h:325
void onStco(MP4Parser::Box &box, bool is64)
Definition ContainerMP4.h:777
void endBoxAccum()
Call once a box_accum-using handler has fully processed a complete box.
Definition ContainerMP4.h:354
DemuxerMP4(Print &video_out, Print &audio_out)
Definition ContainerMP4.h:69
bool send_wav_header_explicit
Definition ContainerMP4.h:308
void setOutputVideo(Print &out) override
Definition ContainerMP4.h:124
void onAvcC(const MP4Parser::Box &box)
Definition ContainerMP4.h:636
Codec
Definition M4ACommonDemuxer.h:27
Minimal ISO-BMFF box builder: appends bytes to an in-memory buffer, with beginBox()/endBox() taking c...
Definition ContainerMP4.h:1022
size_t size()
Definition ContainerMP4.h:1027
void u32(uint32_t v)
Definition ContainerMP4.h:1040
void zeros(size_t len)
Definition ContainerMP4.h:1052
void fourcc(const char *type)
Definition ContainerMP4.h:1046
void u8(uint8_t v)
Definition ContainerMP4.h:1030
void u24(uint32_t v)
Definition ContainerMP4.h:1035
void bytes(const uint8_t *data, size_t len)
Definition ContainerMP4.h:1047
const uint8_t * data()
Definition ContainerMP4.h:1028
void clear()
Definition ContainerMP4.h:1026
size_t beginBox(const char *type)
Definition ContainerMP4.h:1064
void u16(uint16_t v)
Definition ContainerMP4.h:1031
void cstr(const char *s)
Definition ContainerMP4.h:1057
void endBox(size_t pos)
Definition ContainerMP4.h:1072
Vector< uint8_t > buffer
Definition ContainerMP4.h:1024
MP4Parser is a class that parses MP4 container files and extracts boxes (atoms). It provides a callba...
Definition MP4Parser.h:28
bool begin()
Initializes the parser.
Definition MP4Parser.h:107
void setCallback(BoxCallback cb)
Defines the generic callback for all boxes.
Definition MP4Parser.h:75
int parseString(const uint8_t *str, int len, int fileOffset=0, int level=0)
Trigger separate parsing (and callbacks) on the indicated string.
Definition MP4Parser.h:170
void setReference(void *ref)
Defines an optional reference. By default it is the parser itself.
Definition MP4Parser.h:69
bool findBox(const char *name, const uint8_t *data, size_t len, Box &result)
find box in box
Definition MP4Parser.h:197
size_t write(const uint8_t *data, size_t len)
Provide the data to the parser (in chunks if needed).
Definition MP4Parser.h:130
Common interface for muxers (MuxerAVI, MuxerMP4) that combine an already-encoded video track (and opt...
Definition ContainerCommon.h:42
Video track configuration for MuxerMP4 - update before calling begin().
Definition ContainerMP4.h:1163
StreamContentType write_stream_type
Definition ContainerMP4.h:1438
bool is_open
Definition ContainerMP4.h:1434
void setOutput(Print &out) override
Defines the output: e.g. a local File or a network Client.
Definition ContainerMP4.h:1171
void writeMoofMdat(uint32_t trackId, const uint8_t *data, size_t len, uint32_t sampleDuration, bool isKeyFrame, uint32_t baseTime)
Definition ContainerMP4.h:1935
void writeAudioStbl(MP4BoxWriter &b)
Definition ContainerMP4.h:1816
static int aacSampleRateIndex(uint32_t sampleRate)
Definition ContainerMP4.h:1561
uint32_t audio_base_time
Definition ContainerMP4.h:1447
size_t addVideoFrame(const uint8_t *data, size_t len, bool isKeyFrame=true) override
Definition ContainerMP4.h:1307
AudioInfoFormat audio_info
Definition ContainerMP4.h:1430
StreamContentType streamType() override
The track write() currently targets (see setStreamType())
Definition ContainerMP4.h:1268
Vector< uint8_t > sps_data
Definition ContainerMP4.h:1427
size_t addJpegFrame(const uint8_t *data, size_t len) override
Definition ContainerMP4.h:1341
uint32_t video_sample_duration
Definition ContainerMP4.h:1440
void checkVideoFormat(VideoFormat expected)
Definition ContainerMP4.h:1490
uint32_t audioFrameCount()
Number of audio frames (fragments) written so far.
Definition ContainerMP4.h:1419
void writeMdhd(MP4BoxWriter &b, uint32_t timescale)
Definition ContainerMP4.h:1692
uint32_t video_seq
Definition ContainerMP4.h:1444
void writeTkhd(MP4BoxWriter &b, uint32_t trackId, bool isVideo)
Definition ContainerMP4.h:1670
uint32_t video_timescale
Definition ContainerMP4.h:1439
void checkRawFrame(VideoFormat expected, size_t len)
Definition ContainerMP4.h:1498
void writeTrak(MP4BoxWriter &b, uint32_t trackId, bool isVideo)
Definition ContainerMP4.h:1855
bool has_audio
Definition ContainerMP4.h:1431
void setAudioProfile(int aacProfile)
Definition ContainerMP4.h:1197
void writeEsdsMjpeg(MP4BoxWriter &b)
Definition ContainerMP4.h:1591
uint32_t fragment_seq
Definition ContainerMP4.h:1448
void writeMvhd(MP4BoxWriter &b)
Definition ContainerMP4.h:1649
AudioInfoFormat & audioInfo() override
Provides read/write access to the audio track's AudioInfoFormat.
Definition ContainerMP4.h:1193
void writeHdlr(MP4BoxWriter &b, const char *handlerType, const char *name)
Definition ContainerMP4.h:1704
Vector< uint8_t > pps_data
Definition ContainerMP4.h:1428
void writeDinf(MP4BoxWriter &b)
Definition ContainerMP4.h:1714
void end() override
Closes the encoder: no trailer is required for playback.
Definition ContainerMP4.h:1256
void setVideoInfo(MuxerVideoConfig config) override
Defines the video track configuration - call before begin()
Definition ContainerMP4.h:1174
void setStreamType(StreamContentType type) override
Definition ContainerMP4.h:1264
uint32_t audio_timescale
Definition ContainerMP4.h:1441
size_t addAudioFrame(const uint8_t *data, size_t len) override
Definition ContainerMP4.h:1395
size_t write(const uint8_t *data, size_t len) override
Definition ContainerMP4.h:1277
void setAudioInfo(AudioInfoFormat info) override
Definition ContainerMP4.h:1187
uint32_t videoFrameCount()
Number of video frames (fragments) written so far.
Definition ContainerMP4.h:1417
MuxerMP4()
Definition ContainerMP4.h:1165
size_t addRawFrame(const uint8_t *data, size_t len)
Definition ContainerMP4.h:1516
Vector< uint8_t > nal_tmp
scratch buffer for Annex-B -> AVCC conversion
Definition ContainerMP4.h:1452
size_t addYUV422Frame(const uint8_t *data, size_t len) override
Definition ContainerMP4.h:1354
uint32_t audio_frame_count
Definition ContainerMP4.h:1450
static const uint32_t kAudioTrackId
Definition ContainerMP4.h:1423
void writeMvex(MP4BoxWriter &b)
Definition ContainerMP4.h:1886
size_t addI420Frame(const uint8_t *data, size_t len) override
Definition ContainerMP4.h:1381
uint32_t audio_sample_duration
Definition ContainerMP4.h:1442
Print * p_out
Definition ContainerMP4.h:1425
MP4BoxWriter box
scratch buffer, reused for 'moov' and each 'moof'
Definition ContainerMP4.h:1453
void writeFtypMoov()
Definition ContainerMP4.h:1909
MuxerMP4(Print &out)
Definition ContainerMP4.h:1166
size_t addRGB565Frame(const uint8_t *data, size_t len) override
Definition ContainerMP4.h:1365
void setVideoConfigData(const uint8_t *spsPpsAnnexB, size_t len)
Definition ContainerMP4.h:1460
const char * mime() override
Definition ContainerMP4.h:1168
bool moov_written
Definition ContainerMP4.h:1437
int audio_profile
Definition ContainerMP4.h:1432
static const uint32_t kVideoTrackId
Definition ContainerMP4.h:1422
bool tryWriteMoov()
Definition ContainerMP4.h:1479
bool begin() override
Definition ContainerMP4.h:1206
static void forEachAnnexBNal(const uint8_t *data, size_t len, F callback)
Definition ContainerMP4.h:1530
void writeVideoStbl(MP4BoxWriter &b)
Definition ContainerMP4.h:1726
void writeEsds(MP4BoxWriter &b)
Definition ContainerMP4.h:1613
uint32_t video_frame_count
Definition ContainerMP4.h:1449
uint32_t audio_seq
Definition ContainerMP4.h:1446
void writeAudioSampleEntryHeader(MP4BoxWriter &b)
Definition ContainerMP4.h:1805
void writeAvcC(MP4BoxWriter &b)
Definition ContainerMP4.h:1570
MuxerVideoConfig getVideoInfo() override
Provides the video track configuration.
Definition ContainerMP4.h:1177
uint32_t video_base_time
Definition ContainerMP4.h:1445
MuxerVideoConfig video_cfg
Definition ContainerMP4.h:1426
A simple Buffer implementation which just uses a (dynamically sized) array.
Definition Buffers.h:189
int available() override
provides the number of entries that are available to read
Definition Buffers.h:250
int writeArray(const T data[], int len) override
Fills the buffer data.
Definition Buffers.h:218
T * data()
Provides address of actual data.
Definition Buffers.h:301
bool resize(size_t size)
Resizes the buffer if supported: returns false if not supported.
Definition Buffers.h:322
int clearArray(int len) override
consumes len bytes and moves current data to the beginning
Definition Buffers.h:269
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
void push_back(T &&value)
Definition Vector.h:182
void clear()
Definition Vector.h:176
bool resize(size_t newSize, T value)
Definition Vector.h:266
T * data()
Definition Vector.h:316
int size()
Definition Vector.h:178
Abstract class for video playback. This class is used to assemble a complete video frame in memory....
Definition Video.h:192
virtual size_t write(const uint8_t *data, size_t len)=0
virtual void flush()
Definition Video.h:198
Parser for Wav header data for details see https://de.wikipedia.org/wiki/RIFF_WAVE.
Definition CodecWAV.h:78
void setAudioInfo(WAVAudioInfo info)
Sets the info in the header.
Definition CodecWAV.h:155
bool writeHeader(Print *out)
Just write a wav header to the indicated outputbu.
Definition CodecWAV.h:158
VideoFormat
Video codec/pixel-format identifier, shared by two unrelated uses: the (single) video stream of a con...
Definition Video.h:43
StreamContentType
Which track write() feeds, for muxers (MuxerAVI, MuxerMP4) that double as a plain,...
Definition Video.h:19
size_t videoFrameSizeBytes(VideoFormat format, uint16_t width, uint16_t height)
Fixed per-frame size (bytes) for a raw/uncompressed VideoFormat at the given resolution - 0 for compr...
Definition Video.h:61
AudioFormat
Audio format codes used by Microsoft e.g. in avi or wav files.
Definition AudioFormat.h:21
bool isWavFormat(AudioFormat format)
True if the wav code is handled via the WAV decoder (i.e. toMime() maps it to "audio/wav": PCM and al...
Definition AudioFormat.h:348
@ Audio
Definition Video.h:19
@ Video
Definition Video.h:19
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
uint32_t millis()
Returns the milliseconds since the start.
Definition Arduino.h:260
AudioInfo extended with a WAVEFORMATEX-style codec tag (the "wav code"): identifies the codec (PCM,...
Definition AudioFormat.h:389
AudioFormat format
Definition AudioFormat.h:398
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:57
virtual void logInfo(const char *source="")
Definition AudioTypes.h:121
Definition ContainerMP4.h:272
uint32_t sample_count
Definition ContainerMP4.h:274
Track * track
Definition ContainerMP4.h:273
sample-to-chunk table entry (stsc)
Definition ContainerMP4.h:187
uint32_t first_chunk
Definition ContainerMP4.h:188
uint32_t samples_per_chunk
Definition ContainerMP4.h:189
Definition ContainerMP4.h:195
uint32_t sample_count
Definition ContainerMP4.h:196
uint32_t sample_delta
Definition ContainerMP4.h:197
One track (audio or video) as found in 'moov'.
Definition ContainerMP4.h:201
Vector< uint32_t > sample_sizes
Definition ContainerMP4.h:217
Vector< uint8_t > alacMagicCookie
Definition ContainerMP4.h:207
int channelCfg
Definition ContainerMP4.h:206
Vector< uint64_t > chunk_offsets
Definition ContainerMP4.h:221
uint32_t stts_entry_remaining
Definition ContainerMP4.h:236
uint32_t timescale
Definition ContainerMP4.h:226
enum audio_tools::DemuxerMP4::Track::Kind kind
uint16_t height
Definition ContainerMP4.h:210
uint64_t nextPtsTicks()
Definition ContainerMP4.h:255
uint32_t next_sample_index
Definition ContainerMP4.h:230
uint8_t nal_length_size
Definition ContainerMP4.h:211
Kind
Definition ContainerMP4.h:202
Vector< SttsEntry > stts
Definition ContainerMP4.h:227
int aacProfile
Definition ContainerMP4.h:206
Vector< uint8_t > sps_pps_annexb
Definition ContainerMP4.h:212
Vector< StscEntry > stsc
Definition ContainerMP4.h:220
uint32_t sampleSize(uint32_t idx)
Definition ContainerMP4.h:239
Codec audio_codec
Definition ContainerMP4.h:205
uint16_t width
Definition ContainerMP4.h:210
int sampleRateIdx
Definition ContainerMP4.h:206
bool is_hevc_unsupported
Definition ContainerMP4.h:214
uint32_t stts_entry_idx
Definition ContainerMP4.h:235
uint32_t fixed_sample_size
Definition ContainerMP4.h:218
uint32_t sampleCount()
Definition ContainerMP4.h:245
uint32_t fixed_sample_count
Definition ContainerMP4.h:219
uint64_t next_pts_ticks
Definition ContainerMP4.h:237
bool avc_config_sent
Definition ContainerMP4.h:213
A parser for the ESDS segment to extract the relevant aac information.
Definition M4ACommonDemuxer.h:49
uint8_t audioObjectType
Definition M4ACommonDemuxer.h:50
uint8_t channelConfiguration
Definition M4ACommonDemuxer.h:52
uint8_t samplingRateIndex
Definition M4ACommonDemuxer.h:51
bool parse(const uint8_t *data, size_t size)
Definition M4ACommonDemuxer.h:56
Represents an individual box in the MP4 file.
Definition MP4Parser.h:33
const uint8_t * data
Pointer to box payload (not including header)
Definition MP4Parser.h:40
int available
Number of bytes available as data.
Definition MP4Parser.h:47
uint64_t file_offset
File offset where box starts.
Definition MP4Parser.h:46
size_t size
Size of payload including subboxes (not including header)
Definition MP4Parser.h:43
bool is_complete
True if the box data is complete.
Definition MP4Parser.h:48
int level
Nesting depth.
Definition MP4Parser.h:45
size_t data_size
Size of payload (not including header)
Definition MP4Parser.h:42
Shared video track configuration for muxers (MuxerAVI, MuxerMP4) - call before begin().
Definition ContainerCommon.h:11
float fps
Definition ContainerCommon.h:14
uint16_t height
Definition ContainerCommon.h:13
uint16_t width
Definition ContainerCommon.h:12
VideoFormat format
Definition ContainerCommon.h:15
Basic video information (width/height/codec/frame size), analogous to AudioInfo - common to both Demu...
Definition Video.h:102
uint32_t frame_size
Definition Video.h:115
uint32_t total_file_size
Definition Video.h:121
uint16_t height
Frame height in pixels.
Definition Video.h:106
uint16_t width
Frame width in pixels.
Definition Video.h:104
VideoFormat format
Video codec - VideoFormat::UNKNOWN if not (yet) determined.
Definition Video.h:111
Sound information which is available in the WAV header.
Definition CodecWAV.h:30
AudioFormat format
Definition CodecWAV.h:38
bool is_streamed
Definition CodecWAV.h:41
int block_align
Definition CodecWAV.h:40
int byte_rate
Definition CodecWAV.h:39