arduino-audio-tools
Loading...
Searching...
No Matches
CodecMTS.h
Go to the documentation of this file.
1#pragma once
2
3#define TS_PACKET_SIZE 188
4
5#ifndef MTS_WRITE_BUFFER_SIZE
6#define MTS_WRITE_BUFFER_SIZE 2000
7#endif
8
11#include "AudioToolsConfig.h"
12#include "stdlib.h"
13
14namespace audio_tools {
15
20enum class MTSStreamType {
21 VIDEO = 0x01,
22 VIDEO_H262 = 0x02,
23 AUDIO_MP3 = 0x03,
25 PRV_SECTIONS = 0x05,
26 PES_PRV = 0x06,
27 MHEG = 0x07,
28 H222_0_DSM_CC = 0x08,
29 H222_1 = 0x09,
30 A = 0x0A,
31 B = 0x0B,
32 C = 0x0C,
33 D = 0x0D,
34 H222_0_AUX = 0x0E,
35 AUDIO_AAC = 0x0F,
36 VISUAL = 0x10,
37 AUDIO_AAC_LATM = 0x11,
38 SL_PES = 0x12,
39 SL_SECTIONS = 0x13,
40 SYNC_DOWNLOAD = 0x14,
41 PES_METADATA = 0x15,
42 METDATA_SECTIONS = 0x16,
46 IPMP = 0x1A,
47 VIDEO_AVC = 0X1B,
48 VIDEO_H222_0 = 0x1C,
49 DCII_VIDEO = 0x80,
50 AUDIO_A53 = 0x81,
51 SCTE_STD_SUBTITLE = 0x82,
52 SCTE_ISOCH_DATA = 0x83,
53 ATSC_PROG_ID = 0x85,
54 SCTE_25 = 0x86,
55 AUDIO_EAC3 = 0x87,
56 AUDIO_DTS_HD = 0x88,
57 DVB_MPE_FEC = 0x90,
58 ULE = 0x91,
59 VEI = 0x92,
61 SCTE_IP_DATA = 0xA0,
62 DCII_TEXT = 0xC0,
63 ATSC_SYNC_DATA = 0xC2,
64 SCTE_AYSNC_DATA = 0xC3,
66 VC1 = 0xEA,
67 ATSC_USER_PRIV = 0xEB,
68};
69
70// enum class AACProfile : uint8_t {
71// MAIN = 0, // AAC Main (High complexity, rarely used)
72// LC = 1, // AAC Low Complexity (Most common)
73// SSR = 2, // AAC Scalable Sample Rate (Rare)
74// LTP = 3 // AAC Long Term Prediction (Not widely supported)
75// };
76
89class MTSDecoder : public AudioDecoder {
90 public:
92 MTSDecoder() = default;
94 MTSDecoder(AudioDecoder &dec) { p_dec = &dec; };
96 bool begin() override {
97 TRACED();
98 pmt_pid = 0xFFFF; // undefined
99 pes_count = 0;
100 is_adts_missing = false;
102 frame_length = 0;
103
104 // default supported stream types
105 if (stream_types.empty()) {
108 }
109
110 // automatically close when called multiple times
111 if (is_active) {
112 end();
113 }
114
115 if (p_dec) p_dec->begin();
116 is_active = true;
117 return true;
118 }
119
121 void end() override {
122 TRACED();
123 if (p_dec) p_dec->end();
124 is_active = false;
125 }
126
127 virtual operator bool() override { return is_active; }
128
130 const char *mime() { return "video/MP2T"; }
131
132 size_t write(const uint8_t *data, size_t len) override {
133 // only process when open
134 if (!is_active) {
135 TRACEE();
136 return 0;
137 }
138
139 // wait until we have enough data
140 if (buffer.availableForWrite() < len) {
141 LOGI("MTSDecoder::write: Buffer full");
142 demux();
143 return 0;
144 }
145 LOGI("MTSDecoder::write: %d", (int)len);
146 size_t result = buffer.writeArray((uint8_t *)data, len);
147 demux();
148 return result;
149 }
150
152 void resizeBuffer(int size) { buffer.resize(size); }
153
156 TRACED();
158 }
159
162 TRACED();
164 }
165
168 for (int j = 0; j < stream_types.size(); j++) {
169 if (stream_types[j] == type) return true;
170 }
171 return false;
172 }
173
176 if (p_dec) {
178 } else {
180 }
181 }
182
185 if (p_dec) {
187 } else {
189 }
190 }
191
193 void setOutput(Print &out_stream) override {
194 if (p_dec) {
196 } else {
198 }
199 }
200
201 protected:
202 bool is_active = false;
206 AudioDecoder *p_dec = nullptr;
208 // AACProfile aac_profile = AACProfile::LC;
212 bool is_adts_missing = false;
213 size_t pes_count = 0;
214
217 void addPID(uint16_t pid) {
218 if (pid == 0) return;
219 for (int j = 0; j < pids.size(); j++) {
220 if (pids[j] == pid) return;
221 }
222 LOGI("-> PMT PID: 0x%04X(%d)", pid, pid);
223 pids.push_back(pid);
224 }
225
227 void demux() {
228 TRACED();
229 int count = 0;
230 while (parse()) {
231 LOGI("demux: step #%d with PES #%d", ++count, (int)pes_count);
232 }
233 LOGI("Number of demux calls: %d", count);
234 }
235
237 int syncPos() {
238 int len = buffer.available();
239 if (len < TS_PACKET_SIZE) return -1;
240 for (int j = 0; j < len; j++) {
241 if (buffer.data()[j] == 0x47) {
242 return j;
243 }
244 }
245 return -1;
246 }
247
249 bool parse() {
250 int pos = syncPos();
251 if (pos < 0) return false;
252 if (pos != 0) {
253 LOGW("Sync byte not found at position 0. Skipping %d bytes", pos);
254 buffer.clearArray(pos);
255 }
256 // parse data
257 uint8_t *packet = buffer.data();
258 int pid = ((packet[1] & 0x1F) << 8) | (packet[2] & 0xFF);
259 LOGI("PID: 0x%04X(%d)", pid, pid);
260
261 // PES contains the audio data
262 if (!is_adts_missing && pids.contains(pid)) {
263 parsePES(packet, pid);
264 } else {
265 parsePacket(packet, pid);
266 }
267
268 // remove processed data
270 return true;
271 }
272
274 void parsePacket(uint8_t *packet, int pid) {
275 TRACEI();
276 bool payloadUnitStartIndicator = false;
277
278 int payloadStart =
280 int len = TS_PACKET_SIZE - payloadStart;
281
282 // if we are at the beginning we start with a pat
283 if (pid == 0 && payloadUnitStartIndicator) {
284 pids.clear();
285 }
286
287 // PID 0 is for PAT
288 if (pid == 0) {
289 parsePAT(&packet[payloadStart], len);
290 } else if (pid == pmt_pid && packet[payloadStart] == 0x02) {
291 parsePMT(&packet[payloadStart], len);
292 } else {
293 LOGE("-> Packet ignored for PID 0x%x", pid);
294 }
295 }
296
297 int getPayloadStart(uint8_t *packet, bool isPES,
299 uint8_t adaptionField = (packet[3] & 0x30) >> 4;
300 int adaptationSize = 0;
301 int offset = 4; // Start after TS header (4 bytes)
302
303 // Check for adaptation field
304 // 00 (0) → Invalid (should never happen).
305 // 01 (1) → Payload only (no adaptation field).
306 // 10 (2) → Adaptation field only (no payload).
307 // 11 (3) → Adaptation field + payload.
308 if (adaptionField == 0b11) { // Adaptation field exists
309 adaptationSize = packet[4] + 1;
310 offset += adaptationSize;
311 }
312
313 // If PUSI is set, there's a pointer field (skip it)
314 if (packet[1] & 0x40) {
315 if (!isPES) offset += packet[offset] + 1;
317 }
318
319 LOGI("Payload Unit Start Indicator (PUSI): %d", payloadUnitStartIndicator);
320 LOGI("Adaption Field Control: 0x%x / size: %d", adaptionField,
322
323 return offset;
324 }
325
326 void parsePAT(uint8_t *pat, int len) {
327 TRACEI();
328 assert(pat[0] == 0); // Program Association section
329 int startOfProgramNums = 8;
330 int lengthOfPATValue = 4;
331 int sectionLength = ((pat[1] & 0x0F) << 8) | (pat[2] & 0xFF);
332 LOGI("PAT Section Length: %d", sectionLength);
333 if (sectionLength >= len) {
334 LOGE("Unexpected PAT Section Length: %d", sectionLength);
335 sectionLength = len;
336 }
337 int indexOfPids = 0;
338 for (int i = startOfProgramNums; i <= sectionLength;
339 i += lengthOfPATValue) {
340 int program_number = ((pat[i] & 0xFF) << 8) | (pat[i + 1] & 0xFF);
341 int pid = ((pat[i + 2] & 0x1F) << 8) | (pat[i + 3] & 0xFF);
342 LOGI("Program Num: 0x%04X(%d) / PID: 0x%04X(%d) ", program_number,
343 program_number, pid, pid);
344
345 if (pmt_pid == 0xFFFF && pid >= 0x0020 && pid <= 0x1FFE) {
346 pmt_pid = pid;
347 }
348 }
349 LOGI("Using PMT PID: 0x%04X(%d)", pmt_pid, pmt_pid);
350 }
351
352 void parsePMT(uint8_t *pmt, int len) {
353 TRACEI();
354 assert(pmt[0] == 0x02); // Program Association section
355 int staticLengthOfPMT = 12;
356 int sectionLength = ((pmt[1] & 0x0F) << 8) | (pmt[2] & 0xFF);
357 LOGI("- PMT Section Length: %d", sectionLength);
358 int programInfoLength = ((pmt[10] & 0x0F) << 8) | (pmt[11] & 0xFF);
359 LOGI("- PMT Program Info Length: %d", programInfoLength);
360
362 while (cursor < sectionLength - 1) {
363 MTSStreamType streamType = static_cast<MTSStreamType>(pmt[cursor] & 0xFF);
364 int elementaryPID =
365 ((pmt[cursor + 1] & 0x1F) << 8) | (pmt[cursor + 2] & 0xFF);
366 LOGI("-- Stream Type: 0x%02X(%d) [%s] for Elementary PID: 0x%04X(%d)",
369
373 }
374
375 int esInfoLength =
376 ((pmt[cursor + 3] & 0x0F) << 8) | (pmt[cursor + 4] & 0xFF);
377 LOGI("-- ES Info Length: 0x%04X(%d)", esInfoLength, esInfoLength);
378 cursor += 5 + esInfoLength;
379 }
380 }
381
382 void parsePES(uint8_t *packet, int pid) {
383 LOGI("parsePES: %d", pid);
384 ++pes_count;
385
386 // calculate payload start
387 bool payloadUnitStartIndicator = false;
389
390 // PES
391 uint8_t *pes = packet + payloadStart;
392 int len = TS_PACKET_SIZE - payloadStart;
393 // PES (AAC) data
394 uint8_t *pesData = nullptr;
395 int pesDataSize = 0;
396
398 assert(len >= 6);
399 // PES header is not alligned correctly
400 if (!isPESStartCodeValid(pes)) {
401 LOGE("PES header not aligned correctly");
402 return;
403 }
404
405 int pesPacketLength =
406 (static_cast<int>(pes[4]) << 8) | static_cast<int>(pes[5]);
407
408 // PES Header size is at least 6 bytes, but can be larger with optional
409 // fields
410 int pesHeaderSize = 6;
411 if ((pes[6] & 0xC0) != 0) { // Check for PTS/DTS flags
412 pesHeaderSize += 3 + ((pes[7] & 0xC0) == 0xC0 ? 5 : 0);
413 pesHeaderSize += pes[8]; // PES header stuffing size
414 }
415 LOGI("- PES Header Size: %d", pesHeaderSize);
418
419 assert(pesHeaderSize < len);
420 assert(pesDataSize > 0);
421
425 }
426
428
429 } else {
430 pesData = pes;
431 pesDataSize = len;
432 }
433
434 // Recalculate the open data
436 if (open_pes_data_size < 0) {
437 return;
438 }
439
441 LOGI("- writing %d bytes (open: %d)", pesDataSize, open_pes_data_size);
442 if (p_print) {
444 assert(result == pesDataSize);
445 }
446 if (p_dec) {
447 size_t result =
449 assert(result == pesDataSize);
450 }
451 }
452
455 if (pes[0] != 0) return false;
456 if (pes[1] != 0) return false;
457 if (pes[2] != 0x1) return false;
458 return true;
459 }
460
462 const char *toStr(MTSStreamType type) {
463 switch (type) {
465 return "AUDIO_MP3";
467 return "AUDIO_MP3_LOW_BITRATE";
469 return "AUDIO_AAC";
471 return "AUDIO_AAC_LATM";
472 default:
473 return "UNKNOWN";
474 }
475 }
476
478 int findSyncWord(const uint8_t *buf, size_t nBytes, uint8_t synch = 0xFF,
479 uint8_t syncl = 0xF0) {
480 for (int i = 0; i < nBytes - 1; i++) {
481 if ((buf[i + 0] & synch) == synch && (buf[i + 1] & syncl) == syncl)
482 return i;
483 }
484 return -1;
485 }
486};
487
491
492} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define TRACEI()
Definition AudioLoggerIDF.h:32
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define TRACEE()
Definition AudioLoggerIDF.h:34
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define MTS_WRITE_BUFFER_SIZE
Definition CodecMTS.h:6
#define TS_PACKET_SIZE
Definition CodecMTS.h:3
#define assert(T)
Definition avr.h:10
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
virtual bool begin(AudioInfo info) override
Definition AudioCodecsBase.h:54
void end() override
Definition AudioCodecsBase.h:59
virtual void setOutput(AudioStream &out_stream)
Defines where the decoded result is written to.
Definition AudioCodecsBase.h:36
Print * p_print
Definition AudioCodecsBase.h:75
Abstract Audio Ouptut class.
Definition AudioOutput.h:25
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:123
MPEG-TS (MTS) decoder. Extracts (demuxes) the indicated audio/video data from a MPEG-TS (MTS) data st...
Definition CodecMTS.h:89
void setOutput(Print &out_stream) override
Defines where the decoded result is written to.
Definition CodecMTS.h:193
void resizeBuffer(int size)
Set a new write buffer size (default is 2000)
Definition CodecMTS.h:152
const char * toStr(MTSStreamType type)
Convert the relevant MTSStreamType to a string.
Definition CodecMTS.h:462
MTSStreamType selected_stream_type
Definition CodecMTS.h:209
void addStreamType(MTSStreamType type)
Defines the stream type that should be extracted.
Definition CodecMTS.h:161
void addPID(uint16_t pid)
Definition CodecMTS.h:217
bool parse()
Parse a single packet and remove the processed data.
Definition CodecMTS.h:249
bool is_active
Definition CodecMTS.h:202
void parsePMT(uint8_t *pmt, int len)
Definition CodecMTS.h:352
uint16_t pmt_pid
Definition CodecMTS.h:207
bool isStreamTypeActive(MTSStreamType type)
Checks if the stream type is active.
Definition CodecMTS.h:167
bool isPESStartCodeValid(uint8_t *pes)
check for PES packet start code prefix
Definition CodecMTS.h:454
void end() override
Stops the processing.
Definition CodecMTS.h:121
void parsePES(uint8_t *packet, int pid)
Definition CodecMTS.h:382
size_t write(const uint8_t *data, size_t len) override
Definition CodecMTS.h:132
const char * mime()
Provides the mime type: "video/MP2T";.
Definition CodecMTS.h:130
bool is_adts_missing
Definition CodecMTS.h:212
void parsePAT(uint8_t *pat, int len)
Definition CodecMTS.h:326
SingleBuffer< uint8_t > buffer
Definition CodecMTS.h:203
int open_pes_data_size
Definition CodecMTS.h:210
size_t pes_count
Definition CodecMTS.h:213
int findSyncWord(const uint8_t *buf, size_t nBytes, uint8_t synch=0xFF, uint8_t syncl=0xF0)
Finds the mp3/aac sync word.
Definition CodecMTS.h:478
void setOutput(AudioStream &out_stream) override
Defines where the decoded result is written to.
Definition CodecMTS.h:175
bool begin() override
Start the prcessor.
Definition CodecMTS.h:96
int getPayloadStart(uint8_t *packet, bool isPES, bool &payloadUnitStartIndicator)
Definition CodecMTS.h:297
int syncPos()
Find the position of the next sync byte: Usually on position 0.
Definition CodecMTS.h:237
Vector< int > pids
Definition CodecMTS.h:205
Vector< MTSStreamType > stream_types
Definition CodecMTS.h:204
void demux()
demux the available data
Definition CodecMTS.h:227
void parsePacket(uint8_t *packet, int pid)
Detailed processing for parsing a single packet.
Definition CodecMTS.h:274
MTSDecoder(AudioDecoder &dec)
Provide the AAC decoder (or MP3 Decoder) to receive the extracted content.
Definition CodecMTS.h:94
void setOutput(AudioOutput &out_stream) override
Defines where the decoded result is written to.
Definition CodecMTS.h:184
MTSDecoder()=default
Default constructor.
int frame_length
Definition CodecMTS.h:211
void clearStreamTypes()
Clears the stream type filter.
Definition CodecMTS.h:155
AudioDecoder * p_dec
Definition CodecMTS.h:206
Definition NoArduino.h:62
A simple Buffer implementation which just uses a (dynamically sized) array.
Definition Buffers.h:172
int available() override
provides the number of entries that are available to read
Definition Buffers.h:233
int availableForWrite() override
provides the number of entries that are available to write
Definition Buffers.h:238
bool resize(int size)
Resizes the buffer if supported: returns false if not supported.
Definition Buffers.h:305
int writeArray(const T data[], int len) override
Fills the buffer data.
Definition Buffers.h:201
T * data()
Provides address of actual data.
Definition Buffers.h:284
int clearArray(int len) override
consumes len bytes and moves current data to the beginning
Definition Buffers.h:252
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
bool contains(T obj)
Definition Vector.h:327
bool empty()
Definition Vector.h:180
void push_back(T &&value)
Definition Vector.h:182
void clear()
Definition Vector.h:176
int size()
Definition Vector.h:178
MTSStreamType
PMT Program Element Stream Types.
Definition CodecMTS.h:20
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:512