28 void Flush(
uchar *Data,
int &Length,
int MaxLength);
49 int NewSize = (
length + Length) * 3 / 2;
63 if (Data &&
length > 0 && Length +
length <= MaxLength) {
79 void Flush(
int Pid,
uchar *Data,
int &Length,
int MaxLength);
84 for (
int i = 0; i <
MAXPID; i++)
90 for (
int i = 0; i <
MAXPID; i++)
111 bool FindHeader(uint32_t Code,
const char *Header);
117 void AdjGopTime(
int Offset,
int FramesPerSecond);
125 if (
TsPid(Data) == Vpid) {
126 Setup(Data, Length, Vpid);
137 esyslog(
"ERROR: %s header not found!", Header);
184 uchar Frame = ((Byte3 & 0x1F) << 1) | (Byte4 >> 7);
185 uchar Sec = ((Byte2 & 0x07) << 3) | (Byte3 >> 5);
186 uchar Min = ((Byte1 & 0x03) << 4) | (Byte2 >> 4);
187 uchar Hour = ((Byte1 & 0x7C) >> 2);
188 int GopTime = ((Hour * 60 + Min) * 60 + Sec) * FramesPerSecond + Frame;
192 GopTime += 24 * 60 * 60 * FramesPerSecond;
193 Frame = GopTime % FramesPerSecond;
194 GopTime = GopTime / FramesPerSecond;
196 GopTime = GopTime / 60;
198 GopTime = GopTime / 60;
200 SetByte((Byte1 & 0x80) | (Hour << 2) | (Min >> 4), Index1);
201 SetByte(((Min & 0x0F) << 4) | 0x08 | (Sec >> 3), Index2);
202 SetByte(((Sec & 0x07) << 3) | (Frame >> 1), Index3);
203 SetByte((Byte4 & 0x7F) | ((Frame & 0x01) << 7), Index4);
218 SetByte((Tref << 6) | (Byte2 & 0x3F), Index2);
249 bool LoadFrame(
int Index,
uchar *Buffer,
bool &Independent,
int &Length);
255 bool FixFrame(
uchar *Data,
int &Length,
bool Independent,
int Index,
bool CutIn,
bool CutOut);
256 bool ProcessSequence(
int LastEndIndex,
int BeginIndex,
int EndIndex,
int NextBeginIndex);
258 virtual void Action(
void);
266 :
cThread(
"video cutting", true)
300 esyslog(
"no editing sequences found for %s", FromFileName);
303 esyslog(
"no editing marks found for %s", FromFileName);
319 dsyslog(
"suspending cutter thread");
325 dsyslog(
"resuming cutter thread");
335 if (
fromIndex->
Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) {
342 else if (len != Length)
344 return error == NULL;
378 if (!Buffer1 || !Buffer2)
383 if (
LoadFrame(Index1, Buffer1, Independent, Length1) &&
LoadFrame(Index2, Buffer2, Independent, Length2)) {
384 if (Length1 == Length2) {
386 for (
int i = 0; i < Length1; i++) {
387 if (Buffer1[i] != Buffer2[i]) {
403 bool Processed[
MAXPID] = {
false };
407 for (
int NumIndependentFrames = 0; NumIndependentFrames < 2; Index++) {
410 if (
LoadFrame(Index, Buffer, Independent, len)) {
412 NumIndependentFrames++;
418 int64_t d =
PtsDiff(LastPts, Pts);
422 NumIndependentFrames = 0;
423 Processed[Pid] =
true;
466 Mpeg2fixer.SetClosedGop();
475 bool DeletedFrame =
false;
476 bool GotVidPts =
false;
536 if (!DeletedFrame && !GotVidPts) {
547 bool SeamlessBegin = LastEndIndex >= 0 &&
FramesAreEqual(LastEndIndex, BeginIndex);
548 bool SeamlessEnd = NextBeginIndex >= 0 &&
FramesAreEqual(EndIndex, NextBeginIndex);
555 for (
int Index = BeginIndex;
Running() && Index < EndIndex; Index++) {
558 if (
LoadFrame(Index, Buffer, Independent, Length)) {
561 bool CutIn = !SeamlessBegin && Index == BeginIndex;
562 bool CutOut = !SeamlessEnd && Index == EndIndex - 1;
563 bool DeletedFrame =
false;
565 DeletedFrame =
FixFrame(Buffer, Length, Independent, Index, CutIn, CutOut);
581 error =
"safe_write";
606 int LastEndIndex = -1;
607 while (BeginMark &&
Running()) {
617 int NextBeginIndex = -1;
620 NextBeginIndex = NextBeginMark->Position();
622 if (!
ProcessSequence(LastEndIndex, BeginMark->Position(), EndIndex, NextBeginIndex))
626 LastEndIndex = EndIndex;
639 esyslog(
"no editing marks found!");
698 isyslog(
"editing process has been interrupted");
700 esyslog(
"ERROR: '%s' during editing process", Error);
724 #define CUTTINGCHECKINTERVAL 500 // ms between checks for the active cutting process
730 if (Recording.
Name()) {
735 if (Cutter.
Start()) {
740 fprintf(stderr,
"error while cutting\n");
743 fprintf(stderr,
"can't start editing process\n");
746 fprintf(stderr,
"'%s' has no editing sequences\n", FileName);
749 fprintf(stderr,
"'%s' has no editing marks\n", FileName);
752 fprintf(stderr,
"'%s' is not a recording\n", FileName);
755 fprintf(stderr,
"'%s' is not a directory\n", FileName);
bool Start(void)
Starts the actual cutting process.
bool ParsePatPmt(const uchar *Data, int Length)
Parses the given Data (which may consist of several TS packets, typically an entire frame) and extrac...
int64_t PtsAdd(int64_t Pts1, int64_t Pts2)
Adds the given PTS values, taking into account the 33bit wrap around.
uchar GetByte(void)
Gets the next byte of the TS payload, skipping any intermediate TS header data.
ssize_t Write(const void *Data, size_t Size)
bool Active(void)
Returns true if the cutter is currently active.
bool SkipBytes(int Bytes)
Skips the given number of bytes in the payload and returns true if there is still data left to read...
void Append(uchar *Data, int Length)
Appends Length bytes of Data to this packet buffer.
bool IsPmtPid(int Pid) const
Returns true if Pid the one of the PMT pids as defined by the current PAT.
int GetNumSequences(void) const
Returns the actual number of sequences to be cut from the recording.
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
double FramesPerSecond(void) const
cUnbufferedFile * SetOffset(int Number, off_t Offset=0)
bool SwitchFile(bool Force=false)
void Flush(int Pid, uchar *Data, int &Length, int MaxLength)
cCuttingThread(const char *FromFileName, const char *ToFileName)
bool TsHasPayload(const uchar *p)
cUnbufferedFile * NextFile(void)
int Vpid(void) const
Returns the video pid as defined by the current PMT, or 0 if no video pid has been detected...
int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max)
const cMark * GetNextEnd(const cMark *BeginMark) const
Returns the next "end" mark after BeginMark, skipping any marks at the same position as BeginMark...
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...
int Last(void)
Returns the index of the last entry in this file, or -1 if the file is empty.
int64_t TsGetDts(const uchar *p, int l)
void AdjTref(int TrefOffset)
cUnbufferedFile is used for large files that are mainly written or read in a streaming manner...
cPatPmtParser patPmtParser
bool Find(uint32_t Code)
Searches for the four byte sequence given in Code and returns true if it was found within the payload...
void GetPendingPackets(uchar *Buffer, int &Length, int Index)
cString originalVersionName
void TsSetPcr(uchar *p, int64_t Pcr)
const cMark * GetNextBegin(const cMark *EndMark=NULL) const
Returns the next "begin" mark after EndMark, skipping any marks at the same position as EndMark...
int TsPid(const uchar *p)
cCuttingThread * cuttingThread
cCutter(const char *FileName)
Sets up a new cutter for the given FileName, which must be the full path name of an existing recordin...
#define MAXVIDEOFILESIZEPES
int Vtype(void) const
Returns the video stream type as defined by the current PMT, or 0 if no video stream type has been de...
#define RUC_EDITINGRECORDING
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
void AdjGopTime(int Offset, int FramesPerSecond)
void TsSetDts(uchar *p, int l, int64_t Dts)
void Stop(void)
Stops an ongoing cutting process.
cMpeg2Fixer(uchar *Data, int Length, int Vpid)
#define CUTTINGCHECKINTERVAL
int GetLastIndex(void)
Returns the index into the TS data of the payload byte that has most recently been read...
static cString EditedFileName(const char *FileName)
Returns the full path name of the edited version of the recording with the given FileName.
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
int64_t PtsDiff(int64_t Pts1, int64_t Pts2)
Returns the difference between two PTS values.
cUnbufferedFile * fromFile
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
void SetStartTime(time_t Start)
Sets the start time of this recording to the given value.
void TsSetContinuityCounter(uchar *p, uchar Counter)
#define RUC_EDITEDRECORDING
cUnbufferedFile * Open(void)
void Append(int Pid, uchar *Data, int Length)
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex)
void TsHidePayload(uchar *p)
static void SetBrokenLink(uchar *Data, int Length)
bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length)
int64_t TsGetPcr(const uchar *p)
void SetReadAhead(size_t ra)
static bool RemoveVideoFile(const char *FileName)
bool CutRecording(const char *FileName)
bool Active(void)
Checks whether the thread is still alive.
int64_t TsGetPts(const uchar *p, int l)
void SetByte(uchar Byte, int Index)
Sets the TS data byte at the given Index to the value Byte.
bool FindHeader(uint32_t Code, const char *Header)
void Add(int Position)
If this cMarks object is used by multiple threads, the caller must Lock() it before calling Add() and...
const char * Name(void) const
Returns the full name of the recording (without the video directory).
void AssertFreeDiskSpace(int Priority, bool Force)
The special Priority value -1 means that we shall get rid of any deleted recordings faster than norma...
virtual ~cCuttingThread()
bool FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut)
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
uchar TsContinuityCounter(const uchar *p)
cString editedVersionName
bool Load(const char *RecordingFileName, double FramesPerSecond=DEFAULTFRAMESPERSECOND, bool IsPesRecording=false)
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent=NULL, int *Length=NULL)
cPacketBuffer * buffers[MAXPID]
bool Error(void)
Returns true if an error occurred while cutting the recording.
void TsSetPts(uchar *p, int l, int64_t Pts)
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
const char * PrefixFileName(char Prefix)
bool WriteInfo(const char *OtherFileName=NULL)
Writes in info file of this recording.
static void Shutdown(void)
bool IsPesRecording(void) const
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset)
void Flush(uchar *Data, int &Length, int MaxLength)
Flushes the content of this packet buffer into the given Data, starting at position Length...
static const char * NowReplaying(void)
bool FramesAreEqual(int Index1, int Index2)