vdr 2.7.9
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.22 2025/12/30 13:48:59 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)
161{
162 // Find the first packet with a PTS and use it:
163 while (l > 0) {
164 const uchar *d = p;
165 if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasPts(d))
166 return PesGetPts(d);
167 p += TS_SIZE;
168 l -= TS_SIZE;
169 }
170 return -1;
171}
172
173int64_t TsGetDts(const uchar *p, int l)
174{
175 // Find the first packet with a DTS and use it:
176 while (l > 0) {
177 const uchar *d = p;
178 if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d))
179 return PesGetDts(d);
180 p += TS_SIZE;
181 l -= TS_SIZE;
182 }
183 return -1;
184}
185
186void TsSetPts(uchar *p, int l, int64_t Pts)
187{
188 // Find the first packet with a PTS and use it:
189 while (l > 0) {
190 const uchar *d = p;
191 if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasPts(d)) {
192 PesSetPts(const_cast<uchar *>(d), Pts);
193 return;
194 }
195 p += TS_SIZE;
196 l -= TS_SIZE;
197 }
198}
199
200void TsSetDts(uchar *p, int l, int64_t Dts)
201{
202 // Find the first packet with a DTS and use it:
203 while (l > 0) {
204 const uchar *d = p;
205 if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d)) {
206 PesSetDts(const_cast<uchar *>(d), Dts);
207 return;
208 }
209 p += TS_SIZE;
210 l -= TS_SIZE;
211 }
212}
213
214// --- Some PES handling tools -----------------------------------------------
215
216void PesSetPts(uchar *p, int64_t Pts)
217{
218 p[ 9] = ((Pts >> 29) & 0x0E) | (p[9] & 0xF1);
219 p[10] = Pts >> 22;
220 p[11] = ((Pts >> 14) & 0xFE) | 0x01;
221 p[12] = Pts >> 7;
222 p[13] = ((Pts << 1) & 0xFE) | 0x01;
223}
224
225void PesSetDts(uchar *p, int64_t Dts)
226{
227 p[14] = ((Dts >> 29) & 0x0E) | (p[14] & 0xF1);
228 p[15] = Dts >> 22;
229 p[16] = ((Dts >> 14) & 0xFE) | 0x01;
230 p[17] = Dts >> 7;
231 p[18] = ((Dts << 1) & 0xFE) | 0x01;
232}
233
234int64_t PtsDiff(int64_t Pts1, int64_t Pts2)
235{
236 int64_t d = Pts2 - Pts1;
237 if (d > MAX33BIT / 2)
238 return d - (MAX33BIT + 1);
239 if (d < -MAX33BIT / 2)
240 return d + (MAX33BIT + 1);
241 return d;
242}
243
244// --- cTsPayload ------------------------------------------------------------
245
247{
248 data = NULL;
249 length = 0;
250 pid = -1;
251 Reset();
252}
253
254cTsPayload::cTsPayload(uchar *Data, int Length, int Pid)
255{
256 Setup(Data, Length, Pid);
257}
258
260{
261 length = index; // triggers EOF
262 return 0x00;
263}
264
266{
267 index = 0;
268 numPacketsPid = 0;
269 numPacketsOther = 0;
270}
271
272void cTsPayload::Setup(uchar *Data, int Length, int Pid)
273{
274 data = Data;
275 length = Length;
276 pid = Pid >= 0 ? Pid : TsPid(Data);
277 Reset();
278}
279
281{
282 if (!Eof()) {
283 if (index % TS_SIZE == 0) { // encountered the next TS header
284 for (;; index += TS_SIZE) {
285 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
286 uchar *p = data + index;
287 if (TsPid(p) == pid) { // only handle TS packets for the initial PID
289 return SetEof();
290 if (TsError(p))
291 return SetEof(); // don't parse TS packets with errors
292 if (TsHasPayload(p)) {
293 if (index > 0 && TsPayloadStart(p)) // checking index to not skip the very first TS packet
294 return SetEof();
296 break;
297 }
298 }
299 else if (TsPid(p) == PATPID)
300 return SetEof(); // caller must see PAT packets in case of index regeneration
301 else
303 }
304 else
305 return SetEof();
306 }
307 }
308 return data[index++];
309 }
310 return 0x00;
311}
312
314{
315 while (Bytes-- > 0)
316 GetByte();
317 return !Eof();
318}
319
324
326{
327 return index - 1;
328}
329
330void cTsPayload::SetByte(uchar Byte, int Index)
331{
332 if (Index >= 0 && Index < length)
333 data[Index] = Byte;
334}
335
336bool cTsPayload::Find(uint32_t Code)
337{
338 int OldIndex = index;
339 int OldNumPacketsPid = numPacketsPid;
340 int OldNumPacketsOther = numPacketsOther;
341 uint32_t Scanner = EMPTY_SCANNER;
342 while (!Eof()) {
343 Scanner = (Scanner << 8) | GetByte();
344 if (Scanner == Code)
345 return true;
346 }
347 index = OldIndex;
348 numPacketsPid = OldNumPacketsPid;
349 numPacketsOther = OldNumPacketsOther;
350 return false;
351}
352
354{
356 dsyslog("WARNING: required (%d+%d) TS packets to determine frame type", numPacketsOther, numPacketsPid);
358 dsyslog("WARNING: required %d video TS packets to determine frame type", numPacketsPid);
359}
360
361void TsExtendAdaptionField(unsigned char *Packet, int ToLength)
362{
363 // Hint: ExtenAdaptionField(p, TsPayloadOffset(p) - 4) is a null operation
364
365 int Offset = TsPayloadOffset(Packet); // First byte after existing adaption field
366
367 if (ToLength <= 0)
368 {
369 // Remove adaption field
370 Packet[3] = Packet[3] & ~TS_ADAPT_FIELD_EXISTS;
371 return;
372 }
373
374 // Set adaption field present
375 Packet[3] = Packet[3] | TS_ADAPT_FIELD_EXISTS;
376
377 // Set new length of adaption field:
378 Packet[4] = ToLength <= TS_SIZE-4 ? ToLength-1 : TS_SIZE-4-1;
379
380 if (Packet[4] == TS_SIZE-4-1)
381 {
382 // No more payload, remove payload flag
383 Packet[3] = Packet[3] & ~TS_PAYLOAD_EXISTS;
384 }
385
386 int NewPayload = TsPayloadOffset(Packet); // First byte after new adaption field
387
388 // Fill new adaption field
389 if (Offset == 4 && Offset < NewPayload)
390 Offset++; // skip adaptation_field_length
391 if (Offset == 5 && Offset < NewPayload)
392 Packet[Offset++] = 0; // various flags set to 0
393 while (Offset < NewPayload)
394 Packet[Offset++] = 0xff; // stuffing byte
395}
396
397// --- cPatPmtGenerator ------------------------------------------------------
398
400{
401 numPmtPackets = 0;
404 pmtPid = 0;
405 esInfoLength = NULL;
406 SetChannel(Channel);
407}
408
409void cPatPmtGenerator::IncCounter(int &Counter, uchar *TsPacket)
410{
411 TsPacket[3] = (TsPacket[3] & 0xF0) | Counter;
412 if (++Counter > 0x0F)
413 Counter = 0x00;
414}
415
417{
418 if (++Version > 0x1F)
419 Version = 0x00;
420}
421
423{
424 if (esInfoLength) {
425 Length += ((*esInfoLength & 0x0F) << 8) | *(esInfoLength + 1);
426 *esInfoLength = 0xF0 | (Length >> 8);
427 *(esInfoLength + 1) = Length;
428 }
429}
430
431int cPatPmtGenerator::MakeStream(uchar *Target, uchar Type, int Pid)
432{
433 int i = 0;
434 Target[i++] = Type; // stream type
435 Target[i++] = 0xE0 | (Pid >> 8); // dummy (3), pid hi (5)
436 Target[i++] = Pid; // pid lo
437 esInfoLength = &Target[i];
438 Target[i++] = 0xF0; // dummy (4), ES info length hi
439 Target[i++] = 0x00; // ES info length lo
440 return i;
441}
442
444{
445 int i = 0;
446 Target[i++] = Type;
447 Target[i++] = 0x01; // length
448 Target[i++] = 0x00;
450 return i;
451}
452
453int cPatPmtGenerator::MakeSubtitlingDescriptor(uchar *Target, const char *Language, uchar SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId)
454{
455 int i = 0;
456 Target[i++] = SI::SubtitlingDescriptorTag;
457 Target[i++] = 0x08; // length
458 Target[i++] = *Language++;
459 Target[i++] = *Language++;
460 Target[i++] = *Language++;
461 Target[i++] = SubtitlingType;
462 Target[i++] = CompositionPageId >> 8;
463 Target[i++] = CompositionPageId & 0xFF;
464 Target[i++] = AncillaryPageId >> 8;
465 Target[i++] = AncillaryPageId & 0xFF;
467 return i;
468}
469
470int cPatPmtGenerator::MakeLanguageDescriptor(uchar *Target, const char *Language)
471{
472 int i = 0;
474 int Length = i++;
475 Target[Length] = 0x00; // length
476 for (const char *End = Language + strlen(Language); Language < End; ) {
477 Target[i++] = *Language++;
478 Target[i++] = *Language++;
479 Target[i++] = *Language++;
480 Target[i++] = 0x00; // audio type
481 Target[Length] += 0x04; // length
482 if (*Language == '+')
483 Language++;
484 }
486 return i;
487}
488
489int cPatPmtGenerator::MakeCRC(uchar *Target, const uchar *Data, int Length)
490{
491 int crc = SI::CRC32::crc32((const char *)Data, Length, 0xFFFFFFFF);
492 int i = 0;
493 Target[i++] = crc >> 24;
494 Target[i++] = crc >> 16;
495 Target[i++] = crc >> 8;
496 Target[i++] = crc;
497 return i;
498}
499
500#define P_TSID 0x8008 // pseudo TS ID
501#define P_PMT_PID 0x0084 // pseudo PMT pid
502#define MAXPID 0x2000 // the maximum possible number of pids
503
505{
506 bool Used[MAXPID] = { false };
507#define SETPID(p) { if ((p) >= 0 && (p) < MAXPID) Used[p] = true; }
508#define SETPIDS(l) { const int *p = l; while (*p) { SETPID(*p); p++; } }
509 SETPID(Channel->Vpid());
510 SETPID(Channel->Ppid());
511 SETPID(Channel->Tpid());
512 SETPIDS(Channel->Apids());
513 SETPIDS(Channel->Dpids());
514 SETPIDS(Channel->Spids());
515 for (pmtPid = P_PMT_PID; Used[pmtPid]; pmtPid++)
516 ;
517}
518
520{
521 memset(pat, 0xFF, sizeof(pat));
522 uchar *p = pat;
523 int i = 0;
524 p[i++] = TS_SYNC_BYTE; // TS indicator
525 p[i++] = TS_PAYLOAD_START | (PATPID >> 8); // flags (3), pid hi (5)
526 p[i++] = PATPID & 0xFF; // pid lo
527 p[i++] = 0x10; // flags (4), continuity counter (4)
528 p[i++] = 0x00; // pointer field (payload unit start indicator is set)
529 int PayloadStart = i;
530 p[i++] = 0x00; // table id
531 p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
532 int SectionLength = i;
533 p[i++] = 0x00; // section length lo (filled in later)
534 p[i++] = P_TSID >> 8; // TS id hi
535 p[i++] = P_TSID & 0xFF; // TS id lo
536 p[i++] = 0xC1 | (patVersion << 1); // dummy (2), version number (5), current/next indicator (1)
537 p[i++] = 0x00; // section number
538 p[i++] = 0x00; // last section number
539 p[i++] = pmtPid >> 8; // program number hi
540 p[i++] = pmtPid & 0xFF; // program number lo
541 p[i++] = 0xE0 | (pmtPid >> 8); // dummy (3), PMT pid hi (5)
542 p[i++] = pmtPid & 0xFF; // PMT pid lo
543 pat[SectionLength] = i - SectionLength - 1 + 4; // -1 = SectionLength storage, +4 = length of CRC
544 MakeCRC(pat + i, pat + PayloadStart, i - PayloadStart);
546}
547
549{
550 // generate the complete PMT section:
552 memset(buf, 0xFF, sizeof(buf));
553 numPmtPackets = 0;
554 if (Channel) {
555 int Vpid = Channel->Vpid();
556 int Ppid = Channel->Ppid();
557 uchar *p = buf;
558 int i = 0;
559 p[i++] = 0x02; // table id
560 int SectionLength = i;
561 p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
562 p[i++] = 0x00; // section length lo (filled in later)
563 p[i++] = pmtPid >> 8; // program number hi
564 p[i++] = pmtPid & 0xFF; // program number lo
565 p[i++] = 0xC1 | (pmtVersion << 1); // dummy (2), version number (5), current/next indicator (1)
566 p[i++] = 0x00; // section number
567 p[i++] = 0x00; // last section number
568 p[i++] = 0xE0 | (Ppid >> 8); // dummy (3), PCR pid hi (5)
569 p[i++] = Ppid; // PCR pid lo
570 p[i++] = 0xF0; // dummy (4), program info length hi (4)
571 p[i++] = 0x00; // program info length lo
572
573 if (Vpid)
574 i += MakeStream(buf + i, Channel->Vtype(), Vpid);
575 for (int n = 0; Channel->Apid(n); n++) {
576 i += MakeStream(buf + i, Channel->Atype(n), Channel->Apid(n));
577 const char *Alang = Channel->Alang(n);
578 i += MakeLanguageDescriptor(buf + i, Alang);
579 }
580 for (int n = 0; Channel->Dpid(n); n++) {
581 i += MakeStream(buf + i, 0x06, Channel->Dpid(n));
582 i += MakeAC3Descriptor(buf + i, Channel->Dtype(n));
583 i += MakeLanguageDescriptor(buf + i, Channel->Dlang(n));
584 }
585 for (int n = 0; Channel->Spid(n); n++) {
586 i += MakeStream(buf + i, 0x06, Channel->Spid(n));
587 i += MakeSubtitlingDescriptor(buf + i, Channel->Slang(n), Channel->SubtitlingType(n), Channel->CompositionPageId(n), Channel->AncillaryPageId(n));
588 }
589
590 int sl = i - SectionLength - 2 + 4; // -2 = SectionLength storage, +4 = length of CRC
591 buf[SectionLength] |= (sl >> 8) & 0x0F;
592 buf[SectionLength + 1] = sl;
593 MakeCRC(buf + i, buf, i);
594 // split the PMT section into several TS packets:
595 uchar *q = buf;
596 bool pusi = true;
597 while (i > 0) {
598 uchar *p = pmt[numPmtPackets++];
599 int j = 0;
600 p[j++] = TS_SYNC_BYTE; // TS indicator
601 p[j++] = (pusi ? TS_PAYLOAD_START : 0x00) | (pmtPid >> 8); // flags (3), pid hi (5)
602 p[j++] = pmtPid & 0xFF; // pid lo
603 p[j++] = 0x10; // flags (4), continuity counter (4)
604 if (pusi) {
605 p[j++] = 0x00; // pointer field (payload unit start indicator is set)
606 pusi = false;
607 }
608 int l = TS_SIZE - j;
609 memcpy(p + j, q, l);
610 q += l;
611 i -= l;
612 }
614 }
615}
616
617void cPatPmtGenerator::SetVersions(int PatVersion, int PmtVersion)
618{
619 patVersion = PatVersion & 0x1F;
620 pmtVersion = PmtVersion & 0x1F;
621}
622
624{
625 if (Channel) {
626 GeneratePmtPid(Channel);
627 GeneratePat();
628 GeneratePmt(Channel);
629 }
630}
631
633{
635 return pat;
636}
637
639{
640 if (Index < numPmtPackets) {
641 IncCounter(pmtCounter, pmt[Index]);
642 return pmt[Index++];
643 }
644 return NULL;
645}
646
647// --- cPatPmtParser ---------------------------------------------------------
648
649cPatPmtParser::cPatPmtParser(bool UpdatePrimaryDevice)
650{
651 updatePrimaryDevice = UpdatePrimaryDevice;
652 Reset();
653}
654
656{
657 completed = false;
658 pmtSize = 0;
659 patVersion = pmtVersion = -1;
660 pmtPids[0] = 0;
661 vpid = vtype = 0;
662 ppid = 0;
663}
664
665void cPatPmtParser::ParsePat(const uchar *Data, int Length)
666{
667 // Unpack the TS packet:
668 int PayloadOffset = TsPayloadOffset(Data);
669 Data += PayloadOffset;
670 Length -= PayloadOffset;
671 // The PAT is always assumed to fit into a single TS packet
672 if ((Length -= Data[0] + 1) <= 0)
673 return;
674 Data += Data[0] + 1; // process pointer_field
675 SI::PAT Pat(Data, false);
676 if (Pat.CheckCRCAndParse()) {
677 dbgpatpmt("PAT: TSid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pat.getTransportStreamId(), Pat.getCurrentNextIndicator(), Pat.getVersionNumber(), Pat.getSectionNumber(), Pat.getLastSectionNumber());
678 if (patVersion == Pat.getVersionNumber())
679 return;
680 int NumPmtPids = 0;
682 for (SI::Loop::Iterator it; Pat.associationLoop.getNext(assoc, it); ) {
683 dbgpatpmt(" isNITPid = %d\n", assoc.isNITPid());
684 if (!assoc.isNITPid()) {
685 if (NumPmtPids <= MAX_PMT_PIDS)
686 pmtPids[NumPmtPids++] = assoc.getPid();
687 dbgpatpmt(" service id = %d, pid = %d\n", assoc.getServiceId(), assoc.getPid());
688 }
689 }
690 pmtPids[NumPmtPids] = 0;
692 }
693 else
694 esyslog("ERROR: can't parse PAT");
695}
696
697void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
698{
699 // Unpack the TS packet:
700 bool PayloadStart = TsPayloadStart(Data);
701 int PayloadOffset = TsPayloadOffset(Data);
702 Data += PayloadOffset;
703 Length -= PayloadOffset;
704 // The PMT may extend over several TS packets, so we need to assemble them
705 if (PayloadStart) {
706 pmtSize = 0;
707 if ((Length -= Data[0] + 1) <= 0)
708 return;
709 Data += Data[0] + 1; // this is the first packet
710 if (SectionLength(Data, Length) > Length) {
711 if (Length <= int(sizeof(pmt))) {
712 memcpy(pmt, Data, Length);
713 pmtSize = Length;
714 }
715 else
716 esyslog("ERROR: PMT packet length too big (%d byte)!", Length);
717 return;
718 }
719 // the packet contains the entire PMT section, so we run into the actual parsing
720 }
721 else if (pmtSize > 0) {
722 // this is a following packet, so we add it to the pmt storage
723 if (Length <= int(sizeof(pmt)) - pmtSize) {
724 memcpy(pmt + pmtSize, Data, Length);
725 pmtSize += Length;
726 }
727 else {
728 esyslog("ERROR: PMT section length too big (%d byte)!", pmtSize + Length);
729 pmtSize = 0;
730 }
732 return; // more packets to come
733 // the PMT section is now complete, so we run into the actual parsing
734 Data = pmt;
735 }
736 else
737 return; // fragment of broken packet - ignore
738 SI::PMT Pmt(Data, false);
739 if (Pmt.CheckCRCAndParse()) {
740 dbgpatpmt("PMT: sid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pmt.getServiceId(), Pmt.getCurrentNextIndicator(), Pmt.getVersionNumber(), Pmt.getSectionNumber(), Pmt.getLastSectionNumber());
741 dbgpatpmt(" pcr = %d\n", Pmt.getPCRPid());
742 if (pmtVersion == Pmt.getVersionNumber())
743 return;
746 int NumApids = 0;
747 int NumDpids = 0;
748 int NumSpids = 0;
749 vpid = vtype = 0;
750 ppid = 0;
751 apids[0] = 0;
752 dpids[0] = 0;
753 spids[0] = 0;
754 atypes[0] = 0;
755 dtypes[0] = 0;
756 SI::PMT::Stream stream;
757 for (SI::Loop::Iterator it; Pmt.streamLoop.getNext(stream, it); ) {
758 dbgpatpmt(" stream type = %02X, pid = %d", stream.getStreamType(), stream.getPid());
759 switch (stream.getStreamType()) {
760 case 0x01: // STREAMTYPE_11172_VIDEO
761 case 0x02: // STREAMTYPE_13818_VIDEO
762 case 0x1B: // H.264
763 case 0x24: // H.265
764 vpid = stream.getPid();
765 vtype = stream.getStreamType();
766 ppid = Pmt.getPCRPid();
767 break;
768 case 0x03: // STREAMTYPE_11172_AUDIO
769 case 0x04: // STREAMTYPE_13818_AUDIO
770 case 0x0F: // ISO/IEC 13818-7 Audio with ADTS transport syntax
771 case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
772 {
773 if (NumApids < MAXAPIDS) {
774 apids[NumApids] = stream.getPid();
775 atypes[NumApids] = stream.getStreamType();
776 *alangs[NumApids] = 0;
778 for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
779 switch (d->getDescriptorTag()) {
783 char *s = alangs[NumApids];
784 int n = 0;
785 for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) {
786 if (*ld->languageCode != '-') { // some use "---" to indicate "none"
787 dbgpatpmt(" '%s'", l.languageCode);
788 if (n > 0)
789 *s++ = '+';
791 s += strlen(s);
792 if (n++ > 1)
793 break;
794 }
795 }
796 }
797 break;
798 default: ;
799 }
800 delete d;
801 }
803 cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, NumApids, apids[NumApids], alangs[NumApids]);
804 NumApids++;
805 apids[NumApids] = 0;
806 }
807 }
808 break;
809 case 0x06: // STREAMTYPE_13818_PES_PRIVATE
810 {
811 int dpid = 0;
812 int dtype = 0;
813 char lang[MAXLANGCODE1] = "";
815 for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
816 switch (d->getDescriptorTag()) {
819 dbgpatpmt(" AC3");
820 dpid = stream.getPid();
821 dtype = d->getDescriptorTag();
822 break;
824 dbgpatpmt(" subtitling");
825 if (NumSpids < MAXSPIDS) {
826 spids[NumSpids] = stream.getPid();
827 *slangs[NumSpids] = 0;
828 subtitlingTypes[NumSpids] = 0;
829 compositionPageIds[NumSpids] = 0;
830 ancillaryPageIds[NumSpids] = 0;
833 char *s = slangs[NumSpids];
834 int n = 0;
835 for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) {
836 if (sub.languageCode[0]) {
837 dbgpatpmt(" '%s'", sub.languageCode);
838 subtitlingTypes[NumSpids] = sub.getSubtitlingType();
840 ancillaryPageIds[NumSpids] = sub.getAncillaryPageId();
841 if (n > 0)
842 *s++ = '+';
844 s += strlen(s);
845 if (n++ > 1)
846 break;
847 }
848 }
850 cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, spids[NumSpids], slangs[NumSpids]);
851 NumSpids++;
852 spids[NumSpids] = 0;
853 }
854 break;
857 dbgpatpmt(" '%s'", ld->languageCode);
859 }
860 break;
861 default: ;
862 }
863 delete d;
864 }
865 if (dpid) {
866 if (NumDpids < MAXDPIDS) {
867 dpids[NumDpids] = dpid;
868 dtypes[NumDpids] = dtype;
869 strn0cpy(dlangs[NumDpids], lang, sizeof(dlangs[NumDpids]));
870 if (updatePrimaryDevice && Setup.UseDolbyDigital)
871 cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, NumDpids, dpid, lang);
872 NumDpids++;
873 dpids[NumDpids] = 0;
874 }
875 }
876 }
877 break;
878 case 0x81: // STREAMTYPE_USER_PRIVATE - AC3 audio for ATSC and BD
879 case 0x82: // STREAMTYPE_USER_PRIVATE - DTS audio for BD
880 case 0x87: // eac3
881 {
882 dbgpatpmt(" %s",
883 stream.getStreamType() == 0x81 ? "AC3" :
884 stream.getStreamType() == 0x87 ? "AC3" :
885 stream.getStreamType() == 0x82 ? "DTS" : "");
886 char lang[MAXLANGCODE1] = { 0 };
888 for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
889 switch (d->getDescriptorTag()) {
892 dbgpatpmt(" '%s'", ld->languageCode);
894 }
895 break;
896 default: ;
897 }
898 delete d;
899 }
900 if (NumDpids < MAXDPIDS) {
901 dpids[NumDpids] = stream.getPid();
902 dtypes[NumDpids] = SI::AC3DescriptorTag;
903 strn0cpy(dlangs[NumDpids], lang, sizeof(dlangs[NumDpids]));
904 if (updatePrimaryDevice && Setup.UseDolbyDigital)
905 cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, NumDpids, stream.getPid(), lang);
906 NumDpids++;
907 dpids[NumDpids] = 0;
908 }
909 }
910 break;
911 case 0x90: // PGS subtitles for BD
912 {
913 dbgpatpmt(" subtitling");
914 char lang[MAXLANGCODE1] = { 0 };
916 for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
917 switch (d->getDescriptorTag()) {
920 dbgpatpmt(" '%s'", ld->languageCode);
922 if (NumSpids < MAXSPIDS) {
923 spids[NumSpids] = stream.getPid();
924 *slangs[NumSpids] = 0;
925 subtitlingTypes[NumSpids] = 0;
926 compositionPageIds[NumSpids] = 0;
927 ancillaryPageIds[NumSpids] = 0;
929 cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, stream.getPid(), lang);
930 NumSpids++;
931 spids[NumSpids] = 0;
932 }
933 }
934 break;
935 default: ;
936 }
937 delete d;
938 }
939 }
940 break;
941 default: ;
942 }
943 dbgpatpmt("\n");
947 }
948 }
950 completed = true;
951 }
952 else
953 esyslog("ERROR: can't parse PMT");
954 pmtSize = 0;
955}
956
957bool cPatPmtParser::ParsePatPmt(const uchar *Data, int Length)
958{
959 while (Length >= TS_SIZE) {
960 if (*Data != TS_SYNC_BYTE)
961 break; // just for safety
962 int Pid = TsPid(Data);
963 if (Pid == PATPID)
964 ParsePat(Data, TS_SIZE);
965 else if (IsPmtPid(Pid)) {
966 ParsePmt(Data, TS_SIZE);
967 if (patVersion >= 0 && pmtVersion >= 0)
968 return true;
969 }
970 Data += TS_SIZE;
971 Length -= TS_SIZE;
972 }
973 return false;
974}
975
976bool cPatPmtParser::GetVersions(int &PatVersion, int &PmtVersion) const
977{
978 PatVersion = patVersion;
979 PmtVersion = pmtVersion;
980 return patVersion >= 0 && pmtVersion >= 0;
981}
982
983// --- cEitGenerator ---------------------------------------------------------
984
986{
987 counter = 0;
988 version = 0;
989 if (Sid)
990 Generate(Sid);
991}
992
993uint16_t cEitGenerator::YMDtoMJD(int Y, int M, int D)
994{
995 int L = (M < 3) ? 1 : 0;
996 return 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
997}
998
1000{
1002 *p++ = 0x04; // descriptor length
1003 *p++ = '9'; // country code "902" ("All countries") -> EN 300 468 / 6.2.28; www.dvbservices.com/country_codes/index.php
1004 *p++ = '0';
1005 *p++ = '2';
1006 *p++ = ParentalRating;
1007 return p;
1008}
1009
1011{
1012 uchar *PayloadStart;
1013 uchar *SectionStart;
1014 uchar *DescriptorsStart;
1015 memset(eit, 0xFF, sizeof(eit));
1016 struct tm tm_r;
1017 time_t t = time(NULL) - 3600; // let's have the event start one hour in the past
1018 tm *tm = localtime_r(&t, &tm_r);
1019 uint16_t MJD = YMDtoMJD(tm->tm_year, tm->tm_mon + 1, tm->tm_mday);
1020 uchar *p = eit;
1021 // TS header:
1022 *p++ = TS_SYNC_BYTE;
1023 *p++ = TS_PAYLOAD_START;
1024 *p++ = EITPID;
1025 *p++ = 0x10 | (counter++ & 0x0F); // continuity counter
1026 *p++ = 0x00; // pointer field (payload unit start indicator is set)
1027 // payload:
1028 PayloadStart = p;
1029 *p++ = 0x4E; // TID present/following event on this transponder
1030 *p++ = 0xF0;
1031 *p++ = 0x00; // section length
1032 SectionStart = p;
1033 *p++ = Sid >> 8;
1034 *p++ = Sid & 0xFF;
1035 *p++ = 0xC1 | (version << 1);
1036 *p++ = 0x00; // section number
1037 *p++ = 0x00; // last section number
1038 *p++ = 0x00; // transport stream id
1039 *p++ = 0x00; // ...
1040 *p++ = 0x00; // original network id
1041 *p++ = 0x00; // ...
1042 *p++ = 0x00; // segment last section number
1043 *p++ = 0x4E; // last table id
1044 *p++ = 0x00; // event id
1045 *p++ = 0x01; // ...
1046 *p++ = MJD >> 8; // start time
1047 *p++ = MJD & 0xFF; // ...
1048 *p++ = tm->tm_hour; // ...
1049 *p++ = tm->tm_min; // ...
1050 *p++ = tm->tm_sec; // ...
1051 *p++ = 0x24; // duration (one day, should cover everything)
1052 *p++ = 0x00; // ...
1053 *p++ = 0x00; // ...
1054 *p++ = 0x90; // running status, free/CA mode
1055 *p++ = 0x00; // descriptors loop length
1056 DescriptorsStart = p;
1058 // fill in lengths:
1059 *(SectionStart - 1) = p - SectionStart + 4; // +4 = length of CRC
1060 *(DescriptorsStart - 1) = p - DescriptorsStart;
1061 // checksum
1062 int crc = SI::CRC32::crc32((char *)PayloadStart, p - PayloadStart, 0xFFFFFFFF);
1063 *p++ = crc >> 24;
1064 *p++ = crc >> 16;
1065 *p++ = crc >> 8;
1066 *p++ = crc;
1067 return eit;
1068}
1069
1070// --- cTsToPes --------------------------------------------------------------
1071
1073{
1074 data = NULL;
1075 size = 0;
1076 Reset();
1077}
1078
1080{
1081 free(data);
1082}
1083
1084void cTsToPes::PutTs(const uchar *Data, int Length)
1085{
1086 if (TsError(Data)) {
1087 Reset();
1088 return; // ignore packets with TEI set, and drop any PES data collected so far
1089 }
1090 if (TsPayloadStart(Data))
1091 Reset();
1092 else if (!size)
1093 return; // skip everything before the first payload start
1094 Length = TsGetPayload(&Data);
1095 if (length + Length > size) {
1096 int NewSize = max(KILOBYTE(2), length + Length);
1097 if (uchar *NewData = (uchar *)realloc(data, NewSize)) {
1098 data = NewData;
1099 size = NewSize;
1100 }
1101 else {
1102 esyslog("ERROR: out of memory");
1103 Reset();
1104 return;
1105 }
1106 }
1107 memcpy(data + length, Data, Length);
1108 length += Length;
1109}
1110
1111#define MAXPESLENGTH 0xFFF0
1112
1113const uchar *cTsToPes::GetPes(int &Length)
1114{
1115 if (repeatLast) {
1116 repeatLast = false;
1117 Length = lastLength;
1118 return lastData;
1119 }
1120 if (offset < length && PesLongEnough(length)) {
1121 if (!PesHasLength(data)) // this is a video PES packet with undefined length
1122 offset = 6; // trigger setting PES length for initial slice
1123 if (offset) {
1124 uchar *p = data + offset - 6;
1125 if (p != data) {
1126 p -= 3;
1127 if (p < data) {
1128 Reset();
1129 return NULL;
1130 }
1131 memmove(p, data, 4);
1132 }
1133 int l = min(length - offset, MAXPESLENGTH);
1134 offset += l;
1135 if (p != data) {
1136 l += 3;
1137 p[6] = 0x80;
1138 p[7] = 0x00;
1139 p[8] = 0x00;
1140 }
1141 p[4] = l / 256;
1142 p[5] = l & 0xFF;
1143 Length = l + 6;
1144 lastLength = Length;
1145 lastData = p;
1146 return p;
1147 }
1148 else {
1149 Length = PesLength(data);
1150 if (Length <= length) {
1151 offset = Length; // to make sure we break out in case of garbage data
1152 lastLength = Length;
1153 lastData = data;
1154 return data;
1155 }
1156 }
1157 }
1158 return NULL;
1159}
1160
1162{
1163 repeatLast = true;
1164}
1165
1167{
1168 length = offset = 0;
1169 lastData = NULL;
1170 lastLength = 0;
1171 repeatLast = false;
1172}
1173
1174// --- Some helper functions for debugging -----------------------------------
1175
1176void BlockDump(const char *Name, const u_char *Data, int Length)
1177{
1178 printf("--- %s\n", Name);
1179 for (int i = 0; i < Length; i++) {
1180 if (i && (i % 16) == 0)
1181 printf("\n");
1182 printf(" %02X", Data[i]);
1183 }
1184 printf("\n");
1185}
1186
1187void TsDump(const char *Name, const u_char *Data, int Length)
1188{
1189 printf("%s: %04X", Name, Length);
1190 int n = min(Length, 20);
1191 for (int i = 0; i < n; i++)
1192 printf(" %02X", Data[i]);
1193 if (n < Length) {
1194 printf(" ...");
1195 n = max(n, Length - 10);
1196 for (n = max(n, Length - 10); n < Length; n++)
1197 printf(" %02X", Data[n]);
1198 }
1199 printf("\n");
1200}
1201
1202void PesDump(const char *Name, const u_char *Data, int Length)
1203{
1204 TsDump(Name, Data, Length);
1205}
1206
1207// --- cFrameParser ----------------------------------------------------------
1208
1210protected:
1211 bool debug;
1215 uint16_t frameWidth;
1216 uint16_t frameHeight;
1220public:
1221 cFrameParser(void);
1222 virtual ~cFrameParser() {};
1223 virtual int Parse(const uchar *Data, int Length, int Pid) = 0;
1230 void SetDebug(bool Debug) { debug = Debug; }
1231 bool NewFrame(void) { return newFrame; }
1232 bool IndependentFrame(void) { return independentFrame; }
1234 uint16_t FrameWidth(void) { return frameWidth; }
1235 uint16_t FrameHeight(void) { return frameHeight; }
1236 double FramesPerSecond(void) { return framesPerSecond; }
1237 eScanType ScanType(void) { return scanType; }
1239 };
1240
1242{
1243 debug = true;
1244 newFrame = false;
1245 independentFrame = false;
1247 frameWidth = 0;
1248 frameHeight = 0;
1249 framesPerSecond = 0.0;
1252}
1253
1254// --- cAudioParser ----------------------------------------------------------
1255
1257public:
1258 cAudioParser(void);
1259 virtual int Parse(const uchar *Data, int Length, int Pid) override;
1260 };
1261
1265
1266int cAudioParser::Parse(const uchar *Data, int Length, int Pid)
1267{
1268 if (TsPayloadStart(Data)) {
1269 newFrame = independentFrame = true;
1270 if (debug)
1271 dbgframes("/");
1272 }
1273 else
1274 newFrame = independentFrame = false;
1275 return TS_SIZE;
1276}
1277
1278// --- cMpeg2Parser ----------------------------------------------------------
1279
1281private:
1282 uint32_t scanner;
1286 const double frame_rate_table[9] = {
1287 0, // 0 forbidden
1288 24000./1001., // 1 23.976...
1289 24., // 2 24
1290 25., // 3 25
1291 30000./1001., // 4 29.97...
1292 30., // 5 30
1293 50., // 6 50
1294 60000./1001., // 7 59.94...
1295 60. // 8 60
1296 };
1297public:
1298 cMpeg2Parser(void);
1299 virtual int Parse(const uchar *Data, int Length, int Pid) override;
1300 };
1301
1303{
1305 seenIndependentFrame = false;
1306 lastIFrameTemporalReference = -1; // invalid
1307 seenScanType = false;
1308}
1309
1310int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
1311{
1312 newFrame = independentFrame = false;
1313 bool SeenPayloadStart = false;
1314 cTsPayload tsPayload(const_cast<uchar *>(Data), Length, Pid);
1315 if (TsPayloadStart(Data)) {
1316 SeenPayloadStart = true;
1317 tsPayload.SkipPesHeader();
1320 dbgframes("/");
1321 }
1322 uint32_t OldScanner = scanner; // need to remember it in case of multiple frames per payload
1323 for (;;) {
1324 if (!SeenPayloadStart && tsPayload.AtTsStart())
1325 OldScanner = scanner;
1326 scanner = (scanner << 8) | tsPayload.GetByte();
1327 if (scanner == 0x00000100) { // Picture Start Code
1328 if (!SeenPayloadStart && tsPayload.GetLastIndex() > TS_SIZE) {
1329 scanner = OldScanner;
1330 return tsPayload.Used() - TS_SIZE;
1331 }
1332 uchar b1 = tsPayload.GetByte();
1333 uchar b2 = tsPayload.GetByte();
1334 int TemporalReference = (b1 << 2 ) + ((b2 & 0xC0) >> 6);
1335 uchar FrameType = (b2 >> 3) & 0x07;
1336 if (tsPayload.Find(0x000001B5)) { // Extension start code
1337 if (((tsPayload.GetByte() & 0xF0) >> 4) == 0x08) { // Picture coding extension
1338 tsPayload.GetByte();
1339 uchar PictureStructure = tsPayload.GetByte() & 0x03;
1340 if (PictureStructure == 0x02) // bottom field
1341 break;
1342 }
1343 }
1344 newFrame = true;
1345 independentFrame = FrameType == 1; // I-Frame
1346 if (independentFrame) {
1349 lastIFrameTemporalReference = TemporalReference;
1350 }
1351 if (debug) {
1354 static const char FrameTypes[] = "?IPBD???";
1355 dbgframes("%c", FrameTypes[FrameType]);
1356 }
1357 }
1358 tsPayload.Statistics();
1359 break;
1360 }
1361 else if (frameWidth == 0 && scanner == 0x000001B3) { // Sequence header code
1362 frameWidth = tsPayload.GetByte() << 4;
1363 uchar b = tsPayload.GetByte(); // ignoring two MSB of width and height in sequence extension
1364 frameWidth |= b >> 4; // as 12 Bit = max 4095 should be sufficient for all available MPEG2 streams
1365 frameHeight = (b & 0x0F) << 8 | tsPayload.GetByte();
1366 b = tsPayload.GetByte(); // hi: aspect ratio info, lo: frame rate code
1367 switch (b >> 4) {
1368 case 1: aspectRatio = ar_1_1; break;
1369 case 2: aspectRatio = ar_4_3; break;
1370 case 3: aspectRatio = ar_16_9; break;
1371 case 4: aspectRatio = ar_2_21_1; break;
1372 default: aspectRatio = arUnknown;
1373 }
1374 uchar frame_rate_value = b & 0x0F;
1375 if (frame_rate_value > 0 && frame_rate_value <= 8)
1376 framesPerSecond = frame_rate_table[frame_rate_value];
1377 }
1378 else if (!seenScanType && scanner == 0x000001B5) { // Extension start code
1379 if ((tsPayload.GetByte() & 0xF0) == 0x10) { // Sequence Extension
1380 scanType = (tsPayload.GetByte() & 0x40) ? stProgressive : stInterlaced;
1381 seenScanType = true;
1382 if (debug) {
1384 dsyslog("%s", *s);
1385 dbgframes("\n%s", *s);
1386 }
1387 }
1388 }
1389 if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
1390 || tsPayload.Eof()) // or if we're out of data
1391 break;
1392 }
1393 return tsPayload.Used();
1394}
1395
1396// --- cH264Parser -----------------------------------------------------------
1397
1399private:
1406 uchar byte; // holds the current byte value in case of bitwise access
1407 int bit; // the bit index into the current byte (-1 if we're not in bit reading mode)
1408 int zeroBytes; // the number of consecutive zero bytes (to detect 0x000003)
1409 // Identifiers written in '_' notation as in "ITU-T H.264":
1413protected:
1415 uint32_t scanner;
1418 uchar GetByte(bool Raw = false);
1422 uchar GetBit(void);
1423 uint32_t GetBits(int Bits);
1424 uint32_t GetGolombUe(void);
1425 int32_t GetGolombSe(void);
1426 void ParseAccessUnitDelimiter(void);
1427 void ParseSequenceParameterSet(void);
1428 void ParseSliceHeader(void);
1429public:
1430 cH264Parser(void);
1434 virtual int Parse(const uchar *Data, int Length, int Pid) override;
1435 };
1436
1438{
1439 byte = 0;
1440 bit = -1;
1441 zeroBytes = 0;
1445 frame_mbs_only_flag = false;
1446 gotAccessUnitDelimiter = false;
1448}
1449
1451{
1452 uchar b = tsPayload.GetByte();
1453 if (!Raw) {
1454 // If we encounter the byte sequence 0x000003, we need to skip the 0x03:
1455 if (b == 0x00)
1456 zeroBytes++;
1457 else {
1458 if (b == 0x03 && zeroBytes >= 2)
1459 b = tsPayload.GetByte();
1460 zeroBytes = b ? 0 : 1;
1461 }
1462 }
1463 else
1464 zeroBytes = 0;
1465 bit = -1;
1466 return b;
1467}
1468
1470{
1471 if (bit < 0) {
1472 byte = GetByte();
1473 bit = 7;
1474 }
1475 return (byte & (1 << bit--)) ? 1 : 0;
1476}
1477
1478uint32_t cH264Parser::GetBits(int Bits)
1479{
1480 uint32_t b = 0;
1481 while (Bits--)
1482 b |= GetBit() << Bits;
1483 return b;
1484}
1485
1487{
1488 int z = -1;
1489 for (int b = 0; !b && z < 32; z++) // limiting z to no get stuck if GetBit() always returns 0
1490 b = GetBit();
1491 return (1 << z) - 1 + GetBits(z);
1492}
1493
1495{
1496 uint32_t v = GetGolombUe();
1497 if (v) {
1498 if ((v & 0x01) != 0)
1499 return (v + 1) / 2; // fails for v == 0xFFFFFFFF, but that will probably never happen
1500 else
1501 return -int32_t(v / 2);
1502 }
1503 return v;
1504}
1505
1506int cH264Parser::Parse(const uchar *Data, int Length, int Pid)
1507{
1508 newFrame = independentFrame = false;
1509 tsPayload.Setup(const_cast<uchar *>(Data), Length, Pid);
1510 if (TsPayloadStart(Data)) {
1511 tsPayload.SkipPesHeader();
1514 dbgframes("/");
1515 }
1516 }
1517 for (;;) {
1518 scanner = (scanner << 8) | GetByte(true);
1519 if ((scanner & 0xFFFFFF00) == 0x00000100) { // NAL unit start
1520 uchar NalUnitType = scanner & 0x1F;
1521 switch (NalUnitType) {
1524 break;
1528 }
1529 break;
1533 gotAccessUnitDelimiter = false;
1534 if (newFrame)
1535 tsPayload.Statistics();
1536 return tsPayload.Used();
1537 }
1538 break;
1539 default: ;
1540 }
1541 }
1542 if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
1543 || tsPayload.Eof()) // or if we're out of data
1544 break;
1545 }
1546 return tsPayload.Used();
1547}
1548
1550{
1552 dbgframes("A");
1553 GetByte(); // primary_pic_type
1554}
1555
1557{
1558 int chroma_format_idc = 0;
1559 int bitDepth = 0;
1560 uchar profile_idc = GetByte(); // profile_idc
1561 GetByte(); // constraint_set[0-5]_flags, reserved_zero_2bits
1562 GetByte(); // level_idc
1563 GetGolombUe(); // seq_parameter_set_id
1564 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) {
1565 chroma_format_idc = GetGolombUe(); // chroma_format_idc
1566 if (chroma_format_idc == 3)
1568 bitDepth = 8 + GetGolombUe(); // bit_depth_luma_minus8
1569 GetGolombUe(); // bit_depth_chroma_minus8
1570 GetBit(); // qpprime_y_zero_transform_bypass_flag
1571 if (GetBit()) { // seq_scaling_matrix_present_flag
1572 for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); i++) {
1573 if (GetBit()) { // seq_scaling_list_present_flag
1574 int SizeOfScalingList = (i < 6) ? 16 : 64;
1575 int LastScale = 8;
1576 int NextScale = 8;
1577 for (int j = 0; j < SizeOfScalingList; j++) {
1578 if (NextScale)
1579 NextScale = (LastScale + GetGolombSe() + 256) % 256; // delta_scale
1580 if (NextScale)
1581 LastScale = NextScale;
1582 }
1583 }
1584 }
1585 }
1586 }
1587 log2_max_frame_num = GetGolombUe() + 4; // log2_max_frame_num_minus4
1588 int pic_order_cnt_type = GetGolombUe(); // pic_order_cnt_type
1589 if (pic_order_cnt_type == 0)
1590 GetGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
1591 else if (pic_order_cnt_type == 1) {
1592 GetBit(); // delta_pic_order_always_zero_flag
1593 GetGolombSe(); // offset_for_non_ref_pic
1594 GetGolombSe(); // offset_for_top_to_bottom_field
1595 for (int i = GetGolombUe(); i--; ) // num_ref_frames_in_pic_order_cnt_cycle
1596 GetGolombSe(); // offset_for_ref_frame
1597 }
1598 GetGolombUe(); // max_num_ref_frames
1599 GetBit(); // gaps_in_frame_num_value_allowed_flag
1600 uint16_t frame_Width = 16 * (1 + GetGolombUe()); // pic_width_in_mbs_minus1
1601 uint16_t frame_Height = 16 * (1 + GetGolombUe()); // pic_height_in_map_units_minus1
1602 frame_mbs_only_flag = GetBit(); // frame_mbs_only_flag
1603 if (frameWidth == 0) {
1605 if (!frame_mbs_only_flag) {
1606 GetBit(); // mb_adaptive_frame_field_flag
1607 frame_Height *= 2;
1608 }
1609 GetBit(); // direct_8x8_inference_flag
1610 bool frame_cropping_flag = GetBit(); // frame_cropping_flag
1611 if (frame_cropping_flag) {
1612 uint16_t frame_crop_left_offset = GetGolombUe(); // frame_crop_left_offset
1613 uint16_t frame_crop_right_offset = GetGolombUe(); // frame_crop_right_offset
1614 uint16_t frame_crop_top_offset = GetGolombUe(); // frame_crop_top_offset
1615 uint16_t frame_crop_bottom_offset = GetGolombUe(); // frame_crop_bottom_offset
1616 uint16_t CropUnitX = 1;
1617 uint16_t CropUnitY = frame_mbs_only_flag ? 1 : 2;
1618 if (!separate_colour_plane_flag && chroma_format_idc > 0) {
1619 if (chroma_format_idc == 1) {
1620 CropUnitX = 2;
1621 CropUnitY *= 2;
1622 }
1623 else if (chroma_format_idc == 2)
1624 CropUnitX = 2;
1625 }
1626 frame_Width -= CropUnitX * (frame_crop_left_offset + frame_crop_right_offset);
1627 frame_Height -= CropUnitY * (frame_crop_top_offset + frame_crop_bottom_offset);
1628 if (frame_Height > 1080 && frame_Height <= 1090) // workaround for channels with wrong crop parameters
1629 frame_Height = 1080;
1630 }
1631 frameWidth = frame_Width;
1632 frameHeight = frame_Height;
1633 // VUI parameters
1634 if (GetBit()) { // vui_parameters_present_flag
1635 if (GetBit()) { // aspect_ratio_info_present
1636 int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
1637 if (aspect_ratio_idc == 255) // EXTENDED_SAR
1638 GetBits(32); // sar_width, sar_height
1639 else if (frameHeight >= 720 && (aspect_ratio_idc == 1 || aspect_ratio_idc == 14 || aspect_ratio_idc == 15 || aspect_ratio_idc == 16))
1641 // implement decoding of other aspect_ratio_idc values when they are required
1642 }
1643 if (GetBit()) // overscan_info_present_flag
1644 GetBit(); // overscan_approriate_flag
1645 if (GetBit()) { // video_signal_type_present_flag
1646 GetBits(4); // video_format, video_full_range_flag
1647 if (GetBit()) // colour_description_present_flag
1648 GetBits(24); // colour_primaries, transfer_characteristics, matrix_coefficients
1649 }
1650 if (GetBit()) { // chroma_loc_info_present_flag
1651 GetGolombUe(); // chroma_sample_loc_type_top_field
1652 GetGolombUe(); // chroma_sample_loc_type_bottom_field
1653 }
1654 if (GetBit()) { // timing_info_present_flag
1655 uint32_t num_units_in_tick = GetBits(32); // num_units_in_tick
1656 uint32_t time_scale = GetBits(32); // time_scale
1657 if (num_units_in_tick > 0)
1658 framesPerSecond = double(time_scale) / (num_units_in_tick << 1);
1659 }
1660 }
1661 if (debug) {
1662 cString s = cString::sprintf("H.264: %d x %d%c %.2f fps %d Bit %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, bitDepth, AspectRatioTexts[aspectRatio]);
1663 dsyslog("%s", *s);
1664 dbgframes("\n%s", *s);
1665 }
1666 }
1667 if (debug) {
1669 dbgframes("A"); // just for completeness
1670 dbgframes(frame_mbs_only_flag ? "S" : "s");
1671 }
1672}
1673
1675{
1676 newFrame = true;
1677 GetGolombUe(); // first_mb_in_slice
1678 int slice_type = GetGolombUe(); // slice_type, 0 = P, 1 = B, 2 = I, 3 = SP, 4 = SI
1679 independentFrame = (slice_type % 5) == 2;
1680 if (debug) {
1681 static const char SliceTypes[] = "PBIpi";
1682 dbgframes("%c", SliceTypes[slice_type % 5]);
1683 }
1685 return; // don't need the rest - a frame is complete
1686 GetGolombUe(); // pic_parameter_set_id
1688 GetBits(2); // colour_plane_id
1689 GetBits(log2_max_frame_num); // frame_num
1690 if (!frame_mbs_only_flag) {
1691 if (GetBit()) // field_pic_flag
1692 newFrame = !GetBit(); // bottom_field_flag
1693 if (debug)
1694 dbgframes(newFrame ? "t" : "b");
1695 }
1696}
1697
1698// --- cH265Parser -----------------------------------------------------------
1699
1738
1740:cH264Parser()
1741{
1742}
1743
1744int cH265Parser::Parse(const uchar *Data, int Length, int Pid)
1745{
1746 newFrame = independentFrame = false;
1747 tsPayload.Setup(const_cast<uchar *>(Data), Length, Pid);
1748 if (TsPayloadStart(Data)) {
1749 tsPayload.SkipPesHeader();
1751 }
1752 for (;;) {
1753 scanner = (scanner << 8) | GetByte(true);
1754 if ((scanner & 0xFFFFFF00) == 0x00000100) { // NAL unit start
1755 uchar NalUnitType = (scanner >> 1) & 0x3F;
1756 GetByte(); // nuh_layer_id + nuh_temporal_id_plus1
1757 if (NalUnitType <= nutSliceSegmentRASLR || (NalUnitType >= nutSliceSegmentBLAWLP && NalUnitType <= nutSliceSegmentCRANUT)) {
1758 if (NalUnitType == nutSliceSegmentIDRWRADL || NalUnitType == nutSliceSegmentIDRNLP || NalUnitType == nutSliceSegmentCRANUT)
1759 independentFrame = true;
1760 if (GetBit()) { // first_slice_segment_in_pic_flag
1761 newFrame = true;
1762 tsPayload.Statistics();
1763 }
1764 break;
1765 }
1766 else if (frameWidth == 0 && NalUnitType == nutSequenceParameterSet) {
1769 }
1770 }
1771 if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
1772 || tsPayload.Eof()) // or if we're out of data
1773 break;
1774 }
1775 return tsPayload.Used();
1776}
1777
1779{
1781 uint8_t sub_layer_profile_present_flag[8];
1782 uint8_t sub_layer_level_present_flag[8];
1783 GetBits(4); // sps_video_parameter_set_id
1784 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)
1785 GetBit(); // sps_temporal_id_nesting_flag
1786 // begin profile_tier_level(1, sps_max_sub_layers_minus1)
1787 GetByte();
1788 GetByte();
1789 GetByte();
1790 GetByte();
1791 GetByte();
1792 bool general_progressive_source_flag = GetBit(); // general_progressive_source_flag
1793 scanType = general_progressive_source_flag ? stProgressive : stInterlaced;
1794 GetBit(); // general_interlaced_source_flag
1795 GetBits(6);
1796 GetByte();
1797 GetByte();
1798 GetByte();
1799 GetByte();
1800 GetByte();
1801 GetByte(); // general_level_idc
1802 for (int i = 0; i < sps_max_sub_layers_minus1; i++ ) {
1803 sub_layer_profile_present_flag[i] = GetBit(); // sub_layer_profile_present_flag[i]
1804 sub_layer_level_present_flag[i] = GetBit(); // sub_layer_level_present_flag[i]
1805 }
1806 if (sps_max_sub_layers_minus1 > 0) {
1807 for (int i = sps_max_sub_layers_minus1; i < 8; i++ )
1808 GetBits(2); // reserved_zero_2bits[i]
1809 }
1810 for (int i = 0; i < sps_max_sub_layers_minus1; i++ ) {
1811 if (sub_layer_profile_present_flag[i] )
1812 GetBits(88);
1813 if (sub_layer_level_present_flag[i])
1814 GetBits(8);
1815 }
1816 // end profile_tier_level
1817 GetGolombUe(); // sps_seq_parameter_set_id
1818 int chroma_format_idc = GetGolombUe(); // chroma_format_idc
1819 if (chroma_format_idc == 3)
1820 separate_colour_plane_flag = GetBit(); // separate_colour_plane_flag
1821 frameWidth = GetGolombUe(); // pic_width_in_luma_samples
1822 frameHeight = GetGolombUe(); // pic_height_in_luma_samples
1823 bool conformance_window_flag = GetBit(); // conformance_window_flag
1824 if (conformance_window_flag) {
1825 int conf_win_left_offset = GetGolombUe(); // conf_win_left_offset
1826 int conf_win_right_offset = GetGolombUe(); // conf_win_right_offset
1827 int conf_win_top_offset = GetGolombUe(); // conf_win_top_offset
1828 int conf_win_bottom_offset = GetGolombUe(); // conf_win_bottom_offset
1829 uint16_t SubWidthC = 1;
1830 uint16_t SubHeightC = 1;
1831 if (!separate_colour_plane_flag && chroma_format_idc > 0) {
1832 if (chroma_format_idc == 1) {
1833 SubWidthC = 2;
1834 SubHeightC = 2;
1835 }
1836 else if (chroma_format_idc == 2)
1837 SubWidthC = 2;
1838 }
1839 frameWidth -= SubWidthC * (conf_win_left_offset + conf_win_right_offset);
1840 frameHeight -= SubHeightC * (conf_win_top_offset + conf_win_bottom_offset);
1841 }
1842 int bitDepth = 8 + GetGolombUe(); // bit_depth_luma_minus8
1843 GetGolombUe(); // bit_depth_chroma_minus8
1844 int log2_max_pic_order_cnt_lsb_minus4 = GetGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
1845 int sps_sub_layer_ordering_info_present_flag = GetBit(); // sps_sub_layer_ordering_info_present_flag
1846 for (int i = sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1; i <= sps_max_sub_layers_minus1; ++i) {
1847 GetGolombUe(); // sps_max_dec_pic_buffering_minus1[i]
1848 GetGolombUe(); // sps_max_num_reorder_pics[i]
1849 GetGolombUe(); // sps_max_latency_increase_plus1[i]
1850 }
1851 GetGolombUe(); // log2_min_luma_coding_block_size_minus3
1852 GetGolombUe(); // log2_diff_max_min_luma_coding_block_size
1853 GetGolombUe(); // log2_min_luma_transform_block_size_minus2
1854 GetGolombUe(); // log2_diff_max_min_luma_transform_block_size
1855 GetGolombUe(); // max_transform_hierarchy_depth_inter
1856 GetGolombUe(); // max_transform_hierarchy_depth_intra
1857 if (GetBit()) { // scaling_list_enabled_flag
1858 if (GetBit()) { // sps_scaling_list_data_present_flag
1859 // begin scaling_list_data
1860 for (int sizeId = 0; sizeId < 4; ++sizeId) {
1861 for (int matrixId = 0; matrixId < 6; matrixId += (sizeId == 3) ? 3 : 1) {
1862 if (!GetBit()) // scaling_list_pred_mode_flag[sizeId][matrixId]
1863 GetGolombUe(); // scaling_list_pred_matrix_id_delta[sizeId][matrixId]
1864 else {
1865 int coefNum = min(64, (1 << (4 + (sizeId << 1))));
1866 if (sizeId > 1)
1867 GetGolombSe(); // scaling_list_dc_coef_minus8[sizeId−2][matrixId]
1868 for (int i = 0; i < coefNum; ++i)
1869 GetGolombSe(); // scaling_list_delta_coef
1870 }
1871 }
1872 }
1873 }
1874 // end scaling_list_data
1875 }
1876 GetBits(2); // amp_enabled_flag, sample_adaptive_offset_enabled_flag
1877 if (GetBit()) { // pcm_enabled_flag
1878 GetBits(8); // pcm_sample_bit_depth_luma_minus1, pcm_sample_bit_depth_chroma_minus1
1879 GetGolombUe(); // log2_min_pcm_luma_coding_block_size_minus3
1880 GetGolombUe(); // log2_diff_max_min_pcm_luma_coding_block_size
1881 GetBit(); // pcm_loop_filter_disabled_flag
1882 }
1883 uint32_t num_short_term_ref_pic_sets = GetGolombUe(); // num_short_term_ref_pic_sets
1884 uint32_t NumDeltaPocs[num_short_term_ref_pic_sets];
1885 for (uint32_t stRpsIdx = 0; stRpsIdx < num_short_term_ref_pic_sets; ++stRpsIdx) {
1886 // start of st_ref_pic_set(stRpsIdx)
1887 bool inter_ref_pic_set_prediction_flag = false;
1888 if (stRpsIdx != 0)
1889 inter_ref_pic_set_prediction_flag = GetBit(); // inter_ref_pic_set_prediction_flag
1890 if (inter_ref_pic_set_prediction_flag) {
1891 uint32_t RefRpsIdx, delta_idx_minus1 = 0;
1892 if (stRpsIdx == num_short_term_ref_pic_sets)
1893 delta_idx_minus1 = GetGolombUe(); // delta_idx_minus1
1894 GetBit(); // delta_rps_sign
1895 GetGolombUe(); // abs_delta_rps_minus1
1896 RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1);
1897 NumDeltaPocs[stRpsIdx] = 0;
1898 for (uint32_t j = 0; j <= NumDeltaPocs[RefRpsIdx]; ++j) {
1899 if (!GetBit()) { // used_by_curr_pic_flag[j]
1900 if (GetBit()) // use_delta_flag[j]
1901 NumDeltaPocs[stRpsIdx]++;
1902 }
1903 else
1904 NumDeltaPocs[stRpsIdx]++;
1905 }
1906 }
1907 else {
1908 uint32_t num_negative_pics = GetGolombUe(); // num_negative_pics
1909 uint32_t num_positive_pics = GetGolombUe(); // num_positive_pics
1910 for (uint32_t j = 0; j < num_negative_pics; ++j) {
1911 GetGolombUe(); // delta_poc_s0_minus1[i]
1912 GetBit(); // used_by_curr_pic_s0_flag[i]
1913 }
1914 for (uint32_t j = 0; j < num_positive_pics; ++j) {
1915 GetGolombUe(); // delta_poc_s1_minus1[i]
1916 GetBit(); // delta_poc_s1_minus1[i]
1917 }
1918 NumDeltaPocs[stRpsIdx] = num_negative_pics + num_positive_pics;
1919 }
1920 // end of st_ref_pic_set(stRpsIdx)
1921 }
1922 if (GetBit()) { // long_term_ref_pics_present_flag
1923 uint32_t num_long_term_ref_pics_sps = GetGolombUe(); // num_long_term_ref_pics_sps
1924 for (uint32_t i = 0; i < num_long_term_ref_pics_sps; ++i) {
1925 GetBits(log2_max_pic_order_cnt_lsb_minus4 + 4); // lt_ref_pic_poc_lsb_sps[i]
1926 GetBit(); // used_by_curr_pic_lt_sps_flag[i]
1927 }
1928 }
1929 GetBits(2); // sps_temporal_mvp_enabled_flag, strong_intra_smoothing_enabled_flag
1930 if (GetBit()) { // vui_parameters_present_flag
1931 // begin of vui_parameters()
1932 if (GetBit()) { // aspect_ratio_info_present_flag
1933 int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
1934 if (aspect_ratio_idc == 255) // EXTENDED_SAR
1935 GetBits(32); // sar_width, sar_height
1936 else if (aspect_ratio_idc == 1 || aspect_ratio_idc == 14)
1938 // implement decoding of other aspect_ratio_idc values when they are required
1939 }
1940 if (GetBit()) // overscan_info_present_flag
1941 GetBit(); // overscan_appropriate_flag
1942 if (GetBit()) { // video_signal_type_present_flag
1943 GetBits(4); // video_format, video_full_range_flag
1944 if (GetBit()) // colour_description_present_flag
1945 GetBits(24); // colour_primaries, transfer_characteristics, matrix_coeffs
1946 }
1947 if (GetBit()) { // chroma_loc_info_present_flag
1948 GetGolombUe(); // chroma_sample_loc_type_top_field
1949 GetGolombUe(); // chroma_sample_loc_type_bottom_field
1950 }
1951 GetBits(3); // neutral_chroma_indication_flag, field_seq_flag, frame_field_info_present_flag
1952 if (GetBit()) { // default_display_window_flag
1953 GetGolombUe(); // def_disp_win_left_offset
1954 GetGolombUe(); // def_disp_win_right_offset
1955 GetGolombUe(); // def_disp_win_top_offset
1956 GetGolombUe(); // def_disp_win_bottom_offset
1957 }
1958 if (GetBit()) { // vui_timing_info_present_flag
1959 uint32_t vui_num_units_in_tick = GetBits(32); // vui_num_units_in_tick
1960 uint32_t vui_time_scale = GetBits(32); // vui_time_scale
1961 if (vui_num_units_in_tick > 0)
1962 framesPerSecond = (double)vui_time_scale / vui_num_units_in_tick;
1963 }
1964 }
1965 if (debug) {
1966 cString s = cString::sprintf("H.265: %d x %d%c %.2f fps %d Bit %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, bitDepth, AspectRatioTexts[aspectRatio]);
1967 dsyslog("%s", *s);
1968 dbgframes("\n%s", *s);
1969 }
1970}
1971
1972static bool DebugChecks = false;
1973
1974// cTsChecker and cPtsChecker are used to detect errors in the recorded data stream.
1975// While cTsChecker checks the continuity counter of the incoming TS packets, cPtsChecker
1976// works on entire frames, checking their PTS (Presentation Time Stamps) to see whether
1977// all expected frames arrive. The resulting number of errors is not a precise value.
1978// If it is zero, the recording can be safely considered error free. The higher the value,
1979// the more damaged the recording is.
1980
1981// --- cTsChecker ------------------------------------------------------------
1982
1983#define TS_CC_UNKNOWN 0xFF
1984
1986private:
1990 void Report(int Pid, const char *Message);
1991public:
1992 cTsChecker(void);
1993 void Reset(void);
1994 void Clear(void);
1995 int Errors(void) { return errors; }
1996 bool NewErrors(void);
1997 void CheckTs(const uchar *Data, int Length);
1998 };
1999
2001{
2002 Reset();
2003 Clear();
2004}
2005
2007{
2008 memset(counter, TS_CC_UNKNOWN, sizeof(counter));
2009}
2010
2012{
2013 errors = 0;
2014 oldErrors = 0;
2015}
2016
2017void cTsChecker::Report(int Pid, const char *Message)
2018{
2019 errors++;
2020 if (DebugChecks)
2021 fprintf(stderr, "%s: TS error #%d on PID %d (%s)\n", *TimeToString(time(NULL)), errors, Pid, Message);
2022}
2023
2025{
2026 bool e = oldErrors < errors;
2027 oldErrors = errors;
2028 return e;
2029}
2030
2031void cTsChecker::CheckTs(const uchar *Data, int Length)
2032{
2033 while (Length >= TS_SIZE) {
2034 int Pid = TsPid(Data);
2035 uchar Cc = TsContinuityCounter(Data);
2036 if (TsHasPayload(Data)) {
2037 if (TsError(Data))
2038 Report(Pid, "tei");
2039 else if (TsIsScrambled(Data))
2040 Report(Pid, "scrambled");
2041 else {
2042 uchar OldCc = counter[Pid];
2043 if (OldCc != TS_CC_UNKNOWN) {
2044 uchar NewCc = (OldCc + 1) & TS_CONT_CNT_MASK;
2045 if (Cc != NewCc)
2046 Report(Pid, "continuity");
2047 }
2048 }
2049 }
2050 counter[Pid] = Cc;
2051 Data += TS_SIZE;
2052 Length -= TS_SIZE;
2053 }
2054}
2055
2056// --- cPtsChecker -----------------------------------------------------------
2057
2058static inline int ComparePts(const void *a, const void *b)
2059{
2060 return PtsDiff(*(const int64_t *)b, *(const int64_t *)a);
2061}
2063private:
2065 int64_t lastPts;
2070public:
2071 cPtsChecker(void);
2072 void Clear(void);
2073 void SetFrameDelta(int FrameDelta) { frameDelta = FrameDelta; }
2074 void Process(void);
2075 void AddPts(int64_t Pts, bool IndependentFrame = false);
2076 bool NewMissing(void);
2077 int Missing(void);
2078 };
2079
2081{
2082 frameDelta = 0;
2083 Clear();
2084}
2085
2087{
2088 lastPts = -1;
2089 iFrameNoPts = false;
2090 totalMissing = 0;
2091 oldMissing = 0;
2092}
2093
2095{
2096 if (pts.Size() > 1) {
2097 pts.Sort(ComparePts);
2098 if (frameDelta == 0) {
2099 int Delta = INT_MAX;
2100 for (int i = 1; i < pts.Size(); i++) {
2101 int d = int(PtsDiff(pts[i - 1], pts[i]));
2102 if (d > 0 && d < Delta)
2103 Delta = d;
2104 }
2105 if (Delta < INT_MAX)
2106 frameDelta = Delta;
2107 }
2108 if (frameDelta == 0)
2109 return; // can't continue without frameDelta
2110 int Missing = 0;
2111 int Number = pts.Size();
2112 if (Number > 0 && pts[Number - 1] == lastPts) {
2113 // Don't get stuck at a total discontinuity:
2114 pts.Remove(Number - 1);
2115 Number--;
2116 }
2117 for (int i = 1; i < Number; i++) {
2118 int d = int(PtsDiff(pts[i - 1], pts[i]));
2119 if (d > frameDelta) {
2120 d = (d / frameDelta) - 1;
2121 if (d > 0 && iFrameNoPts) {
2122 d--;
2123 iFrameNoPts = false;
2124 }
2125 if (d > 0)
2126 Missing += d;
2127 }
2128 }
2130 pts.Remove(0, Number - 1); // leave the last value in the list
2131 if (pts.Size() == 1)
2132 lastPts = pts[0];
2133 }
2134}
2135
2136void cPtsChecker::AddPts(int64_t Pts, bool IndependentFrame)
2137{
2138 if (IndependentFrame) {
2139 Process();
2140 // In H.265 there can be I-frames that have no PTS (if anybody knows how players
2141 // handle this, please let me know). This is a workaround for such cases:
2142 if (Pts < 0)
2143 iFrameNoPts = true;
2144 }
2145 if (Pts >= 0)
2146 pts.Append(Pts);
2147}
2148
2150{
2151 bool m = oldMissing < totalMissing;
2153 return m;
2154}
2155
2157{
2158 return totalMissing;
2159}
2160
2161// --- cFrameChecker ---------------------------------------------------------
2162
2168
2170{
2171 delete ptsChecker;
2172 delete tsChecker;
2173}
2174
2176{
2177 tsChecker->Reset();
2178}
2179
2180bool cFrameChecker::Check(const uchar *Data, int Length, bool Independent, bool &Errors, bool &Missing, bool Final)
2181{
2182 tsChecker->CheckTs(Data, Length);
2183 Errors = tsChecker->NewErrors();
2184 ptsChecker->AddPts(TsGetPts(Data, Length), Independent);
2185 if (Final)
2186 ptsChecker->Process();
2187 Missing = ptsChecker->NewMissing();
2188 return Errors || Missing;
2189}
2190
2192{
2193 return tsChecker->Errors() + ptsChecker->Missing();
2194}
2195
2196// --- cFrameDetector --------------------------------------------------------
2197
2198const char *ScanTypeChars = "-pi"; // index is eScanType
2199const char *AspectRatioTexts[] = { // index is eAspectRatio
2200 "-",
2201 "1:1",
2202 "4:3",
2203 "16:9",
2204 "2.21:1",
2205 NULL
2206 };
2207
2209{
2210 parser = NULL;
2211 tsChecker = new cTsChecker;
2212 ptsChecker = new cPtsChecker; // must be done before calling SetPid()
2213 SetPid(Pid, Type);
2214 synced = false;
2215 newFrame = independentFrame = false;
2216 numPtsValues = 0;
2217 numIFrames = 0;
2218 framesPerSecond = 0;
2219 frameWidth = 0;
2220 frameHeight = 0;
2224 scanning = false;
2225 firstIframeSeen = false;
2226 previousErrors = 0;
2227 missingFrames = 0;
2228}
2229
2231{
2232 delete ptsChecker;
2233 delete tsChecker;
2234}
2235
2236static int CmpUint32(const void *p1, const void *p2)
2237{
2238 if (*(uint32_t *)p1 < *(uint32_t *)p2) return -1;
2239 if (*(uint32_t *)p1 > *(uint32_t *)p2) return 1;
2240 return 0;
2241}
2242
2243void cFrameDetector::SetPid(int Pid, int Type)
2244{
2245 pid = Pid;
2246 type = Type;
2247 isVideo = type == 0x01 || type == 0x02 || type == 0x1B || type == 0x24; // MPEG 1, 2, H.264 or H.265
2248 delete parser;
2249 parser = NULL;
2250 if (type == 0x01 || type == 0x02)
2251 parser = new cMpeg2Parser;
2252 else if (type == 0x1B)
2253 parser = new cH264Parser;
2254 else if (type == 0x24)
2255 parser = new cH265Parser;
2256 else if (type == 0x03 || type == 0x04 || type == 0x06) // MPEG audio or AC3 audio
2257 parser = new cAudioParser;
2258 else if (type != 0)
2259 esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
2260 tsChecker->Reset();
2261}
2262
2263void cFrameDetector::SetLastPts(int64_t LastPts)
2264{
2265 ptsChecker->AddPts(LastPts);
2266 ptsChecker->Clear();
2267}
2268
2269int cFrameDetector::Errors(bool *PreviousErrors, bool *MissingFrames)
2270{
2271 if (PreviousErrors)
2272 *PreviousErrors = tsChecker->NewErrors();
2273 if (MissingFrames) {
2274 ptsChecker->Process();
2275 *MissingFrames = ptsChecker->NewMissing();
2276 }
2277 return tsChecker->Errors() + ptsChecker->Missing();
2278}
2279
2280bool cFrameDetector::NewFrame(int *PreviousErrors, int *MissingFrames)
2281{
2282 // This function is deprecated, PreviousErrors and MissingFrames only return 0 or 1, not actual numbers!
2283 if (newFrame) {
2284 if (PreviousErrors)
2285 *PreviousErrors = previousErrors;
2286 if (MissingFrames)
2287 *MissingFrames = missingFrames;
2288 }
2289 return newFrame;
2290}
2291
2292bool cFrameDetector::NewFrame(bool &PreviousErrors, bool &MissingFrames)
2293{
2294 if (newFrame) {
2295 PreviousErrors = previousErrors;
2296 MissingFrames = missingFrames;
2297 }
2298 return newFrame;
2299}
2300
2301int cFrameDetector::Analyze(const uchar *Data, int Length, bool ErrorCheck)
2302{
2303 if (!parser)
2304 return 0;
2305 int Processed = 0;
2306 newFrame = independentFrame = false;
2307 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
2308 // Sync on TS packet borders:
2309 if (int Skipped = TS_SYNC(Data, Length))
2310 return Processed + Skipped;
2311 // Handle at least one TS packet:
2312 int Handled = TS_SIZE;
2313 if (TsHasPayload(Data) && !TsIsScrambled(Data)) {
2314 int Pid = TsPid(Data);
2315 if (Pid == pid) {
2316 if (Processed)
2317 return Processed;
2318 if (TsPayloadStart(Data))
2319 scanning = true;
2320 if (scanning) {
2321 // Detect the beginning of a new frame:
2322 if (TsPayloadStart(Data)) {
2325 }
2326 int n = parser->Parse(Data, Length, pid);
2327 if (n > 0) {
2328 if (parser->NewFrame()) {
2329 newFrame = true;
2330 independentFrame = parser->IndependentFrame();
2332 if (synced) {
2333 if (framesPerPayloadUnit <= 1)
2334 scanning = false;
2335 }
2336 else {
2337 if (parser->FramesPerSecond() > 0.0) {
2338 framesPerSecond = parser->FramesPerSecond();
2339 frameWidth = parser->FrameWidth();
2340 frameHeight = parser->FrameHeight();
2341 scanType = parser->ScanType();
2342 aspectRatio = parser->AspectRatio();
2343 ptsChecker->SetFrameDelta(PTSTICKS / framesPerSecond);
2344 synced = true;
2345 parser->SetDebug(false);
2346 }
2348 if (independentFrame)
2349 numIFrames++;
2350 }
2351 if (synced && firstIframeSeen && ErrorCheck)
2352 ptsChecker->AddPts(TsGetPts(Data, n), independentFrame);
2353 }
2354 Handled = n;
2355 }
2356 }
2357 if (TsPayloadStart(Data)) {
2358 // Determine the frame rate from the PTS values in the PES headers:
2359 if (framesPerSecond <= 0.0) {
2360 // frame rate unknown, so collect a sequence of PTS values:
2361 if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
2362 if (newFrame) { // only take PTS values at the beginning of a frame (in case of fields!)
2363 const uchar *Pes = Data + TsPayloadOffset(Data);
2364 if (numIFrames && PesHasPts(Pes)) {
2366 // check for rollover:
2367 if (numPtsValues && ptsValues[numPtsValues - 1] > 0xF0000000 && ptsValues[numPtsValues] < 0x10000000) {
2368 dbgframes("#");
2369 numPtsValues = 0;
2370 numIFrames = 0;
2371 }
2372 else
2373 numPtsValues++;
2374 }
2375 }
2376 }
2377 if (numPtsValues >= 2 && numIFrames >= 2) {
2378 // find the smallest PTS delta:
2379 qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
2380 numPtsValues--;
2381 for (int i = 0; i < numPtsValues; i++)
2382 ptsValues[i] = ptsValues[i + 1] - ptsValues[i];
2383 qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
2384 int Div = framesPerPayloadUnit;
2385 if (framesPerPayloadUnit > 1)
2386 Div += parser->IFrameTemporalReferenceOffset();
2387 if (Div <= 0)
2388 Div = 1;
2389 int Delta = ptsValues[0] / Div;
2390 // determine frame info:
2391 if (isVideo) {
2392 if (Delta == 3753)
2393 framesPerSecond = 24.0 / 1.001;
2394 else if (abs(Delta - 3600) <= 1)
2395 framesPerSecond = 25.0;
2396 else if (Delta % 3003 == 0)
2397 framesPerSecond = 30.0 / 1.001;
2398 else if (abs(Delta - 1800) <= 1)
2399 framesPerSecond = 50.0;
2400 else if (Delta == 1501)
2401 framesPerSecond = 60.0 / 1.001;
2402 else {
2404 dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, framesPerSecond);
2405 }
2406 }
2407 else // audio
2408 framesPerSecond = double(PTSTICKS) / Delta; // PTS of audio frames is always increasing
2409 ptsChecker->SetFrameDelta(PTSTICKS / framesPerSecond);
2410 dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d TRO = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1, parser->IFrameTemporalReferenceOffset());
2411 synced = true;
2412 parser->SetDebug(false);
2413 }
2414 }
2415 }
2416 }
2417 else if (Pid == PATPID && synced && Processed)
2418 return Processed; // allow the caller to see any PAT packets
2419 }
2420 if (synced && firstIframeSeen && ErrorCheck) {
2421 if (newFrame) {
2422 previousErrors = tsChecker->NewErrors();
2423 missingFrames = ptsChecker->NewMissing();
2424 }
2425 tsChecker->CheckTs(Data, Handled);
2426 }
2427 Data += Handled;
2428 Length -= Handled;
2429 Processed += Handled;
2430 if (newFrame)
2431 break;
2432 }
2433 return Processed;
2434}
2435
2436// --- cNaluDumper ---------------------------------------------------------
2437
2439{
2441 reset();
2442}
2443
2445{
2447 ContinuityOffset = 0;
2448 PesId = -1;
2449 PesOffset = 0;
2451 NaluOffset = 0;
2452 History = 0xffffffff;
2453 DropAllPayload = false;
2454}
2455
2456void cNaluDumper::ProcessPayload(unsigned char *Payload, int size, bool PayloadStart, sPayloadInfo &Info)
2457{
2458 Info.DropPayloadStartBytes = 0;
2459 Info.DropPayloadEndBytes = 0;
2460 int LastKeepByte = -1;
2461
2462 if (PayloadStart)
2463 {
2464 History = 0xffffffff;
2465 PesId = -1;
2467 }
2468
2469 for (int i=0; i<size; i++) {
2470 History = (History << 8) | Payload[i];
2471
2472 PesOffset++;
2473 NaluOffset++;
2474
2475 bool DropByte = false;
2476
2477 if (History >= 0x00000180 && History <= 0x000001FF)
2478 {
2479 // Start of PES packet
2480 PesId = History & 0xff;
2481 PesOffset = 0;
2483 }
2484 else if (PesId >= 0xe0 && PesId <= 0xef // video stream
2485 && History >= 0x00000100 && History <= 0x0000017F) // NALU start code
2486 {
2487 int NaluId = History & 0xff;
2488 NaluOffset = 0;
2489 NaluFillState = ((NaluId & 0x1f) == 0x0c) ? NALU_FILL : NALU_NONE;
2490 }
2491
2492 if (PesId >= 0xe0 && PesId <= 0xef // video stream
2493 && PesOffset >= 1 && PesOffset <= 2)
2494 {
2495 Payload[i] = 0; // Zero out PES length field
2496 }
2497
2498 if (NaluFillState == NALU_FILL && NaluOffset > 0) // Within NALU fill data
2499 {
2500 // We expect a series of 0xff bytes terminated by a single 0x80 byte.
2501
2502 if (Payload[i] == 0xFF)
2503 {
2504 DropByte = true;
2505 }
2506 else if (Payload[i] == 0x80)
2507 {
2508 NaluFillState = NALU_TERM; // Last byte of NALU fill, next byte sets NaluFillEnd=true
2509 DropByte = true;
2510 }
2511 else // Invalid NALU fill
2512 {
2513 dsyslog("cNaluDumper: Unexpected NALU fill data: %02x", Payload[i]);
2515 if (LastKeepByte == -1)
2516 {
2517 // Nalu fill from beginning of packet until last byte
2518 // packet start needs to be dropped
2519 Info.DropPayloadStartBytes = i;
2520 }
2521 }
2522 }
2523 else if (NaluFillState == NALU_TERM) // Within NALU fill data
2524 {
2525 // We are after the terminating 0x80 byte
2527 if (LastKeepByte == -1)
2528 {
2529 // Nalu fill from beginning of packet until last byte
2530 // packet start needs to be dropped
2531 Info.DropPayloadStartBytes = i;
2532 }
2533 }
2534
2535 if (!DropByte)
2536 LastKeepByte = i; // Last useful byte
2537 }
2538
2539 Info.DropAllPayloadBytes = (LastKeepByte == -1);
2540 Info.DropPayloadEndBytes = size-1-LastKeepByte;
2541}
2542
2543bool cNaluDumper::ProcessTSPacket(unsigned char *Packet)
2544{
2545 bool HasAdaption = TsHasAdaptationField(Packet);
2546 bool HasPayload = TsHasPayload(Packet);
2547
2548 // Check continuity:
2549 int ContinuityInput = TsContinuityCounter(Packet);
2550 if (LastContinuityInput >= 0)
2551 {
2552 int NewContinuityInput = HasPayload ? (LastContinuityInput + 1) & TS_CONT_CNT_MASK : LastContinuityInput;
2553 int Offset = (NewContinuityInput - ContinuityInput) & TS_CONT_CNT_MASK;
2554 if (Offset > 0)
2555 dsyslog("cNaluDumper: TS continuity offset %i", Offset);
2556 if (Offset > ContinuityOffset)
2557 ContinuityOffset = Offset; // max if packets get dropped, otherwise always the current one.
2558 }
2559 LastContinuityInput = ContinuityInput;
2560
2561 if (HasPayload) {
2562 sPayloadInfo Info;
2563 int Offset = TsPayloadOffset(Packet);
2564 ProcessPayload(Packet + Offset, TS_SIZE - Offset, TsPayloadStart(Packet), Info);
2565
2567 {
2568 // Return from drop packet mode to normal mode
2569 DropAllPayload = false;
2570
2571 // Does the packet start with some remaining NALU fill data?
2572 if (Info.DropPayloadStartBytes > 0)
2573 {
2574 // Add these bytes as stuffing to the adaption field.
2575
2576 // Sample payload layout:
2577 // FF FF FF FF FF 80 00 00 01 xx xx xx xx
2578 // ^DropPayloadStartBytes
2579
2580 TsExtendAdaptionField(Packet, Offset - 4 + Info.DropPayloadStartBytes);
2581 }
2582 }
2583
2584 bool DropThisPayload = DropAllPayload;
2585
2586 if (!DropAllPayload && Info.DropPayloadEndBytes > 0) // Payload ends with 0xff NALU Fill
2587 {
2588 // Last packet of useful data
2589 // Do early termination of NALU fill data
2590 Packet[TS_SIZE-1] = 0x80;
2591 DropAllPayload = true;
2592 // Drop all packets AFTER this one
2593
2594 // Since we already wrote the 0x80, we have to make sure that
2595 // as soon as we stop dropping packets, any beginning NALU fill of next
2596 // packet gets dumped. (see DropPayloadStartBytes above)
2597 }
2598
2599 if (DropThisPayload && HasAdaption)
2600 {
2601 // Drop payload data, but keep adaption field data
2602 TsExtendAdaptionField(Packet, TS_SIZE-4);
2603 DropThisPayload = false;
2604 }
2605
2606 if (DropThisPayload)
2607 {
2608 return true; // Drop packet
2609 }
2610 }
2611
2612 // Fix Continuity Counter and reproduce incoming offsets:
2613 int NewContinuityOutput = TsHasPayload(Packet) ? (LastContinuityOutput + 1) & TS_CONT_CNT_MASK : LastContinuityOutput;
2614 NewContinuityOutput = (NewContinuityOutput + ContinuityOffset) & TS_CONT_CNT_MASK;
2615 TsSetContinuityCounter(Packet, NewContinuityOutput);
2616 LastContinuityOutput = NewContinuityOutput;
2617 ContinuityOffset = 0;
2618
2619 return false; // Keep packet
2620}
2621
2622// --- cNaluStreamProcessor ---------------------------------------------------------
2623
2625{
2626 pPatPmtParser = NULL;
2627 vpid = -1;
2628 data = NULL;
2629 length = 0;
2630 tempLength = 0;
2631 tempLengthAtEnd = false;
2632 TotalPackets = 0;
2633 DroppedPackets = 0;
2634}
2635
2637{
2638 if (length > 0)
2639 esyslog("cNaluStreamProcessor::PutBuffer: New data before old data was processed!");
2640
2641 data = Data;
2642 length = Length;
2643}
2644
2646{
2647 if (length <= 0)
2648 {
2649 // Need more data - quick exit
2650 OutLength = 0;
2651 return NULL;
2652 }
2653 if (tempLength > 0) // Data in temp buffer?
2654 {
2655 if (tempLengthAtEnd) // Data is at end, copy to beginning
2656 {
2657 // Overlapping src and dst!
2658 for (int i=0; i<tempLength; i++)
2660 }
2661 // Normalize TempBuffer fill
2663 {
2664 int Size = min(TS_SIZE-tempLength, length);
2665 memcpy(tempBuffer+tempLength, data, Size);
2666 data += Size;
2667 length -= Size;
2668 tempLength += Size;
2669 }
2670 if (tempLength < TS_SIZE)
2671 {
2672 // All incoming data buffered, but need more data
2673 tempLengthAtEnd = false;
2674 OutLength = 0;
2675 return NULL;
2676 }
2677 // Now: TempLength==TS_SIZE
2678 if (tempBuffer[0] != TS_SYNC_BYTE)
2679 {
2680 // Need to sync on TS within temp buffer
2681 int Skipped = 1;
2682 while (Skipped < TS_SIZE && (tempBuffer[Skipped] != TS_SYNC_BYTE || (Skipped < length && data[Skipped] != TS_SYNC_BYTE)))
2683 Skipped++;
2684 esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
2685 // Pass through skipped bytes
2686 tempLengthAtEnd = true;
2687 tempLength = TS_SIZE - Skipped; // may be 0, thats ok
2688 OutLength = Skipped;
2689 return tempBuffer;
2690 }
2691 // Now: TempBuffer is a TS packet
2692 int Pid = TsPid(tempBuffer);
2693 if (pPatPmtParser)
2694 {
2695 if (Pid == 0)
2696 pPatPmtParser->ParsePat(tempBuffer, TS_SIZE);
2697 else if (pPatPmtParser->IsPmtPid(Pid))
2698 pPatPmtParser->ParsePmt(tempBuffer, TS_SIZE);
2699 }
2700
2701 TotalPackets++;
2702 bool Drop = false;
2703 if (Pid == vpid || (pPatPmtParser && Pid == pPatPmtParser->Vpid() && pPatPmtParser->Vtype() == 0x1B))
2704 Drop = NaluDumper.ProcessTSPacket(tempBuffer);
2705 if (!Drop)
2706 {
2707 // Keep this packet, then continue with new data
2708 tempLength = 0;
2709 OutLength = TS_SIZE;
2710 return tempBuffer;
2711 }
2712 // Drop TempBuffer
2714 tempLength = 0;
2715 }
2716 // Now: TempLength==0, just process data/length
2717
2718 // Pointer to processed data / length:
2719 uchar *Out = data;
2720 uchar *OutEnd = Out;
2721
2722 while (length >= TS_SIZE)
2723 {
2724 if (data[0] != TS_SYNC_BYTE) {
2725 int Skipped = 1;
2726 while (Skipped < length && (data[Skipped] != TS_SYNC_BYTE || (length - Skipped > TS_SIZE && data[Skipped + TS_SIZE] != TS_SYNC_BYTE)))
2727 Skipped++;
2728 esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
2729
2730 // Pass through skipped bytes
2731 if (OutEnd != data)
2732 memcpy(OutEnd, data, Skipped);
2733 OutEnd += Skipped;
2734 continue;
2735 }
2736 // Now: Data starts with complete TS packet
2737
2738 int Pid = TsPid(data);
2739 if (pPatPmtParser)
2740 {
2741 if (Pid == 0)
2742 pPatPmtParser->ParsePat(data, TS_SIZE);
2743 else if (pPatPmtParser->IsPmtPid(Pid))
2744 pPatPmtParser->ParsePmt(data, TS_SIZE);
2745 }
2746
2747 TotalPackets++;
2748 bool Drop = false;
2749 if (Pid == vpid || (pPatPmtParser && Pid == pPatPmtParser->Vpid() && pPatPmtParser->Vtype() == 0x1B))
2750 Drop = NaluDumper.ProcessTSPacket(data);
2751 if (!Drop)
2752 {
2753 if (OutEnd != data)
2754 memcpy(OutEnd, data, TS_SIZE);
2755 OutEnd += TS_SIZE;
2756 }
2757 else
2758 {
2760 }
2761 data += TS_SIZE;
2762 length -= TS_SIZE;
2763 }
2764 // Now: Less than a packet remains.
2765 if (length > 0)
2766 {
2767 // copy remains into temp buffer
2768 memcpy(tempBuffer, data, length);
2770 tempLengthAtEnd = false;
2771 length = 0;
2772 }
2773 OutLength = (OutEnd - Out);
2774 return OutLength > 0 ? Out : NULL;
2775}
#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:1266
cAudioParser(void)
Definition remux.c:1262
const int * Dpids(void) const
Definition channels.h:158
uint16_t AncillaryPageId(int i) const
Definition channels.h:170
uint16_t CompositionPageId(int i) const
Definition channels.h:169
int Tpid(void) const
Definition channels.h:171
const char * Slang(int i) const
Definition channels.h:165
int Vpid(void) const
Definition channels.h:154
int Atype(int i) const
Definition channels.h:166
int Dtype(int i) const
Definition channels.h:167
int Dpid(int i) const
Definition channels.h:161
int Vtype(void) const
Definition channels.h:156
int Apid(int i) const
Definition channels.h:160
int Ppid(void) const
Definition channels.h:155
uchar SubtitlingType(int i) const
Definition channels.h:168
const char * Dlang(int i) const
Definition channels.h:164
int Spid(int i) const
Definition channels.h:162
const int * Apids(void) const
Definition channels.h:157
const char * Alang(int i) const
Definition channels.h:163
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:1227
void ClrAvailableTracks(bool DescriptionsOnly=false, bool IdsOnly=false)
Clears the list of currently available tracks.
Definition device.c:1091
void EnsureSubtitleTrack(void)
Makes sure one of the preferred language subtitle tracks is selected.
Definition device.c:1260
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:1114
uchar eit[TS_SIZE]
Definition remux.h:440
cEitGenerator(int Sid=0)
Definition remux.c:985
int counter
Definition remux.h:441
uchar * AddParentalRatingDescriptor(uchar *p, uchar ParentalRating=0)
Definition remux.c:999
uint16_t YMDtoMJD(int Y, int M, int D)
Definition remux.c:993
uchar * Generate(int Sid)
Definition remux.c:1010
int version
Definition remux.h:442
void Reset(void)
Definition remux.c:2175
cTsChecker * tsChecker
Definition remux.h:537
bool Check(const uchar *Data, int Length, bool Independent, bool &Errors, bool &Missing, bool Final)
Check Length bytes of the given Data (which must be a complete frame), with Independent telling wheth...
Definition remux.c:2180
cFrameChecker(void)
Definition remux.c:2163
cPtsChecker * ptsChecker
Definition remux.h:538
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:2191
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:2269
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:2280
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:2301
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:2208
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:2263
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:2243
eAspectRatio aspectRatio
Definition remux.h:570
uint16_t FrameWidth(void)
Definition remux.c:1234
void SetDebug(bool Debug)
Definition remux.c:1230
bool NewFrame(void)
Definition remux.c:1231
eAspectRatio aspectRatio
Definition remux.c:1219
double FramesPerSecond(void)
Definition remux.c:1236
bool IndependentFrame(void)
Definition remux.c:1232
bool newFrame
Definition remux.c:1212
int iFrameTemporalReferenceOffset
Definition remux.c:1214
double framesPerSecond
Definition remux.c:1217
eScanType ScanType(void)
Definition remux.c:1237
bool independentFrame
Definition remux.c:1213
virtual ~cFrameParser()
Definition remux.c:1222
uint16_t frameHeight
Definition remux.c:1216
bool debug
Definition remux.c:1211
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:1238
eScanType scanType
Definition remux.c:1218
uint16_t FrameHeight(void)
Definition remux.c:1235
int IFrameTemporalReferenceOffset(void)
Definition remux.c:1233
uint16_t frameWidth
Definition remux.c:1215
cFrameParser(void)
Definition remux.c:1241
uint32_t GetBits(int Bits)
Definition remux.c:1478
cTsPayload tsPayload
Definition remux.c:1414
void ParseAccessUnitDelimiter(void)
Definition remux.c:1549
@ nutSequenceParameterSet
Definition remux.c:1403
@ nutCodedSliceNonIdr
Definition remux.c:1401
@ nutAccessUnitDelimiter
Definition remux.c:1404
@ nutCodedSliceIdr
Definition remux.c:1402
bool separate_colour_plane_flag
Definition remux.c:1410
bool gotAccessUnitDelimiter
Definition remux.c:1416
int zeroBytes
Definition remux.c:1408
bool frame_mbs_only_flag
Definition remux.c:1412
int log2_max_frame_num
Definition remux.c:1411
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:1506
uint32_t GetGolombUe(void)
Definition remux.c:1486
uchar GetByte(bool Raw=false)
Gets the next data byte.
Definition remux.c:1450
void ParseSliceHeader(void)
Definition remux.c:1674
void ParseSequenceParameterSet(void)
Definition remux.c:1556
uchar byte
Definition remux.c:1406
uint32_t scanner
Definition remux.c:1415
bool gotSequenceParameterSet
Definition remux.c:1417
int32_t GetGolombSe(void)
Definition remux.c:1494
cH264Parser(void)
Sets up a new H.264 parser.
Definition remux.c:1437
uchar GetBit(void)
Definition remux.c:1469
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:1744
cH265Parser(void)
Definition remux.c:1739
@ nutSliceSegmentIDRWRADL
Definition remux.c:1716
@ nutUnspecified0
Definition remux.c:1730
@ nutSliceSegmentSTSAN
Definition remux.c:1707
@ nutSliceSegmentRADLN
Definition remux.c:1709
@ nutSliceSegmentIDRNLP
Definition remux.c:1717
@ nutPrefixSEI
Definition remux.c:1726
@ nutAccessUnitDelimiter
Definition remux.c:1722
@ nutUnspecified7
Definition remux.c:1731
@ nutSliceSegmentBLAWRADL
Definition remux.c:1714
@ nutSliceSegmentTSAR
Definition remux.c:1706
@ nutSliceSegmentRADLR
Definition remux.c:1710
@ nutSliceSegmentTrailingR
Definition remux.c:1704
@ nutVideoParameterSet
Definition remux.c:1719
@ nutSequenceParameterSet
Definition remux.c:1720
@ nutSliceSegmentTSAN
Definition remux.c:1705
@ nutSliceSegmentRASLN
Definition remux.c:1711
@ nutSuffixSEI
Definition remux.c:1727
@ nutSliceSegmentBLAWLP
Definition remux.c:1713
@ nutEndOfBitstream
Definition remux.c:1724
@ nutSliceSegmentBLANLP
Definition remux.c:1715
@ nutSliceSegmentRASLR
Definition remux.c:1712
@ nutPictureParameterSet
Definition remux.c:1721
@ nutNonVCLRes3
Definition remux.c:1729
@ nutSliceSegmentCRANUT
Definition remux.c:1718
@ nutSliceSegmentTrailingN
Definition remux.c:1703
@ nutNonVCLRes0
Definition remux.c:1728
@ nutFillerData
Definition remux.c:1725
@ nutSliceSegmentSTSAR
Definition remux.c:1708
@ nutEndOfSequence
Definition remux.c:1723
void ParseSequenceParameterSet(void)
Definition remux.c:1778
bool seenIndependentFrame
Definition remux.c:1283
cMpeg2Parser(void)
Definition remux.c:1302
bool seenScanType
Definition remux.c:1285
int lastIFrameTemporalReference
Definition remux.c:1284
uint32_t scanner
Definition remux.c:1282
const double frame_rate_table[9]
Definition remux.c:1286
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:1310
void reset()
Definition remux.c:2444
int LastContinuityInput
Definition remux.h:639
int PesId
Definition remux.h:645
void ProcessPayload(unsigned char *Payload, int size, bool PayloadStart, sPayloadInfo &Info)
Definition remux.c:2456
bool DropAllPayload
Definition remux.h:643
@ NALU_TERM
Definition remux.h:653
@ NALU_NONE
Definition remux.h:651
@ NALU_FILL
Definition remux.h:652
unsigned int History
Definition remux.h:637
int PesOffset
Definition remux.h:646
eNaluFillState NaluFillState
Definition remux.h:657
bool ProcessTSPacket(unsigned char *Packet)
Definition remux.c:2543
cNaluDumper()
Definition remux.c:2438
int NaluOffset
Definition remux.h:648
int ContinuityOffset
Definition remux.h:641
int LastContinuityOutput
Definition remux.h:640
cPatPmtParser * pPatPmtParser
Definition remux.h:685
long long int DroppedPackets
Definition remux.h:689
long long int TotalPackets
Definition remux.h:688
uchar tempBuffer[TS_SIZE]
Definition remux.h:682
cNaluDumper NaluDumper
Definition remux.h:686
void PutBuffer(uchar *Data, int Length)
Definition remux.c:2636
uchar * GetBuffer(int &OutLength)
Definition remux.c:2645
int MakeCRC(uchar *Target, const uchar *Data, int Length)
Definition remux.c:489
uchar * GetPmt(int &Index)
Returns a pointer to the Index'th TS packet of the PMT section.
Definition remux.c:638
void SetChannel(const cChannel *Channel)
Sets the Channel for which the PAT/PMT shall be generated.
Definition remux.c:623
void IncEsInfoLength(int Length)
Definition remux.c:422
void IncCounter(int &Counter, uchar *TsPacket)
Definition remux.c:409
cPatPmtGenerator(const cChannel *Channel=NULL)
Definition remux.c:399
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:617
int numPmtPackets
Definition remux.h:308
uchar * esInfoLength
Definition remux.h:314
uchar pat[TS_SIZE]
Definition remux.h:306
int MakeAC3Descriptor(uchar *Target, uchar Type)
Definition remux.c:443
void GeneratePat(void)
Generates a PAT section for later use with GetPat().
Definition remux.c:519
uchar * GetPat(void)
Returns a pointer to the PAT section, which consists of exactly one TS packet.
Definition remux.c:632
uchar pmt[MAX_PMT_TS][TS_SIZE]
Definition remux.h:307
int MakeLanguageDescriptor(uchar *Target, const char *Language)
Definition remux.c:470
void GeneratePmt(const cChannel *Channel)
Generates a PMT section for the given Channel, for later use with GetPmt().
Definition remux.c:548
void GeneratePmtPid(const cChannel *Channel)
Generates a PMT pid that doesn't collide with any of the actual pids of the Channel.
Definition remux.c:504
int MakeStream(uchar *Target, uchar Type, int Pid)
Definition remux.c:431
int MakeSubtitlingDescriptor(uchar *Target, const char *Language, uchar SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId)
Definition remux.c:453
void IncVersion(int &Version)
Definition remux.c:416
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:976
int dpids[MAXDPIDS+1]
Definition remux.h:372
uchar pmt[MAX_SECTION_SIZE]
Definition remux.h:361
int apids[MAXAPIDS+1]
Definition remux.h:369
cPatPmtParser(bool UpdatePrimaryDevice=false)
Definition remux.c:649
void Reset(void)
Resets the parser.
Definition remux.c:655
void ParsePat(const uchar *Data, int Length)
Parses the PAT data from the single TS packet in Data.
Definition remux.c:665
int pmtSize
Definition remux.h:362
char dlangs[MAXDPIDS][MAXLANGCODE2]
Definition remux.h:374
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:957
int patVersion
Definition remux.h:363
int pmtPids[MAX_PMT_PIDS+1]
Definition remux.h:365
uchar subtitlingTypes[MAXSPIDS]
Definition remux.h:377
int dtypes[MAXDPIDS+1]
Definition remux.h:373
uint16_t ancillaryPageIds[MAXSPIDS]
Definition remux.h:379
int SectionLength(const uchar *Data, int Length)
Definition remux.h:383
void ParsePmt(const uchar *Data, int Length)
Parses the PMT data from the single TS packet in Data.
Definition remux.c:697
bool completed
Definition remux.h:381
bool IsPmtPid(int Pid) const
Returns true if Pid the one of the PMT pids as defined by the current PAT.
Definition remux.h:406
uint16_t compositionPageIds[MAXSPIDS]
Definition remux.h:378
char alangs[MAXAPIDS][MAXLANGCODE2]
Definition remux.h:371
int spids[MAXSPIDS+1]
Definition remux.h:375
int pmtVersion
Definition remux.h:364
bool updatePrimaryDevice
Definition remux.h:380
char slangs[MAXSPIDS][MAXLANGCODE2]
Definition remux.h:376
int atypes[MAXAPIDS+1]
Definition remux.h:370
bool NewMissing(void)
Definition remux.c:2149
void SetFrameDelta(int FrameDelta)
Definition remux.c:2073
bool iFrameNoPts
Definition remux.c:2066
int frameDelta
Definition remux.c:2067
void Clear(void)
Definition remux.c:2086
void Process(void)
Definition remux.c:2094
int totalMissing
Definition remux.c:2068
int64_t lastPts
Definition remux.c:2065
int oldMissing
Definition remux.c:2069
int Missing(void)
Definition remux.c:2156
cPtsChecker(void)
Definition remux.c:2080
cVector< int64_t > pts
Definition remux.c:2064
void AddPts(int64_t Pts, bool IndependentFrame=false)
Definition remux.c:2136
static void SetBrokenLink(uchar *Data, int Length)
Definition remux.c:102
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1212
uchar counter[MAXPID]
Definition remux.c:1987
cTsChecker(void)
Definition remux.c:2000
void CheckTs(const uchar *Data, int Length)
Definition remux.c:2031
int errors
Definition remux.c:1988
int Errors(void)
Definition remux.c:1995
void Reset(void)
Definition remux.c:2006
void Report(int Pid, const char *Message)
Definition remux.c:2017
void Clear(void)
Definition remux.c:2011
int oldErrors
Definition remux.c:1989
bool NewErrors(void)
Definition remux.c:2024
int numPacketsPid
Definition remux.h:238
int pid
Definition remux.h:236
cTsPayload(void)
Definition remux.c:246
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:258
int Used(void)
Returns the number of raw bytes that have already been used (e.g.
Definition remux.h:264
bool Eof(void) const
Returns true if all available bytes of the TS payload have been processed.
Definition remux.h:268
void SetByte(uchar Byte, int Index)
Sets the TS data byte at the given Index to the value Byte.
Definition remux.c:330
uchar GetByte(void)
Gets the next byte of the TS payload, skipping any intermediate TS header data.
Definition remux.c:280
bool AtTsStart(void)
Returns true if this payload handler is currently pointing to first byte of a TS packet.
Definition remux.h:255
int GetLastIndex(void)
Returns the index into the TS data of the payload byte that has most recently been read.
Definition remux.c:325
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:272
bool SkipPesHeader(void)
Skips all bytes belonging to the PES header of the payload.
Definition remux.c:320
int index
Definition remux.h:237
int numPacketsOther
Definition remux.h:239
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:353
uchar SetEof(void)
Definition remux.c:259
int length
Definition remux.h:235
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:336
void Reset(void)
Definition remux.c:265
uchar * data
Definition remux.h:234
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:313
int lastLength
Definition remux.h:463
bool repeatLast
Definition remux.h:464
uchar * lastData
Definition remux.h:462
uchar * data
Definition remux.h:458
void PutTs(const uchar *Data, int Length)
Puts the payload data of the single TS packet at Data into the converter.
Definition remux.c:1084
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:1161
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:1113
cTsToPes(void)
Definition remux.c:1072
int length
Definition remux.h:460
~cTsToPes()
Definition remux.c:1079
void Reset(void)
Resets the converter.
Definition remux.c:1166
int offset
Definition remux.h:461
int size
Definition remux.h:459
cSetup Setup
Definition config.c:372
@ 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:385
void TsSetPcr(uchar *p, int64_t Pcr)
Definition remux.c:131
const char * AspectRatioTexts[]
Definition remux.c:2199
#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:1202
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:234
const char * ScanTypeChars
Definition remux.c:2198
#define MAXPESLENGTH
Definition remux.c:1111
#define P_PMT_PID
Definition remux.c:501
void TsHidePayload(uchar *p)
Definition remux.c:121
static int CmpUint32(const void *p1, const void *p2)
Definition remux.c:2236
#define MAXPID
Definition remux.c:502
void PesSetDts(uchar *p, int64_t Dts)
Definition remux.c:225
#define EMPTY_SCANNER
Definition remux.c:30
static bool DebugPatPmt
Definition remux.c:20
static bool DebugChecks
Definition remux.c:1972
int64_t TsGetDts(const uchar *p, int l)
Definition remux.c:173
#define TS_CC_UNKNOWN
Definition remux.c:1983
void TsExtendAdaptionField(unsigned char *Packet, int ToLength)
Definition remux.c:361
static int ComparePts(const void *a, const void *b)
Definition remux.c:2058
void TsSetDts(uchar *p, int l, int64_t Dts)
Definition remux.c:200
void TsSetPts(uchar *p, int l, int64_t Pts)
Definition remux.c:186
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:216
int64_t TsGetPts(const uchar *p, int l)
Definition remux.c:160
void BlockDump(const char *Name, const u_char *Data, int Length)
Definition remux.c:1176
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:500
void TsDump(const char *Name, const u_char *Data, int Length)
Definition remux.c:1187
#define MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION
Definition remux.c:26
bool TsError(const uchar *p)
Definition remux.h:82
#define TS_ADAPT_PCR
Definition remux.h:46
int TsPid(const uchar *p)
Definition remux.h:87
bool TsHasPayload(const uchar *p)
Definition remux.h:62
#define MAX33BIT
Definition remux.h:59
#define MAX_PMT_PIDS
Definition remux.h:357
int PesPayloadOffset(const uchar *p)
Definition remux.h:184
#define PATPID
Definition remux.h:52
bool TsIsScrambled(const uchar *p)
Definition remux.h:98
void TsSetContinuityCounter(uchar *p, uchar Counter)
Definition remux.h:108
uchar TsContinuityCounter(const uchar *p)
Definition remux.h:103
int TsGetPayload(const uchar **p)
Definition remux.h:119
#define TS_PAYLOAD_EXISTS
Definition remux.h:41
bool PesHasPts(const uchar *p)
Definition remux.h:189
bool PesLongEnough(int Length)
Definition remux.h:169
#define TS_SIZE
Definition remux.h:34
eAspectRatio
Definition remux.h:520
@ ar_16_9
Definition remux.h:524
@ ar_1_1
Definition remux.h:522
@ arUnknown
Definition remux.h:521
@ ar_4_3
Definition remux.h:523
@ ar_2_21_1
Definition remux.h:525
eScanType
Definition remux.h:513
@ stInterlaced
Definition remux.h:516
@ stProgressive
Definition remux.h:515
@ stUnknown
Definition remux.h:514
#define MAX_SECTION_SIZE
Definition remux.h:301
int64_t PesGetDts(const uchar *p)
Definition remux.h:208
#define TS_ADAPT_FIELD_EXISTS
Definition remux.h:40
int64_t PesGetPts(const uchar *p)
Definition remux.h:199
bool TsPayloadStart(const uchar *p)
Definition remux.h:77
#define TS_SYNC(Data, Length)
Definition remux.h:154
int TsPayloadOffset(const uchar *p)
Definition remux.h:113
#define MAXPID
Definition remux.h:55
bool PesHasDts(const uchar *p)
Definition remux.h:194
#define PCRFACTOR
Definition remux.h:58
#define TS_SYNC_BYTE
Definition remux.h:33
bool PesHasLength(const uchar *p)
Definition remux.h:174
bool TsHasAdaptationField(const uchar *p)
Definition remux.h:72
void TsExtendAdaptionField(unsigned char *Packet, int ToLength)
Definition remux.c:361
#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:509
int PesLength(const uchar *p)
Definition remux.h:179
int64_t TsGetPts(const uchar *p, int l)
Definition remux.c:160
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
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:1288
char * strn0cpy(char *dest, const char *src, size_t n)
Definition tools.c:131
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