arduino-audio-tools
Loading...
Searching...
No Matches
ContainerAVI.h
Go to the documentation of this file.
1#pragma once
2#include <string.h>
10
11#define LIST_HEADER_SIZE 12
12#define CHUNK_HEADER_SIZE 8
13
14namespace audio_tools {
15
24public:
25 size_t writeArray(uint8_t *data, size_t len) {
26 int to_write = min(availableToWrite(), (size_t)len);
27 memmove(vector.data() + available_byte_count, data, to_write);
28 available_byte_count += to_write;
29 return to_write;
30 }
31 void consume(int size) {
32 // use pointer arithmetic (not the bounds-checked operator[]): when size
33 // == available_byte_count (buffer fully drained, common for chunks at
34 // least as big as the buffer capacity, e.g. large raw video frames),
35 // vector.data() + size legally points one-past-the-end and the memmove
36 // length is 0, which operator[] would reject even though it's a no-op
39 }
40 bool resize(size_t size) {
41 vector.resize(size + 4);
42 return vector.data() != nullptr;
43 }
44
45 uint8_t *data() { return vector.data(); }
46
48
49 size_t available() { return available_byte_count; }
50
51 void clear() {
53 memset(vector.data(), 0, vector.size());
54 }
55
56 bool isEmpty() { return available_byte_count == 0; }
57
58 size_t size() { return vector.size(); }
59
60 long indexOf(const char *str) {
61 uint8_t *ptr = (uint8_t *)memmem(vector.data(), available_byte_count, str,
62 strlen(str));
63 return ptr == nullptr ? -1l : ptr - vector.data();
64 }
65
66protected:
69};
70
73using FOURCC = char[4];
74
76 // FOURCC fcc;
77 // uint32_t cb;
81 uint32_t dwFlags;
82 uint32_t dwTotalFrames;
84 uint32_t dwStreams;
86 uint32_t dwWidth;
87 uint32_t dwHeight;
88 uint32_t dwReserved[4];
89};
90
91struct RECT {
92 uint32_t dwWidth;
93 uint32_t dwHeight;
94};
95
112
113// field widths mirror the on-disk 40-byte BITMAPINFOHEADER exactly (all
114// DWORD/uint32_t, except the two WORD/uint16_t fields) - do not widen these,
115// the decoder reinterpret_casts raw file bytes directly onto this struct
117 uint32_t biSize;
118 uint32_t biWidth;
119 uint32_t biHeight;
120 uint16_t biPlanes;
121 uint16_t biBitCount;
123 uint32_t biSizeImage;
126 uint32_t biClrUsed;
128};
129
139
140// struct WAVFormat {
141// uint16_t wFormatTag;
142// uint16_t nChannels;
143// uint32_t nSamplesPerSec;
144// uint32_t nAvgBytesPerSec;
145// uint16_t nBlockAlign;
146// };
147
149
163
171public:
172 void set(size_t currentPos, StrView id, size_t size, ParseObjectType type) {
173 set(currentPos, id.c_str(), size, type);
174 }
175
176 void set(size_t currentPos, const char *id, size_t size,
179 data_size = size;
181 start_pos = currentPos;
182 // allign on word
183 if (size % 2 != 0) {
184 data_size++;
185 }
186 end_pos = currentPos + data_size + 4;
187 // save FOURCC
188 if (id != nullptr) {
189 memcpy(chunk_id, id, 4);
190 chunk_id[4] = 0;
191 }
192 open = data_size;
193 }
194 const char *id() { return chunk_id; }
195 size_t size() { return data_size; }
199 size_t declaredSize() { return declared_size; }
200
202 bool isValid() {
203 switch (object_type) {
204 case AVIStreamData:
205 return isAudio() || isVideo();
206 case AVIChunk:
207 return open > 0;
208 case AVIList:
209 return true;
210 }
211 return false;
212 }
213
214 // for Chunk
215 AVIMainHeader *asAVIMainHeader(void *ptr) { return (AVIMainHeader *)ptr; }
217 return (AVIStreamHeader *)ptr;
218 }
219 WAVFormatX *asAVIAudioFormat(void *ptr) { return (WAVFormatX *)ptr; }
221 return (BitmapInfoHeader *)ptr;
222 }
223
224 // Zero-initialized so a default-constructed/not-yet-set() ParseObject is
225 // deterministically "empty" (open==0 => isValid()==false for the default
226 // AVIChunk object_type below) instead of reading whatever was previously
227 // on the stack at this memory - the same uninitialized-read hazard that
228 // caused a real, platform-dependent infinite loop in cleanupStack() (see
229 // its own comment) when it read an unset ParseObject's end_pos.
230 size_t open = 0;
231 size_t end_pos = 0;
232 size_t start_pos = 0;
233 size_t data_size = 0;
234 size_t declared_size = 0;
235
236 // for AVIStreamData
238 return object_type == AVIStreamData ? (chunk_id[1] << 8) | chunk_id[0] : 0;
239 }
240 bool isAudio() {
241 return object_type == AVIStreamData
242 ? chunk_id[2] == 'w' && chunk_id[3] == 'b'
243 : false;
244 }
246 return object_type == AVIStreamData
247 ? chunk_id[2] == 'd' && chunk_id[3] == 'b'
248 : false;
249 }
251 return object_type == AVIStreamData
252 ? chunk_id[2] == 'd' && chunk_id[3] == 'c'
253 : false;
254 }
256
257protected:
258 // ParseBuffer data_buffer;
259 char chunk_id[5] = {};
260 // Default is AVIChunk (not e.g. AVIList, whose isValid() is
261 // unconditionally true) so that a default-constructed/not-yet-set()
262 // ParseObject deterministically reports isValid()==false via the
263 // open==0 default above, instead of depending on whatever this member
264 // would otherwise be left as (see the class comment on the members
265 // above - this mirrors that fix).
267};
268
281class DemuxerAVI : public Demuxer {
282public:
291 DemuxerAVI(int bufferSize = 1024) {
292 parse_buffer.resize(bufferSize);
293 }
294
296 const char *mimeVideo() { return "video/avi"; }
297
299 const char *mime() override { return toMime(audio_info.wFormatTag); }
300
303 bool isValid(const uint8_t *data, size_t len) override {
304 return len >= 12 && memcmp(data, "RIFF", 4) == 0 &&
305 memcmp(data + 8, "AVI ", 4) == 0;
306 }
307
316 void setSendWavHeader(bool flag) override {
317 send_wav_header = flag;
319 }
320
321 bool begin() override {
323 header_is_avi = false;
324 is_parsing_active = true;
325 current_pos = 0;
326 header_is_avi = false;
328 is_metadata_ready = false;
329 is_wav_header_sent = false;
330 riff_file_size = 0;
331 return true;
332 }
333
337 void setOutputAudio(Print &out_stream) override {
338 p_output_audio = &out_stream;
339 }
340
345 virtual void setOutput(Print &out_stream) override {
346 setOutputAudio(out_stream);
347 }
348
350 void setMute(bool mute) { is_mute = mute; }
351
352 void setOutputVideo(Print &out_stream) override {
353 p_output_video = &out_stream;
354 }
355 void setOutputVideo(VideoOutput &out_stream) override {
356 p_output_video_video = &out_stream;
357 }
358
359 virtual size_t write(const uint8_t *data, size_t len) override {
360 LOGD("write: %d", (int)len);
361 int result = parse_buffer.writeArray((uint8_t *)data, len);
362 // Reading any chunk/stream-data header needs up to LIST_HEADER_SIZE (12)
363 // bytes (parseList()/tryParseList() read offsets 0-11; parseAVIStreamData
364 // /parseChunk only need 8) - none of those getInt()/getStr() calls guard
365 // against running past what's actually buffered. Calling parse() with
366 // fewer bytes available reads stale leftover bytes past
367 // available_byte_count (consume() shifts via memmove but never zeroes
368 // the vacated tail), producing a garbage FOURCC/size - exactly the
369 // "unknown subchunk"/huge-bogus-size corruption this avoids by simply
370 // waiting for more data to accumulate instead of parsing prematurely.
372 // we expect the first parse to succeed
373 if (parse()) {
374 // if so we process the parse_buffer
376 if (!parse())
377 break;
378 }
379 } else {
380 LOGD("Parse Error");
382 result = len;
383 is_parsing_active = false;
384 }
385 }
386 return result;
387 }
388
389 operator bool() override { return is_parsing_active; }
390
391 void end() override { is_parsing_active = false; };
392
395
398
401
402 const char *aviVideoFormat() { return video_format; }
403
406
414 VideoInfo result;
415 result.width = (uint16_t)video_info.biWidth;
416 result.height = (uint16_t)video_info.biHeight;
417 result.format =
419 result.frame_size =
420 (uint32_t)videoFrameSizeBytes(result.format, result.width, result.height);
423 ? 1000000.0f / main_header.dwMicroSecPerFrame
424 : 0;
425 return result;
426 }
427
428
439 AudioInfoFormat result(info);
441 return result;
442 }
443
448 void setValidationCallback(bool (*cb)(DemuxerAVI &avi)) {
449 validation_cb = cb;
450 }
451
453 int videoSeconds() { return video_seconds; }
454
455protected:
456 bool header_is_avi = false;
457 bool is_parsing_active = true;
481 long current_pos = 0;
482 long movi_end_pos = 0;
485 char video_format[5] = {0};
486 bool is_metadata_ready = false;
487 bool (*validation_cb)(DemuxerAVI &avi) = nullptr;
488 bool is_mute = false;
489 bool send_wav_header = false;
491 bool is_wav_header_sent = false;
495 uint32_t riff_file_size = 0;
497
499 return strncmp(stream_header[stream_header_idx].fccType, "auds", 4) == 0;
500 }
501
503 return strncmp(stream_header[stream_header_idx].fccType, "vids", 4) == 0;
504 }
505
506 // we return true if at least one parse step was successful
507 bool parse() {
508 bool result = true;
509 switch (parse_state) {
510 case ParseHeader: {
511 result = parseHeader();
512 if (result)
514 } break;
515
516 case ParseHdrl: {
517 ParseObject hdrl = parseList("hdrl");
518 result = hdrl.isValid();
519 if (result) {
521 }
522 } break;
523
524 case ParseAvih: {
525 ParseObject avih = parseChunk("avih");
526 result = avih.isValid();
527 if (result) {
530 consume(avih.size());
532 }
533 } break;
534
535 case ParseStrl: {
536 ParseObject strl = parseList("strl");
537 ParseObject strh = parseChunk("strh");
540 consume(strh.size());
542 } break;
543
544 case ParseStrf: {
545 ParseObject strf = parseChunk("strf");
546 if (isCurrentStreamAudio()) {
549 LOGI("audioFormat: %d (%x) - %s", (int)audio_info.wFormatTag,
552 consume(strf.size());
553 } else if (isCurrentStreamVideo()) {
556 LOGI("videoFormat: %s", aviVideoFormat());
558 video_format[4] = 0;
559 consume(strf.size());
560 } else {
561 result = false;
562 }
564 } break;
565
566 case AfterStrf: {
567 // ignore all data until we find a new List
568 int pos = parse_buffer.indexOf("LIST");
569 if (pos >= 0) {
570 consume(pos);
572 if (StrView(tmp.id()).equals("strl")) {
574 } else if (StrView(tmp.id()).equals("movi")) {
576 } else {
577 // e.g. ignore info
579 }
580 } else {
581 // no valid data, so throw it away, we keep the last 4 digits in case
582 // if it contains the beginning of a LIST
583 cleanupStack();
585 }
586 } break;
587
588 case ParseMovi: {
589 ParseObject movi = tryParseList();
590 if (StrView(movi.id()).equals("movi")) {
592 is_metadata_ready = true;
593 if (validation_cb)
595 processStack(movi);
596 movi_end_pos = movi.end_pos;
598 // trigger new write
599 result = false;
600 }
601 } break;
602
603 case SubChunk: {
604 // rec is optinal
605 ParseObject hdrl = tryParseList();
606 if (StrView(hdrl.id()).equals("rec")) {
608 processStack(hdrl);
609 }
610
615 LOGD("video:[%d]->[%d]", (int)current_stream_data.start_pos,
617 } else if (current_stream_data.isAudio()) {
618 LOGD("audio:[%d]->[%d]", (int)current_stream_data.start_pos,
620 } else {
621 // Not fatal - writeData() (SubChunkContinue) skips its payload below.
622 LOGW("unknown subchunk '%s' at %d, skipping %d bytes",
625 }
626
627 } break;
628
629 case SubChunkContinue: {
630 writeData();
631 if (open_subchunk_len == 0) {
633 (p_output_video != nullptr || p_output_video_video != nullptr)) {
634 if (p_output_video != nullptr) p_output_video->flush();
636 }
637 if (tryParseChunk("idx").isValid()) {
639 } else if (tryParseList("rec").isValid()) {
641 } else {
642 if (current_pos >= movi_end_pos) {
644 } else {
646 }
647 }
648 }
649 } break;
650
651 case ParseIgnore: {
652 LOGD("ParseIgnore");
654 } break;
655
656 default:
657 result = false;
658 break;
659 }
660 return result;
661 }
662
673
681 if (is_wav_header_sent || p_output_audio == nullptr) return;
682 WAVAudioInfo winfo(info);
684 winfo.is_streamed = true;
687 winfo.ext_adpcm_header = true;
688 WAVHeader wav_header;
689 wav_header.setAudioInfo(winfo);
690 wav_header.writeHeader(p_output_audio);
691 is_wav_header_sent = true;
692 }
693
695 memcpy(video_format, stream_header[stream_header_idx].fccHandler, 4);
697 if (vh->dwScale <= 0) {
698 vh->dwScale = 1;
699 }
700 int rate = vh->dwRate / vh->dwScale;
701 video_seconds = rate <= 0 ? 0 : vh->dwLength / rate;
702 LOGI("videoSeconds: %d seconds", video_seconds);
703 }
704
709 static VideoFormat toVideoFormat(uint32_t biCompression,
710 uint16_t biBitCount) {
711 if (biCompression == 0) return VideoFormat::RAW; // BI_RGB
712 if (biCompression == 3) // BI_BITFIELDS
713 return biBitCount == 16 ? VideoFormat::RGB565 : VideoFormat::RAW;
714
715 auto is = [biCompression](const char *fourcc) {
716 return memcmp(&biCompression, fourcc, 4) == 0;
717 };
718 if (is("H264") || is("h264") || is("X264") || is("x264") ||
719 is("avc1") || is("AVC1"))
720 return VideoFormat::H264;
721 if (is("MJPG") || is("mjpg"))
722 return VideoFormat::MJPEG;
723 if (is("FMP4") || is("XVID") || is("DIVX") || is("DX50") ||
724 is("mp4v"))
725 return VideoFormat::MPEG4;
726 if (is("YUY2") || is("YUYV") || is("yuy2") || is("yuyv"))
727 return VideoFormat::YUV422;
728 if (is("I420") || is("IYUV"))
729 return VideoFormat::I420;
730 if (is("RGBP"))
731 return VideoFormat::RGB565;
733 }
734
735 void writeData() {
736 long to_write = min((long)parse_buffer.available(), open_subchunk_len);
737 // open_subchunk_len spans the word-aligned chunk (current_stream_data
738 // .size()), which for an odd declared size includes a trailing RIFF pad
739 // byte that must be consumed from the buffer but not delivered as
740 // payload to the audio/video sink - clip to the real, unpadded length.
741 long already_written = (long)current_stream_data.size() - open_subchunk_len;
742 long payload_left = (long)current_stream_data.declaredSize() - already_written;
743 if (payload_left < 0) payload_left = 0;
744 long payload_to_write = min(to_write, payload_left);
745
747 LOGD("audio %d", (int)to_write);
748 if (!is_mute && payload_to_write > 0 && p_output_audio != nullptr){
749 p_output_audio->write(parse_buffer.data(), payload_to_write);
750 }
751 open_subchunk_len -= to_write;
752 cleanupStack();
753 consume(to_write);
754 } else if (current_stream_data.isVideo()) {
755 LOGD("video %d", (int)to_write);
756 // Accumulate rather than forward each piece immediately: a video
757 // chunk (one whole encoded frame) is routinely bigger than
758 // parse_buffer's capacity, so writeData() runs several times per
759 // chunk as more bytes stream in - forwarding each piece straight to
760 // p_output_video as it arrived used to split H.264 NAL units across
761 // separate write() calls at arbitrary byte offsets. TinyH264's
762 // NalReader resets fresh on every write() with no memory of an
763 // incomplete trailing NAL, so a mid-NAL split silently truncated
764 // that NAL and discarded its real continuation bytes - confirmed by
765 // byte-diffing exactly what H264Decoder received against a known-
766 // good ffmpeg extraction of the same stream (identical content,
767 // wrong call boundaries). Buffering the whole chunk here and
768 // delivering it in one write() call once open_subchunk_len reaches
769 // 0 below sidesteps that entirely - the same "one complete
770 // NAL-aligned buffer per frame" delivery DemuxerMP4 already does
771 // (trivially, since MP4's sample table gives it each frame's exact
772 // size up front).
773 if (payload_to_write > 0) {
774 size_t offset = video_accumulator.size();
775 video_accumulator.resize(offset + payload_to_write);
776 memcpy(video_accumulator.data() + offset, parse_buffer.data(),
777 payload_to_write);
778 }
779 open_subchunk_len -= to_write;
780 if (open_subchunk_len == 0 && video_accumulator.size() > 0) {
781 if (p_output_video != nullptr)
784 if (p_output_video_video != nullptr)
788 }
789 cleanupStack();
790 consume(to_write);
791 } else {
792 // Unknown subchunk (not audio/video, e.g. an index or padding chunk) -
793 // skip its payload without delivering it anywhere, using the same
794 // word-aligned length already parsed, so the demuxer keeps making
795 // progress instead of getting stuck reprocessing it.
796 LOGD("skipping unknown subchunk %d", (int)to_write);
797 open_subchunk_len -= to_write;
798 cleanupStack();
799 consume(to_write);
800 }
801 }
802
803 // 'RIFF' fileSize fileType (data)
804 bool parseHeader() {
805 bool header_is_avi = false;
806 int headerSize = 12;
807 if (getStr(0, 4).equals("RIFF")) {
808 ParseObject result;
809 uint32_t header_file_size = getInt(4);
810 // chunk_size = file_size - 8; treat a near-max value (unbounded/
811 // streamed placeholder, mirroring WAVHeader's own convention) as
812 // "unknown" rather than wrapping around when normalizing to file_size
814 header_file_size >= 0xFFFFFFF0 ? 0 : header_file_size + 8;
815 header_is_avi = getStr(8, 4).equals("AVI ");
816 result.set(current_pos, "AVI ", header_file_size, AVIChunk);
817 processStack(result);
818 consume(headerSize);
819
820 } else {
821 LOGE("parseHeader");
822 }
823 return header_is_avi;
824 }
825
829 ParseObject result;
830 result.set(current_pos, getStr(0, 4), 0, AVIChunk);
831 return result;
832 }
833
836 ParseObject tryParseChunk(const char *id) {
837 ParseObject result;
838 if (getStr(0, 4).equals(id)) {
839 result.set(current_pos, id, 0, AVIChunk);
840 }
841 return result;
842 }
843
844 ParseObject tryParseList(const char *id) {
845 ParseObject result;
846 StrView &list_id = getStr(8, 4);
847 if (list_id.equals(id) && getStr(0, 3).equals("LIST")) {
848 result.set(current_pos, getStr(8, 4), getInt(4), AVIList);
849 }
850 return result;
851 }
852
855 ParseObject result;
856 if (getStr(0, 4).equals("LIST")) {
857 result.set(current_pos, getStr(8, 4), getInt(4), AVIList);
858 }
859 return result;
860 }
861
863 ParseObject parseChunk(const char *id) {
864 ParseObject result;
865 int chunk_size = getInt(4);
866 if (getStr(0, 4).equals(id) && parse_buffer.size() >= chunk_size) {
867 result.set(current_pos, id, chunk_size, AVIChunk);
868 processStack(result);
870 }
871 return result;
872 }
873
875 ParseObject parseList(const char *id) {
876 ParseObject result;
877 if (getStr(0, 4).equals("LIST") && getStr(8, 4).equals(id)) {
878 int size = getInt(4);
879 result.set(current_pos, id, size, AVIList);
880 processStack(result);
882 }
883 return result;
884 }
885
887 ParseObject result;
888 int size = getInt(4);
889 result.set(current_pos, getStr(0, 4), size, AVIStreamData);
890 // Always consume the 8-byte chunk header, even for a FOURCC we don't
891 // recognize as audio/video (e.g. an OpenDML 'ix00'/'ix01' index chunk or
892 // 'JUNK' padding embedded inside 'movi') - otherwise current_pos never
893 // advances past it and every subsequent parse() call re-reads the same
894 // unknown header forever (writeData() has nothing to consume for it),
895 // stalling the whole demuxer.
896 processStack(result);
897 consume(8);
898 return result;
899 }
900
901 void processStack(ParseObject &result) {
902 cleanupStack();
903 object_stack.push(result);
904 spaces.setChars(' ', object_stack.size());
905 LOGD("%s - %s (%d-%d) size:%d", spaces.c_str(), result.id(),
906 (int)result.start_pos, (int)result.end_pos, (int)result.data_size);
907 }
908
910 ParseObject current;
911 // make sure that we remove the object from the stack if we past the end.
912 // peek() returns false (leaving 'current' untouched) once the stack is
913 // empty - checking that return value, not just current.end_pos, avoids
914 // reading current's garbage initial value on an empty stack (e.g. the
915 // very first call, before anything has ever been pushed): that garbage
916 // is platform/compiler-dependent, and this loop previously kept
917 // spinning forever on some builds (observed hanging on ESP32-S3 while
918 // an x86-64 desktop build of the exact same source/input happened not
919 // to) whenever it looked <= current_pos.
920 while (object_stack.peek(current) && current.end_pos <= current_pos) {
921 object_stack.pop(current);
922 }
923 }
924
926 StrView &getStr(int offset, int len) {
927 str.setCapacity(len + 1);
928 const char *data = (const char *)parse_buffer.data();
929 str.copyFrom((data + offset), len, 5);
930
931 return str;
932 }
933
935 uint32_t getInt(int offset) {
936 uint32_t *result = (uint32_t *)(parse_buffer.data() + offset);
937 return *result;
938 }
939
941 void consume(int len) {
942 current_pos += len;
944 }
945};
946
949
989class MuxerAVI : public Muxer {
990 public:
992 MuxerAVI(Print &out) : MuxerAVI() { setOutput(out); }
993
994 const char *mimeVideo() override { return "video/avi"; }
995
997 void setOutput(Print &out) override { p_out = &out; }
998
1000 void setVideoInfo(MuxerVideoConfig config) override { video_cfg = config; }
1001
1005
1011 void setAudioInfo(AudioInfoFormat info) override {
1012 audio_info = info;
1013 has_audio = true;
1014 }
1016 AudioInfoFormat &audioInfo() override { return audio_info; }
1017
1020 bool begin() override {
1021 if (p_out == nullptr) {
1022 LOGE("output not defined");
1023 return false;
1024 }
1025 if (video_cfg.width == 0 || video_cfg.height == 0) {
1026 LOGE("invalid video size: %d x %d", (int)video_cfg.width,
1027 (int)video_cfg.height);
1028 return false;
1029 }
1030 writeHeader();
1033 frame_open = false;
1034 is_open = true;
1035 return true;
1036 }
1037
1041 void setStreamType(StreamContentType type) override { write_stream_type = type; }
1044
1046 void end() override { is_open = false; }
1047
1048 operator bool() override { return is_open; }
1049
1054 void beginFrame(size_t size) {
1055 if (!is_open) return;
1056 writeChunkHeader("00dc", (uint32_t)size);
1057 frame_open = true;
1058 frame_remaining = size;
1059 frame_pad = (size % 2) != 0;
1060 }
1061
1067 size_t writeFrame(const uint8_t *data, size_t len) {
1068 if (!is_open || !frame_open) return 0;
1069 size_t to_write = len < frame_remaining ? len : frame_remaining;
1070 size_t written = p_out->write(data, to_write);
1071 frame_remaining -= written;
1072 return written;
1073 }
1074
1076 uint32_t endFrame() {
1077 if (frame_open && frame_pad) p_out->write((uint8_t)0);
1078 frame_open = false;
1080 return 0;
1081 }
1082
1090 size_t write(const uint8_t *data, size_t len) override {
1092 return addAudioFrame(data, len);
1093 }
1094 switch (video_cfg.format) {
1095 case VideoFormat::MJPEG:
1096 return addJpegFrame(data, len);
1098 return addYUV422Frame(data, len);
1100 return addRGB565Frame(data, len);
1101 case VideoFormat::I420:
1102 return addI420Frame(data, len);
1103 default:
1104 return addVideoFrame(data, len);
1105 }
1106 }
1107
1116 size_t addVideoFrame(const uint8_t *data, size_t len,
1117 bool isKeyFrame = true) override {
1118 beginFrame(len);
1119 size_t written = writeFrame(data, len);
1120 endFrame();
1121 return written;
1122 }
1123
1128 size_t addJpegFrame(const uint8_t *data, size_t len) override {
1130 return addVideoFrame(data, len);
1131 }
1132
1135 size_t addYUV422Frame(const uint8_t *data, size_t len) override {
1137 return addVideoFrame(data, len);
1138 }
1139
1142 size_t addRGB565Frame(const uint8_t *data, size_t len) override {
1144 return addVideoFrame(data, len);
1145 }
1146
1150 size_t addI420Frame(const uint8_t *data, size_t len) override {
1152 return addVideoFrame(data, len);
1153 }
1154
1157 size_t addAudioFrame(const uint8_t *data, size_t len) override {
1158 if (!is_open || !has_audio) return 0;
1159 writeChunkHeader("01wb", (uint32_t)len);
1160 size_t written = p_out->write(data, len);
1161 if (len % 2 != 0) p_out->write((uint8_t)0);
1163 return written;
1164 }
1165
1167 uint32_t videoFrameCount() { return video_frame_count; }
1169 uint32_t audioChunkCount() { return audio_chunk_count; }
1170
1171 protected:
1172 static const uint32_t AVIF_ISINTERLEAVED = 0x00000100;
1173
1174 Print *p_out = nullptr;
1178 bool has_audio = false;
1179 bool is_open = false;
1180 bool frame_open = false;
1181 bool frame_pad = false;
1183 uint32_t video_frame_count = 0;
1184 uint32_t audio_chunk_count = 0;
1185
1186 const char *fourCC() {
1187 if (video_cfg.fourcc != nullptr) return video_cfg.fourcc;
1188 switch (video_cfg.format) {
1189 case VideoFormat::H264:
1190 return "H264";
1191 case VideoFormat::MJPEG:
1192 return "MJPG";
1193 case VideoFormat::MPEG4:
1194 return "FMP4";
1195 case VideoFormat::RAW:
1196 return "DIB ";
1198 return "YUY2";
1200 return "RGBP";
1201 case VideoFormat::I420:
1202 return "I420";
1204 return "H264";
1205 }
1206 return "H264";
1207 }
1208
1212
1215 uint16_t biBitCount() {
1216 switch (video_cfg.format) {
1217 case VideoFormat::RAW:
1218 return 24;
1221 return 16;
1222 case VideoFormat::I420:
1223 return 12;
1224 default:
1225 return 24;
1226 }
1227 }
1228
1233 size_t px = (size_t)video_cfg.width * (size_t)video_cfg.height;
1234 switch (format) {
1235 case VideoFormat::RAW:
1236 return px * 3;
1239 return px * 2;
1240 case VideoFormat::I420:
1241 return px + px / 2;
1242 default:
1243 return 0;
1244 }
1245 }
1246
1249 uint32_t biSizeImage() { return (uint32_t)expectedRawFrameSize(video_cfg.format); }
1250
1255 writeU32(0); // BI_RGB
1256 } else if (video_cfg.format == VideoFormat::RGB565) {
1257 writeU32(3); // BI_BITFIELDS
1258 } else {
1259 writeFourCC(fourCC()); // e.g. H264, MJPG, FMP4, YUY2, I420
1260 }
1261 }
1262
1266 if (video_cfg.format != expected) {
1267 LOGW("getVideoInfo().format does not match the addXxxFrame() called");
1268 }
1269 }
1270
1273 void checkRawFrame(VideoFormat expected, size_t len) {
1274 checkVideoFormat(expected);
1275 size_t expected_size = expectedRawFrameSize(expected);
1276 if (expected_size > 0 && len != expected_size) {
1277 LOGW("frame size %d does not match the expected %d bytes for %d x %d",
1278 (int)len, (int)expected_size, (int)video_cfg.width,
1279 (int)video_cfg.height);
1280 }
1281 }
1282
1283 void writeU8(uint8_t v) { p_out->write(v); }
1284 void writeU16(uint16_t v) {
1285 uint8_t b[2] = {(uint8_t)(v & 0xFF), (uint8_t)((v >> 8) & 0xFF)};
1286 p_out->write(b, 2);
1287 }
1288 void writeI16(int16_t v) { writeU16((uint16_t)v); }
1289 void writeU32(uint32_t v) {
1290 uint8_t b[4] = {(uint8_t)(v & 0xFF), (uint8_t)((v >> 8) & 0xFF),
1291 (uint8_t)((v >> 16) & 0xFF), (uint8_t)((v >> 24) & 0xFF)};
1292 p_out->write(b, 4);
1293 }
1294 void writeFourCC(const char *cc) { p_out->write((const uint8_t *)cc, 4); }
1295 void writeZeros(int n) {
1296 for (int j = 0; j < n; j++) writeU8(0);
1297 }
1298 void writeChunkHeader(const char *id, uint32_t size) {
1299 writeFourCC(id);
1300 writeU32(size);
1301 }
1302
1305 uint32_t videoStrfSize() { return 40 + (isBitfields() ? 12 : 0); }
1306
1309 uint32_t videoStrlSize() { return 4 + (8 + 56) + (8 + videoStrfSize()); }
1312 uint32_t audioStrlSize() { return 4 + (8 + 56) + (8 + 18); }
1313
1315 uint32_t micros_per_frame =
1316 video_cfg.fps > 0 ? (uint32_t)(1000000.0f / video_cfg.fps) : 0;
1317 writeChunkHeader("avih", 56);
1318 writeU32(micros_per_frame); // dwMicroSecPerFrame
1319 writeU32(0); // dwMaxBytesPerSec
1320 writeU32(0); // dwPaddingGranularity
1321 writeU32(has_audio ? AVIF_ISINTERLEAVED : 0); // dwFlags
1322 writeU32(0); // dwTotalFrames (unknown)
1323 writeU32(0); // dwInitialFrames
1324 writeU32(has_audio ? 2 : 1); // dwStreams
1325 writeU32(0); // dwSuggestedBufferSize
1326 writeU32(video_cfg.width); // dwWidth
1327 writeU32(video_cfg.height); // dwHeight
1328 writeZeros(16); // dwReserved[4]
1329 }
1330
1332 writeFourCC("LIST");
1334 writeFourCC("strl");
1335
1336 // strh (AVIStreamHeader)
1337 writeChunkHeader("strh", 56);
1338 writeFourCC("vids");
1340 writeU32(0); // dwFlags
1341 writeU16(0); // wPriority
1342 writeU16(0); // wLanguage
1343 writeU32(0); // dwInitialFrames
1344 writeU32(1000); // dwScale
1345 writeU32((uint32_t)(video_cfg.fps * 1000)); // dwRate
1346 writeU32(0); // dwStart
1347 writeU32(0); // dwLength (unknown)
1348 writeU32(0); // dwSuggestedBufferSize
1349 writeU32(0xFFFFFFFF); // dwQuality (use default)
1350 writeU32(0); // dwSampleSize (variable per frame)
1351 writeI16(0); // rcFrame.left
1352 writeI16(0); // rcFrame.top
1353 writeI16((int16_t)video_cfg.width); // rcFrame.right
1354 writeI16((int16_t)video_cfg.height); // rcFrame.bottom
1355
1356 // strf (BITMAPINFOHEADER [+ 3 DWORD color masks for RGB565])
1358 writeU32(40); // biSize (base header; masks, if any, follow)
1359 writeU32(video_cfg.width); // biWidth
1360 writeU32(video_cfg.height); // biHeight
1361 writeU16(1); // biPlanes
1362 writeU16(biBitCount()); // biBitCount
1363 writeBiCompression(); // biCompression
1364 writeU32(biSizeImage()); // biSizeImage
1365 writeU32(0); // biXPelsPerMeter
1366 writeU32(0); // biYPelsPerMeter
1367 writeU32(0); // biClrUsed
1368 writeU32(0); // biClrImportant
1369 if (isBitfields()) {
1370 writeU32(0x0000F800); // red mask
1371 writeU32(0x000007E0); // green mask
1372 writeU32(0x0000001F); // blue mask
1373 }
1374 }
1375
1377 writeFourCC("LIST");
1379 writeFourCC("strl");
1380
1381 uint16_t block_align =
1382 (uint16_t)(audio_info.channels * (audio_info.bits_per_sample / 8));
1383 uint32_t byte_rate = (uint32_t)audio_info.sample_rate * block_align;
1384
1385 // strh (AVIStreamHeader)
1386 writeChunkHeader("strh", 56);
1387 writeFourCC("auds");
1388 writeZeros(4); // fccHandler (unspecified)
1389 writeU32(0); // dwFlags
1390 writeU16(0); // wPriority
1391 writeU16(0); // wLanguage
1392 writeU32(0); // dwInitialFrames
1393 writeU32(1); // dwScale
1394 writeU32((uint32_t)audio_info.sample_rate); // dwRate
1395 writeU32(0); // dwStart
1396 writeU32(0); // dwLength (unknown)
1397 writeU32(0); // dwSuggestedBufferSize
1398 writeU32(0xFFFFFFFF); // dwQuality
1399 writeU32(block_align); // dwSampleSize
1400 writeZeros(8); // rcFrame (unused for audio)
1401
1402 // strf (WAVEFORMATEX)
1403 writeChunkHeader("strf", 18);
1404 writeU16((uint16_t)audio_info.format); // wFormatTag
1405 writeU16(audio_info.channels); // nChannels
1406 writeU32((uint32_t)audio_info.sample_rate); // nSamplesPerSec
1407 writeU32(byte_rate); // nAvgBytesPerSec
1408 writeU16(block_align); // nBlockAlign
1409 writeU16(audio_info.bits_per_sample); // wBitsPerSample
1410 writeU16(0); // cbSize
1411 }
1412
1414 // hdrl LIST payload: "hdrl" FOURCC + avih chunk + strl LIST(s), each incl
1415 // their own headers
1416 uint32_t hdrl_size = 4 + (8 + 56) + (8 + videoStrlSize());
1417 if (has_audio) hdrl_size += (8 + audioStrlSize());
1418
1419 writeFourCC("RIFF");
1420 writeU32(0xFFFFFFFF); // total file size: unknown while streaming
1421 writeFourCC("AVI ");
1422
1423 writeFourCC("LIST");
1424 writeU32(hdrl_size);
1425 writeFourCC("hdrl");
1429
1430 writeFourCC("LIST");
1431 writeU32(0xFFFFFFFF); // movi size: unknown while streaming
1432 writeFourCC("movi");
1433 }
1434};
1435
1436} // namespace audio_tools
WAV Audio Formats used by Microsoft e.g. in AVI video files.
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define LIST_HEADER_SIZE
Definition ContainerAVI.h:11
#define CHUNK_HEADER_SIZE
Definition ContainerAVI.h:12
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:80
void notifyAudioChange(AudioInfo info)
Definition AudioTypes.h:175
AVI Container Decoder which can be fed with small chunks of data. The minimum length must be bigger t...
Definition ContainerAVI.h:281
virtual size_t write(const uint8_t *data, size_t len) override
Definition ContainerAVI.h:359
BitmapInfoHeader video_info
Definition ContainerAVI.h:463
bool isValid(const uint8_t *data, size_t len) override
Definition ContainerAVI.h:303
void setupVideoInfo()
Definition ContainerAVI.h:694
bool parse()
Definition ContainerAVI.h:507
StrView & getStr(int offset, int len)
Provides the string at the indicated byte offset with the indicated length.
Definition ContainerAVI.h:926
void setMute(bool mute)
Definition ContainerAVI.h:350
ParseObject parseList(const char *id)
We load the indicated list from the current data.
Definition ContainerAVI.h:875
Str str
Definition ContainerAVI.h:484
AVIMainHeader main_header
Definition ContainerAVI.h:460
long current_pos
Definition ContainerAVI.h:481
ParseObject current_stream_data
Definition ContainerAVI.h:467
void setupAudioInfo()
Definition ContainerAVI.h:663
ParseObject parseChunk(const char *id)
We load the indicated chunk from the current data.
Definition ContainerAVI.h:863
Print * p_output_audio
Definition ContainerAVI.h:468
WAVFormatX audio_info
Definition ContainerAVI.h:464
bool(* validation_cb)(DemuxerAVI &avi)
Definition ContainerAVI.h:487
VideoOutput * p_output_video_video
Definition ContainerAVI.h:470
VideoInfo getVideoInfo() override
Definition ContainerAVI.h:413
const char * mimeVideo()
Provides the video mime.
Definition ContainerAVI.h:296
bool send_wav_header
Definition ContainerAVI.h:489
AudioInfoFormat getAudioInfo() override
Definition ContainerAVI.h:438
Print * p_output_video
Definition ContainerAVI.h:469
void end() override
Definition ContainerAVI.h:391
long open_subchunk_len
Definition ContainerAVI.h:480
int videoSeconds()
Provide the length of the video in seconds.
Definition ContainerAVI.h:453
ParseObject tryParseList(const char *id)
Definition ContainerAVI.h:844
bool is_parsing_active
Definition ContainerAVI.h:457
void sendWavHeader()
Definition ContainerAVI.h:680
bool is_mute
Definition ContainerAVI.h:488
bool is_metadata_ready
Definition ContainerAVI.h:486
AVIMainHeader aviMainHeader()
Provides the information from the main header chunk.
Definition ContainerAVI.h:394
uint32_t getInt(int offset)
Provides the int32 at the indicated byte offset.
Definition ContainerAVI.h:935
bool is_wav_header_sent
Definition ContainerAVI.h:491
char video_format[5]
Definition ContainerAVI.h:485
int stream_header_idx
Definition ContainerAVI.h:461
ParseObject tryParseList()
We try to parse the actual state for any list.
Definition ContainerAVI.h:854
Stack< ParseObject > object_stack
Definition ContainerAVI.h:466
long movi_end_pos
Definition ContainerAVI.h:482
void cleanupStack()
Definition ContainerAVI.h:909
Vector< AVIStreamHeader > stream_header
Definition ContainerAVI.h:462
bool isMetadataReady()
Returns true if all metadata has been parsed and is available.
Definition ContainerAVI.h:445
ParseObject parseAVIStreamData()
Definition ContainerAVI.h:886
Vector< uint8_t > video_accumulator
Definition ContainerAVI.h:479
Str spaces
Definition ContainerAVI.h:483
bool isCurrentStreamAudio()
Definition ContainerAVI.h:498
BitmapInfoHeader aviVideoInfo()
Provides the video information.
Definition ContainerAVI.h:400
const char * mime() override
provides the audio mime
Definition ContainerAVI.h:299
void setOutputVideo(Print &out_stream) override
Definition ContainerAVI.h:352
void consume(int len)
We remove the processed bytes from the beginning of the buffer.
Definition ContainerAVI.h:941
void setOutputVideo(VideoOutput &out_stream) override
Definition ContainerAVI.h:355
int video_seconds
Definition ContainerAVI.h:496
void setSendWavHeader(bool flag) override
Definition ContainerAVI.h:316
bool begin() override
Definition ContainerAVI.h:321
bool isCurrentStreamVideo()
Definition ContainerAVI.h:502
virtual void setOutput(Print &out_stream) override
Definition ContainerAVI.h:345
void writeData()
Definition ContainerAVI.h:735
Vector< StreamContentType > content_types
Definition ContainerAVI.h:465
bool header_is_avi
Definition ContainerAVI.h:456
uint32_t riff_file_size
Definition ContainerAVI.h:495
bool parseHeader()
Definition ContainerAVI.h:804
ParseObject tryParseChunk()
Definition ContainerAVI.h:828
ParseBuffer parse_buffer
Definition ContainerAVI.h:459
bool send_wav_header_explicit
Definition ContainerAVI.h:490
WAVFormatX aviAudioInfo()
Provides the audio information.
Definition ContainerAVI.h:405
void setOutputAudio(Print &out_stream) override
Definition ContainerAVI.h:337
const char * aviVideoFormat()
Definition ContainerAVI.h:402
AVIStreamHeader aviStreamHeader(int idx)
Provides the information from the stream header chunks.
Definition ContainerAVI.h:397
static VideoFormat toVideoFormat(uint32_t biCompression, uint16_t biBitCount)
Definition ContainerAVI.h:709
ParseObject tryParseChunk(const char *id)
Definition ContainerAVI.h:836
ParseState parse_state
Definition ContainerAVI.h:458
void setValidationCallback(bool(*cb)(DemuxerAVI &avi))
Definition ContainerAVI.h:448
void processStack(ParseObject &result)
Definition ContainerAVI.h:901
DemuxerAVI(int bufferSize=1024)
Definition ContainerAVI.h:291
Common interface for demuxers (DemuxerAVI, DemuxerMP4) that split a container's video and (optional) ...
Definition ContainerCommon.h:164
Configuration for the (single) video track written by MuxerAVI.
Definition ContainerAVI.h:989
StreamContentType write_stream_type
Definition ContainerAVI.h:1176
bool is_open
Definition ContainerAVI.h:1179
void setOutput(Print &out) override
Defines the output: e.g. a local File or a network Client.
Definition ContainerAVI.h:997
bool isBitfields()
Definition ContainerAVI.h:1211
void writeI16(int16_t v)
Definition ContainerAVI.h:1288
uint32_t videoStrfSize()
Definition ContainerAVI.h:1305
bool frame_pad
Definition ContainerAVI.h:1181
void writeBiCompression()
Definition ContainerAVI.h:1253
uint32_t biSizeImage()
Definition ContainerAVI.h:1249
size_t addVideoFrame(const uint8_t *data, size_t len, bool isKeyFrame=true) override
Definition ContainerAVI.h:1116
AudioInfoFormat audio_info
Definition ContainerAVI.h:1177
void writeU8(uint8_t v)
Definition ContainerAVI.h:1283
StreamContentType streamType() override
The track write() currently targets (see setStreamType())
Definition ContainerAVI.h:1043
size_t addJpegFrame(const uint8_t *data, size_t len) override
Definition ContainerAVI.h:1128
void writeHeader()
Definition ContainerAVI.h:1413
void checkVideoFormat(VideoFormat expected)
Definition ContainerAVI.h:1265
MuxerAVI(Print &out)
Definition ContainerAVI.h:992
void writeFourCC(const char *cc)
Definition ContainerAVI.h:1294
void checkRawFrame(VideoFormat expected, size_t len)
Definition ContainerAVI.h:1273
bool has_audio
Definition ContainerAVI.h:1178
bool frame_open
Definition ContainerAVI.h:1180
void beginFrame(size_t size)
Definition ContainerAVI.h:1054
AudioInfoFormat & audioInfo() override
Provides read/write access to the audio track's AudioInfoFormat.
Definition ContainerAVI.h:1016
uint32_t audioStrlSize()
Definition ContainerAVI.h:1312
void end() override
Closes the encoder: no trailer is written (streaming AVI has no idx1)
Definition ContainerAVI.h:1046
size_t writeFrame(const uint8_t *data, size_t len)
Definition ContainerAVI.h:1067
void writeU32(uint32_t v)
Definition ContainerAVI.h:1289
void setVideoInfo(MuxerVideoConfig config) override
Defines the video track configuration - call before begin()
Definition ContainerAVI.h:1000
void setStreamType(StreamContentType type) override
Definition ContainerAVI.h:1041
uint32_t endFrame()
Closes the current video frame (word-aligns the chunk)
Definition ContainerAVI.h:1076
size_t addAudioFrame(const uint8_t *data, size_t len) override
Definition ContainerAVI.h:1157
size_t write(const uint8_t *data, size_t len) override
Definition ContainerAVI.h:1090
size_t frame_remaining
Definition ContainerAVI.h:1182
void writeAudioStrl()
Definition ContainerAVI.h:1376
void setAudioInfo(AudioInfoFormat info) override
Definition ContainerAVI.h:1011
uint32_t videoFrameCount()
Number of video frames written so far.
Definition ContainerAVI.h:1167
size_t expectedRawFrameSize(VideoFormat format)
Definition ContainerAVI.h:1232
uint32_t audioChunkCount()
Number of audio chunks written so far.
Definition ContainerAVI.h:1169
size_t addYUV422Frame(const uint8_t *data, size_t len) override
Definition ContainerAVI.h:1135
uint32_t audio_chunk_count
Definition ContainerAVI.h:1184
size_t addI420Frame(const uint8_t *data, size_t len) override
Definition ContainerAVI.h:1150
MuxerAVI()
Definition ContainerAVI.h:991
Print * p_out
Definition ContainerAVI.h:1174
uint32_t videoStrlSize()
Definition ContainerAVI.h:1309
size_t addRGB565Frame(const uint8_t *data, size_t len) override
Definition ContainerAVI.h:1142
void writeVideoStrl()
Definition ContainerAVI.h:1331
const char * fourCC()
Definition ContainerAVI.h:1186
bool begin() override
Definition ContainerAVI.h:1020
void writeU16(uint16_t v)
Definition ContainerAVI.h:1284
void writeChunkHeader(const char *id, uint32_t size)
Definition ContainerAVI.h:1298
uint16_t biBitCount()
Definition ContainerAVI.h:1215
uint32_t video_frame_count
Definition ContainerAVI.h:1183
void writeZeros(int n)
Definition ContainerAVI.h:1295
const char * mimeVideo() override
Definition ContainerAVI.h:994
void writeMainHeader()
Definition ContainerAVI.h:1314
MuxerVideoConfig getVideoInfo() override
Definition ContainerAVI.h:1004
MuxerVideoConfig video_cfg
Definition ContainerAVI.h:1175
static const uint32_t AVIF_ISINTERLEAVED
Definition ContainerAVI.h:1172
Common interface for muxers (MuxerAVI, MuxerMP4) that combine an already-encoded video track (and opt...
Definition ContainerCommon.h:42
We try to keep the necessary buffer for parsing as small as possible, The data() method provides the ...
Definition ContainerAVI.h:23
Vector< uint8_t > vector
Definition ContainerAVI.h:67
size_t size()
Definition ContainerAVI.h:58
size_t writeArray(uint8_t *data, size_t len)
Definition ContainerAVI.h:25
size_t availableToWrite()
Definition ContainerAVI.h:47
size_t available_byte_count
Definition ContainerAVI.h:68
void consume(int size)
Definition ContainerAVI.h:31
size_t available()
Definition ContainerAVI.h:49
uint8_t * data()
Definition ContainerAVI.h:45
long indexOf(const char *str)
Definition ContainerAVI.h:60
void clear()
Definition ContainerAVI.h:51
bool resize(size_t size)
Definition ContainerAVI.h:40
bool isEmpty()
Definition ContainerAVI.h:56
Represents a LIST or a CHUNK: The ParseObject represents the current parsing result....
Definition ContainerAVI.h:170
size_t size()
Definition ContainerAVI.h:195
bool isVideo()
Definition ContainerAVI.h:255
size_t open
Definition ContainerAVI.h:230
size_t end_pos
Definition ContainerAVI.h:231
WAVFormatX * asAVIAudioFormat(void *ptr)
Definition ContainerAVI.h:219
size_t start_pos
Definition ContainerAVI.h:232
ParseObjectType type()
Definition ContainerAVI.h:201
bool isVideoCompressed()
Definition ContainerAVI.h:250
void set(size_t currentPos, const char *id, size_t size, ParseObjectType type)
Definition ContainerAVI.h:176
char chunk_id[5]
Definition ContainerAVI.h:259
size_t declared_size
Definition ContainerAVI.h:234
ParseObjectType object_type
Definition ContainerAVI.h:266
size_t declaredSize()
Definition ContainerAVI.h:199
BitmapInfoHeader * asVideoFormat(void *ptr)
Definition ContainerAVI.h:220
void set(size_t currentPos, StrView id, size_t size, ParseObjectType type)
Definition ContainerAVI.h:172
int streamNumber()
Definition ContainerAVI.h:237
AVIMainHeader * asAVIMainHeader(void *ptr)
Definition ContainerAVI.h:215
bool isVideoUncompressed()
Definition ContainerAVI.h:245
size_t data_size
Definition ContainerAVI.h:233
bool isAudio()
Definition ContainerAVI.h:240
AVIStreamHeader * asAVIStreamHeader(void *ptr)
Definition ContainerAVI.h:216
const char * id()
Definition ContainerAVI.h:194
bool isValid()
Definition ContainerAVI.h:202
LIFO Stack which is based on a List.
Definition Stack.h:14
Str which keeps the data on the heap. We grow the allocated memory only if the copy source is not fit...
Definition Str.h:24
void copyFrom(const char *source, int len, int maxlen=0)
assigns a memory buffer
Definition Str.h:96
void setCapacity(size_t newLen)
Definition Str.h:86
void setChars(char c, int len)
Fills the string with len chars.
Definition Str.h:109
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:29
virtual bool equals(const char *str)
checks if the string equals indicated parameter string
Definition StrView.h:197
virtual const char * c_str()
provides the string value as const char*
Definition StrView.h:456
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 VideoOutput.h:107
virtual size_t write(const uint8_t *data, size_t len)=0
virtual void flush()
Definition VideoOutput.h:113
virtual bool isKeyFrame(const uint8_t *data, size_t len)
Definition VideoOutput.h:135
Parser for Wav header data for details see https://de.wikipedia.org/wiki/RIFF_WAVE.
Definition CodecWAV.h:89
void setAudioInfo(WAVAudioInfo info)
Sets the info in the header.
Definition CodecWAV.h:166
bool writeHeader(Print *out)
Just write a wav header to the indicated outputbu.
Definition CodecWAV.h:169
char[4] FOURCC
Four-character code identifier for AVI format.
Definition ContainerAVI.h:73
VideoFormat
Video codec/pixel-format identifier, shared by two unrelated uses: the (single) video stream of a con...
Definition VideoOutput.h:29
StreamContentType
Which track write() feeds, for muxers (MuxerAVI, MuxerMP4) that double as a plain,...
Definition Video.h:21
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:27
AudioFormat
Audio format codes used by Microsoft e.g. in avi or wav files.
Definition AudioFormat.h:21
const char * toMime(AudioFormat format)
Provides the mime type for a AudioFormat wav code, or nullptr if not known/mapped.
Definition AudioFormat.h:300
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:350
@ Audio
Definition Video.h:21
@ Video
Definition Video.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
ParseObjectType
Definition ContainerAVI.h:148
@ AVIChunk
Definition ContainerAVI.h:148
@ AVIList
Definition ContainerAVI.h:148
@ AVIStreamData
Definition ContainerAVI.h:148
ParseState
Definition ContainerAVI.h:150
@ ParseStrl
Definition ContainerAVI.h:154
@ ParseStrf
Definition ContainerAVI.h:158
@ AfterStrf
Definition ContainerAVI.h:159
@ SubChunkContinue
Definition ContainerAVI.h:155
@ ParseMovi
Definition ContainerAVI.h:160
@ SubChunk
Definition ContainerAVI.h:156
@ ParseRec
Definition ContainerAVI.h:157
@ ParseHdrl
Definition ContainerAVI.h:152
@ ParseHeader
Definition ContainerAVI.h:151
@ ParseIgnore
Definition ContainerAVI.h:161
@ ParseAvih
Definition ContainerAVI.h:153
Definition ContainerAVI.h:75
uint32_t dwStreams
Definition ContainerAVI.h:84
uint32_t dwInitialFrames
Definition ContainerAVI.h:83
uint32_t dwMaxBytesPerSec
Definition ContainerAVI.h:79
uint32_t dwHeight
Definition ContainerAVI.h:87
uint32_t dwReserved[4]
Definition ContainerAVI.h:88
uint32_t dwFlags
Definition ContainerAVI.h:81
uint32_t dwPaddingGranularity
Definition ContainerAVI.h:80
uint32_t dwMicroSecPerFrame
Definition ContainerAVI.h:78
uint32_t dwSuggestedBufferSize
Definition ContainerAVI.h:85
uint32_t dwWidth
Definition ContainerAVI.h:86
uint32_t dwTotalFrames
Definition ContainerAVI.h:82
Definition ContainerAVI.h:96
uint32_t dwSampleSize
Definition ContainerAVI.h:109
uint32_t dwLength
Definition ContainerAVI.h:106
uint32_t dwInitialFrames
Definition ContainerAVI.h:102
uint16_t wPriority
Definition ContainerAVI.h:100
uint16_t wLanguage
Definition ContainerAVI.h:101
FOURCC fccType
Definition ContainerAVI.h:97
uint32_t dwScale
Definition ContainerAVI.h:103
FOURCC fccHandler
Definition ContainerAVI.h:98
uint32_t dwFlags
Definition ContainerAVI.h:99
uint32_t dwStart
Definition ContainerAVI.h:105
uint32_t dwQuality
Definition ContainerAVI.h:108
RECT rcFrame
Definition ContainerAVI.h:110
uint32_t dwRate
Definition ContainerAVI.h:104
uint32_t dwSuggestedBufferSize
Definition ContainerAVI.h:107
AudioInfo extended with a WAVEFORMATEX-style codec tag (the "wav code"): identifies the codec (PCM,...
Definition AudioFormat.h:392
AudioFormat format
Definition AudioFormat.h:401
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:122
Definition ContainerAVI.h:116
uint32_t biSizeImage
Definition ContainerAVI.h:123
uint32_t biCompression
Definition ContainerAVI.h:122
uint32_t biWidth
Definition ContainerAVI.h:118
uint16_t biBitCount
Definition ContainerAVI.h:121
uint32_t biHeight
Definition ContainerAVI.h:119
uint32_t biYPelsPerMeter
Definition ContainerAVI.h:125
uint32_t biClrImportant
Definition ContainerAVI.h:127
uint32_t biSize
Definition ContainerAVI.h:117
uint16_t biPlanes
Definition ContainerAVI.h:120
uint32_t biClrUsed
Definition ContainerAVI.h:126
uint32_t biXPelsPerMeter
Definition ContainerAVI.h:124
Shared video track configuration for muxers (MuxerAVI, MuxerMP4) - call before begin().
Definition ContainerCommon.h:11
float fps
Definition ContainerCommon.h:14
const char * fourcc
Definition ContainerCommon.h:20
uint16_t height
Definition ContainerCommon.h:13
uint16_t width
Definition ContainerCommon.h:12
VideoFormat format
Definition ContainerCommon.h:15
Definition ContainerAVI.h:91
uint32_t dwHeight
Definition ContainerAVI.h:93
uint32_t dwWidth
Definition ContainerAVI.h:92
Basic video information (width/height/codec/frame size), analogous to AudioInfo - common to both Demu...
Definition VideoOutput.h:49
uint32_t frame_size
Definition VideoOutput.h:62
uint32_t total_file_size
Definition VideoOutput.h:68
float fps
Definition VideoOutput.h:56
uint16_t height
Frame height in pixels.
Definition VideoOutput.h:53
uint16_t width
Frame width in pixels.
Definition VideoOutput.h:51
VideoFormat format
Video codec - VideoFormat::UNKNOWN if not (yet) determined.
Definition VideoOutput.h:58
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
bool ext_adpcm_header
write the extended 'fmt ' chunk + 'fact' chunk for ADPCM formats
Definition CodecWAV.h:47
Definition ContainerAVI.h:130
uint16_t wBitsPerSample
Definition ContainerAVI.h:136
uint16_t nChannels
Definition ContainerAVI.h:132
uint16_t nBlockAlign
Definition ContainerAVI.h:135
uint32_t nSamplesPerSec
Definition ContainerAVI.h:133
uint32_t nAvgBytesPerSec
Definition ContainerAVI.h:134
uint16_t cbSize
Definition ContainerAVI.h:137
AudioFormat wFormatTag
Definition ContainerAVI.h:131