vdr 2.8.3
remux.c
Go to the documentation of this file.
1/*
2 * remux.c: Tools for detecting frames and handling PAT/PMT
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * $Id: remux.c 5.29 2026/08/17 11:15:30 kls Exp $
8 */
9
10#include "remux.h"
11#include "device.h"
12#include "libsi/si.h"
13#include "libsi/section.h"
14#include "libsi/descriptor.h"
15#include "recording.h"
16#include "shutdown.h"
17#include "tools.h"
18
19// Set these to 'true' for debug output:
20static bool DebugPatPmt = false;
21static bool DebugFrames = false;
22
23#define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
24#define dbgframes(a...) if (DebugFrames) fprintf(stderr, a)
25
26#define MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION 6
27#define WRN_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION (MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION / 2)
28#define WRN_TS_PACKETS_FOR_FRAME_DETECTOR (MIN_TS_PACKETS_FOR_FRAME_DETECTOR / 2)
29
30#define EMPTY_SCANNER (0xFFFFFFFF)
31
32ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
33{
34 if (Count < 7)
35 return phNeedMoreData; // too short
36
37 if ((Data[6] & 0xC0) == 0x80) { // MPEG 2
38 if (Count < 9)
39 return phNeedMoreData; // too short
40
41 PesPayloadOffset = 6 + 3 + Data[8];
42 if (Count < PesPayloadOffset)
43 return phNeedMoreData; // too short
44
45 if (ContinuationHeader)
46 *ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]);
47
48 return phMPEG2; // MPEG 2
49 }
50
51 // check for MPEG 1 ...
53
54 // skip up to 16 stuffing bytes
55 for (int i = 0; i < 16; i++) {
56 if (Data[PesPayloadOffset] != 0xFF)
57 break;
58
59 if (Count <= ++PesPayloadOffset)
60 return phNeedMoreData; // too short
61 }
62
63 // skip STD_buffer_scale/size
64 if ((Data[PesPayloadOffset] & 0xC0) == 0x40) {
66
67 if (Count <= PesPayloadOffset)
68 return phNeedMoreData; // too short
69 }
70
71 if (ContinuationHeader)
72 *ContinuationHeader = false;
73
74 if ((Data[PesPayloadOffset] & 0xF0) == 0x20) {
75 // skip PTS only
77 }
78 else if ((Data[PesPayloadOffset] & 0xF0) == 0x30) {
79 // skip PTS and DTS
80 PesPayloadOffset += 10;
81 }
82 else if (Data[PesPayloadOffset] == 0x0F) {
83 // continuation header
85
86 if (ContinuationHeader)
87 *ContinuationHeader = true;
88 }
89 else
90 return phInvalid; // unknown
91
92 if (Count < PesPayloadOffset)
93 return phNeedMoreData; // too short
94
95 return phMPEG1; // MPEG 1
96}
97
98#define VIDEO_STREAM_S 0xE0
99
100// --- cRemux ----------------------------------------------------------------
101
102void cRemux::SetBrokenLink(uchar *Data, int Length)
103{
104 int PesPayloadOffset = 0;
105 if (AnalyzePesHeader(Data, Length, PesPayloadOffset) >= phMPEG1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
106 for (int i = PesPayloadOffset; i < Length - 7; i++) {
107 if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
108 if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
109 Data[i + 7] |= 0x20;
110 return;
111 }
112 }
113 dsyslog("SetBrokenLink: no GOP header found in video packet");
114 }
115 else
116 dsyslog("SetBrokenLink: no video packet in frame");
117}
118
119// --- Some TS handling tools ------------------------------------------------
120
122{
123 p[1] &= ~TS_PAYLOAD_START;
124 p[3] |= TS_ADAPT_FIELD_EXISTS;
125 p[3] &= ~TS_PAYLOAD_EXISTS;
126 p[4] = TS_SIZE - 5;
127 p[5] = 0x00;
128 memset(p + 6, 0xFF, TS_SIZE - 6);
129}
130
131void TsSetPcr(uchar *p, int64_t Pcr)
132{
133 if (TsHasAdaptationField(p)) {
134 if (p[4] >= 7 && (p[5] & TS_ADAPT_PCR)) {
135 int64_t b = Pcr / PCRFACTOR;
136 int e = Pcr % PCRFACTOR;
137 p[ 6] = b >> 25;
138 p[ 7] = b >> 17;
139 p[ 8] = b >> 9;
140 p[ 9] = b >> 1;
141 p[10] = (b << 7) | (p[10] & 0x7E) | ((e >> 8) & 0x01);
142 p[11] = e;
143 }
144 }
145}
146
147int TsSync(const uchar *Data, int Length, const char *File, const char *Function, int Line)
148{
149 int Skipped = 0;
150 while (Length > 0 && (*Data != TS_SYNC_BYTE || Length > TS_SIZE && Data[TS_SIZE] != TS_SYNC_BYTE)) {
151 Data++;
152 Length--;
153 Skipped++;
154 }
155 if (Skipped && File && Function && Line)
156 esyslog("ERROR: skipped %d bytes to sync on start of TS packet at %s/%s(%d)", Skipped, File, Function, Line);
157 return Skipped;
158}
159
160int64_t TsGetPts(const uchar *p, int l, int Pid)
161{
162 // Find the first packet with a PTS and use it:
163 while (l > 0) {
164 if (Pid < 0 || TsPid(p) == Pid) {
165 const uchar *d = p;
166 if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasPts(d))
167 return PesGetPts(d);
168 }
169 p += TS_SIZE;
170 l -= TS_SIZE;
171 }
172 return -1;
173}
174
175int64_t TsGetDts(const uchar *p, int l)
176{
177 // Find the first packet with a DTS and use it:
178 while (l > 0) {
179 const uchar *d = p;
180 if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d))
181 return PesGetDts(d);
182 p += TS_SIZE;
183 l -= TS_SIZE;
184 }
185 return -1;
186}
187
188void TsSetPts(uchar *p, int l, int64_t Pts)
189{
190 // Find the first packet with a PTS and use it:
191 while (l > 0) {
192 const uchar *d = p;
193 if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasPts(d)) {
194 PesSetPts(const_cast<uchar *>(d), Pts);
195 return;
196 }
197 p += TS_SIZE;
198 l -= TS_SIZE;
199 }
200}
201
202void TsSetDts(uchar *p, int l, int64_t Dts)
203{
204 // Find the first packet with a DTS and use it:
205 while (l > 0) {
206 const uchar *d = p;
207 if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d)) {
208 PesSetDts(const_cast<uchar *>(d), Dts);
209 return;
210 }
211 p += TS_SIZE;
212 l -= TS_SIZE;
213 }
214}
215
216// --- Some PES handling tools -----------------------------------------------
217
218void PesSetPts(uchar *p, int64_t Pts)
219{
220 p[ 9] = ((Pts >> 29) & 0x0E) | (p[9] & 0xF1);
221 p[10] = Pts >> 22;
222 p[11] = ((Pts >> 14) & 0xFE) | 0x01;
223 p[12] = Pts >> 7;
224 p[13] = ((Pts << 1) & 0xFE) | 0x01;
225}
226
227void PesSetDts(uchar *p, int64_t Dts)
228{
229 p[14] = ((Dts >> 29) & 0x0E) | (p[14] & 0xF1);
230 p[15] = Dts >> 22;
231 p[16] = ((Dts >> 14) & 0xFE) | 0x01;
232 p[17] = Dts >> 7;
233 p[18] = ((Dts << 1) & 0xFE) | 0x01;
234}
235
236int64_t PtsDiff(int64_t Pts1, int64_t Pts2)
237{
238 int64_t d = Pts2 - Pts1;
239 if (d > MAX33BIT / 2)
240 return d - (MAX33BIT + 1);
241 if (d < -MAX33BIT / 2)
242 return d + (MAX33BIT + 1);
243 return d;
244}
245
246// --- cTsPayload ------------------------------------------------------------
247
249{
250 data = NULL;
251 length = 0;
252 pid = -1;
253 Reset();
254}
255
256cTsPayload::cTsPayload(uchar *Data, int Length, int Pid)
257{
258 Setup(Data, Length, Pid);
259}
260
262{
263 length = index; // triggers EOF
264 return 0x00;
265}
266
268{
269 index = 0;
270 numPacketsPid = 0;
271 numPacketsOther = 0;
272}
273
274void cTsPayload::Setup(uchar *Data, int Length, int Pid)
275{
276 data = Data;
277 length = Length;
278 pid = Pid >= 0 ? Pid : TsPid(Data);
279 Reset();
280}
281
283{
284 if (!Eof()) {
285 if (index % TS_SIZE == 0) { // encountered the next TS header
286 for (;; index += TS_SIZE) {
287 if (data[index] == TS_SYNC_BYTE && index + TS_SIZE <= length) { // to make sure we are at a TS header start and drop incomplete TS packets at the end
288 uchar *p = data + index;
289 if (TsPid(p) == pid) { // only handle TS packets for the initial PID
291 return SetEof();
292 if (TsError(p))
293 return SetEof(); // don't parse TS packets with errors
294 if (TsHasPayload(p)) {
295 if (index > 0 && TsPayloadStart(p)) // checking index to not skip the very first TS packet
296 return SetEof();
298 break;
299 }
300 }
301 else if (TsPid(p) == PATPID)
302 return SetEof(); // caller must see PAT packets in case of index regeneration
303 else
305 }
306 else
307 return SetEof();
308 }
309 }
310 return data[index++];
311 }
312 return 0x00;
313}
314
316{
317 while (Bytes-- > 0)
318 GetByte();
319 return !Eof();
320}
321
326
328{
329 return index - 1;
330}
331
332void cTsPayload::SetByte(uchar Byte, int Index)
333{
334 if (Index >= 0 && Index < length)
335 data[Index] = Byte;
336}
337
338bool cTsPayload::Find(uint32_t Code)
339{
340 int OldIndex = index;
341 int OldNumPacketsPid = numPacketsPid;
342 int OldNumPacketsOther = numPacketsOther;
343 uint32_t Scanner = EMPTY_SCANNER;
344 while (!Eof()) {
345 Scanner = (Scanner << 8) | GetByte();
346 if (Scanner == Code)
347 return true;
348 }
349 index = OldIndex;
350 numPacketsPid = OldNumPacketsPid;
351 numPacketsOther = OldNumPacketsOther;
352 return false;
353}
354
356{
358 dsyslog("WARNING: required (%d+%d) TS packets to determine frame type", numPacketsOther, numPacketsPid);
360 dsyslog("WARNING: could not determine frame type within %d video TS packets", MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION);
362 dsyslog("WARNING: required %d video TS packets to determine frame type", numPacketsPid);
363}
364
365// --- cPatPmtGenerator ------------------------------------------------------
366
368{
369 numPmtPackets = 0;
372 pmtPid = 0;
373 esInfoLength = NULL;
374 SetChannel(Channel);
375}
376
377void cPatPmtGenerator::IncCounter(int &Counter, uchar *TsPacket)
378{
379 TsPacket[3] = (TsPacket[3] & 0xF0) | Counter;
380 if (++Counter > 0x0F)
381 Counter = 0x00;
382}
383
385{
386 if (++Version > 0x1F)
387 Version = 0x00;
388}
389
391{
392 if (esInfoLength) {
393 Length += ((*esInfoLength & 0x0F) << 8) | *(esInfoLength + 1);
394 *esInfoLength = 0xF0 | (Length >> 8);
395 *(esInfoLength + 1) = Length;
396 }
397}
398
399int cPatPmtGenerator::MakeStream(uchar *Target, uchar Type, int Pid)
400{
401 int i = 0;
402 Target[i++] = Type; // stream type
403 Target[i++] = 0xE0 | (Pid >> 8); // dummy (3), pid hi (5)
404 Target[i++] = Pid; // pid lo
405 esInfoLength = &Target[i];
406 Target[i++] = 0xF0; // dummy (4), ES info length hi
407 Target[i++] = 0x00; // ES info length lo
408 return i;
409}
410
412{
413 int i = 0;
414 Target[i++] = Type;
415 Target[i++] = 0x00;
417 return i;
418}
419
420int cPatPmtGenerator::MakeSubtitlingDescriptor(uchar *Target, const char *Language, uchar SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId)
421{
422 int i = 0;
423 Target[i++] = SI::SubtitlingDescriptorTag;
424 Target[i++] = 0x08; // length
425 Target[i++] = *Language++;
426 Target[i++] = *Language++;
427 Target[i++] = *Language++;
428 Target[i++] = SubtitlingType;
429 Target[i++] = CompositionPageId >> 8;
430 Target[i++] = CompositionPageId & 0xFF;
431 Target[i++] = AncillaryPageId >> 8;
432 Target[i++] = AncillaryPageId & 0xFF;
434 return i;
435}
436
437int cPatPmtGenerator::MakeLanguageDescriptor(uchar *Target, const char *Language)
438{
439 int i = 0;
441 int Length = i++;
442 Target[Length] = 0x00; // length
443 for (const char *End = Language + strlen(Language); Language < End; ) {
444 Target[i++] = *Language++;
445 Target[i++] = *Language++;
446 Target[i++] = *Language++;
447 Target[i++] = 0x00; // audio type
448 Target[Length] += 0x04; // length
449 if (*Language == '+')
450 Language++;
451 }
453 return i;
454}
455
456int cPatPmtGenerator::MakeCRC(uchar *Target, const uchar *Data, int Length)
457{
458 int crc = SI::CRC32::crc32((const char *)Data, Length, 0xFFFFFFFF);
459 int i = 0;
460 Target[i++] = crc >> 24;
461 Target[i++] = crc >> 16;
462 Target[i++] = crc >> 8;
463 Target[i++] = crc;
464 return i;
465}
466
467#define P_TSID 0x8008 // pseudo TS ID
468#define P_PMT_PID 0x0084 // pseudo PMT pid
469#define MAXPID 0x2000 // the maximum possible number of pids
470
471void cPatPmtGenerator::GeneratePmtPid(int Vpid, int Ppid, int Tpid, const int *Apids, const int *Dpids, const int *Spids)
472{
473 bool Used[MAXPID] = { false };
474#define SETPID(p) { if ((p) >= 0 && (p) < MAXPID) Used[p] = true; }
475#define SETPIDS(l) { const int *p = l; while (*p) { SETPID(*p); p++; } }
476 SETPID(Vpid);
477 SETPID(Ppid);
478 SETPID(Tpid);
479 SETPIDS(Apids);
480 SETPIDS(Dpids);
481 SETPIDS(Spids);
482 for (pmtPid = P_PMT_PID; Used[pmtPid]; pmtPid++)
483 ;
484}
485
487{
488 memset(pat, 0xFF, sizeof(pat));
489 uchar *p = pat;
490 int i = 0;
491 p[i++] = TS_SYNC_BYTE; // TS indicator
492 p[i++] = TS_PAYLOAD_START | (PATPID >> 8); // flags (3), pid hi (5)
493 p[i++] = PATPID & 0xFF; // pid lo
494 p[i++] = 0x10; // flags (4), continuity counter (4)
495 p[i++] = 0x00; // pointer field (payload unit start indicator is set)
496 int PayloadStart = i;
497 p[i++] = 0x00; // table id
498 p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
499 int SectionLength = i;
500 p[i++] = 0x00; // section length lo (filled in later)
501 p[i++] = P_TSID >> 8; // TS id hi
502 p[i++] = P_TSID & 0xFF; // TS id lo
503 p[i++] = 0xC1 | (patVersion << 1); // dummy (2), version number (5), current/next indicator (1)
504 p[i++] = 0x00; // section number
505 p[i++] = 0x00; // last section number
506 p[i++] = pmtPid >> 8; // program number hi
507 p[i++] = pmtPid & 0xFF; // program number lo
508 p[i++] = 0xE0 | (pmtPid >> 8); // dummy (3), PMT pid hi (5)
509 p[i++] = pmtPid & 0xFF; // PMT pid lo
510 pat[SectionLength] = i - SectionLength - 1 + 4; // -1 = SectionLength storage, +4 = length of CRC
511 MakeCRC(pat + i, pat + PayloadStart, i - PayloadStart);
513}
514
515void cPatPmtGenerator::SetPids(int Vpid, int Vtype, int Ppid, int Tpid, const int *Apids, const int *Atypes, const char Alangs[][MAXLANGCODE2], const int *Dpids, const int *Dtypes, const char Dlangs[][MAXLANGCODE2], const int *Spids, const uchar *Stypes, const char Slangs[][MAXLANGCODE2], const uint16_t *CompositionPageIds, const uint16_t *AncillaryPageIds)
516{
517 GeneratePmtPid(Vpid, Ppid, Tpid, Apids, Dpids, Spids);
518 GeneratePat();
519 // generate the complete PMT section:
521 memset(buf, 0xFF, sizeof(buf));
522 numPmtPackets = 0;
523 uchar *p = buf;
524 int i = 0;
525 p[i++] = 0x02; // table id
526 int SectionLength = i;
527 p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
528 p[i++] = 0x00; // section length lo (filled in later)
529 p[i++] = pmtPid >> 8; // program number hi
530 p[i++] = pmtPid & 0xFF; // program number lo
531 p[i++] = 0xC1 | (pmtVersion << 1); // dummy (2), version number (5), current/next indicator (1)
532 p[i++] = 0x00; // section number
533 p[i++] = 0x00; // last section number
534 p[i++] = 0xE0 | (Ppid >> 8); // dummy (3), PCR pid hi (5)
535 p[i++] = Ppid; // PCR pid lo
536 p[i++] = 0xF0; // dummy (4), program info length hi (4)
537 p[i++] = 0x00; // program info length lo
538
539 if (Vpid)
540 i += MakeStream(buf + i, Vtype, Vpid);
541 for (int n = 0; Apids[n]; n++) {
542 i += MakeStream(buf + i, Atypes[n], Apids[n]);
543 i += MakeLanguageDescriptor(buf + i, Alangs[n]);
544 }
545 for (int n = 0; Dpids[n]; n++) {
546 i += MakeStream(buf + i, 0x06, Dpids[n]);
547 i += MakeAC3Descriptor(buf + i, Dtypes[n]);
548 i += MakeLanguageDescriptor(buf + i, Dlangs[n]);
549 }
550 for (int n = 0; Spids[n]; n++) {
551 i += MakeStream(buf + i, 0x06, Spids[n]);
552 i += MakeSubtitlingDescriptor(buf + i, Slangs[n], Stypes[n], CompositionPageIds[n], AncillaryPageIds[n]);
553 }
554
555 int sl = i - SectionLength - 2 + 4; // -2 = SectionLength storage, +4 = length of CRC
556 buf[SectionLength] |= (sl >> 8) & 0x0F;
557 buf[SectionLength + 1] = sl;
558 MakeCRC(buf + i, buf, i);
559 // split the PMT section into several TS packets:
560 uchar *q = buf;
561 bool pusi = true;
562 while (i > 0) {
563 uchar *p = pmt[numPmtPackets++];
564 int j = 0;
565 p[j++] = TS_SYNC_BYTE; // TS indicator
566 p[j++] = (pusi ? TS_PAYLOAD_START : 0x00) | (pmtPid >> 8); // flags (3), pid hi (5)
567 p[j++] = pmtPid & 0xFF; // pid lo
568 p[j++] = 0x10; // flags (4), continuity counter (4)
569 if (pusi) {
570 p[j++] = 0x00; // pointer field (payload unit start indicator is set)
571 pusi = false;
572 }
573 int l = TS_SIZE - j;
574 memcpy(p + j, q, l);
575 q += l;
576 i -= l;
577 }
579}
580
581void cPatPmtGenerator::SetVersions(int PatVersion, int PmtVersion)
582{
583 patVersion = PatVersion & 0x1F;
584 pmtVersion = PmtVersion & 0x1F;
585}
586
588{
589 if (Channel)
590 SetPids(Channel->Vpid(), Channel->Vtype(), Channel->Ppid(), Channel->Tpid(), Channel->Apids(), Channel->Atypes(), Channel->Alangs(), Channel->Dpids(), Channel->Dtypes(), Channel->Dlangs(), Channel->Spids(), Channel->Stypes(), Channel->Slangs(), Channel->CompositionPageIds(), Channel->AncillaryPageIds());
591}
592
594{
596 return pat;
597}
598
600{
601 if (Index < numPmtPackets) {
602 IncCounter(pmtCounter, pmt[Index]);
603 return pmt[Index++];
604 }
605 return NULL;
606}
607
609{
610 return numPmtPackets ? (numPmtPackets + 1) * TS_SIZE : 0;
611}
612
614{
615 if (numPmtPackets > 0) {
616 int PatPmtSize = GetPatPmtSize();
617 if (PatPmtSize <= Size) {
619 memcpy(Dest, pat, TS_SIZE);
620 Dest += TS_SIZE;
621 for (int i = 0; i < numPmtPackets; i++)
623 memcpy(Dest, pmt, TS_SIZE * numPmtPackets);
624 return PatPmtSize;
625 }
626 }
627 return 0;
628}
629
630// --- cPatPmtParser ---------------------------------------------------------
631
632cPatPmtParser::cPatPmtParser(bool UpdatePrimaryDevice)
633{
634 updatePrimaryDevice = UpdatePrimaryDevice;
635 Reset();
636}
637
639{
640 completed = false;
641 pmtSize = 0;
642 patVersion = pmtVersion = -1;
643 pmtPids[0] = 0;
644 vpid = vtype = 0;
645 ppid = 0;
646}
647
648void cPatPmtParser::ParsePat(const uchar *Data, int Length)
649{
650 // Unpack the TS packet:
651 int PayloadOffset = TsPayloadOffset(Data);
652 Data += PayloadOffset;
653 Length -= PayloadOffset;
654 // The PAT is always assumed to fit into a single TS packet
655 if ((Length -= Data[0] + 1) <= 0)
656 return;
657 Data += Data[0] + 1; // process pointer_field
658 SI::PAT Pat(Data, false);
659 if (Pat.CheckCRCAndParse()) {
660 dbgpatpmt("PAT: TSid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pat.getTransportStreamId(), Pat.getCurrentNextIndicator(), Pat.getVersionNumber(), Pat.getSectionNumber(), Pat.getLastSectionNumber());
661 if (patVersion == Pat.getVersionNumber())
662 return;
663 int NumPmtPids = 0;
665 for (SI::Loop::Iterator it; Pat.associationLoop.getNext(assoc, it); ) {
666 dbgpatpmt(" isNITPid = %d\n", assoc.isNITPid());
667 if (!assoc.isNITPid()) {
668 if (NumPmtPids <= MAX_PMT_PIDS)
669 pmtPids[NumPmtPids++] = assoc.getPid();
670 dbgpatpmt(" service id = %d, pid = %d\n", assoc.getServiceId(), assoc.getPid());
671 }
672 }
673 pmtPids[NumPmtPids] = 0;
675 }
676 else
677 esyslog("ERROR: can't parse PAT");
678}
679
680void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
681{
682 // Unpack the TS packet:
683 bool PayloadStart = TsPayloadStart(Data);
684 int PayloadOffset = TsPayloadOffset(Data);
685 Data += PayloadOffset;
686 Length -= PayloadOffset;
687 // The PMT may extend over several TS packets, so we need to assemble them
688 if (PayloadStart) {
689 pmtSize = 0;
690 if ((Length -= Data[0] + 1) <= 0)
691 return;
692 Data += Data[0] + 1; // this is the first packet
693 if (SectionLength(Data, Length) > Length) {
694 if (Length <= int(sizeof(pmt))) {
695 memcpy(pmt, Data, Length);
696 pmtSize = Length;
697 }
698 else
699 esyslog("ERROR: PMT packet length too big (%d byte)!", Length);
700 return;
701 }
702 // the packet contains the entire PMT section, so we run into the actual parsing
703 }
704 else if (pmtSize > 0) {
705 // this is a following packet, so we add it to the pmt storage
706 if (Length <= int(sizeof(pmt)) - pmtSize) {
707 memcpy(pmt + pmtSize, Data, Length);
708 pmtSize += Length;
709 }
710 else {
711 esyslog("ERROR: PMT section length too big (%d byte)!", pmtSize + Length);
712 pmtSize = 0;
713 }
715 return; // more packets to come
716 // the PMT section is now complete, so we run into the actual parsing
717 Data = pmt;
718 }
719 else
720 return; // fragment of broken packet - ignore
721 SI::PMT Pmt(Data, false);
722 if (Pmt.CheckCRCAndParse()) {
723 dbgpatpmt("PMT: sid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pmt.getServiceId(), Pmt.getCurrentNextIndicator(), Pmt.getVersionNumber(), Pmt.getSectionNumber(), Pmt.getLastSectionNumber());
724 dbgpatpmt(" pcr = %d\n", Pmt.getPCRPid());
725 if (pmtVersion == Pmt.getVersionNumber())
726 return;
729 int NumApids = 0;
730 int NumDpids = 0;
731 int NumSpids = 0;
732 vpid = vtype = 0;
733 ppid = 0;
734 apids[0] = 0;
735 dpids[0] = 0;
736 spids[0] = 0;
737 atypes[0] = 0;
738 dtypes[0] = 0;
739 SI::PMT::Stream stream;
740 for (SI::Loop::Iterator it; Pmt.streamLoop.getNext(stream, it); ) {
741 dbgpatpmt(" stream type = %02X, pid = %d", stream.getStreamType(), stream.getPid());
742 switch (stream.getStreamType()) {
743 case 0x01: // STREAMTYPE_11172_VIDEO
744 case 0x02: // STREAMTYPE_13818_VIDEO
745 case 0x1B: // H.264
746 case 0x24: // H.265
747 vpid = stream.getPid();
748 vtype = stream.getStreamType();
749 ppid = Pmt.getPCRPid();
750 break;
751 case 0x03: // STREAMTYPE_11172_AUDIO
752 case 0x04: // STREAMTYPE_13818_AUDIO
753 case 0x0F: // ISO/IEC 13818-7 Audio with ADTS transport syntax
754 case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
755 {
756 if (NumApids < MAXAPIDS) {
757 apids[NumApids] = stream.getPid();
758 atypes[NumApids] = stream.getStreamType();
759 *alangs[NumApids] = 0;
761 for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
762 switch (d->getDescriptorTag()) {
766 char *s = alangs[NumApids];
767 int n = 0;
768 for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) {
769 if (*ld->languageCode != '-') { // some use "---" to indicate "none"
770 dbgpatpmt(" '%s'", l.languageCode);
771 if (n > 0)
772 *s++ = '+';
774 s += strlen(s);
775 if (n++ > 1)
776 break;
777 }
778 }
779 }
780 break;
781 default: ;
782 }
783 delete d;
784 }
786 cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, NumApids, apids[NumApids], alangs[NumApids]);
787 NumApids++;
788 apids[NumApids] = 0;
789 }
790 }
791 break;
792 case 0x06: // STREAMTYPE_13818_PES_PRIVATE
793 {
794 int dpid = 0;
795 int dtype = 0;
796 char lang[MAXLANGCODE1] = "";
798 for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
799 switch (d->getDescriptorTag()) {
802 dbgpatpmt(" AC3");
803 dpid = stream.getPid();
804 dtype = d->getDescriptorTag();
805 break;
807 dbgpatpmt(" subtitling");
808 if (NumSpids < MAXSPIDS) {
809 spids[NumSpids] = stream.getPid();
810 *slangs[NumSpids] = 0;
811 subtitlingTypes[NumSpids] = 0;
812 compositionPageIds[NumSpids] = 0;
813 ancillaryPageIds[NumSpids] = 0;
816 char *s = slangs[NumSpids];
817 int n = 0;
818 for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) {
819 if (sub.languageCode[0]) {
820 dbgpatpmt(" '%s'", sub.languageCode);
821 subtitlingTypes[NumSpids] = sub.getSubtitlingType();
823 ancillaryPageIds[NumSpids] = sub.getAncillaryPageId();
824 if (n > 0)
825 *s++ = '+';
827 s += strlen(s);
828 if (n++ > 1)
829 break;
830 }
831 }
833 cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, spids[NumSpids], slangs[NumSpids]);
834 NumSpids++;
835 spids[NumSpids] = 0;
836 }
837 break;
840 dbgpatpmt(" '%s'", ld->languageCode);
842 }
843 break;
844 default: ;
845 }
846 delete d;
847 }
848 if (dpid) {
849 if (NumDpids < MAXDPIDS) {
850 dpids[NumDpids] = dpid;
851 dtypes[NumDpids] = dtype;
852 strn0cpy(dlangs[NumDpids], lang, sizeof(dlangs[NumDpids]));
854 cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, NumDpids, dpid, lang);
855 NumDpids++;
856 dpids[NumDpids] = 0;
857 }
858 }
859 }
860 break;
861 case 0x81: // STREAMTYPE_USER_PRIVATE - AC3 audio for ATSC and BD
862 case 0x82: // STREAMTYPE_USER_PRIVATE - DTS audio for BD
863 case 0x87: // eac3
864 {
865 dbgpatpmt(" %s",
866 stream.getStreamType() == 0x81 ? "AC3" :
867 stream.getStreamType() == 0x87 ? "AC3" :
868 stream.getStreamType() == 0x82 ? "DTS" : "");
869 char lang[MAXLANGCODE1] = { 0 };
871 for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
872 switch (d->getDescriptorTag()) {
875 dbgpatpmt(" '%s'", ld->languageCode);
877 }
878 break;
879 default: ;
880 }
881 delete d;
882 }
883 if (NumDpids < MAXDPIDS) {
884 dpids[NumDpids] = stream.getPid();
885 dtypes[NumDpids] = SI::AC3DescriptorTag;
886 strn0cpy(dlangs[NumDpids], lang, sizeof(dlangs[NumDpids]));
888 cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, NumDpids, stream.getPid(), lang);
889 NumDpids++;
890 dpids[NumDpids] = 0;
891 }
892 }
893 break;
894 case 0x90: // PGS subtitles for BD
895 {
896 dbgpatpmt(" subtitling");
897 char lang[MAXLANGCODE1] = { 0 };
899 for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
900 switch (d->getDescriptorTag()) {
903 dbgpatpmt(" '%s'", ld->languageCode);
905 if (NumSpids < MAXSPIDS) {
906 spids[NumSpids] = stream.getPid();
907 *slangs[NumSpids] = 0;
908 subtitlingTypes[NumSpids] = 0;
909 compositionPageIds[NumSpids] = 0;
910 ancillaryPageIds[NumSpids] = 0;
912 cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, stream.getPid(), lang);
913 NumSpids++;
914 spids[NumSpids] = 0;
915 }
916 }
917 break;
918 default: ;
919 }
920 delete d;
921 }
922 }
923 break;
924 default: ;
925 }
926 dbgpatpmt("\n");
930 }
931 }
933 completed = true;
934 }
935 else
936 esyslog("ERROR: can't parse PMT");
937 pmtSize = 0;
938}
939
940bool cPatPmtParser::ParsePatPmt(const uchar *Data, int Length)
941{
942 while (Length >= TS_SIZE) {
943 if (*Data != TS_SYNC_BYTE)
944 break; // just for safety
945 int Pid = TsPid(Data);
946 if (Pid == PATPID)
947 ParsePat(Data, TS_SIZE);
948 else if (IsPmtPid(Pid)) {
949 ParsePmt(Data, TS_SIZE);
950 if (patVersion >= 0 && pmtVersion >= 0)
951 return true;
952 }
953 Data += TS_SIZE;
954 Length -= TS_SIZE;
955 }
956 return false;
957}
958
959bool cPatPmtParser::GetVersions(int &PatVersion, int &PmtVersion) const
960{
961 PatVersion = patVersion;
962 PmtVersion = pmtVersion;
963 return patVersion >= 0 && pmtVersion >= 0;
964}
965
966// --- cEitGenerator ---------------------------------------------------------
967
969{
970 counter = 0;
971 version = 0;
972 if (Sid)
973 Generate(Sid);
974}
975
976uint16_t cEitGenerator::YMDtoMJD(int Y, int M, int D)
977{
978 int L = (M < 3) ? 1 : 0;
979 return 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
980}
981
983{
985 *p++ = 0x04; // descriptor length
986 *p++ = '9'; // country code "902" ("All countries") -> EN 300 468 / 6.2.28; www.dvbservices.com/country_codes/index.php
987 *p++ = '0';
988 *p++ = '2';
989 *p++ = ParentalRating;
990 return p;
991}
992
994{
995 uchar *PayloadStart;
996 uchar *SectionStart;
997 uchar *DescriptorsStart;
998 memset(eit, 0xFF, sizeof(eit));
999 struct tm tm_r;
1000 time_t t = time(NULL) - 3600; // let's have the event start one hour in the past
1001 tm *tm = localtime_r(&t, &tm_r);
1002 uint16_t MJD = YMDtoMJD(tm->tm_year, tm->tm_mon + 1, tm->tm_mday);
1003 uchar *p = eit;
1004 // TS header:
1005 *p++ = TS_SYNC_BYTE;
1006 *p++ = TS_PAYLOAD_START;
1007 *p++ = EITPID;
1008 *p++ = 0x10 | (counter++ & 0x0F); // continuity counter
1009 *p++ = 0x00; // pointer field (payload unit start indicator is set)
1010 // payload:
1011 PayloadStart = p;
1012 *p++ = 0x4E; // TID present/following event on this transponder
1013 *p++ = 0xF0;
1014 *p++ = 0x00; // section length
1015 SectionStart = p;
1016 *p++ = Sid >> 8;
1017 *p++ = Sid & 0xFF;
1018 *p++ = 0xC1 | (version << 1);
1019 *p++ = 0x00; // section number
1020 *p++ = 0x00; // last section number
1021 *p++ = 0x00; // transport stream id
1022 *p++ = 0x00; // ...
1023 *p++ = 0x00; // original network id
1024 *p++ = 0x00; // ...
1025 *p++ = 0x00; // segment last section number
1026 *p++ = 0x4E; // last table id
1027 *p++ = 0x00; // event id
1028 *p++ = 0x01; // ...
1029 *p++ = MJD >> 8; // start time
1030 *p++ = MJD & 0xFF; // ...
1031 *p++ = tm->tm_hour; // ...
1032 *p++ = tm->tm_min; // ...
1033 *p++ = tm->tm_sec; // ...
1034 *p++ = 0x24; // duration (one day, should cover everything)
1035 *p++ = 0x00; // ...
1036 *p++ = 0x00; // ...
1037 *p++ = 0x90; // running status, free/CA mode
1038 *p++ = 0x00; // descriptors loop length
1039 DescriptorsStart = p;
1041 // fill in lengths:
1042 *(SectionStart - 1) = p - SectionStart + 4; // +4 = length of CRC
1043 *(DescriptorsStart - 1) = p - DescriptorsStart;
1044 // checksum
1045 int crc = SI::CRC32::crc32((char *)PayloadStart, p - PayloadStart, 0xFFFFFFFF);
1046 *p++ = crc >> 24;
1047 *p++ = crc >> 16;
1048 *p++ = crc >> 8;
1049 *p++ = crc;
1050 return eit;
1051}
1052
1053// --- cTsToPes --------------------------------------------------------------
1054
1056{
1057 data = NULL;
1058 size = 0;
1059 Reset();
1060}
1061
1063{
1064 free(data);
1065}
1066
1067void cTsToPes::PutTs(const uchar *Data, int Length)
1068{
1069 if (TsError(Data)) {
1070 Reset();
1071 return; // ignore packets with TEI set, and drop any PES data collected so far
1072 }
1073 if (TsPayloadStart(Data))
1074 Reset();
1075 else if (!size)
1076 return; // skip everything before the first payload start
1077 Length = TsGetPayload(&Data);
1078 if (length + Length > size) {
1079 int NewSize = max(KILOBYTE(2), length + Length);
1080 if (uchar *NewData = (uchar *)realloc(data, NewSize)) {
1081 data = NewData;
1082 size = NewSize;
1083 }
1084 else {
1085 esyslog("ERROR: out of memory");
1086 Reset();
1087 return;
1088 }
1089 }
1090 memcpy(data + length, Data, Length);
1091 length += Length;
1092}
1093
1094#define MAXPESLENGTH 0xFFF0
1095
1096const uchar *cTsToPes::GetPes(int &Length)
1097{
1098 if (repeatLast) {
1099 repeatLast = false;
1100 Length = lastLength;
1101 return lastData;
1102 }
1103 if (offset < length && PesLongEnough(length)) {
1104 if (!PesHasLength(data)) // this is a video PES packet with undefined length
1105 offset = 6; // trigger setting PES length for initial slice
1106 if (offset) {
1107 uchar *p = data + offset - 6;
1108 if (p != data) {
1109 p -= 3;
1110 if (p < data) {
1111 Reset();
1112 return NULL;
1113 }
1114 memmove(p, data, 4);
1115 }
1116 int l = min(length - offset, MAXPESLENGTH);
1117 offset += l;
1118 if (p != data) {
1119 l += 3;
1120 p[6] = 0x80;
1121 p[7] = 0x00;
1122 p[8] = 0x00;
1123 }
1124 p[4] = l / 256;
1125 p[5] = l & 0xFF;
1126 Length = l + 6;
1127 lastLength = Length;
1128 lastData = p;
1129 return p;
1130 }
1131 else {
1132 Length = PesLength(data);
1133 if (Length <= length) {
1134 offset = Length; // to make sure we break out in case of garbage data
1135 lastLength = Length;
1136 lastData = data;
1137 return data;
1138 }
1139 }
1140 }
1141 return NULL;
1142}
1143
1145{
1146 repeatLast = true;
1147}
1148
1150{
1151 length = offset = 0;
1152 lastData = NULL;
1153 lastLength = 0;
1154 repeatLast = false;
1155}
1156
1157// --- Some helper functions for debugging -----------------------------------
1158
1159void BlockDump(const char *Name, const u_char *Data, int Length)
1160{
1161 printf("--- %s\n", Name);
1162 for (int i = 0; i < Length; i++) {
1163 if (i && (i % 16) == 0)
1164 printf("\n");
1165 printf(" %02X", Data[i]);
1166 }
1167 printf("\n");
1168}
1169
1170void TsDump(const char *Name, const u_char *Data, int Length)
1171{
1172 printf("%s: %04X", Name, Length);
1173 int n = min(Length, 20);
1174 for (int i = 0; i < n; i++)
1175 printf(" %02X", Data[i]);
1176 if (n < Length) {
1177 printf(" ...");
1178 n = max(n, Length - 10);
1179 for (n = max(n, Length - 10); n < Length; n++)
1180 printf(" %02X", Data[n]);
1181 }
1182 printf("\n");
1183}
1184
1185void PesDump(const char *Name, const u_char *Data, int Length)
1186{
1187 TsDump(Name, Data, Length);
1188}
1189
1190// --- cFrameParser ----------------------------------------------------------
1191
1193protected:
1194 bool debug;
1198 uint16_t frameWidth;
1199 uint16_t frameHeight;
1203public:
1204 cFrameParser(void);
1205 virtual ~cFrameParser() {};
1206 virtual int Parse(const uchar *Data, int Length, int Pid) = 0;
1213 void SetDebug(bool Debug) { debug = Debug; }
1214 bool NewFrame(void) { return newFrame; }
1215 bool IndependentFrame(void) { return independentFrame; }
1217 uint16_t FrameWidth(void) { return frameWidth; }
1218 uint16_t FrameHeight(void) { return frameHeight; }
1219 double FramesPerSecond(void) { return framesPerSecond; }
1220 eScanType ScanType(void) { return scanType; }
1222 };
1223
1225{
1226 debug = true;
1227 newFrame = false;
1228 independentFrame = false;
1230 frameWidth = 0;
1231 frameHeight = 0;
1232 framesPerSecond = 0.0;
1235}
1236
1237// --- cAudioParser ----------------------------------------------------------
1238
1240public:
1241 cAudioParser(void);
1242 virtual int Parse(const uchar *Data, int Length, int Pid) override;
1243 };
1244
1248
1249int cAudioParser::Parse(const uchar *Data, int Length, int Pid)
1250{
1251 if (TsPayloadStart(Data)) {
1252 newFrame = independentFrame = true;
1253 if (debug)
1254 dbgframes("/");
1255 }
1256 else
1257 newFrame = independentFrame = false;
1258 return TS_SIZE;
1259}
1260
1261// --- cMpeg2Parser ----------------------------------------------------------
1262
1264private:
1265 uint32_t scanner;
1269 const double frame_rate_table[9] = {
1270 0, // 0 forbidden
1271 24000./1001., // 1 23.976...
1272 24., // 2 24
1273 25., // 3 25
1274 30000./1001., // 4 29.97...
1275 30., // 5 30
1276 50., // 6 50
1277 60000./1001., // 7 59.94...
1278 60. // 8 60
1279 };
1280public:
1281 cMpeg2Parser(void);
1282 virtual int Parse(const uchar *Data, int Length, int Pid) override;
1283 };
1284
1286{
1288 seenIndependentFrame = false;
1289 lastIFrameTemporalReference = -1; // invalid
1290 seenScanType = false;
1291}
1292
1293int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
1294{
1295 newFrame = independentFrame = false;
1296 bool SeenPayloadStart = false;
1297 cTsPayload tsPayload(const_cast<uchar *>(Data), Length, Pid);
1298 if (TsPayloadStart(Data)) {
1299 SeenPayloadStart = true;
1300 tsPayload.SkipPesHeader();
1303 dbgframes("/");
1304 }
1305 uint32_t OldScanner = scanner; // need to remember it in case of multiple frames per payload
1306 for (;;) {
1307 if (!SeenPayloadStart && tsPayload.AtTsStart())
1308 OldScanner = scanner;
1309 scanner = (scanner << 8) | tsPayload.GetByte();
1310 if (scanner == 0x00000100) { // Picture Start Code
1311 if (!SeenPayloadStart && tsPayload.GetLastIndex() > TS_SIZE) {
1312 scanner = OldScanner;
1313 return tsPayload.Used() - TS_SIZE;
1314 }
1315 uchar b1 = tsPayload.GetByte();
1316 uchar b2 = tsPayload.GetByte();
1317 int TemporalReference = (b1 << 2 ) + ((b2 & 0xC0) >> 6);
1318 uchar FrameType = (b2 >> 3) & 0x07;
1319 if (tsPayload.Find(0x000001B5)) { // Extension start code
1320 if (((tsPayload.GetByte() & 0xF0) >> 4) == 0x08) { // Picture coding extension
1321 tsPayload.GetByte();
1322 uchar PictureStructure = tsPayload.GetByte() & 0x03;
1323 if (PictureStructure == 0x02) // bottom field
1324 break;
1325 }
1326 }
1327 else
1328 break;
1329 newFrame = true;
1330 independentFrame = FrameType == 1; // I-Frame
1331 if (independentFrame) {
1334 lastIFrameTemporalReference = TemporalReference;
1335 }
1336 if (debug) {
1339 static const char FrameTypes[] = "?IPBD???";
1340 dbgframes("%c", FrameTypes[FrameType]);
1341 }
1342 }
1343 tsPayload.Statistics();
1344 break;
1345 }
1346 else if (frameWidth == 0 && scanner == 0x000001B3) { // Sequence header code
1347 frameWidth = tsPayload.GetByte() << 4;
1348 uchar b = tsPayload.GetByte(); // ignoring two MSB of width and height in sequence extension
1349 frameWidth |= b >> 4; // as 12 Bit = max 4095 should be sufficient for all available MPEG2 streams
1350 frameHeight = (b & 0x0F) << 8 | tsPayload.GetByte();
1351 b = tsPayload.GetByte(); // hi: aspect ratio info, lo: frame rate code
1352 switch (b >> 4) {
1353 case 1: aspectRatio = ar_1_1; break;
1354 case 2: aspectRatio = ar_4_3; break;
1355 case 3: aspectRatio = ar_16_9; break;
1356 case 4: aspectRatio = ar_2_21_1; break;
1357 default: aspectRatio = arUnknown;
1358 }
1359 uchar frame_rate_value = b & 0x0F;
1360 if (frame_rate_value > 0 && frame_rate_value <= 8)
1361 framesPerSecond = frame_rate_table[frame_rate_value];
1362 }
1363 else if (!seenScanType && scanner == 0x000001B5) { // Extension start code
1364 if ((tsPayload.GetByte() & 0xF0) == 0x10) { // Sequence Extension
1365 scanType = (tsPayload.GetByte() & 0x40) ? stProgressive : stInterlaced;
1366 seenScanType = true;
1367 if (debug) {
1369 dsyslog("%s", *s);
1370 dbgframes("\n%s", *s);
1371 }
1372 }
1373 }
1374 if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
1375 || tsPayload.Eof()) // or if we're out of data
1376 break;
1377 }
1378 return tsPayload.Used();
1379}
1380
1381// --- cH264Parser -----------------------------------------------------------
1382
1384private:
1391 uchar byte; // holds the current byte value in case of bitwise access
1392 int bit; // the bit index into the current byte (-1 if we're not in bit reading mode)
1393 int zeroBytes; // the number of consecutive zero bytes (to detect 0x000003)
1394 // Identifiers written in '_' notation as in "ITU-T H.264":
1398protected:
1400 uint32_t scanner;
1403 uchar GetByte(bool Raw = false);
1407 uchar GetBit(void);
1408 uint32_t GetBits(int Bits);
1409 uint32_t GetGolombUe(void);
1410 int32_t GetGolombSe(void);
1411 void ParseAccessUnitDelimiter(void);
1412 void ParseSequenceParameterSet(void);
1413 void ParseSliceHeader(void);
1414public:
1415 cH264Parser(void);
1419 virtual int Parse(const uchar *Data, int Length, int Pid) override;
1420 };
1421
1423{
1424 byte = 0;
1425 bit = -1;
1426 zeroBytes = 0;
1430 frame_mbs_only_flag = false;
1431 gotAccessUnitDelimiter = false;
1433}
1434
1436{
1437 uchar b = tsPayload.GetByte();
1438 if (!Raw) {
1439 // If we encounter the byte sequence 0x000003, we need to skip the 0x03:
1440 if (b == 0x00)
1441 zeroBytes++;
1442 else {
1443 if (b == 0x03 && zeroBytes >= 2)
1444 b = tsPayload.GetByte();
1445 zeroBytes = b ? 0 : 1;
1446 }
1447 }
1448 else
1449 zeroBytes = 0;
1450 bit = -1;
1451 return b;
1452}
1453
1455{
1456 if (bit < 0) {
1457 byte = GetByte();
1458 bit = 7;
1459 }
1460 return (byte & (1 << bit--)) ? 1 : 0;
1461}
1462
1463uint32_t cH264Parser::GetBits(int Bits)
1464{
1465 uint32_t b = 0;
1466 while (Bits--)
1467 b |= GetBit() << Bits;
1468 return b;
1469}
1470
1472{
1473 int z = -1;
1474 for (int b = 0; !b && z < 32; z++) // limiting z to no get stuck if GetBit() always returns 0
1475 b = GetBit();
1476 return (1 << z) - 1 + GetBits(z);
1477}
1478
1480{
1481 uint32_t v = GetGolombUe();
1482 if (v) {
1483 if ((v & 0x01) != 0)
1484 return (v + 1) / 2; // fails for v == 0xFFFFFFFF, but that will probably never happen
1485 else
1486 return -int32_t(v / 2);
1487 }
1488 return v;
1489}
1490
1491int cH264Parser::Parse(const uchar *Data, int Length, int Pid)
1492{
1493 newFrame = independentFrame = false;
1494 tsPayload.Setup(const_cast<uchar *>(Data), Length, Pid);
1495 if (TsPayloadStart(Data)) {
1499 dbgframes("/");
1500 }
1501 }
1502 for (;;) {
1503 scanner = (scanner << 8) | GetByte(true);
1504 if ((scanner & 0xFFFFFF00) == 0x00000100) { // NAL unit start
1505 uchar NalUnitType = scanner & 0x1F;
1506 switch (NalUnitType) {
1509 break;
1513 }
1514 break;
1518 gotAccessUnitDelimiter = false;
1519 if (newFrame)
1521 return tsPayload.Used();
1522 }
1523 break;
1524 default: ;
1525 }
1526 }
1527 if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
1528 || tsPayload.Eof()) // or if we're out of data
1529 break;
1530 }
1531 return tsPayload.Used();
1532}
1533
1535{
1537 dbgframes("A");
1538 GetByte(); // primary_pic_type
1539}
1540
1542{
1543 int chroma_format_idc = 0;
1544 int bitDepth = 0;
1545 uchar profile_idc = GetByte(); // profile_idc
1546 GetByte(); // constraint_set[0-5]_flags, reserved_zero_2bits
1547 GetByte(); // level_idc
1548 GetGolombUe(); // seq_parameter_set_id
1549 if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc ==118 || profile_idc == 128) {
1550 chroma_format_idc = GetGolombUe(); // chroma_format_idc
1551 if (chroma_format_idc == 3)
1553 bitDepth = 8 + GetGolombUe(); // bit_depth_luma_minus8
1554 GetGolombUe(); // bit_depth_chroma_minus8
1555 GetBit(); // qpprime_y_zero_transform_bypass_flag
1556 if (GetBit()) { // seq_scaling_matrix_present_flag
1557 for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); i++) {
1558 if (GetBit()) { // seq_scaling_list_present_flag
1559 int SizeOfScalingList = (i < 6) ? 16 : 64;
1560 int LastScale = 8;
1561 int NextScale = 8;
1562 for (int j = 0; j < SizeOfScalingList; j++) {
1563 if (NextScale)
1564 NextScale = (LastScale + GetGolombSe() + 256) % 256; // delta_scale
1565 if (NextScale)
1566 LastScale = NextScale;
1567 }
1568 }
1569 }
1570 }
1571 }
1572 log2_max_frame_num = GetGolombUe() + 4; // log2_max_frame_num_minus4
1573 int pic_order_cnt_type = GetGolombUe(); // pic_order_cnt_type
1574 if (pic_order_cnt_type == 0)
1575 GetGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
1576 else if (pic_order_cnt_type == 1) {
1577 GetBit(); // delta_pic_order_always_zero_flag
1578 GetGolombSe(); // offset_for_non_ref_pic
1579 GetGolombSe(); // offset_for_top_to_bottom_field
1580 for (int i = GetGolombUe(); i--; ) // num_ref_frames_in_pic_order_cnt_cycle
1581 GetGolombSe(); // offset_for_ref_frame
1582 }
1583 GetGolombUe(); // max_num_ref_frames
1584 GetBit(); // gaps_in_frame_num_value_allowed_flag
1585 uint16_t frame_Width = 16 * (1 + GetGolombUe()); // pic_width_in_mbs_minus1
1586 uint16_t frame_Height = 16 * (1 + GetGolombUe()); // pic_height_in_map_units_minus1
1587 frame_mbs_only_flag = GetBit(); // frame_mbs_only_flag
1588 if (frameWidth == 0) {
1590 if (!frame_mbs_only_flag) {
1591 GetBit(); // mb_adaptive_frame_field_flag
1592 frame_Height *= 2;
1593 }
1594 GetBit(); // direct_8x8_inference_flag
1595 bool frame_cropping_flag = GetBit(); // frame_cropping_flag
1596 if (frame_cropping_flag) {
1597 uint16_t frame_crop_left_offset = GetGolombUe(); // frame_crop_left_offset
1598 uint16_t frame_crop_right_offset = GetGolombUe(); // frame_crop_right_offset
1599 uint16_t frame_crop_top_offset = GetGolombUe(); // frame_crop_top_offset
1600 uint16_t frame_crop_bottom_offset = GetGolombUe(); // frame_crop_bottom_offset
1601 uint16_t CropUnitX = 1;
1602 uint16_t CropUnitY = frame_mbs_only_flag ? 1 : 2;
1603 if (!separate_colour_plane_flag && chroma_format_idc > 0) {
1604 if (chroma_format_idc == 1) {
1605 CropUnitX = 2;
1606 CropUnitY *= 2;
1607 }
1608 else if (chroma_format_idc == 2)
1609 CropUnitX = 2;
1610 }
1611 frame_Width -= CropUnitX * (frame_crop_left_offset + frame_crop_right_offset);
1612 frame_Height -= CropUnitY * (frame_crop_top_offset + frame_crop_bottom_offset);
1613 if (frame_Height > 1080 && frame_Height <= 1090) // workaround for channels with wrong crop parameters
1614 frame_Height = 1080;
1615 }
1616 frameWidth = frame_Width;
1617 frameHeight = frame_Height;
1618 // VUI parameters
1619 if (GetBit()) { // vui_parameters_present_flag
1620 if (GetBit()) { // aspect_ratio_info_present
1621 int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
1622 if (aspect_ratio_idc == 255) // EXTENDED_SAR
1623 GetBits(32); // sar_width, sar_height
1624 else if (frameHeight >= 720 && (aspect_ratio_idc == 1 || aspect_ratio_idc == 14 || aspect_ratio_idc == 15 || aspect_ratio_idc == 16))
1626 // implement decoding of other aspect_ratio_idc values when they are required
1627 }
1628 if (GetBit()) // overscan_info_present_flag
1629 GetBit(); // overscan_approriate_flag
1630 if (GetBit()) { // video_signal_type_present_flag
1631 GetBits(4); // video_format, video_full_range_flag
1632 if (GetBit()) // colour_description_present_flag
1633 GetBits(24); // colour_primaries, transfer_characteristics, matrix_coefficients
1634 }
1635 if (GetBit()) { // chroma_loc_info_present_flag
1636 GetGolombUe(); // chroma_sample_loc_type_top_field
1637 GetGolombUe(); // chroma_sample_loc_type_bottom_field
1638 }
1639 if (GetBit()) { // timing_info_present_flag
1640 uint32_t num_units_in_tick = GetBits(32); // num_units_in_tick
1641 uint32_t time_scale = GetBits(32); // time_scale
1642 if (num_units_in_tick > 0)
1643 framesPerSecond = double(time_scale) / (num_units_in_tick << 1);
1644 }
1645 }
1646 if (debug) {
1647 cString s = cString::sprintf("H.264: %d x %d%c %.2f fps %d Bit %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, bitDepth, AspectRatioTexts[aspectRatio]);
1648 dsyslog("%s", *s);
1649 dbgframes("\n%s", *s);
1650 }
1651 }
1652 if (debug) {
1654 dbgframes("A"); // just for completeness
1655 dbgframes(frame_mbs_only_flag ? "S" : "s");
1656 }
1657}
1658
1660{
1661 newFrame = true;
1662 GetGolombUe(); // first_mb_in_slice
1663 int slice_type = GetGolombUe(); // slice_type, 0 = P, 1 = B, 2 = I, 3 = SP, 4 = SI
1664 independentFrame = (slice_type % 5) == 2;
1665 if (debug) {
1666 static const char SliceTypes[] = "PBIpi";
1667 dbgframes("%c", SliceTypes[slice_type % 5]);
1668 }
1670 return; // don't need the rest - a frame is complete
1671 GetGolombUe(); // pic_parameter_set_id
1673 GetBits(2); // colour_plane_id
1674 GetBits(log2_max_frame_num); // frame_num
1675 if (!frame_mbs_only_flag) {
1676 if (GetBit()) // field_pic_flag
1677 newFrame = !GetBit(); // bottom_field_flag
1678 if (debug)
1679 dbgframes(newFrame ? "t" : "b");
1680 }
1681}
1682
1683// --- cH265Parser -----------------------------------------------------------
1684
1723
1725:cH264Parser()
1726{
1727}
1728
1729int cH265Parser::Parse(const uchar *Data, int Length, int Pid)
1730{
1731 newFrame = independentFrame = false;
1732 tsPayload.Setup(const_cast<uchar *>(Data), Length, Pid);
1733 if (TsPayloadStart(Data)) {
1736 }
1737 for (;;) {
1738 scanner = (scanner << 8) | GetByte(true);
1739 if ((scanner & 0xFFFFFF00) == 0x00000100) { // NAL unit start
1740 uchar NalUnitType = (scanner >> 1) & 0x3F;
1741 GetByte(); // nuh_layer_id + nuh_temporal_id_plus1
1742 if (NalUnitType <= nutSliceSegmentRASLR || (NalUnitType >= nutSliceSegmentBLAWLP && NalUnitType <= nutSliceSegmentCRANUT)) {
1743 if (NalUnitType == nutSliceSegmentIDRWRADL || NalUnitType == nutSliceSegmentIDRNLP || NalUnitType == nutSliceSegmentCRANUT)
1744 independentFrame = true;
1745 if (GetBit()) { // first_slice_segment_in_pic_flag
1746 newFrame = true;
1748 }
1749 break;
1750 }
1751 else if (frameWidth == 0 && NalUnitType == nutSequenceParameterSet) {
1754 }
1755 }
1756 if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
1757 || tsPayload.Eof()) // or if we're out of data
1758 break;
1759 }
1760 return tsPayload.Used();
1761}
1762
1764{
1766 uint8_t sub_layer_profile_present_flag[8];
1767 uint8_t sub_layer_level_present_flag[8];
1768 GetBits(4); // sps_video_parameter_set_id
1769 int sps_max_sub_layers_minus1 = GetBits(3) & 7; // sps_max_sub_layers_minus1 ("& 7" to silence a compiler warning with gcc 14.1.0)
1770 GetBit(); // sps_temporal_id_nesting_flag
1771 // begin profile_tier_level(1, sps_max_sub_layers_minus1)
1772 GetByte();
1773 GetByte();
1774 GetByte();
1775 GetByte();
1776 GetByte();
1777 bool general_progressive_source_flag = GetBit(); // general_progressive_source_flag
1778 scanType = general_progressive_source_flag ? stProgressive : stInterlaced;
1779 GetBit(); // general_interlaced_source_flag
1780 GetBits(6);
1781 GetByte();
1782 GetByte();
1783 GetByte();
1784 GetByte();
1785 GetByte();
1786 GetByte(); // general_level_idc
1787 for (int i = 0; i < sps_max_sub_layers_minus1; i++ ) {
1788 sub_layer_profile_present_flag[i] = GetBit(); // sub_layer_profile_present_flag[i]
1789 sub_layer_level_present_flag[i] = GetBit(); // sub_layer_level_present_flag[i]
1790 }
1791 if (sps_max_sub_layers_minus1 > 0) {
1792 for (int i = sps_max_sub_layers_minus1; i < 8; i++ )
1793 GetBits(2); // reserved_zero_2bits[i]
1794 }
1795 for (int i = 0; i < sps_max_sub_layers_minus1; i++ ) {
1796 if (sub_layer_profile_present_flag[i] )
1797 GetBits(88);
1798 if (sub_layer_level_present_flag[i])
1799 GetBits(8);
1800 }
1801 // end profile_tier_level
1802 GetGolombUe(); // sps_seq_parameter_set_id
1803 int chroma_format_idc = GetGolombUe(); // chroma_format_idc
1804 if (chroma_format_idc == 3)
1805 separate_colour_plane_flag = GetBit(); // separate_colour_plane_flag
1806 frameWidth = GetGolombUe(); // pic_width_in_luma_samples
1807 frameHeight = GetGolombUe(); // pic_height_in_luma_samples
1808 bool conformance_window_flag = GetBit(); // conformance_window_flag
1809 if (conformance_window_flag) {
1810 int conf_win_left_offset = GetGolombUe(); // conf_win_left_offset
1811 int conf_win_right_offset = GetGolombUe(); // conf_win_right_offset
1812 int conf_win_top_offset = GetGolombUe(); // conf_win_top_offset
1813 int conf_win_bottom_offset = GetGolombUe(); // conf_win_bottom_offset
1814 uint16_t SubWidthC = 1;
1815 uint16_t SubHeightC = 1;
1816 if (!separate_colour_plane_flag && chroma_format_idc > 0) {
1817 if (chroma_format_idc == 1) {
1818 SubWidthC = 2;
1819 SubHeightC = 2;
1820 }
1821 else if (chroma_format_idc == 2)
1822 SubWidthC = 2;
1823 }
1824 frameWidth -= SubWidthC * (conf_win_left_offset + conf_win_right_offset);
1825 frameHeight -= SubHeightC * (conf_win_top_offset + conf_win_bottom_offset);
1826 }
1827 int bitDepth = 8 + GetGolombUe(); // bit_depth_luma_minus8
1828 GetGolombUe(); // bit_depth_chroma_minus8
1829 int log2_max_pic_order_cnt_lsb_minus4 = GetGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
1830 int sps_sub_layer_ordering_info_present_flag = GetBit(); // sps_sub_layer_ordering_info_present_flag
1831 for (int i = sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1; i <= sps_max_sub_layers_minus1; ++i) {
1832 GetGolombUe(); // sps_max_dec_pic_buffering_minus1[i]
1833 GetGolombUe(); // sps_max_num_reorder_pics[i]
1834 GetGolombUe(); // sps_max_latency_increase_plus1[i]
1835 }
1836 GetGolombUe(); // log2_min_luma_coding_block_size_minus3
1837 GetGolombUe(); // log2_diff_max_min_luma_coding_block_size
1838 GetGolombUe(); // log2_min_luma_transform_block_size_minus2
1839 GetGolombUe(); // log2_diff_max_min_luma_transform_block_size
1840 GetGolombUe(); // max_transform_hierarchy_depth_inter
1841 GetGolombUe(); // max_transform_hierarchy_depth_intra
1842 if (GetBit()) { // scaling_list_enabled_flag
1843 if (GetBit()) { // sps_scaling_list_data_present_flag
1844 // begin scaling_list_data
1845 for (int sizeId = 0; sizeId < 4; ++sizeId) {
1846 for (int matrixId = 0; matrixId < 6; matrixId += (sizeId == 3) ? 3 : 1) {
1847 if (!GetBit()) // scaling_list_pred_mode_flag[sizeId][matrixId]
1848 GetGolombUe(); // scaling_list_pred_matrix_id_delta[sizeId][matrixId]
1849 else {
1850 int coefNum = min(64, (1 << (4 + (sizeId << 1))));
1851 if (sizeId > 1)
1852 GetGolombSe(); // scaling_list_dc_coef_minus8[sizeId−2][matrixId]
1853 for (int i = 0; i < coefNum; ++i)
1854 GetGolombSe(); // scaling_list_delta_coef
1855 }
1856 }
1857 }
1858 }
1859 // end scaling_list_data
1860 }
1861 GetBits(2); // amp_enabled_flag, sample_adaptive_offset_enabled_flag
1862 if (GetBit()) { // pcm_enabled_flag
1863 GetBits(8); // pcm_sample_bit_depth_luma_minus1, pcm_sample_bit_depth_chroma_minus1
1864 GetGolombUe(); // log2_min_pcm_luma_coding_block_size_minus3
1865 GetGolombUe(); // log2_diff_max_min_pcm_luma_coding_block_size
1866 GetBit(); // pcm_loop_filter_disabled_flag
1867 }
1868 uint32_t num_short_term_ref_pic_sets = GetGolombUe(); // num_short_term_ref_pic_sets
1869 uint32_t NumDeltaPocs[num_short_term_ref_pic_sets];
1870 for (uint32_t stRpsIdx = 0; stRpsIdx < num_short_term_ref_pic_sets; ++stRpsIdx) {
1871 // start of st_ref_pic_set(stRpsIdx)
1872 bool inter_ref_pic_set_prediction_flag = false;
1873 if (stRpsIdx != 0)
1874 inter_ref_pic_set_prediction_flag = GetBit(); // inter_ref_pic_set_prediction_flag
1875 if (inter_ref_pic_set_prediction_flag) {
1876 uint32_t RefRpsIdx, delta_idx_minus1 = 0;
1877 if (stRpsIdx == num_short_term_ref_pic_sets)
1878 delta_idx_minus1 = GetGolombUe(); // delta_idx_minus1
1879 GetBit(); // delta_rps_sign
1880 GetGolombUe(); // abs_delta_rps_minus1
1881 RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1);
1882 NumDeltaPocs[stRpsIdx] = 0;
1883 for (uint32_t j = 0; j <= NumDeltaPocs[RefRpsIdx]; ++j) {
1884 if (!GetBit()) { // used_by_curr_pic_flag[j]
1885 if (GetBit()) // use_delta_flag[j]
1886 NumDeltaPocs[stRpsIdx]++;
1887 }
1888 else
1889 NumDeltaPocs[stRpsIdx]++;
1890 }
1891 }
1892 else {
1893 uint32_t num_negative_pics = GetGolombUe(); // num_negative_pics
1894 uint32_t num_positive_pics = GetGolombUe(); // num_positive_pics
1895 for (uint32_t j = 0; j < num_negative_pics; ++j) {
1896 GetGolombUe(); // delta_poc_s0_minus1[i]
1897 GetBit(); // used_by_curr_pic_s0_flag[i]
1898 }
1899 for (uint32_t j = 0; j < num_positive_pics; ++j) {
1900 GetGolombUe(); // delta_poc_s1_minus1[i]
1901 GetBit(); // delta_poc_s1_minus1[i]
1902 }
1903 NumDeltaPocs[stRpsIdx] = num_negative_pics + num_positive_pics;
1904 }
1905 // end of st_ref_pic_set(stRpsIdx)
1906 }
1907 if (GetBit()) { // long_term_ref_pics_present_flag
1908 uint32_t num_long_term_ref_pics_sps = GetGolombUe(); // num_long_term_ref_pics_sps
1909 for (uint32_t i = 0; i < num_long_term_ref_pics_sps; ++i) {
1910 GetBits(log2_max_pic_order_cnt_lsb_minus4 + 4); // lt_ref_pic_poc_lsb_sps[i]
1911 GetBit(); // used_by_curr_pic_lt_sps_flag[i]
1912 }
1913 }
1914 GetBits(2); // sps_temporal_mvp_enabled_flag, strong_intra_smoothing_enabled_flag
1915 if (GetBit()) { // vui_parameters_present_flag
1916 // begin of vui_parameters()
1917 if (GetBit()) { // aspect_ratio_info_present_flag
1918 int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
1919 if (aspect_ratio_idc == 255) // EXTENDED_SAR
1920 GetBits(32); // sar_width, sar_height
1921 else if (aspect_ratio_idc == 1 || aspect_ratio_idc == 14)
1923 // implement decoding of other aspect_ratio_idc values when they are required
1924 }
1925 if (GetBit()) // overscan_info_present_flag
1926 GetBit(); // overscan_appropriate_flag
1927 if (GetBit()) { // video_signal_type_present_flag
1928 GetBits(4); // video_format, video_full_range_flag
1929 if (GetBit()) // colour_description_present_flag
1930 GetBits(24); // colour_primaries, transfer_characteristics, matrix_coeffs
1931 }
1932 if (GetBit()) { // chroma_loc_info_present_flag
1933 GetGolombUe(); // chroma_sample_loc_type_top_field
1934 GetGolombUe(); // chroma_sample_loc_type_bottom_field
1935 }
1936 GetBits(3); // neutral_chroma_indication_flag, field_seq_flag, frame_field_info_present_flag
1937 if (GetBit()) { // default_display_window_flag
1938 GetGolombUe(); // def_disp_win_left_offset
1939 GetGolombUe(); // def_disp_win_right_offset
1940 GetGolombUe(); // def_disp_win_top_offset
1941 GetGolombUe(); // def_disp_win_bottom_offset
1942 }
1943 if (GetBit()) { // vui_timing_info_present_flag
1944 uint32_t vui_num_units_in_tick = GetBits(32); // vui_num_units_in_tick
1945 uint32_t vui_time_scale = GetBits(32); // vui_time_scale
1946 if (vui_num_units_in_tick > 0)
1947 framesPerSecond = (double)vui_time_scale / vui_num_units_in_tick;
1948 }
1949 }
1950 if (debug) {
1951 cString s = cString::sprintf("H.265: %d x %d%c %.2f fps %d Bit %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, bitDepth, AspectRatioTexts[aspectRatio]);
1952 dsyslog("%s", *s);
1953 dbgframes("\n%s", *s);
1954 }
1955}
1956
1957static bool DebugChecks = false;
1958
1959// cTsChecker and cPtsChecker are used to detect errors in the recorded data stream.
1960// While cTsChecker checks the continuity counter of the incoming TS packets, cPtsChecker
1961// works on entire frames, checking their PTS (Presentation Time Stamps) to see whether
1962// all expected frames arrive. The resulting number of errors is not a precise value.
1963// If it is zero, the recording can be safely considered error free. The higher the value,
1964// the more damaged the recording is.
1965
1966// --- cTsChecker ------------------------------------------------------------
1967
1968#define TS_CC_UNKNOWN 0xFF
1969
1971private:
1975 void Report(int Pid, const char *Message);
1976public:
1977 cTsChecker(void);
1978 void Reset(void);
1979 void Clear(void);
1980 int Errors(void) { return errors; }
1981 bool NewErrors(void);
1982 void CheckTs(const uchar *Data, int Length);
1983 };
1984
1986{
1987 Reset();
1988 Clear();
1989}
1990
1992{
1993 memset(counter, TS_CC_UNKNOWN, sizeof(counter));
1994}
1995
1997{
1998 errors = 0;
1999 oldErrors = 0;
2000}
2001
2002void cTsChecker::Report(int Pid, const char *Message)
2003{
2004 errors++;
2005 if (DebugChecks)
2006 fprintf(stderr, "%s: TS error #%d on PID %d (%s)\n", *TimeToString(time(NULL)), errors, Pid, Message);
2007}
2008
2010{
2011 bool e = oldErrors < errors;
2012 oldErrors = errors;
2013 return e;
2014}
2015
2016void cTsChecker::CheckTs(const uchar *Data, int Length)
2017{
2018 while (Length >= TS_SIZE) {
2019 int Pid = TsPid(Data);
2020 uchar Cc = TsContinuityCounter(Data);
2021 if (TsHasPayload(Data)) {
2022 if (TsError(Data))
2023 Report(Pid, "tei");
2024 else if (TsIsScrambled(Data))
2025 Report(Pid, "scrambled");
2026 else {
2027 uchar OldCc = counter[Pid];
2028 if (OldCc != TS_CC_UNKNOWN) {
2029 uchar NewCc = (OldCc + 1) & TS_CONT_CNT_MASK;
2030 if (Cc != NewCc)
2031 Report(Pid, "continuity");
2032 }
2033 }
2034 }
2035 counter[Pid] = Cc;
2036 Data += TS_SIZE;
2037 Length -= TS_SIZE;
2038 }
2039}
2040
2041// --- cPtsChecker -----------------------------------------------------------
2042
2043static inline int ComparePts(const void *a, const void *b)
2044{
2045 return PtsDiff(*(const int64_t *)b, *(const int64_t *)a);
2046}
2048private:
2050 int64_t lastPts;
2055public:
2056 cPtsChecker(void);
2057 void Clear(void);
2058 void SetFrameDelta(int FrameDelta) { frameDelta = FrameDelta; }
2059 void Process(void);
2060 void AddPts(int64_t Pts, bool IndependentFrame = false);
2061 bool NewMissing(void);
2062 int Missing(void);
2063 };
2064
2066{
2067 frameDelta = 0;
2068 Clear();
2069}
2070
2072{
2073 lastPts = -1;
2074 iFrameNoPts = false;
2075 totalMissing = 0;
2076 oldMissing = 0;
2077}
2078
2080{
2081 if (pts.Size() > 1) {
2083 if (frameDelta == 0) {
2084 int Delta = INT_MAX;
2085 for (int i = 1; i < pts.Size(); i++) {
2086 int d = int(PtsDiff(pts[i - 1], pts[i]));
2087 if (d > 0 && d < Delta)
2088 Delta = d;
2089 }
2090 if (Delta < INT_MAX)
2091 frameDelta = Delta;
2092 }
2093 if (frameDelta == 0)
2094 return; // can't continue without frameDelta
2095 int Missing = 0;
2096 int Number = pts.Size();
2097 if (Number > 0 && pts[Number - 1] == lastPts) {
2098 // Don't get stuck at a total discontinuity:
2099 pts.Remove(Number - 1);
2100 Number--;
2101 }
2102 for (int i = 1; i < Number; i++) {
2103 int d = int(PtsDiff(pts[i - 1], pts[i]));
2104 if (d > frameDelta) {
2105 d = (d / frameDelta) - 1;
2106 if (d > 0 && iFrameNoPts) {
2107 d--;
2108 iFrameNoPts = false;
2109 }
2110 if (d > 0)
2111 Missing += d;
2112 }
2113 }
2115 pts.Remove(0, Number - 1); // leave the last value in the list
2116 if (pts.Size() == 1)
2117 lastPts = pts[0];
2118 }
2119}
2120
2121void cPtsChecker::AddPts(int64_t Pts, bool IndependentFrame)
2122{
2123 if (IndependentFrame) {
2124 Process();
2125 // In H.265 there can be I-frames that have no PTS (if anybody knows how players
2126 // handle this, please let me know). This is a workaround for such cases:
2127 if (Pts < 0)
2128 iFrameNoPts = true;
2129 }
2130 else if (Pts < 0) {
2131 // In MPEG 2 it is possible that only I-frames have PTS. As a workaround for
2132 // such cases we generate the intermediate PTS values:
2133 if (pts.Size() > 0)
2134 Pts = PtsAdd(pts[pts.Size() - 1], frameDelta);
2135 }
2136 if (Pts >= 0)
2137 pts.Append(Pts);
2138}
2139
2141{
2142 bool m = oldMissing < totalMissing;
2144 return m;
2145}
2146
2148{
2149 return totalMissing;
2150}
2151
2152// --- cFrameChecker ---------------------------------------------------------
2153
2159
2161{
2162 delete ptsChecker;
2163 delete tsChecker;
2164}
2165
2167{
2168 tsChecker->Reset();
2169}
2170
2171bool cFrameChecker::Check(const uchar *Data, int Length, bool Independent, bool &Errors, bool &Missing, bool Final, int Pid)
2172{
2173 tsChecker->CheckTs(Data, Length);
2174 Errors = tsChecker->NewErrors();
2175 ptsChecker->AddPts(TsGetPts(Data, Length, Pid), Independent);
2176 if (Final)
2178 Missing = ptsChecker->NewMissing();
2179 return Errors || Missing;
2180}
2181
2183{
2184 return tsChecker->Errors() + ptsChecker->Missing();
2185}
2186
2187// --- cFrameDetector --------------------------------------------------------
2188
2189const char *ScanTypeChars = "-pi"; // index is eScanType
2190const char *AspectRatioTexts[] = { // index is eAspectRatio
2191 "-",
2192 "1:1",
2193 "4:3",
2194 "16:9",
2195 "2.21:1",
2196 NULL
2197 };
2198
2200{
2201 parser = NULL;
2202 tsChecker = new cTsChecker;
2203 ptsChecker = new cPtsChecker; // must be done before calling SetPid()
2204 SetPid(Pid, Type);
2205 synced = false;
2206 newFrame = independentFrame = false;
2207 numPtsValues = 0;
2208 numIFrames = 0;
2209 framesPerSecond = 0;
2210 frameWidth = 0;
2211 frameHeight = 0;
2215 scanning = false;
2216 firstIframeSeen = false;
2217 previousErrors = 0;
2218 missingFrames = 0;
2219}
2220
2222{
2223 delete ptsChecker;
2224 delete tsChecker;
2225}
2226
2227static int CmpUint32(const void *p1, const void *p2)
2228{
2229 if (*(uint32_t *)p1 < *(uint32_t *)p2) return -1;
2230 if (*(uint32_t *)p1 > *(uint32_t *)p2) return 1;
2231 return 0;
2232}
2233
2234void cFrameDetector::SetPid(int Pid, int Type)
2235{
2236 pid = Pid;
2237 type = Type;
2238 isVideo = type == 0x01 || type == 0x02 || type == 0x1B || type == 0x24; // MPEG 1, 2, H.264 or H.265
2239 delete parser;
2240 parser = NULL;
2241 if (type == 0x01 || type == 0x02)
2242 parser = new cMpeg2Parser;
2243 else if (type == 0x1B)
2244 parser = new cH264Parser;
2245 else if (type == 0x24)
2246 parser = new cH265Parser;
2247 else if (type == 0x03 || type == 0x04 || type == 0x06) // MPEG audio or AC3 audio
2248 parser = new cAudioParser;
2249 else if (type != 0)
2250 esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
2251 tsChecker->Reset();
2252}
2253
2254void cFrameDetector::SetLastPts(int64_t LastPts)
2255{
2256 ptsChecker->AddPts(LastPts);
2257 ptsChecker->Clear();
2258}
2259
2260int cFrameDetector::Errors(bool *PreviousErrors, bool *MissingFrames)
2261{
2262 if (PreviousErrors)
2263 *PreviousErrors = tsChecker->NewErrors();
2264 if (MissingFrames) {
2266 *MissingFrames = ptsChecker->NewMissing();
2267 }
2268 return tsChecker->Errors() + ptsChecker->Missing();
2269}
2270
2271bool cFrameDetector::NewFrame(int *PreviousErrors, int *MissingFrames)
2272{
2273 // This function is deprecated, PreviousErrors and MissingFrames only return 0 or 1, not actual numbers!
2274 if (newFrame) {
2275 if (PreviousErrors)
2276 *PreviousErrors = previousErrors;
2277 if (MissingFrames)
2278 *MissingFrames = missingFrames;
2279 }
2280 return newFrame;
2281}
2282
2283bool cFrameDetector::NewFrame(bool &PreviousErrors, bool &MissingFrames)
2284{
2285 if (newFrame) {
2286 PreviousErrors = previousErrors;
2287 MissingFrames = missingFrames;
2288 }
2289 return newFrame;
2290}
2291
2292int cFrameDetector::Analyze(const uchar *Data, int Length, bool ErrorCheck)
2293{
2294 if (!parser)
2295 return 0;
2296 int Processed = 0;
2297 newFrame = independentFrame = false;
2298 while (Length >= MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE) { // makes sure we are looking at enough data, in case the frame type is not stored in the first TS packet
2299 // Sync on TS packet borders:
2300 if (int Skipped = TS_SYNC(Data, Length))
2301 return Processed + Skipped;
2302 // Handle at least one TS packet:
2303 int Handled = TS_SIZE;
2304 if (TsHasPayload(Data) && !TsIsScrambled(Data)) {
2305 int Pid = TsPid(Data);
2306 if (Pid == pid) {
2307 if (Processed)
2308 return Processed;
2309 if (TsPayloadStart(Data))
2310 scanning = true;
2311 if (scanning) {
2312 // Detect the beginning of a new frame:
2313 if (TsPayloadStart(Data)) {
2316 }
2317 int n = parser->Parse(Data, Length, pid);
2318 if (n > 0) {
2319 if (parser->NewFrame()) {
2320 newFrame = true;
2323 if (synced) {
2324 if (framesPerPayloadUnit <= 1)
2325 scanning = false;
2326 }
2327 else {
2328 if (parser->FramesPerSecond() > 0.0) {
2335 synced = true;
2336 parser->SetDebug(false);
2337 }
2339 if (independentFrame)
2340 numIFrames++;
2341 }
2342 if (synced && firstIframeSeen && ErrorCheck)
2344 }
2345 Handled = n;
2346 }
2347 }
2348 if (TsPayloadStart(Data)) {
2349 // Determine the frame rate from the PTS values in the PES headers:
2350 if (framesPerSecond <= 0.0) {
2351 // frame rate unknown, so collect a sequence of PTS values:
2352 if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
2353 if (newFrame) { // only take PTS values at the beginning of a frame (in case of fields!)
2354 const uchar *Pes = Data + TsPayloadOffset(Data);
2355 if (numIFrames && PesHasPts(Pes)) {
2357 // check for rollover:
2358 if (numPtsValues && ptsValues[numPtsValues - 1] > 0xF0000000 && ptsValues[numPtsValues] < 0x10000000) {
2359 dbgframes("#");
2360 numPtsValues = 0;
2361 numIFrames = 0;
2362 }
2363 else
2364 numPtsValues++;
2365 }
2366 }
2367 }
2368 if (numPtsValues >= 2 && numIFrames >= 2) {
2369 // find the smallest PTS delta:
2370 qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
2371 numPtsValues--;
2372 for (int i = 0; i < numPtsValues; i++)
2373 ptsValues[i] = ptsValues[i + 1] - ptsValues[i];
2374 qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
2375 int Div = framesPerPayloadUnit;
2376 if (framesPerPayloadUnit > 1)
2378 if (Div <= 0)
2379 Div = 1;
2380 int Delta = ptsValues[0] / Div;
2381 // determine frame info:
2382 if (isVideo) {
2383 if (Delta == 3753)
2384 framesPerSecond = 24.0 / 1.001;
2385 else if (abs(Delta - 3600) <= 1)
2386 framesPerSecond = 25.0;
2387 else if (Delta % 3003 == 0)
2388 framesPerSecond = 30.0 / 1.001;
2389 else if (abs(Delta - 1800) <= 1)
2390 framesPerSecond = 50.0;
2391 else if (Delta == 1501)
2392 framesPerSecond = 60.0 / 1.001;
2393 else {
2395 dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, framesPerSecond);
2396 }
2397 }
2398 else // audio
2399 framesPerSecond = double(PTSTICKS) / Delta; // PTS of audio frames is always increasing
2401 dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d TRO = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1, parser->IFrameTemporalReferenceOffset());
2402 synced = true;
2403 parser->SetDebug(false);
2404 }
2405 }
2406 }
2407 }
2408 else if (Pid == PATPID && synced && Processed)
2409 return Processed; // allow the caller to see any PAT packets
2410 }
2411 if (synced && firstIframeSeen && ErrorCheck) {
2412 if (newFrame) {
2415 }
2416 tsChecker->CheckTs(Data, Handled);
2417 }
2418 Data += Handled;
2419 Length -= Handled;
2420 Processed += Handled;
2421 if (newFrame)
2422 break;
2423 }
2424 return Processed;
2425}
#define MAXLANGCODE2
Definition channels.h:37
#define MAXDPIDS
Definition channels.h:32
#define MAXAPIDS
Definition channels.h:31
#define MAXSPIDS
Definition channels.h:33
#define MAXLANGCODE1
Definition channels.h:36
static u_int32_t crc32(const char *d, int len, u_int32_t CRCvalue)
Definition util.c:267
bool CheckCRCAndParse()
Definition si.c:65
Descriptor * getNext(Iterator &it)
Definition si.c:112
DescriptorTag getDescriptorTag() const
Definition si.c:100
StructureLoop< Language > languageLoop
Definition descriptor.h:490
bool getCurrentNextIndicator() const
Definition si.c:80
int getSectionNumber() const
Definition si.c:88
int getLastSectionNumber() const
Definition si.c:92
int getVersionNumber() const
Definition si.c:84
int getPid() const
Definition section.c:34
int getServiceId() const
Definition section.c:30
bool isNITPid() const
Definition section.h:31
StructureLoop< Association > associationLoop
Definition section.h:39
int getTransportStreamId() const
Definition section.c:26
DescriptorLoop streamDescriptors
Definition section.h:63
int getPid() const
Definition section.c:65
int getStreamType() const
Definition section.c:69
int getServiceId() const
Definition section.c:57
int getPCRPid() const
Definition section.c:61
StructureLoop< Stream > streamLoop
Definition section.h:71
StructureLoop< Subtitling > subtitlingLoop
Definition descriptor.h:332
virtual int Parse(const uchar *Data, int Length, int Pid) override
Parses the given Data, which is a sequence of Length bytes of TS packets.
Definition remux.c:1249
cAudioParser(void)
Definition remux.c:1245
const int * Dpids(void) const
Definition channels.h:158
const char(* Alangs() const)[MAXLANGCODE2]
Definition channels.h:163
const uchar * Stypes(void) const
Definition channels.h:171
const int * Atypes(void) const
Definition channels.h:169
const char(* Slangs() const)[MAXLANGCODE2]
Definition channels.h:165
int Tpid(void) const
Definition channels.h:179
int Vpid(void) const
Definition channels.h:154
const uint16_t * CompositionPageIds(void) const
Definition channels.h:177
int Vtype(void) const
Definition channels.h:156
int Ppid(void) const
Definition channels.h:155
const int * Dtypes(void) const
Definition channels.h:170
const int * Apids(void) const
Definition channels.h:157
const char(* Dlangs() const)[MAXLANGCODE2]
Definition channels.h:164
const uint16_t * AncillaryPageIds(void) const
Definition channels.h:178
const int * Spids(void) const
Definition channels.h:159
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition device.h:148
void EnsureAudioTrack(bool Force=false)
Makes sure an audio track is selected that is actually available.
Definition device.c:1284
void ClrAvailableTracks(bool DescriptionsOnly=false, bool IdsOnly=false)
Clears the list of currently available tracks.
Definition device.c:1123
void EnsureSubtitleTrack(void)
Makes sure one of the preferred language subtitle tracks is selected.
Definition device.c:1317
bool SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language=NULL, const char *Description=NULL)
Sets the track of the given Type and Index to the given values.
Definition device.c:1147
uchar eit[TS_SIZE]
Definition remux.h:438
cEitGenerator(int Sid=0)
Definition remux.c:968
int counter
Definition remux.h:439
uchar * AddParentalRatingDescriptor(uchar *p, uchar ParentalRating=0)
Definition remux.c:982
uint16_t YMDtoMJD(int Y, int M, int D)
Definition remux.c:976
uchar * Generate(int Sid)
Definition remux.c:993
int version
Definition remux.h:440
void Reset(void)
Definition remux.c:2166
cTsChecker * tsChecker
Definition remux.h:535
cFrameChecker(void)
Definition remux.c:2154
cPtsChecker * ptsChecker
Definition remux.h:536
int TotalErrors(void)
Returns the total number of all errors and missing frames detected in the data given to the calls to ...
Definition remux.c:2182
bool Check(const uchar *Data, int Length, bool Independent, bool &Errors, bool &Missing, bool Final, int Pid)
Check Length bytes of the given Data (which must be a complete frame), with Independent telling wheth...
Definition remux.c:2171
int previousErrors
Definition remux.h:576
uint32_t ptsValues[MaxPtsValues]
Definition remux.h:562
bool synced
Definition remux.h:559
uint16_t frameHeight
Definition remux.h:568
int Errors(bool *PreviousErrors=NULL, bool *MissingFrames=NULL)
Returns the total number of errors so far.
Definition remux.c:2260
cTsChecker * tsChecker
Definition remux.h:579
uint16_t frameWidth
Definition remux.h:567
bool scanning
Definition remux.h:574
int framesPerPayloadUnit
Definition remux.h:572
bool NewFrame(int *PreviousErrors=NULL, int *MissingFrames=NULL)
Definition remux.c:2271
bool firstIframeSeen
Definition remux.h:575
int Analyze(const uchar *Data, int Length, bool ErrorCheck=true)
Analyzes the TS packets pointed to by Data.
Definition remux.c:2292
double framesPerSecond
Definition remux.h:566
int framesInPayloadUnit
Definition remux.h:571
int numIFrames
Definition remux.h:564
cPtsChecker * ptsChecker
Definition remux.h:580
bool independentFrame
Definition remux.h:561
cFrameDetector(int Pid=0, int Type=0)
Sets up a frame detector for the given Pid and stream Type.
Definition remux.c:2199
eScanType scanType
Definition remux.h:569
int missingFrames
Definition remux.h:577
void SetLastPts(int64_t LastPts)
If this is a resumed recording, call this function with the last PTS of the previous recording.
Definition remux.c:2254
bool newFrame
Definition remux.h:560
cFrameParser * parser
Definition remux.h:578
int numPtsValues
Definition remux.h:563
bool isVideo
Definition remux.h:565
void SetPid(int Pid, int Type)
Sets the Pid and stream Type to detect frames for.
Definition remux.c:2234
eAspectRatio aspectRatio
Definition remux.h:570
uint16_t FrameWidth(void)
Definition remux.c:1217
void SetDebug(bool Debug)
Definition remux.c:1213
bool NewFrame(void)
Definition remux.c:1214
eAspectRatio aspectRatio
Definition remux.c:1202
double FramesPerSecond(void)
Definition remux.c:1219
bool IndependentFrame(void)
Definition remux.c:1215
bool newFrame
Definition remux.c:1195
int iFrameTemporalReferenceOffset
Definition remux.c:1197
double framesPerSecond
Definition remux.c:1200
eScanType ScanType(void)
Definition remux.c:1220
bool independentFrame
Definition remux.c:1196
virtual ~cFrameParser()
Definition remux.c:1205
uint16_t frameHeight
Definition remux.c:1199
bool debug
Definition remux.c:1194
virtual int Parse(const uchar *Data, int Length, int Pid)=0
Parses the given Data, which is a sequence of Length bytes of TS packets.
eAspectRatio AspectRatio(void)
Definition remux.c:1221
eScanType scanType
Definition remux.c:1201
uint16_t FrameHeight(void)
Definition remux.c:1218
int IFrameTemporalReferenceOffset(void)
Definition remux.c:1216
uint16_t frameWidth
Definition remux.c:1198
cFrameParser(void)
Definition remux.c:1224
uint32_t GetBits(int Bits)
Definition remux.c:1463
cTsPayload tsPayload
Definition remux.c:1399
void ParseAccessUnitDelimiter(void)
Definition remux.c:1534
@ nutSequenceParameterSet
Definition remux.c:1388
@ nutCodedSliceNonIdr
Definition remux.c:1386
@ nutAccessUnitDelimiter
Definition remux.c:1389
@ nutCodedSliceIdr
Definition remux.c:1387
bool separate_colour_plane_flag
Definition remux.c:1395
bool gotAccessUnitDelimiter
Definition remux.c:1401
int zeroBytes
Definition remux.c:1393
bool frame_mbs_only_flag
Definition remux.c:1397
int log2_max_frame_num
Definition remux.c:1396
virtual int Parse(const uchar *Data, int Length, int Pid) override
Parses the given Data, which is a sequence of Length bytes of TS packets.
Definition remux.c:1491
uint32_t GetGolombUe(void)
Definition remux.c:1471
uchar GetByte(bool Raw=false)
Gets the next data byte.
Definition remux.c:1435
void ParseSliceHeader(void)
Definition remux.c:1659
void ParseSequenceParameterSet(void)
Definition remux.c:1541
uchar byte
Definition remux.c:1391
uint32_t scanner
Definition remux.c:1400
bool gotSequenceParameterSet
Definition remux.c:1402
int32_t GetGolombSe(void)
Definition remux.c:1479
cH264Parser(void)
Sets up a new H.264 parser.
Definition remux.c:1422
uchar GetBit(void)
Definition remux.c:1454
virtual int Parse(const uchar *Data, int Length, int Pid) override
Parses the given Data, which is a sequence of Length bytes of TS packets.
Definition remux.c:1729
cH265Parser(void)
Definition remux.c:1724
@ nutSliceSegmentIDRWRADL
Definition remux.c:1701
@ nutUnspecified0
Definition remux.c:1715
@ nutSliceSegmentSTSAN
Definition remux.c:1692
@ nutSliceSegmentRADLN
Definition remux.c:1694
@ nutSliceSegmentIDRNLP
Definition remux.c:1702
@ nutPrefixSEI
Definition remux.c:1711
@ nutAccessUnitDelimiter
Definition remux.c:1707
@ nutUnspecified7
Definition remux.c:1716
@ nutSliceSegmentBLAWRADL
Definition remux.c:1699
@ nutSliceSegmentTSAR
Definition remux.c:1691
@ nutSliceSegmentRADLR
Definition remux.c:1695
@ nutSliceSegmentTrailingR
Definition remux.c:1689
@ nutVideoParameterSet
Definition remux.c:1704
@ nutSequenceParameterSet
Definition remux.c:1705
@ nutSliceSegmentTSAN
Definition remux.c:1690
@ nutSliceSegmentRASLN
Definition remux.c:1696
@ nutSuffixSEI
Definition remux.c:1712
@ nutSliceSegmentBLAWLP
Definition remux.c:1698
@ nutEndOfBitstream
Definition remux.c:1709
@ nutSliceSegmentBLANLP
Definition remux.c:1700
@ nutSliceSegmentRASLR
Definition remux.c:1697
@ nutPictureParameterSet
Definition remux.c:1706
@ nutNonVCLRes3
Definition remux.c:1714
@ nutSliceSegmentCRANUT
Definition remux.c:1703
@ nutSliceSegmentTrailingN
Definition remux.c:1688
@ nutNonVCLRes0
Definition remux.c:1713
@ nutFillerData
Definition remux.c:1710
@ nutSliceSegmentSTSAR
Definition remux.c:1693
@ nutEndOfSequence
Definition remux.c:1708
void ParseSequenceParameterSet(void)
Definition remux.c:1763
bool seenIndependentFrame
Definition remux.c:1266
cMpeg2Parser(void)
Definition remux.c:1285
bool seenScanType
Definition remux.c:1268
int lastIFrameTemporalReference
Definition remux.c:1267
uint32_t scanner
Definition remux.c:1265
const double frame_rate_table[9]
Definition remux.c:1269
virtual int Parse(const uchar *Data, int Length, int Pid) override
Parses the given Data, which is a sequence of Length bytes of TS packets.
Definition remux.c:1293
int MakeCRC(uchar *Target, const uchar *Data, int Length)
Definition remux.c:456
uchar * GetPmt(int &Index)
Returns a pointer to the Index'th TS packet of the PMT section.
Definition remux.c:599
void SetChannel(const cChannel *Channel)
Sets the Channel for which the PAT/PMT shall be generated.
Definition remux.c:587
void IncEsInfoLength(int Length)
Definition remux.c:390
void IncCounter(int &Counter, uchar *TsPacket)
Definition remux.c:377
cPatPmtGenerator(const cChannel *Channel=NULL)
Definition remux.c:367
void SetVersions(int PatVersion, int PmtVersion)
Sets the version numbers for the generated PAT and PMT, in case this generator is used to,...
Definition remux.c:581
int numPmtPackets
Definition remux.h:302
uchar * esInfoLength
Definition remux.h:308
uchar pat[TS_SIZE]
Definition remux.h:300
int MakeAC3Descriptor(uchar *Target, uchar Type)
Definition remux.c:411
void GeneratePat(void)
Generates a PAT section for later use with GetPat().
Definition remux.c:486
int GetPatPmtSize(void)
Returns the combined size of PAT and all PMT packets.
Definition remux.c:608
uchar * GetPat(void)
Returns a pointer to the PAT section, which consists of exactly one TS packet.
Definition remux.c:593
uchar pmt[MAX_PMT_TS][TS_SIZE]
Definition remux.h:301
int MakeLanguageDescriptor(uchar *Target, const char *Language)
Definition remux.c:437
void SetPids(int Vpid, int Vtype, int Ppid, int Tpid, const int *Apids, const int *Atypes, const char Alangs[][MAXLANGCODE2], const int *Dpids, const int *Dtypes, const char Dlangs[][MAXLANGCODE2], const int *Spids, const uchar *Stypes, const char Slangs[][MAXLANGCODE2], const uint16_t *CompositionPageIds, const uint16_t *AncillaryPageIds)
Sets the PIDs this PAT/PMT shall contain.
Definition remux.c:515
int GetPatPmt(uchar *Dest, int Size)
Writes the PAT and all PMT packets to Dest.
Definition remux.c:613
int MakeStream(uchar *Target, uchar Type, int Pid)
Definition remux.c:399
void GeneratePmtPid(int Vpid, int Ppid, int Tpid, const int *Apids, const int *Dpids, const int *Spids)
Generates a PMT pid that doesn't collide with any of the given pids.
Definition remux.c:471
int MakeSubtitlingDescriptor(uchar *Target, const char *Language, uchar SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId)
Definition remux.c:420
void IncVersion(int &Version)
Definition remux.c:384
bool GetVersions(int &PatVersion, int &PmtVersion) const
Returns true if a valid PAT/PMT has been parsed and stores the current version numbers in the given v...
Definition remux.c:959
int dpids[MAXDPIDS+1]
Definition remux.h:370
uchar pmt[MAX_SECTION_SIZE]
Definition remux.h:359
int apids[MAXAPIDS+1]
Definition remux.h:367
cPatPmtParser(bool UpdatePrimaryDevice=false)
Definition remux.c:632
void Reset(void)
Resets the parser.
Definition remux.c:638
void ParsePat(const uchar *Data, int Length)
Parses the PAT data from the single TS packet in Data.
Definition remux.c:648
int pmtSize
Definition remux.h:360
char dlangs[MAXDPIDS][MAXLANGCODE2]
Definition remux.h:372
bool ParsePatPmt(const uchar *Data, int Length)
Parses the given Data (which may consist of several TS packets, typically an entire frame) and extrac...
Definition remux.c:940
int patVersion
Definition remux.h:361
int pmtPids[MAX_PMT_PIDS+1]
Definition remux.h:363
uchar subtitlingTypes[MAXSPIDS]
Definition remux.h:375
int dtypes[MAXDPIDS+1]
Definition remux.h:371
uint16_t ancillaryPageIds[MAXSPIDS]
Definition remux.h:377
int SectionLength(const uchar *Data, int Length)
Definition remux.h:381
void ParsePmt(const uchar *Data, int Length)
Parses the PMT data from the single TS packet in Data.
Definition remux.c:680
bool completed
Definition remux.h:379
bool IsPmtPid(int Pid) const
Returns true if Pid the one of the PMT pids as defined by the current PAT.
Definition remux.h:404
uint16_t compositionPageIds[MAXSPIDS]
Definition remux.h:376
char alangs[MAXAPIDS][MAXLANGCODE2]
Definition remux.h:369
int spids[MAXSPIDS+1]
Definition remux.h:373
int pmtVersion
Definition remux.h:362
bool updatePrimaryDevice
Definition remux.h:378
char slangs[MAXSPIDS][MAXLANGCODE2]
Definition remux.h:374
int atypes[MAXAPIDS+1]
Definition remux.h:368
bool NewMissing(void)
Definition remux.c:2140
void SetFrameDelta(int FrameDelta)
Definition remux.c:2058
bool iFrameNoPts
Definition remux.c:2051
int frameDelta
Definition remux.c:2052
void Clear(void)
Definition remux.c:2071
void Process(void)
Definition remux.c:2079
int totalMissing
Definition remux.c:2053
int64_t lastPts
Definition remux.c:2050
int oldMissing
Definition remux.c:2054
int Missing(void)
Definition remux.c:2147
cPtsChecker(void)
Definition remux.c:2065
cVector< int64_t > pts
Definition remux.c:2049
void AddPts(int64_t Pts, bool IndependentFrame=false)
Definition remux.c:2121
static void SetBrokenLink(uchar *Data, int Length)
Definition remux.c:102
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1242
uchar counter[MAXPID]
Definition remux.c:1972
cTsChecker(void)
Definition remux.c:1985
void CheckTs(const uchar *Data, int Length)
Definition remux.c:2016
int errors
Definition remux.c:1973
int Errors(void)
Definition remux.c:1980
void Reset(void)
Definition remux.c:1991
void Report(int Pid, const char *Message)
Definition remux.c:2002
void Clear(void)
Definition remux.c:1996
int oldErrors
Definition remux.c:1974
bool NewErrors(void)
Definition remux.c:2009
int numPacketsPid
Definition remux.h:232
int pid
Definition remux.h:230
cTsPayload(void)
Definition remux.c:248
bool AtPayloadStart(void)
Returns true if this payload handler is currently pointing to the first byte of a TS packet that star...
Definition remux.h:252
int Used(void)
Returns the number of raw bytes that have already been used (e.g.
Definition remux.h:258
bool Eof(void) const
Returns true if all available bytes of the TS payload have been processed.
Definition remux.h:262
void SetByte(uchar Byte, int Index)
Sets the TS data byte at the given Index to the value Byte.
Definition remux.c:332
uchar GetByte(void)
Gets the next byte of the TS payload, skipping any intermediate TS header data.
Definition remux.c:282
bool AtTsStart(void)
Returns true if this payload handler is currently pointing to first byte of a TS packet.
Definition remux.h:249
int GetLastIndex(void)
Returns the index into the TS data of the payload byte that has most recently been read.
Definition remux.c:327
void Setup(uchar *Data, int Length, int Pid=-1)
Sets up this TS payload handler with the given Data, which points to a sequence of Length bytes of co...
Definition remux.c:274
bool SkipPesHeader(void)
Skips all bytes belonging to the PES header of the payload.
Definition remux.c:322
int index
Definition remux.h:231
int numPacketsOther
Definition remux.h:233
void Statistics(void) const
May be called after a new frame has been detected, and will log a warning if the number of TS packets...
Definition remux.c:355
uchar SetEof(void)
Definition remux.c:261
int length
Definition remux.h:229
bool Find(uint32_t Code)
Searches for the four byte sequence given in Code and returns true if it was found within the payload...
Definition remux.c:338
void Reset(void)
Definition remux.c:267
uchar * data
Definition remux.h:228
bool SkipBytes(int Bytes)
Skips the given number of bytes in the payload and returns true if there is still data left to read.
Definition remux.c:315
int lastLength
Definition remux.h:461
bool repeatLast
Definition remux.h:462
uchar * lastData
Definition remux.h:460
uchar * data
Definition remux.h:456
void PutTs(const uchar *Data, int Length)
Puts the payload data of the single TS packet at Data into the converter.
Definition remux.c:1067
void SetRepeatLast(void)
Makes the next call to GetPes() return exactly the same data as the last one (provided there was no c...
Definition remux.c:1144
const uchar * GetPes(int &Length)
Gets a pointer to the complete PES packet, or NULL if the packet is not complete yet.
Definition remux.c:1096
cTsToPes(void)
Definition remux.c:1055
int length
Definition remux.h:458
~cTsToPes()
Definition remux.c:1062
void Reset(void)
Resets the converter.
Definition remux.c:1149
int offset
Definition remux.h:459
int size
Definition remux.h:457
int Size(void) const
Definition tools.h:773
void Sort(__compar_fn_t Compare)
Definition tools.h:834
virtual void Remove(int Index, int Count=1)
Definition tools.h:808
virtual void Append(T Data)
Definition tools.h:794
@ ttSubtitle
Definition device.h:70
@ ttDolby
Definition device.h:67
@ ttAudio
Definition device.h:64
const char * I18nNormalizeLanguageCode(const char *Code)
Returns a 3 letter language code that may not be zero terminated.
Definition i18n.c:300
@ EnhancedAC3DescriptorTag
Definition si.h:136
@ SubtitlingDescriptorTag
Definition si.h:102
@ ISO639LanguageDescriptorTag
Definition si.h:60
@ ParentalRatingDescriptorTag
Definition si.h:98
@ AC3DescriptorTag
Definition si.h:119
#define DEFAULTFRAMESPERSECOND
Definition recording.h:408
void TsSetPcr(uchar *p, int64_t Pcr)
Definition remux.c:131
const char * AspectRatioTexts[]
Definition remux.c:2190
#define WRN_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION
Definition remux.c:27
#define dbgframes(a...)
Definition remux.c:24
#define WRN_TS_PACKETS_FOR_FRAME_DETECTOR
Definition remux.c:28
#define SETPID(p)
void PesDump(const char *Name, const u_char *Data, int Length)
Definition remux.c:1185
static bool DebugFrames
Definition remux.c:21
int64_t PtsDiff(int64_t Pts1, int64_t Pts2)
Returns the difference between two PTS values.
Definition remux.c:236
const char * ScanTypeChars
Definition remux.c:2189
#define MAXPESLENGTH
Definition remux.c:1094
#define P_PMT_PID
Definition remux.c:468
void TsHidePayload(uchar *p)
Definition remux.c:121
static int CmpUint32(const void *p1, const void *p2)
Definition remux.c:2227
#define MAXPID
Definition remux.c:469
void PesSetDts(uchar *p, int64_t Dts)
Definition remux.c:227
#define EMPTY_SCANNER
Definition remux.c:30
static bool DebugPatPmt
Definition remux.c:20
static bool DebugChecks
Definition remux.c:1957
int64_t TsGetDts(const uchar *p, int l)
Definition remux.c:175
#define TS_CC_UNKNOWN
Definition remux.c:1968
static int ComparePts(const void *a, const void *b)
Definition remux.c:2043
void TsSetDts(uchar *p, int l, int64_t Dts)
Definition remux.c:202
void TsSetPts(uchar *p, int l, int64_t Pts)
Definition remux.c:188
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
Definition remux.c:32
#define dbgpatpmt(a...)
Definition remux.c:23
#define VIDEO_STREAM_S
Definition remux.c:98
void PesSetPts(uchar *p, int64_t Pts)
Definition remux.c:218
void BlockDump(const char *Name, const u_char *Data, int Length)
Definition remux.c:1159
int TsSync(const uchar *Data, int Length, const char *File, const char *Function, int Line)
Definition remux.c:147
#define SETPIDS(l)
#define P_TSID
Definition remux.c:467
void TsDump(const char *Name, const u_char *Data, int Length)
Definition remux.c:1170
int64_t TsGetPts(const uchar *p, int l, int Pid)
Definition remux.c:160
#define MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION
Definition remux.c:26
bool TsError(const uchar *p)
Definition remux.h:77
#define TS_ADAPT_PCR
Definition remux.h:46
int TsPid(const uchar *p)
Definition remux.h:82
bool TsHasPayload(const uchar *p)
Definition remux.h:62
#define MAX33BIT
Definition remux.h:59
#define MAX_PMT_PIDS
Definition remux.h:355
int PesPayloadOffset(const uchar *p)
Definition remux.h:178
#define PATPID
Definition remux.h:52
bool TsIsScrambled(const uchar *p)
Definition remux.h:93
uchar TsContinuityCounter(const uchar *p)
Definition remux.h:98
int TsGetPayload(const uchar **p)
Definition remux.h:114
#define TS_PAYLOAD_EXISTS
Definition remux.h:41
bool PesHasPts(const uchar *p)
Definition remux.h:183
bool PesLongEnough(int Length)
Definition remux.h:163
#define TS_SIZE
Definition remux.h:34
eAspectRatio
Definition remux.h:518
@ ar_16_9
Definition remux.h:522
@ ar_1_1
Definition remux.h:520
@ arUnknown
Definition remux.h:519
@ ar_4_3
Definition remux.h:521
@ ar_2_21_1
Definition remux.h:523
eScanType
Definition remux.h:511
@ stInterlaced
Definition remux.h:514
@ stProgressive
Definition remux.h:513
@ stUnknown
Definition remux.h:512
#define MAX_SECTION_SIZE
Definition remux.h:295
int64_t PesGetDts(const uchar *p)
Definition remux.h:202
#define TS_ADAPT_FIELD_EXISTS
Definition remux.h:40
int64_t PesGetPts(const uchar *p)
Definition remux.h:193
bool TsPayloadStart(const uchar *p)
Definition remux.h:72
#define TS_SYNC(Data, Length)
Definition remux.h:149
int TsPayloadOffset(const uchar *p)
Definition remux.h:108
#define MAXPID
Definition remux.h:55
bool PesHasDts(const uchar *p)
Definition remux.h:188
#define PCRFACTOR
Definition remux.h:58
#define TS_SYNC_BYTE
Definition remux.h:33
int64_t TsGetPts(const uchar *p, int l, int Pid=-1)
Definition remux.c:160
bool PesHasLength(const uchar *p)
Definition remux.h:168
bool TsHasAdaptationField(const uchar *p)
Definition remux.h:67
#define PTSTICKS
Definition remux.h:57
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader=NULL)
Definition remux.c:32
#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR
Definition remux.h:507
int PesLength(const uchar *p)
Definition remux.h:173
ePesHeader
Definition remux.h:16
@ phMPEG2
Definition remux.h:20
@ phNeedMoreData
Definition remux.h:17
@ phInvalid
Definition remux.h:18
@ phMPEG1
Definition remux.h:19
#define EITPID
Definition remux.h:54
#define TS_CONT_CNT_MASK
Definition remux.h:42
#define TS_PAYLOAD_START
Definition remux.h:36
int64_t PtsAdd(int64_t Pts1, int64_t Pts2)
Adds the given PTS values, taking into account the 33bit wrap around.
Definition remux.h:216
cString TimeToString(time_t t)
Converts the given time to a string of the form "www mmm dd hh:mm:ss yyyy".
Definition tools.c:1318
char * strn0cpy(char *dest, const char *src, size_t n)
Definition tools.c:128
unsigned char uchar
Definition tools.h:31
#define dsyslog(a...)
Definition tools.h:37
T min(T a, T b)
Definition tools.h:63
T max(T a, T b)
Definition tools.h:64
#define esyslog(a...)
Definition tools.h:35
#define KILOBYTE(n)
Definition tools.h:44