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.
41static const uint8_t MTS_STREAM_TYPE_H264 = 0x1B;
42static const uint8_t MTS_STREAM_TYPE_PRIVATE = 0x06;
52
87 switch (st) {
89 return AudioFormat::AAC;
92 return AudioFormat::MP3;
93 default:
95 }
96}
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}
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;
339
340 uint16_t pmt_pid = 0xFFFF;
341 uint16_t video_pid = 0xFFFF;
342 uint16_t audio_pid = 0xFFFF;
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
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
701 void setAudioInfo(AudioInfoFormat info) override {
702 audio_info = info;
704 // AAC: 1024 samples per frame; MP3: 1152 for MPEG-1 (32 kHz and up),
705 // 576 for the MPEG-2/2.5 frames used at lower sample rates
707 audio_samples_per_frame = audio_info.sample_rate >= 32000 ? 1152 : 576;
708 } else {
710 }
711 has_audio = true;
712 }
713 AudioInfoFormat &audioInfo() override { return audio_info; }
714
717 bool begin() override {
718 if (p_out == nullptr) {
719 LOGE("output not defined");
720 return false;
721 }
724 out_len = 0;
725 start_ms = millis();
726 pat_cc = 0;
727 pmt_cc = 0;
728 video_cc = 0;
729 audio_cc = 0;
730 writePat();
731 writePmt();
732 flushPackets();
733 is_open = true;
734 return true;
735 }
736
737 void setStreamType(StreamContentType type) override { write_stream_type = type; }
739
741 void end() override { is_open = false; }
742
743 operator bool() override { return is_open; }
744
745 size_t write(const uint8_t *data, size_t len) override {
747 return addVideoFrame(data, len);
748 }
749
753 size_t addVideoFrame(const uint8_t *data, size_t len, bool isKeyFrame = true) override {
754 if (!is_open || data == nullptr || len == 0) return 0;
755 if (isKeyFrame) {
756 writePat();
757 writePmt();
758 }
759 uint64_t pts = videoPts();
761 /*withPcr=*/true);
762 flushPackets();
764 return len;
765 }
766
774 size_t addJpegFrame(const uint8_t *data, size_t len) override {
776 LOGW("MuxerMTS: getVideoInfo().format does not match addJpegFrame()");
777 return addVideoFrame(data, len, true);
778 }
779 size_t addYUV422Frame(const uint8_t *data, size_t len) override {
780 LOGW("MuxerMTS: raw YUV422 is not supported by MPEG-TS");
781 return addVideoFrame(data, len);
782 }
783 size_t addRGB565Frame(const uint8_t *data, size_t len) override {
784 LOGW("MuxerMTS: raw RGB565 is not supported by MPEG-TS");
785 return addVideoFrame(data, len);
786 }
787 size_t addI420Frame(const uint8_t *data, size_t len) override {
788 LOGW("MuxerMTS: raw I420 is not supported by MPEG-TS");
789 return addVideoFrame(data, len);
790 }
791
795 size_t addAudioFrame(const uint8_t *data, size_t len) override {
796 if (!is_open || !has_audio || data == nullptr || len == 0) return 0;
797 uint64_t pts = audioPts();
799 /*withPcr=*/false);
800 flushPackets();
802 return len;
803 }
804
811 void setRealTime(bool flag) { is_real_time = flag; }
812
815
816 protected:
817 Print *p_out = nullptr;
820 bool has_audio = false;
821 bool is_open = false;
825 uint32_t audio_samples_per_frame = 1024; // see setAudioInfo()
826 bool is_real_time = false;
828 // TS packets are collected and written in batches of up to
829 // kPacketsPerWrite (7 x 188 = 1316 bytes, the usual network payload):
830 // one write per packet is slow on network clients
831 static const size_t kPacketsPerWrite = 7;
833 size_t out_len = 0;
834
836
837 void writePacket(const uint8_t *pkt) {
838 memcpy(out_buf + out_len, pkt, MTS_PACKET_SIZE);
840 if (out_len == sizeof(out_buf)) flushPackets();
841 }
842
844 if (out_len > 0) p_out->write(out_buf, out_len);
845 out_len = 0;
846 }
847
849 if (is_real_time) return (uint64_t)(millis() - start_ms) * (MTS_CLOCK_HZ / 1000);
850 float fps = video_cfg.fps > 0 ? video_cfg.fps : 25.0f;
851 return (uint64_t)((double)video_frame_count * MTS_CLOCK_HZ / fps);
852 }
857
861 static void writeTsTimestamp(uint8_t *b, uint64_t ts) {
862 b[0] = (uint8_t)((0x02 << 4) | (((ts >> 30) & 0x07) << 1) | 0x01);
863 uint16_t mid = (uint16_t)((((ts >> 15) & 0x7FFF) << 1) | 0x01);
864 uint16_t low = (uint16_t)(((ts & 0x7FFF) << 1) | 0x01);
865 b[1] = (uint8_t)(mid >> 8);
866 b[2] = (uint8_t)mid;
867 b[3] = (uint8_t)(low >> 8);
868 b[4] = (uint8_t)low;
869 }
870
877 static void writePcr(uint8_t *buf, uint64_t pcr_base) {
878 pcr_base &= 0x1FFFFFFFFULL;
879 buf[0] = (uint8_t)(pcr_base >> 25);
880 buf[1] = (uint8_t)(pcr_base >> 17);
881 buf[2] = (uint8_t)(pcr_base >> 9);
882 buf[3] = (uint8_t)(pcr_base >> 1);
883 buf[4] = (uint8_t)(((pcr_base & 0x1) << 7) | 0x7E);
884 buf[5] = 0x00;
885 }
886
892 static void copyVirtual(uint8_t *dst, size_t voffset, size_t n, const uint8_t *a, size_t alen,
893 const uint8_t *b, size_t blen) {
894 size_t written = 0;
895 if (voffset < alen) {
896 size_t take = min(n, alen - voffset);
897 memcpy(dst, a + voffset, take);
898 written += take;
899 }
900 if (written < n) {
901 size_t bOffset = (voffset + written) - alen;
902 memcpy(dst + written, b + bOffset, n - written);
903 }
904 }
905
916 void packBytesToTs(uint16_t pid, uint8_t &cc, const uint8_t *header, size_t header_len,
917 const uint8_t *payload, size_t payload_len, bool withPcr, uint64_t pcr) {
918 size_t total_len = header_len + payload_len;
919 size_t offset = 0;
920 bool first = true;
921 do {
922 size_t remaining = total_len - offset;
923 bool addPcr = first && withPcr;
924 bool use_af = addPcr || remaining < 184;
926 pkt[0] = MTS_SYNC_BYTE;
927 pkt[1] = (uint8_t)((first ? 0x40 : 0x00) | ((pid >> 8) & 0x1F));
928 pkt[2] = (uint8_t)(pid & 0xFF);
929 size_t pos, chunk;
930 if (!use_af) {
931 pkt[3] = (uint8_t)(0x10 | (cc & 0x0F)); // adaptation_field_control=01 (payload only)
932 pos = 4;
933 chunk = 184;
934 } else {
935 size_t overhead = 2 + (addPcr ? 6 : 0); // adaptation_field_length byte + flags byte + PCR
936 chunk = min(remaining, 184 - overhead);
937 size_t stuffing = 184 - overhead - chunk;
938 pkt[3] = (uint8_t)(0x30 | (cc & 0x0F)); // adaptation_field_control=11 (adaptation + payload)
939 pkt[4] = (uint8_t)(1 + (addPcr ? 6 : 0) + stuffing);
940 pkt[5] = addPcr ? 0x10 : 0x00; // PCR_flag
941 pos = 6;
942 if (addPcr) {
943 writePcr(pkt + pos, pcr);
944 pos += 6;
945 }
946 for (size_t i = 0; i < stuffing; i++) pkt[pos++] = 0xFF;
947 }
948 copyVirtual(pkt + pos, offset, chunk, header, header_len, payload, payload_len);
949 pos += chunk;
950 cc = (uint8_t)((cc + 1) & 0x0F);
951 writePacket(pkt);
952 offset += chunk;
953 first = false;
954 } while (offset < total_len);
955 }
956
960 void writeAccessUnit(uint16_t pid, uint8_t stream_id, uint8_t &cc, const uint8_t *data, size_t len,
961 uint64_t pts, bool withPcr) {
962 static const size_t kOptionalLen = 8; // 3 flag/length bytes + 5-byte PTS field
963 size_t pes_payload = kOptionalLen + len;
964 uint16_t pes_len = (pes_payload <= 0xFFFF) ? (uint16_t)pes_payload : 0;
965
966 uint8_t hdr[6 + kOptionalLen];
967 hdr[0] = 0x00;
968 hdr[1] = 0x00;
969 hdr[2] = 0x01;
970 hdr[3] = stream_id;
971 hdr[4] = (uint8_t)(pes_len >> 8);
972 hdr[5] = (uint8_t)pes_len;
973 hdr[6] = 0x80; // '10' marker + scrambling/priority/alignment/copyright flags = 0
974 hdr[7] = 0x80; // PTS_DTS_flags='10' (PTS only), other flags 0
975 hdr[8] = 0x05; // PES_header_data_length: 5 bytes (the PTS field) follow
976 writeTsTimestamp(hdr + 9, pts);
977
978 packBytesToTs(pid, cc, hdr, sizeof(hdr), data, len, withPcr, pts);
979 }
980
981 void writeStreamEntry(uint8_t *pkt, size_t &pos, uint8_t stream_type, uint16_t pid) {
982 pkt[pos++] = stream_type;
983 pkt[pos++] = (uint8_t)(0xE0 | ((pid >> 8) & 0x1F));
984 pkt[pos++] = (uint8_t)(pid & 0xFF);
985 pkt[pos++] = 0xF0; // reserved(4) + ES_info_length high(4)=0
986 pkt[pos++] = 0x00; // ES_info_length low = 0 (no descriptors)
987 }
988
991 void writePat() {
993 memset(pkt, 0xFF, sizeof(pkt));
994 pkt[0] = MTS_SYNC_BYTE;
995 pkt[1] = (uint8_t)(0x40 | ((MTS_PID_PAT >> 8) & 0x1F));
996 pkt[2] = (uint8_t)(MTS_PID_PAT & 0xFF);
997 pkt[3] = (uint8_t)(0x10 | (pat_cc & 0x0F));
998 pat_cc = (uint8_t)((pat_cc + 1) & 0x0F);
999
1000 size_t pos = 4;
1001 pkt[pos++] = 0x00; // pointer_field
1002 size_t section_start = pos;
1003 pkt[pos++] = 0x00; // table_id: program_association_section
1004 size_t len_pos = pos;
1005 pos += 2; // section_length placeholder
1006 size_t after_len = pos;
1007 pkt[pos++] = 0x00;
1008 pkt[pos++] = 0x01; // transport_stream_id = 1
1009 pkt[pos++] = 0xC1; // reserved(2)=11 + version(5)=0 + current_next_indicator(1)=1
1010 pkt[pos++] = 0x00; // section_number
1011 pkt[pos++] = 0x00; // last_section_number
1012 pkt[pos++] = 0x00;
1013 pkt[pos++] = 0x01; // program_number = 1
1014 pkt[pos++] = (uint8_t)(0xE0 | ((MTS_PID_PMT >> 8) & 0x1F));
1015 pkt[pos++] = (uint8_t)(MTS_PID_PMT & 0xFF);
1016
1017 size_t section_length = (pos - after_len) + 4; // + CRC32
1018 pkt[len_pos] = (uint8_t)(0xB0 | ((section_length >> 8) & 0x0F));
1019 pkt[len_pos + 1] = (uint8_t)(section_length & 0xFF);
1020 uint32_t crc = mtsCrc32(pkt + section_start, pos - section_start);
1021 pkt[pos++] = (uint8_t)(crc >> 24);
1022 pkt[pos++] = (uint8_t)(crc >> 16);
1023 pkt[pos++] = (uint8_t)(crc >> 8);
1024 pkt[pos++] = (uint8_t)crc;
1025
1026 writePacket(pkt);
1027 }
1028
1031 void writePmt() {
1033 memset(pkt, 0xFF, sizeof(pkt));
1034 pkt[0] = MTS_SYNC_BYTE;
1035 pkt[1] = (uint8_t)(0x40 | ((MTS_PID_PMT >> 8) & 0x1F));
1036 pkt[2] = (uint8_t)(MTS_PID_PMT & 0xFF);
1037 pkt[3] = (uint8_t)(0x10 | (pmt_cc & 0x0F));
1038 pmt_cc = (uint8_t)((pmt_cc + 1) & 0x0F);
1039
1040 size_t pos = 4;
1041 pkt[pos++] = 0x00; // pointer_field
1042 size_t section_start = pos;
1043 pkt[pos++] = 0x02; // table_id: TS_program_map_section
1044 size_t len_pos = pos;
1045 pos += 2; // section_length placeholder
1046 size_t after_len = pos;
1047 pkt[pos++] = 0x00;
1048 pkt[pos++] = 0x01; // program_number = 1
1049 pkt[pos++] = 0xC1; // reserved(2)=11 + version(5)=0 + current_next_indicator(1)=1
1050 pkt[pos++] = 0x00; // section_number
1051 pkt[pos++] = 0x00; // last_section_number
1052 pkt[pos++] = (uint8_t)(0xE0 | ((MTS_PID_VIDEO >> 8) & 0x1F)); // PCR_PID = video PID
1053 pkt[pos++] = (uint8_t)(MTS_PID_VIDEO & 0xFF);
1054 pkt[pos++] = 0xF0; // reserved(4) + program_info_length high(4)=0
1055 pkt[pos++] = 0x00; // program_info_length low = 0
1056
1059
1060 size_t section_length = (pos - after_len) + 4; // + CRC32
1061 pkt[len_pos] = (uint8_t)(0xB0 | ((section_length >> 8) & 0x0F));
1062 pkt[len_pos + 1] = (uint8_t)(section_length & 0xFF);
1063 uint32_t crc = mtsCrc32(pkt + section_start, pos - section_start);
1064 pkt[pos++] = (uint8_t)(crc >> 24);
1065 pkt[pos++] = (uint8_t)(crc >> 16);
1066 pkt[pos++] = (uint8_t)(crc >> 8);
1067 pkt[pos++] = (uint8_t)crc;
1068
1069 writePacket(pkt);
1070 }
1071};
1072
1073} // 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:184
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:822
bool is_open
Definition ContainerMTS.h:821
void setOutput(Print &out) override
Defines the output: e.g. a local File or a network Client.
Definition ContainerMTS.h:692
static const size_t kPacketsPerWrite
Definition ContainerMTS.h:831
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:916
uint8_t audio_cc
Definition ContainerMTS.h:835
void setRealTime(bool flag)
Definition ContainerMTS.h:811
size_t addVideoFrame(const uint8_t *data, size_t len, bool isKeyFrame=true) override
Definition ContainerMTS.h:753
AudioInfoFormat audio_info
Definition ContainerMTS.h:819
void writePat()
Definition ContainerMTS.h:991
StreamContentType streamType() override
The track write() currently targets (see setStreamType())
Definition ContainerMTS.h:738
size_t addJpegFrame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:774
uint32_t audioFrameCount()
Definition ContainerMTS.h:814
static void writePcr(uint8_t *buf, uint64_t pcr_base)
Definition ContainerMTS.h:877
MuxerMTS()
Definition ContainerMTS.h:685
bool has_audio
Definition ContainerMTS.h:820
uint32_t start_ms
Definition ContainerMTS.h:827
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:892
MuxerMTS(Print &out)
Definition ContainerMTS.h:689
size_t out_len
Definition ContainerMTS.h:833
AudioInfoFormat & audioInfo() override
Provides read/write access to the audio track's AudioInfoFormat.
Definition ContainerMTS.h:713
uint64_t audioPts()
Definition ContainerMTS.h:853
void end() override
MPEG-TS has no defined trailer - simply stops accepting frames.
Definition ContainerMTS.h:741
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:737
uint8_t pmt_cc
Definition ContainerMTS.h:835
size_t addAudioFrame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:795
size_t write(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:745
uint64_t videoPts()
Definition ContainerMTS.h:848
void setAudioInfo(AudioInfoFormat info) override
Definition ContainerMTS.h:701
uint8_t pat_cc
Definition ContainerMTS.h:835
uint32_t videoFrameCount()
Definition ContainerMTS.h:813
void writeStreamEntry(uint8_t *pkt, size_t &pos, uint8_t stream_type, uint16_t pid)
Definition ContainerMTS.h:981
void flushPackets()
Definition ContainerMTS.h:843
size_t addYUV422Frame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:779
uint32_t audio_frame_count
Definition ContainerMTS.h:824
uint32_t audio_samples_per_frame
Definition ContainerMTS.h:825
size_t addI420Frame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:787
bool is_real_time
Definition ContainerMTS.h:826
Print * p_out
Definition ContainerMTS.h:817
size_t addRGB565Frame(const uint8_t *data, size_t len) override
Definition ContainerMTS.h:783
uint8_t out_buf[kPacketsPerWrite *MTS_PACKET_SIZE]
Definition ContainerMTS.h:832
void writePmt()
Definition ContainerMTS.h:1031
bool begin() override
Definition ContainerMTS.h:717
uint32_t video_frame_count
Definition ContainerMTS.h:823
uint8_t video_cc
Definition ContainerMTS.h:835
const char * mimeVideo() override
Definition ContainerMTS.h:691
static void writeTsTimestamp(uint8_t *b, uint64_t ts)
Definition ContainerMTS.h:861
MuxerVideoConfig getVideoInfo() override
Provides the video track configuration.
Definition ContainerMTS.h:695
void writePacket(const uint8_t *pkt)
Definition ContainerMTS.h:837
MuxerVideoConfig video_cfg
Definition ContainerMTS.h:818
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:960
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:321
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
uint32_t millis()
Returns the milliseconds since the start.
Definition Arduino.h:260
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