29 void Flush(
uchar *Data,
int &Length,
int MaxLength);
50 int NewSize = (
length + Length) * 3 / 2;
64 if (Data &&
length > 0 && Length +
length <= MaxLength) {
80 void Flush(
int Pid,
uchar *Data,
int &Length,
int MaxLength);
85 for (
int i = 0; i <
MAXPID; i++)
91 for (
int i = 0; i <
MAXPID; i++)
112 bool FindHeader(uint32_t Code,
const char *Header);
118 void AdjGopTime(
int Offset,
int FramesPerSecond);
126 if (
TsPid(Data) == Vpid) {
127 Setup(Data, Length, Vpid);
138 esyslog(
"ERROR: %s header not found!", Header);
185 uchar Frame = ((Byte3 & 0x1F) << 1) | (Byte4 >> 7);
186 uchar Sec = ((Byte2 & 0x07) << 3) | (Byte3 >> 5);
187 uchar Min = ((Byte1 & 0x03) << 4) | (Byte2 >> 4);
188 uchar Hour = ((Byte1 & 0x7C) >> 2);
189 int GopTime = ((Hour * 60 + Min) * 60 + Sec) * FramesPerSecond + Frame;
193 GopTime += 24 * 60 * 60 * FramesPerSecond;
194 Frame = GopTime % FramesPerSecond;
195 GopTime = GopTime / FramesPerSecond;
197 GopTime = GopTime / 60;
199 GopTime = GopTime / 60;
201 SetByte((Byte1 & 0x80) | (Hour << 2) | (Min >> 4), Index1);
202 SetByte(((Min & 0x0F) << 4) | 0x08 | (Sec >> 3), Index2);
203 SetByte(((Sec & 0x07) << 3) | (Frame >> 1), Index3);
204 SetByte((Byte4 & 0x7F) | ((Frame & 0x01) << 7), Index4);
219 SetByte((Tref << 6) | (Byte2 & 0x3F), Index2);
250 bool LoadFrame(
int Index,
uchar *Buffer,
bool &Independent,
int &Length);
256 bool FixFrame(
uchar *Data,
int &Length,
bool Independent,
int Index,
bool CutIn,
bool CutOut);
257 bool ProcessSequence(
int LastEndIndex,
int BeginIndex,
int EndIndex,
int NextBeginIndex);
259 virtual void Action(
void);
267 :
cThread(
"video cutting", true)
301 esyslog(
"no editing sequences found for %s", FromFileName);
304 esyslog(
"no editing marks found for %s", FromFileName);
320 dsyslog(
"suspending cutter thread");
326 dsyslog(
"resuming cutter thread");
336 if (
fromIndex->
Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) {
343 else if (len != Length)
345 return error == NULL;
379 if (!Buffer1 || !Buffer2)
384 if (
LoadFrame(Index1, Buffer1, Independent, Length1) &&
LoadFrame(Index2, Buffer2, Independent, Length2)) {
385 if (Length1 == Length2) {
387 for (
int i = 0; i < Length1; i++) {
388 if (Buffer1[i] != Buffer2[i]) {
404 bool Processed[
MAXPID] = {
false };
408 for (
int NumIndependentFrames = 0; NumIndependentFrames < 2; Index++) {
411 if (
LoadFrame(Index, Buffer, Independent, len)) {
413 NumIndependentFrames++;
419 int64_t d =
PtsDiff(LastPts, Pts);
423 NumIndependentFrames = 0;
424 Processed[Pid] =
true;
467 Mpeg2fixer.SetClosedGop();
476 bool DeletedFrame =
false;
477 bool GotVidPts =
false;
537 if (!DeletedFrame && !GotVidPts) {
548 bool SeamlessBegin = LastEndIndex >= 0 &&
FramesAreEqual(LastEndIndex, BeginIndex);
549 bool SeamlessEnd = NextBeginIndex >= 0 &&
FramesAreEqual(EndIndex, NextBeginIndex);
556 for (
int Index = BeginIndex;
Running() && Index < EndIndex; Index++) {
559 if (
LoadFrame(Index, Buffer, Independent, Length)) {
562 bool CutIn = !SeamlessBegin && Index == BeginIndex;
563 bool CutOut = !SeamlessEnd && Index == EndIndex - 1;
564 bool DeletedFrame =
false;
566 DeletedFrame =
FixFrame(Buffer, Length, Independent, Index, CutIn, CutOut);
582 error =
"safe_write";
607 int LastEndIndex = -1;
608 while (BeginMark &&
Running()) {
618 int NextBeginIndex = -1;
621 NextBeginIndex = NextBeginMark->Position();
623 if (!
ProcessSequence(LastEndIndex, BeginMark->Position(), EndIndex, NextBeginIndex))
627 LastEndIndex = EndIndex;
641 esyslog(
"no editing marks found!");
653 bool cCutter::Start(
const char *FileName,
const char *TargetFileName,
bool Overwrite)
668 if (!Overwrite && *evn && (access(*evn, F_OK) == 0) && !
Interface->
Confirm(
tr(
"File already exists - overwrite?"))) {
671 }
while (*evn && (access(*evn, F_OK) == 0));
676 char *s = strdup(*evn);
677 char *e = strrchr(s,
'.');
679 if (strcmp(e,
".rec") == 0) {
705 isyslog(
"editing process has been interrupted");
707 esyslog(
"ERROR: '%s' during editing process", Error);
748 #define CUTTINGCHECKINTERVAL 500 // ms between checks for the active cutting process
754 if (Recording.
Name()) {
764 fprintf(stderr,
"can't start editing process\n");
767 fprintf(stderr,
"'%s' has no editing sequences\n", FileName);
770 fprintf(stderr,
"'%s' has no editing marks\n", FileName);
773 fprintf(stderr,
"'%s' is not a recording\n", FileName);
776 fprintf(stderr,
"'%s' is not a directory\n", FileName);
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 RemoveVideoFile(const char *FileName)
const char * UpdateFileName(const char *FileName)
bool Confirm(const char *s, int Seconds=10, bool WaitForTimeout=false)
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.
void DelByName(const char *FileName, bool RemoveRecording=true)
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
double FramesPerSecond(void) const
cMark * GetNextBegin(cMark *EndMark=NULL)
Returns the next "begin" mark after EndMark, skipping any marks at the same position as EndMark...
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)
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)
static cString originalVersionName
void TsSetPcr(uchar *p, int64_t Pcr)
int TsPid(const uchar *p)
cMark * GetNextEnd(cMark *BeginMark)
Returns the next "end" mark after BeginMark, skipping any marks at the same position as BeginMark...
#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...
static bool Start(const char *FileName, const char *TargetFileName=NULL, bool Overwrite=true)
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)
uchar TsGetContinuityCounter(const uchar *p)
void TsSetDts(uchar *p, int l, int64_t Dts)
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 cCuttingThread * cuttingThread
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)
Actually starts the thread.
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.
void TouchUpdate(void)
Touches the '.update' file in the video directory, so that other instances of VDR that access the sam...
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)
static bool Active(const char *FileName=NULL)
Returns true if the cutter is currently active.
int GetNumSequences(void)
Returns the actual number of sequences to be cut from the recording.
void SetReadAhead(size_t ra)
cString PrefixVideoFileName(const char *FileName, char Prefix)
bool CutRecording(const char *FileName)
bool Active(void)
Checks whether the thread is still alive.
int64_t TsGetPts(const uchar *p, int l)
cRecordings Recordings
Any access to Recordings that loops through the list of recordings needs to hold a thread lock on thi...
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)
const char * Name(void) const
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.
static 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]
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)
static void Shutdown(void)
void AddByName(const char *FileName, bool TriggerUpdate=true)
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)