111 LOGW(
"MuxerMTS: VideoFormat %d has no standard MPEG-TS stream_type - using private data (0x06)",
125 LOGW(
"MuxerMTS: AudioFormat %d has no standard MPEG-TS stream_type - using private data (0x06)",
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);
241 const char *
mimeVideo()
override {
return "video/mp2t"; }
247 return m !=
nullptr ? m :
"audio/aac";
253 bool isValid(
const uint8_t *data,
size_t len)
override {
323 size_t write(
const uint8_t *data,
size_t len)
override {
370 for (; idx < avail; idx++) {
392 if (pkt[1] & 0x80)
return;
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;
398 if (adaptation_control == 0x02 || adaptation_control == 0x03) {
399 uint8_t af_len = pkt[4];
403 if (adaptation_control == 0x02)
return;
405 if (payload_len == 0)
return;
406 const uint8_t *payload = pkt + pos;
409 parsePat(payload, payload_len, pusi);
411 parsePmt(payload, payload_len, pusi);
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];
424 if (off + 8 > len)
return;
425 if (data[off] != 0x00)
return;
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;
429 if (section_end > len) section_end = len;
430 size_t prog_off = after_length + 5;
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) {
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];
447 if (off + 12 > len)
return;
448 if (data[off] != 0x02)
return;
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;
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;
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];
464 new_video_pid = es_pid;
465 new_video_type = stream_type;
467 new_audio_pid = es_pid;
468 new_audio_type = stream_type;
470 stream_off += 5 + es_info_length;
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");
492 LOGW(
"DemuxerMTS: PES header split across TS packet boundary - dropping unit");
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;
499 if (len < header_total) {
500 LOGW(
"DemuxerMTS: PES header split across TS packet boundary - dropping unit");
504 const uint8_t *es = payload + header_total;
505 size_t es_len = len - header_total;
511 if (!frame_open)
return;
530 if (newCap < needed) newCap = needed;
540 if (newCap < needed) newCap = needed;
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++) {
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,
596 uint32_t rate = rates[sr_index];
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};
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];
691 const char *
mimeVideo()
override {
return "video/mp2t"; }
712 if (
p_out ==
nullptr) {
713 LOGE(
"output not defined");
736 size_t write(
const uint8_t *data,
size_t len)
override {
745 if (!
is_open || data ==
nullptr || len == 0)
return 0;
766 LOGW(
"MuxerMTS: getVideoInfo().format does not match addJpegFrame()");
770 LOGW(
"MuxerMTS: raw YUV422 is not supported by MPEG-TS");
774 LOGW(
"MuxerMTS: raw RGB565 is not supported by MPEG-TS");
778 LOGW(
"MuxerMTS: raw I420 is not supported by MPEG-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);
828 b[3] = (uint8_t)(low >> 8);
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);
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) {
856 if (voffset < alen) {
857 size_t take = min(n, alen - voffset);
858 memcpy(dst, a + voffset, take);
862 size_t bOffset = (voffset + written) - alen;
863 memcpy(dst + written, b + bOffset, n - written);
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;
883 size_t remaining = total_len - offset;
884 bool addPcr = first && withPcr;
885 bool use_af = addPcr || remaining < 184;
888 pkt[1] = (uint8_t)((first ? 0x40 : 0x00) | ((pid >> 8) & 0x1F));
889 pkt[2] = (uint8_t)(pid & 0xFF);
892 pkt[3] = (uint8_t)(0x10 | (cc & 0x0F));
896 size_t overhead = 2 + (addPcr ? 6 : 0);
897 chunk = min(remaining, 184 - overhead);
898 size_t stuffing = 184 - overhead - chunk;
899 pkt[3] = (uint8_t)(0x30 | (cc & 0x0F));
900 pkt[4] = (uint8_t)(1 + (addPcr ? 6 : 0) + stuffing);
901 pkt[5] = addPcr ? 0x10 : 0x00;
907 for (
size_t i = 0; i < stuffing; i++) pkt[pos++] = 0xFF;
909 copyVirtual(pkt + pos, offset, chunk, header, header_len, payload, payload_len);
911 cc = (uint8_t)((cc + 1) & 0x0F);
915 }
while (offset < total_len);
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;
924 size_t pes_payload = kOptionalLen + len;
925 uint16_t pes_len = (pes_payload <= 0xFFFF) ? (uint16_t)pes_payload : 0;
927 uint8_t hdr[6 + kOptionalLen];
932 hdr[4] = (uint8_t)(pes_len >> 8);
933 hdr[5] = (uint8_t)pes_len;
939 packBytesToTs(pid, cc, hdr,
sizeof(hdr), data, len, withPcr, pts);
943 pkt[pos++] = stream_type;
944 pkt[pos++] = (uint8_t)(0xE0 | ((pid >> 8) & 0x1F));
945 pkt[pos++] = (uint8_t)(pid & 0xFF);
954 memset(pkt, 0xFF,
sizeof(pkt));
956 pkt[1] = (uint8_t)(0x40 | ((
MTS_PID_PAT >> 8) & 0x1F));
958 pkt[3] = (uint8_t)(0x10 | (
pat_cc & 0x0F));
963 size_t section_start = pos;
965 size_t len_pos = pos;
967 size_t after_len = pos;
975 pkt[pos++] = (uint8_t)(0xE0 | ((
MTS_PID_PMT >> 8) & 0x1F));
978 size_t section_length = (pos - after_len) + 4;
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;
994 memset(pkt, 0xFF,
sizeof(pkt));
996 pkt[1] = (uint8_t)(0x40 | ((
MTS_PID_PMT >> 8) & 0x1F));
998 pkt[3] = (uint8_t)(0x10 | (
pmt_cc & 0x0F));
1003 size_t section_start = pos;
1005 size_t len_pos = pos;
1007 size_t after_len = pos;
1013 pkt[pos++] = (uint8_t)(0xE0 | ((
MTS_PID_VIDEO >> 8) & 0x1F));
1021 size_t section_length = (pos - after_len) + 4;
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;
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGE(...)
Definition AudioLoggerIDF.h:30
virtual size_t write(const uint8_t *data, size_t len)
Definition Arduino.h:120
virtual void flush()
Definition Arduino.h:130
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