#! /bin/sh /usr/share/dpatch/dpatch-run ## opt-24_jumpplay.dpatch by Torsten Kunkel , Thomas Günther ## http://toms-cafe.de/vdr/download/vdr-jumpplay-1.0-1.7.6.diff ## ## All lines beginning with `## DP:' are a description of the patch. ## DP: Play after jump to next mark. Automatically jump over commercial breaks. ## DP: See README.jumpplay and MANUAL for details. @DPATCH@ diff -Naurp vdr-1.7.6/MANUAL vdr-1.7.6-jumpplay/MANUAL --- vdr-1.7.6/MANUAL 2008-02-24 10:09:17.000000000 +0000 +++ vdr-1.7.6-jumpplay/MANUAL 2009-04-27 15:49:00.000000000 +0000 @@ -813,6 +813,30 @@ Version 1.6 0 resulting in a file named 'resume.vdr', and any other value resulting in 'resume.n.vdr'. + Jump&Play = no Turns playing on or off after jumping forward to the + next editing mark with the '9' key. + + Play&Jump = no Turns automatic jumping over commercial breaks on or + off. This includes jumping to the first mark, if the + replay starts at the beginning of a recording - and + stopping the replay at the last mark. + With this setting enabled, the behaviour of the '8' + key during replay is changed too. It moves the actual + replay position not only three seconds before the + next "start" mark, but also before the next "end" + mark. This can be used to test, if the editing marks + are correctly positioned for a "smooth" jump over a + commercial break. + + Pause at last mark = no + Turns pausing of replay at the last editing mark on or + off. + + Reload marks = no Turns reloading of editing marks on or off. This can + be used if an external programme adjusts the editing + marks, e.g. noad in online mode. The marks are reloaded + in 10 seconds intervals. + Miscellaneous: Min. event timeout = 30 diff -Naurp vdr-1.7.6/README.jumpplay vdr-1.7.6-jumpplay/README.jumpplay --- vdr-1.7.6/README.jumpplay 1970-01-01 00:00:00.000000000 +0000 +++ vdr-1.7.6-jumpplay/README.jumpplay 2009-04-27 15:49:00.000000000 +0000 @@ -0,0 +1,92 @@ +JumpPlay patch for VDR +---------------------- + +This patch changes the replay behaviour for recordings that contain editing +marks. It allows to immediately continue the replay after jumping forward to +the next mark, and to automatically jump over the commercial break to the next +"start" mark, if an "end" mark is reached. + +The features of this patch can be turned on or off with parameters in the replay +setup. See MANUAL for description of this parameters: "Jump&Play", "Play&Jump", +"Pause at last mark" and "Reload marks". + + +* History + + 2003-07-04: jumpandrun.diff - the Noad + Jump&Play + + 2003-12-06: Version 0.0 - Torsten Kunkel + Play&Jump (only if progressbar is visible) + Setup parameters Jump&Play and Play&Jump in the replay setup + + 2004-01-20: Version 0.1 - Thomas Günther + Jump&Play: + - fixed speed after jump + - fixed removing of marks + Play&Jump: + - jump only on "end" marks + + 2004-01-27: Version 0.2 - Thomas Günther + Jump&Play: + - fixed double jump + Play&Jump: + - fixed mark detection: fuzzy detection (until 3 seconds after mark) + - jump without progressbar + - mode "progressbar only" for old behaviour + + 2004-01-31: Version 0.3 - Thomas Günther + Jump&Play: + - fixed display frames + Play&Jump: + - fixed end of playing at last mark + + 2004-07-11: Version 0.4 - Thomas Günther + Jump&Play: + - don't play after jump to end + Play&Jump: + - don't prevent jumping after hide or show + Less conflicts with other patches (Elchi/AutoPID) + + 2004-08-21: Version 0.5 - Thomas Günther + Play&Jump: + - exact jumps, replay like edited recording (no fuzzy mark detection) + - jump to first mark if replay starts at the beginning + - check jump marks with '8' key + - mode "progressbar only" removed + Description in README.jumpplay + + 2004-12-28: Version 0.6 - Thomas Günther + Adapted noad extensions (from the Noad ) to + jumpplay-0.5: + - cyclic reloading of marks found by noad online-scan + - don't stop after the last mark in case of live-recordings + New setup parameter "Load marks interval (s)" + Updated description in README.jumpplay + + 2006-04-14: Version 0.7 - Thomas Günther + Fixed jump to first mark (crashed with plugin extrecmenu-0.9) + Added version define JUMPPLAYVERSNUM + Added placeholders for Czech language texts + Cleaned up i18n entries (support only VDR >= 1.3.29) + Improved description of i18n placeholders - hoping for real language texts + + 2006-05-12: Version 0.8 - Thomas Günther + Fixed segfault in dvbplayer thread while the replaycontrol thread is + reloading the marks (thanks to horchi at vdrportal.de for reporting this - + see http://vdrportal.de/board/thread.php?postid=450463#post450463): + New class cMarksReload checks the timestamp of marks.vdr in 10 seconds + intervals, so the marks in the threads dvbplayer and replaycontrol can be + reloaded independently + Changed setup parameter "Load marks interval (s)" to "Reload marks" + Updated description in README.jumpplay + + 2006-05-28: Version 0.9 - Thomas Günther + New setup parameter "Pause at last mark" + Updated description in README.jumpplay + Moved parameters description to MANUAL + + 2009-03-31: Version 1.0 - Thomas Günther + Play&Jump: + - set resume position to 0 if replay stops at the first mark + Added French language texts (thanks to Michaël Nival) diff -Naurp vdr-1.7.6/config.c vdr-1.7.6-jumpplay/config.c --- vdr-1.7.6/config.c 2009-01-24 15:05:32.000000000 +0000 +++ vdr-1.7.6-jumpplay/config.c 2009-04-27 15:49:00.000000000 +0000 @@ -283,6 +283,10 @@ cSetup::cSetup(void) MultiSpeedMode = 0; ShowReplayMode = 0; ResumeID = 0; + JumpPlay = 0; + PlayJump = 0; + PauseLastMark = 0; + ReloadMarks = 0; CurrentChannel = -1; CurrentVolume = MAXVOLUME; CurrentDolby = 0; @@ -456,6 +460,10 @@ bool cSetup::Parse(const char *Name, con else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value); else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value); else if (!strcasecmp(Name, "ResumeID")) ResumeID = atoi(Value); + else if (!strcasecmp(Name, "JumpPlay")) JumpPlay = atoi(Value); + else if (!strcasecmp(Name, "PlayJump")) PlayJump = atoi(Value); + else if (!strcasecmp(Name, "PauseLastMark")) PauseLastMark = atoi(Value); + else if (!strcasecmp(Name, "ReloadMarks")) ReloadMarks = atoi(Value); else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value); else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value); else if (!strcasecmp(Name, "CurrentDolby")) CurrentDolby = atoi(Value); @@ -539,6 +547,10 @@ bool cSetup::Save(void) Store("MultiSpeedMode", MultiSpeedMode); Store("ShowReplayMode", ShowReplayMode); Store("ResumeID", ResumeID); + Store("JumpPlay", JumpPlay); + Store("PlayJump", PlayJump); + Store("PauseLastMark", PauseLastMark); + Store("ReloadMarks", ReloadMarks); Store("CurrentChannel", CurrentChannel); Store("CurrentVolume", CurrentVolume); Store("CurrentDolby", CurrentDolby); diff -Naurp vdr-1.7.6/config.h vdr-1.7.6-jumpplay/config.h --- vdr-1.7.6/config.h 2009-04-12 14:20:52.000000000 +0000 +++ vdr-1.7.6-jumpplay/config.h 2009-04-27 15:49:00.000000000 +0000 @@ -36,6 +36,8 @@ // plugins to work with newer versions of the core VDR as long as no // VDR header files have changed. +#define JUMPPLAYVERSNUM 100 + #define MAXPRIORITY 99 #define MAXLIFETIME 99 @@ -260,6 +262,10 @@ public: int MultiSpeedMode; int ShowReplayMode; int ResumeID; + int JumpPlay; + int PlayJump; + int PauseLastMark; + int ReloadMarks; int CurrentChannel; int CurrentVolume; int CurrentDolby; diff -Naurp vdr-1.7.6/dvbplayer.c vdr-1.7.6-jumpplay/dvbplayer.c --- vdr-1.7.6/dvbplayer.c 2009-04-19 15:19:10.000000000 +0000 +++ vdr-1.7.6-jumpplay/dvbplayer.c 2009-04-27 17:33:20.000000000 +0000 @@ -204,6 +204,7 @@ private: cNonBlockingFileReader *nonBlockingFileReader; cRingBufferFrame *ringBuffer; cPtsIndex ptsIndex; + cMarksReload marks; cFileName *fileName; cIndexFile *index; cUnbufferedFile *replayFile; @@ -249,7 +250,7 @@ public: int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 }; cDvbPlayer::cDvbPlayer(const char *FileName) -:cThread("dvbplayer") +:cThread("dvbplayer"), marks(FileName) { nonBlockingFileReader = NULL; ringBuffer = NULL; @@ -357,6 +358,10 @@ bool cDvbPlayer::Save(void) if (index) { int Index = ptsIndex.FindIndex(DeviceGetSTC()); if (Index >= 0) { + // set resume position to 0 if replay stops at the first mark + if (Setup.PlayJump && marks.First() && + abs(Index - marks.First()->position) <= int(round(RESUMEBACKUP * framesPerSecond))) + Index = 0; Index -= int(round(RESUMEBACKUP * framesPerSecond)); if (Index > 0) Index = index->GetNextIFrame(Index, false); @@ -384,11 +389,26 @@ void cDvbPlayer::Action(void) uchar *b = NULL; uchar *p = NULL; int pc = 0; + bool cutIn = false; + int total = -1; readIndex = Resume(); if (readIndex >= 0) isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond)); + if (Setup.PlayJump && readIndex <= 0 && marks.First() && index) { + int Index = marks.First()->position; + uint16_t FileNumber; + off_t FileOffset; + if (index->Get(Index, &FileNumber, &FileOffset) && + NextFile(FileNumber, FileOffset)) { + isyslog("PlayJump: start replay at first mark %d (%s)", + Index, *IndexToHMSF(Index, true, framesPerSecond)); + readIndex = Index; + } + } + + bool LastMarkPause = false; nonBlockingFileReader = new cNonBlockingFileReader; int Length = 0; bool Sleep = false; @@ -411,7 +431,7 @@ void cDvbPlayer::Action(void) // Read the next frame from the file: - if (playMode != pmStill && playMode != pmPause) { + if (playMode != pmStill && playMode != pmPause && !LastMarkPause) { if (!readFrame && (replayFile || readIndex >= 0)) { if (!nonBlockingFileReader->Reading()) { if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) { @@ -448,6 +468,44 @@ void cDvbPlayer::Action(void) else if (index) { uint16_t FileNumber; off_t FileOffset; + if (Setup.PlayJump || Setup.PauseLastMark) { + // check for end mark - jump to next mark or pause + readIndex++; + marks.Reload(); + cMark *m = marks.Get(readIndex); + if (m && (m->Index() & 0x01) != 0) { + m = marks.Next(m); + int Index; + if (m) + Index = m->position; + else if (Setup.PauseLastMark) { + // pause at last mark + isyslog("PauseLastMark: pause at position %d (%s)", + readIndex, *IndexToHMSF(readIndex, true, framesPerSecond)); + LastMarkPause = true; + Index = -1; + } + else if (total == index->Last()) + // at last mark jump to end of recording + Index = index->Last() - 1; + else + // jump but stay off end of live-recordings + Index = index->GetNextIFrame(index->Last() - int(round(MAXSTUCKATEOF * framesPerSecond)), true); + // don't jump in edited recordings + if (Setup.PlayJump && Index > readIndex && + Index > index->GetNextIFrame(readIndex, true)) { + isyslog("PlayJump: %d frames to %d (%s)", + Index - readIndex, Index, + *IndexToHMSF(Index, true, framesPerSecond)); + readIndex = Index; + cutIn = true; + } + } + readIndex--; + } + // for detecting growing length of live-recordings + if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent) && readIndependent) + total = index->Last(); if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset)) readIndex++; else @@ -489,6 +547,13 @@ void cDvbPlayer::Action(void) // Store the frame in the buffer: if (readFrame) { + if (cutIn) { + if (isPesRecording) + cRemux::SetBrokenLink(readFrame->Data(), readFrame->Count()); + else + TsSetTeiOnBrokenPackets(readFrame->Data(), readFrame->Count()); + cutIn = false; + } if (ringBuffer->Put(readFrame)) readFrame = NULL; else @@ -548,8 +613,13 @@ void cDvbPlayer::Action(void) p = NULL; } } - else + else { + if (LastMarkPause) { + LastMarkPause = false; + playMode = pmPause; + } Sleep = true; + } // Handle hitting begin/end of recording: diff -Naurp vdr-1.7.6/menu.c vdr-1.7.6-jumpplay/menu.c --- vdr-1.7.6/menu.c 2009-01-24 15:05:43.000000000 +0000 +++ vdr-1.7.6-jumpplay/menu.c 2009-04-27 15:49:00.000000000 +0000 @@ -2717,6 +2717,10 @@ cMenuSetupReplay::cMenuSetupReplay(void) Add(new cMenuEditBoolItem(tr("Setup.Replay$Multi speed mode"), &data.MultiSpeedMode)); Add(new cMenuEditBoolItem(tr("Setup.Replay$Show replay mode"), &data.ShowReplayMode)); Add(new cMenuEditIntItem(tr("Setup.Replay$Resume ID"), &data.ResumeID, 0, 99)); + Add(new cMenuEditBoolItem(tr("Setup.Replay$Jump&Play"), &data.JumpPlay)); + Add(new cMenuEditBoolItem(tr("Setup.Replay$Play&Jump"), &data.PlayJump)); + Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause at last mark"), &data.PauseLastMark)); + Add(new cMenuEditBoolItem(tr("Setup.Replay$Reload marks"), &data.ReloadMarks)); } void cMenuSetupReplay::Store(void) @@ -4008,7 +4012,7 @@ char *cReplayControl::fileName = NULL; char *cReplayControl::title = NULL; cReplayControl::cReplayControl(void) -:cDvbPlayerControl(fileName) +:cDvbPlayerControl(fileName), marks(fileName) { currentReplayControl = this; displayReplay = NULL; @@ -4020,7 +4024,6 @@ cReplayControl::cReplayControl(void) timeSearchActive = false; cRecording Recording(fileName); cStatus::MsgReplaying(this, Recording.Name(), Recording.FileName(), true); - marks.Load(fileName, Recording.FramesPerSecond(), Recording.IsPesRecording()); SetTrackDescriptions(false); } @@ -4248,8 +4251,10 @@ void cReplayControl::MarkToggle(void) ShowTimed(2); bool Play, Forward; int Speed; - if (GetReplayMode(Play, Forward, Speed) && !Play) + if (GetReplayMode(Play, Forward, Speed) && !Play) { Goto(Current, true); + displayFrames = true; + } } marks.Save(); } @@ -4262,8 +4267,17 @@ void cReplayControl::MarkJump(bool Forwa if (GetIndex(Current, Total)) { cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current); if (m) { - Goto(m->position, true); - displayFrames = true; + bool Play2, Forward2; + int Speed; + if (Setup.JumpPlay && GetReplayMode(Play2, Forward2, Speed) && + Play2 && Forward && m->position < Total - SecondsToFrames(3, FramesPerSecond())) { + Goto(m->position); + Play(); + } + else { + Goto(m->position, true); + displayFrames = true; + } } } } @@ -4318,7 +4332,7 @@ void cReplayControl::EditTest(void) if (!m) m = marks.GetNext(Current); if (m) { - if ((m->Index() & 0x01) != 0) + if ((m->Index() & 0x01) != 0 && !Setup.PlayJump) m = marks.Next(m); if (m) { Goto(m->position - SecondsToFrames(3, FramesPerSecond())); @@ -4340,6 +4354,7 @@ eOSState cReplayControl::ProcessKey(eKey { if (!Active()) return osEnd; + marks.Reload(); if (visible) { if (timeoutShow && time(NULL) > timeoutShow) { Hide(); diff -Naurp vdr-1.7.6/menu.h vdr-1.7.6-jumpplay/menu.h --- vdr-1.7.6/menu.h 2008-02-10 16:01:53.000000000 +0000 +++ vdr-1.7.6-jumpplay/menu.h 2009-04-27 15:49:00.000000000 +0000 @@ -212,7 +212,7 @@ public: class cReplayControl : public cDvbPlayerControl { private: cSkinDisplayReplay *displayReplay; - cMarks marks; + cMarksReload marks; bool visible, modeOnly, shown, displayFrames; int lastCurrent, lastTotal; bool lastPlay, lastForward; diff -Naurp vdr-1.7.6/po/de_DE.po vdr-1.7.6-jumpplay/po/de_DE.po --- vdr-1.7.6/po/de_DE.po 2009-04-18 15:24:48.000000000 +0000 +++ vdr-1.7.6-jumpplay/po/de_DE.po 2009-04-27 15:49:00.000000000 +0000 @@ -755,6 +755,18 @@ msgstr "Editierte Dateien aufteilen" msgid "Replay" msgstr "Wiedergabe" +msgid "Setup.Replay$Jump&Play" +msgstr "Wiedergabe nach Sprung" + +msgid "Setup.Replay$Play&Jump" +msgstr "Sprung bei Schnittmarke" + +msgid "Setup.Replay$Pause at last mark" +msgstr "Pause bei letzter Marke" + +msgid "Setup.Replay$Reload marks" +msgstr "Marken aktualisieren" + msgid "Setup.Replay$Multi speed mode" msgstr "Mehrstufiger Vor-/Rücklauf" diff -Naurp vdr-1.7.6/po/fr_FR.po vdr-1.7.6-jumpplay/po/fr_FR.po --- vdr-1.7.6/po/fr_FR.po 2009-04-18 15:24:48.000000000 +0000 +++ vdr-1.7.6-jumpplay/po/fr_FR.po 2009-04-27 15:49:00.000000000 +0000 @@ -761,6 +761,18 @@ msgstr "Séparer les séquences éditées" msgid "Replay" msgstr "Lecture" +msgid "Setup.Replay$Jump&Play" +msgstr "Lecture après saut" + +msgid "Setup.Replay$Play&Jump" +msgstr "Saut sur les marques de découpes" + +msgid "Setup.Replay$Pause at last mark" +msgstr "Pause après la dernière marque" + +msgid "Setup.Replay$Reload marks" +msgstr "Actualiser les marques" + msgid "Setup.Replay$Multi speed mode" msgstr "Mode multi-vitesses" diff -Naurp vdr-1.7.6/recording.c vdr-1.7.6-jumpplay/recording.c --- vdr-1.7.6/recording.c 2009-04-13 13:50:39.000000000 +0000 +++ vdr-1.7.6-jumpplay/recording.c 2009-04-27 15:49:00.000000000 +0000 @@ -1295,6 +1295,52 @@ cMark *cMarks::GetNext(int Position) return NULL; } +// --- cMarksReload ---------------------------------------------------------- + +#define MARKS_RELOAD_MS 10000 + +time_t cMarksReload::lastsavetime = 0; + +cMarksReload::cMarksReload(const char *RecordingFileName) +:recDir(RecordingFileName) +{ + struct stat sbuf; + cRecording rec(recDir); + if (Load(recDir, rec.FramesPerSecond(), rec.IsPesRecording()) && + stat(FileName(), &sbuf) == 0) + lastmodtime = sbuf.st_mtime; + else + lastmodtime = 0; + nextreload.Set(MARKS_RELOAD_MS - cTimeMs::Now() % MARKS_RELOAD_MS); +} + +bool cMarksReload::Reload(void) +{ + // Check the timestamp of marks.vdr in 10 seconds intervals + // Independent but synchronized reloading of marks in two threads + if ((Setup.ReloadMarks && nextreload.TimedOut()) || + lastsavetime > lastmodtime) { + nextreload.Set(MARKS_RELOAD_MS - cTimeMs::Now() % MARKS_RELOAD_MS); + struct stat sbuf; + if (stat(FileName(), &sbuf) == 0 && sbuf.st_mtime != lastmodtime) { + lastmodtime = sbuf.st_mtime; + cRecording rec(recDir); + if (Load(recDir, rec.FramesPerSecond(), rec.IsPesRecording())) + return true; + } + } + return false; +} + +bool cMarksReload::Save(void) +{ + bool ok = cMarks::Save(); + struct stat sbuf; + if (ok && stat(FileName(), &sbuf) == 0) + lastsavetime = lastmodtime = sbuf.st_mtime; + return ok; +} + // --- cRecordingUserCommand ------------------------------------------------- const char *cRecordingUserCommand::command = NULL; diff -Naurp vdr-1.7.6/recording.h vdr-1.7.6-jumpplay/recording.h --- vdr-1.7.6/recording.h 2009-04-19 09:00:45.000000000 +0000 +++ vdr-1.7.6-jumpplay/recording.h 2009-04-27 15:49:00.000000000 +0000 @@ -194,6 +194,18 @@ public: cMark *GetNext(int Position); }; +class cMarksReload : public cMarks { +private: + cString recDir; + cTimeMs nextreload; + time_t lastmodtime; + static time_t lastsavetime; +public: + cMarksReload(const char *RecordingFileName); + bool Reload(void); + bool Save(void); + }; + #define RUC_BEFORERECORDING "before" #define RUC_AFTERRECORDING "after" #define RUC_EDITEDRECORDING "edited"