commit:     8fb604b710052829970783edc8de32804b2ad7ca
Author:     Martin Dummer <martin.dummer <AT> gmx <DOT> net>
AuthorDate: Mon Jun 20 09:38:59 2022 +0000
Commit:     Joonas Niilola <juippis <AT> gentoo <DOT> org>
CommitDate: Sun Jul  3 08:57:54 2022 +0000
URL:        https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=8fb604b7

media-video/vdr: version bump to 2.6.1

- new stable release
- rebase most patches for this version
- retirement of http://projects.vdr-developer.org: move some SRC_URI
  to github.com/vdr-projects

Package-Manager: Portage-3.0.30, Repoman-3.0.3
Signed-off-by: Martin Dummer <martin.dummer <AT> gmx.net>
Closes: https://github.com/gentoo/gentoo/pull/26000
Signed-off-by: Joonas Niilola <juippis <AT> gentoo.org>

 media-video/vdr/Manifest                           |   2 +
 .../vdr/files/vdr-2.4.1_mainmenuhook-1.0.1.patch   | 114 ++++
 .../vdr/files/vdr-2.6.1-patch-for-permashift.patch | 517 ++++++++++++++++++
 media-video/vdr/files/vdr-2.6.1_naludump.patch     | 598 +++++++++++++++++++++
 media-video/vdr/files/vdr-2.6.1_pinplugin.patch    | 447 +++++++++++++++
 media-video/vdr/vdr-2.6.1.ebuild                   | 197 +++++++
 6 files changed, 1875 insertions(+)

diff --git a/media-video/vdr/Manifest b/media-video/vdr/Manifest
index 42d12f50673f..b4c6683d67bb 100644
--- a/media-video/vdr/Manifest
+++ b/media-video/vdr/Manifest
@@ -6,4 +6,6 @@ DIST vdr-2.4.1_mainmenuhook-1.0.1.patch.bz2 1463 BLAKE2B 
2be3597a273a92d02be0d07
 DIST vdr-2.4.6_pinplugin.patch.bz2 5176 BLAKE2B 
142ad6551b8e37d223acf86f8c2f511cf2cb3664b5ff968bac5e44420c56bf5fdb974360df574141c424cd7a4b1e1489c4a504207420af10f722a77e6d2bad2f
 SHA512 
d5c7263b908302ebf88a5e5ddcb658f56ee92656b40797fd48af1ff9852454adc1d1672cc97411e7744cc31a56f4d0f51df5ece102c21b2aa25d87e3c3c8fe12
 DIST vdr-2.4.6_ttxtsubs_v2.patch.bz2 51971 BLAKE2B 
e4915314e8f659df1c3e0ff631fd0c3dcec89053ff727fd28400dc6eecc49c1d4743a9bedaf3b11fce03fc6082309d4b4658d00a5b01aa5cbe8686e05412f882
 SHA512 
14c7b4397ba65e0ff9a5fb0705872f1cb6f1cdd1752b14f83f260540da25b5957632900232f83904c3d4fd6759e537c85bda2cce61455729eab3050a96441548
 DIST vdr-2.4.7.tbz2 919632 BLAKE2B 
be4e1ec365330855e4b2b26f44b1f35dc323e4783e96ef344a67b3e9fe2c0499760ab0f3d27c5e3bdddf5a65ebb65b0c81a62092301d34370aa19d0dd63bb1ab
 SHA512 
41b8453bc6cdbb0db728b5d7d6624ab3ff9034b58ba443a8196f5fcc9fa78d18698cc91905890244e69d482ffc493374e6da2c97baed95fe742b888bdc63e42b
+DIST vdr-2.6.1.tbz2 937103 BLAKE2B 
d8bbf8cb74f79c328ba7a367cd120d9472be667def851fbdff2f2fda3addbf33ae1f0ab0f20dc0ea6b3a03667e36164a5acf5a17cff8f7934c15f4e61b3a8db2
 SHA512 
235ffd1654d8f13ba658533bfa5db9c9669e93106a63a770297997f9b8342807f270f26e7a6f5a3c127cd9f760bb94ae77f884dcad42a500615c28e1cf3fd92f
+DIST vdr-2.6.1_ttxtsubs_v2.patch 40549 BLAKE2B 
525c05b72fa957372cc1ef91428428f664f42d92eebc06fd0b179afbe99e4ecc4919c69cef000cc15c8340c9a2506d38cbf755fd6665cde1708ce17d4320a35f
 SHA512 
590c43e63818de8be9637cb3b32bf3399b82a181ca151d67e2341232669619020ab78ba9934da3a9c58cdb3bac56fc2a332a5838a92ee0851e42c3707726eb49
 DIST vdr-menuorg-2.3.x.diff 8852 BLAKE2B 
19b98d51a69f52ecda5500f51ef1741a8397953b20c490055eab0393da5f56ff9598c3e1e8ed8b915f5877e08deeb9ba7a9ef8d9356ad3a1fa12e3778869174a
 SHA512 
7b41c3a529858a4953a57f21619ea01864e140cc1755ee0b03caf1c4de41e80c3f805653502bc8d39d02a4dfcddf720acd4a8c8bd91f4871eef31d86e8e915c0

diff --git a/media-video/vdr/files/vdr-2.4.1_mainmenuhook-1.0.1.patch 
b/media-video/vdr/files/vdr-2.4.1_mainmenuhook-1.0.1.patch
new file mode 100644
index 000000000000..deec2cd7cfdb
--- /dev/null
+++ b/media-video/vdr/files/vdr-2.4.1_mainmenuhook-1.0.1.patch
@@ -0,0 +1,114 @@
+original 
https://raw.githubusercontent.com/VDR4Arch/vdr4arch/master/vdr/vdr-MainMenuHooks.patch
+
+rebased for media-video/vdr-2.4.1
+
+Signed-off-by: Joerg Bornkessel <[email protected]> ( 2019 Dez 22 )
+diff -Naur vdr-2.4.1.orig/config.h vdr-2.4.1/config.h
+--- vdr-2.4.1.orig/config.h    2019-12-22 00:04:59.000000000 +0100
++++ vdr-2.4.1/config.h 2019-12-22 00:11:25.000000000 +0100
+@@ -36,6 +36,10 @@
+ // plugins to work with newer versions of the core VDR as long as no
+ // VDR header files have changed.
+ 
++// The MainMenuHook Patch's version number:
++#define MAINMENUHOOKSVERSION "1.0.1"
++#define MAINMENUHOOKSVERSNUM 10001  // Version * 10000 + Major * 100 + Minor
++
+ #define MAXPRIORITY       99
+ #define MINPRIORITY       (-MAXPRIORITY)
+ #define LIVEPRIORITY      0                  // priority used when selecting 
a device for live viewing
+diff -Naur vdr-2.4.1.orig/menu.c vdr-2.4.1/menu.c
+--- vdr-2.4.1.orig/menu.c      2019-12-22 00:04:59.000000000 +0100
++++ vdr-2.4.1/menu.c   2019-12-22 00:11:25.000000000 +0100
+@@ -4395,15 +4395,31 @@
+ 
+   // Initial submenus:
+ 
++  cOsdObject *menu = NULL;
+   switch (State) {
+-    case osSchedule:   AddSubMenu(new cMenuSchedule); break;
+-    case osChannels:   AddSubMenu(new cMenuChannels); break;
+-    case osTimers:     AddSubMenu(new cMenuTimers); break;
+-    case osRecordings: AddSubMenu(new cMenuRecordings(NULL, 0, 
OpenSubMenus)); break;
+-    case osSetup:      AddSubMenu(new cMenuSetup); break;
+-    case osCommands:   AddSubMenu(new cMenuCommands(tr("Commands"), 
&Commands)); break;
++    case osSchedule:
++        if 
(!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", 
&menu))
++            menu = new cMenuSchedule;
++        break;
++    case osChannels:
++        if 
(!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", 
&menu))
++            menu = new cMenuChannels;
++        break;
++    case osTimers:
++        if 
(!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu))
++            menu = new cMenuTimers;
++        break;
++    case osRecordings:
++        if 
(!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", 
&menu))
++            menu = new cMenuRecordings(NULL, 0, OpenSubMenus);
++        break;
++    case osSetup:      menu = new cMenuSetup; break;
++    case osCommands:   menu = new cMenuCommands(tr("Commands"), &Commands); 
break;
+     default: break;
+     }
++  if (menu)
++     if (menu->IsMenu())
++        AddSubMenu((cOsdMenu *) menu);
+ }
+ 
+ cOsdObject *cMenuMain::PluginOsdObject(void)
+@@ -4511,13 +4527,34 @@
+   eOSState state = cOsdMenu::ProcessKey(Key);
+   HadSubMenu |= HasSubMenu();
+ 
++  cOsdObject *menu = NULL;
+   switch (state) {
+-    case osSchedule:   return AddSubMenu(new cMenuSchedule);
+-    case osChannels:   return AddSubMenu(new cMenuChannels);
+-    case osTimers:     return AddSubMenu(new cMenuTimers);
+-    case osRecordings: return AddSubMenu(new cMenuRecordings);
+-    case osSetup:      return AddSubMenu(new cMenuSetup);
+-    case osCommands:   return AddSubMenu(new cMenuCommands(tr("Commands"), 
&Commands));
++    case osSchedule:
++        if 
(!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", 
&menu))
++            menu = new cMenuSchedule;
++        else
++            state = osContinue;
++        break;
++    case osChannels:
++        if 
(!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", 
&menu))
++            menu = new cMenuChannels;
++        else
++            state = osContinue;
++        break;
++    case osTimers:
++        if 
(!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu))
++            menu = new cMenuTimers;
++        else
++            state = osContinue;
++        break;
++    case osRecordings:
++        if 
(!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", 
&menu))
++            menu = new cMenuRecordings;
++        else
++            state = osContinue;
++        break;
++    case osSetup:      menu = new cMenuSetup; break;
++    case osCommands:   menu = new cMenuCommands(tr("Commands"), &Commands); 
break;
+     case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
+                           if (cOsdItem *item = Get(Current())) {
+                              cRecordControls::Stop(item->Text() + 
strlen(tr(STOP_RECORDING)));
+@@ -4568,6 +4605,12 @@
+                default:      break;
+                }
+     }
++  if (menu) {
++     if (menu->IsMenu())
++        return AddSubMenu((cOsdMenu *) menu);
++     pluginOsdObject = menu;
++     return osPlugin;
++  } 
+   if (!HasSubMenu() && Update(HadSubMenu))
+      Display();
+   if (Key != kNone) {

diff --git a/media-video/vdr/files/vdr-2.6.1-patch-for-permashift.patch 
b/media-video/vdr/files/vdr-2.6.1-patch-for-permashift.patch
new file mode 100644
index 000000000000..03b476f986bb
--- /dev/null
+++ b/media-video/vdr/files/vdr-2.6.1-patch-for-permashift.patch
@@ -0,0 +1,517 @@
+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 <[email protected]>
+
+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!"));
+                         }
+                      }

diff --git a/media-video/vdr/files/vdr-2.6.1_naludump.patch 
b/media-video/vdr/files/vdr-2.6.1_naludump.patch
new file mode 100644
index 000000000000..efea3a1d74f7
--- /dev/null
+++ b/media-video/vdr/files/vdr-2.6.1_naludump.patch
@@ -0,0 +1,598 @@
+diff -a -U 2 -r a/config.c b/config.c
+--- a/config.c
++++ b/config.c
+@@ -469,4 +469,5 @@
+   SplitEditedFiles = 0;
+   DelTimeshiftRec = 0;
++  DumpNaluFill = 0;
+   MinEventTimeout = 30;
+   MinUserInactivity = 300;
+@@ -697,4 +698,5 @@
+   else if (!strcasecmp(Name, "SplitEditedFiles"))    SplitEditedFiles   = 
atoi(Value);
+   else if (!strcasecmp(Name, "DelTimeshiftRec"))     DelTimeshiftRec    = 
atoi(Value);
++  else if (!strcasecmp(Name, "DumpNaluFill"))        DumpNaluFill       = 
atoi(Value);
+   else if (!strcasecmp(Name, "MinEventTimeout"))     MinEventTimeout    = 
atoi(Value);
+   else if (!strcasecmp(Name, "MinUserInactivity"))   MinUserInactivity  = 
atoi(Value);
+@@ -829,4 +831,5 @@
+   Store("SplitEditedFiles",   SplitEditedFiles);
+   Store("DelTimeshiftRec",    DelTimeshiftRec);
++  Store("DumpNaluFill",       DumpNaluFill);
+   Store("MinEventTimeout",    MinEventTimeout);
+   Store("MinUserInactivity",  MinUserInactivity);
+diff -a -U 2 -r a/config.h b/config.h
+--- a/config.h
++++ b/config.h
+@@ -341,4 +341,5 @@
+   int SplitEditedFiles;
+   int DelTimeshiftRec;
++  int DumpNaluFill;
+   int MinEventTimeout, MinUserInactivity;
+   time_t NextWakeupTime;
+diff -a -U 2 -r a/menu.c b/menu.c
+--- a/menu.c
++++ b/menu.c
+@@ -4185,4 +4185,5 @@
+   Add(new cMenuEditBoolItem(tr("Setup.Recording$Split edited files"),        
&data.SplitEditedFiles));
+   Add(new cMenuEditStraItem(tr("Setup.Recording$Delete timeshift 
recording"),&data.DelTimeshiftRec, 3, delTimeshiftRecTexts));
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Dump NALU Fill data"),       
&data.DumpNaluFill));
+ }
+ 
+diff -a -U 2 -r a/recorder.c b/recorder.c
+--- a/recorder.c
++++ b/recorder.c
+@@ -195,4 +195,12 @@
+      }
+   frameDetector = new cFrameDetector(Pid, Type);
++  if (   Type == 0x1B // MPEG4 video
++      && (Setup.DumpNaluFill ? (strstr(FileName, "NALUKEEP") == NULL) : 
(strstr(FileName, "NALUDUMP") != NULL))) { // MPEG4
++     isyslog("Starting NALU fill dumper");
++     naluStreamProcessor = new cNaluStreamProcessor();
++     naluStreamProcessor->SetPid(Pid);
++     }
++  else
++     naluStreamProcessor = NULL;
+   index = NULL;
+   fileSize = 0;
+@@ -217,4 +225,10 @@
+ {
+   Detach();
++  if (naluStreamProcessor) {
++     long long int TotalPackets = naluStreamProcessor->GetTotalPackets();
++     long long int DroppedPackets = naluStreamProcessor->GetDroppedPackets();
++     isyslog("NALU fill dumper: %lld of %lld packets dropped, %lli%%", 
DroppedPackets, TotalPackets, TotalPackets ? DroppedPackets*100/TotalPackets : 
0);
++     delete naluStreamProcessor;
++     }
+   delete index;
+   delete fileName;
+@@ -357,10 +371,32 @@
+                        t.Set(MAXBROKENTIMEOUT);
+                        }
+-                    if (recordFile->Write(b, Count) < 0) {
+-                       LOG_ERROR_STR(fileName->Name());
+-                       break;
++                  if (naluStreamProcessor) {
++                       naluStreamProcessor->PutBuffer(b, Count);
++                       bool Fail = false;
++                       while (true) {
++                             int OutLength = 0;
++                             uchar *OutData = 
naluStreamProcessor->GetBuffer(OutLength);
++                             if (!OutData || OutLength <= 0)
++                                break;
++                             if (recordFile->Write(OutData, OutLength) < 0) {
++                                LOG_ERROR_STR(fileName->Name());
++                                Fail = true;
++                                break;
++                                }
++                             HandleErrors();
++                             fileSize += OutLength;
++                             }
++                       if (Fail)
++                          break;
++                       }
++                    else {
++                       if (recordFile->Write(b, Count) < 0) {
++                          LOG_ERROR_STR(fileName->Name());
++                          break;
++                          }
++                       HandleErrors();
++                       fileSize += Count;
+                        }
+-                    HandleErrors();
+-                    fileSize += Count;
++
+                     }
+                  }
+diff -a -U 2 -r a/recorder.h b/recorder.h
+--- a/recorder.h
++++ b/recorder.h       C2022-01-08 17:51:00.397595525 +0100
+@@ -27,4 +27,5 @@
+   cFrameDetector *frameDetector;
+   cPatPmtGenerator patPmtGenerator;
++  cNaluStreamProcessor *naluStreamProcessor;
+   cFileName *fileName;
+   cRecordingInfo *recordingInfo;
+diff -a -U 2 -r a/remux.c b/remux.c
+--- a/remux.c
++++ b/remux.c
+@@ -357,4 +357,40 @@
+ }
+ 
++void TsExtendAdaptionField(unsigned char *Packet, int ToLength)
++{
++    // Hint: ExtenAdaptionField(p, TsPayloadOffset(p) - 4) is a null operation
++
++    int Offset = TsPayloadOffset(Packet); // First byte after existing 
adaption field
++
++    if (ToLength <= 0)
++    {
++        // Remove adaption field
++        Packet[3] = Packet[3] & ~TS_ADAPT_FIELD_EXISTS;
++        return;
++    }
++
++    // Set adaption field present
++    Packet[3] = Packet[3] | TS_ADAPT_FIELD_EXISTS;
++
++    // Set new length of adaption field:
++    Packet[4] = ToLength <= TS_SIZE-4 ? ToLength-1 : TS_SIZE-4-1;
++
++    if (Packet[4] == TS_SIZE-4-1)
++    {
++        // No more payload, remove payload flag
++        Packet[3] = Packet[3] & ~TS_PAYLOAD_EXISTS;
++    }
++
++    int NewPayload = TsPayloadOffset(Packet); // First byte after new 
adaption field
++
++    // Fill new adaption field
++    if (Offset == 4 && Offset < NewPayload)
++        Offset++; // skip adaptation_field_length
++    if (Offset == 5 && Offset < NewPayload)
++        Packet[Offset++] = 0; // various flags set to 0
++    while (Offset < NewPayload)
++        Packet[Offset++] = 0xff; // stuffing byte
++}
++
+ // --- cPatPmtGenerator ------------------------------------------------------
+ 
+@@ -1765,2 +1801,343 @@
+   return Processed;
+ }
++
++// --- cNaluDumper ---------------------------------------------------------
++
++cNaluDumper::cNaluDumper()
++{
++    LastContinuityOutput = -1;
++    reset();
++}
++
++void cNaluDumper::reset()
++{
++    LastContinuityInput = -1;
++    ContinuityOffset = 0;
++    PesId = -1;
++    PesOffset = 0;
++    NaluFillState = NALU_NONE;
++    NaluOffset = 0;
++    History = 0xffffffff;
++    DropAllPayload = false;
++}
++
++void cNaluDumper::ProcessPayload(unsigned char *Payload, int size, bool 
PayloadStart, sPayloadInfo &Info)
++{
++    Info.DropPayloadStartBytes = 0;
++    Info.DropPayloadEndBytes = 0;
++    int LastKeepByte = -1;
++
++    if (PayloadStart)
++    {
++        History = 0xffffffff;
++        PesId = -1;
++        NaluFillState = NALU_NONE;
++    }
++
++    for (int i=0; i<size; i++) {
++        History = (History << 8) | Payload[i];
++
++        PesOffset++;
++        NaluOffset++;
++
++        bool DropByte = false;
++
++        if (History >= 0x00000180 && History <= 0x000001FF)
++        {
++            // Start of PES packet
++            PesId = History & 0xff;
++            PesOffset = 0;
++            NaluFillState = NALU_NONE;
++        }
++        else if (PesId >= 0xe0 && PesId <= 0xef // video stream
++                 && History >= 0x00000100 && History <= 0x0000017F) // NALU 
start code
++        {
++            int NaluId = History & 0xff;
++            NaluOffset = 0;
++            NaluFillState = ((NaluId & 0x1f) == 0x0c) ? NALU_FILL : NALU_NONE;
++        }
++
++        if (PesId >= 0xe0 && PesId <= 0xef // video stream
++            && PesOffset >= 1 && PesOffset <= 2)
++        {
++            Payload[i] = 0; // Zero out PES length field
++        }
++
++        if (NaluFillState == NALU_FILL && NaluOffset > 0) // Within NALU fill 
data
++        {
++            // We expect a series of 0xff bytes terminated by a single 0x80 
byte.
++
++            if (Payload[i] == 0xFF)
++            {
++                DropByte = true;
++            }
++            else if (Payload[i] == 0x80)
++            {
++                NaluFillState = NALU_TERM; // Last byte of NALU fill, next 
byte sets NaluFillEnd=true
++                DropByte = true;
++            }
++            else // Invalid NALU fill
++            {
++                dsyslog("cNaluDumper: Unexpected NALU fill data: %02x", 
Payload[i]);
++                NaluFillState = NALU_END;
++                if (LastKeepByte == -1)
++                {
++                    // Nalu fill from beginning of packet until last byte
++                    // packet start needs to be dropped
++                    Info.DropPayloadStartBytes = i;
++                }
++            }
++        }
++        else if (NaluFillState == NALU_TERM) // Within NALU fill data
++        {
++            // We are after the terminating 0x80 byte
++            NaluFillState = NALU_END;
++            if (LastKeepByte == -1)
++            {
++                // Nalu fill from beginning of packet until last byte
++                // packet start needs to be dropped
++                Info.DropPayloadStartBytes = i;
++            }
++        }
++
++        if (!DropByte)
++            LastKeepByte = i; // Last useful byte
++    }
++
++    Info.DropAllPayloadBytes = (LastKeepByte == -1);
++    Info.DropPayloadEndBytes = size-1-LastKeepByte;
++}
++
++bool cNaluDumper::ProcessTSPacket(unsigned char *Packet)
++{
++    bool HasAdaption = TsHasAdaptationField(Packet);
++    bool HasPayload = TsHasPayload(Packet);
++
++    // Check continuity:
++    int ContinuityInput = TsContinuityCounter(Packet);
++    if (LastContinuityInput >= 0)
++    {
++        int NewContinuityInput = HasPayload ? (LastContinuityInput + 1) & 
TS_CONT_CNT_MASK : LastContinuityInput;
++        int Offset = (NewContinuityInput - ContinuityInput) & 
TS_CONT_CNT_MASK;
++        if (Offset > 0)
++            dsyslog("cNaluDumper: TS continuity offset %i", Offset);
++        if (Offset > ContinuityOffset)
++            ContinuityOffset = Offset; // max if packets get dropped, 
otherwise always the current one.
++    }
++    LastContinuityInput = ContinuityInput;
++
++    if (HasPayload) {
++        sPayloadInfo Info;
++        int Offset = TsPayloadOffset(Packet);
++        ProcessPayload(Packet + Offset, TS_SIZE - Offset, 
TsPayloadStart(Packet), Info);
++
++        if (DropAllPayload && !Info.DropAllPayloadBytes)
++        {
++            // Return from drop packet mode to normal mode
++            DropAllPayload = false;
++
++            // Does the packet start with some remaining NALU fill data?
++            if (Info.DropPayloadStartBytes > 0)
++            {
++                // Add these bytes as stuffing to the adaption field.
++
++                // Sample payload layout:
++                // FF FF FF FF FF 80 00 00 01 xx xx xx xx
++                //                   ^DropPayloadStartBytes
++
++                TsExtendAdaptionField(Packet, Offset - 4 + 
Info.DropPayloadStartBytes);
++            }
++        }
++
++        bool DropThisPayload = DropAllPayload;
++
++        if (!DropAllPayload && Info.DropPayloadEndBytes > 0) // Payload ends 
with 0xff NALU Fill
++        {
++            // Last packet of useful data
++            // Do early termination of NALU fill data
++            Packet[TS_SIZE-1] = 0x80;
++            DropAllPayload = true;
++            // Drop all packets AFTER this one
++
++            // Since we already wrote the 0x80, we have to make sure that
++            // as soon as we stop dropping packets, any beginning NALU fill 
of next
++            // packet gets dumped. (see DropPayloadStartBytes above)
++        }
++
++        if (DropThisPayload && HasAdaption)
++        {
++            // Drop payload data, but keep adaption field data
++            TsExtendAdaptionField(Packet, TS_SIZE-4);
++            DropThisPayload = false;
++        }
++
++        if (DropThisPayload)
++        {
++            return true; // Drop packet
++        }
++    }
++
++    // Fix Continuity Counter and reproduce incoming offsets:
++    int NewContinuityOutput = TsHasPayload(Packet) ? (LastContinuityOutput + 
1) & TS_CONT_CNT_MASK : LastContinuityOutput;
++    NewContinuityOutput = (NewContinuityOutput + ContinuityOffset) & 
TS_CONT_CNT_MASK;
++    TsSetContinuityCounter(Packet, NewContinuityOutput);
++    LastContinuityOutput = NewContinuityOutput;
++    ContinuityOffset = 0;
++
++    return false; // Keep packet
++}
++
++// --- cNaluStreamProcessor 
---------------------------------------------------------
++
++cNaluStreamProcessor::cNaluStreamProcessor()
++{
++    pPatPmtParser = NULL;
++    vpid = -1;
++    data = NULL;
++    length = 0;
++    tempLength = 0;
++    tempLengthAtEnd = false;
++    TotalPackets = 0;
++    DroppedPackets = 0;
++}
++
++void cNaluStreamProcessor::PutBuffer(uchar *Data, int Length)
++{
++    if (length > 0)
++        esyslog("cNaluStreamProcessor::PutBuffer: New data before old data 
was processed!");
++
++    data = Data;
++    length = Length;
++}
++
++uchar* cNaluStreamProcessor::GetBuffer(int &OutLength)
++{
++    if (length <= 0)
++    {
++        // Need more data - quick exit
++        OutLength = 0;
++        return NULL;
++    }
++    if (tempLength > 0) // Data in temp buffer?
++    {
++        if (tempLengthAtEnd) // Data is at end, copy to beginning
++        {
++            // Overlapping src and dst!
++            for (int i=0; i<tempLength; i++)
++                tempBuffer[i] = tempBuffer[TS_SIZE-tempLength+i];
++        }
++        // Normalize TempBuffer fill
++        if (tempLength < TS_SIZE && length > 0)
++        {
++            int Size = min(TS_SIZE-tempLength, length);
++            memcpy(tempBuffer+tempLength, data, Size);
++            data += Size;
++            length -= Size;
++            tempLength += Size;
++        }
++        if (tempLength < TS_SIZE)
++        {
++            // All incoming data buffered, but need more data
++            tempLengthAtEnd = false;
++            OutLength = 0;
++            return NULL;
++        }
++        // Now: TempLength==TS_SIZE
++        if (tempBuffer[0] != TS_SYNC_BYTE)
++        {
++            // Need to sync on TS within temp buffer
++            int Skipped = 1;
++            while (Skipped < TS_SIZE && (tempBuffer[Skipped] != TS_SYNC_BYTE 
|| (Skipped < length && data[Skipped] != TS_SYNC_BYTE)))
++                Skipped++;
++            esyslog("ERROR: skipped %d bytes to sync on start of TS packet", 
Skipped);
++            // Pass through skipped bytes
++            tempLengthAtEnd = true;
++            tempLength = TS_SIZE - Skipped; // may be 0, thats ok
++            OutLength = Skipped;
++            return tempBuffer;
++        }
++        // Now: TempBuffer is a TS packet
++        int Pid = TsPid(tempBuffer);
++        if (pPatPmtParser)
++        {
++            if (Pid == 0)
++                pPatPmtParser->ParsePat(tempBuffer, TS_SIZE);
++            else if (pPatPmtParser->IsPmtPid(Pid))
++                pPatPmtParser->ParsePmt(tempBuffer, TS_SIZE);
++        }
++
++        TotalPackets++;
++        bool Drop = false;
++        if (Pid == vpid || (pPatPmtParser && Pid == pPatPmtParser->Vpid() && 
pPatPmtParser->Vtype() == 0x1B))
++            Drop = NaluDumper.ProcessTSPacket(tempBuffer);
++        if (!Drop)
++        {
++            // Keep this packet, then continue with new data
++            tempLength = 0;
++            OutLength = TS_SIZE;
++            return tempBuffer;
++        }
++        // Drop TempBuffer
++        DroppedPackets++;
++        tempLength = 0;
++    }
++    // Now: TempLength==0, just process data/length
++
++    // Pointer to processed data / length:
++    uchar *Out = data;
++    uchar *OutEnd = Out;
++
++    while (length >= TS_SIZE)
++    {
++        if (data[0] != TS_SYNC_BYTE) {
++            int Skipped = 1;
++            while (Skipped < length && (data[Skipped] != TS_SYNC_BYTE || 
(length - Skipped > TS_SIZE && data[Skipped + TS_SIZE] != TS_SYNC_BYTE)))
++                Skipped++;
++            esyslog("ERROR: skipped %d bytes to sync on start of TS packet", 
Skipped);
++
++            // Pass through skipped bytes
++            if (OutEnd != data)
++                memcpy(OutEnd, data, Skipped);
++            OutEnd += Skipped;
++            continue;
++        }
++        // Now: Data starts with complete TS packet
++
++        int Pid = TsPid(data);
++        if (pPatPmtParser)
++        {
++            if (Pid == 0)
++                pPatPmtParser->ParsePat(data, TS_SIZE);
++            else if (pPatPmtParser->IsPmtPid(Pid))
++                pPatPmtParser->ParsePmt(data, TS_SIZE);
++        }
++
++        TotalPackets++;
++        bool Drop = false;
++        if (Pid == vpid || (pPatPmtParser && Pid == pPatPmtParser->Vpid() && 
pPatPmtParser->Vtype() == 0x1B))
++            Drop = NaluDumper.ProcessTSPacket(data);
++        if (!Drop)
++        {
++            if (OutEnd != data)
++                memcpy(OutEnd, data, TS_SIZE);
++            OutEnd += TS_SIZE;
++        }
++        else
++        {
++            DroppedPackets++;
++        }
++        data += TS_SIZE;
++        length -= TS_SIZE;
++    }
++    // Now: Less than a packet remains.
++    if (length > 0)
++    {
++        // copy remains into temp buffer
++        memcpy(tempBuffer, data, length);
++        tempLength = length;
++        tempLengthAtEnd = false;
++        length = 0;
++    }
++    OutLength = (OutEnd - Out);
++    return OutLength > 0 ? Out : NULL;
++}
+diff -a -U 2 -r a/remux.h b/remux.h
+--- a/remux.h
++++ b/remux.h
+@@ -65,4 +65,9 @@
+ }
+ 
++inline bool TsSetPayload(const uchar *p)
++{
++  return p[3] & TS_PAYLOAD_EXISTS;
++}
++
+ inline bool TsHasAdaptationField(const uchar *p)
+ {
+@@ -156,4 +161,5 @@
+ void TsSetPts(uchar *p, int l, int64_t Pts);
+ void TsSetDts(uchar *p, int l, int64_t Dts);
++void TsExtendAdaptionField(unsigned char *Packet, int ToLength);
+ 
+ // Some PES handling tools:
+@@ -550,3 +556,77 @@
+   };
+ 
++
++#define PATCH_NALUDUMP 100
++
++class cNaluDumper {
++    unsigned int History;
++
++    int LastContinuityInput;
++    int LastContinuityOutput;
++    int ContinuityOffset;
++
++    bool DropAllPayload;
++
++    int PesId;
++    int PesOffset;
++
++    int NaluOffset;
++
++    enum eNaluFillState {
++        NALU_NONE=0,    // currently not NALU fill stream
++        NALU_FILL,      // Within NALU fill stream, 0xff bytes and NALU start 
code in byte 0
++        NALU_TERM,      // Within NALU fill stream, read 0x80 terminating byte
++        NALU_END        // Beyond end of NALU fill stream, expecting 0x00 
0x00 0x01 now
++        };
++
++    eNaluFillState NaluFillState;
++
++    struct sPayloadInfo {
++        int DropPayloadStartBytes;
++        int DropPayloadEndBytes;
++        bool DropAllPayloadBytes;
++    };
++
++public:
++    cNaluDumper();
++
++    void reset();
++
++    // Single packet interface:
++    bool ProcessTSPacket(unsigned char *Packet);
++
++private:
++    void ProcessPayload(unsigned char *Payload, int size, bool PayloadStart, 
sPayloadInfo &Info);
++};
++
++class cNaluStreamProcessor {
++    //Buffer stream interface:
++    int vpid;
++    uchar *data;
++    int length;
++    uchar tempBuffer[TS_SIZE];
++    int tempLength;
++    bool tempLengthAtEnd;
++    cPatPmtParser *pPatPmtParser;
++    cNaluDumper NaluDumper;
++
++    long long int TotalPackets;
++    long long int DroppedPackets;
++public:
++    cNaluStreamProcessor();
++
++    void SetPid(int VPid) { vpid = VPid; }
++    void SetPatPmtParser(cPatPmtParser *_pPatPmtParser) { pPatPmtParser = 
_pPatPmtParser; }
++    // Set either a PID or set a pointer to an PatPmtParser that will detect 
_one_ PID
++
++    void PutBuffer(uchar *Data, int Length);
++    // Add new data to be processed. Data must be valid until Get() returns 
NULL.
++    uchar* GetBuffer(int &OutLength);
++    // Returns filtered data, or NULL/0 to indicate that all data from Put() 
was processed
++    // or buffered.
++
++    long long int GetTotalPackets() { return TotalPackets; }
++    long long int GetDroppedPackets() { return DroppedPackets; }
++};
++
+ #endif // __REMUX_H

diff --git a/media-video/vdr/files/vdr-2.6.1_pinplugin.patch 
b/media-video/vdr/files/vdr-2.6.1_pinplugin.patch
new file mode 100644
index 000000000000..2552f9e7109f
--- /dev/null
+++ b/media-video/vdr/files/vdr-2.6.1_pinplugin.patch
@@ -0,0 +1,447 @@
+original vdr-pinplugin_vdr-2.3.1.diff
+rebased for media-video/vdr-2.6.1
+
+Signed-off-by: Christian Kunkel <[email protected]> ( 2021 Feb 12 )
+Reviewed-by: Martin Dummer <[email protected]> ( 2022-06-22 )
+diff -Naur a/Makefile b/Makefile
+--- a/Makefile 2022-02-02 10:56:43.000000000 +0100
++++ b/Makefile 2022-06-20 08:08:11.346956148 +0200
+@@ -351,7 +351,7 @@
+ clean:
+       @$(MAKE) --no-print-directory -C $(LSIDIR) clean
+       @-rm -f $(OBJS) $(DEPFILE) vdr vdr.pc core* *~
+-      @-rm -rf $(LOCALEDIR) $(PODIR)/*.mo $(PODIR)/*.pot
++      @-rm -rf $(LOCALEDIR) $(PODIR)/*~ $(PODIR)/*.mo $(PODIR)/*.pot
+       @-rm -rf include
+       @-rm -rf srcdoc
+ CLEAN: clean
+diff -Naur a/device.c b/device.c
+--- a/device.c 2022-02-02 10:56:43.000000000 +0100
++++ b/device.c 2022-06-20 08:08:11.346956148 +0200
+@@ -839,6 +839,7 @@
+      const cChannel *Channel;
+      while ((Channel = Channels->GetByNumber(n, Direction)) != NULL) {
+            // try only channels which are currently available
++           if (!cStatus::MsgChannelProtected(0, Channel))      // PIN PATCH
+            if (GetDevice(Channel, LIVEPRIORITY, true, true))
+               break;
+            n = Channel->Number() + Direction;
+@@ -860,6 +861,12 @@
+ 
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++  // I hope 'LiveView = false' indicates a channel switch for recording, // 
PIN PATCH
++  // I really don't know, but it works ...                               // 
PIN PATCH
++
++  if (LiveView && cStatus::MsgChannelProtected(this, Channel))           // 
PIN PATCH
++     return scrNotAvailable;                                             // 
PIN PATCH
++
+   cMutexLock MutexLock(&mutexChannel); // to avoid a race between SVDRP CHAN 
and HasProgramme()
+   cStatus::MsgChannelSwitch(this, 0, LiveView);
+ 
+diff -Naur a/menu.c b/menu.c
+--- a/menu.c   2022-02-02 10:56:43.000000000 +0100
++++ b/menu.c   2022-06-20 08:08:11.346956148 +0200
+@@ -1035,6 +1035,18 @@
+      Add(new cMenuEditBitItem( tr("VPS"),          &data.flags, tfVps));
+      Add(new cMenuEditIntItem( tr("Priority"),     &data.priority, 0, 
MAXPRIORITY));
+      Add(new cMenuEditIntItem( tr("Lifetime"),     &data.lifetime, 0, 
MAXLIFETIME));
++
++     // PIN PATCH
++     if (cOsd::pinValid || !data.fskProtection) Add(new 
cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++     else {
++        char* buf = 0;
++        int res = 0;
++        res = asprintf(&buf, "%s\t%s", tr("Childlock"), data.fskProtection ? 
tr("yes") : tr("no"));
++        if (res < 0) ; // memory problems :o
++        Add(new cOsdItem(buf));
++        free(buf);
++        }
++
+      Add(file = new cMenuEditStrItem( tr("File"),   data.file, 
sizeof(data.file)));
+      SetFirstDayItem();
+      SetPatternItem(true);
+@@ -3130,7 +3142,8 @@
+                       }
+                    }
+                }
+-            if (*Item->Text() && !LastDir) {
++            if (*Item->Text() && !LastDir
++                && (!cStatus::MsgReplayProtected(Item->Recording(), 
Item->Name(), base, Item->IsDirectory(), true))) { // PIN PATCH
+                Add(Item);
+                LastItem = Item;
+                if (Item->IsDirectory())
+@@ -3201,6 +3214,9 @@
+ {
+   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+   if (ri) {
++     if (cStatus::MsgReplayProtected(ri->Recording(), ri->Name(), base,
++                                     ri->IsDirectory()) == true)    // PIN 
PATCH
++        return osContinue;
+      if (ri->IsDirectory())
+         Open();
+      else {
+@@ -4506,28 +4522,32 @@
+ 
+   // Basic menu items:
+ 
+-  Add(new cOsdItem(hk(tr("Schedule")),   osSchedule));
+-  Add(new cOsdItem(hk(tr("Channels")),   osChannels));
+-  Add(new cOsdItem(hk(tr("Timers")),     osTimers));
+-  Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++  // PIN PATCH
++  if (!cStatus::MsgMenuItemProtected("Schedule", true))   Add(new 
cOsdItem(hk(tr("Schedule")),   osSchedule));
++  if (!cStatus::MsgMenuItemProtected("Channels", true))   Add(new 
cOsdItem(hk(tr("Channels")),   osChannels));
++  if (!cStatus::MsgMenuItemProtected("Timers", true))     Add(new 
cOsdItem(hk(tr("Timers")),     osTimers));
++  if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new 
cOsdItem(hk(tr("Recordings")), osRecordings));
+ 
+   // Plugins:
+ 
+   for (int i = 0; ; i++) {
+       cPlugin *p = cPluginManager::GetPlugin(i);
+       if (p) {
++         if (!cStatus::MsgPluginProtected(p, true)) {          // PIN PATCH
+          const char *item = p->MainMenuEntry();
+          if (item)
+             Add(new cMenuPluginItem(hk(item), i));
+          }
++      }
+       else
+          break;
+       }
+ 
+   // More basic menu items:
+ 
+-  Add(new cOsdItem(hk(tr("Setup")),      osSetup));
++  if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new 
cOsdItem(hk(tr("Setup")), osSetup));  // PIN PATCH
+   if (Commands.Count())
++     if (!cStatus::MsgMenuItemProtected("Commands", true))     // PIN PATCH
+      Add(new cOsdItem(hk(tr("Commands")),  osCommands));
+ 
+   Update(true);
+@@ -4600,6 +4620,14 @@
+   eOSState state = cOsdMenu::ProcessKey(Key);
+   HadSubMenu |= HasSubMenu();
+ 
++  // > PIN PATCH
++  cOsdItem* item = Get(Current());
++
++  if (item && item->Text() && state != osContinue && state != osUnknown && 
state != osBack)
++     if (cStatus::MsgMenuItemProtected(item->Text()))
++        return osContinue;
++  // PIN PATCH <
++
+   switch (state) {
+     case osSchedule:   return AddSubMenu(new cMenuSchedule);
+     case osChannels:   return AddSubMenu(new cMenuChannels);
+@@ -4624,6 +4652,7 @@
+                          if (item) {
+                             cPlugin *p = 
cPluginManager::GetPlugin(item->PluginIndex());
+                             if (p) {
++                            if (!cStatus::MsgPluginProtected(p)) {  // PIN 
PATCH
+                                cOsdObject *menu = p->MainMenuAction();
+                                if (menu) {
+                                   if (menu->IsMenu())
+@@ -4635,6 +4664,7 @@
+                                   }
+                                }
+                             }
++                         }
+                          state = osEnd;
+                        }
+                        break;
+@@ -4814,6 +4844,7 @@
+            Channel = Direction > 0 ? Channels->Next(Channel) : 
Channels->Prev(Channel);
+            if (!Channel && Setup.ChannelsWrap)
+               Channel = Direction > 0 ? Channels->First() : Channels->Last();
++           if (!cStatus::MsgChannelProtected(0, Channel))                   
// PIN PATCH
+            if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 
LIVEPRIORITY, true, true))
+               return Channel;
+            }
+@@ -5491,6 +5522,7 @@
+            for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+                if (!RecordControls[i]) {
+                   RecordControls[i] = new cRecordControl(device, Timers, 
Timer, Pause);
++                  cStatus::MsgRecordingFile(RecordControls[i]->FileName());  
// PIN PATCH
+                   return RecordControls[i]->Process(time(NULL));
+                   }
+                }
+diff -Naur a/osd.c b/osd.c
+--- a/osd.c    2022-02-02 10:56:43.000000000 +0100
++++ b/osd.c    2022-06-20 08:08:11.346956148 +0200
+@@ -1844,6 +1844,7 @@
+ cSize cOsd::maxPixmapSize(INT_MAX, INT_MAX);
+ cVector<cOsd *> cOsd::Osds;
+ cMutex cOsd::mutex;
++bool cOsd::pinValid = false;   // PIN PATCH
+ 
+ cOsd::cOsd(int Left, int Top, uint Level)
+ {
+diff -Naur a/osd.h b/osd.h
+--- a/osd.h    2022-02-02 10:56:43.000000000 +0100
++++ b/osd.h    2022-06-20 08:08:11.346956148 +0200
+@@ -957,6 +957,7 @@
+        ///<
+        ///< If a plugin uses a derived cPixmap implementation, it needs to 
use that
+        ///< type instead of cPixmapMemory.
++  static bool pinValid;   // PIN PATCH
+   };
+ 
+ #define MAXOSDIMAGES 64
+diff -Naur a/status.c b/status.c
+--- a/status.c 2022-02-02 10:56:43.000000000 +0100
++++ b/status.c 2022-06-20 08:08:11.346956148 +0200
+@@ -136,3 +136,55 @@
+   for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+       sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, 
FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* 
Channel)     // PIN PATCH
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      if (sm->ChannelProtected(Device, Channel) == true)
++       return true;
++
++  return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* 
Name,
++                                 const char* Base, bool isDirectory, int 
menuView)    // PIN PATCH
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++     if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == 
true)
++         return true;
++      return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = 
statusMonitors.Next(sm))   // PIN PATCH
++      sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = 
statusMonitors.Next(sm))   // PIN PATCH
++     sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView)               
   // PIN PATCH
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++     if (sm->PluginProtected(Plugin, menuView) == true)
++         return true;
++      return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key)                                  
   // PIN PATCH
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++     sm->UserAction(key);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView)            
   // PIN PATCH
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++     if (sm->MenuItemProtected(Name, menuView) == true)
++         return true;
++      return false;
++}
+diff -Naur a/status.h b/status.h
+--- a/status.h 2022-02-02 10:56:43.000000000 +0100
++++ b/status.h 2022-06-20 08:08:11.350956230 +0200
+@@ -14,6 +14,7 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#include "plugin.h"
+ 
+ // Several member functions of the following classes are called with a 
pointer to
+ // an object from a global list (cTimer, cChannel, cRecording or cEvent). In 
these
+@@ -99,6 +100,22 @@
+                // The OSD displays the single line Text with the current 
channel information.
+   virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, 
const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, 
const char *FollowingSubtitle) {}
+                // The OSD displays the given programme information.
++  virtual bool ChannelProtected(const cDevice *Device, const cChannel* 
Channel)  { return false; }         // PIN PATCH
++               // Checks if a channel is protected.
++  virtual bool ReplayProtected(const cRecording* Recording, const char* Name,
++                               const char* Base, bool isDirectory, int 
menuView = false) { return false; } // PIN PATCH
++               // Checks if a recording is protected.
++  virtual void RecordingFile(const char* FileName) {}                         
                             // PIN PATCH
++               // The given DVB device has started recording to FileName. 
FileName is the name of the
++               // recording directory
++  virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {}           
                             // PIN PATCH
++               // The given timer is created
++  virtual bool PluginProtected(cPlugin* Plugin, int menuView = false)  { 
return false; }                   // PIN PATCH
++               // Checks if a plugin is protected.
++  virtual void UserAction(const eKeys key) {}                                 
                             // PIN PATCH
++               // report user action
++  virtual bool MenuItemProtected(const char* Name, int menuView = false)  { 
return false; }                // PIN PATCH
++
+ public:
+   cStatus(void);
+   virtual ~cStatus();
+@@ -122,6 +139,14 @@
+   static void MsgOsdTextItem(const char *Text,  bool Scroll = false);
+   static void MsgOsdChannel(const char *Text);
+   static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, 
const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, 
const char *FollowingSubtitle);
++  static bool MsgChannelProtected(const cDevice* Device, const cChannel* 
Channel);                 // PIN PATCH
++  static bool MsgReplayProtected(const cRecording* Recording, const char* 
Name,
++                                 const char* Base, bool isDirectory, int 
menuView = false);        // PIN PATCH
++  static void MsgRecordingFile(const char* FileName);                         
                     // PIN PATCH
++  static void MsgTimerCreation(cTimer* Timer, const cEvent *Event);           
                     // PIN PATCH
++  static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false);      
                     // PIN PATCH
++  static void MsgUserAction(const eKeys key);                                 
                     // PIN PATCH
++  static bool MsgMenuItemProtected(const char* Name, int menuView = false);   
                     // PIN PATCH
+   };
+ 
+ #endif //__STATUS_H
+diff -Naur a/timers.c b/timers.c
+--- a/timers.c 2022-02-02 10:56:43.000000000 +0100
++++ b/timers.c 2022-06-20 08:14:07.898392829 +0200
+@@ -81,6 +81,7 @@
+      stop -= 2400;
+   priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+   lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++  fskProtection = 0;                                        // PIN PATCH
+   if (Instant && channel)
+      snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", 
*Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
+ }
+@@ -212,11 +213,13 @@
+      stop -= 2400;
+   priority = PatternTimer ? PatternTimer->Priority() : Setup.DefaultPriority;
+   lifetime = PatternTimer ? PatternTimer->Lifetime() : Setup.DefaultLifetime;
++  fskProtection = 0;                                        // PIN PATCH
+   if (!FileName)
+      FileName = Event->Title();
+   if (!isempty(FileName))
+      Utf8Strn0Cpy(file, FileName, sizeof(file));
+   SetEvent(Event);
++  cStatus::MsgTimerCreation(this, Event);                   // PIN PATCH
+ }
+ 
+ cTimer::cTimer(const cTimer &Timer)
+@@ -255,6 +258,7 @@
+      stop         = Timer.stop;
+      priority     = Timer.priority;
+      lifetime     = Timer.lifetime;
++     fskProtection = Timer.fskProtection;    // PIN PATCH
+      strncpy(pattern, Timer.pattern, sizeof(pattern));
+      strncpy(file, Timer.file, sizeof(file));
+      free(aux);
+@@ -484,6 +488,7 @@
+         result = false;
+         }
+      }
++  fskProtection = aux && strstr(aux, 
"<pin-plugin><protected>yes</protected></pin-plugin>");  // PIN PATCH
+   free(channelbuffer);
+   free(daybuffer);
+   free(filebuffer);
+@@ -1037,6 +1042,36 @@
+   Matches(); // refresh start and end time
+ }
+ 
++void cTimer::SetFskProtection(int aFlag)      // PIN PATCH
++{
++   char* p;
++   char* tmp = 0;
++   int res = 0;
++
++   fskProtection = aFlag;
++
++   if (fskProtection && (!aux || !strstr(aux, 
"<pin-plugin><protected>yes</protected></pin-plugin>")))
++   {
++      // add protection info to aux
++
++      if (aux) { tmp = strdup(aux); free(aux); }
++      res = asprintf(&aux, 
"%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++   }
++   else if (!fskProtection && aux && (p = strstr(aux, 
"<pin-plugin><protected>yes</protected></pin-plugin>")))
++   {
++      // remove protection info from aux
++
++      res = asprintf(&tmp, "%.*s%s", (int)(p-aux), aux, 
p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++      free(aux);
++      aux = strdup(tmp);
++   }
++
++   if (res < 0) ; // memory problems :o
++
++   if (tmp)
++      free(tmp);
++}
++
+ // --- cTimers ---------------------------------------------------------------
+ 
+ cTimers cTimers::timers;
+diff -Naur a/timers.h b/timers.h
+--- a/timers.h 2022-02-02 10:56:43.000000000 +0100
++++ b/timers.h 2022-06-20 08:08:11.350956230 +0200
+@@ -45,6 +45,7 @@
+   int start;          ///< the start and stop time of this timer as given by 
the user,
+   int stop;           ///< in the form hhmm, with hh (00..23) and mm (00..59) 
added as hh*100+mm
+   int priority;
++  int fskProtection;                                               // PIN 
PATCH
+   int lifetime;
+   mutable char pattern[NAME_MAX * 2 + 1]; // same size as 'file', to be able 
to initially fill 'pattern' with 'file' in the 'Edit timer' menu
+   mutable char file[NAME_MAX * 2 + 1]; // *2 to be able to hold 'title' and 
'episode', which can each be up to 255 characters long
+@@ -70,6 +71,7 @@
+   int Start(void) const { return start; }
+   int Stop(void) const { return stop; }
+   int Priority(void) const { return priority; }
++  int FskProtection(void) const { return fskProtection; }          // PIN 
PATCH
+   int Lifetime(void) const { return lifetime; }
+   const char *Pattern(void) const { return pattern; }
+   const char *File(void) const { return file; }
+@@ -120,6 +122,7 @@
+   void SetRemote(const char *Remote);
+   void SetDeferred(int Seconds);
+   void SetFlags(uint Flags);
++  void SetFskProtection(int aFlag);                                // PIN 
PATCH
+   void ClrFlags(uint Flags);
+   void InvFlags(uint Flags);
+   bool HasFlags(uint Flags) const;
+diff -Naur a/vdr.c b/vdr.c
+--- a/vdr.c    2022-02-02 10:56:43.000000000 +0100
++++ b/vdr.c    2022-06-20 08:08:11.350956230 +0200
+@@ -71,6 +71,7 @@
+ #include "tools.h"
+ #include "transfer.h"
+ #include "videodir.h"
++#include "status.h"     // PIN PATCH
+ 
+ #define MINCHANNELWAIT        10 // seconds to wait between failed channel 
switchings
+ #define ACTIVITYTIMEOUT       60 // seconds before starting housekeeping
+@@ -1210,6 +1211,7 @@
+         if (!Menu)
+            Interact = Control = cControl::Control(ControlMutexLock);
+         if (ISREALKEY(key)) {
++           cStatus::MsgUserAction(key);           // PIN PATCH
+            EITScanner.Activity();
+            // Cancel shutdown countdown:
+            if (ShutdownHandler.countdown)
+@@ -1282,10 +1284,12 @@
+                      Control->Hide();
+                   cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+                   if (plugin) {
++                     if (!cStatus::MsgPluginProtected(plugin)) {  // PIN PATCH
+                      Menu = plugin->MainMenuAction();
+                      if (Menu)
+                         Menu->Show();
+                      }
++                  }
+                   else
+                      esyslog("ERROR: unknown plugin '%s'", PluginName);
+                   }
+@@ -1505,9 +1509,11 @@
+              case kPlay:
+                   if (cReplayControl::LastReplayed()) {
+                      Control = NULL;
++                     if (cStatus::MsgReplayProtected(0, 
cReplayControl::LastReplayed(), 0, false) == false) {  // PIN PATCH
+                      cControl::Shutdown();
+                      cControl::Launch(new cReplayControl);
+                      }
++                  }
+                   else
+                      DirectMainFunction(osRecordings); // no last viewed 
recording, so enter the Recordings menu
+                   break;

diff --git a/media-video/vdr/vdr-2.6.1.ebuild b/media-video/vdr/vdr-2.6.1.ebuild
new file mode 100644
index 000000000000..4300433dcc75
--- /dev/null
+++ b/media-video/vdr/vdr-2.6.1.ebuild
@@ -0,0 +1,197 @@
+# Copyright 2021-2022 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+inherit flag-o-matic strip-linguas toolchain-funcs user-info
+
+DESCRIPTION="Video Disk Recorder - turns a pc into a powerful set top box for 
DVB"
+HOMEPAGE="http://www.tvdr.de/";
+SRC_URI="http://git.tvdr.de/?p=vdr.git;a=snapshot;h=refs/tags/${PV};sf=tbz2 -> 
${P}.tbz2
+       menuorg? ( 
https://github.com/vdr-projects/vdr-plugin-menuorg/raw/master/vdr-patch/vdr-menuorg-2.3.x.diff
 )
+       ttxtsubs? ( 
https://md11.it.cx/download/${PN}/${PN}-2.6.1_ttxtsubs_v2.patch )"
+
+LICENSE="GPL-2+"
+SLOT="0"
+KEYWORDS="~amd64 ~arm ~arm64 ~ppc ~x86"
+IUSE="bidi debug demoplugins html keyboard mainmenuhooks menuorg naludump 
permashift pinplugin systemd ttxtsubs verbose"
+
+COMMON_DEPEND="
+       media-libs/fontconfig
+       media-libs/freetype
+       media-libs/libjpeg-turbo
+       sys-libs/libcap"
+DEPEND="${COMMON_DEPEND}
+       >=virtual/linuxtv-dvb-headers-5.3"
+RDEPEND="${COMMON_DEPEND}
+       dev-lang/perl
+       media-tv/gentoo-vdr-scripts
+       media-fonts/corefonts
+       bidi? ( dev-libs/fribidi )
+       systemd? ( sys-apps/systemd )"
+BDEPEND="
+       acct-user/vdr
+       sys-devel/gettext"
+
+REQUIRED_USE="permashift? ( !naludump !pinplugin )"
+
+CONF_DIR="/etc/vdr"
+CAP_FILE="${S}/capabilities.sh"
+CAPS="# Capabilities of the vdr-executable for use by startscript etc."
+
+pkg_setup() {
+       use debug && append-flags -g
+
+       PLUGIN_LIBDIR="/usr/$(get_libdir)/vdr/plugins"
+       VIDEO_DIR="$(egethome vdr)/video"
+
+       tc-export CC CXX AR
+}
+
+add_cap() {
+       local arg
+       for arg; do
+               CAPS="${CAPS}\n${arg}=1"
+       done
+}
+
+lang_po() {
+       LING_PO=$( ls ${S}/po | sed -e "s:.po::g" | cut -d_ -f1 | tr \\\012 ' ' 
)
+}
+
+src_prepare() {
+       # apply maintenance-patches
+       ebegin "Changing paths for gentoo"
+
+       local DVBDIR=/usr/include
+       local i
+       for i in ${DVB_HEADER_PATH} /usr/include/v4l-dvb-hg /usr/include; do
+               [[ -d ${i} ]] || continue
+               if [[ -f ${i}/linux/dvb/dmx.h ]]; then
+                       einfo "Found DVB header files in ${i}"
+                       DVBDIR=${i}
+                       break
+               fi
+       done
+
+       # checking for s2api headers
+       local api_version
+       api_version=$(awk -F' ' '/define DVB_API_VERSION / {print $3}' 
"${DVBDIR}"/linux/dvb/version.h)
+       api_version=${api_version}*$(awk -F' ' '/define DVB_API_VERSION_MINOR / 
{print $3}' "${DVBDIR}"/linux/dvb/version.h)
+
+       if [[ ${api_version:-0} -lt 5*3 ]]; then
+               eerror "DVB header files do not contain s2api support or too 
old for ${P}"
+               eerror "You cannot compile VDR against old dvb-header"
+               die "DVB headers too old"
+       fi
+
+       cat > Make.config <<-EOT || die "cannot write to Make.config"
+               #
+               # Generated by ebuild ${PF}
+               #
+               PREFIX                  = /usr
+               DVBDIR                  = ${DVBDIR}
+               PLUGINLIBDIR    = ${PLUGIN_LIBDIR}
+               CONFDIR                 = ${CONF_DIR}
+               ARGSDIR                 = \$(CONFDIR)/conf.d
+               VIDEODIR                = ${VIDEO_DIR}
+               LOCDIR                  = \$(PREFIX)/share/locale
+               INCDIR                  = \$(PREFIX)/include
+
+               DEFINES                 += -DCONFDIR=\"\$(CONFDIR)\"
+               INCLUDES                += -I\$(DVBDIR)
+
+               # >=vdr-1.7.36-r1; parameter only used for compiletime on vdr
+               # PLUGINLIBDIR (plugin Makefile old) = LIBDIR (plugin Makefile 
new)
+               LIBDIR                  = ${PLUGIN_LIBDIR}
+               PCDIR                   = /usr/$(get_libdir)/pkgconfig
+
+       EOT
+       eend 0
+
+       eapply "${FILESDIR}/${PN}-2.4.6_gentoo.patch"
+       use demoplugins || eapply "${FILESDIR}/vdr-2.4_remove_plugins.patch"
+       eapply "${FILESDIR}/${PN}-2.4.6_makefile-variables.patch"
+
+       # fix clang/LLVM compile
+       eapply "${FILESDIR}/${PN}-2.4.6_clang.patch"
+
+       use naludump && eapply "${FILESDIR}/${P}_naludump.patch"
+       use permashift && eapply "${FILESDIR}/${P}-patch-for-permashift.patch"
+       use pinplugin && eapply "${FILESDIR}/${P}_pinplugin.patch"
+       use ttxtsubs && eapply "${DISTDIR}/${P}_ttxtsubs_v2.patch"
+       use menuorg && eapply "${DISTDIR}/vdr-menuorg-2.3.x.diff"
+       use mainmenuhooks && eapply 
"${FILESDIR}/${PN}-2.4.1_mainmenuhook-1.0.1.patch"
+
+       add_cap CAP_UTF8 \
+               CAP_IRCTRL_RUNTIME_PARAM \
+               CAP_VFAT_RUNTIME_PARAM \
+               CAP_CHUID \
+               CAP_SHUTDOWN_AUTO_RETRY
+
+       echo -e ${CAPS} > "${CAP_FILE}" || die "cannot write to CAP_FILE"
+
+       # LINGUAS support
+       einfo "\n \t VDR supports the LINGUAS values"
+
+       lang_po
+
+       einfo "\t Please set one of this values in your sytem make.conf"
+       einfo "\t LINGUAS=\"${LING_PO}\"\n"
+
+       if [[ -z ${LINGUAS} ]]; then
+               einfo "\n \t No values in LINGUAS="
+               einfo "\t You will get only english text on OSD \n"
+       fi
+
+       strip-linguas ${LING_PO} en
+
+       default
+}
+
+src_configure() {
+       # support languages, written from right to left
+       export "BIDI=$(usex bidi 1 0)"
+       # systemd notification support
+       export "SDNOTIFY=$(usex systemd 1 0)"
+       # with/without keyboard
+       export "USE_KBD=$(usex keyboard 1 0)"
+       # detailed compile output for debug
+       export "VERBOSE=$(usex verbose 1 0)"
+}
+
+src_install() {
+       # trick the makefile to not create a VIDEODIR by supplying it with an
+       # existing directory
+       emake VIDEODIR="/" DESTDIR="${ED}" install
+
+       keepdir "${PLUGIN_LIBDIR}"
+
+       # backup for plugins they don't be able to create this dir
+       keepdir "${CONF_DIR}/plugins"
+
+       if use html; then
+               local HTML_DOCS=( *.html )
+       fi
+       local DOCS=( MANUAL INSTALL README* HISTORY CONTRIBUTORS UPDATE-2* )
+       einstalldocs
+
+       insinto /usr/share/vdr
+       doins "${CAP_FILE}"
+
+       fowners vdr:vdr "${CONF_DIR}" -R
+}
+
+pkg_postinst() {
+       elog "Please read the /usr/share/doc/${PF}/UPDATE-2.4"
+       elog "for major changes in this version\n"
+
+       elog "It is a good idea to run vdrplugin-rebuild now.\n"
+
+       elog "To get nice symbols in OSD we recommend to install"
+       elog "\t1. emerge media-fonts/vdrsymbols-ttf"
+       elog "\t2. select font VDRSymbolsSans in Setup\n"
+
+       elog "To get an idea how to proceed now, have a look at our vdr-guide:"
+       elog "\thttps://wiki.gentoo.org/wiki/VDR";
+}

Reply via email to