vdr 2.8.3
pes2ts.c
Go to the documentation of this file.
1/*
2 * pes2ts.c: Convert legacy PES recordings to TS
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * $Id: pes2ts.c 1.1 2026/07/20 08:25:33 kls Exp $
8 */
9
10#include "pes2ts.h"
11#include "config.h"
12#include "recording.h"
13#include "tools.h"
14
15// --- Helpers ---------------------------------------------------------------
16
17#define USE __attribute__((warn_unused_result)) // marks functions where the retrun value must be checked
18
20
21#define LOGFILESUFFIX "/pes2ts.log"
22
23static void SetLogFileName(const char *FileName)
24{
26 unlink(LogFileName);
27}
28
29static void Log(const char *s)
30{
31 if (FILE *f = fopen(LogFileName, "a")) {
32 fputs(s, f);
33 fputc('\n', f);
34 fclose(f);
35 }
36}
37
38static void Info(int Level, const char *Fmt, ...) __attribute__ ((format (printf, 2, 3)));
39static void Info(int Level, const char *Fmt, ...)
40{
41 va_list ap;
42 va_start(ap, Fmt);
43 cString s = cString::vsprintf(Fmt, ap);
44 if (Level > 1)
45 s = cString::sprintf("%*s%s", (Level - 1) * 3, "", *s);
46 va_end(ap);
47 if (Level <= SysLogLevel) {
48 fputs(s, stdout);
49 fputc('\n', stdout);
50 }
51 Log(s);
52}
53
54static void Warning(const char *Fmt, ...) __attribute__ ((format (printf, 1, 2)));
55static void Warning(const char *Fmt, ...)
56{
57 va_list ap;
58 va_start(ap, Fmt);
59 cString s = cString::vsprintf(Fmt, ap);
60 s = cString::sprintf("WARNING: %s", *s);
61 va_end(ap);
62 fputs(s, stdout);
63 fputc('\n', stdout);
64 Log(s);
65}
66
67static bool Error(const char *Fmt, ...) __attribute__ ((format (printf, 1, 2)));
68static bool Error(const char *Fmt, ...)
69{
70 va_list ap;
71 va_start(ap, Fmt);
72 cString s = cString::vsprintf(Fmt, ap);
73 s = cString::sprintf("ERROR: %s", *s);
74 va_end(ap);
75 fputs(s, stderr);
76 fputc('\n', stderr);
77 Log(s);
78 return false; // convenience return value
79}
80
81// --- CopyMarks -------------------------------------------------------------
82
83#define MARKSFILESUFFIX "/marks"
84
85static USE bool CopyMarks(const char *PesFileName, const char *TsFileName)
86{
87 cString PesMarksFileName = AddDirectory(PesFileName, MARKSFILESUFFIX ".vdr");
88 cString TsMarksFileName = AddDirectory(TsFileName, MARKSFILESUFFIX);
89 if (FILE *fp = fopen(PesMarksFileName, "r")) {
90 Info(2, "copying PES marks file %s to %s", *PesMarksFileName, *TsMarksFileName);
91 if (FILE *ft = fopen(TsMarksFileName, "w")) {
92 cReadLine ReadLine;
93 while (char *s = ReadLine.Read(fp)) {
94 fprintf(ft, "%s\n", s);
95 Info(3, "%s", s);
96 }
97 fclose(ft);
98 }
99 else if (errno != ENOENT) {
100 fclose(fp);
101 return Error("%s: %m", *TsMarksFileName);
102 }
103 fclose(fp);
104 }
105 else if (errno != ENOENT)
106 return Error("%s: %m", *PesMarksFileName);
107 return true; // a missing marks file is not an error
108}
109
110// --- cRecordingTimerId -----------------------------------------------------
111
112// Creates a recording timer id to make sure replay of the resulting TS recording can
113// begin immediately, and makes sure the id is removed when finished.
114
116private:
118public:
119 cRecordingTimerId(const char *FileName);
121 };
122
124{
125 fileName = FileName;
127}
128
133
134// --- cPesIndex -------------------------------------------------------------
135
136// Stripped down version of cIndexFile, just enough to read a PES index file.
137
138struct __attribute__((packed)) tIndexPes {
139 uint32_t offset;
140 uchar type;
141 uchar number;
142 uint16_t reserved;
143 };
144
146private:
148 tIndexPes *index;
149 int last;
150public:
151 cPesIndex(void);
152 ~cPesIndex();
153 USE bool Load(const char *FileName);
154 USE bool IsValid(int Index) { return Index >= 0 && Index <= last; }
155 USE bool Get(int Index, uchar &FileNumber, uint32_t &FileOffset, bool &Independent, int &Length);
156 };
157
158#define INDEXFILESUFFIX "/index.vdr"
159
161{
162 index = NULL;
163 last = -1;
164}
165
166bool cPesIndex::Load(const char *FileName)
167{
169 if (access(fileName, R_OK) == 0) {
170 struct stat buf;
171 if (stat(fileName, &buf) == 0) {
172 int delta = int(buf.st_size % sizeof(tIndexPes));
173 if (delta) {
174 delta = sizeof(tIndexPes) - delta;
175 Warning("invalid file size (%u) in '%s', continuing anyway", uint32_t(buf.st_size), *fileName);
176 // no 'return false' here
177 }
178 last = int((buf.st_size + delta) / sizeof(tIndexPes) - 1);
179 if (last >= 0) {
180 int size = last + 1;
181 index = MALLOC(tIndexPes, size);
182 if (index) {
183 int f = open(fileName, O_RDONLY);
184 if (f >= 0) {
185 if (safe_read(f, index, size_t(buf.st_size)) != buf.st_size) {
186 Error("can't read from file '%s'", *fileName);
187 free(index);
188 last = -1;
189 index = NULL;
190 }
191 close(f);
192 }
193 else
194 Error("%s: %m", *fileName);
195 }
196 else
197 Error("can't allocate %zd bytes for index '%s'", size * sizeof(tIndexPes), *fileName);
198 }
199 else
200 Error("%s: improper file size", *fileName);
201 }
202 else
203 Error("%s: %m", *fileName);
204 }
205 else
206 Error("missing index file %s", *fileName);
207 return index != NULL;
208}
209
211{
212 free(index);
213}
214
215bool cPesIndex::Get(int Index, uchar &FileNumber, uint32_t &FileOffset, bool &Independent, int &Length)
216{
217 if (IsValid(Index)) {
218 FileNumber = index[Index].number;
219 FileOffset = index[Index].offset;
220 Independent = index[Index].type == 1;
221 if (Index < last) {
222 uchar fn = index[Index + 1].number;
223 uint32_t fo = index[Index + 1].offset;
224 if (fn == FileNumber)
225 Length = int(fo - FileOffset);
226 else
227 Length = -1; // this means "everything up to EOF"
228 }
229 else
230 Length = -1;
231 return true;
232 }
233 return false;
234}
235
236// --- cPesResume ------------------------------------------------------------
237
238// Stripped down version of cResumeFile, just enough to read a PES resume file.
239
241private:
242 int index;
243public:
244 cPesResume(void);
245 USE bool Read(const char *FileName);
246 USE bool Write(const char *TsFileName);
247 };
248
249#define RESUMEFILESUFFIX "/resume"
250
252{
253 index = -1;
254}
255
256bool cPesResume::Read(const char *FileName)
257{
258 cString PesResumeFileName = AddDirectory(FileName, RESUMEFILESUFFIX ".vdr");
259 int f = open(PesResumeFileName, O_RDONLY);
260 if (f >= 0) {
261 Info(2, "reading PES resume file %s", *PesResumeFileName);
262 int Read = safe_read(f, &index, sizeof(index));
263 close(f);
264 if (Read != sizeof(index)) {
265 index = -1;
266 return Error("%s: %m", *PesResumeFileName);
267 }
268 Info(3, "index = %d", index);
269 }
270 else if (errno != ENOENT)
271 return Error("%s: %m", *PesResumeFileName);
272 return true; // a missing resume file is not an error
273}
274
275bool cPesResume::Write(const char *TsFileName)
276{
277 if (index >= 0) {
278 cString TsResumeFileName = AddDirectory(TsFileName, RESUMEFILESUFFIX);
279 Info(2, "writing TS resume file %s", *TsResumeFileName);
280 FILE *f = fopen(TsResumeFileName, "w");
281 if (f) {
282 fprintf(f, "I %d\n", index);
283 Info(3, "I %d", index);
284 fclose(f);
285 }
286 else
287 return Error("%s: %m", *TsResumeFileName);
288 }
289 return true;
290}
291
292// --- cPesSummary -----------------------------------------------------------
293
294#define SUMMARYFILESUFFIX "/summary.vdr"
295
297private:
299public:
300 USE bool Read(const char *FileName);
301 const char *Title(void) { return data[0]; }
302 const char *ShortText(void) { return data[1]; }
303 const char *Description(void) { return data[2]; }
304 };
305
306bool cPesSummary::Read(const char *FileName)
307{
308 cString SummaryFileName = AddDirectory(FileName, SUMMARYFILESUFFIX);
309 if (FILE *f = fopen(SummaryFileName, "r")) {
310 Info(2, "reading PES summary file %s", *SummaryFileName);
311 int line = 0;
312 cReadLine ReadLine;
313 while (char *s = ReadLine.Read(f)) {
314 if (*s) {
315 if (*data[line]) {
316 data[line].Append("|");
317 data[line].Append(s);
318 }
319 else
320 data[line] = s;
321 if (line < 2)
322 line++;
323 }
324 }
325 fclose(f);
326 if (*data[1] && strlen(data[1]) > 80) {
327 // If line 1 is too long, it can't be the short text,
328 // so assume the short text is missing and concatenate
329 // line 1 and line 2 to be the description:
330 if (*data[2]) {
331 data[1].Append("|");
332 data[1].Append(data[2]);
333 }
334 data[2] = data[1];
335 data[1] = NULL;
336 }
337 Info(3, "Title = '%s'", *data[0]);
338 Info(3, "Short text = '%s'", *data[1]);
339 Info(3, "Description = '%s'", *data[2]);
340 }
341 else if (errno != ENOENT)
342 return Error("%s: %m", *SummaryFileName);
343 return true; // a missing summary file is not an error
344}
345
346// --- cPesInfo --------------------------------------------------------------
347
348class cPesInfo {
349private:
352public:
353 USE bool Read(const char *FileName);
354 USE bool Write(const char *TsFileName);
356 };
357
358#define INFOFILESUFFIX "/info"
359
360bool cPesInfo::Read(const char *FileName)
361{
362 cString InfoFileName = AddDirectory(FileName, INFOFILESUFFIX ".vdr");
363 if (access(InfoFileName, R_OK) == 0) {
364 Info(2, "reading PES info file %s", *InfoFileName);
365 if (FILE *f = fopen(InfoFileName, "r")) {
366 cReadLine ReadLine;
367 while (char *s = ReadLine.Read(f)) {
368 if (*s == 'X') {
369 int i = components.NumComponents();
371 if (!*components.Component(i)->language) {
372 // This is a dummy component, inserted to handle an offset in the PES file.
373 // We need it during the conversion from PES to TS, but not in the final TS info file.
374 Info(3, "%s - skipped!", s);
375 continue;
376 }
377 }
378 info.Append(strdup(s));
379 Info(3, "%s", s);
380 }
381 fclose(f);
382 }
383 else
384 return Error("%s: %m", *InfoFileName);
385 }
386 else {
387 cPesSummary PesSummary;
388 if (PesSummary.Read(FileName)) {
389 if (PesSummary.Title())
390 info.Append(strdup(cString::sprintf("T %s", PesSummary.Title())));
391 if (PesSummary.ShortText())
392 info.Append(strdup(cString::sprintf("S %s", PesSummary.ShortText())));
393 if (PesSummary.Description())
394 info.Append(strdup(cString::sprintf("D %s", PesSummary.Description())));
395 }
396 else
397 return false;
398 }
399 Info(2, "taking priority and lifetime from PES info file name %s", *InfoFileName);
400 int Lifetime = MAXLIFETIME;
401 int Priority = MAXPRIORITY;
402 int n = 0;
403 int len = strlen(FileName);
404 const char *p = FileName + len - 1;
405 while (len-- > 0) {
406 if (*p == '.') {
407 n++;
408 if (n == 2)
409 Lifetime = atoi(p + 1);
410 else if (n == 3) {
411 Priority = atoi(p + 1);
412 break;
413 }
414 }
415 p--;
416 }
417 info.Append(strdup(cString::sprintf("L %d", Lifetime)));
418 info.Append(strdup(cString::sprintf("P %d", Priority)));
419 Info(3, "priority = %d", Priority);
420 Info(3, "lifetime = %d", Lifetime);
421 return true;
422}
423
424bool cPesInfo::Write(const char *TsFileName)
425{
426 cString InfoFileName = AddDirectory(TsFileName, INFOFILESUFFIX);
427 Info(2, "writing TS info file %s", *InfoFileName);
428 if (FILE *f = fopen(InfoFileName, "w")) {
429 for (int i = 0; i < info.Size(); i++) {
430 fprintf(f, "%s\n", info[i]);
431 Info(3, "%s", info[i]);
432 }
433 fclose(f);
434 return true;
435 }
436 return Error("%s: %m", *InfoFileName);
437}
438
439// --- cPesFile --------------------------------------------------------------
440
441class cPesFile {
442private:
445 int file;
446 uint32_t size;
448public:
449 cPesFile(void);
450 ~cPesFile();
451 USE bool Init(const char *FileName);
452 // Initializes the PES file handler.
453 // Returns false in case of an error.
454 USE bool Open(int Number);
455 // Opens the PES recording file with the given number.
456 // Returns false in case of an error.
457 void Close(void);
458 // Closes the currently open PES file.
459 USE const uchar *Read(int Number, uint32_t Offset, int Length, int &Bytes);
460 // Reads Length bytes at Offset from the PES file with the given Number.
461 // Returns a pointer to the read bytes. Bytes returns the number of
462 // bytes actually read, which must normally be equal to Length, but
463 // is the remaining number of bytes in the PES file if Length is -1.
464 // Returns NULL in case of an error.
465 };
466
467#define MAXFILESPERRECORDINGPES 255
468#define RECORDFILESUFFIXPES "/%03d.vdr"
469
471{
472 number = 0;
473 file = -1;
474 size = 0;
475}
476
478{
479 Close();
480}
481
482bool cPesFile::Init(const char *FileName)
483{
484 fileName = FileName;
485 return true;
486}
487
488bool cPesFile::Open(int Number)
489{
490 if (number == Number)
491 return true;
492 Close();
493 if (0 < Number && Number <= MAXFILESPERRECORDINGPES) {
495 if (access(Name, R_OK) == 0) {
496 file = open(Name, O_RDONLY | O_LARGEFILE);
497 if (file >= 0) {
498 size = FileSize(Name);
499 return true;
500 }
501 }
502 Error("%s: %m", *Name);
503 }
504 else
505 Error("invalid file number '%d'", Number);
506 return false;
507}
508
510{
511 if (file >= 0) {
512 close(file);
513 file = -1;
514 }
515}
516
517const uchar *cPesFile::Read(int Number, uint32_t Offset, int Length, int &Bytes)
518{
519 if (Length < 0)
520 Length = size - Offset;
521 if (Length <= MAXFRAMESIZE) {
522 if (Open(Number)) {
523 if (Offset >= 0 && lseek(file, Offset, SEEK_SET) == Offset) {
524 Bytes = safe_read(file, frame, Length);
525 if (Bytes == Length)
526 return frame;
527 Error("invalid length '%d', only read '%d' bytes", Length, Bytes);
528 }
529 else
530 Error("invalid offset '%u'", Offset);
531 }
532 else
533 Error("invalid number '%d'", Number);
534 }
535 else
536 Error("invalid length '%d'", Length);
537 return NULL;
538}
539
540// --- cPesReader ------------------------------------------------------------
541
543private:
546public:
547 USE bool Init(const char *FileName);
548 // Initializes this PES reader.
549 USE bool HasData(int Index) { return index.IsValid(Index); }
550 // Returns true if data exists for the given Index.
551 const uchar *ReadPesFrame(int Index, bool &Independent, int &Bytes);
552 // Reads one complete PES frame at the given Index.
553 // Returns a pointer to the read bytes. Bytes returns the number of
554 // bytes actually read. Independent is true if this in an I-frame.
555 // Returns NULL in case of an error.
556 };
557
558bool cPesReader::Init(const char *FileName)
559{
560 return file.Init(FileName) && index.Load(FileName);
561}
562
563const uchar *cPesReader::ReadPesFrame(int Index, bool &Independent, int &Bytes)
564{
565 uchar FileNumber;
566 uint32_t FileOffset;
567 int Length;
568 if (index.Get(Index, FileNumber, FileOffset, Independent, Length))
569 return file.Read(FileNumber, FileOffset, Length, Bytes);
570 return NULL;
571}
572
573// --- cPesSubtitleAssembler -------------------------------------------------
574
575#define MAX_PES_PACKET_SIZE (0xFFFF + 6) // 6 = PES header size
576
578private:
580 int size;
584public:
587 USE bool Put(const uchar *Data, int Length);
588 const uchar *Get(int &Length);
589 uint16_t CompositionPageId(void) { return compositionPageId; }
590 };
591
600
605
606bool cPesSubtitleAssembler::Put(const uchar *Data, int Length)
607{
608 if (Data && PesLongEnough(Length)) {
609 int PayloadOffset = PesPayloadOffset(Data);
610 int SubstreamHeaderLength = 4;
611 bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
612
613 // Compatibility mode for old subtitles plugin:
614 if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) {
615 SubstreamHeaderLength = 0;
616 ResetSubtitleAssembler = Data[8] >= 5; // has PTS
617 }
618
619 if (ResetSubtitleAssembler)
620 length = 0;
621
622 if (length == 0) {
623 // First packet, take header:
624 memcpy(data, Data, PayloadOffset);
625 length += PayloadOffset;
626 }
627 Data += PayloadOffset + SubstreamHeaderLength;
628 Length -= (PayloadOffset + SubstreamHeaderLength);
629 if (length + Length > size)
630 return Error("packet too large in subtitle assembler");
631 if (compositionPageId == 0) {
632 for (int i = 0; i < Length; i++) {
633 if (Data[i] == 0x0F && i + 3 < Length) {
634 compositionPageId = (Data[i + 2] << 8) | Data[i + 3];
635 break;
636 }
637 }
638 }
639 memcpy(data + length, Data, Length);
640 length += Length;
641 uint16_t NewLength = length - 6;
642 data[4] = NewLength >> 8;
643 data[5] = NewLength & 0xFF;
644 // Check for end of packet
645 uchar *p = data + length - 1;
646 if (Length < 2048 && *p == 0xFF)
647 complete = true;
648 if (*p-- == 0xFF && *p-- == 0x00 && *p-- == 0x00 && *p-- == (compositionPageId & 0xFF) && *p-- == (compositionPageId >> 8) && *p-- == 0x80 && *p-- == 0x0F)
649 complete = true;
650 return true;
651 }
652 return Error("no data for subtitle assembler");
653}
654
656{
657 if (complete && length > 0) {
658 Length = length;
659 length = 0;
660 complete = false;
661 return data;
662 }
663 return NULL;
664}
665
666// --- cTsGenerator ----------------------------------------------------------
667
669private:
670 int pid;
672public:
673 cTsGenerator(int Pid);
674 // Sets up a TS generator for the given Pid.
675 int GenerateTs(const uchar *Data, int Length, bool Pusi, uchar *Dest);
676 // Generates one TS packet from the first bytes in Data, and writes the result
677 // (which is always exactly TS_SIZE bytes in size) to Dest. If Pusi is true, the
678 // "payload unit start indicator" flag will be set.
679 // Returns the number of bytes used from Data.
680 };
681
683{
684 pid = Pid;
685 counter = 0;
686}
687
688int cTsGenerator::GenerateTs(const uchar *Data, int Length, bool Pusi, uchar *Dest)
689{
690 uchar *p = Dest;
691 *p++ = TS_SYNC_BYTE; // TS indicator
692 *p++ = (Pusi ? TS_PAYLOAD_START : 0) | (pid >> 8); // flags (3), pid hi (5)
693 *p++ = pid & 0xFF; // pid low
694 int Payload = TS_SIZE - 4; // maximum payload after TS header
695 int Fill = Payload - Length;
696 if (Fill > 0) {
697 // Last TS packet, use adaptation field for stuffing:
698 *p++ = 0x30 | counter; // adaptation field + payload
699 if (Fill == 1) // minimal adaptation field, only length byte
700 *p++ = 0x00; // adaptation_field_length = 0
701 else { // normal adaptation field with stuffing
702 *p++ = Fill - 1; // adaptation_field_length
703 *p++ = 0x00; // adaptation flags
704 if (Fill > 2)
705 memset(p, 0xFF, Fill - 2); // stuffing bytes
706 p += Fill - 2;
707 }
708 // Copy remaining PES data into payload
709 memcpy(p, Data, Length);
710 Payload = Length;
711 }
712 else {
713 *p++ = 0x10 | counter; // payload only
714 memcpy(p, Data, Payload);
715 }
716 counter = (counter + 1) & 0x0F;
717 return Payload; // number of PES bytes written into this TS packet
718}
719
720// --- cPatPmtMaker ----------------------------------------------------------
721
722#define PID_BASE_VIDEO 101
723#define PID_BASE_AUDIO 201
724#define PID_BASE_DOLBY 301
725#define PID_BASE_SUBT 401
726#define PID_BASE_LCPM 501
727
729private:
730 int vpid;
731 int vtype;
732 int ppid;
742 cVector<uint16_t> compositionPageIds; // also used for ancillaryPageIds
745public:
746 cPatPmtMaker(cComponents *Components);
747 void ResetPids(void);
748 USE bool ListPids(void);
749 void MakePatPmt(void);
750 void SetVpid(int Vpid, int Vtype);
751 void SetApid(int Apid, int Atype, int Index);
752 void SetDpid(int Dpid, int Dtype, int Index);
753 void SetSpid(int Spid, uchar Stype, uint16_t CompositionPageId, int Index);
754 };
755
757{
758 components = Components;
759 ResetPids();
760}
761
763{
764 vpid = 0;
765 vtype = 0;
766 ppid = 0;
767 apids.Clear();
768 atypes.Clear();
769 memset(alangs, 0, sizeof(alangs));
770 dpids.Clear();
771 dtypes.Clear();
772 memset(dlangs, 0, sizeof(dlangs));
773 spids.Clear();
774 stypes.Clear();
775 memset(slangs, 0, sizeof(slangs));
777 usedPids.Clear();
778}
779
781{
782 bool HasPids = false;
783 if (vpid) {
784 Info(3, "vpid = %d %02X", vpid, vtype);
785 HasPids = true;
786 }
787 for (int i = 0; apids[i]; i++) {
788 Info(3, "apid[%d] = %d %02X '%s'", i, apids[i], atypes[i], alangs[i]);
789 HasPids = true;
790 }
791 for (int i = 0; dpids[i]; i++) {
792 Info(3, "dpid[%d] = %d %02X '%s'", i, dpids[i], dtypes[i], dlangs[i]);
793 HasPids = true;
794 }
795 for (int i = 0; spids[i]; i++) {
796 Info(3, "spid[%d] = %d %02X '%s' %d", i, spids[i], stypes[i], slangs[i], compositionPageIds[i]);
797 HasPids = true;
798 }
799 if (!HasPids)
800 return Error("no PIDs found");
801 return true;
802}
803
808
809void cPatPmtMaker::SetVpid(int Vpid, int Vtype)
810{
811 if (vpid != Vpid) {
812 vpid = Vpid;
813 vtype = Vtype;
814 ppid = vpid;
815 MakePatPmt();
816 }
817}
818
819template<class T> int AppendZeroTerminated(cVector<T> &Pids, T Pid)
820{
821 int i = Pids.Size();
822 if (i > 0)
823 i--;
824 Pids[i] = Pid;
825 Pids[i + 1] = 0;
826 return i;
827}
828
829void cPatPmtMaker::SetApid(int Apid, int Atype, int Index)
830{
831 if (!usedPids[Apid]) {
832 int i = AppendZeroTerminated(apids, Apid);
833 atypes.Append(Atype);
834 if (tComponent *Component = components->GetComponent(Index, 2, 0))
835 strn0cpy(alangs[i], Component->language, MAXLANGCODE2);
836 MakePatPmt();
837 usedPids[Apid] = true;
838 }
839}
840
841void cPatPmtMaker::SetDpid(int Dpid, int Dtype, int Index)
842{
843 if (!usedPids[Dpid]) {
844 int i = AppendZeroTerminated(dpids, Dpid);
845 dtypes.Append(Dtype);
846 if (tComponent *Component = components->GetComponent(Index, 2, 0))
847 strn0cpy(dlangs[i], Component->language, MAXLANGCODE2);
848 MakePatPmt();
849 usedPids[Dpid] = true;
850 }
851}
852
853void cPatPmtMaker::SetSpid(int Spid, uchar Stype, uint16_t CompositionPageId, int Index)
854{
855 if (!usedPids[Spid]) {
856 int i = AppendZeroTerminated(spids, Spid);
857 stypes.Append(Stype);
858 if (tComponent *Component = components->GetComponent(Index, 3, 0))
859 strn0cpy(slangs[i], Component->language, MAXLANGCODE2);
860 compositionPageIds.Append(CompositionPageId);
861 MakePatPmt();
862 usedPids[Spid] = true;
863 }
864}
865
866// --- cPes2TsConverter ------------------------------------------------------
867
868#define MIN_PRE_1_3_19_PRIVATESTREAM 10 // the minimum number of unknown PS1 packets to consider this a "pre 1.3.19 private stream"
869
870#define MAX_SUBSTREAMS 32
871#define MAX_PIDS (PID_BASE_LCPM + MAX_SUBSTREAMS)
872
874private:
882 USE bool MakeTs(const uchar *Data, int Length, int Pid);
883 // Converts the PES packet in Data to a sequence of TS packets for Pid and stores them
884 // in the member variable frame.
885 // Returns false in case of an error.
886 USE bool ProcessPesPacket(const uchar *Data, int Length);
887 // Processes the PES packet in Data.
888 // Returns false in case of an error.
889public:
890 cPes2TsConverter(cComponents *Components);
891 // Components contains the data of the stream components, if available.
894 void SetAnalyzing(bool Analyzing) { analyzing = Analyzing; }
895 // Sets this converter to analyzing mode.
896 void ResetPids(void);
897 // Resets any PIDs detected so far.
898 USE bool ListPids(void);
899 // Lists all PIDs detected so far.
900 // Returns true is any PIDs have been found,
901 USE bool ConvertPesFrame(const uchar *Data, int Length, bool Independent);
902 // Converts the complete PES frame in Data into a TS frame.
903 // If Independent is true, this is an I-frame.
904 // Returns false in case of an error.
905 const uchar *TsFrame(int &Length) { Length = length; return frame; }
906 // Returns the resulting TS frame and its Length.
907 // The result is only valid if the previous call to ConvertPesFrame() has returned true.
908 };
909
911:patPmtMaker(Components)
912{
914 memset(tsGenerators, 0, sizeof(tsGenerators));
916 memset(frame, 0, sizeof(frame));
917 length = 0;
918 analyzing = false;
919}
920
922{
923 for (int i = 0; i < MAX_PIDS; i++)
924 delete tsGenerators[i];
925 for (int i = 0; i < MAX_SUBSTREAMS; i++)
926 delete pesSubtitleAssemblers[i];
927}
928
933
935{
936 return patPmtMaker.ListPids();
937}
938
939bool cPes2TsConverter::MakeTs(const uchar *Data, int Length, int Pid)
940{
941 if (0 <= Pid && Pid < MAX_PIDS) {
942 cTsGenerator *TsGenerator = tsGenerators[Pid];
943 if (!TsGenerator)
944 TsGenerator = tsGenerators[Pid] = new cTsGenerator(Pid);
945 uchar *p = frame + length;
946 bool First = true;
947 while (Length > 0) {
949 return Error("frame size exceeded");
950 int w = TsGenerator->GenerateTs(Data, Length, First, p);
951 if (w > 0) {
952 Data += w;
953 Length -= w;
954 length += TS_SIZE;
955 p += TS_SIZE;
956 }
957 else
958 return false;
959 First = false;
960 }
961 }
962 else
963 return Error("invalid PID '%d'", Pid);
964 return true;
965}
966
967bool cPes2TsConverter::ProcessPesPacket(const uchar *Data, int Length)
968{
969 uchar c = Data[3];
970 switch (c) {
971 case 0xBE: // padding stream, needed for MPEG1
972 break;
973 case 0xE0 ... 0xEF: { // video
974 int Vpid = PID_BASE_VIDEO + c - 0xE0;
975 patPmtMaker.SetVpid(Vpid, 0x02); // assuming STREAMTYPE_13818_VIDEO
976 if (!MakeTs(Data, Length, Vpid))
977 return false;
978 break;
979 }
980 case 0xC0 ... 0xDF: { // audio
981 int Index = c - 0xC0;
982 int Apid = PID_BASE_AUDIO + Index;
983 patPmtMaker.SetApid(Apid, 0x04, Index); // assuming STREAMTYPE_13818_AUDIO
984 if (!MakeTs(Data, Length, Apid))
985 return false;
986 break;
987 }
988 case 0xBD: { // private stream 1
989 int PayloadOffset = Data[8] + 9;
990
991 // Compatibility mode for old subtitles plugin:
992 if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81)
993 PayloadOffset--;
994
995 uchar SubStreamId = Data[PayloadOffset];
996 uchar SubStreamType = SubStreamId & 0xF0; // deliberately overlapping masks:
997 uchar SubStreamIndex = SubStreamId & 0x1F; // 0x20..0x3F become subtitle indices 0..31.
998
999 // Compatibility mode for old VDR recordings, where 0xBD was only AC3:
1000pre_1_3_19_PrivateStreamDetected:
1002 SubStreamId = c;
1003 SubStreamType = 0x80;
1004 SubStreamIndex = 0;
1005 }
1006 else if (pre_1_3_19_PrivateStream)
1007 pre_1_3_19_PrivateStream--; // every known PS1 packet counts down towards 0 to recover from glitches...
1008
1009 switch (SubStreamType) {
1010 case 0x20: // SPU
1011 case 0x30: { // SPU
1012 int Spid = PID_BASE_SUBT + SubStreamIndex;
1013 cPesSubtitleAssembler *PesSubtitleAssembler = pesSubtitleAssemblers[SubStreamIndex];
1014 if (!PesSubtitleAssembler)
1015 PesSubtitleAssembler = pesSubtitleAssemblers[SubStreamIndex] = new cPesSubtitleAssembler;
1016 if (!PesSubtitleAssembler->Put(Data, Length))
1017 return false;
1018 Data = PesSubtitleAssembler->Get(Length);
1019 if (Data && !MakeTs(Data, Length, Spid))
1020 return false;
1021 uint16_t CompositionPageId = PesSubtitleAssembler->CompositionPageId();
1022 patPmtMaker.SetSpid(Spid, SubStreamType, CompositionPageId, SubStreamIndex);
1023 break;
1024 }
1025 case 0x80: { // AC3 & DTS
1026 uchar buf[Length];
1027 const uchar *p = Data + PayloadOffset;
1028 if (*p++ == 0x80 && *p++ == 0x01 && *p++ == 0x00 && *p++ == 0x01) {
1029 // remove VDR's AC-3 header:
1030 memcpy(buf, Data, PayloadOffset);
1031 memcpy(buf + PayloadOffset, Data + PayloadOffset + 4, Length - (PayloadOffset + 4));
1032 Length -= 4;
1033 int PacketLength = Length - 6;
1034 buf[4] = PacketLength >> 8;
1035 buf[5] = PacketLength & 0xFF;
1036 Data = buf;
1037 }
1038 int Dpid = PID_BASE_DOLBY + SubStreamIndex;
1039 patPmtMaker.SetDpid(Dpid, 0x6A, SubStreamIndex);
1040 if (!MakeTs(Data, Length, Dpid))
1041 return false;
1042 break;
1043 }
1044 case 0xA0: { // LPCM
1045 if (!analyzing) {
1046 Warning("don't know what to do with LPCM");
1047 analyzing = true; // quick way of making sure this message is issued only once
1048 }
1049 /*
1050 int Apid = PID_BASE_LCPM + SubStreamIndex;
1051 patPmtMaker.SetApid(Apid, 0x04, SubStreamIndex);
1052 if (!MakeTs(Data, Length, Apid))
1053 return false;
1054 */
1055 break;
1056 }
1057 default:
1058 // Compatibility mode for old VDR recordings, where 0xBD was only AC3:
1060 pre_1_3_19_PrivateStream += 2; // ...and every unknown PS1 packet counts up (the very first one counts twice, but that's ok)
1063 goto pre_1_3_19_PrivateStreamDetected;
1064 }
1065 }
1066 }
1067 }
1068 break;
1069 default:
1070 return Error("unexpected packet id %02X", c);
1071 }
1072 return true;
1073}
1074
1075bool cPes2TsConverter::ConvertPesFrame(const uchar *Data, int Length, bool Independent)
1076{
1077 length = 0;
1078 if (Independent)
1079 length += patPmtMaker.GetPatPmt(frame, sizeof(frame));
1080 int i = 0;
1081 while (i <= Length - 6) {
1082 if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
1083 int l = PesLength(Data + i);
1084 if (i + l > Length)
1085 return Error("incomplete PES packet");
1086 if (!ProcessPesPacket(Data + i, l))
1087 return false;
1088 i += l;
1089 }
1090 else {
1091 Warning("syncing on PES data!");
1092 i++;
1093 }
1094 }
1095 if (i < Length)
1096 return Error("leftover PES data");
1097 return true;
1098}
1099
1100// --- cTsWriter -------------------------------------------------------------
1101
1103private:
1105 int f;
1106 off_t offset;
1108public:
1109 cTsWriter(void);
1110 ~cTsWriter();
1111 USE bool Open(const char *FileName);
1112 USE bool Write(const uchar *Data, int Length, bool Independent);
1113 };
1114
1116{
1117 f = -1;
1118 offset = 0;
1119 index = NULL;
1120}
1121
1122bool cTsWriter::Open(const char *FileName)
1123{
1124 cString TsIndexFileName = cIndexFile::IndexFileName(FileName, false);
1125 if (access(TsIndexFileName, R_OK) == 0) {
1126 Info(2, "deleting existing TS index file %s", *TsIndexFileName);
1127 if (unlink(TsIndexFileName) < 0)
1128 return Error("%s: %m", *TsIndexFileName);
1129 }
1130 tsRecordingFileName = AddDirectory(FileName, "00001.ts");
1131 Info(2, "generating TS recording file %s", *tsRecordingFileName);
1132 f = open(tsRecordingFileName, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
1133 if (f > 0) {
1134 Info(2, "generating TS index file %s", *TsIndexFileName);
1135 index = new cIndexFile(FileName, true);
1136 }
1137 else
1138 return Error("%s: %m", *tsRecordingFileName);
1139 return true;
1140}
1141
1143{
1144 close(f);
1145 delete index;
1146}
1147
1148bool cTsWriter::Write(const uchar *Data, int Length, bool Independent)
1149{
1150 int Written = write(f, Data, Length);
1151 if (Written < 0)
1152 return Error("%s: %m", *tsRecordingFileName);
1153 if (index->Write(Independent, 1, offset)) {
1154 offset += Length;
1155 return true;
1156 }
1157 return false;
1158}
1159
1160// --- Pes2Ts ----------------------------------------------------------------
1161
1162#define NUM_FRAMES_ANALYZE 100
1163
1164bool Pes2Ts(const char *PesFileName, const char *TsFileName)
1165{
1166 if (!DirectoryOk(PesFileName))
1167 return Error("'%s' is not a directory", PesFileName);
1168 if (!DirectoryOk(TsFileName))
1169 return Error("'%s' is not a directory", TsFileName);
1170 char p1[PATH_MAX];
1171 char p2[PATH_MAX];
1172 if (realpath(PesFileName, p1) && realpath(TsFileName, p2)) {
1173 if (strcmp(p1, p2) == 0)
1174 return Error("PES and TS file names must be different");
1175 }
1176 else
1177 return Error("%m");
1178 SetLogFileName(TsFileName);
1179 Info(1, "processing PES recording %s", PesFileName);
1180 cPesInfo PesInfo;
1181 if (!PesInfo.Read(PesFileName))
1182 return false;
1183 if (!PesInfo.Write(TsFileName))
1184 return false;
1185 if (!CopyMarks(PesFileName, TsFileName))
1186 return false;
1187 cPesResume PesResume;
1188 if (!PesResume.Read(PesFileName))
1189 return false;
1190 if (!PesResume.Write(TsFileName))
1191 return false;
1192 cPesReader PesReader;
1193 if (!PesReader.Init(PesFileName))
1194 return false;
1195 cPes2TsConverter Pes2TsConverter(PesInfo.Components());
1196
1197 Info(2, "analyzing PES recording");
1198 Pes2TsConverter.SetAnalyzing(true);
1199 for (int Index = 0; Index < NUM_FRAMES_ANALYZE; Index++) {
1200 if (PesReader.HasData(Index)) {
1201 bool Independent = false;
1202 int Bytes = 0;
1203 if (const uchar *PesFrame = PesReader.ReadPesFrame(Index, Independent, Bytes)) {
1204 if (Pes2TsConverter.ConvertPesFrame(PesFrame, Bytes, Independent)) {
1205 if (Pes2TsConverter.IsPre_1_3_19_PrivateStream()) {
1206 Info(3, "recording %s is pre 1.3.19", PesFileName);
1207 break;
1208 }
1209 }
1210 else
1211 return false;
1212 }
1213 }
1214 else
1215 break;
1216 }
1217 if (Pes2TsConverter.IsPre_1_3_19_PrivateStream()) {
1218 Info(2, "analyzing pre 1.3.19 PES recording");
1219 Pes2TsConverter.ResetPids();
1220 for (int Index = 0; Index < NUM_FRAMES_ANALYZE; Index++) {
1221 if (PesReader.HasData(Index)) {
1222 bool Independent = false;
1223 int Bytes = 0;
1224 if (const uchar *PesFrame = PesReader.ReadPesFrame(Index, Independent, Bytes)) {
1225 if (!Pes2TsConverter.ConvertPesFrame(PesFrame, Bytes, Independent))
1226 return false;
1227 }
1228 }
1229 else
1230 break;
1231 }
1232 }
1233 Pes2TsConverter.SetAnalyzing(false);
1234 if (!Pes2TsConverter.ListPids())
1235 return false;
1236
1237 cTsWriter TsWriter;
1238 if (!TsWriter.Open(TsFileName))
1239 return false;
1240 cRecordingTimerId RecordingTimerId(TsFileName);
1241 int Index = 0;
1242 while (PesReader.HasData(Index)) {
1243 bool Independent = false;
1244 int Bytes = 0;
1245 if (const uchar *PesFrame = PesReader.ReadPesFrame(Index, Independent, Bytes)) {
1246 if (Pes2TsConverter.ConvertPesFrame(PesFrame, Bytes, Independent)) {
1247 int Length;
1248 const uchar *TsFrame = Pes2TsConverter.TsFrame(Length);
1249 if (!TsWriter.Write(TsFrame, Length, Independent))
1250 return false;
1251 }
1252 else
1253 return false;
1254 }
1255 else
1256 return Error("no data for index '%d'", Index);
1257 Index++;
1258 }
1259 Info(1, "SUCCESS!");
1260 return true;
1261}
#define MAXLANGCODE2
Definition channels.h:37
#define MAXDPIDS
Definition channels.h:32
#define MAXAPIDS
Definition channels.h:31
#define MAXSPIDS
Definition channels.h:33
tComponent * GetComponent(int Index, uchar Stream, uchar Type)
Definition epg.c:97
tComponent * Component(int Index) const
Definition epg.h:65
int NumComponents(void) const
Definition epg.h:62
void SetComponent(int Index, const char *s)
Definition epg.c:77
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset, bool Errors=false, bool Missing=false)
Definition recording.c:3108
cIndexFile(const char *FileName, bool Record, bool IsPesRecording=false, bool PauseLive=false)
Definition recording.c:2916
static cString IndexFileName(const char *FileName, bool IsPesRecording)
Definition recording.c:3014
cPatPmtGenerator(const cChannel *Channel=NULL)
Definition remux.c:367
void SetPids(int Vpid, int Vtype, int Ppid, int Tpid, const int *Apids, const int *Atypes, const char Alangs[][MAXLANGCODE2], const int *Dpids, const int *Dtypes, const char Dlangs[][MAXLANGCODE2], const int *Spids, const uchar *Stypes, const char Slangs[][MAXLANGCODE2], const uint16_t *CompositionPageIds, const uint16_t *AncillaryPageIds)
Sets the PIDs this PAT/PMT shall contain.
Definition remux.c:515
int GetPatPmt(uchar *Dest, int Size)
Writes the PAT and all PMT packets to Dest.
Definition remux.c:613
char dlangs[MAXDPIDS][MAXLANGCODE2]
Definition pes2ts.c:738
cVector< uint16_t > compositionPageIds
Definition pes2ts.c:742
cComponents * components
Definition pes2ts.c:744
cVector< int > spids
Definition pes2ts.c:739
cVector< int > apids
Definition pes2ts.c:733
cPatPmtMaker(cComponents *Components)
Definition pes2ts.c:756
void ResetPids(void)
Definition pes2ts.c:762
USE bool ListPids(void)
Definition pes2ts.c:780
cVector< uchar > stypes
Definition pes2ts.c:740
cVector< int > dpids
Definition pes2ts.c:736
void MakePatPmt(void)
Definition pes2ts.c:804
void SetVpid(int Vpid, int Vtype)
Definition pes2ts.c:809
void SetDpid(int Dpid, int Dtype, int Index)
Definition pes2ts.c:841
cVector< bool > usedPids
Definition pes2ts.c:743
void SetApid(int Apid, int Atype, int Index)
Definition pes2ts.c:829
cVector< int > atypes
Definition pes2ts.c:734
void SetSpid(int Spid, uchar Stype, uint16_t CompositionPageId, int Index)
Definition pes2ts.c:853
char slangs[MAXSPIDS][MAXLANGCODE2]
Definition pes2ts.c:741
cVector< int > dtypes
Definition pes2ts.c:737
char alangs[MAXAPIDS][MAXLANGCODE2]
Definition pes2ts.c:735
int pre_1_3_19_PrivateStream
Definition pes2ts.c:875
USE bool ListPids(void)
Definition pes2ts.c:934
USE bool ProcessPesPacket(const uchar *Data, int Length)
Definition pes2ts.c:967
cPes2TsConverter(cComponents *Components)
Definition pes2ts.c:910
USE bool ConvertPesFrame(const uchar *Data, int Length, bool Independent)
Definition pes2ts.c:1075
cPatPmtMaker patPmtMaker
Definition pes2ts.c:876
USE bool MakeTs(const uchar *Data, int Length, int Pid)
Definition pes2ts.c:939
const uchar * TsFrame(int &Length)
Definition pes2ts.c:905
void SetAnalyzing(bool Analyzing)
Definition pes2ts.c:894
cPesSubtitleAssembler * pesSubtitleAssemblers[MAX_SUBSTREAMS]
Definition pes2ts.c:878
bool IsPre_1_3_19_PrivateStream(void)
Definition pes2ts.c:893
void ResetPids(void)
Definition pes2ts.c:929
uchar frame[MAXFRAMESIZE]
Definition pes2ts.c:879
cTsGenerator * tsGenerators[MAX_PIDS]
Definition pes2ts.c:877
int file
Definition pes2ts.c:445
USE const uchar * Read(int Number, uint32_t Offset, int Length, int &Bytes)
Definition pes2ts.c:517
~cPesFile()
Definition pes2ts.c:477
cPesFile(void)
Definition pes2ts.c:470
USE bool Open(int Number)
Definition pes2ts.c:488
void Close(void)
Definition pes2ts.c:509
uint32_t size
Definition pes2ts.c:446
USE bool Init(const char *FileName)
Definition pes2ts.c:482
uchar frame[MAXFRAMESIZE]
Definition pes2ts.c:447
int number
Definition pes2ts.c:444
cString fileName
Definition pes2ts.c:443
cPesIndex(void)
Definition pes2ts.c:160
USE bool Get(int Index, uchar &FileNumber, uint32_t &FileOffset, bool &Independent, int &Length)
Definition pes2ts.c:215
USE bool Load(const char *FileName)
Definition pes2ts.c:166
cString fileName
Definition pes2ts.c:147
USE bool IsValid(int Index)
Definition pes2ts.c:154
~cPesIndex()
Definition pes2ts.c:210
tIndexPes * index
Definition pes2ts.c:148
int last
Definition pes2ts.c:149
cComponents * Components(void)
Definition pes2ts.c:355
cStringList info
Definition pes2ts.c:350
cComponents components
Definition pes2ts.c:351
USE bool Write(const char *TsFileName)
Definition pes2ts.c:424
USE bool Read(const char *FileName)
Definition pes2ts.c:360
cPesFile file
Definition pes2ts.c:545
const uchar * ReadPesFrame(int Index, bool &Independent, int &Bytes)
Definition pes2ts.c:563
USE bool HasData(int Index)
Definition pes2ts.c:549
USE bool Init(const char *FileName)
Definition pes2ts.c:558
cPesIndex index
Definition pes2ts.c:544
USE bool Read(const char *FileName)
Definition pes2ts.c:256
cPesResume(void)
Definition pes2ts.c:251
int index
Definition pes2ts.c:242
USE bool Write(const char *TsFileName)
Definition pes2ts.c:275
cPesSubtitleAssembler(void)
Definition pes2ts.c:592
uint16_t compositionPageId
Definition pes2ts.c:582
const uchar * Get(int &Length)
Definition pes2ts.c:655
USE bool Put(const uchar *Data, int Length)
Definition pes2ts.c:606
uint16_t CompositionPageId(void)
Definition pes2ts.c:589
const char * Description(void)
Definition pes2ts.c:303
const char * Title(void)
Definition pes2ts.c:301
cString data[3]
Definition pes2ts.c:298
USE bool Read(const char *FileName)
Definition pes2ts.c:306
const char * ShortText(void)
Definition pes2ts.c:302
char * Read(FILE *f)
Definition tools.c:1574
cString fileName
Definition pes2ts.c:117
cRecordingTimerId(const char *FileName)
Definition pes2ts.c:123
char SVDRPHostName[HOST_NAME_MAX]
Definition config.h:310
static cString static cString vsprintf(const char *fmt, va_list &ap)
Definition tools.c:1255
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1242
cString & Append(const char *String)
Definition tools.c:1195
int GenerateTs(const uchar *Data, int Length, bool Pusi, uchar *Dest)
Definition pes2ts.c:688
uchar counter
Definition pes2ts.c:671
cTsGenerator(int Pid)
Definition pes2ts.c:682
USE bool Write(const uchar *Data, int Length, bool Independent)
Definition pes2ts.c:1148
cString tsRecordingFileName
Definition pes2ts.c:1104
cIndexFile * index
Definition pes2ts.c:1107
~cTsWriter()
Definition pes2ts.c:1142
off_t offset
Definition pes2ts.c:1106
USE bool Open(const char *FileName)
Definition pes2ts.c:1122
cTsWriter(void)
Definition pes2ts.c:1115
int Size(void) const
Definition tools.h:773
const T * Data(void) const
Definition tools.h:774
virtual void Clear(void)
Definition tools.h:828
virtual void Append(T Data)
Definition tools.h:794
cSetup Setup
Definition config.c:373
#define MAXLIFETIME
Definition config.h:46
#define MAXPRIORITY
Definition config.h:41
#define PID_BASE_VIDEO
Definition pes2ts.c:722
#define MAX_SUBSTREAMS
Definition pes2ts.c:870
#define MAX_PIDS
Definition pes2ts.c:871
static USE bool CopyMarks(const char *PesFileName, const char *TsFileName)
Definition pes2ts.c:85
#define NUM_FRAMES_ANALYZE
Definition pes2ts.c:1162
int AppendZeroTerminated(cVector< T > &Pids, T Pid)
Definition pes2ts.c:819
#define USE
Definition pes2ts.c:17
#define INFOFILESUFFIX
Definition pes2ts.c:358
#define PID_BASE_AUDIO
Definition pes2ts.c:723
#define MARKSFILESUFFIX
Definition pes2ts.c:83
#define MAX_PES_PACKET_SIZE
Definition pes2ts.c:575
static void Warning(const char *Fmt,...) __attribute__((format(printf
Definition pes2ts.c:55
#define PID_BASE_DOLBY
Definition pes2ts.c:724
#define MAXFILESPERRECORDINGPES
Definition pes2ts.c:467
#define INDEXFILESUFFIX
Definition pes2ts.c:158
static void Log(const char *s)
Definition pes2ts.c:29
static void Info(int Level, const char *Fmt,...) __attribute__((format(printf
Definition pes2ts.c:39
#define MIN_PRE_1_3_19_PRIVATESTREAM
Definition pes2ts.c:868
struct __attribute__((packed))
Definition pes2ts.c:138
static cString LogFileName
Definition pes2ts.c:19
static bool Error(const char *Fmt,...) __attribute__((format(printf
Definition pes2ts.c:68
#define RECORDFILESUFFIXPES
Definition pes2ts.c:468
bool Pes2Ts(const char *PesFileName, const char *TsFileName)
Definition pes2ts.c:1164
static void SetLogFileName(const char *FileName)
Definition pes2ts.c:23
#define RESUMEFILESUFFIX
Definition pes2ts.c:249
#define SUMMARYFILESUFFIX
Definition pes2ts.c:294
#define PID_BASE_SUBT
Definition pes2ts.c:725
#define LOGFILESUFFIX
Definition pes2ts.c:21
void SetRecordingTimerId(const char *Directory, const char *TimerId)
Definition recording.c:3646
#define MAXFRAMESIZE
Definition recording.h:504
int PesPayloadOffset(const uchar *p)
Definition remux.h:178
bool PesLongEnough(int Length)
Definition remux.h:163
#define TS_SIZE
Definition remux.h:34
#define TS_SYNC_BYTE
Definition remux.h:33
int PesLength(const uchar *p)
Definition remux.h:173
#define TS_PAYLOAD_START
Definition remux.h:36
char language[MAXLANGCODE2]
Definition epg.h:48
ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition tools.c:50
int SysLogLevel
Definition tools.c:31
bool DirectoryOk(const char *DirName, bool LogErrors)
Definition tools.c:524
off_t FileSize(const char *FileName)
returns the size of the given file, or -1 in case of an error (e.g. if the file doesn't exist)
Definition tools.c:782
char * strn0cpy(char *dest, const char *src, size_t n)
Definition tools.c:128
cString AddDirectory(const char *DirName, const char *FileName)
Definition tools.c:445
unsigned char uchar
Definition tools.h:31
#define MALLOC(type, size)
Definition tools.h:47
char * skipspace(const char *s)
Definition tools.h:244