arduino-audio-tools
Loading...
Searching...
No Matches
ContainerMPG.h
Go to the documentation of this file.
1#pragma once
2#include <string.h>
8
9namespace audio_tools {
10
11// packet_start_code / stream_id values used by the ISO/IEC 11172-1 (MPEG-1)
12// Program Stream (also reused, unchanged, by MPEG-2 Program Streams)
13static const uint8_t MPG_PACK_START_CODE = 0xBA;
14static const uint8_t MPG_SYSTEM_HEADER_START_CODE = 0xBB;
15static const uint8_t MPG_PROGRAM_END_CODE = 0xB9;
16static const uint8_t MPG_PROGRAM_STREAM_MAP = 0xBC;
17static const uint8_t MPG_PRIVATE_STREAM_1 = 0xBD;
18static const uint8_t MPG_PADDING_STREAM = 0xBE;
19static const uint8_t MPG_PRIVATE_STREAM_2 = 0xBF;
21static const uint8_t MPG_VIDEO_STREAM_ID = 0xE0;
23static const uint8_t MPG_AUDIO_STREAM_ID = 0xC0;
25static const uint32_t MPG_CLOCK_HZ = 90000;
26
37 public:
38 void resize(size_t size) { vec.resize(size + 4); }
39 size_t writeArray(const uint8_t *data, size_t len) {
40 size_t to_write = min(availableToWrite(), len);
41 memmove(vec.data() + count, data, to_write);
42 count += to_write;
43 return to_write;
44 }
45 void consume(size_t len) {
46 if (len > count) len = count;
47 memmove(vec.data(), vec.data() + len, count - len);
48 count -= len;
49 }
50 uint8_t *data() { return vec.data(); }
51 size_t available() { return count; }
52 size_t availableToWrite() { return vec.size() - count; }
53 void clear() { count = 0; }
54
56 long indexOfStartCode(size_t from = 0) {
57 if (count < 3 || from + 3 > count) return -1;
58 for (size_t i = from; i + 2 < count; i++) {
59 if (vec[i] == 0 && vec[i + 1] == 0 && vec[i + 2] == 1) return (long)i;
60 }
61 return -1;
62 }
63
64 protected:
66 size_t count = 0;
67};
68
111class DemuxerMPG : public Demuxer {
112 public:
115 DemuxerMPG(int bufferSize = 1024) { parse_buffer.resize(bufferSize); }
116
118 const char *mimeVideo() override { return "video/mpeg"; }
126 const char *mime() override {
127 if (audio_layer == 2) return "audio/mpeg; codecs=\"mpeg1-layer2\"";
128 return toMime(AudioFormat::MP3);
129 }
130
133 bool isValid(const uint8_t *data, size_t len) override {
134 return len >= 4 && data[0] == 0x00 && data[1] == 0x00 &&
135 data[2] == 0x01 && data[3] == MPG_PACK_START_CODE;
136 }
137
138 bool begin() override {
140 is_parsing_active = true;
141 unit_open = false;
142 is_skip_unit = false;
143 unbounded = false;
144 video_frame_open = false;
145 audio_frame_open = false;
146 video_unit_len = 0;
147 audio_unit_len = 0;
149 video_header_parsed = false;
150 audio_header_parsed = false;
153 video_width = 0;
154 video_height = 0;
155 video_fps = 0;
157 audio_channels = 0;
158 audio_layer = 0;
159 total_bytes = 0;
160 return true;
161 }
162
163 void end() override {
166 video_frame_open = false;
167 audio_frame_open = false;
168 is_parsing_active = false;
169 }
170
171 operator bool() override { return is_parsing_active; }
172
176 void setOutputAudio(Print &out) override { p_output_audio = &out; }
177
182 void setOutput(Print &out) override { setOutputAudio(out); }
183
186 void setOutputVideo(Print &out) override { p_output_video = &out; }
187 void setOutputVideo(VideoOutput &out) override { p_output_video_video = &out; }
188
192 void setSendWavHeader(bool flag) override { (void)flag; }
193
198 VideoInfo result;
199 result.width = video_width;
200 result.height = video_height;
201 result.fps = video_fps;
202 result.format = VideoFormat::MPEG1;
203 result.frame_size = 0; // compressed: size varies per frame
205 return result;
206 }
207
211 AudioInfoFormat result;
212 result.sample_rate = audio_sample_rate > 0 ? audio_sample_rate : 44100;
213 result.channels = audio_channels > 0 ? audio_channels : 2;
214 result.bits_per_sample = 16; // nominal - the codec is compressed
215 result.format = AudioFormat::MP3;
216 return result;
217 }
218
219 size_t write(const uint8_t *data, size_t len) override {
220 size_t result = parse_buffer.writeArray(data, len);
221 total_bytes += result;
222 if (is_parsing_active) {
223 while (parse()) {
224 }
225 }
226 return result;
227 }
228
229 protected:
230 bool is_parsing_active = true;
235 uint32_t total_bytes = 0;
236
237 // current streamed unit (PES payload, or a skipped block's payload)
238 bool unit_open = false;
239 bool is_skip_unit = false;
240 bool is_video_unit = false;
241 bool unbounded = false; // PES_packet_length == 0 (video only)
242 size_t remaining = 0;
243
244 // video access-unit framing: a new PES with a PTS starts a new picture
245 bool video_frame_open = false;
246 // audio access-unit framing: a new PES with a PTS starts a new audio frame
247 bool audio_frame_open = false;
248
249 // Accumulates one complete access unit (a whole picture, or a whole
250 // audio frame) across every PES fragment and every parse_buffer-sized
251 // write() chunk that contributes to it, so the decoder downstream gets
252 // one single write() call per unit instead of an arbitrary number of
253 // partial ones - see deliver()/flushVideoUnit()/flushAudioUnit(). A
254 // container-demuxed stream's chunk boundaries don't line up with
255 // picture/frame boundaries at all (the parse buffer above is a small,
256 // fixed size; a real access unit is routinely several times larger),
257 // and feeding a video decoder like TinyMPGDecoder a boundary-split
258 // buffer under its default whole-buffer contract can silently
259 // reconstruct a picture from stale/wrong macroblocks rather than
260 // failing cleanly - see that project's own TinyMPGDecoder::write() doc
261 // comment, which calls this exact demuxer out by name.
262 //
263 // Separate buffers for audio/video rather than one shared one: a real
264 // encoder (ffmpeg's MPEG-PS muxer among them, confirmed by testing
265 // against files it produced) routinely interleaves audio PES packets
266 // in the middle of a video picture's own PES fragments, before that
267 // picture's bytes are complete - unlike MuxerMPG (this project's own
268 // encoder), which happens to write one access unit's fragments back to
269 // back. A single shared buffer can't survive that: whichever unit was
270 // mid-accumulation would have to be flushed the moment the other type's
271 // bytes arrived, truncating it and permanently losing/misaligning
272 // whatever hadn't arrived yet - confirmed by testing (a truncated video
273 // unit's trailing bytes reappear, unprefixed by their own start code,
274 // corrupting the *next* unit too). Two independent buffers mean
275 // real interleaving no longer needs any special-casing at all - each
276 // side just keeps accumulating exactly where it left off.
277 //
278 // *_unit_len is tracked separately from each Vector's own size(), which
279 // is deliberately over-allocated for amortized growth - Vector::resize()
280 // always allocates exactly the requested size with no spare capacity
281 // (AudioBasic/Collections/Vector.h), so resizing it to the exact new
282 // length on every single delivered fragment - one picture can need a
283 // dozen of these - would reallocate and copy everything accumulated so
284 // far *every single call*, the same O(N^2) cost this project's
285 // ChunkedSampleTableStore (SampleTableStore.h) exists to avoid for
286 // MP4's sample tables.
288 size_t video_unit_len = 0;
290 size_t audio_unit_len = 0;
291 // Whether the current (not yet flushed) video unit has already seen its
292 // first internal picture_start_code - see deliverVideo().
294
295 // lazily parsed from the elementary streams themselves
298 uint16_t video_width = 0;
299 uint16_t video_height = 0;
300 float video_fps = 0;
301 uint32_t audio_sample_rate = 0;
302 uint8_t audio_channels = 0;
303 uint8_t audio_layer = 0;
306 static const size_t VIDEO_PROBE_MAX = 64;
307
311 bool parse() {
312 if (unit_open) return continuePayload();
313
314 if (parse_buffer.available() < 4) return false;
315 uint8_t *buf = parse_buffer.data();
316 if (!(buf[0] == 0 && buf[1] == 0 && buf[2] == 1)) {
317 long idx = parse_buffer.indexOfStartCode();
318 if (idx < 0) {
319 size_t avail = parse_buffer.available();
320 if (avail > 2) parse_buffer.consume(avail - 2);
321 return false;
322 }
323 parse_buffer.consume((size_t)idx);
324 if (parse_buffer.available() < 4) return false;
325 buf = parse_buffer.data();
326 }
327
328 uint8_t id = buf[3];
329 if (id == MPG_PACK_START_CODE) return parsePack();
331 if (id == MPG_PROGRAM_END_CODE) {
335 video_frame_open = false;
336 audio_frame_open = false;
337 LOGI("MPEG_program_end_code");
338 return true;
339 }
340 if (id == MPG_PROGRAM_STREAM_MAP || id == MPG_PRIVATE_STREAM_1 ||
342 return parseSkip();
343 if (id >= 0xC0 && id <= 0xDF) return parsePes(id, false);
344 if (id >= 0xE0 && id <= 0xEF) return parsePes(id, true);
345
346 // reserved/unrecognized id: drop this byte and resync on the next
347 // start code
349 return true;
350 }
351
352 bool parsePack() {
353 if (parse_buffer.available() < 12) return false;
355 return true;
356 }
357
359 if (parse_buffer.available() < 6) return false;
360 uint8_t *buf = parse_buffer.data();
361 uint16_t header_length = ((uint16_t)buf[4] << 8) | buf[5];
362 size_t total = 6 + header_length;
363 if (parse_buffer.available() < total) return false;
364 parse_buffer.consume(total);
365 return true;
366 }
367
371 bool parseSkip() {
372 if (parse_buffer.available() < 6) return false;
373 uint8_t *buf = parse_buffer.data();
374 uint16_t len = ((uint16_t)buf[4] << 8) | buf[5];
376 if (len == 0) return true;
377 unit_open = true;
378 is_skip_unit = true;
379 is_video_unit = false;
380 unbounded = false;
381 remaining = len;
382 return continuePayload();
383 }
384
385 bool parsePes(uint8_t id, bool isVideo) {
386 // need the fixed 6-byte PES header plus 1 byte to peek the optional
387 // header's shape
388 if (parse_buffer.available() < 7) return false;
389 uint8_t *buf = parse_buffer.data();
390 uint16_t pes_len = ((uint16_t)buf[4] << 8) | buf[5];
391 uint8_t peek = buf[6];
392
393 size_t optional_len;
394 bool has_pts = false;
395 if (peek == 0x0F) {
396 optional_len = 1;
397 } else if ((peek >> 4) == 0x02) {
398 optional_len = 5;
399 has_pts = true;
400 } else if ((peek >> 4) == 0x03) {
401 optional_len = 10;
402 has_pts = true;
403 } else {
404 LOGW("DemuxerMPG: unsupported PES optional header 0x%02X - treating as no-timestamp",
405 (int)peek);
406 optional_len = 1;
407 }
408 if (parse_buffer.available() < 6 + optional_len) return false;
409
410 parse_buffer.consume(6 + optional_len);
411 (void)id;
412
413 bool is_unbounded = false;
414 size_t payload_len = 0;
415 if (pes_len == 0) {
416 // PES_packet_length == 0 ("unbounded") is only meaningful for video
417 is_unbounded = isVideo;
418 } else if (pes_len < optional_len) {
419 payload_len = 0; // malformed - be defensive rather than underflow
420 } else {
421 payload_len = (size_t)pes_len - optional_len;
422 }
423
424 if (!isVideo && has_pts) {
425 // a PTS marks the first fragment of a new audio access unit - close
426 // out the previous one (see audio_unit_buffer's comment)
428 audio_frame_open = true;
429 }
430
431 if (isVideo && has_pts) {
432 // a PTS marks the first fragment of a new access unit - close out
433 // the previous one
435 video_frame_open = true;
436 // No wall-clock scheduling or skip decision here anymore - every
437 // picture is always decoded and forwarded immediately, in full.
438 // Pacing is the caller's responsibility now - see PacedVideoOutput
439 // (Video/PacedVideoOutput.h), which schedules frames from its own
440 // background task instead of blocking this dispatch loop (and, with
441 // it, audio delivery) the way an inline wait here used to.
442 }
443
444 unit_open = true;
445 is_skip_unit = false;
446 is_video_unit = isVideo;
447 unbounded = is_unbounded;
448 remaining = payload_len;
449 return continuePayload();
450 }
451
459 static bool isContainerLevelStartCode(uint8_t id) {
463 id == MPG_PRIVATE_STREAM_2 || (id >= 0xC0 && id <= 0xEF);
464 }
465
477 long indexOfContainerStartCode(size_t from = 0) {
478 long idx = parse_buffer.indexOfStartCode(from);
479 while (idx >= 0) {
480 size_t idPos = (size_t)idx + 3;
481 if (idPos >= parse_buffer.available()) return -1; // id byte not buffered yet
482 if (isContainerLevelStartCode(parse_buffer.data()[idPos])) return idx;
483 idx = parse_buffer.indexOfStartCode((size_t)idx + 3);
484 }
485 return -1;
486 }
487
489 if (unbounded) {
490 long idx = indexOfContainerStartCode();
491 size_t avail = parse_buffer.available();
492 if (idx >= 0) {
493 deliver((size_t)idx);
494 parse_buffer.consume((size_t)idx);
495 unit_open = false;
496 return true;
497 }
498 if (avail > 2) {
499 deliver(avail - 2);
500 parse_buffer.consume(avail - 2);
501 }
502 return false;
503 }
504 size_t avail = parse_buffer.available();
505 size_t to_write = min(avail, remaining);
506 deliver(to_write);
507 parse_buffer.consume(to_write);
508 remaining -= to_write;
509 if (remaining == 0) {
510 unit_open = false;
511 return true;
512 }
513 return false;
514 }
515
520 void deliver(size_t len) {
521 if (len == 0 || is_skip_unit) return;
522 uint8_t *data = parse_buffer.data();
523 if (is_video_unit) {
524 if (!video_header_parsed) probeVideoHeader(data, len);
525 deliverVideo(data, len);
526 } else {
527 if (!audio_header_parsed) probeAudioHeader(data, len);
528 appendToAudio(data, len);
529 }
530 }
531
534 void appendToVideo(const uint8_t *data, size_t len) {
535 size_t needed = video_unit_len + len;
536 if (needed > (size_t)video_unit_buffer.size()) {
537 size_t newCap = video_unit_buffer.size() == 0 ? 8192 : (size_t)video_unit_buffer.size() * 2;
538 if (newCap < needed) newCap = needed;
540 }
541 memcpy(video_unit_buffer.data() + video_unit_len, data, len);
542 video_unit_len += len;
543 }
544
546 void appendToAudio(const uint8_t *data, size_t len) {
547 size_t needed = audio_unit_len + len;
548 if (needed > (size_t)audio_unit_buffer.size()) {
549 size_t newCap = audio_unit_buffer.size() == 0 ? 4096 : (size_t)audio_unit_buffer.size() * 2;
550 if (newCap < needed) newCap = needed;
552 }
553 memcpy(audio_unit_buffer.data() + audio_unit_len, data, len);
554 audio_unit_len += len;
555 }
556
577 void deliverVideo(const uint8_t *data, size_t len) {
578 size_t old_len = video_unit_len;
579 appendToVideo(data, len);
580 // 3-byte lookback so a start code split across the previously
581 // buffered tail and this call's newly appended bytes is still found -
582 // video_unit_buffer now holds both contiguously, no separate boundary
583 // handling needed.
584 size_t search_from = old_len > 3 ? old_len - 3 : 0;
585 uint8_t *buf = video_unit_buffer.data();
586 while (true) {
587 long idx = -1;
588 for (size_t i = search_from; i + 3 < video_unit_len; i++) {
589 if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1 && buf[i + 3] == 0) {
590 idx = (long)i;
591 break;
592 }
593 }
594 if (idx < 0) break;
597 search_from = (size_t)idx + 4;
598 continue;
599 }
600 size_t remaining = video_unit_len - (size_t)idx;
601 video_unit_len = (size_t)idx;
602 flushVideoUnit(); // writes [0, idx), resets video_unit_len to 0
603 memmove(buf, buf + idx, remaining);
605 video_unit_seen_picture = true; // the retained remainder already starts with one
606 search_from = 4; // skip the start code now sitting at offset 0
607 }
608 }
609
627
631 if (audio_unit_len == 0) return;
632 if (p_output_audio != nullptr)
634 audio_unit_len = 0;
635 }
636
641 void probeVideoHeader(const uint8_t *data, size_t len) {
642 size_t old_size = (size_t)video_probe.size();
643 size_t take = old_size < VIDEO_PROBE_MAX ? VIDEO_PROBE_MAX - old_size : 0;
644 if (take > len) take = len;
645 for (size_t i = 0; i < take; i++) {
646 uint8_t b = data[i];
648 }
649
650 size_t n = (size_t)video_probe.size();
651 for (size_t i = 0; i + 7 < n; i++) {
652 if (video_probe[i] == 0 && video_probe[i + 1] == 0 &&
653 video_probe[i + 2] == 1 && video_probe[i + 3] == 0xB3) {
654 uint8_t b0 = video_probe[i + 4], b1 = video_probe[i + 5],
655 b2 = video_probe[i + 6], b3 = video_probe[i + 7];
656 video_width = (uint16_t)(((uint16_t)b0 << 4) | (b1 >> 4));
657 video_height = (uint16_t)(((uint16_t)(b1 & 0x0F) << 8) | b2);
658 static const float rates[16] = {0, 23.976f, 24.0f, 25.0f,
659 29.97f, 30.0f, 50.0f, 59.94f,
660 60.0f, 0, 0, 0,
661 0, 0, 0, 0};
662 video_fps = rates[b3 & 0x0F];
663 video_header_parsed = true;
664 break;
665 }
666 }
670 }
671 }
672
675 void probeAudioHeader(const uint8_t *data, size_t len) {
676 size_t old_size = (size_t)audio_probe.size();
677 size_t take = old_size < 4 ? 4 - old_size : 0;
678 if (take > len) take = len;
679 for (size_t i = 0; i < take; i++) {
680 uint8_t b = data[i];
682 }
683 if ((size_t)audio_probe.size() < 4) return;
684
685 uint8_t b0 = audio_probe[0], b1 = audio_probe[1], b2 = audio_probe[2],
686 b3 = audio_probe[3];
687 if (b0 == 0xFF && (b1 & 0xE0) == 0xE0) {
688 uint8_t version = (b1 >> 3) & 0x03; // 11=MPEG1, 10=MPEG2, 00=MPEG2.5
689 uint8_t layer_bits = (b1 >> 1) & 0x03; // 01=Layer III, 10=Layer II, 11=Layer I
690 uint8_t sr_index = (b2 >> 2) & 0x03;
691 uint8_t mode = (b3 >> 6) & 0x03; // 11=mono
692 static const uint32_t rates_v1[4] = {44100, 48000, 32000, 0};
693 static const uint32_t rates_v2[4] = {22050, 24000, 16000, 0};
694 static const uint32_t rates_v25[4] = {11025, 12000, 8000, 0};
695 uint32_t rate = 0;
696 if (version == 0x03)
697 rate = rates_v1[sr_index];
698 else if (version == 0x02)
699 rate = rates_v2[sr_index];
700 else if (version == 0x00)
701 rate = rates_v25[sr_index];
702 if (rate > 0) {
703 audio_sample_rate = rate;
704 audio_channels = (mode == 0x03) ? 1 : 2;
705 // layer_bits -> standard layer number (I/II/III), 0 if reserved
706 static const uint8_t layers[4] = {0, 3, 2, 1};
707 audio_layer = layers[layer_bits];
708 audio_header_parsed = true;
709 }
710 } else {
711 // not a frame sync where we expected one - stop probing so we don't
712 // keep re-checking every write(); getAudioInfo() falls back to
713 // 44100/stereo
714 audio_header_parsed = true;
715 }
718 }
719};
720
782class MuxerMPG : public Muxer {
783 public:
788 MuxerMPG(Print &out) : MuxerMPG() { setOutput(out); }
789
790 const char *mimeVideo() override { return "video/mpeg"; }
791
793 void setOutput(Print &out) override { p_out = &out; }
794
798 void setVideoInfo(MuxerVideoConfig config) override { video_cfg = config; }
800
805 void setAudioInfo(AudioInfoFormat info) override {
806 audio_info = info;
809 has_audio = true;
810 }
811 AudioInfoFormat &audioInfo() override { return audio_info; }
812
815 bool begin() override {
816 if (p_out == nullptr) {
817 LOGE("output not defined");
818 return false;
819 }
820 if (video_cfg.width == 0 || video_cfg.height == 0) {
821 LOGE("invalid video size: %d x %d", (int)video_cfg.width,
822 (int)video_cfg.height);
823 return false;
824 }
827 last_scr = 0;
830 is_open = true;
831 return true;
832 }
833
835 void setStreamType(StreamContentType type) override {
836 write_stream_type = type;
837 }
839
841 void end() override {
842 if (is_open && p_out != nullptr) {
843 static const uint8_t end_code[4] = {0x00, 0x00, 0x01,
845 p_out->write(end_code, 4);
846 }
847 is_open = false;
848 }
849
850 operator bool() override { return is_open; }
851
854 size_t write(const uint8_t *data, size_t len) override {
856 return addAudioFrame(data, len);
857 return addVideoFrame(data, len);
858 }
859
866 size_t addVideoFrame(const uint8_t *data, size_t len,
867 bool isKeyFrame = true) override {
868 (void)isKeyFrame;
869 if (!is_open || data == nullptr || len == 0) return 0;
870 size_t written = writeAccessUnit(MPG_VIDEO_STREAM_ID, data, len, videoPts());
872 return written;
873 }
874
878 size_t addJpegFrame(const uint8_t *data, size_t len) override {
879 LOGW("MuxerMPG: MJPEG is not supported by MPEG-1 Program Stream");
880 return addVideoFrame(data, len);
881 }
882 size_t addYUV422Frame(const uint8_t *data, size_t len) override {
883 LOGW("MuxerMPG: raw YUV422 is not supported by MPEG-1 Program Stream");
884 return addVideoFrame(data, len);
885 }
886 size_t addRGB565Frame(const uint8_t *data, size_t len) override {
887 LOGW("MuxerMPG: raw RGB565 is not supported by MPEG-1 Program Stream");
888 return addVideoFrame(data, len);
889 }
890 size_t addI420Frame(const uint8_t *data, size_t len) override {
891 LOGW("MuxerMPG: raw I420 is not supported by MPEG-1 Program Stream");
892 return addVideoFrame(data, len);
893 }
894
898 size_t addAudioFrame(const uint8_t *data, size_t len) override {
899 if (!is_open || !has_audio || data == nullptr || len == 0) return 0;
900 size_t written = writeAccessUnit(MPG_AUDIO_STREAM_ID, data, len, audioPts());
902 return written;
903 }
904
905 uint32_t videoFrameCount() { return video_frame_count; }
906 uint32_t audioFrameCount() { return audio_frame_count; }
907
908 protected:
913 static const size_t MAX_PES_PAYLOAD = 4096;
915 static const uint32_t AUDIO_SAMPLES_PER_FRAME = 1152;
916
917 Print *p_out = nullptr;
920 bool has_audio = false;
921 bool is_open = false;
923 uint32_t video_frame_count = 0;
924 uint32_t audio_frame_count = 0;
925 uint64_t last_scr = 0;
929 static const uint32_t MUX_RATE = 0x3FFFFF;
930
931 uint64_t videoPts() {
932 float fps = video_cfg.fps > 0 ? video_cfg.fps : 25.0f;
933 return (uint64_t)((double)video_frame_count * MPG_CLOCK_HZ / fps);
934 }
935 uint64_t audioPts() {
936 uint32_t sr = audio_info.sample_rate > 0 ? audio_info.sample_rate : 44100;
937 return (uint64_t)((double)audio_frame_count * AUDIO_SAMPLES_PER_FRAME *
938 MPG_CLOCK_HZ / sr);
939 }
940
941 uint64_t clampScr(uint64_t scr) {
942 if (scr < last_scr) scr = last_scr;
943 last_scr = scr;
944 return scr;
945 }
946
947 void writePackHeader(uint64_t scr_in) {
948 uint64_t scr = clampScr(scr_in);
949 static const uint8_t start[4] = {0x00, 0x00, 0x01, MPG_PACK_START_CODE};
950 p_out->write(start, 4);
951 uint8_t b[8];
952 b[0] = (uint8_t)(0x20 | (((scr >> 30) & 0x07) << 1) | 0x01);
953 b[1] = (uint8_t)((scr >> 22) & 0xFF);
954 b[2] = (uint8_t)((((scr >> 15) & 0x7F) << 1) | 0x01);
955 b[3] = (uint8_t)((scr >> 7) & 0xFF);
956 b[4] = (uint8_t)(((scr & 0x7F) << 1) | 0x01);
957 b[5] = (uint8_t)(0x80 | ((MUX_RATE >> 15) & 0x7F));
958 b[6] = (uint8_t)((MUX_RATE >> 7) & 0xFF);
959 b[7] = (uint8_t)(((MUX_RATE & 0x7F) << 1) | 0x01);
960 p_out->write(b, 8);
961 }
962
963 void writeStreamEntry(uint8_t id, bool isVideo) {
964 // P-STD buffer bound: informational only - 46 (x1024 = ~46KB) for
965 // video, 4 (x128 = 512 bytes, ~1 audio frame) for audio
966 uint16_t bound = isVideo ? 46 : 4;
967 uint8_t b[3];
968 b[0] = id;
969 b[1] = (uint8_t)(0xC0 | (isVideo ? 0x20 : 0x00) | ((bound >> 8) & 0x1F));
970 b[2] = (uint8_t)(bound & 0xFF);
971 p_out->write(b, 3);
972 }
973
975 int num_streams = 1 + (has_audio ? 1 : 0);
976 uint16_t header_length = (uint16_t)(6 + 3 * num_streams);
977 static const uint8_t start[4] = {0x00, 0x00, 0x01,
979 p_out->write(start, 4);
980 uint8_t len_b[2] = {(uint8_t)(header_length >> 8),
981 (uint8_t)header_length};
982 p_out->write(len_b, 2);
983
984 uint16_t audio_bound = has_audio ? 1 : 0;
985 uint16_t video_bound = 1;
986 uint8_t b[6];
987 // marker(1) + mux_rate(22) + marker(1)
988 b[0] = (uint8_t)(0x80 | ((MUX_RATE >> 15) & 0x7F));
989 b[1] = (uint8_t)((MUX_RATE >> 7) & 0xFF);
990 b[2] = (uint8_t)(((MUX_RATE & 0x7F) << 1) | 0x01);
991 // audio_bound(6) + fixed_flag(1)=0 + CSPS_flag(1)=0
992 b[3] = (uint8_t)((audio_bound & 0x3F) << 2);
993 // audio_locked(1)=0 + video_locked(1)=0 + marker(1)=1 + video_bound(5)
994 b[4] = (uint8_t)(0x20 | (video_bound & 0x1F));
995 b[5] = 0xFF; // reserved_byte
996 p_out->write(b, 6);
997
1000 }
1001
1002 void writeTimestampField(uint8_t id4, uint64_t ts) {
1003 uint8_t b[5];
1004 b[0] = (uint8_t)((id4 << 4) | (((ts >> 30) & 0x07) << 1) | 0x01);
1005 uint16_t mid = (uint16_t)((((ts >> 15) & 0x7FFF) << 1) | 0x01);
1006 uint16_t low = (uint16_t)(((ts & 0x7FFF) << 1) | 0x01);
1007 b[1] = (uint8_t)(mid >> 8);
1008 b[2] = (uint8_t)mid;
1009 b[3] = (uint8_t)(low >> 8);
1010 b[4] = (uint8_t)low;
1011 p_out->write(b, 5);
1012 }
1013
1015 size_t writeElementaryChunk(uint8_t stream_id, const uint8_t *data,
1016 size_t len, bool withPts, uint64_t pts) {
1017 writePackHeader(pts);
1018 uint16_t optional_len = withPts ? 5 : 1;
1019 uint16_t pes_len = (uint16_t)(len + optional_len);
1020 uint8_t hdr[6] = {0,
1021 0,
1022 1,
1023 stream_id,
1024 (uint8_t)(pes_len >> 8),
1025 (uint8_t)pes_len};
1026 p_out->write(hdr, 6);
1027 if (withPts) {
1028 writeTimestampField(0x02, pts);
1029 } else {
1030 // avoid Print::write(uint8_t): some Print/Stream implementations
1031 // (e.g. BaseStream-derived ones) buffer single-byte writes
1032 // separately from the write(data, len) path below, which would
1033 // reorder this marker byte after the payload - write it as a
1034 // one-element array instead so it goes through the same path.
1035 static const uint8_t no_ts = 0x0F;
1036 p_out->write(&no_ts, 1);
1037 }
1038 return p_out->write(data, len);
1039 }
1040
1044 size_t writeAccessUnit(uint8_t stream_id, const uint8_t *data, size_t len,
1045 uint64_t pts) {
1046 size_t offset = 0;
1047 size_t written = 0;
1048 bool first = true;
1049 do {
1050 size_t chunk = len - offset;
1051 if (chunk > MAX_PES_PAYLOAD) chunk = MAX_PES_PAYLOAD;
1052 written +=
1053 writeElementaryChunk(stream_id, data + offset, chunk, first, pts);
1054 offset += chunk;
1055 first = false;
1056 } while (offset < len);
1057 return written;
1058 }
1059};
1060
1061} // 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 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
Common interface for demuxers (DemuxerAVI, DemuxerMP4) that split a container's video and (optional) ...
Definition ContainerCommon.h:164
MPEG-1 System (Program) Stream Demuxer, as defined by ISO/IEC 11172-1: splits the pack_header/system_...
Definition ContainerMPG.h:111
bool is_skip_unit
Definition ContainerMPG.h:239
long indexOfContainerStartCode(size_t from=0)
Definition ContainerMPG.h:477
void setOutput(Print &out) override
Definition ContainerMPG.h:182
MPGParseBuffer parse_buffer
Definition ContainerMPG.h:231
bool isValid(const uint8_t *data, size_t len) override
Definition ContainerMPG.h:133
void setOutputVideo(VideoOutput &out) override
Definition ContainerMPG.h:187
bool audio_header_parsed
Definition ContainerMPG.h:297
bool parse()
Definition ContainerMPG.h:311
void deliver(size_t len)
Definition ContainerMPG.h:520
bool continuePayload()
Definition ContainerMPG.h:488
Vector< uint8_t > video_probe
Definition ContainerMPG.h:304
bool parsePes(uint8_t id, bool isVideo)
Definition ContainerMPG.h:385
float video_fps
Definition ContainerMPG.h:300
void deliverVideo(const uint8_t *data, size_t len)
Definition ContainerMPG.h:577
bool video_unit_seen_picture
Definition ContainerMPG.h:293
Print * p_output_audio
Definition ContainerMPG.h:232
bool parsePack()
Definition ContainerMPG.h:352
VideoOutput * p_output_video_video
Definition ContainerMPG.h:234
VideoInfo getVideoInfo() override
Definition ContainerMPG.h:197
AudioInfoFormat getAudioInfo() override
Definition ContainerMPG.h:210
Print * p_output_video
Definition ContainerMPG.h:233
void setOutputAudio(Print &out) override
Definition ContainerMPG.h:176
uint8_t audio_layer
0=not yet probed/reserved, else 1/2/3
Definition ContainerMPG.h:303
bool unbounded
Definition ContainerMPG.h:241
size_t audio_unit_len
Definition ContainerMPG.h:290
static const size_t VIDEO_PROBE_MAX
Definition ContainerMPG.h:306
bool video_frame_open
Definition ContainerMPG.h:245
void end() override
Definition ContainerMPG.h:163
uint32_t audio_sample_rate
Definition ContainerMPG.h:301
void flushVideoUnit()
Definition ContainerMPG.h:615
Vector< uint8_t > audio_unit_buffer
Definition ContainerMPG.h:289
bool is_parsing_active
Definition ContainerMPG.h:230
size_t write(const uint8_t *data, size_t len) override
Definition ContainerMPG.h:219
void appendToAudio(const uint8_t *data, size_t len)
Same as appendToVideo(), for audio_unit_buffer.
Definition ContainerMPG.h:546
Vector< uint8_t > video_unit_buffer
Definition ContainerMPG.h:287
uint16_t video_width
Definition ContainerMPG.h:298
bool unit_open
Definition ContainerMPG.h:238
bool parseSystemHeader()
Definition ContainerMPG.h:358
void appendToVideo(const uint8_t *data, size_t len)
Definition ContainerMPG.h:534
void probeVideoHeader(const uint8_t *data, size_t len)
Definition ContainerMPG.h:641
Vector< uint8_t > audio_probe
Definition ContainerMPG.h:305
bool is_video_unit
Definition ContainerMPG.h:240
bool video_header_parsed
Definition ContainerMPG.h:296
const char * mime() override
Definition ContainerMPG.h:126
static bool isContainerLevelStartCode(uint8_t id)
Definition ContainerMPG.h:459
size_t video_unit_len
Definition ContainerMPG.h:288
void setSendWavHeader(bool flag) override
Definition ContainerMPG.h:192
void flushAudioUnit()
Definition ContainerMPG.h:630
bool begin() override
Definition ContainerMPG.h:138
void probeAudioHeader(const uint8_t *data, size_t len)
Definition ContainerMPG.h:675
uint8_t audio_channels
Definition ContainerMPG.h:302
size_t remaining
Definition ContainerMPG.h:242
uint32_t total_bytes
Definition ContainerMPG.h:235
const char * mimeVideo() override
Provides the container mime.
Definition ContainerMPG.h:118
uint16_t video_height
Definition ContainerMPG.h:299
void setOutputVideo(Print &out) override
Definition ContainerMPG.h:186
bool audio_frame_open
Definition ContainerMPG.h:247
bool parseSkip()
Definition ContainerMPG.h:371
DemuxerMPG(int bufferSize=1024)
Definition ContainerMPG.h:115
Minimal byte accumulator used by DemuxerMPG to parse start-code delimited units that may straddle wri...
Definition ContainerMPG.h:36
long indexOfStartCode(size_t from=0)
Index of the next 00 00 01 start-code prefix at/after 'from', or -1.
Definition ContainerMPG.h:56
Vector< uint8_t > vec
Definition ContainerMPG.h:65
size_t writeArray(const uint8_t *data, size_t len)
Definition ContainerMPG.h:39
void consume(size_t len)
Definition ContainerMPG.h:45
void resize(size_t size)
Definition ContainerMPG.h:38
size_t count
Definition ContainerMPG.h:66
size_t availableToWrite()
Definition ContainerMPG.h:52
size_t available()
Definition ContainerMPG.h:51
uint8_t * data()
Definition ContainerMPG.h:50
void clear()
Definition ContainerMPG.h:53
Common interface for muxers (MuxerAVI, MuxerMP4) that combine an already-encoded video track (and opt...
Definition ContainerCommon.h:42
MPEG-1 System (Program) Stream Encoder, as defined by ISO/IEC 11172-1: muxes an already-encoded MPEG-...
Definition ContainerMPG.h:782
StreamContentType write_stream_type
Definition ContainerMPG.h:922
bool is_open
Definition ContainerMPG.h:921
void setOutput(Print &out) override
Defines the output: e.g. a local File or a network Client.
Definition ContainerMPG.h:793
void writeStreamEntry(uint8_t id, bool isVideo)
Definition ContainerMPG.h:963
MuxerMPG(Print &out)
Definition ContainerMPG.h:788
size_t addVideoFrame(const uint8_t *data, size_t len, bool isKeyFrame=true) override
Definition ContainerMPG.h:866
AudioInfoFormat audio_info
Definition ContainerMPG.h:919
StreamContentType streamType() override
The track write() currently targets (see setStreamType())
Definition ContainerMPG.h:838
size_t addJpegFrame(const uint8_t *data, size_t len) override
Definition ContainerMPG.h:878
MuxerMPG()
Definition ContainerMPG.h:784
uint32_t audioFrameCount()
Definition ContainerMPG.h:906
static const size_t MAX_PES_PAYLOAD
Definition ContainerMPG.h:913
bool has_audio
Definition ContainerMPG.h:920
static const uint32_t AUDIO_SAMPLES_PER_FRAME
MPEG-1 Layer II/III audio frames are always 1152 samples.
Definition ContainerMPG.h:915
AudioInfoFormat & audioInfo() override
Provides read/write access to the audio track's AudioInfoFormat.
Definition ContainerMPG.h:811
uint64_t audioPts()
Definition ContainerMPG.h:935
void end() override
Writes the MPEG_program_end_code trailer and closes the muxer.
Definition ContainerMPG.h:841
void setVideoInfo(MuxerVideoConfig config) override
Definition ContainerMPG.h:798
void setStreamType(StreamContentType type) override
Selects whether write() feeds the video or the audio track.
Definition ContainerMPG.h:835
size_t addAudioFrame(const uint8_t *data, size_t len) override
Definition ContainerMPG.h:898
size_t write(const uint8_t *data, size_t len) override
Definition ContainerMPG.h:854
uint64_t videoPts()
Definition ContainerMPG.h:931
void setAudioInfo(AudioInfoFormat info) override
Definition ContainerMPG.h:805
uint32_t videoFrameCount()
Definition ContainerMPG.h:905
void writeTimestampField(uint8_t id4, uint64_t ts)
Definition ContainerMPG.h:1002
size_t writeAccessUnit(uint8_t stream_id, const uint8_t *data, size_t len, uint64_t pts)
Definition ContainerMPG.h:1044
void writeSystemHeader()
Definition ContainerMPG.h:974
size_t addYUV422Frame(const uint8_t *data, size_t len) override
Definition ContainerMPG.h:882
uint32_t audio_frame_count
Definition ContainerMPG.h:924
size_t addI420Frame(const uint8_t *data, size_t len) override
Definition ContainerMPG.h:890
Print * p_out
Definition ContainerMPG.h:917
uint64_t last_scr
Definition ContainerMPG.h:925
size_t addRGB565Frame(const uint8_t *data, size_t len) override
Definition ContainerMPG.h:886
size_t writeElementaryChunk(uint8_t stream_id, const uint8_t *data, size_t len, bool withPts, uint64_t pts)
Writes one pack_header + PES header + payload chunk (<= a few KB).
Definition ContainerMPG.h:1015
bool begin() override
Definition ContainerMPG.h:815
uint64_t clampScr(uint64_t scr)
Definition ContainerMPG.h:941
uint32_t video_frame_count
Definition ContainerMPG.h:923
const char * mimeVideo() override
Definition ContainerMPG.h:790
MuxerVideoConfig getVideoInfo() override
Provides the video track configuration.
Definition ContainerMPG.h:799
static const uint32_t MUX_RATE
Definition ContainerMPG.h:929
void writePackHeader(uint64_t scr_in)
Definition ContainerMPG.h:947
MuxerVideoConfig video_cfg
Definition ContainerMPG.h:918
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
void shrink_to_fit()
Definition Vector.h:276
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
StreamContentType
Which track write() feeds, for muxers (MuxerAVI, MuxerMP4) that double as a plain,...
Definition Video.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
@ 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
static const uint8_t MPG_PACK_START_CODE
Definition ContainerMPG.h:13
static const uint8_t MPG_SYSTEM_HEADER_START_CODE
Definition ContainerMPG.h:14
static const uint8_t MPG_VIDEO_STREAM_ID
First (and, in this single-track implementation, only) video stream_id.
Definition ContainerMPG.h:21
static const uint8_t MPG_PROGRAM_END_CODE
Definition ContainerMPG.h:15
static const uint8_t MPG_PROGRAM_STREAM_MAP
Definition ContainerMPG.h:16
static const uint8_t MPG_PRIVATE_STREAM_1
Definition ContainerMPG.h:17
static const uint32_t MPG_CLOCK_HZ
MPEG system clock runs at 90 kHz - the unit PTS/DTS/SCR are expressed in.
Definition ContainerMPG.h:25
static const uint8_t MPG_PADDING_STREAM
Definition ContainerMPG.h:18
static const uint8_t MPG_PRIVATE_STREAM_2
Definition ContainerMPG.h:19
static const uint8_t MPG_AUDIO_STREAM_ID
First (and, in this single-track implementation, only) audio stream_id.
Definition ContainerMPG.h:23
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
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 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