arduino-audio-tools
Loading...
Searching...
No Matches
ContainerMTS.h
Go to the documentation of this file.
1#pragma once
2#include <string.h>
8
9namespace audio_tools {
10
11// ISO/IEC 13818-1 MPEG Transport Stream framing constants
12static const size_t MTS_PACKET_SIZE = 188;
13static const uint8_t MTS_SYNC_BYTE = 0x47;
14static const uint32_t MTS_CLOCK_HZ = 90000; // PTS/PCR base clock
15
16// PIDs MuxerMTS assigns its own output to - a fixed, conventional layout
17// (matching what e.g. ffmpeg's own MPEG-TS muxer defaults to) rather than
18// something DemuxerMTS needs to know about: DemuxerMTS discovers the real
19// PIDs of whatever stream it is fed from the PAT/PMT tables themselves, so
20// it works with any standards-conformant .ts file, not just ones produced
21// by MuxerMTS.
22static const uint16_t MTS_PID_PAT = 0x0000;
23static const uint16_t MTS_PID_PMT = 0x1000;
24static const uint16_t MTS_PID_VIDEO = 0x0100;
25static const uint16_t MTS_PID_AUDIO = 0x0101;
26
27// PES stream_id values (ISO/IEC 13818-1 Table 2-18) used for the video/
28// audio elementary streams - the same convention ContainerMPG.h uses for
29// its own (unrelated, MPEG-1 Program Stream) PES packets.
30static const uint8_t MTS_STREAM_ID_VIDEO = 0xE0;
31static const uint8_t MTS_STREAM_ID_AUDIO = 0xC0;
32
33// PMT stream_type values (ISO/IEC 13818-1 Table 2-34) this container
34// recognizes.
35static const uint8_t MTS_STREAM_TYPE_MPEG1_VIDEO = 0x01;
36static const uint8_t MTS_STREAM_TYPE_MPEG2_VIDEO = 0x02;
37static const uint8_t MTS_STREAM_TYPE_MPEG1_AUDIO = 0x03;
38static const uint8_t MTS_STREAM_TYPE_MPEG2_AUDIO = 0x04;
39static const uint8_t MTS_STREAM_TYPE_AAC_ADTS = 0x0F;
40static const uint8_t MTS_STREAM_TYPE_MPEG4_VIDEO = 0x10;
41static const uint8_t MTS_STREAM_TYPE_H264 = 0x1B;
42static const uint8_t MTS_STREAM_TYPE_PRIVATE = 0x06;
52
60inline bool isMtsAudioStreamType(uint8_t st) {
63}
69inline VideoFormat mtsVideoFormat(uint8_t st) {
70 switch (st) {
72 return VideoFormat::H264;
75 return VideoFormat::MPEG1;
77 return VideoFormat::MPEG4;
79 return VideoFormat::MJPEG;
80 default:
82 }
83}
86inline AudioFormat mtsAudioFormat(uint8_t st) {
87 switch (st) {
89 return AudioFormat::AAC;
92 return AudioFormat::MP3;
93 default:
95 }
96}
100inline uint8_t mtsVideoStreamType(VideoFormat format) {
101 switch (format) {
109 return MTS_STREAM_TYPE_MJPEG; // see its own comment: user-private by convention
110 default:
111 LOGW("MuxerMTS: VideoFormat %d has no standard MPEG-TS stream_type - using private data (0x06)",
112 (int)format);
114 }
115}
118inline uint8_t mtsAudioStreamType(AudioFormat format) {
119 switch (format) {
120 case AudioFormat::AAC:
122 case AudioFormat::MP3:
124 default:
125 LOGW("MuxerMTS: AudioFormat %d has no standard MPEG-TS stream_type - using private data (0x06)",
126 (int)format);
128 }
129}
130
137inline uint32_t mtsCrc32(const uint8_t *data, size_t len) {
138 uint32_t crc = 0xFFFFFFFF;
139 for (size_t i = 0; i < len; i++) {
140 crc ^= (uint32_t)data[i] << 24;
141 for (int b = 0; b < 8; b++) {
142 crc = (crc & 0x80000000) ? (crc << 1) ^ 0x04C11DB7 : (crc << 1);
143 }
144 }
145 return crc;
146}
147
157 public:
158 void resize(size_t size) { vec.resize(size); }
159 size_t writeArray(const uint8_t *data, size_t len) {
160 size_t to_write = min(availableToWrite(), len);
161 memmove(vec.data() + count, data, to_write);
162 count += to_write;
163 return to_write;
164 }
165 void consume(size_t len) {
166 if (len > count) len = count;
167 memmove(vec.data(), vec.data() + len, count - len);
168 count -= len;
169 }
170 uint8_t *data() { return vec.data(); }
171 size_t available() { return count; }
172 size_t availableToWrite() { return vec.size() - count; }
173 void clear() { count = 0; }
174
175 protected:
177 size_t count = 0;
178};
179
234class DemuxerMTS : public Demuxer {
235 public:
239 DemuxerMTS(int bufferSize = 8 * MTS_PACKET_SIZE) { parse_buffer.resize(bufferSize); }
240
241 const char *mimeVideo() override { return "video/mp2t"; }
245 const char *mime() override {
246 const char *m = toMime(mtsAudioFormat(audio_stream_type));
247 return m != nullptr ? m : "audio/aac";
248 }
249
253 bool isValid(const uint8_t *data, size_t len) override {
254 if (len < 1 || data[0] != MTS_SYNC_BYTE) return false;
255 if (len >= MTS_PACKET_SIZE + 1) return data[MTS_PACKET_SIZE] == MTS_SYNC_BYTE;
256 return true;
257 }
258
259 bool begin() override {
261 is_parsing_active = true;
262 pmt_pid = 0xFFFF;
263 video_pid = 0xFFFF;
264 audio_pid = 0xFFFF;
267 video_frame_open = false;
268 audio_frame_open = false;
269 video_unit_len = 0;
270 audio_unit_len = 0;
271 audio_header_parsed = false;
273 audio_channels = 0;
275 total_bytes = 0;
276 return true;
277 }
278
279 void end() override {
282 video_frame_open = false;
283 audio_frame_open = false;
284 is_parsing_active = false;
285 }
286
287 operator bool() override { return is_parsing_active; }
288
289 void setOutputAudio(Print &out) override { p_output_audio = &out; }
290 void setOutput(Print &out) override { setOutputAudio(out); }
291 void setOutputVideo(Print &out) override { p_output_video = &out; }
292 void setOutputVideo(VideoOutput &out) override { p_output_video_video = &out; }
293
297 void setSendWavHeader(bool flag) override { (void)flag; }
298
304 VideoInfo result;
307 return result;
308 }
309
314 AudioInfoFormat result;
315 result.sample_rate = audio_sample_rate > 0 ? audio_sample_rate : 44100;
316 result.channels = audio_channels > 0 ? audio_channels : 2;
317 result.bits_per_sample = 16;
319 if (result.format == AudioFormat::UNKNOWN) result.format = AudioFormat::AAC;
320 return result;
321 }
322
323 size_t write(const uint8_t *data, size_t len) override {
324 size_t result = parse_buffer.writeArray(data, len);
325 if (is_parsing_active) {
326 while (parse()) {
327 }
328 }
329 return result;
330 }
331
332 protected:
333 bool is_parsing_active = false;
338 uint32_t total_bytes = 0;
339
340 uint16_t pmt_pid = 0xFFFF;
341 uint16_t video_pid = 0xFFFF;
342 uint16_t audio_pid = 0xFFFF;
343 uint8_t video_stream_type = 0;
344 uint8_t audio_stream_type = 0;
345
346 bool video_frame_open = false;
347 bool audio_frame_open = false;
349 size_t video_unit_len = 0;
351 size_t audio_unit_len = 0;
352
354 uint32_t audio_sample_rate = 0;
355 uint8_t audio_channels = 0;
357
361 bool parse() {
362 size_t avail = parse_buffer.available();
363 if (avail < MTS_PACKET_SIZE) return false;
364 uint8_t *buf = parse_buffer.data();
365 if (buf[0] != MTS_SYNC_BYTE) {
366 // resync: find a 0x47 that is also followed by a 0x47 exactly one
367 // packet later (when enough data is buffered to check) so a stray
368 // 0x47 inside a payload isn't mistaken for realignment.
369 size_t idx = 1;
370 for (; idx < avail; idx++) {
371 if (buf[idx] != MTS_SYNC_BYTE) continue;
372 if (idx + MTS_PACKET_SIZE < avail) {
373 if (buf[idx + MTS_PACKET_SIZE] == MTS_SYNC_BYTE) break;
374 } else {
375 break; // can't confirm yet, but it's the best guess available
376 }
377 }
378 if (idx < avail) {
380 return true;
381 }
382 parse_buffer.consume(avail);
383 return false;
384 }
385 parsePacket(buf);
388 return true;
389 }
390
391 void parsePacket(const uint8_t *pkt) {
392 if (pkt[1] & 0x80) return; // transport_error_indicator
393 uint16_t pid = (uint16_t)((pkt[1] & 0x1F) << 8) | pkt[2];
394 bool pusi = (pkt[1] & 0x40) != 0;
395 uint8_t adaptation_control = (pkt[3] >> 4) & 0x03;
396 if (adaptation_control == 0x00) return; // reserved
397 size_t pos = 4;
398 if (adaptation_control == 0x02 || adaptation_control == 0x03) {
399 uint8_t af_len = pkt[4];
400 pos = 5 + af_len;
401 if (pos > MTS_PACKET_SIZE) return; // malformed
402 }
403 if (adaptation_control == 0x02) return; // adaptation field only, no payload
404 size_t payload_len = MTS_PACKET_SIZE - pos;
405 if (payload_len == 0) return;
406 const uint8_t *payload = pkt + pos;
407
408 if (pid == MTS_PID_PAT) {
409 parsePat(payload, payload_len, pusi);
410 } else if (pmt_pid != 0xFFFF && pid == pmt_pid) {
411 parsePmt(payload, payload_len, pusi);
412 } else if (pid == video_pid) {
413 handlePesPayload(true, payload, payload_len, pusi);
414 } else if (pid == audio_pid) {
415 handlePesPayload(false, payload, payload_len, pusi);
416 }
417 }
418
421 void parsePat(const uint8_t *data, size_t len, bool pusi) {
422 if (!pusi || len < 1) return;
423 size_t off = 1 + (size_t)data[0]; // skip pointer_field
424 if (off + 8 > len) return;
425 if (data[off] != 0x00) return; // table_id: program_association_section
426 uint16_t section_length = (uint16_t)((data[off + 1] & 0x0F) << 8) | data[off + 2];
427 size_t after_length = off + 3;
428 size_t section_end = after_length + section_length - 4; // exclude CRC32
429 if (section_end > len) section_end = len;
430 size_t prog_off = after_length + 5; // skip transport_stream_id/flags/section#/last_section#
431 while (prog_off + 4 <= section_end) {
432 uint16_t program_number = (uint16_t)(data[prog_off] << 8) | data[prog_off + 1];
433 uint16_t pid = (uint16_t)((data[prog_off + 2] & 0x1F) << 8) | data[prog_off + 3];
434 if (program_number != 0) {
435 pmt_pid = pid;
436 break;
437 }
438 prog_off += 4;
439 }
440 }
441
444 void parsePmt(const uint8_t *data, size_t len, bool pusi) {
445 if (!pusi || len < 1) return;
446 size_t off = 1 + (size_t)data[0]; // skip pointer_field
447 if (off + 12 > len) return;
448 if (data[off] != 0x02) return; // table_id: TS_program_map_section
449 uint16_t section_length = (uint16_t)((data[off + 1] & 0x0F) << 8) | data[off + 2];
450 size_t after_length = off + 3;
451 size_t section_end = after_length + section_length - 4; // exclude CRC32
452 if (section_end > len) section_end = len;
453 uint16_t program_info_length =
454 (uint16_t)((data[after_length + 7] & 0x0F) << 8) | data[after_length + 8];
455 size_t stream_off = after_length + 9 + program_info_length;
456
457 uint16_t new_video_pid = 0xFFFF, new_audio_pid = 0xFFFF;
458 uint8_t new_video_type = 0, new_audio_type = 0;
459 while (stream_off + 5 <= section_end) {
460 uint8_t stream_type = data[stream_off];
461 uint16_t es_pid = (uint16_t)((data[stream_off + 1] & 0x1F) << 8) | data[stream_off + 2];
462 uint16_t es_info_length = (uint16_t)((data[stream_off + 3] & 0x0F) << 8) | data[stream_off + 4];
463 if (isMtsVideoStreamType(stream_type) && new_video_pid == 0xFFFF) {
464 new_video_pid = es_pid;
465 new_video_type = stream_type;
466 } else if (isMtsAudioStreamType(stream_type) && new_audio_pid == 0xFFFF) {
467 new_audio_pid = es_pid;
468 new_audio_type = stream_type;
469 }
470 stream_off += 5 + es_info_length;
471 }
472 video_pid = new_video_pid;
473 video_stream_type = new_video_type;
474 audio_pid = new_audio_pid;
475 audio_stream_type = new_audio_type;
476 }
477
482 void handlePesPayload(bool isVideo, const uint8_t *payload, size_t len, bool pusi) {
483 bool &frame_open = isVideo ? video_frame_open : audio_frame_open;
484 if (pusi) {
485 if (frame_open) flushUnit(isVideo);
486 if (len < 6 || !(payload[0] == 0 && payload[1] == 0 && payload[2] == 1)) {
487 LOGW("DemuxerMTS: missing PES start code on PID %s",
488 isVideo ? "video" : "audio");
489 return;
490 }
491 if (len < 7) {
492 LOGW("DemuxerMTS: PES header split across TS packet boundary - dropping unit");
493 return;
494 }
495 uint8_t flags2 = payload[7];
496 size_t header_data_length = (len >= 9) ? payload[8] : 0;
497 size_t header_total = 9 + header_data_length;
498 (void)flags2;
499 if (len < header_total) {
500 LOGW("DemuxerMTS: PES header split across TS packet boundary - dropping unit");
501 return;
502 }
503 frame_open = true;
504 const uint8_t *es = payload + header_total;
505 size_t es_len = len - header_total;
506 if (es_len > 0) {
507 if (!isVideo && !audio_header_parsed) probeAudioHeader(es, es_len);
508 appendToUnit(isVideo, es, es_len);
509 }
510 } else {
511 if (!frame_open) return; // no unit in progress - drop stray continuation
512 if (!isVideo && !audio_header_parsed) probeAudioHeader(payload, len);
513 appendToUnit(isVideo, payload, len);
514 }
515 }
516
517 void appendToUnit(bool isVideo, const uint8_t *data, size_t len) {
518 if (isVideo)
519 appendToVideo(data, len);
520 else
521 appendToAudio(data, len);
522 }
523
526 void appendToVideo(const uint8_t *data, size_t len) {
527 size_t needed = video_unit_len + len;
528 if (needed > (size_t)video_unit_buffer.size()) {
529 size_t newCap = video_unit_buffer.size() == 0 ? 8192 : (size_t)video_unit_buffer.size() * 2;
530 if (newCap < needed) newCap = needed;
532 }
533 memcpy(video_unit_buffer.data() + video_unit_len, data, len);
534 video_unit_len += len;
535 }
536 void appendToAudio(const uint8_t *data, size_t len) {
537 size_t needed = audio_unit_len + len;
538 if (needed > (size_t)audio_unit_buffer.size()) {
539 size_t newCap = audio_unit_buffer.size() == 0 ? 4096 : (size_t)audio_unit_buffer.size() * 2;
540 if (newCap < needed) newCap = needed;
542 }
543 memcpy(audio_unit_buffer.data() + audio_unit_len, data, len);
544 audio_unit_len += len;
545 }
546
547 void flushUnit(bool isVideo) {
548 if (isVideo)
550 else
552 }
553
565
567 if (audio_unit_len > 0) {
569 audio_unit_len = 0;
570 }
571 audio_frame_open = false;
572 }
573
578 void probeAudioHeader(const uint8_t *data, size_t len) {
579 size_t old_size = (size_t)audio_probe.size();
580 size_t take = old_size < 4 ? 4 - old_size : 0;
581 if (take > len) take = len;
582 for (size_t i = 0; i < take; i++) {
583 uint8_t b = data[i];
585 }
586 if ((size_t)audio_probe.size() < 4) return;
587
588 uint8_t b0 = audio_probe[0], b1 = audio_probe[1], b2 = audio_probe[2], b3 = audio_probe[3];
590 if (b0 == 0xFF && (b1 & 0xF0) == 0xF0) {
591 uint8_t sr_index = (b2 >> 2) & 0x0F;
592 uint8_t chan_cfg = (uint8_t)(((b2 & 0x01) << 2) | ((b3 >> 6) & 0x03));
593 static const uint32_t rates[16] = {96000, 88200, 64000, 48000, 44100, 32000,
594 24000, 22050, 16000, 12000, 11025, 8000,
595 7350, 0, 0, 0};
596 uint32_t rate = rates[sr_index];
597 if (rate > 0) {
598 audio_sample_rate = rate;
599 audio_channels = chan_cfg > 0 ? chan_cfg : 2;
600 }
601 }
604 if (b0 == 0xFF && (b1 & 0xE0) == 0xE0) {
605 uint8_t version = (b1 >> 3) & 0x03;
606 uint8_t sr_index = (b2 >> 2) & 0x03;
607 uint8_t mode = (b3 >> 6) & 0x03;
608 static const uint32_t rates_v1[4] = {44100, 48000, 32000, 0};
609 static const uint32_t rates_v2[4] = {22050, 24000, 16000, 0};
610 static const uint32_t rates_v25[4] = {11025, 12000, 8000, 0};
611 uint32_t rate = 0;
612 if (version == 0x03) rate = rates_v1[sr_index];
613 else if (version == 0x02) rate = rates_v2[sr_index];
614 else if (version == 0x00) rate = rates_v25[sr_index];
615 if (rate > 0) {
616 audio_sample_rate = rate;
617 audio_channels = (mode == 0x03) ? 1 : 2;
618 }
619 }
620 }
621 audio_header_parsed = true;
624 }
625};
626
683class MuxerMTS : public Muxer {
684 public:
689 MuxerMTS(Print &out) : MuxerMTS() { setOutput(out); }
690
691 const char *mimeVideo() override { return "video/mp2t"; }
692 void setOutput(Print &out) override { p_out = &out; }
693
694 void setVideoInfo(MuxerVideoConfig config) override { video_cfg = config; }
696
707 AudioInfoFormat &audioInfo() override { return audio_info; }
708
711 bool begin() override {
712 if (p_out == nullptr) {
713 LOGE("output not defined");
714 return false;
715 }
718 pat_cc = 0;
719 pmt_cc = 0;
720 video_cc = 0;
721 audio_cc = 0;
722 writePat();
723 writePmt();
724 is_open = true;
725 return true;
726 }
727
728 void setStreamType(StreamContentType type) override { write_stream_type = type; }
730
732 void end() override { is_open = false; }
733
734 operator bool() override { return is_open; }
735
736 size_t write(const uint8_t *data, size_t len) override {
738 return addVideoFrame(data, len);
739 }
740
744 size_t addVideoFrame(const uint8_t *data, size_t len, bool isKeyFrame = true) override {
745 if (!is_open || data == nullptr || len == 0) return 0;
746 if (isKeyFrame) {
747 writePat();
748 writePmt();
749 }
750 uint64_t pts = videoPts();
752 /*withPcr=*/true);
754 return len;
755 }
756
764 size_t addJpegFrame(const uint8_t *data, size_t len) override {
766 LOGW("MuxerMTS: getVideoInfo().format does not match addJpegFrame()");
767 return addVideoFrame(data, len, true);
768 }
769 size_t addYUV422Frame(const uint8_t *data, size_t len) override {
770 LOGW("MuxerMTS: raw YUV422 is not supported by MPEG-TS");
771 return addVideoFrame(data, len);
772 }
773 size_t addRGB565Frame(const uint8_t *data, size_t len) override {
774 LOGW("MuxerMTS: raw RGB565 is not supported by MPEG-TS");
775 return addVideoFrame(data, len);
776 }
777 size_t addI420Frame(const uint8_t *data, size_t len) override {
778 LOGW("MuxerMTS: raw I420 is not supported by MPEG-TS");
779 return addVideoFrame(data, len);
780 }
781
785 size_t addAudioFrame(const uint8_t *data, size_t len) override {
786 if (!is_open || !has_audio || data == nullptr || len == 0) return 0;
787 uint64_t pts = audioPts();
789 /*withPcr=*/false);
791 return len;
792 }
793
794 uint32_t videoFrameCount() { return video_frame_count; }
795 uint32_t audioFrameCount() { return audio_frame_count; }
796
797 protected:
798 Print *p_out = nullptr;
801 bool has_audio = false;
802 bool is_open = false;
804 uint32_t video_frame_count = 0;
805 uint32_t audio_frame_count = 0;
806 uint32_t audio_samples_per_frame = 1024; // AAC: 1024, MP3: 1152
807
808 uint8_t pat_cc = 0, pmt_cc = 0, video_cc = 0, audio_cc = 0;
809
810 uint64_t videoPts() {
811 float fps = video_cfg.fps > 0 ? video_cfg.fps : 25.0f;
812 return (uint64_t)((double)video_frame_count * MTS_CLOCK_HZ / fps);
813 }
814 uint64_t audioPts() {
815 uint32_t sr = audio_info.sample_rate > 0 ? audio_info.sample_rate : 44100;
816 return (uint64_t)((double)audio_frame_count * audio_samples_per_frame * MTS_CLOCK_HZ / sr);
817 }
818
822 static void writeTsTimestamp(uint8_t *b, uint64_t ts) {
823 b[0] = (uint8_t)((0x02 << 4) | (((ts >> 30) & 0x07) << 1) | 0x01);
824 uint16_t mid = (uint16_t)((((ts >> 15) & 0x7FFF) << 1) | 0x01);
825 uint16_t low = (uint16_t)(((ts & 0x7FFF) << 1) | 0x01);
826 b[1] = (uint8_t)(mid >> 8);
827 b[2] = (uint8_t)mid;
828 b[3] = (uint8_t)(low >> 8);
829 b[4] = (uint8_t)low;
830 }
831
838 static void writePcr(uint8_t *buf, uint64_t pcr_base) {
839 pcr_base &= 0x1FFFFFFFFULL;
840 buf[0] = (uint8_t)(pcr_base >> 25);
841 buf[1] = (uint8_t)(pcr_base >> 17);
842 buf[2] = (uint8_t)(pcr_base >> 9);
843 buf[3] = (uint8_t)(pcr_base >> 1);
844 buf[4] = (uint8_t)(((pcr_base & 0x1) << 7) | 0x7E);
845 buf[5] = 0x00;
846 }
847
853 static void copyVirtual(uint8_t *dst, size_t voffset, size_t n, const uint8_t *a, size_t alen,
854 const uint8_t *b, size_t blen) {
855 size_t written = 0;
856 if (voffset < alen) {
857 size_t take = min(n, alen - voffset);
858 memcpy(dst, a + voffset, take);
859 written += take;
860 }
861 if (written < n) {
862 size_t bOffset = (voffset + written) - alen;
863 memcpy(dst + written, b + bOffset, n - written);
864 }
865 }
866
877 void packBytesToTs(uint16_t pid, uint8_t &cc, const uint8_t *header, size_t header_len,
878 const uint8_t *payload, size_t payload_len, bool withPcr, uint64_t pcr) {
879 size_t total_len = header_len + payload_len;
880 size_t offset = 0;
881 bool first = true;
882 do {
883 size_t remaining = total_len - offset;
884 bool addPcr = first && withPcr;
885 bool use_af = addPcr || remaining < 184;
886 uint8_t pkt[MTS_PACKET_SIZE];
887 pkt[0] = MTS_SYNC_BYTE;
888 pkt[1] = (uint8_t)((first ? 0x40 : 0x00) | ((pid >> 8) & 0x1F));
889 pkt[2] = (uint8_t)(pid & 0xFF);
890 size_t pos, chunk;
891 if (!use_af) {
892 pkt[3] = (uint8_t)(0x10 | (cc & 0x0F)); // adaptation_field_control=01 (payload only)
893 pos = 4;
894 chunk = 184;
895 } else {
896 size_t overhead = 2 + (addPcr ? 6 : 0); // adaptation_field_length byte + flags byte + PCR
897 chunk = min(remaining, 184 - overhead);
898 size_t stuffing = 184 - overhead - chunk;
899 pkt[3] = (uint8_t)(0x30 | (cc & 0x0F)); // adaptation_field_control=11 (adaptation + payload)
900 pkt[4] = (uint8_t)(1 + (addPcr ? 6 : 0) + stuffing);
901 pkt[5] = addPcr ? 0x10 : 0x00; // PCR_flag
902 pos = 6;
903 if (addPcr) {
904 writePcr(pkt + pos, pcr);
905 pos += 6;
906 }
907 for (size_t i = 0; i < stuffing; i++) pkt[pos++] = 0xFF;
908 }
909 copyVirtual(pkt + pos, offset, chunk, header, header_len, payload, payload_len);
910 pos += chunk;
911 cc = (uint8_t)((cc + 1) & 0x0F);
913 offset += chunk;
914 first = false;
915 } while (offset < total_len);
916 }
917
921 void writeAccessUnit(uint16_t pid, uint8_t stream_id, uint8_t &cc, const uint8_t *data, size_t len,
922 uint64_t pts, bool withPcr) {
923 static const size_t kOptionalLen = 8; // 3 flag/length bytes + 5-byte PTS field
924 size_t pes_payload = kOptionalLen + len;
925 uint16_t pes_len = (pes_payload <= 0xFFFF) ? (uint16_t)pes_payload : 0;
926
927 uint8_t hdr[6 + kOptionalLen];
928 hdr[0] = 0x00;
929 hdr[1] = 0x00;
930 hdr[2] = 0x01;
931 hdr[3] = stream_id;
932 hdr[4] = (uint8_t)(pes_len >> 8);
933 hdr[5] = (uint8_t)pes_len;
934 hdr[6] = 0x80; // '10' marker + scrambling/priority/alignment/copyright flags = 0
935 hdr[7] = 0x80; // PTS_DTS_flags='10' (PTS only), other flags 0
936 hdr[8] = 0x05; // PES_header_data_length: 5 bytes (the PTS field) follow
937 writeTsTimestamp(hdr + 9, pts);
938
939 packBytesToTs(pid, cc, hdr, sizeof(hdr), data, len, withPcr, pts);
940 }
941
942 void writeStreamEntry(uint8_t *pkt, size_t &pos, uint8_t stream_type, uint16_t pid) {
943 pkt[pos++] = stream_type;
944 pkt[pos++] = (uint8_t)(0xE0 | ((pid >> 8) & 0x1F));
945 pkt[pos++] = (uint8_t)(pid & 0xFF);
946 pkt[pos++] = 0xF0; // reserved(4) + ES_info_length high(4)=0
947 pkt[pos++] = 0x00; // ES_info_length low = 0 (no descriptors)
948 }
949
952 void writePat() {
953 uint8_t pkt[MTS_PACKET_SIZE];
954 memset(pkt, 0xFF, sizeof(pkt));
955 pkt[0] = MTS_SYNC_BYTE;
956 pkt[1] = (uint8_t)(0x40 | ((MTS_PID_PAT >> 8) & 0x1F));
957 pkt[2] = (uint8_t)(MTS_PID_PAT & 0xFF);
958 pkt[3] = (uint8_t)(0x10 | (pat_cc & 0x0F));
959 pat_cc = (uint8_t)((pat_cc + 1) & 0x0F);
960
961 size_t pos = 4;
962 pkt[pos++] = 0x00; // pointer_field
963 size_t section_start = pos;
964 pkt[pos++] = 0x00; // table_id: program_association_section
965 size_t len_pos = pos;
966 pos += 2; // section_length placeholder
967 size_t after_len = pos;
968 pkt[pos++] = 0x00;
969 pkt[pos++] = 0x01; // transport_stream_id = 1
970 pkt[pos++] = 0xC1; // reserved(2)=11 + version(5)=0 + current_next_indicator(1)=1
971 pkt[pos++] = 0x00; // section_number
972 pkt[pos++] = 0x00; // last_section_number
973 pkt[pos++] = 0x00;
974 pkt[pos++] = 0x01; // program_number = 1
975 pkt[pos++] = (uint8_t)(0xE0 | ((MTS_PID_PMT >> 8) & 0x1F));
976 pkt[pos++] = (uint8_t)(MTS_PID_PMT & 0xFF);
977
978 size_t section_length = (pos - after_len) + 4; // + CRC32
979 pkt[len_pos] = (uint8_t)(0xB0 | ((section_length >> 8) & 0x0F));
980 pkt[len_pos + 1] = (uint8_t)(section_length & 0xFF);
981 uint32_t crc = mtsCrc32(pkt + section_start, pos - section_start);
982 pkt[pos++] = (uint8_t)(crc >> 24);
983 pkt[pos++] = (uint8_t)(crc >> 16);
984 pkt[pos++] = (uint8_t)(crc >> 8);
985 pkt[pos++] = (uint8_t)crc;
986
988 }
989
992 void writePmt() {
993 uint8_t pkt[MTS_PACKET_SIZE];
994 memset(pkt, 0xFF, sizeof(pkt));
995 pkt[0] = MTS_SYNC_BYTE;
996 pkt[1] = (uint8_t)(0x40 | ((MTS_PID_PMT >> 8) & 0x1F));
997 pkt[2] = (uint8_t)(MTS_PID_PMT & 0xFF);
998 pkt[3] = (uint8_t)(0x10 | (pmt_cc & 0x0F));
999 pmt_cc = (uint8_t)((pmt_cc + 1) & 0x0F);
1000
1001 size_t pos = 4;
1002 pkt[pos++] = 0x00; // pointer_field
1003 size_t section_start = pos;
1004 pkt[pos++] = 0x02; // table_id: TS_program_map_section
1005 size_t len_pos = pos;
1006 pos += 2; // section_length placeholder
1007 size_t after_len = pos;
1008 pkt[pos++] = 0x00;
1009 pkt[pos++] = 0x01; // program_number = 1
1010 pkt[pos++] = 0xC1; // reserved(2)=11 + version(5)=0 + current_next_indicator(1)=1
1011 pkt[pos++] = 0x00; // section_number
1012 pkt[pos++] = 0x00; // last_section_number
1013 pkt[pos++] = (uint8_t)(0xE0 | ((MTS_PID_VIDEO >> 8) & 0x1F)); // PCR_PID = video PID
1014 pkt[pos++] = (uint8_t)(MTS_PID_VIDEO & 0xFF);
1015 pkt[pos++] = 0xF0; // reserved(4) + program_info_length high(4)=0
1016 pkt[pos++] = 0x00; // program_info_length low = 0
1017
1020
1021 size_t section_length = (pos - after_len) + 4; // + CRC32
1022 pkt[len_pos] = (uint8_t)(0xB0 | ((section_length >> 8) & 0x0F));
1023 pkt[len_pos + 1] = (uint8_t)(section_length & 0xFF);
1024 uint32_t crc = mtsCrc32(pkt + section_start, pos - section_start);
1025 pkt[pos++] = (uint8_t)(crc >> 24);
1026 pkt[pos++] = (uint8_t)(crc >> 16);
1027 pkt[pos++] = (uint8_t)(crc >> 8);
1028 pkt[pos++] = (uint8_t)crc;
1029
1031 }
1032};
1033
1034} // namespace audio_tools
WAV Audio Formats used by Microsoft e.g. in AVI video files.
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#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
ISO/IEC 13818-1 MPEG Transport Stream Demuxer: parses the fixed 188-byte TS packet framing,...
Definition ContainerMTS.h:234
void setOutput(Print &out) override
Defines where the decoded result is written to.
Definition ContainerMTS.h:290
bool isValid(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:253
void setOutputVideo(VideoOutput &out) override
Definition ContainerMTS.h:292
bool audio_header_parsed
Definition ContainerMTS.h:353
bool parse()
Definition ContainerMTS.h:361
void parsePacket(const uint8_t *pkt)
Definition ContainerMTS.h:391
Print * p_output_audio
Definition ContainerMTS.h:335
VideoOutput * p_output_video_video
Definition ContainerMTS.h:337
VideoInfo getVideoInfo() override
Definition ContainerMTS.h:303
AudioInfoFormat getAudioInfo() override
Definition ContainerMTS.h:313
Print * p_output_video
Definition ContainerMTS.h:336
void setOutputAudio(Print &out) override
Definition ContainerMTS.h:289
MTSParseBuffer parse_buffer
Definition ContainerMTS.h:334
uint16_t pmt_pid
Definition ContainerMTS.h:340
size_t audio_unit_len
Definition ContainerMTS.h:351
bool video_frame_open
Definition ContainerMTS.h:346
void end() override
Definition ContainerMTS.h:279
uint32_t audio_sample_rate
Definition ContainerMTS.h:354
void flushVideoUnit()
Definition ContainerMTS.h:554
uint16_t audio_pid
Definition ContainerMTS.h:342
Vector< uint8_t > audio_unit_buffer
Definition ContainerMTS.h:350
bool is_parsing_active
Definition ContainerMTS.h:333
size_t write(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:323
void appendToAudio(const uint8_t *data, size_t len)
Definition ContainerMTS.h:536
Vector< uint8_t > video_unit_buffer
Definition ContainerMTS.h:348
void flushUnit(bool isVideo)
Definition ContainerMTS.h:547
void parsePat(const uint8_t *data, size_t len, bool pusi)
Definition ContainerMTS.h:421
void handlePesPayload(bool isVideo, const uint8_t *payload, size_t len, bool pusi)
Definition ContainerMTS.h:482
void appendToVideo(const uint8_t *data, size_t len)
Definition ContainerMTS.h:526
Vector< uint8_t > audio_probe
Definition ContainerMTS.h:356
DemuxerMTS(int bufferSize=8 *MTS_PACKET_SIZE)
Definition ContainerMTS.h:239
const char * mime() override
Definition ContainerMTS.h:245
void parsePmt(const uint8_t *data, size_t len, bool pusi)
Definition ContainerMTS.h:444
size_t video_unit_len
Definition ContainerMTS.h:349
void setSendWavHeader(bool flag) override
Definition ContainerMTS.h:297
uint8_t video_stream_type
Definition ContainerMTS.h:343
void flushAudioUnit()
Definition ContainerMTS.h:566
bool begin() override
Definition ContainerMTS.h:259
void probeAudioHeader(const uint8_t *data, size_t len)
Definition ContainerMTS.h:578
uint8_t audio_stream_type
Definition ContainerMTS.h:344
uint8_t audio_channels
Definition ContainerMTS.h:355
uint32_t total_bytes
Definition ContainerMTS.h:338
const char * mimeVideo() override
The container's MIME type (e.g. "video/avi", "video/mp4").
Definition ContainerMTS.h:241
void setOutputVideo(Print &out) override
Definition ContainerMTS.h:291
bool audio_frame_open
Definition ContainerMTS.h:347
void appendToUnit(bool isVideo, const uint8_t *data, size_t len)
Definition ContainerMTS.h:517
uint16_t video_pid
Definition ContainerMTS.h:341
Minimal byte accumulator used by DemuxerMTS to buffer incoming bytes until at least one full 188-byte...
Definition ContainerMTS.h:156
Vector< uint8_t > vec
Definition ContainerMTS.h:176
size_t writeArray(const uint8_t *data, size_t len)
Definition ContainerMTS.h:159
void consume(size_t len)
Definition ContainerMTS.h:165
void resize(size_t size)
Definition ContainerMTS.h:158
size_t count
Definition ContainerMTS.h:177
size_t availableToWrite()
Definition ContainerMTS.h:172
size_t available()
Definition ContainerMTS.h:171
uint8_t * data()
Definition ContainerMTS.h:170
void clear()
Definition ContainerMTS.h:173
Common interface for muxers (MuxerAVI, MuxerMP4) that combine an already-encoded video track (and opt...
Definition ContainerCommon.h:42
ISO/IEC 13818-1 MPEG Transport Stream Encoder: packetizes an already-encoded video elementary stream ...
Definition ContainerMTS.h:683
StreamContentType write_stream_type
Definition ContainerMTS.h:803
bool is_open
Definition ContainerMTS.h:802
void setOutput(Print &out) override
Defines the output: e.g. a local File or a network Client.
Definition ContainerMTS.h:692
void packBytesToTs(uint16_t pid, uint8_t &cc, const uint8_t *header, size_t header_len, const uint8_t *payload, size_t payload_len, bool withPcr, uint64_t pcr)
Definition ContainerMTS.h:877
uint8_t audio_cc
Definition ContainerMTS.h:808
size_t addVideoFrame(const uint8_t *data, size_t len, bool isKeyFrame=true) override
Definition ContainerMTS.h:744
AudioInfoFormat audio_info
Definition ContainerMTS.h:800
void writePat()
Definition ContainerMTS.h:952
StreamContentType streamType() override
The track write() currently targets (see setStreamType())
Definition ContainerMTS.h:729
size_t addJpegFrame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:764
uint32_t audioFrameCount()
Definition ContainerMTS.h:795
static void writePcr(uint8_t *buf, uint64_t pcr_base)
Definition ContainerMTS.h:838
MuxerMTS()
Definition ContainerMTS.h:685
bool has_audio
Definition ContainerMTS.h:801
static void copyVirtual(uint8_t *dst, size_t voffset, size_t n, const uint8_t *a, size_t alen, const uint8_t *b, size_t blen)
Definition ContainerMTS.h:853
MuxerMTS(Print &out)
Definition ContainerMTS.h:689
AudioInfoFormat & audioInfo() override
Provides read/write access to the audio track's AudioInfoFormat.
Definition ContainerMTS.h:707
uint64_t audioPts()
Definition ContainerMTS.h:814
void end() override
MPEG-TS has no defined trailer - simply stops accepting frames.
Definition ContainerMTS.h:732
void setVideoInfo(MuxerVideoConfig config) override
Defines the video track configuration - call before begin()
Definition ContainerMTS.h:694
void setStreamType(StreamContentType type) override
Selects whether write() feeds the video or the audio track.
Definition ContainerMTS.h:728
uint8_t pmt_cc
Definition ContainerMTS.h:808
size_t addAudioFrame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:785
size_t write(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:736
uint64_t videoPts()
Definition ContainerMTS.h:810
void setAudioInfo(AudioInfoFormat info) override
Definition ContainerMTS.h:701
uint8_t pat_cc
Definition ContainerMTS.h:808
uint32_t videoFrameCount()
Definition ContainerMTS.h:794
void writeStreamEntry(uint8_t *pkt, size_t &pos, uint8_t stream_type, uint16_t pid)
Definition ContainerMTS.h:942
size_t addYUV422Frame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:769
uint32_t audio_frame_count
Definition ContainerMTS.h:805
uint32_t audio_samples_per_frame
Definition ContainerMTS.h:806
size_t addI420Frame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:777
Print * p_out
Definition ContainerMTS.h:798
size_t addRGB565Frame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:773
void writePmt()
Definition ContainerMTS.h:992
bool begin() override
Definition ContainerMTS.h:711
uint32_t video_frame_count
Definition ContainerMTS.h:804
uint8_t video_cc
Definition ContainerMTS.h:808
const char * mimeVideo() override
Definition ContainerMTS.h:691
static void writeTsTimestamp(uint8_t *b, uint64_t ts)
Definition ContainerMTS.h:822
MuxerVideoConfig getVideoInfo() override
Provides the video track configuration.
Definition ContainerMTS.h:695
MuxerVideoConfig video_cfg
Definition ContainerMTS.h:799
void writeAccessUnit(uint16_t pid, uint8_t stream_id, uint8_t &cc, const uint8_t *data, size_t len, uint64_t pts, bool withPcr)
Definition ContainerMTS.h:921
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
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
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
@ 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
uint32_t mtsCrc32(const uint8_t *data, size_t len)
Definition ContainerMTS.h:137
static const uint8_t MTS_STREAM_TYPE_MPEG1_VIDEO
Definition ContainerMTS.h:35
static const uint32_t MTS_CLOCK_HZ
Definition ContainerMTS.h:14
static const uint8_t MTS_STREAM_TYPE_MJPEG
Definition ContainerMTS.h:51
bool isMtsAudioStreamType(uint8_t st)
True if 'st' is one of the audio stream_type values recognized above.
Definition ContainerMTS.h:60
static const uint8_t MTS_SYNC_BYTE
Definition ContainerMTS.h:13
static const size_t MTS_PACKET_SIZE
Definition ContainerMTS.h:12
static const uint8_t MTS_STREAM_TYPE_AAC_ADTS
Definition ContainerMTS.h:39
static const uint8_t MTS_STREAM_TYPE_MPEG2_VIDEO
Definition ContainerMTS.h:36
static const uint8_t MTS_STREAM_TYPE_MPEG4_VIDEO
Definition ContainerMTS.h:40
static const uint16_t MTS_PID_VIDEO
Definition ContainerMTS.h:24
VideoFormat mtsVideoFormat(uint8_t st)
Definition ContainerMTS.h:69
static const uint16_t MTS_PID_PMT
Definition ContainerMTS.h:23
static const uint8_t MTS_STREAM_TYPE_PRIVATE
Definition ContainerMTS.h:42
static const uint8_t MTS_STREAM_TYPE_MPEG1_AUDIO
Definition ContainerMTS.h:37
uint8_t mtsVideoStreamType(VideoFormat format)
Definition ContainerMTS.h:100
static const uint8_t MTS_STREAM_ID_AUDIO
Definition ContainerMTS.h:31
static const uint8_t MTS_STREAM_ID_VIDEO
Definition ContainerMTS.h:30
static const uint8_t MTS_STREAM_TYPE_MPEG2_AUDIO
Definition ContainerMTS.h:38
AudioFormat mtsAudioFormat(uint8_t st)
Definition ContainerMTS.h:86
uint8_t mtsAudioStreamType(AudioFormat format)
Definition ContainerMTS.h:118
static const uint16_t MTS_PID_AUDIO
Definition ContainerMTS.h:25
bool isMtsVideoStreamType(uint8_t st)
True if 'st' is one of the video stream_type values recognized above.
Definition ContainerMTS.h:54
static const uint8_t MTS_STREAM_TYPE_H264
Definition ContainerMTS.h:41
static const uint16_t MTS_PID_PAT
Definition ContainerMTS.h:22
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:58
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:60
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:62
Shared video track configuration for muxers (MuxerAVI, MuxerMP4) - call before begin().
Definition ContainerCommon.h:11
float fps
Definition ContainerCommon.h:14
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 total_file_size
Definition VideoOutput.h:68
VideoFormat format
Video codec - VideoFormat::UNKNOWN if not (yet) determined.
Definition VideoOutput.h:58