17#define USE __attribute__((warn_unused_result))
21#define LOGFILESUFFIX "/pes2ts.log"
29static void Log(
const char *s)
38static void Info(
int Level,
const char *Fmt, ...)
__attribute__ ((format (printf, 2, 3)));
39static
void Info(
int Level, const
char *Fmt, ...)
68static
bool Error(const
char *Fmt, ...)
83#define MARKSFILESUFFIX "/marks"
85static USE bool CopyMarks(
const char *PesFileName,
const char *TsFileName)
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")) {
93 while (
char *s = ReadLine.
Read(fp)) {
94 fprintf(ft,
"%s\n", s);
99 else if (errno != ENOENT) {
101 return Error(
"%s: %m", *TsMarksFileName);
105 else if (errno != ENOENT)
106 return Error(
"%s: %m", *PesMarksFileName);
153 USE bool Load(
const char *FileName);
155 USE bool Get(
int Index,
uchar &FileNumber, uint32_t &FileOffset,
bool &Independent,
int &Length);
158#define INDEXFILESUFFIX "/index.vdr"
172 int delta = int(buf.st_size %
sizeof(tIndexPes));
174 delta =
sizeof(tIndexPes) - delta;
175 Warning(
"invalid file size (%u) in '%s', continuing anyway", uint32_t(buf.st_size), *
fileName);
178 last = int((buf.st_size + delta) /
sizeof(tIndexPes) - 1);
197 Error(
"can't allocate %zd bytes for index '%s'", size *
sizeof(tIndexPes), *
fileName);
207 return index != NULL;
218 FileNumber =
index[Index].number;
219 FileOffset =
index[Index].offset;
220 Independent =
index[Index].type == 1;
223 uint32_t fo =
index[Index + 1].offset;
224 if (fn == FileNumber)
225 Length = int(fo - FileOffset);
245 USE bool Read(
const char *FileName);
246 USE bool Write(
const char *TsFileName);
249#define RESUMEFILESUFFIX "/resume"
259 int f = open(PesResumeFileName, O_RDONLY);
261 Info(2,
"reading PES resume file %s", *PesResumeFileName);
266 return Error(
"%s: %m", *PesResumeFileName);
270 else if (errno != ENOENT)
271 return Error(
"%s: %m", *PesResumeFileName);
279 Info(2,
"writing TS resume file %s", *TsResumeFileName);
280 FILE *f = fopen(TsResumeFileName,
"w");
282 fprintf(f,
"I %d\n",
index);
287 return Error(
"%s: %m", *TsResumeFileName);
294#define SUMMARYFILESUFFIX "/summary.vdr"
300 USE bool Read(
const char *FileName);
309 if (FILE *f = fopen(SummaryFileName,
"r")) {
310 Info(2,
"reading PES summary file %s", *SummaryFileName);
313 while (
char *s = ReadLine.
Read(f)) {
326 if (*
data[1] && strlen(
data[1]) > 80) {
338 Info(3,
"Short text = '%s'", *
data[1]);
339 Info(3,
"Description = '%s'", *
data[2]);
341 else if (errno != ENOENT)
342 return Error(
"%s: %m", *SummaryFileName);
353 USE bool Read(
const char *FileName);
354 USE bool Write(
const char *TsFileName);
358#define INFOFILESUFFIX "/info"
363 if (access(InfoFileName, R_OK) == 0) {
364 Info(2,
"reading PES info file %s", *InfoFileName);
365 if (FILE *f = fopen(InfoFileName,
"r")) {
367 while (
char *s = ReadLine.
Read(f)) {
374 Info(3,
"%s - skipped!", s);
384 return Error(
"%s: %m", *InfoFileName);
388 if (PesSummary.
Read(FileName)) {
389 if (PesSummary.
Title())
399 Info(2,
"taking priority and lifetime from PES info file name %s", *InfoFileName);
403 int len = strlen(FileName);
404 const char *p = FileName + len - 1;
409 Lifetime = atoi(p + 1);
411 Priority = atoi(p + 1);
419 Info(3,
"priority = %d", Priority);
420 Info(3,
"lifetime = %d", Lifetime);
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]);
436 return Error(
"%s: %m", *InfoFileName);
451 USE bool Init(
const char *FileName);
459 USE const uchar *
Read(
int Number, uint32_t Offset,
int Length,
int &Bytes);
467#define MAXFILESPERRECORDINGPES 255
468#define RECORDFILESUFFIXPES "/%03d.vdr"
495 if (access(Name, R_OK) == 0) {
496 file = open(Name, O_RDONLY | O_LARGEFILE);
502 Error(
"%s: %m", *Name);
505 Error(
"invalid file number '%d'", Number);
520 Length =
size - Offset;
523 if (Offset >= 0 && lseek(
file, Offset, SEEK_SET) == Offset) {
527 Error(
"invalid length '%d', only read '%d' bytes", Length, Bytes);
530 Error(
"invalid offset '%u'", Offset);
533 Error(
"invalid number '%d'", Number);
536 Error(
"invalid length '%d'", Length);
547 USE bool Init(
const char *FileName);
568 if (
index.
Get(Index, FileNumber, FileOffset, Independent, Length))
569 return file.
Read(FileNumber, FileOffset, Length, Bytes);
575#define MAX_PES_PACKET_SIZE (0xFFFF + 6)
610 int SubstreamHeaderLength = 4;
611 bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
614 if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81) {
615 SubstreamHeaderLength = 0;
616 ResetSubtitleAssembler = Data[8] >= 5;
619 if (ResetSubtitleAssembler)
624 memcpy(
data, Data, PayloadOffset);
627 Data += PayloadOffset + SubstreamHeaderLength;
628 Length -= (PayloadOffset + SubstreamHeaderLength);
630 return Error(
"packet too large in subtitle assembler");
632 for (
int i = 0; i < Length; i++) {
633 if (Data[i] == 0x0F && i + 3 < Length) {
641 uint16_t NewLength =
length - 6;
642 data[4] = NewLength >> 8;
643 data[5] = NewLength & 0xFF;
646 if (Length < 2048 && *p == 0xFF)
652 return Error(
"no data for subtitle assembler");
695 int Fill = Payload - Length;
705 memset(p, 0xFF, Fill - 2);
709 memcpy(p, Data, Length);
714 memcpy(p, Data, Payload);
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
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);
782 bool HasPids =
false;
787 for (
int i = 0;
apids[i]; i++) {
791 for (
int i = 0;
dpids[i]; i++) {
795 for (
int i = 0;
spids[i]; i++) {
800 return Error(
"no PIDs found");
806 SetPids(
vpid,
vtype,
ppid, 0,
apids.
Data(),
atypes.
Data(),
alangs,
dpids.
Data(),
dtypes.
Data(),
dlangs,
spids.
Data(),
stypes.
Data(),
slangs,
compositionPageIds.
Data(),
compositionPageIds.
Data());
868#define MIN_PRE_1_3_19_PRIVATESTREAM 10
870#define MAX_SUBSTREAMS 32
871#define MAX_PIDS (PID_BASE_LCPM + MAX_SUBSTREAMS)
949 return Error(
"frame size exceeded");
950 int w = TsGenerator->
GenerateTs(Data, Length, First, p);
963 return Error(
"invalid PID '%d'", Pid);
973 case 0xE0 ... 0xEF: {
976 if (!
MakeTs(Data, Length, Vpid))
980 case 0xC0 ... 0xDF: {
981 int Index = c - 0xC0;
984 if (!
MakeTs(Data, Length, Apid))
989 int PayloadOffset = Data[8] + 9;
992 if ((Data[7] & 0x01) && (Data[PayloadOffset - 3] & 0x81) == 0x01 && Data[PayloadOffset - 2] == 0x81)
995 uchar SubStreamId = Data[PayloadOffset];
996 uchar SubStreamType = SubStreamId & 0xF0;
997 uchar SubStreamIndex = SubStreamId & 0x1F;
1000pre_1_3_19_PrivateStreamDetected:
1003 SubStreamType = 0x80;
1009 switch (SubStreamType) {
1014 if (!PesSubtitleAssembler)
1016 if (!PesSubtitleAssembler->
Put(Data, Length))
1018 Data = PesSubtitleAssembler->
Get(Length);
1019 if (Data && !
MakeTs(Data, Length, Spid))
1027 const uchar *p = Data + PayloadOffset;
1028 if (*p++ == 0x80 && *p++ == 0x01 && *p++ == 0x00 && *p++ == 0x01) {
1030 memcpy(buf, Data, PayloadOffset);
1031 memcpy(buf + PayloadOffset, Data + PayloadOffset + 4, Length - (PayloadOffset + 4));
1033 int PacketLength = Length - 6;
1034 buf[4] = PacketLength >> 8;
1035 buf[5] = PacketLength & 0xFF;
1040 if (!
MakeTs(Data, Length, Dpid))
1046 Warning(
"don't know what to do with LPCM");
1063 goto pre_1_3_19_PrivateStreamDetected;
1070 return Error(
"unexpected packet id %02X", c);
1081 while (i <= Length - 6) {
1082 if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
1085 return Error(
"incomplete PES packet");
1091 Warning(
"syncing on PES data!");
1096 return Error(
"leftover PES data");
1111 USE bool Open(
const char *FileName);
1112 USE bool Write(
const uchar *Data,
int Length,
bool Independent);
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);
1134 Info(2,
"generating TS index file %s", *TsIndexFileName);
1150 int Written = write(
f, Data, Length);
1162#define NUM_FRAMES_ANALYZE 100
1164bool Pes2Ts(
const char *PesFileName,
const char *TsFileName)
1167 return Error(
"'%s' is not a directory", PesFileName);
1169 return Error(
"'%s' is not a directory", TsFileName);
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");
1179 Info(1,
"processing PES recording %s", PesFileName);
1181 if (!PesInfo.
Read(PesFileName))
1183 if (!PesInfo.
Write(TsFileName))
1185 if (!
CopyMarks(PesFileName, TsFileName))
1188 if (!PesResume.
Read(PesFileName))
1190 if (!PesResume.
Write(TsFileName))
1193 if (!PesReader.
Init(PesFileName))
1197 Info(2,
"analyzing PES recording");
1200 if (PesReader.
HasData(Index)) {
1201 bool Independent =
false;
1203 if (
const uchar *PesFrame = PesReader.
ReadPesFrame(Index, Independent, Bytes)) {
1206 Info(3,
"recording %s is pre 1.3.19", PesFileName);
1218 Info(2,
"analyzing pre 1.3.19 PES recording");
1221 if (PesReader.
HasData(Index)) {
1222 bool Independent =
false;
1224 if (
const uchar *PesFrame = PesReader.
ReadPesFrame(Index, Independent, Bytes)) {
1238 if (!TsWriter.
Open(TsFileName))
1242 while (PesReader.
HasData(Index)) {
1243 bool Independent =
false;
1245 if (
const uchar *PesFrame = PesReader.
ReadPesFrame(Index, Independent, Bytes)) {
1248 const uchar *TsFrame = Pes2TsConverter.
TsFrame(Length);
1249 if (!TsWriter.
Write(TsFrame, Length, Independent))
1256 return Error(
"no data for index '%d'", Index);
1259 Info(1,
"SUCCESS!");
tComponent * GetComponent(int Index, uchar Stream, uchar Type)
tComponent * Component(int Index) const
int NumComponents(void) const
void SetComponent(int Index, const char *s)
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset, bool Errors=false, bool Missing=false)
cIndexFile(const char *FileName, bool Record, bool IsPesRecording=false, bool PauseLive=false)
static cString IndexFileName(const char *FileName, bool IsPesRecording)
cPatPmtGenerator(const cChannel *Channel=NULL)
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.
int GetPatPmt(uchar *Dest, int Size)
Writes the PAT and all PMT packets to Dest.
char dlangs[MAXDPIDS][MAXLANGCODE2]
cVector< uint16_t > compositionPageIds
cPatPmtMaker(cComponents *Components)
void SetVpid(int Vpid, int Vtype)
void SetDpid(int Dpid, int Dtype, int Index)
void SetApid(int Apid, int Atype, int Index)
void SetSpid(int Spid, uchar Stype, uint16_t CompositionPageId, int Index)
char slangs[MAXSPIDS][MAXLANGCODE2]
char alangs[MAXAPIDS][MAXLANGCODE2]
int pre_1_3_19_PrivateStream
USE bool ProcessPesPacket(const uchar *Data, int Length)
cPes2TsConverter(cComponents *Components)
USE bool ConvertPesFrame(const uchar *Data, int Length, bool Independent)
USE bool MakeTs(const uchar *Data, int Length, int Pid)
const uchar * TsFrame(int &Length)
void SetAnalyzing(bool Analyzing)
cPesSubtitleAssembler * pesSubtitleAssemblers[MAX_SUBSTREAMS]
bool IsPre_1_3_19_PrivateStream(void)
uchar frame[MAXFRAMESIZE]
cTsGenerator * tsGenerators[MAX_PIDS]
USE const uchar * Read(int Number, uint32_t Offset, int Length, int &Bytes)
USE bool Open(int Number)
USE bool Init(const char *FileName)
uchar frame[MAXFRAMESIZE]
USE bool Get(int Index, uchar &FileNumber, uint32_t &FileOffset, bool &Independent, int &Length)
USE bool Load(const char *FileName)
USE bool IsValid(int Index)
cComponents * Components(void)
USE bool Write(const char *TsFileName)
USE bool Read(const char *FileName)
const uchar * ReadPesFrame(int Index, bool &Independent, int &Bytes)
USE bool HasData(int Index)
USE bool Init(const char *FileName)
USE bool Read(const char *FileName)
USE bool Write(const char *TsFileName)
cPesSubtitleAssembler(void)
uint16_t compositionPageId
const uchar * Get(int &Length)
USE bool Put(const uchar *Data, int Length)
uint16_t CompositionPageId(void)
const char * Description(void)
USE bool Read(const char *FileName)
const char * ShortText(void)
cRecordingTimerId(const char *FileName)
char SVDRPHostName[HOST_NAME_MAX]
static cString static cString vsprintf(const char *fmt, va_list &ap)
static cString sprintf(const char *fmt,...) __attribute__((format(printf
cString & Append(const char *String)
int GenerateTs(const uchar *Data, int Length, bool Pusi, uchar *Dest)
USE bool Write(const uchar *Data, int Length, bool Independent)
cString tsRecordingFileName
USE bool Open(const char *FileName)
const T * Data(void) const
virtual void Append(T Data)
static USE bool CopyMarks(const char *PesFileName, const char *TsFileName)
#define NUM_FRAMES_ANALYZE
int AppendZeroTerminated(cVector< T > &Pids, T Pid)
#define MAX_PES_PACKET_SIZE
static void Warning(const char *Fmt,...) __attribute__((format(printf
#define MAXFILESPERRECORDINGPES
static void Log(const char *s)
static void Info(int Level, const char *Fmt,...) __attribute__((format(printf
#define MIN_PRE_1_3_19_PRIVATESTREAM
struct __attribute__((packed))
static cString LogFileName
static bool Error(const char *Fmt,...) __attribute__((format(printf
#define RECORDFILESUFFIXPES
bool Pes2Ts(const char *PesFileName, const char *TsFileName)
static void SetLogFileName(const char *FileName)
#define SUMMARYFILESUFFIX
void SetRecordingTimerId(const char *Directory, const char *TimerId)
int PesPayloadOffset(const uchar *p)
bool PesLongEnough(int Length)
int PesLength(const uchar *p)
char language[MAXLANGCODE2]