vdr  2.2.0
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 3.9 2015/01/14 09:57:09 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:
20 static bool DebugPatPmt = false;
21 static 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 
32 ePesHeader 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 ...
52  PesPayloadOffset = 6;
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) {
65  PesPayloadOffset += 2;
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
76  PesPayloadOffset += 5;
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
84  PesPayloadOffset++;
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 
102 void 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 
131 void 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 
147 int64_t TsGetPts(const uchar *p, int l)
148 {
149  // Find the first packet with a PTS and use it:
150  while (l > 0) {
151  const uchar *d = p;
152  if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasPts(d))
153  return PesGetPts(d);
154  p += TS_SIZE;
155  l -= TS_SIZE;
156  }
157  return -1;
158 }
159 
160 int64_t TsGetDts(const uchar *p, int l)
161 {
162  // Find the first packet with a DTS and use it:
163  while (l > 0) {
164  const uchar *d = p;
165  if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d))
166  return PesGetDts(d);
167  p += TS_SIZE;
168  l -= TS_SIZE;
169  }
170  return -1;
171 }
172 
173 void TsSetPts(uchar *p, int l, int64_t Pts)
174 {
175  // Find the first packet with a PTS and use it:
176  while (l > 0) {
177  const uchar *d = p;
178  if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasPts(d)) {
179  PesSetPts(const_cast<uchar *>(d), Pts);
180  return;
181  }
182  p += TS_SIZE;
183  l -= TS_SIZE;
184  }
185 }
186 
187 void TsSetDts(uchar *p, int l, int64_t Dts)
188 {
189  // Find the first packet with a DTS and use it:
190  while (l > 0) {
191  const uchar *d = p;
192  if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d)) {
193  PesSetDts(const_cast<uchar *>(d), Dts);
194  return;
195  }
196  p += TS_SIZE;
197  l -= TS_SIZE;
198  }
199 }
200 
201 // --- Some PES handling tools -----------------------------------------------
202 
203 void PesSetPts(uchar *p, int64_t Pts)
204 {
205  p[ 9] = ((Pts >> 29) & 0x0E) | (p[9] & 0xF1);
206  p[10] = Pts >> 22;
207  p[11] = ((Pts >> 14) & 0xFE) | 0x01;
208  p[12] = Pts >> 7;
209  p[13] = ((Pts << 1) & 0xFE) | 0x01;
210 }
211 
212 void PesSetDts(uchar *p, int64_t Dts)
213 {
214  p[14] = ((Dts >> 29) & 0x0E) | (p[14] & 0xF1);
215  p[15] = Dts >> 22;
216  p[16] = ((Dts >> 14) & 0xFE) | 0x01;
217  p[17] = Dts >> 7;
218  p[18] = ((Dts << 1) & 0xFE) | 0x01;
219 }
220 
221 int64_t PtsDiff(int64_t Pts1, int64_t Pts2)
222 {
223  int64_t d = Pts2 - Pts1;
224  if (d > MAX33BIT / 2)
225  return d - (MAX33BIT + 1);
226  if (d < -MAX33BIT / 2)
227  return d + (MAX33BIT + 1);
228  return d;
229 }
230 
231 // --- cTsPayload ------------------------------------------------------------
232 
234 {
235  data = NULL;
236  length = 0;
237  pid = -1;
238  Reset();
239 }
240 
241 cTsPayload::cTsPayload(uchar *Data, int Length, int Pid)
242 {
243  Setup(Data, Length, Pid);
244 }
245 
247 {
248  length = index; // triggers EOF
249  return 0x00;
250 }
251 
253 {
254  index = 0;
255  numPacketsPid = 0;
256  numPacketsOther = 0;
257 }
258 
259 void cTsPayload::Setup(uchar *Data, int Length, int Pid)
260 {
261  data = Data;
262  length = Length;
263  pid = Pid >= 0 ? Pid : TsPid(Data);
264  Reset();
265 }
266 
268 {
269  if (!Eof()) {
270  if (index % TS_SIZE == 0) { // encountered the next TS header
271  for (;; index += TS_SIZE) {
272  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
273  uchar *p = data + index;
274  if (TsPid(p) == pid) { // only handle TS packets for the initial PID
276  return SetEof();
277  if (TsHasPayload(p)) {
278  if (index > 0 && TsPayloadStart(p)) // checking index to not skip the very first TS packet
279  return SetEof();
280  index += TsPayloadOffset(p);
281  break;
282  }
283  }
284  else if (TsPid(p) == PATPID)
285  return SetEof(); // caller must see PAT packets in case of index regeneration
286  else
287  numPacketsOther++;
288  }
289  else
290  return SetEof();
291  }
292  }
293  return data[index++];
294  }
295  return 0x00;
296 }
297 
298 bool cTsPayload::SkipBytes(int Bytes)
299 {
300  while (Bytes-- > 0)
301  GetByte();
302  return !Eof();
303 }
304 
306 {
308 }
309 
311 {
312  return index - 1;
313 }
314 
315 void cTsPayload::SetByte(uchar Byte, int Index)
316 {
317  if (Index >= 0 && Index < length)
318  data[Index] = Byte;
319 }
320 
321 bool cTsPayload::Find(uint32_t Code)
322 {
323  int OldIndex = index;
324  int OldNumPacketsPid = numPacketsPid;
325  int OldNumPacketsOther = numPacketsOther;
326  uint32_t Scanner = EMPTY_SCANNER;
327  while (!Eof()) {
328  Scanner = (Scanner << 8) | GetByte();
329  if (Scanner == Code)
330  return true;
331  }
332  index = OldIndex;
333  numPacketsPid = OldNumPacketsPid;
334  numPacketsOther = OldNumPacketsOther;
335  return false;
336 }
337 
338 void cTsPayload::Statistics(void) const
339 {
341  dsyslog("WARNING: required (%d+%d) TS packets to determine frame type", numPacketsOther, numPacketsPid);
343  dsyslog("WARNING: required %d video TS packets to determine frame type", numPacketsPid);
344 }
345 
346 // --- cPatPmtGenerator ------------------------------------------------------
347 
349 {
350  numPmtPackets = 0;
351  patCounter = pmtCounter = 0;
352  patVersion = pmtVersion = 0;
353  pmtPid = 0;
354  esInfoLength = NULL;
355  SetChannel(Channel);
356 }
357 
358 void cPatPmtGenerator::IncCounter(int &Counter, uchar *TsPacket)
359 {
360  TsPacket[3] = (TsPacket[3] & 0xF0) | Counter;
361  if (++Counter > 0x0F)
362  Counter = 0x00;
363 }
364 
366 {
367  if (++Version > 0x1F)
368  Version = 0x00;
369 }
370 
372 {
373  if (esInfoLength) {
374  Length += ((*esInfoLength & 0x0F) << 8) | *(esInfoLength + 1);
375  *esInfoLength = 0xF0 | (Length >> 8);
376  *(esInfoLength + 1) = Length;
377  }
378 }
379 
380 int cPatPmtGenerator::MakeStream(uchar *Target, uchar Type, int Pid)
381 {
382  int i = 0;
383  Target[i++] = Type; // stream type
384  Target[i++] = 0xE0 | (Pid >> 8); // dummy (3), pid hi (5)
385  Target[i++] = Pid; // pid lo
386  esInfoLength = &Target[i];
387  Target[i++] = 0xF0; // dummy (4), ES info length hi
388  Target[i++] = 0x00; // ES info length lo
389  return i;
390 }
391 
393 {
394  int i = 0;
395  Target[i++] = Type;
396  Target[i++] = 0x01; // length
397  Target[i++] = 0x00;
398  IncEsInfoLength(i);
399  return i;
400 }
401 
402 int cPatPmtGenerator::MakeSubtitlingDescriptor(uchar *Target, const char *Language, uchar SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId)
403 {
404  int i = 0;
405  Target[i++] = SI::SubtitlingDescriptorTag;
406  Target[i++] = 0x08; // length
407  Target[i++] = *Language++;
408  Target[i++] = *Language++;
409  Target[i++] = *Language++;
410  Target[i++] = SubtitlingType;
411  Target[i++] = CompositionPageId >> 8;
412  Target[i++] = CompositionPageId & 0xFF;
413  Target[i++] = AncillaryPageId >> 8;
414  Target[i++] = AncillaryPageId & 0xFF;
415  IncEsInfoLength(i);
416  return i;
417 }
418 
419 int cPatPmtGenerator::MakeLanguageDescriptor(uchar *Target, const char *Language)
420 {
421  int i = 0;
422  Target[i++] = SI::ISO639LanguageDescriptorTag;
423  int Length = i++;
424  Target[Length] = 0x00; // length
425  for (const char *End = Language + strlen(Language); Language < End; ) {
426  Target[i++] = *Language++;
427  Target[i++] = *Language++;
428  Target[i++] = *Language++;
429  Target[i++] = 0x00; // audio type
430  Target[Length] += 0x04; // length
431  if (*Language == '+')
432  Language++;
433  }
434  IncEsInfoLength(i);
435  return i;
436 }
437 
438 int cPatPmtGenerator::MakeCRC(uchar *Target, const uchar *Data, int Length)
439 {
440  int crc = SI::CRC32::crc32((const char *)Data, Length, 0xFFFFFFFF);
441  int i = 0;
442  Target[i++] = crc >> 24;
443  Target[i++] = crc >> 16;
444  Target[i++] = crc >> 8;
445  Target[i++] = crc;
446  return i;
447 }
448 
449 #define P_TSID 0x8008 // pseudo TS ID
450 #define P_PMT_PID 0x0084 // pseudo PMT pid
451 #define MAXPID 0x2000 // the maximum possible number of pids
452 
454 {
455  bool Used[MAXPID] = { false };
456 #define SETPID(p) { if ((p) >= 0 && (p) < MAXPID) Used[p] = true; }
457 #define SETPIDS(l) { const int *p = l; while (*p) { SETPID(*p); p++; } }
458  SETPID(Channel->Vpid());
459  SETPID(Channel->Ppid());
460  SETPID(Channel->Tpid());
461  SETPIDS(Channel->Apids());
462  SETPIDS(Channel->Dpids());
463  SETPIDS(Channel->Spids());
464  for (pmtPid = P_PMT_PID; Used[pmtPid]; pmtPid++)
465  ;
466 }
467 
469 {
470  memset(pat, 0xFF, sizeof(pat));
471  uchar *p = pat;
472  int i = 0;
473  p[i++] = TS_SYNC_BYTE; // TS indicator
474  p[i++] = TS_PAYLOAD_START | (PATPID >> 8); // flags (3), pid hi (5)
475  p[i++] = PATPID & 0xFF; // pid lo
476  p[i++] = 0x10; // flags (4), continuity counter (4)
477  p[i++] = 0x00; // pointer field (payload unit start indicator is set)
478  int PayloadStart = i;
479  p[i++] = 0x00; // table id
480  p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
481  int SectionLength = i;
482  p[i++] = 0x00; // section length lo (filled in later)
483  p[i++] = P_TSID >> 8; // TS id hi
484  p[i++] = P_TSID & 0xFF; // TS id lo
485  p[i++] = 0xC1 | (patVersion << 1); // dummy (2), version number (5), current/next indicator (1)
486  p[i++] = 0x00; // section number
487  p[i++] = 0x00; // last section number
488  p[i++] = pmtPid >> 8; // program number hi
489  p[i++] = pmtPid & 0xFF; // program number lo
490  p[i++] = 0xE0 | (pmtPid >> 8); // dummy (3), PMT pid hi (5)
491  p[i++] = pmtPid & 0xFF; // PMT pid lo
492  pat[SectionLength] = i - SectionLength - 1 + 4; // -2 = SectionLength storage, +4 = length of CRC
493  MakeCRC(pat + i, pat + PayloadStart, i - PayloadStart);
495 }
496 
498 {
499  // generate the complete PMT section:
500  uchar buf[MAX_SECTION_SIZE];
501  memset(buf, 0xFF, sizeof(buf));
502  numPmtPackets = 0;
503  if (Channel) {
504  int Vpid = Channel->Vpid();
505  int Ppid = Channel->Ppid();
506  uchar *p = buf;
507  int i = 0;
508  p[i++] = 0x02; // table id
509  int SectionLength = i;
510  p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
511  p[i++] = 0x00; // section length lo (filled in later)
512  p[i++] = pmtPid >> 8; // program number hi
513  p[i++] = pmtPid & 0xFF; // program number lo
514  p[i++] = 0xC1 | (pmtVersion << 1); // dummy (2), version number (5), current/next indicator (1)
515  p[i++] = 0x00; // section number
516  p[i++] = 0x00; // last section number
517  p[i++] = 0xE0 | (Ppid >> 8); // dummy (3), PCR pid hi (5)
518  p[i++] = Ppid; // PCR pid lo
519  p[i++] = 0xF0; // dummy (4), program info length hi (4)
520  p[i++] = 0x00; // program info length lo
521 
522  if (Vpid)
523  i += MakeStream(buf + i, Channel->Vtype(), Vpid);
524  for (int n = 0; Channel->Apid(n); n++) {
525  i += MakeStream(buf + i, Channel->Atype(n), Channel->Apid(n));
526  const char *Alang = Channel->Alang(n);
527  i += MakeLanguageDescriptor(buf + i, Alang);
528  }
529  for (int n = 0; Channel->Dpid(n); n++) {
530  i += MakeStream(buf + i, 0x06, Channel->Dpid(n));
531  i += MakeAC3Descriptor(buf + i, Channel->Dtype(n));
532  i += MakeLanguageDescriptor(buf + i, Channel->Dlang(n));
533  }
534  for (int n = 0; Channel->Spid(n); n++) {
535  i += MakeStream(buf + i, 0x06, Channel->Spid(n));
536  i += MakeSubtitlingDescriptor(buf + i, Channel->Slang(n), Channel->SubtitlingType(n), Channel->CompositionPageId(n), Channel->AncillaryPageId(n));
537  }
538 
539  int sl = i - SectionLength - 2 + 4; // -2 = SectionLength storage, +4 = length of CRC
540  buf[SectionLength] |= (sl >> 8) & 0x0F;
541  buf[SectionLength + 1] = sl;
542  MakeCRC(buf + i, buf, i);
543  // split the PMT section into several TS packets:
544  uchar *q = buf;
545  bool pusi = true;
546  while (i > 0) {
547  uchar *p = pmt[numPmtPackets++];
548  int j = 0;
549  p[j++] = TS_SYNC_BYTE; // TS indicator
550  p[j++] = (pusi ? TS_PAYLOAD_START : 0x00) | (pmtPid >> 8); // flags (3), pid hi (5)
551  p[j++] = pmtPid & 0xFF; // pid lo
552  p[j++] = 0x10; // flags (4), continuity counter (4)
553  if (pusi) {
554  p[j++] = 0x00; // pointer field (payload unit start indicator is set)
555  pusi = false;
556  }
557  int l = TS_SIZE - j;
558  memcpy(p + j, q, l);
559  q += l;
560  i -= l;
561  }
563  }
564 }
565 
566 void cPatPmtGenerator::SetVersions(int PatVersion, int PmtVersion)
567 {
568  patVersion = PatVersion & 0x1F;
569  pmtVersion = PmtVersion & 0x1F;
570 }
571 
573 {
574  if (Channel) {
575  GeneratePmtPid(Channel);
576  GeneratePat();
577  GeneratePmt(Channel);
578  }
579 }
580 
582 {
584  return pat;
585 }
586 
588 {
589  if (Index < numPmtPackets) {
590  IncCounter(pmtCounter, pmt[Index]);
591  return pmt[Index++];
592  }
593  return NULL;
594 }
595 
596 // --- cPatPmtParser ---------------------------------------------------------
597 
598 cPatPmtParser::cPatPmtParser(bool UpdatePrimaryDevice)
599 {
600  updatePrimaryDevice = UpdatePrimaryDevice;
601  Reset();
602 }
603 
605 {
606  pmtSize = 0;
607  patVersion = pmtVersion = -1;
608  pmtPids[0] = 0;
609  vpid = vtype = 0;
610  ppid = 0;
611 }
612 
613 void cPatPmtParser::ParsePat(const uchar *Data, int Length)
614 {
615  // Unpack the TS packet:
616  int PayloadOffset = TsPayloadOffset(Data);
617  Data += PayloadOffset;
618  Length -= PayloadOffset;
619  // The PAT is always assumed to fit into a single TS packet
620  if ((Length -= Data[0] + 1) <= 0)
621  return;
622  Data += Data[0] + 1; // process pointer_field
623  SI::PAT Pat(Data, false);
624  if (Pat.CheckCRCAndParse()) {
625  dbgpatpmt("PAT: TSid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pat.getTransportStreamId(), Pat.getCurrentNextIndicator(), Pat.getVersionNumber(), Pat.getSectionNumber(), Pat.getLastSectionNumber());
626  if (patVersion == Pat.getVersionNumber())
627  return;
628  int NumPmtPids = 0;
629  SI::PAT::Association assoc;
630  for (SI::Loop::Iterator it; Pat.associationLoop.getNext(assoc, it); ) {
631  dbgpatpmt(" isNITPid = %d\n", assoc.isNITPid());
632  if (!assoc.isNITPid()) {
633  if (NumPmtPids <= MAX_PMT_PIDS)
634  pmtPids[NumPmtPids++] = assoc.getPid();
635  dbgpatpmt(" service id = %d, pid = %d\n", assoc.getServiceId(), assoc.getPid());
636  }
637  }
638  pmtPids[NumPmtPids] = 0;
640  }
641  else
642  esyslog("ERROR: can't parse PAT");
643 }
644 
645 void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
646 {
647  // Unpack the TS packet:
648  bool PayloadStart = TsPayloadStart(Data);
649  int PayloadOffset = TsPayloadOffset(Data);
650  Data += PayloadOffset;
651  Length -= PayloadOffset;
652  // The PMT may extend over several TS packets, so we need to assemble them
653  if (PayloadStart) {
654  pmtSize = 0;
655  if ((Length -= Data[0] + 1) <= 0)
656  return;
657  Data += Data[0] + 1; // this is the first packet
658  if (SectionLength(Data, Length) > Length) {
659  if (Length <= int(sizeof(pmt))) {
660  memcpy(pmt, Data, Length);
661  pmtSize = Length;
662  }
663  else
664  esyslog("ERROR: PMT packet length too big (%d byte)!", Length);
665  return;
666  }
667  // the packet contains the entire PMT section, so we run into the actual parsing
668  }
669  else if (pmtSize > 0) {
670  // this is a following packet, so we add it to the pmt storage
671  if (Length <= int(sizeof(pmt)) - pmtSize) {
672  memcpy(pmt + pmtSize, Data, Length);
673  pmtSize += Length;
674  }
675  else {
676  esyslog("ERROR: PMT section length too big (%d byte)!", pmtSize + Length);
677  pmtSize = 0;
678  }
680  return; // more packets to come
681  // the PMT section is now complete, so we run into the actual parsing
682  Data = pmt;
683  }
684  else
685  return; // fragment of broken packet - ignore
686  SI::PMT Pmt(Data, false);
687  if (Pmt.CheckCRCAndParse()) {
688  dbgpatpmt("PMT: sid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pmt.getServiceId(), Pmt.getCurrentNextIndicator(), Pmt.getVersionNumber(), Pmt.getSectionNumber(), Pmt.getLastSectionNumber());
689  dbgpatpmt(" pcr = %d\n", Pmt.getPCRPid());
690  if (pmtVersion == Pmt.getVersionNumber())
691  return;
694  int NumApids = 0;
695  int NumDpids = 0;
696  int NumSpids = 0;
697  vpid = vtype = 0;
698  ppid = 0;
699  apids[0] = 0;
700  dpids[0] = 0;
701  spids[0] = 0;
702  atypes[0] = 0;
703  dtypes[0] = 0;
704  SI::PMT::Stream stream;
705  for (SI::Loop::Iterator it; Pmt.streamLoop.getNext(stream, it); ) {
706  dbgpatpmt(" stream type = %02X, pid = %d", stream.getStreamType(), stream.getPid());
707  switch (stream.getStreamType()) {
708  case 0x01: // STREAMTYPE_11172_VIDEO
709  case 0x02: // STREAMTYPE_13818_VIDEO
710  case 0x1B: // H.264
711  vpid = stream.getPid();
712  vtype = stream.getStreamType();
713  ppid = Pmt.getPCRPid();
714  break;
715  case 0x03: // STREAMTYPE_11172_AUDIO
716  case 0x04: // STREAMTYPE_13818_AUDIO
717  case 0x0F: // ISO/IEC 13818-7 Audio with ADTS transport syntax
718  case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
719  {
720  if (NumApids < MAXAPIDS) {
721  apids[NumApids] = stream.getPid();
722  atypes[NumApids] = stream.getStreamType();
723  *alangs[NumApids] = 0;
724  SI::Descriptor *d;
725  for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
726  switch (d->getDescriptorTag()) {
730  char *s = alangs[NumApids];
731  int n = 0;
732  for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) {
733  if (*ld->languageCode != '-') { // some use "---" to indicate "none"
734  dbgpatpmt(" '%s'", l.languageCode);
735  if (n > 0)
736  *s++ = '+';
738  s += strlen(s);
739  if (n++ > 1)
740  break;
741  }
742  }
743  }
744  break;
745  default: ;
746  }
747  delete d;
748  }
750  cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, NumApids, apids[NumApids], alangs[NumApids]);
751  NumApids++;
752  apids[NumApids] = 0;
753  }
754  }
755  break;
756  case 0x06: // STREAMTYPE_13818_PES_PRIVATE
757  {
758  int dpid = 0;
759  int dtype = 0;
760  char lang[MAXLANGCODE1] = "";
761  SI::Descriptor *d;
762  for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
763  switch (d->getDescriptorTag()) {
766  dbgpatpmt(" AC3");
767  dpid = stream.getPid();
768  dtype = d->getDescriptorTag();
769  break;
771  dbgpatpmt(" subtitling");
772  if (NumSpids < MAXSPIDS) {
773  spids[NumSpids] = stream.getPid();
774  *slangs[NumSpids] = 0;
775  subtitlingTypes[NumSpids] = 0;
776  compositionPageIds[NumSpids] = 0;
777  ancillaryPageIds[NumSpids] = 0;
780  char *s = slangs[NumSpids];
781  int n = 0;
782  for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) {
783  if (sub.languageCode[0]) {
784  dbgpatpmt(" '%s'", sub.languageCode);
785  subtitlingTypes[NumSpids] = sub.getSubtitlingType();
786  compositionPageIds[NumSpids] = sub.getCompositionPageId();
787  ancillaryPageIds[NumSpids] = sub.getAncillaryPageId();
788  if (n > 0)
789  *s++ = '+';
791  s += strlen(s);
792  if (n++ > 1)
793  break;
794  }
795  }
797  cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, spids[NumSpids], slangs[NumSpids]);
798  NumSpids++;
799  spids[NumSpids] = 0;
800  }
801  break;
804  dbgpatpmt(" '%s'", ld->languageCode);
806  }
807  break;
808  default: ;
809  }
810  delete d;
811  }
812  if (dpid) {
813  if (NumDpids < MAXDPIDS) {
814  dpids[NumDpids] = dpid;
815  dtypes[NumDpids] = dtype;
816  strn0cpy(dlangs[NumDpids], lang, sizeof(dlangs[NumDpids]));
818  cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, NumDpids, dpid, lang);
819  NumDpids++;
820  dpids[NumDpids] = 0;
821  }
822  }
823  }
824  break;
825  case 0x81: // STREAMTYPE_USER_PRIVATE - AC3 audio for ATSC and BD
826  case 0x82: // STREAMTYPE_USER_PRIVATE - DTS audio for BD
827  {
828  dbgpatpmt(" %s",
829  stream.getStreamType() == 0x81 ? "AC3" :
830  stream.getStreamType() == 0x82 ? "DTS" : "");
831  char lang[MAXLANGCODE1] = { 0 };
832  SI::Descriptor *d;
833  for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
834  switch (d->getDescriptorTag()) {
837  dbgpatpmt(" '%s'", ld->languageCode);
839  }
840  break;
841  default: ;
842  }
843  delete d;
844  }
845  if (NumDpids < MAXDPIDS) {
846  dpids[NumDpids] = stream.getPid();
847  dtypes[NumDpids] = SI::AC3DescriptorTag;
848  strn0cpy(dlangs[NumDpids], lang, sizeof(dlangs[NumDpids]));
850  cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, NumDpids, stream.getPid(), lang);
851  NumDpids++;
852  dpids[NumDpids] = 0;
853  }
854  }
855  break;
856  case 0x90: // PGS subtitles for BD
857  {
858  dbgpatpmt(" subtitling");
859  char lang[MAXLANGCODE1] = { 0 };
860  SI::Descriptor *d;
861  for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
862  switch (d->getDescriptorTag()) {
865  dbgpatpmt(" '%s'", ld->languageCode);
867  if (NumSpids < MAXSPIDS) {
868  spids[NumSpids] = stream.getPid();
869  *slangs[NumSpids] = 0;
870  subtitlingTypes[NumSpids] = 0;
871  compositionPageIds[NumSpids] = 0;
872  ancillaryPageIds[NumSpids] = 0;
874  cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, stream.getPid(), lang);
875  NumSpids++;
876  spids[NumSpids] = 0;
877  }
878  }
879  break;
880  default: ;
881  }
882  delete d;
883  }
884  }
885  break;
886  default: ;
887  }
888  dbgpatpmt("\n");
889  if (updatePrimaryDevice) {
892  }
893  }
895  }
896  else
897  esyslog("ERROR: can't parse PMT");
898  pmtSize = 0;
899 }
900 
901 bool cPatPmtParser::ParsePatPmt(const uchar *Data, int Length)
902 {
903  while (Length >= TS_SIZE) {
904  if (*Data != TS_SYNC_BYTE)
905  break; // just for safety
906  int Pid = TsPid(Data);
907  if (Pid == PATPID)
908  ParsePat(Data, TS_SIZE);
909  else if (IsPmtPid(Pid)) {
910  ParsePmt(Data, TS_SIZE);
911  if (patVersion >= 0 && pmtVersion >= 0)
912  return true;
913  }
914  Data += TS_SIZE;
915  Length -= TS_SIZE;
916  }
917  return false;
918 }
919 
920 bool cPatPmtParser::GetVersions(int &PatVersion, int &PmtVersion) const
921 {
922  PatVersion = patVersion;
923  PmtVersion = pmtVersion;
924  return patVersion >= 0 && pmtVersion >= 0;
925 }
926 
927 // --- cTsToPes --------------------------------------------------------------
928 
930 {
931  data = NULL;
932  size = 0;
933  Reset();
934 }
935 
937 {
938  free(data);
939 }
940 
941 void cTsToPes::PutTs(const uchar *Data, int Length)
942 {
943  if (TsError(Data)) {
944  Reset();
945  return; // ignore packets with TEI set, and drop any PES data collected so far
946  }
947  if (TsPayloadStart(Data))
948  Reset();
949  else if (!size)
950  return; // skip everything before the first payload start
951  Length = TsGetPayload(&Data);
952  if (length + Length > size) {
953  int NewSize = max(KILOBYTE(2), length + Length);
954  if (uchar *NewData = (uchar *)realloc(data, NewSize)) {
955  data = NewData;
956  size = NewSize;
957  }
958  else {
959  esyslog("ERROR: out of memory");
960  Reset();
961  return;
962  }
963  }
964  memcpy(data + length, Data, Length);
965  length += Length;
966 }
967 
968 #define MAXPESLENGTH 0xFFF0
969 
970 const uchar *cTsToPes::GetPes(int &Length)
971 {
972  if (repeatLast) {
973  repeatLast = false;
974  Length = lastLength;
975  return lastData;
976  }
977  if (offset < length && PesLongEnough(length)) {
978  if (!PesHasLength(data)) // this is a video PES packet with undefined length
979  offset = 6; // trigger setting PES length for initial slice
980  if (offset) {
981  uchar *p = data + offset - 6;
982  if (p != data) {
983  p -= 3;
984  if (p < data) {
985  Reset();
986  return NULL;
987  }
988  memmove(p, data, 4);
989  }
990  int l = min(length - offset, MAXPESLENGTH);
991  offset += l;
992  if (p != data) {
993  l += 3;
994  p[6] = 0x80;
995  p[7] = 0x00;
996  p[8] = 0x00;
997  }
998  p[4] = l / 256;
999  p[5] = l & 0xFF;
1000  Length = l + 6;
1001  lastLength = Length;
1002  lastData = p;
1003  return p;
1004  }
1005  else {
1006  Length = PesLength(data);
1007  if (Length <= length) {
1008  offset = Length; // to make sure we break out in case of garbage data
1009  lastLength = Length;
1010  lastData = data;
1011  return data;
1012  }
1013  }
1014  }
1015  return NULL;
1016 }
1017 
1019 {
1020  repeatLast = true;
1021 }
1022 
1024 {
1025  length = offset = 0;
1026  lastData = NULL;
1027  lastLength = 0;
1028  repeatLast = false;
1029 }
1030 
1031 // --- Some helper functions for debugging -----------------------------------
1032 
1033 void BlockDump(const char *Name, const u_char *Data, int Length)
1034 {
1035  printf("--- %s\n", Name);
1036  for (int i = 0; i < Length; i++) {
1037  if (i && (i % 16) == 0)
1038  printf("\n");
1039  printf(" %02X", Data[i]);
1040  }
1041  printf("\n");
1042 }
1043 
1044 void TsDump(const char *Name, const u_char *Data, int Length)
1045 {
1046  printf("%s: %04X", Name, Length);
1047  int n = min(Length, 20);
1048  for (int i = 0; i < n; i++)
1049  printf(" %02X", Data[i]);
1050  if (n < Length) {
1051  printf(" ...");
1052  n = max(n, Length - 10);
1053  for (n = max(n, Length - 10); n < Length; n++)
1054  printf(" %02X", Data[n]);
1055  }
1056  printf("\n");
1057 }
1058 
1059 void PesDump(const char *Name, const u_char *Data, int Length)
1060 {
1061  TsDump(Name, Data, Length);
1062 }
1063 
1064 // --- cFrameParser ----------------------------------------------------------
1065 
1067 protected:
1068  bool debug;
1069  bool newFrame;
1072 public:
1073  cFrameParser(void);
1074  virtual ~cFrameParser() {};
1075  virtual int Parse(const uchar *Data, int Length, int Pid) = 0;
1082  void SetDebug(bool Debug) { debug = Debug; }
1083  bool NewFrame(void) { return newFrame; }
1084  bool IndependentFrame(void) { return independentFrame; }
1086  };
1087 
1089 {
1090  debug = true;
1091  newFrame = false;
1092  independentFrame = false;
1094 }
1095 
1096 // --- cAudioParser ----------------------------------------------------------
1097 
1098 class cAudioParser : public cFrameParser {
1099 public:
1100  cAudioParser(void);
1101  virtual int Parse(const uchar *Data, int Length, int Pid);
1102  };
1103 
1105 {
1106 }
1107 
1108 int cAudioParser::Parse(const uchar *Data, int Length, int Pid)
1109 {
1110  if (TsPayloadStart(Data)) {
1111  newFrame = independentFrame = true;
1112  if (debug)
1113  dbgframes("/");
1114  }
1115  else
1116  newFrame = independentFrame = false;
1117  return TS_SIZE;
1118 }
1119 
1120 // --- cMpeg2Parser ----------------------------------------------------------
1121 
1122 class cMpeg2Parser : public cFrameParser {
1123 private:
1124  uint32_t scanner;
1127 public:
1128  cMpeg2Parser(void);
1129  virtual int Parse(const uchar *Data, int Length, int Pid);
1130  };
1131 
1133 {
1135  seenIndependentFrame = false;
1136  lastIFrameTemporalReference = -1; // invalid
1137 }
1138 
1139 int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
1140 {
1141  newFrame = independentFrame = false;
1142  bool SeenPayloadStart = false;
1143  cTsPayload tsPayload(const_cast<uchar *>(Data), Length, Pid);
1144  if (TsPayloadStart(Data)) {
1145  SeenPayloadStart = true;
1146  tsPayload.SkipPesHeader();
1148  if (debug && seenIndependentFrame)
1149  dbgframes("/");
1150  }
1151  uint32_t OldScanner = scanner; // need to remember it in case of multiple frames per payload
1152  for (;;) {
1153  if (!SeenPayloadStart && tsPayload.AtTsStart())
1154  OldScanner = scanner;
1155  scanner = (scanner << 8) | tsPayload.GetByte();
1156  if (scanner == 0x00000100) { // Picture Start Code
1157  if (!SeenPayloadStart && tsPayload.GetLastIndex() > TS_SIZE) {
1158  scanner = OldScanner;
1159  return tsPayload.Used() - TS_SIZE;
1160  }
1161  uchar b1 = tsPayload.GetByte();
1162  uchar b2 = tsPayload.GetByte();
1163  int TemporalReference = (b1 << 2 ) + ((b2 & 0xC0) >> 6);
1164  uchar FrameType = (b2 >> 3) & 0x07;
1165  if (tsPayload.Find(0x000001B5)) { // Extension start code
1166  if (((tsPayload.GetByte() & 0xF0) >> 4) == 0x08) { // Picture coding extension
1167  tsPayload.GetByte();
1168  uchar PictureStructure = tsPayload.GetByte() & 0x03;
1169  if (PictureStructure == 0x02) // bottom field
1170  break;
1171  }
1172  }
1173  newFrame = true;
1174  independentFrame = FrameType == 1; // I-Frame
1175  if (independentFrame) {
1176  if (lastIFrameTemporalReference >= 0)
1178  lastIFrameTemporalReference = TemporalReference;
1179  }
1180  if (debug) {
1182  if (seenIndependentFrame) {
1183  static const char FrameTypes[] = "?IPBD???";
1184  dbgframes("%c", FrameTypes[FrameType]);
1185  }
1186  }
1187  tsPayload.Statistics();
1188  break;
1189  }
1190  if (tsPayload.AtPayloadStart() // stop at any new payload start to have the buffer refilled if necessary
1191  || tsPayload.Eof()) // or if we're out of data
1192  break;
1193  }
1194  return tsPayload.Used();
1195 }
1196 
1197 // --- cH264Parser -----------------------------------------------------------
1198 
1199 class cH264Parser : public cFrameParser {
1200 private:
1206  };
1208  uchar byte; // holds the current byte value in case of bitwise access
1209  int bit; // the bit index into the current byte (-1 if we're not in bit reading mode)
1210  int zeroBytes; // the number of consecutive zero bytes (to detect 0x000003)
1211  uint32_t scanner;
1212  // Identifiers written in '_' notation as in "ITU-T H.264":
1216  //
1219  uchar GetByte(bool Raw = false);
1223  uchar GetBit(void);
1224  uint32_t GetBits(int Bits);
1225  uint32_t GetGolombUe(void);
1226  int32_t GetGolombSe(void);
1227  void ParseAccessUnitDelimiter(void);
1228  void ParseSequenceParameterSet(void);
1229  void ParseSliceHeader(void);
1230 public:
1231  cH264Parser(void);
1235  virtual int Parse(const uchar *Data, int Length, int Pid);
1236  };
1237 
1239 {
1240  byte = 0;
1241  bit = -1;
1242  zeroBytes = 0;
1245  log2_max_frame_num = 0;
1246  frame_mbs_only_flag = false;
1247  gotAccessUnitDelimiter = false;
1248  gotSequenceParameterSet = false;
1249 }
1250 
1252 {
1253  uchar b = tsPayload.GetByte();
1254  if (!Raw) {
1255  // If we encounter the byte sequence 0x000003, we need to skip the 0x03:
1256  if (b == 0x00)
1257  zeroBytes++;
1258  else {
1259  if (b == 0x03 && zeroBytes >= 2)
1260  b = tsPayload.GetByte();
1261  zeroBytes = 0;
1262  }
1263  }
1264  else
1265  zeroBytes = 0;
1266  bit = -1;
1267  return b;
1268 }
1269 
1271 {
1272  if (bit < 0) {
1273  byte = GetByte();
1274  bit = 7;
1275  }
1276  return (byte & (1 << bit--)) ? 1 : 0;
1277 }
1278 
1279 uint32_t cH264Parser::GetBits(int Bits)
1280 {
1281  uint32_t b = 0;
1282  while (Bits--)
1283  b |= GetBit() << Bits;
1284  return b;
1285 }
1286 
1288 {
1289  int z = -1;
1290  for (int b = 0; !b && z < 32; z++) // limiting z to no get stuck if GetBit() always returns 0
1291  b = GetBit();
1292  return (1 << z) - 1 + GetBits(z);
1293 }
1294 
1296 {
1297  uint32_t v = GetGolombUe();
1298  if (v) {
1299  if ((v & 0x01) != 0)
1300  return (v + 1) / 2; // fails for v == 0xFFFFFFFF, but that will probably never happen
1301  else
1302  return -int32_t(v / 2);
1303  }
1304  return v;
1305 }
1306 
1307 int cH264Parser::Parse(const uchar *Data, int Length, int Pid)
1308 {
1309  newFrame = independentFrame = false;
1310  tsPayload.Setup(const_cast<uchar *>(Data), Length, Pid);
1311  if (TsPayloadStart(Data)) {
1314  if (debug && gotSequenceParameterSet) {
1315  dbgframes("/");
1316  }
1317  }
1318  for (;;) {
1319  scanner = (scanner << 8) | GetByte(true);
1320  if ((scanner & 0xFFFFFF00) == 0x00000100) { // NAL unit start
1321  uchar NalUnitType = scanner & 0x1F;
1322  switch (NalUnitType) {
1324  gotAccessUnitDelimiter = true;
1325  break;
1328  gotSequenceParameterSet = true;
1329  }
1330  break;
1331  case nutCodedSliceNonIdr:
1333  ParseSliceHeader();
1334  gotAccessUnitDelimiter = false;
1335  if (newFrame)
1337  return tsPayload.Used();
1338  }
1339  break;
1340  default: ;
1341  }
1342  }
1343  if (tsPayload.AtPayloadStart() // stop at any new payload start to have the buffer refilled if necessary
1344  || tsPayload.Eof()) // or if we're out of data
1345  break;
1346  }
1347  return tsPayload.Used();
1348 }
1349 
1351 {
1353  dbgframes("A");
1354  GetByte(); // primary_pic_type
1355 }
1356 
1358 {
1359  uchar profile_idc = GetByte(); // profile_idc
1360  GetByte(); // constraint_set[0-5]_flags, reserved_zero_2bits
1361  GetByte(); // level_idc
1362  GetGolombUe(); // seq_parameter_set_id
1363  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) {
1364  int chroma_format_idc = GetGolombUe(); // chroma_format_idc
1365  if (chroma_format_idc == 3)
1367  GetGolombUe(); // bit_depth_luma_minus8
1368  GetGolombUe(); // bit_depth_chroma_minus8
1369  GetBit(); // qpprime_y_zero_transform_bypass_flag
1370  if (GetBit()) { // seq_scaling_matrix_present_flag
1371  for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); i++) {
1372  if (GetBit()) { // seq_scaling_list_present_flag
1373  int SizeOfScalingList = (i < 6) ? 16 : 64;
1374  int LastScale = 8;
1375  int NextScale = 8;
1376  for (int j = 0; j < SizeOfScalingList; j++) {
1377  if (NextScale)
1378  NextScale = (LastScale + GetGolombSe() + 256) % 256; // delta_scale
1379  if (NextScale)
1380  LastScale = NextScale;
1381  }
1382  }
1383  }
1384  }
1385  }
1386  log2_max_frame_num = GetGolombUe() + 4; // log2_max_frame_num_minus4
1387  int pic_order_cnt_type = GetGolombUe(); // pic_order_cnt_type
1388  if (pic_order_cnt_type == 0)
1389  GetGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
1390  else if (pic_order_cnt_type == 1) {
1391  GetBit(); // delta_pic_order_always_zero_flag
1392  GetGolombSe(); // offset_for_non_ref_pic
1393  GetGolombSe(); // offset_for_top_to_bottom_field
1394  for (int i = GetGolombUe(); i--; ) // num_ref_frames_in_pic_order_cnt_cycle
1395  GetGolombSe(); // offset_for_ref_frame
1396  }
1397  GetGolombUe(); // max_num_ref_frames
1398  GetBit(); // gaps_in_frame_num_value_allowed_flag
1399  GetGolombUe(); // pic_width_in_mbs_minus1
1400  GetGolombUe(); // pic_height_in_map_units_minus1
1401  frame_mbs_only_flag = GetBit(); // frame_mbs_only_flag
1402  if (debug) {
1404  dbgframes("A"); // just for completeness
1405  dbgframes(frame_mbs_only_flag ? "S" : "s");
1406  }
1407 }
1408 
1410 {
1411  newFrame = true;
1412  GetGolombUe(); // first_mb_in_slice
1413  int slice_type = GetGolombUe(); // slice_type, 0 = P, 1 = B, 2 = I, 3 = SP, 4 = SI
1414  independentFrame = (slice_type % 5) == 2;
1415  if (debug) {
1416  static const char SliceTypes[] = "PBIpi";
1417  dbgframes("%c", SliceTypes[slice_type % 5]);
1418  }
1419  if (frame_mbs_only_flag)
1420  return; // don't need the rest - a frame is complete
1421  GetGolombUe(); // pic_parameter_set_id
1423  GetBits(2); // colour_plane_id
1424  GetBits(log2_max_frame_num); // frame_num
1425  if (!frame_mbs_only_flag) {
1426  if (GetBit()) // field_pic_flag
1427  newFrame = !GetBit(); // bottom_field_flag
1428  if (debug)
1429  dbgframes(newFrame ? "t" : "b");
1430  }
1431 }
1432 
1433 // --- cFrameDetector --------------------------------------------------------
1434 
1436 {
1437  parser = NULL;
1438  SetPid(Pid, Type);
1439  synced = false;
1440  newFrame = independentFrame = false;
1441  numPtsValues = 0;
1442  numIFrames = 0;
1443  framesPerSecond = 0;
1445  scanning = false;
1446 }
1447 
1448 static int CmpUint32(const void *p1, const void *p2)
1449 {
1450  if (*(uint32_t *)p1 < *(uint32_t *)p2) return -1;
1451  if (*(uint32_t *)p1 > *(uint32_t *)p2) return 1;
1452  return 0;
1453 }
1454 
1455 void cFrameDetector::SetPid(int Pid, int Type)
1456 {
1457  pid = Pid;
1458  type = Type;
1459  isVideo = type == 0x01 || type == 0x02 || type == 0x1B; // MPEG 1, 2 or H.264
1460  delete parser;
1461  parser = NULL;
1462  if (type == 0x01 || type == 0x02)
1463  parser = new cMpeg2Parser;
1464  else if (type == 0x1B)
1465  parser = new cH264Parser;
1466  else if (type == 0x04 || type == 0x06) // MPEG audio or AC3 audio
1467  parser = new cAudioParser;
1468  else if (type != 0)
1469  esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
1470 }
1471 
1472 int cFrameDetector::Analyze(const uchar *Data, int Length)
1473 {
1474  if (!parser)
1475  return 0;
1476  int Processed = 0;
1477  newFrame = independentFrame = false;
1478  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
1479  // Sync on TS packet borders:
1480  if (Data[0] != TS_SYNC_BYTE) {
1481  int Skipped = 1;
1482  while (Skipped < Length && (Data[Skipped] != TS_SYNC_BYTE || Length - Skipped > TS_SIZE && Data[Skipped + TS_SIZE] != TS_SYNC_BYTE))
1483  Skipped++;
1484  esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
1485  return Processed + Skipped;
1486  }
1487  // Handle one TS packet:
1488  int Handled = TS_SIZE;
1489  if (TsHasPayload(Data) && !TsIsScrambled(Data)) {
1490  int Pid = TsPid(Data);
1491  if (Pid == pid) {
1492  if (Processed)
1493  return Processed;
1494  if (TsPayloadStart(Data))
1495  scanning = true;
1496  if (scanning) {
1497  // Detect the beginning of a new frame:
1498  if (TsPayloadStart(Data)) {
1499  if (!framesPerPayloadUnit)
1501  }
1502  int n = parser->Parse(Data, Length, pid);
1503  if (n > 0) {
1504  if (parser->NewFrame()) {
1505  newFrame = true;
1507  if (synced) {
1508  if (framesPerPayloadUnit <= 1)
1509  scanning = false;
1510  }
1511  else {
1513  if (independentFrame)
1514  numIFrames++;
1515  }
1516  }
1517  Handled = n;
1518  }
1519  }
1520  if (TsPayloadStart(Data)) {
1521  // Determine the frame rate from the PTS values in the PES headers:
1522  if (framesPerSecond <= 0.0) {
1523  // frame rate unknown, so collect a sequence of PTS values:
1524  if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
1525  if (newFrame) { // only take PTS values at the beginning of a frame (in case if fields!)
1526  const uchar *Pes = Data + TsPayloadOffset(Data);
1527  if (numIFrames && PesHasPts(Pes)) {
1529  // check for rollover:
1530  if (numPtsValues && ptsValues[numPtsValues - 1] > 0xF0000000 && ptsValues[numPtsValues] < 0x10000000) {
1531  dbgframes("#");
1532  numPtsValues = 0;
1533  numIFrames = 0;
1534  }
1535  else
1536  numPtsValues++;
1537  }
1538  }
1539  }
1540  if (numPtsValues >= 2 && numIFrames >= 2) {
1541  // find the smallest PTS delta:
1542  qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
1543  numPtsValues--;
1544  for (int i = 0; i < numPtsValues; i++)
1545  ptsValues[i] = ptsValues[i + 1] - ptsValues[i];
1546  qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
1547  int Div = framesPerPayloadUnit;
1548  if (framesPerPayloadUnit > 1)
1550  if (Div <= 0)
1551  Div = 1;
1552  uint32_t Delta = ptsValues[0] / Div;
1553  // determine frame info:
1554  if (isVideo) {
1555  if (abs(Delta - 3600) <= 1)
1556  framesPerSecond = 25.0;
1557  else if (Delta % 3003 == 0)
1558  framesPerSecond = 30.0 / 1.001;
1559  else if (abs(Delta - 1800) <= 1)
1560  framesPerSecond = 50.0;
1561  else if (Delta == 1501)
1562  framesPerSecond = 60.0 / 1.001;
1563  else {
1565  dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, DEFAULTFRAMESPERSECOND);
1566  }
1567  }
1568  else // audio
1569  framesPerSecond = double(PTSTICKS) / Delta; // PTS of audio frames is always increasing
1570  dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d TRO = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1, parser->IFrameTemporalReferenceOffset());
1571  synced = true;
1572  parser->SetDebug(false);
1573  }
1574  }
1575  }
1576  }
1577  else if (Pid == PATPID && synced && Processed)
1578  return Processed; // allow the caller to see any PAT packets
1579  }
1580  Data += Handled;
1581  Length -= Handled;
1582  Processed += Handled;
1583  if (newFrame)
1584  break;
1585  }
1586  return Processed;
1587 }
int framesInPayloadUnit
Definition: remux.h:490
#define VIDEO_STREAM_S
Definition: remux.c:98
uint16_t AncillaryPageId(int i) const
Definition: channels.h:170
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:901
unsigned char uchar
Definition: tools.h:30
void ParsePat(const uchar *Data, int Length)
Parses the PAT data from the single TS packet in Data.
Definition: remux.c:613
uchar * data
Definition: remux.h:217
int Used(void)
Returns the number of raw bytes that have already been used (e.g.
Definition: remux.h:247
uchar GetBit(void)
Definition: remux.c:1270
int Vpid(void) const
Definition: channels.h:154
bool separate_colour_plane_flag
Definition: remux.c:1213
uchar GetByte(void)
Gets the next byte of the TS payload, skipping any intermediate TS header data.
Definition: remux.c:267
const int * Dpids(void) const
Definition: channels.h:158
bool repeatLast
Definition: remux.h:428
int vpid
Definition: remux.h:349
int index
Definition: remux.h:220
int pid
Definition: remux.h:219
uchar subtitlingTypes[MAXSPIDS]
Definition: remux.h:360
Definition: device.h:71
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:566
#define dsyslog(a...)
Definition: tools.h:36
bool TsError(const uchar *p)
Definition: remux.h:76
void SetPid(int Pid, int Type)
Sets the Pid and stream Type to detect frames for.
Definition: remux.c:1455
#define MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION
Definition: remux.c:26
#define DEFAULTFRAMESPERSECOND
Definition: recording.h:333
bool newFrame
Definition: remux.c:1069
int Dtype(int i) const
Definition: channels.h:167
int PesPayloadOffset(const uchar *p)
Definition: remux.h:167
void IncCounter(int &Counter, uchar *TsPacket)
Definition: remux.c:358
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:298
bool TsHasAdaptationField(const uchar *p)
Definition: remux.h:66
bool getCurrentNextIndicator() const
Definition: si.c:80
void ParsePmt(const uchar *Data, int Length)
Parses the PMT data from the single TS packet in Data.
Definition: remux.c:645
uint16_t ancillaryPageIds[MAXSPIDS]
Definition: remux.h:362
bool IsPmtPid(int Pid) const
Returns true if Pid the one of the PMT pids as defined by the current PAT.
Definition: remux.h:388
int framesPerPayloadUnit
Definition: remux.h:491
virtual int Parse(const uchar *Data, int Length, int Pid)
Parses the given Data, which is a sequence of Length bytes of TS packets.
Definition: remux.c:1139
int pmtSize
Definition: remux.h:345
uchar SubtitlingType(int i) const
Definition: channels.h:168
char alangs[MAXAPIDS][MAXLANGCODE2]
Definition: remux.h:354
bool IndependentFrame(void)
Definition: remux.c:1084
int Spid(int i) const
Definition: channels.h:162
#define SETPID(p)
#define MAX33BIT
Definition: remux.h:58
bool TsPayloadStart(const uchar *p)
Definition: remux.h:71
int Dpid(int i) const
Definition: channels.h:161
uint32_t scanner
Definition: remux.c:1211
bool gotAccessUnitDelimiter
Definition: remux.c:1217
int MakeLanguageDescriptor(uchar *Target, const char *Language)
Definition: remux.c:419
void GeneratePmtPid(const cChannel *Channel)
Generates a PMT pid that doesn&#39;t collide with any of the actual pids of the Channel.
Definition: remux.c:453
int64_t PesGetPts(const uchar *p)
Definition: remux.h:182
int Analyze(const uchar *Data, int Length)
Analyzes the TS packets pointed to by Data.
Definition: remux.c:1472
uchar pat[TS_SIZE]
Definition: remux.h:289
int numPacketsPid
Definition: remux.h:221
bool debug
Definition: remux.c:1068
bool TsHasPayload(const uchar *p)
Definition: remux.h:61
StructureLoop< Association > associationLoop
Definition: section.h:39
uint32_t ptsValues[MaxPtsValues]
Definition: remux.h:485
#define esyslog(a...)
Definition: tools.h:34
StructureLoop< Stream > streamLoop
Definition: section.h:71
char slangs[MAXSPIDS][MAXLANGCODE2]
Definition: remux.h:359
#define TS_ADAPT_FIELD_EXISTS
Definition: remux.h:40
char * strn0cpy(char *dest, const char *src, size_t n)
Definition: tools.c:131
static u_int32_t crc32(const char *d, int len, u_int32_t CRCvalue)
Definition: util.c:267
void IncEsInfoLength(int Length)
Definition: remux.c:371
int MakeCRC(uchar *Target, const uchar *Data, int Length)
Definition: remux.c:438
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:1020
T max(T a, T b)
Definition: tools.h:55
#define WRN_TS_PACKETS_FOR_FRAME_DETECTOR
Definition: remux.c:28
void SetChannel(const cChannel *Channel)
Sets the Channel for which the PAT/PMT shall be generated.
Definition: remux.c:572
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:241
bool PesHasPts(const uchar *p)
Definition: remux.h:172
#define PTSTICKS
Definition: remux.h:56
cPatPmtGenerator(const cChannel *Channel=NULL)
Definition: remux.c:348
uchar SetEof(void)
Definition: remux.c:246
int MakeSubtitlingDescriptor(uchar *Target, const char *Language, uchar SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId)
Definition: remux.c:402
DescriptorTag getDescriptorTag() const
Definition: si.c:100
int length
Definition: remux.h:424
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:259
int getServiceId() const
Definition: section.c:57
const char * Dlang(int i) const
Definition: channels.h:164
StructureLoop< Subtitling > subtitlingLoop
Definition: descriptor.h:331
bool isVideo
Definition: remux.h:488
void ParseSequenceParameterSet(void)
Definition: remux.c:1357
int pmtVersion
Definition: remux.h:347
int64_t TsGetDts(const uchar *p, int l)
Definition: remux.c:160
int numPtsValues
Definition: remux.h:486
bool independentFrame
Definition: remux.c:1070
int Ppid(void) const
Definition: channels.h:155
T min(T a, T b)
Definition: tools.h:54
bool independentFrame
Definition: remux.h:484
#define TS_SYNC_BYTE
Definition: remux.h:33
int lastLength
Definition: remux.h:427
int MakeAC3Descriptor(uchar *Target, uchar Type)
Definition: remux.c:392
static bool DebugPatPmt
Definition: remux.c:20
void GeneratePat(void)
Generates a PAT section for later use with GetPat().
Definition: remux.c:468
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:321
#define dbgpatpmt(a...)
Definition: remux.c:23
cFrameParser(void)
Definition: remux.c:1088
int SectionLength(const uchar *Data, int Length)
Definition: remux.h:365
int MakeStream(uchar *Target, uchar Type, int Pid)
Definition: remux.c:380
int getPid() const
Definition: section.c:65
uchar GetByte(bool Raw=false)
Gets the next data byte.
Definition: remux.c:1251
int iFrameTemporalReferenceOffset
Definition: remux.c:1071
int patCounter
Definition: remux.h:292
bool Eof(void) const
Returns true if all available bytes of the TS payload have been processed.
Definition: remux.h:251
uchar pmt[MAX_PMT_TS][TS_SIZE]
Definition: remux.h:290
uchar * lastData
Definition: remux.h:426
const char * Alang(int i) const
Definition: channels.h:163
bool PesLongEnough(int Length)
Definition: remux.h:152
void TsSetPcr(uchar *p, int64_t Pcr)
Definition: remux.c:131
#define MAX_SECTION_SIZE
Definition: remux.h:284
#define EMPTY_SCANNER
Definition: remux.c:30
int TsPid(const uchar *p)
Definition: remux.h:81
virtual int Parse(const uchar *Data, int Length, int Pid)
Parses the given Data, which is a sequence of Length bytes of TS packets.
Definition: remux.c:1307
int getPid() const
Definition: section.c:34
cFrameDetector(int Pid=0, int Type=0)
Sets up a frame detector for the given Pid and stream Type.
Definition: remux.c:1435
#define SETPIDS(l)
void ParseSliceHeader(void)
Definition: remux.c:1409
double framesPerSecond
Definition: remux.h:489
void EnsureSubtitleTrack(void)
Makes sure one of the preferred language subtitle tracks is selected.
Definition: device.c:1153
#define TS_PAYLOAD_EXISTS
Definition: remux.h:41
int getSectionNumber() const
Definition: si.c:88
int PesLength(const uchar *p)
Definition: remux.h:162
void PesSetPts(uchar *p, int64_t Pts)
Definition: remux.c:203
void ParseAccessUnitDelimiter(void)
Definition: remux.c:1350
Definition: remux.h:20
int lastIFrameTemporalReference
Definition: remux.c:1126
int zeroBytes
Definition: remux.c:1210
int ppid
Definition: remux.h:350
cH264Parser(void)
Sets up a new H.264 parser.
Definition: remux.c:1238
char dlangs[MAXDPIDS][MAXLANGCODE2]
Definition: remux.h:357
void Reset(void)
Resets the converter.
Definition: remux.c:1023
#define MAXPID
Definition: remux.c:451
bool synced
Definition: remux.h:482
bool PesHasDts(const uchar *p)
Definition: remux.h:177
void PesSetDts(uchar *p, int64_t Dts)
Definition: remux.c:212
void BlockDump(const char *Name, const u_char *Data, int Length)
Definition: remux.c:1033
void TsSetDts(uchar *p, int l, int64_t Dts)
Definition: remux.c:187
cPatPmtParser(bool UpdatePrimaryDevice=false)
Definition: remux.c:598
int Tpid(void) const
Definition: channels.h:171
int GetLastIndex(void)
Returns the index into the TS data of the payload byte that has most recently been read...
Definition: remux.c:310
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.
cTsPayload(void)
Definition: remux.c:233
int dtypes[MAXDPIDS+1]
Definition: remux.h:356
int dpids[MAXDPIDS+1]
Definition: remux.h:355
void TsDump(const char *Name, const u_char *Data, int Length)
Definition: remux.c:1044
virtual int Parse(const uchar *Data, int Length, int Pid)
Parses the given Data, which is a sequence of Length bytes of TS packets.
Definition: remux.c:1108
int64_t PtsDiff(int64_t Pts1, int64_t Pts2)
Returns the difference between two PTS values.
Definition: remux.c:221
void PutTs(const uchar *Data, int Length)
Puts the payload data of the single TS packet at Data into the converter.
Definition: remux.c:941
void ClrAvailableTracks(bool DescriptionsOnly=false, bool IdsOnly=false)
Clears the list of currently available tracks.
Definition: device.c:997
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
Definition: remux.c:32
int IFrameTemporalReferenceOffset(void)
Definition: remux.c:1085
int atypes[MAXAPIDS+1]
Definition: remux.h:353
uchar byte
Definition: remux.c:1208
uchar * GetPmt(int &Index)
Returns a pointer to the Index&#39;th TS packet of the PMT section.
Definition: remux.c:587
int Atype(int i) const
Definition: channels.h:166
int numPmtPackets
Definition: remux.h:291
cSetup Setup
Definition: config.c:372
StructureLoop< Language > languageLoop
Definition: descriptor.h:489
#define WRN_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION
Definition: remux.c:27
~cTsToPes()
Definition: remux.c:936
#define MAXLANGCODE1
Definition: channels.h:40
int TsGetPayload(const uchar **p)
Definition: remux.h:107
#define MAXPESLENGTH
Definition: remux.c:968
int pmtCounter
Definition: remux.h:293
void GeneratePmt(const cChannel *Channel)
Generates a PMT section for the given Channel, for later use with GetPmt().
Definition: remux.c:497
uchar pmt[MAX_SECTION_SIZE]
Definition: remux.h:344
int32_t GetGolombSe(void)
Definition: remux.c:1295
void TsHidePayload(uchar *p)
Definition: remux.c:121
#define P_TSID
Definition: remux.c:449
int length
Definition: remux.h:218
uint16_t compositionPageIds[MAXSPIDS]
Definition: remux.h:361
int getStreamType() const
Definition: section.c:69
void PesDump(const char *Name, const u_char *Data, int Length)
Definition: remux.c:1059
int pmtPids[MAX_PMT_PIDS+1]
Definition: remux.h:348
bool CheckCRCAndParse()
Definition: si.c:65
bool isNITPid() const
Definition: section.h:31
uint32_t GetBits(int Bits)
Definition: remux.c:1279
bool frame_mbs_only_flag
Definition: remux.c:1215
static void SetBrokenLink(uchar *Data, int Length)
Definition: remux.c:102
virtual ~cFrameParser()
Definition: remux.c:1074
static bool DebugFrames
Definition: remux.c:21
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:338
Definition: device.h:74
bool seenIndependentFrame
Definition: remux.c:1125
const int * Apids(void) const
Definition: channels.h:157
int getServiceId() const
Definition: section.c:30
int patVersion
Definition: remux.h:294
int UseDolbyDigital
Definition: config.h:314
cFrameParser * parser
Definition: remux.h:494
int numPacketsOther
Definition: remux.h:222
#define MAXDPIDS
Definition: channels.h:36
int spids[MAXSPIDS+1]
Definition: remux.h:358
#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR
Definition: remux.h:473
int size
Definition: remux.h:423
#define PCRFACTOR
Definition: remux.h:57
void EnsureAudioTrack(bool Force=false)
Makes sure an audio track is selected that is actually available.
Definition: device.c:1120
#define PATPID
Definition: remux.h:52
#define MAX_PMT_PIDS
Definition: remux.h:340
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition: device.h:137
int getVersionNumber() const
Definition: si.c:84
bool PesHasLength(const uchar *p)
Definition: remux.h:157
#define P_PMT_PID
Definition: remux.c:450
int64_t TsGetPts(const uchar *p, int l)
Definition: remux.c:147
#define KILOBYTE(n)
Definition: tools.h:43
uint16_t CompositionPageId(int i) const
Definition: channels.h:169
int Apid(int i) const
Definition: channels.h:160
unsigned char u_char
Definition: headers.h:24
int getLastSectionNumber() const
Definition: si.c:92
int getPCRPid() const
Definition: section.c:61
bool TsIsScrambled(const uchar *p)
Definition: remux.h:86
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:920
int64_t PesGetDts(const uchar *p)
Definition: remux.h:191
cTsPayload tsPayload
Definition: remux.c:1207
ePesHeader
Definition: remux.h:16
void SetByte(uchar Byte, int Index)
Sets the TS data byte at the given Index to the value Byte.
Definition: remux.c:315
int getTransportStreamId() const
Definition: section.c:26
uchar * esInfoLength
Definition: remux.h:297
#define TS_PAYLOAD_START
Definition: remux.h:36
bool updatePrimaryDevice
Definition: remux.h:363
DescriptorLoop streamDescriptors
Definition: section.h:63
void Reset(void)
Definition: remux.c:252
#define MAXSPIDS
Definition: channels.h:37
bool newFrame
Definition: remux.h:483
bool gotSequenceParameterSet
Definition: remux.c:1218
const char * Slang(int i) const
Definition: channels.h:165
#define TS_SIZE
Definition: remux.h:34
cMpeg2Parser(void)
Definition: remux.c:1132
Definition: remux.h:19
void IncVersion(int &Version)
Definition: remux.c:365
const int * Spids(void) const
Definition: channels.h:159
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:970
uint32_t GetGolombUe(void)
Definition: remux.c:1287
int bit
Definition: remux.c:1209
int log2_max_frame_num
Definition: remux.c:1214
void SetDebug(bool Debug)
Definition: remux.c:1082
int TsPayloadOffset(const uchar *p)
Definition: remux.h:101
bool scanning
Definition: remux.h:493
int offset
Definition: remux.h:425
#define TS_ADAPT_PCR
Definition: remux.h:46
int numIFrames
Definition: remux.h:487
bool NewFrame(void)
Definition: remux.c:1083
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:1018
bool AtTsStart(void)
Returns true if this payload handler is currently pointing to first byte of a TS packet.
Definition: remux.h:238
void TsSetPts(uchar *p, int l, int64_t Pts)
Definition: remux.c:173
const char * I18nNormalizeLanguageCode(const char *Code)
Returns a 3 letter language code that may not be zero terminated.
Definition: i18n.c:238
static int CmpUint32(const void *p1, const void *p2)
Definition: remux.c:1448
void Reset(void)
Resets the parser.
Definition: remux.c:604
int Vtype(void) const
Definition: channels.h:156
Descriptor * getNext(Iterator &it)
Definition: si.c:112
uchar * data
Definition: remux.h:422
uchar * GetPat(void)
Returns a pointer to the PAT section, which consists of exactly one TS packet.
Definition: remux.c:581
int patVersion
Definition: remux.h:346
int vtype
Definition: remux.h:351
#define dbgframes(a...)
Definition: remux.c:24
int pmtVersion
Definition: remux.h:295
bool SkipPesHeader(void)
Skips all bytes belonging to the PES header of the payload.
Definition: remux.c:305
uint32_t scanner
Definition: remux.c:1124
cTsToPes(void)
Definition: remux.c:929
int apids[MAXAPIDS+1]
Definition: remux.h:352
#define MAXAPIDS
Definition: channels.h:35
cAudioParser(void)
Definition: remux.c:1104