diff options
-rw-r--r-- | HISTORY.h | 7 | ||||
-rw-r--r-- | lib/vdrlocks.h | 31 | ||||
-rw-r--r-- | status.c | 193 | ||||
-rw-r--r-- | timer.c | 23 | ||||
-rw-r--r-- | update.c | 3 | ||||
-rw-r--r-- | update.h | 15 |
6 files changed, 149 insertions, 123 deletions
@@ -5,8 +5,8 @@ * */ -#define _VERSION "1.1.68" -#define VERSION_DATE "11.06.2017" +#define _VERSION "1.1.69" +#define VERSION_DATE "22.06.2017" #define DB_API 4 @@ -19,6 +19,9 @@ /* * ------------------------------------ +2017-06-22 version 1.1.69 (horchi) + - change: More rework of lock handling + 2017-06-11: version 1.1.68 (horchi) - change: Added lock macros for easier handling the vdr versions diff --git a/lib/vdrlocks.h b/lib/vdrlocks.h index a3a1f2b..5032f09 100644 --- a/lib/vdrlocks.h +++ b/lib/vdrlocks.h @@ -31,12 +31,41 @@ # define GET_TIMERS_WRITE(name) cTimers* name = &Timers; #endif - //*************************************************************************** // Channel List Lock Macros //*************************************************************************** +#if defined (APIVERSNUM) && (APIVERSNUM >= 20301) +# define GET_CHANNELS_READ(name) USE_LIST_LOCK_READ(Channels); \ + const cChannels* name = Channels; +#else +# define GET_CHANNELS_READ(name) cChannels* name = &Channels; +#endif + +#if defined (APIVERSNUM) && (APIVERSNUM >= 20301) +# define GET_CHANNELS_WRITE(name) USE_LIST_LOCK_WRITE(Channels); \ + cChannels* name = Channels; +#else +# define GET_CHANNELS_WRITE(name) cChannels* name = &Channels; +#endif + +//*************************************************************************** +// Recording List Lock Macros +//*************************************************************************** + +#if defined (APIVERSNUM) && (APIVERSNUM >= 20301) +# define GET_RECORDINGS_READ(name) USE_LIST_LOCK_READ(Recordings); \ + const cRecordings* name = Recordings; +#else +# define GET_RECORDINGS_READ(name) cRecordings* name = &Recordings; +#endif +#if defined (APIVERSNUM) && (APIVERSNUM >= 20301) +# define GET_RECORDINGS_WRITE(name) USE_LIST_LOCK_WRITE(Recordings); \ + cRecordings* name = Recordings; +#else +# define GET_RECORDINGS_WRITE(name) cRecordings* name = &Recordings; +#endif //*************************************************************************** # endif // VDR_PLUGIN @@ -67,7 +67,7 @@ struct tIndexTs } }; -int RecLengthInSecs(const cRecording *pRecording) +int RecLengthInSecs(const cRecording* pRecording) { struct stat buf; cString fullname = cString::sprintf("%s%s", pRecording->FileName(), IsPesRecording(pRecording) ? LOC_INDEXFILESUFFIX ".vdr" : LOC_INDEXFILESUFFIX); @@ -108,105 +108,114 @@ void cUpdate::TimerChange(const cTimer* Timer, eTimerChange Change) } //*************************************************************************** -// Recording Notification +// Recording Notification (cStatus::MsgRecording(....)) //*************************************************************************** void cUpdate::Recording(const cDevice* Device, const char* Name, const char* FileName, bool On) { - cMutexLock lock(&runningRecMutex); - const int allowedBreakDuration = 2; + RecordingAction action; // Recording of 'Peter Hase' has 'started' [/srv/vdr/video.00/Peter_Hase/2014-10-08.11.05.18-0.rec] // Recording of '(null)' has 'stopped' [/srv/vdr/video.00/Peter_Hase/2014-10-08.11.05.18-0.rec] tell(1, "Recording of '%s' has '%s' [%s]", Name, On ? "started" : "stopped", FileName); - // at start of recording store event details to recording directory (info.epg2vdr) + // schedule this notification to perfrom it in oure context not in the cStatus Interface context + // due to the needed list locks! + + action.name = Name; + action.fileName = FileName; + action.cardIndex = Device->CardIndex(); + action.on = On; + pendingRecordingActions.push(action); if (On) pendingNewRecordings.push(FileName); - // get timers lock + recordingStateChangedTrigger = yes; + waitCondition.Broadcast(); // wakeup +} -#if defined (APIVERSNUM) && (APIVERSNUM >= 20301) - LOCK_TIMERS_READ; - const cTimers* timers = Timers; - // cTimersLock timersLock(false); - // const cTimers* timers = timersLock.Timers(); -#else - const cTimers* timers = &Timers; -#endif +//*************************************************************************** +// Perform Pending Recording Notification +// (got by cStatus::MsgRecording(....) above) +//*************************************************************************** - // recording started ... +int cUpdate::performRecordingActions() +{ + const int allowedBreakDuration = 2; - if (On && Name) + GET_TIMERS_READ(timers); // get timers lock + GET_RECORDINGS_READ(recordings); // recordings lock + + while (!pendingNewRecordings.empty()) { - for (const cTimer* ti = timers->First(); ti; ti = timers->Next(ti)) + cMutexLock lock(&runningRecMutex); + RecordingAction action = pendingRecordingActions.front(); + pendingRecordingActions.pop(); + + if (action.on && action.name.length()) // recording started ... { - if (ti->Recording()) // timer nimmt gerade auf + for (const cTimer* ti = timers->First(); ti; ti = timers->Next(ti)) { - cRunningRecording* recording = 0; + if (ti->Recording()) // timer nimmt gerade auf + { + cRunningRecording* recording = 0; - // check if already known + // check if already known - for (cRunningRecording* rr = runningRecordings.First(); rr; rr = runningRecordings.Next(rr)) - { - if (rr->timer == ti) + for (cRunningRecording* rr = runningRecordings.First(); rr; rr = runningRecordings.Next(rr)) { - recording = rr; - break; + if (rr->timer == ti) + { + recording = rr; + break; + } } - } - if (recording) // already handled -> a resume?! - { - tell(1, "Info: Detected resume of '%s' on device %d", Name, Device->CardIndex()); - continue; - } + if (recording) // already handled -> a resume?! + { + tell(1, "Info: Detected resume of '%s' on device %d", action.name.c_str(), action.cardIndex); + continue; + } - int doneid = na; - contentOfTag(ti, "doneid", doneid); + int doneid = na; + contentOfTag(ti, "doneid", doneid); - recording = new cRunningRecording(ti, doneid); - runningRecordings.Add(recording); - tell(1, "Info: Recording '%s' with doneid %d added to running list", Name, doneid); + recording = new cRunningRecording(ti, doneid); + runningRecordings.Add(recording); + tell(1, "Info: Recording '%s' with doneid %d added to running list", action.name.c_str(), doneid); + } } } - } - - // recording stopped ... - if (!On) - { - // loop over running recordings .. + // recording stopped ... - for (cRunningRecording* rr = runningRecordings.First(); rr; rr = runningRecordings.Next(rr)) + if (!action.on) { - const cTimer* pendingTimer = 0; - int complete; - double recFraction = 100.0; - long timerLengthSecs = rr->timer->StopTime() - rr->timer->StartTime(); - bool vpsUsed = rr->timer->HasFlags(tfVps) && rr->timer->Event() && rr->timer->Event()->Vps(); - - // check if timer still exists + // loop over running recordings .. - for (pendingTimer = timers->First(); pendingTimer; pendingTimer = timers->Next(pendingTimer)) + for (cRunningRecording* rr = runningRecordings.First(); rr; rr = runningRecordings.Next(rr)) { - if (pendingTimer == rr->timer) - break; - } + const cRecording* pRecording = recordings->GetByName(action.fileName.c_str()); + const cTimer* pendingTimer = 0; + int complete; + double recFraction = 100.0; + long timerLengthSecs = rr->timer->StopTime() - rr->timer->StartTime(); + bool vpsUsed = rr->timer->HasFlags(tfVps) && rr->timer->Event() && rr->timer->Event()->Vps(); - // still recording :o ? + // check if timer still exists - if (pendingTimer && pendingTimer->Recording()) - continue; + for (pendingTimer = timers->First(); pendingTimer; pendingTimer = timers->Next(pendingTimer)) + { + if (pendingTimer == rr->timer) + break; + } -#if defined (APIVERSNUM) && (APIVERSNUM >= 20301) - cStateKey stateKey; + // still recording :o ? - if (const cRecordings* recordings = cRecordings::GetRecordingsRead(stateKey)) - { - const cRecording* pRecording = recordings->GetByName(FileName); + if (pendingTimer && pendingTimer->Recording()) + continue; if (pRecording && timerLengthSecs) { @@ -214,54 +223,42 @@ void cUpdate::Recording(const cDevice* Device, const char* Name, const char* Fil recFraction = double(recLen) * 100 / timerLengthSecs; } - stateKey.Remove(); - } -#else - const cRecording* pRecording = Recordings.GetByName(FileName); - - if (pRecording && timerLengthSecs) - { - int recLen = RecLengthInSecs(pRecording); - recFraction = double(recLen) * 100 / timerLengthSecs; - } -#endif - - // assure timer has reached it's end or at least 90% (vps) / 98% were recorded + // assure timer has reached it's end or at least 90% (vps) / 98% were recorded - complete = recFraction >= (vpsUsed ? 90 : 98); + complete = recFraction >= (vpsUsed ? 90 : 98); - if (complete) - tell(1, "Info: Finished: '%s'; recorded %d%%; VPS %s", - rr->timer->File(), (int)round(recFraction), vpsUsed ? "Yes": "No"); - else - tell(1, "Info: Finished: '%s' (not complete! - recorded only %d%%); VPS %s", - rr->timer->File(), (int)round(recFraction), vpsUsed ? "Yes": "No"); + if (complete) + tell(1, "Info: Finished: '%s'; recorded %d%%; VPS %s", + rr->timer->File(), (int)round(recFraction), vpsUsed ? "Yes": "No"); + else + tell(1, "Info: Finished: '%s' (not complete! - recorded only %d%%); VPS %s", + rr->timer->File(), (int)round(recFraction), vpsUsed ? "Yes": "No"); - if (complete) - rr->lastBreak = 0; // reset break - else if (!rr->lastBreak) - rr->lastBreak = time(0); // store first break + if (complete) + rr->lastBreak = 0; // reset break + else if (!rr->lastBreak) + rr->lastBreak = time(0); // store first break - if (!rr->lastBreak || (time(0) - rr->lastBreak) > allowedBreakDuration) - { - char* infoTxt; + if (!rr->lastBreak || (time(0) - rr->lastBreak) > allowedBreakDuration) + { + char* infoTxt; - asprintf(&infoTxt, "Recording '%s' finished - %s complete (%d%%)", - rr->timer->File(), complete ? "" : "NOT", (int)round(recFraction)); + asprintf(&infoTxt, "Recording '%s' finished - %s complete (%d%%)", + rr->timer->File(), complete ? "" : "NOT", (int)round(recFraction)); - tell(1, "Info: %s", infoTxt); + tell(1, "Info: %s", infoTxt); - rr->finished = yes; - rr->failed = !complete; - rr->setInfo(infoTxt); + rr->finished = yes; + rr->failed = !complete; + rr->setInfo(infoTxt); - free(infoTxt); + free(infoTxt); + } } } } - recordingStateChangedTrigger = yes; - waitCondition.Broadcast(); // wakeup + return done; } //*************************************************************************** @@ -66,27 +66,8 @@ int cUpdate::performTimerJobs() selectPendingTimerActions->freeResult(); } - // get timers lock - -#if defined (APIVERSNUM) && (APIVERSNUM >= 20301) - LOCK_TIMERS_WRITE; - cTimers* timers = Timers; - // cTimersLock timersLock(true); - // cTimers* timers = timersLock.Timers(); -#else - cTimers* timers = &Timers; -#endif - - // get channels lock - -#if defined (APIVERSNUM) && (APIVERSNUM >= 20301) - LOCK_CHANNELS_WRITE; - const cChannels* channels = Channels; - // cChannelsLock channelsLock(false); - // const cChannels* channels = channelsLock.Channels(); -#else - cChannels* channels = &Channels; -#endif + GET_TIMERS_WRITE(timers); // get timers lock + GET_CHANNELS_READ(channels); // get channels lock // get schedules lock @@ -1224,6 +1224,9 @@ void cUpdate::Action() if (dbConnected() && recordingStateChangedTrigger) { + if (!pendingRecordingActions.empty()) + performRecordingActions(); + if (Epg2VdrConfig.shareInWeb) recordingChanged(); // update timer state @@ -16,6 +16,7 @@ #include "lib/common.h" #include "lib/db.h" #include "lib/epgservice.h" +#include "lib/vdrlocks.h" #include "epg2vdr.h" #include "parameters.h" @@ -146,6 +147,16 @@ class cUpdate : public cThread, public cStatus, public cParameters char channelId[100]; }; + // struct to store a recording action delieverd by the status interface + + struct RecordingAction + { + std::string name; + std::string fileName; + int cardIndex; + bool on; + }; + // functions int initDb(); @@ -189,6 +200,7 @@ class cUpdate : public cThread, public cStatus, public cParameters int cleanupDeletedRecordings(int force = no); int updateRecordingDirectory(const cRecording* recording); int updatePendingRecordingInfoFiles(const cRecordings* recordings); + int performRecordingActions(); int storeAllRecordingInfoFiles(); int updateRecordingInfoFiles(); @@ -273,7 +285,8 @@ class cUpdate : public cThread, public cStatus, public cParameters cDbValue* viewMergeSource; cDbValue* viewLongDescription; - std::queue<std::string> pendingNewRecordings; // recordings to store details + std::queue<std::string> pendingNewRecordings; // recordings to store details (obsolete if pendingRecordingActions implemented finally) + std::queue<RecordingAction> pendingRecordingActions; // recordings actions (start/stop) std::vector<TimerId> deletedTimers; static const char* auxFields[]; |