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++)
98 buffers[Pid]->Append(Data, Length);
104 buffers[Pid]->Flush(Data, Length, MaxLength);
111 bool FindHeader(uint32_t Code,
const char *Header);
114 void SetBrokenLink(
void);
115 void SetClosedGop(
void);
117 void AdjGopTime(
int Offset,
int FramesPerSecond);
118 void AdjTref(
int TrefOffset);
125 if (
TsPid(Data) == Vpid) {
126 Setup(Data, Length, Vpid);
137 esyslog(
"ERROR: %s header not found!", Header);
143 if (!FindHeader(0x000001B8,
"GOP"))
149 SetByte(b, GetLastIndex());
155 if (!FindHeader(0x000001B8,
"GOP"))
160 SetByte(b, GetLastIndex());
165 if (!FindHeader(0x00000100,
"picture"))
167 int Tref = GetByte() << 2;
168 Tref |= GetByte() >> 6;
174 if (!FindHeader(0x000001B8,
"GOP"))
176 uchar Byte1 = GetByte();
177 int Index1 = GetLastIndex();
178 uchar Byte2 = GetByte();
179 int Index2 = GetLastIndex();
180 uchar Byte3 = GetByte();
181 int Index3 = GetLastIndex();
182 uchar Byte4 = GetByte();
183 int Index4 = GetLastIndex();
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);
209 if (!FindHeader(0x00000100,
"picture"))
211 int Tref = GetByte() << 2;
212 int Index1 = GetLastIndex();
213 uchar Byte2 = GetByte();
214 int Index2 = GetLastIndex();
217 SetByte(Tref >> 2, Index1);
218 SetByte((Tref << 6) | (Byte2 & 0x3F), Index2);
247 bool Throttled(
void);
248 bool SwitchFile(
bool Force =
false);
249 bool LoadFrame(
int Index,
uchar *Buffer,
bool &Independent,
int &Length);
250 bool FramesAreEqual(
int Index1,
int Index2);
251 void GetPendingPackets(
uchar *Buffer,
int &Length,
int Index);
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);
262 const char *
Error(
void) {
return error; }
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;
371 operator uchar * () {
return buffer; }
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;
640 esyslog(
"no editing marks found!");
647 cuttingThread = NULL;
649 originalVersionName = FileName;
671 if (!cuttingThread) {
673 if (*originalVersionName) {
675 editedVersionName = EditedFileName(originalVersionName);
676 if (*editedVersionName) {
677 if (strcmp(originalVersionName, editedVersionName) != 0) {
681 cuttingThread =
new cCuttingThread(originalVersionName, editedVersionName);
693 bool Interrupted = cuttingThread && cuttingThread->Active();
694 const char *
Error = cuttingThread ? cuttingThread->Error() : NULL;
695 delete cuttingThread;
696 cuttingThread = NULL;
697 if ((Interrupted || Error) && *editedVersionName) {
699 isyslog(
"editing process has been interrupted");
701 esyslog(
"ERROR: '%s' during editing process", Error);
712 if (cuttingThread->Active())
714 error = cuttingThread->Error();
727 #define CUTTINGCHECKINTERVAL 500 // ms between checks for the active cutting process 733 if (Recording.
Name()) {
738 if (Cutter.
Start()) {
743 fprintf(stderr,
"error while cutting\n");
746 fprintf(stderr,
"can't start editing process\n");
749 fprintf(stderr,
"'%s' has no editing sequences\n", FileName);
752 fprintf(stderr,
"'%s' has no editing marks\n", FileName);
755 fprintf(stderr,
"'%s' is not a recording\n", FileName);
758 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.
ssize_t Write(const void *Data, size_t Size)
bool Active(void)
Returns true if the cutter is currently active.
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.
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)
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
void GetPendingPackets(uchar *Buffer, int &Length, int Index)
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...
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...
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)
void Stop(void)
Stops an ongoing cutting process.
cMpeg2Fixer(uchar *Data, int Length, int Vpid)
#define CUTTINGCHECKINTERVAL
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.
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)
int GetNumSequences(void)
Returns the actual number of sequences to be cut from the recording.
void SetReadAhead(size_t ra)
static bool RemoveVideoFile(const char *FileName)
bool CutRecording(const char *FileName)
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...
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)
void DelByName(const char *FileName)
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
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)
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)
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)