@@ -64,6 +64,13 @@ enum class MTSStreamType {
64
64
ATSC_USER_PRIV = 0xEB ,
65
65
};
66
66
67
+ // enum class AACProfile : uint8_t {
68
+ // MAIN = 0, // AAC Main (High complexity, rarely used)
69
+ // LC = 1, // AAC Low Complexity (Most common)
70
+ // SSR = 2, // AAC Scalable Sample Rate (Rare)
71
+ // LTP = 3 // AAC Long Term Prediction (Not widely supported)
72
+ // };
73
+
67
74
/* *
68
75
* @brief MPEG-TS (MTS) decoder. Extracts (demuxes) the AAC audio data from a
69
76
*MPEG-TS (MTS) data stream. You can define the relevant stream types via the
@@ -159,6 +166,9 @@ class MTSDecoder : public AudioDecoder {
159
166
Vector<int > pids{0 };
160
167
AudioDecoder *p_dec = nullptr ;
161
168
uint16_t pmt_pid = 0xFFFF ;
169
+ // AACProfile aac_profile = AACProfile::LC;
170
+ MTSStreamType selected_stream_type;
171
+ int open_pes_data_size = 0 ;
162
172
163
173
// / Add the PID for which we want to extract the audio data from the PES
164
174
// / packets
@@ -212,9 +222,13 @@ class MTSDecoder : public AudioDecoder {
212
222
int pid = ((packet[1 ] & 0x1F ) << 8 ) | (packet[2 ] & 0xFF );
213
223
LOGI (" PID: 0x%04X(%d)" , pid, pid);
214
224
int payloadUnitStartIndicator = (packet[1 ] & 0x40 ) >> 6 ;
215
- LOGI (" Payload Unit Start Indicator: %d" , payloadUnitStartIndicator);
225
+ LOGI (" Payload Unit Start Indicator (PUSI) : %d" , payloadUnitStartIndicator);
216
226
int adaptionFieldControl = (packet[3 ] & 0x30 ) == 0x20 ;
217
- LOGI (" Adaption Field Control: %d" , adaptionFieldControl);
227
+ int adaptationSize = 0 ;
228
+ if (adaptionFieldControl) {
229
+ adaptationSize = packet[4 ];
230
+ }
231
+ LOGI (" Adaption Field Control: 0x%x / size: %d" , adaptionFieldControl, adaptationSize);
218
232
219
233
bool has_payload = true ;
220
234
if ((packet[3 ] & 0x10 ) >> 4 == 0 ) {
@@ -247,7 +261,7 @@ class MTSDecoder : public AudioDecoder {
247
261
parsePMT (&packet[payloadStart], len);
248
262
} else if (pids.contains (pid)) {
249
263
parsePES (&packet[payloadStart], payloadUnitStartIndicator ? true : false ,
250
- len);
264
+ len, adaptationSize );
251
265
} else {
252
266
LOGE (" -> Packet ignored for %d" , pid);
253
267
}
@@ -279,62 +293,161 @@ class MTSDecoder : public AudioDecoder {
279
293
LOGI (" Using PMT PID: 0x%04X(%d)" , pmt_pid, pmt_pid);
280
294
}
281
295
282
- void parsePMT (uint8_t *pat , int len) {
296
+ void parsePMT (uint8_t *pmt , int len) {
283
297
TRACEI ();
284
- assert (pat [0 ] == 0x02 ); // Program Association section
298
+ assert (pmt [0 ] == 0x02 ); // Program Association section
285
299
int staticLengthOfPMT = 12 ;
286
- int sectionLength = ((pat [1 ] & 0x0F ) << 8 ) | (pat [2 ] & 0xFF );
287
- LOGI (" PMT Section Length: %d" , sectionLength);
288
- int programInfoLength = ((pat [10 ] & 0x0F ) << 8 ) | (pat [11 ] & 0xFF );
289
- LOGI (" PMT Program Info Length: %d" , programInfoLength);
300
+ int sectionLength = ((pmt [1 ] & 0x0F ) << 8 ) | (pmt [2 ] & 0xFF );
301
+ LOGI (" - PMT Section Length: %d" , sectionLength);
302
+ int programInfoLength = ((pmt [10 ] & 0x0F ) << 8 ) | (pmt [11 ] & 0xFF );
303
+ LOGI (" - PMT Program Info Length: %d" , programInfoLength);
290
304
291
305
int cursor = staticLengthOfPMT + programInfoLength;
292
306
while (cursor < sectionLength - 1 ) {
293
- int streamType = pat [cursor] & 0xFF ;
307
+ MTSStreamType streamType = static_cast <MTSStreamType>(pmt [cursor] & 0xFF ) ;
294
308
int elementaryPID =
295
- ((pat[cursor + 1 ] & 0x1F ) << 8 ) | (pat[cursor + 2 ] & 0xFF );
296
- LOGI (" Stream Type: 0x%02X(%d) Elementary PID: 0x%04X(%d)" , streamType,
297
- streamType, elementaryPID, elementaryPID);
309
+ ((pmt[cursor + 1 ] & 0x1F ) << 8 ) | (pmt[cursor + 2 ] & 0xFF );
310
+ LOGI (" -- Stream Type: 0x%02X(%d) [%s] for Elementary PID: 0x%04X(%d)" ,
311
+ streamType, streamType, toStr (streamType), elementaryPID,
312
+ elementaryPID);
298
313
299
- if (isStreamTypeActive ((MTSStreamType)streamType)) {
314
+ if (isStreamTypeActive (streamType)) {
315
+ selected_stream_type = streamType;
300
316
addPID (elementaryPID);
301
317
}
302
318
303
319
int esInfoLength =
304
- ((pat [cursor + 3 ] & 0x0F ) << 8 ) | (pat [cursor + 4 ] & 0xFF );
305
- LOGI (" ES Info Length: 0x%04X(%d)" , esInfoLength, esInfoLength);
320
+ ((pmt [cursor + 3 ] & 0x0F ) << 8 ) | (pmt [cursor + 4 ] & 0xFF );
321
+ LOGI (" -- ES Info Length: 0x%04X(%d)" , esInfoLength, esInfoLength);
306
322
cursor += 5 + esInfoLength;
307
323
}
308
324
}
309
325
310
- void parsePES (uint8_t *pat, const bool isNewPayload, int len) {
326
+ void parsePES (uint8_t *pes, const bool isNewPayload, int len,
327
+ int adaptationSize) {
311
328
TRACEI ();
329
+ uint8_t *data = nullptr ;
312
330
int dataSize = 0 ;
331
+
313
332
if (isNewPayload) {
314
- uint8_t streamID = pat[3 ] & 0xFF ;
315
- LOGI (" Stream ID:%02X " , streamID);
316
- const uint8_t posOfPacketLengthLatterHalf = 5 ;
317
- int pesRemainingPacketLength = ((pat[4 ] & 0xFF ) << 8 ) | (pat[5 ] & 0xFF );
318
- LOGI (" PES Packet length: %d" , pesRemainingPacketLength);
319
- // pesDataLength = pesRemainingPacketLength;
320
- const uint8_t posOfHeaderLength = 8 ;
321
- uint8_t pesRemainingHeaderLength = pat[posOfHeaderLength] & 0xFF ;
322
- LOGI (" PES Header length: %d" , pesRemainingHeaderLength);
323
- int startOfData = pesRemainingHeaderLength;
324
- dataSize = len - startOfData;
325
- LOGI (" PES Data size: %d" , dataSize);
326
- if (dataSize < 0 ) dataSize = 0 ;
327
- if (p_print) p_print->write (&pat[startOfData], dataSize);
328
- if (p_dec) p_dec->write (&pat[startOfData], dataSize);
329
- // pesDataLength -= len - (posOfPacketLengthLatterHalf + 1);
333
+ // check for PES packet start code prefix
334
+ assert (pes[0 ] == 0 );
335
+ assert (pes[1 ] == 0 );
336
+ assert (pes[3 ] == 0x1 );
337
+ assert (len >= 6 );
338
+
339
+ int pesPacketLength = (static_cast <int >(pes[4 ]) << 8 ) | static_cast <int >(pes[5 ]);
340
+
341
+ // PES Header size is at least 6 bytes, but can be larger with optional
342
+ // fields
343
+ int pesHeaderSize = 6 ;
344
+ if ((pes[6 ] & 0xC0 ) != 0 ) { // Check for PTS/DTS flags
345
+ pesHeaderSize += 3 + ((pes[7 ] & 0xC0 ) == 0xC0 ? 5 : 0 );
346
+ pesHeaderSize += pes[8 ]; // PES header stuffing size
347
+ }
348
+ LOGI (" - PES Header Size: %d" , pesHeaderSize);
349
+
350
+ assert (pesHeaderSize < len);
351
+
352
+ data = pes + pesHeaderSize;
353
+ dataSize = len - pesHeaderSize;
354
+ assert (dataSize > 0 );
355
+
356
+ open_pes_data_size = pesPacketLength;
357
+
330
358
} else {
331
- dataSize = len;
332
- LOGI (" PES Data size: %d" , dataSize);
333
- if (p_print) p_print->write (pat, dataSize);
334
- if (p_dec) p_dec->write (pat, dataSize);
335
- // pesDataLength -= dataSize;
359
+ data = pes + adaptationSize;
360
+ dataSize = len - adaptationSize;
336
361
}
362
+ open_pes_data_size -= dataSize;
363
+
364
+ LOGI (" - writing %d bytes (open: %d)" , dataSize, open_pes_data_size);
365
+
366
+ if (p_print) p_print->write (data, dataSize);
367
+ if (p_dec) p_dec->write (data, dataSize);
337
368
}
369
+
370
+ const char *toStr (MTSStreamType type) {
371
+ switch (type) {
372
+ case MTSStreamType::AUDIO_MP3:
373
+ return " AUDIO_MP3" ;
374
+ case MTSStreamType::AUDIO_MP3_LOW_BITRATE:
375
+ return " AUDIO_MP3_LOW_BITRATE" ;
376
+ case MTSStreamType::AUDIO_AAC:
377
+ return " AUDIO_AAC" ;
378
+ case MTSStreamType::AUDIO_AAC_LATM:
379
+ return " AUDIO_AAC_LATM" ;
380
+ default :
381
+ return " UNKNOWN" ;
382
+ }
383
+ }
384
+
385
+ // // Sampling Frequency Index Lookup
386
+ // uint8_t getSamplingFrequencyIndex(uint32_t sample_rate) {
387
+ // switch (sample_rate) {
388
+ // case 96000:
389
+ // return 0;
390
+ // case 88200:
391
+ // return 1;
392
+ // case 64000:
393
+ // return 2;
394
+ // case 48000:
395
+ // return 3;
396
+ // case 44100:
397
+ // return 4;
398
+ // case 32000:
399
+ // return 5;
400
+ // case 24000:
401
+ // return 6;
402
+ // case 22050:
403
+ // return 7;
404
+ // case 16000:
405
+ // return 8;
406
+ // case 12000:
407
+ // return 9;
408
+ // case 11025:
409
+ // return 10;
410
+ // case 8000:
411
+ // return 11;
412
+ // case 7350:
413
+ // return 12;
414
+ // default:
415
+ // return 4; // Default to 44.1kHz
416
+ // }
417
+ // }
418
+
419
+ // // Function to add ADTS header to raw AAC data
420
+ // void writeADTSHeader(size_t frame_length) {
421
+ // LOGI("writeADTSHeader: %d", (int)frame_length);
422
+ // AudioInfo info = audioInfo();
423
+ // uint8_t profile = 1; // AAC-LC (Low Complexity)
424
+ // uint8_t sample_index = getSamplingFrequencyIndex(info.sample_rate);
425
+
426
+ // uint8_t adts_header[7] = {0};
427
+
428
+ // // ADTS Header (7 bytes)
429
+ // adts_header[0] = 0xFF; // Syncword (first byte)
430
+ // adts_header[1] = 0xF1; // Syncword (second byte) + MPEG Version + Layer
431
+ // adts_header[2] =
432
+ // ((profile - 1) << 6) | (sample_index << 2) | (info.channels >> 2);
433
+ // adts_header[3] = ((info.channels & 3) << 6) | ((frame_length >> 11) & 0x03);
434
+ // adts_header[4] = (frame_length >> 3) & 0xFF;
435
+ // adts_header[5] = ((frame_length & 0x07) << 5) | 0x1F;
436
+ // adts_header[6] = 0xFC; // Buffer fullness (0x7FF), 1 AAC frame per ADTS
437
+
438
+ // if (p_print) p_print->write(adts_header, sizeof(adts_header));
439
+ // if (p_dec) p_print->write(adts_header, sizeof(adts_header));
440
+ // }
441
+
442
+ // / Finds the mp3/aac sync word
443
+ // int findSyncWord(const uint8_t *buf, size_t nBytes, uint8_t synch = 0xFF,
444
+ // uint8_t syncl = 0xF0) {
445
+ // for (int i = 0; i < nBytes - 1; i++) {
446
+ // if ((buf[i + 0] & synch) == synch && (buf[i + 1] & syncl) == syncl)
447
+ // return i;
448
+ // }
449
+ // return -1;
450
+ // }
338
451
};
339
452
340
453
using MPEG_TSDecoder = MTSDecoder;
0 commit comments