Adapted Patch from forum post https://www.vdr-portal.de/forum/index.php?thread/134171-permashift-1-0-4-f%C3%BCr-vdr-2-4-betaversion/&postID=1341243#post1341243 SRC Url https://www.vdr-portal.de/index.php?attachment/45632-vdr-2-5-4-patch-for-permashift-diff-gz/ adapted for vdr-2.6.1 Signed-off-by: Martin Dummer diff -Naur vdr-2.6.1.orig/device.c vdr-2.6.1/device.c --- vdr-2.6.1.orig/device.c 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/device.c 2022-02-06 18:05:26.452890690 +0100 @@ -1880,6 +1880,17 @@ ReleaseCamSlot(); } +cRecorder* cDevice::GetPreRecording(const cChannel *Channel) +{ + cMutexLock MutexLock(&mutexReceiver); + for (int i = 0; i < MAXRECEIVERS; i++) { + if (receiver[i]) + if (receiver[i]->IsPreRecording(Channel)) + return (cRecorder*)receiver[i]; + } + return NULL; +} + // --- cTSBuffer ------------------------------------------------------------- cTSBuffer::cTSBuffer(int File, int Size, int DeviceNumber) diff -Naur vdr-2.6.1.orig/device.h vdr-2.6.1/device.h --- vdr-2.6.1.orig/device.h 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/device.h 2022-02-06 18:05:51.541429884 +0100 @@ -85,6 +85,7 @@ class cPlayer; class cReceiver; +class cRecorder; class cLiveSubtitle; class cDeviceHook : public cListObject { @@ -854,6 +855,8 @@ ///< Returns true if we are currently receiving. The parameter has no meaning (for backwards compatibility only). bool AttachReceiver(cReceiver *Receiver); ///< Attaches the given receiver to this device. + cRecorder* GetPreRecording(const cChannel *Channel); + ///< Get precocious recording for the channel if there is one. void Detach(cReceiver *Receiver, bool ReleaseCam = true); ///< Detaches the given receiver from this device. ///< If ReleaseCam is true, the CAM slot will be released if it diff -Naur vdr-2.6.1.orig/dvbplayer.c vdr-2.6.1/dvbplayer.c --- vdr-2.6.1.orig/dvbplayer.c 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/dvbplayer.c 2022-02-06 18:05:26.452890690 +0100 @@ -249,13 +249,14 @@ cUnbufferedFile *replayFile; double framesPerSecond; bool isPesRecording; - bool pauseLive; + ReplayState replayState; bool eof; bool firstPacket; ePlayModes playMode; ePlayDirs playDir; int trickSpeed; int readIndex; + int startIndex; bool readIndependent; cFrame *readFrame; cFrame *playFrame; @@ -271,6 +272,8 @@ virtual void Action(void); public: cDvbPlayer(const char *FileName, bool PauseLive); + cDvbPlayer(const char *FileName, ReplayState newReplayState); + void Construct(const char *FileName, ReplayState newReplayState); virtual ~cDvbPlayer(); void SetMarks(const cMarks *Marks); bool Active(void) { return cThread::Running(); } @@ -297,6 +300,17 @@ cDvbPlayer::cDvbPlayer(const char *FileName, bool PauseLive) :cThread("dvbplayer") { + Construct(FileName, PauseLive? restPauseLive : restNormal); +} + +cDvbPlayer::cDvbPlayer(const char *FileName, ReplayState newReplayState) +:cThread("dvbplayer") +{ + Construct(FileName, newReplayState); +} + +void cDvbPlayer::Construct(const char *FileName, ReplayState newReplayState) +{ nonBlockingFileReader = NULL; ringBuffer = NULL; marks = NULL; @@ -304,7 +318,8 @@ cRecording Recording(FileName); framesPerSecond = Recording.FramesPerSecond(); isPesRecording = Recording.IsPesRecording(); - pauseLive = PauseLive; + replayState = newReplayState; + bool reuse = (replayState == restReusePause || replayState == restReuseRewind); eof = false; firstPacket = true; playMode = pmPlay; @@ -323,15 +338,21 @@ return; ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE); // Create the index file: - index = new cIndexFile(FileName, false, isPesRecording, pauseLive); + index = new cIndexFile(FileName, false, isPesRecording, replayState == restPauseLive); if (!index) esyslog("ERROR: can't allocate index"); else if (!index->Ok()) { delete index; index = NULL; } - else if (PauseLive) + else if (reuse) framesPerSecond = cRecording(FileName).FramesPerSecond(); // the fps rate might have changed from the default + startIndex = 0; + if (replayState == restReuseRewind || replayState == restReusePause) { + int Current, Total; + GetIndex(Current, Total, false); + startIndex = max(Total - 1, 0); + } } cDvbPlayer::~cDvbPlayer() @@ -481,8 +502,21 @@ bool CutIn = false; bool AtLastMark = false; - if (pauseLive) - Goto(0, true); + if (replayState == restPauseLive) { + Goto(0, true); + } + else if (replayState == restReuseRewind || replayState == restReusePause) { + readIndex = startIndex; + Goto(readIndex, true); + playMode = pmPlay; + if (replayState == restReuseRewind) { + Backward(); + } + else if (replayState == restReusePause) { + Pause(); + } + } + while (Running()) { if (WaitingForData) WaitingForData = !nonBlockingFileReader->WaitForDataMs(3); // this keeps the CPU load low, but reacts immediately on new data @@ -985,6 +1019,11 @@ { } +cDvbPlayerControl::cDvbPlayerControl(const char *FileName, ReplayState replayState) +:cControl(player = new cDvbPlayer(FileName, replayState)) +{ +} + cDvbPlayerControl::~cDvbPlayerControl() { Stop(); diff -Naur vdr-2.6.1.orig/dvbplayer.h vdr-2.6.1/dvbplayer.h --- vdr-2.6.1.orig/dvbplayer.h 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/dvbplayer.h 2022-02-06 18:05:26.452890690 +0100 @@ -16,6 +16,14 @@ class cDvbPlayer; +enum ReplayState +{ + restNormal, + restPauseLive, + restReusePause, + restReuseRewind +}; + class cDvbPlayerControl : public cControl { private: cDvbPlayer *player; @@ -25,6 +33,8 @@ // If PauseLive is true, special care is taken to make sure the index // file of the recording is long enough to allow the player to display // the first frame in still picture mode. + cDvbPlayerControl(const char *FileName, ReplayState replayState); + // Sets up a player for the given file. replayState represents the initial state. virtual ~cDvbPlayerControl(); void SetMarks(const cMarks *Marks); bool Active(void); diff -Naur vdr-2.6.1.orig/menu.c vdr-2.6.1/menu.c --- vdr-2.6.1.orig/menu.c 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/menu.c 2022-02-06 18:05:26.452890690 +0100 @@ -5303,6 +5303,16 @@ cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause) { + Construct(Device, Timers, Timer, Pause, NULL); +} + +cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause, bool* reused) +{ + Construct(Device, Timers, Timer, Pause, reused); +} + +void cRecordControl::Construct(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause, bool* reused) +{ const char *LastReplayed = cReplayControl::LastReplayed(); // must do this before locking schedules! // Whatever happens here, the timers will be modified in some way... Timers->SetModified(); @@ -5331,6 +5341,7 @@ timer->SetPending(true); timer->SetRecording(true); event = timer->Event(); + if (reused != NULL) *reused = false; if (event || GetEvent()) dsyslog("Title: '%s' Subtitle: '%s'", event->Title(), event->ShortText()); @@ -5360,8 +5371,21 @@ if (MakeDirs(fileName, true)) { Recording.WriteInfo(); // we write this *before* attaching the recorder to the device, to make sure the info file is present when the recorder needs to update the fps value! const cChannel *ch = timer->Channel(); - recorder = new cRecorder(fileName, ch, timer->Priority()); - if (device->AttachReceiver(recorder)) { + + if (!Timer) { + recorder = device->GetPreRecording(ch); + if (recorder != NULL) { + recorder->ActivatePreRecording(fileName, timer->Priority()); + if (reused != NULL) *reused = true; + } + } + + if (recorder == NULL) { + recorder = new cRecorder(fileName, ch, timer->Priority()); + if (!device->AttachReceiver(recorder)) DELETENULL(recorder); + } + + if (recorder != NULL) { cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true); if (!Timer && !LastReplayed) // an instant recording, maybe from cRecordControls::PauseLiveVideo() cReplayControl::SetRecording(fileName); @@ -5371,8 +5395,6 @@ Recordings->AddByName(fileName); return; } - else - DELETENULL(recorder); } else timer->SetDeferred(DEFERTIMER); @@ -5452,7 +5474,7 @@ cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL }; int cRecordControls::state = 0; -bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause) +bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause, bool* reused) { static time_t LastNoDiskSpaceMessage = 0; int FreeMB = 0; @@ -5490,7 +5512,7 @@ if (!Timer || Timer->Matches()) { for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (!RecordControls[i]) { - RecordControls[i] = new cRecordControl(device, Timers, Timer, Pause); + RecordControls[i] = new cRecordControl(device, Timers, Timer, Pause, reused); return RecordControls[i]->Process(time(NULL)); } } @@ -5514,6 +5536,11 @@ return Start(Timers, NULL, Pause); } +bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause) +{ + return Start(Timers, Timer, Pause, NULL); +} + void cRecordControls::Stop(const char *InstantId) { LOCK_TIMERS_WRITE; @@ -5549,10 +5576,17 @@ bool cRecordControls::PauseLiveVideo(void) { + return PauseLiveVideo(false); +} + +bool cRecordControls::PauseLiveVideo(bool rewind) +{ Skins.Message(mtStatus, tr("Pausing live video...")); + bool reused = false; cReplayControl::SetRecording(NULL); // make sure the new cRecordControl will set cReplayControl::LastReplayed() - if (Start(true)) { - cReplayControl *rc = new cReplayControl(true); + LOCK_TIMERS_WRITE; + if (Start(Timers, NULL, true, &reused)) { + cReplayControl *rc = new cReplayControl(rewind? restReuseRewind : reused? restReusePause : restPauseLive); cControl::Launch(rc); cControl::Attach(); Skins.Message(mtStatus, NULL); @@ -5695,7 +5729,18 @@ cReplayControl::cReplayControl(bool PauseLive) :cDvbPlayerControl(fileName, PauseLive) { - cDevice::PrimaryDevice()->SetKeepTracks(PauseLive); + Construct(PauseLive? restPauseLive : restNormal); +} + +cReplayControl::cReplayControl(ReplayState replayState) +:cDvbPlayerControl(fileName, replayState) +{ + Construct(replayState); +} + +void cReplayControl::Construct(ReplayState replayState) +{ + cDevice::PrimaryDevice()->SetKeepTracks(replayState == restPauseLive); currentReplayControl = this; displayReplay = NULL; marksModified = false; diff -Naur vdr-2.6.1.orig/menu.h vdr-2.6.1/menu.h --- vdr-2.6.1.orig/menu.h 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/menu.h 2022-02-06 18:05:26.452890690 +0100 @@ -246,6 +246,8 @@ bool GetEvent(void); public: cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer = NULL, bool Pause = false); + cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause, bool* reused); + void Construct(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause, bool* reused); virtual ~cRecordControl(); bool Process(time_t t); cDevice *Device(void) { return device; } @@ -261,10 +263,12 @@ static int state; public: static bool Start(cTimers *Timers, cTimer *Timer, bool Pause = false); + static bool Start(cTimers *Timers, cTimer *Timer, bool Pause, bool* reused); static bool Start(bool Pause = false); static void Stop(const char *InstantId); static void Stop(cTimer *Timer); static bool PauseLiveVideo(void); + static bool PauseLiveVideo(bool rewind); static const char *GetInstantId(const char *LastInstantId); static cRecordControl *GetRecordControl(const char *FileName); static cRecordControl *GetRecordControl(const cTimer *Timer); @@ -320,6 +324,8 @@ void EditTest(void); public: cReplayControl(bool PauseLive = false); + cReplayControl(ReplayState replayState); + void Construct(ReplayState replayState); virtual ~cReplayControl(); void Stop(void); virtual cOsdObject *GetInfo(void); diff -Naur vdr-2.6.1.orig/receiver.h vdr-2.6.1/receiver.h --- vdr-2.6.1.orig/receiver.h 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/receiver.h 2022-02-06 18:05:26.452890690 +0100 @@ -85,6 +85,10 @@ ///< case the device is needed otherwise, so code that uses a cReceiver ///< should repeatedly check whether it is still attached, and if ///< it isn't, delete it (or take any other appropriate measures). + virtual bool IsPreRecording(const cChannel *Channel) { return false; } + ///< prerecords given channel; may be turned into a disc recording. + virtual bool ActivatePreRecording(const char* fileName, int Priority) { return false; } + ///< turn prerecording into a disc recording }; #endif //__RECEIVER_H diff -Naur vdr-2.6.1.orig/recorder.c vdr-2.6.1/recorder.c --- vdr-2.6.1.orig/recorder.c 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/recorder.c 2022-02-06 18:05:26.452890690 +0100 @@ -164,11 +164,25 @@ cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority) :cReceiver(Channel, Priority) ,cThread("recording") +,tsChecker(NULL), frameChecker(NULL), recordingInfo(NULL), ringBuffer(NULL), frameDetector(NULL), fileName(NULL), index(NULL), recordFile(NULL), recordingName(NULL) { - tsChecker = new cTsChecker; - frameChecker = new cFrameChecker; + if (FileName != NULL) { + InitializeFile(FileName, Channel); + } +} + +void cRecorder::InitializeFile(const char *FileName, const cChannel *Channel) +{ + if (tsChecker == NULL) { + tsChecker = new cTsChecker; + } + if (frameChecker == NULL) { + frameChecker = new cFrameChecker; + } recordingName = strdup(FileName); - recordingInfo = new cRecordingInfo(recordingName); + if (recordingInfo == NULL) { + recordingInfo = new cRecordingInfo(recordingName); + } recordingInfo->Read(); oldErrors = max(0, recordingInfo->Errors()); // in case this is a re-started recording errors = oldErrors; @@ -193,7 +207,9 @@ Pid = Channel->Dpid(0); Type = 0x06; } - frameDetector = new cFrameDetector(Pid, Type); + if (frameDetector == NULL) { + frameDetector = new cFrameDetector(Pid, Type); + } index = NULL; fileSize = 0; lastDiskSpaceCheck = time(NULL); diff -Naur vdr-2.6.1.orig/recorder.h vdr-2.6.1/recorder.h --- vdr-2.6.1.orig/recorder.h 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/recorder.h 2022-02-06 18:05:26.452890690 +0100 @@ -19,8 +19,8 @@ class cTsChecker; class cFrameChecker; -class cRecorder : public cReceiver, cThread { -private: +class cRecorder : public cReceiver, protected cThread { +protected: cTsChecker *tsChecker; cFrameChecker *frameChecker; cRingBufferLinear *ringBuffer; @@ -41,7 +41,6 @@ bool RunningLowOnDiskSpace(void); bool NextFile(void); void HandleErrors(bool Force = false); -protected: virtual void Activate(bool On); ///< If you override Activate() you need to call Detach() (which is a ///< member of the cReceiver class) from your own destructor in order @@ -49,6 +48,9 @@ ///< destroyed. virtual void Receive(const uchar *Data, int Length); virtual void Action(void); + void InitializeFile(const char *FileName, const cChannel *Channel); + ///< Starts recording to file. + ///< Called in constructor if file name has been given. public: cRecorder(const char *FileName, const cChannel *Channel, int Priority); ///< Creates a new recorder for the given Channel and diff -Naur vdr-2.6.1.orig/ringbuffer.c vdr-2.6.1/ringbuffer.c --- vdr-2.6.1.orig/ringbuffer.c 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/ringbuffer.c 2022-02-06 18:05:26.452890690 +0100 @@ -368,6 +368,25 @@ return NULL; } +uchar *cRingBufferLinear::GetRest(int &Count) +{ + int Head = head; + if (getThreadTid <= 0) + getThreadTid = cThread::ThreadId(); + int rest = Size() - tail; + int diff = Head - tail; + int cont = (diff >= 0) ? diff : Size() + diff - margin; + if (cont > rest) + cont = rest; + uchar *p = buffer + tail; + if (cont > 0) { + Count = gotten = cont; + return p; + } + WaitForGet(); + return NULL; +} + void cRingBufferLinear::Del(int Count) { if (Count > gotten) { diff -Naur vdr-2.6.1.orig/ringbuffer.h vdr-2.6.1/ringbuffer.h --- vdr-2.6.1.orig/ringbuffer.h 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/ringbuffer.h 2022-02-06 18:05:26.452890690 +0100 @@ -98,6 +98,12 @@ ///< The data will remain in the buffer until a call to Del() deletes it. ///< Returns a pointer to the data, and stores the number of bytes ///< actually available in Count. If the returned pointer is NULL, Count has no meaning. + uchar *GetRest(int &Count); + ///< Gets data from the ring buffer disregarding the margin. + ///< Might have to be called several times to get all data. + ///< The data will remain in the buffer until a call to Del() deletes it. + ///< Returns a pointer to the data, and stores the number of bytes + ///< actually available in Count. If the returned pointer is NULL, Count has no meaning. void Del(int Count); ///< Deletes at most Count bytes from the ring buffer. ///< Count must be less or equal to the number that was returned by a previous diff -Naur vdr-2.6.1.orig/vdr.c vdr-2.6.1/vdr.c --- vdr-2.6.1.orig/vdr.c 2022-02-02 10:56:43.000000000 +0100 +++ vdr-2.6.1/vdr.c 2022-02-06 18:05:26.452890690 +0100 @@ -1352,13 +1352,22 @@ key = kNone; break; // Pausing live video: + case kFastRew: + { + // test if there's a live buffer to rewind into... + LOCK_CHANNELS_READ; + if (cDevice::ActualDevice()->GetPreRecording(Channels->GetByNumber(cDevice::CurrentChannel())) == NULL) { + break; + } + } + // fall through to pause case kPlayPause: case kPause: if (!Control) { DELETE_MENU; if (Setup.PauseKeyHandling) { if (Setup.PauseKeyHandling > 1 || Interface->Confirm(tr("Pause live video?"))) { - if (!cRecordControls::PauseLiveVideo()) + if (!cRecordControls::PauseLiveVideo(int(key) == kFastRew)) Skins.QueueMessage(mtError, tr("No free DVB device to record!")); } }