mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
The list of recordings is now read in a separate thread
This commit is contained in:
parent
83985eff47
commit
9e8aac3882
@ -14,6 +14,8 @@ Carsten Koch <Carsten.Koch@icem.de>
|
||||
for fixing the watchdog timer if the program hangs in OSD activities
|
||||
for his support in keeping the Premiere World channels up to date in 'channels.conf'
|
||||
for fixing converting summary.vdr files that would result in a very long 'short text'
|
||||
for his help in testing and debugging reading the list of recordings in a
|
||||
separate thread
|
||||
|
||||
Plamen Ganev <pganev@com-it.net>
|
||||
for fixing the frequency offset for Hotbird channels
|
||||
|
19
HISTORY
19
HISTORY
@ -3807,7 +3807,7 @@ Video Disk Recorder Revision History
|
||||
- Implemented a hash for the channels to reduce the system load in the EIT scanning
|
||||
thread (based on a patch by Georg Acher).
|
||||
|
||||
2005-09-18: Version 1.3.33
|
||||
2005-09-25: Version 1.3.33
|
||||
|
||||
- Fixed two errors in 'newplugin' (thanks to Alexander Rieger).
|
||||
- Fixed converting arbitrarily formatted summary.vdr files (thanks to Thomas Günther).
|
||||
@ -3824,3 +3824,20 @@ Video Disk Recorder Revision History
|
||||
- Removed obsolete 'shift' code in device.[hc].
|
||||
- The SVDRP command DELR no longer triggers a complete reload of the global Recordings
|
||||
list, but rather deletes that particular entry.
|
||||
- The list of recordings is now read in a separate thread, resulting in a faster
|
||||
startup if there are a great many of recordings, or the disk(s) have to spin up.
|
||||
If the Recordings menu is opened while the list of recordings is still being read,
|
||||
the menu will be updated accordingly.
|
||||
Plugins that access the global Recordings variable should lock the thread, either
|
||||
by calling
|
||||
|
||||
Recordings.Lock();
|
||||
...
|
||||
Recordings.Unlock();
|
||||
|
||||
or by putting something like
|
||||
|
||||
cThreadLock RecordingsLock(&Recordings);
|
||||
|
||||
into the respective code block. Thanks to Carsten Koch for his help in testing
|
||||
and debugging this.
|
||||
|
74
menu.c
74
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 1.359 2005/09/03 11:42:27 kls Exp $
|
||||
* $Id: menu.c 1.360 2005/09/25 09:45:01 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
@ -1506,35 +1506,12 @@ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
|
||||
{
|
||||
base = Base ? strdup(Base) : NULL;
|
||||
level = Setup.RecordingDirs ? Level : -1;
|
||||
Recordings.StateChanged(recordingsState); // just to get the current state
|
||||
Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay
|
||||
const char *LastReplayed = cReplayControl::LastReplayed();
|
||||
cMenuRecordingItem *LastItem = NULL;
|
||||
char *LastItemText = NULL;
|
||||
if (!Base)
|
||||
Recordings.Sort();
|
||||
for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
|
||||
if (!Base || (strstr(recording->Name(), Base) == recording->Name() && recording->Name()[strlen(Base)] == '~')) {
|
||||
cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
|
||||
if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
|
||||
Add(Item);
|
||||
LastItem = Item;
|
||||
free(LastItemText);
|
||||
LastItemText = strdup(LastItem->Text()); // must use a copy because of the counters!
|
||||
}
|
||||
else
|
||||
delete Item;
|
||||
if (LastItem) {
|
||||
if (LastReplayed && strcmp(LastReplayed, recording->FileName()) == 0)
|
||||
SetCurrent(LastItem);
|
||||
if (LastItem->IsDirectory())
|
||||
LastItem->IncrementCounter(recording->IsNew());
|
||||
}
|
||||
}
|
||||
}
|
||||
free(LastItemText);
|
||||
Set();
|
||||
if (Current() < 0)
|
||||
SetCurrent(First());
|
||||
else if (OpenSubMenus && Open(true))
|
||||
else if (OpenSubMenus && cReplayControl::LastReplayed() && Open(true))
|
||||
return;
|
||||
SetHelpKeys();
|
||||
}
|
||||
@ -1570,6 +1547,45 @@ void cMenuRecordings::SetHelpKeys(void)
|
||||
}
|
||||
}
|
||||
|
||||
void cMenuRecordings::Set(bool Refresh)
|
||||
{
|
||||
const char *CurrentRecording = cReplayControl::LastReplayed();
|
||||
cMenuRecordingItem *LastItem = NULL;
|
||||
char *LastItemText = NULL;
|
||||
cThreadLock RecordingsLock(&Recordings);
|
||||
if (Refresh) {
|
||||
cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
|
||||
if (ri) {
|
||||
cRecording *Recording = GetRecording(ri);
|
||||
if (Recording)
|
||||
CurrentRecording = Recording->FileName();
|
||||
}
|
||||
}
|
||||
Clear();
|
||||
Recordings.Sort();
|
||||
for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
|
||||
if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == '~')) {
|
||||
cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
|
||||
if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
|
||||
Add(Item);
|
||||
LastItem = Item;
|
||||
free(LastItemText);
|
||||
LastItemText = strdup(LastItem->Text()); // must use a copy because of the counters!
|
||||
}
|
||||
else
|
||||
delete Item;
|
||||
if (LastItem) {
|
||||
if (CurrentRecording && strcmp(CurrentRecording, recording->FileName()) == 0)
|
||||
SetCurrent(LastItem);
|
||||
if (LastItem->IsDirectory())
|
||||
LastItem->IncrementCounter(recording->IsNew());
|
||||
}
|
||||
}
|
||||
}
|
||||
free(LastItemText);
|
||||
Display();
|
||||
}
|
||||
|
||||
cRecording *cMenuRecordings::GetRecording(cMenuRecordingItem *Item)
|
||||
{
|
||||
cRecording *recording = Recordings.GetByName(Item->FileName());
|
||||
@ -1716,6 +1732,9 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key)
|
||||
case kYellow: return Delete();
|
||||
case kBlue: return Info();
|
||||
case k1...k9: return Commands(Key);
|
||||
case kNone: if (Recordings.StateChanged(recordingsState))
|
||||
Set(true);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@ -2597,6 +2616,7 @@ static void SetTrackDescriptions(bool Live)
|
||||
}
|
||||
}
|
||||
else if (cReplayControl::LastReplayed()) {
|
||||
cThreadLock RecordingsLock(&Recordings);
|
||||
cRecording *Recording = Recordings.GetByName(cReplayControl::LastReplayed());
|
||||
if (Recording)
|
||||
Components = Recording->Info()->Components();
|
||||
|
4
menu.h
4
menu.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.h 1.73 2005/09/03 11:41:41 kls Exp $
|
||||
* $Id: menu.h 1.74 2005/09/25 09:03:32 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __MENU_H
|
||||
@ -147,8 +147,10 @@ class cMenuRecordings : public cOsdMenu {
|
||||
private:
|
||||
char *base;
|
||||
int level;
|
||||
int recordingsState;
|
||||
static int helpKeys;
|
||||
void SetHelpKeys(void);
|
||||
void Set(bool Refresh = false);
|
||||
cRecording *GetRecording(cMenuRecordingItem *Item);
|
||||
bool Open(bool OpenSubMenus = false);
|
||||
eOSState Play(void);
|
||||
|
98
recording.c
98
recording.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recording.c 1.114 2005/09/17 09:14:36 kls Exp $
|
||||
* $Id: recording.c 1.115 2005/09/25 10:40:31 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recording.h"
|
||||
@ -60,17 +60,23 @@
|
||||
|
||||
bool VfatFileSystem = false;
|
||||
|
||||
static cRecordings DeletedRecordings(true);
|
||||
|
||||
void RemoveDeletedRecordings(void)
|
||||
{
|
||||
static time_t LastRemoveCheck = 0;
|
||||
if (time(NULL) - LastRemoveCheck > REMOVECHECKDELTA) {
|
||||
if (LastRemoveCheck == 0) {
|
||||
DeletedRecordings.Update();
|
||||
LastRemoveCheck = time(NULL) - REMOVECHECKDELTA * 9 / 10;
|
||||
}
|
||||
else if (time(NULL) - LastRemoveCheck > REMOVECHECKDELTA) {
|
||||
// Make sure only one instance of VDR does this:
|
||||
cLockFile LockFile(VideoDirectory);
|
||||
if (!LockFile.Lock())
|
||||
return;
|
||||
// Remove the oldest file that has been "deleted":
|
||||
cRecordings DeletedRecordings(true);
|
||||
if (DeletedRecordings.Load()) {
|
||||
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
|
||||
if (DeletedRecordings.Count()) {
|
||||
cRecording *r = DeletedRecordings.First();
|
||||
cRecording *r0 = r;
|
||||
while (r) {
|
||||
@ -80,11 +86,14 @@ void RemoveDeletedRecordings(void)
|
||||
}
|
||||
if (r0 && time(NULL) - r0->start > DELETEDLIFETIME * 3600) {
|
||||
r0->Remove();
|
||||
DeletedRecordings.Del(r0);
|
||||
RemoveEmptyVideoDirectories();
|
||||
LastRemoveCheck += REMOVELATENCY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
DeletedRecordings.Update();
|
||||
LastRemoveCheck = time(NULL);
|
||||
}
|
||||
}
|
||||
@ -104,8 +113,8 @@ void AssertFreeDiskSpace(int Priority)
|
||||
return;
|
||||
// Remove the oldest file that has been "deleted":
|
||||
isyslog("low disk space while recording, trying to remove a deleted recording...");
|
||||
cRecordings DeletedRecordings(true);
|
||||
if (DeletedRecordings.Load()) {
|
||||
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
|
||||
if (DeletedRecordings.Count()) {
|
||||
cRecording *r = DeletedRecordings.First();
|
||||
cRecording *r0 = r;
|
||||
while (r) {
|
||||
@ -114,13 +123,20 @@ void AssertFreeDiskSpace(int Priority)
|
||||
r = DeletedRecordings.Next(r);
|
||||
}
|
||||
if (r0 && r0->Remove()) {
|
||||
DeletedRecordings.Del(r0);
|
||||
LastFreeDiskCheck += REMOVELATENCY / Factor;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// DeletedRecordings was empty, so to be absolutely sure there are no
|
||||
// deleted recordings we need to double check:
|
||||
DeletedRecordings.Update(true);
|
||||
if (DeletedRecordings.Count())
|
||||
return; // the next call will actually remove it
|
||||
// No "deleted" files to remove, so let's see if we can delete a recording:
|
||||
isyslog("...no deleted recording found, trying to delete an old recording...");
|
||||
if (Recordings.Load()) {
|
||||
cThreadLock RecordingsLock(&Recordings);
|
||||
if (Recordings.Count()) {
|
||||
cRecording *r = Recordings.First();
|
||||
cRecording *r0 = NULL;
|
||||
while (r) {
|
||||
@ -454,6 +470,7 @@ cRecording::cRecording(const char *FileName)
|
||||
name[p - FileName] = 0;
|
||||
name = ExchangeChars(name, false);
|
||||
}
|
||||
GetResume();
|
||||
// read an optional info file:
|
||||
char *InfoFileName = NULL;
|
||||
asprintf(&InfoFileName, "%s%s", fileName, INFOFILESUFFIX);
|
||||
@ -720,16 +737,38 @@ bool cRecording::Remove(void)
|
||||
cRecordings Recordings;
|
||||
|
||||
cRecordings::cRecordings(bool Deleted)
|
||||
:cThread("video directory scanner")
|
||||
{
|
||||
deleted = Deleted;
|
||||
lastUpdate = 0;
|
||||
state = 0;
|
||||
}
|
||||
|
||||
void cRecordings::ScanVideoDir(const char *DirName)
|
||||
cRecordings::~cRecordings()
|
||||
{
|
||||
Cancel(3);
|
||||
}
|
||||
|
||||
void cRecordings::Action(void)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void cRecordings::Refresh(bool Foreground)
|
||||
{
|
||||
lastUpdate = time(NULL); // doing this first to make sure we don't miss anything
|
||||
Lock();
|
||||
Clear();
|
||||
ChangeState();
|
||||
Unlock();
|
||||
ScanVideoDir(VideoDirectory, Foreground);
|
||||
}
|
||||
|
||||
void cRecordings::ScanVideoDir(const char *DirName, bool Foreground)
|
||||
{
|
||||
cReadDir d(DirName);
|
||||
struct dirent *e;
|
||||
while ((e = d.Next()) != NULL) {
|
||||
while ((Foreground || Running()) && (e = d.Next()) != NULL) {
|
||||
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
|
||||
char *buffer;
|
||||
asprintf(&buffer, "%s/%s", DirName, e->d_name);
|
||||
@ -749,13 +788,17 @@ void cRecordings::ScanVideoDir(const char *DirName)
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (endswith(buffer, deleted ? DELEXT : RECEXT)) {
|
||||
cRecording *r = new cRecording(buffer);
|
||||
if (r->Name())
|
||||
if (r->Name()) {
|
||||
Lock();
|
||||
Add(r);
|
||||
ChangeState();
|
||||
Unlock();
|
||||
}
|
||||
else
|
||||
delete r;
|
||||
}
|
||||
else
|
||||
ScanVideoDir(buffer);
|
||||
ScanVideoDir(buffer, Foreground);
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
@ -763,18 +806,28 @@ void cRecordings::ScanVideoDir(const char *DirName)
|
||||
}
|
||||
}
|
||||
|
||||
bool cRecordings::NeedsUpdate(void)
|
||||
bool cRecordings::StateChanged(int &State)
|
||||
{
|
||||
return lastUpdate <= LastModifiedTime(AddDirectory(VideoDirectory, ".update"));
|
||||
int NewState = state;
|
||||
bool Result = State != NewState;
|
||||
State = state;
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool cRecordings::Load(void)
|
||||
bool cRecordings::NeedsUpdate(void)
|
||||
{
|
||||
lastUpdate = time(NULL); // doing this first to make sure we don't miss anything
|
||||
Clear();
|
||||
ScanVideoDir(VideoDirectory);
|
||||
Sort();
|
||||
return Count() > 0;
|
||||
return lastUpdate < LastModifiedTime(AddDirectory(VideoDirectory, ".update"));
|
||||
}
|
||||
|
||||
bool cRecordings::Update(bool Wait)
|
||||
{
|
||||
if (Wait) {
|
||||
Refresh(true);
|
||||
return Count() > 0;
|
||||
}
|
||||
else
|
||||
Start();
|
||||
return false;
|
||||
}
|
||||
|
||||
cRecording *cRecordings::GetByName(const char *FileName)
|
||||
@ -788,18 +841,23 @@ cRecording *cRecordings::GetByName(const char *FileName)
|
||||
|
||||
void cRecordings::AddByName(const char *FileName)
|
||||
{
|
||||
LOCK_THREAD;
|
||||
cRecording *recording = GetByName(FileName);
|
||||
if (!recording) {
|
||||
recording = new cRecording(FileName);
|
||||
Add(recording);
|
||||
ChangeState();
|
||||
}
|
||||
}
|
||||
|
||||
void cRecordings::DelByName(const char *FileName)
|
||||
{
|
||||
LOCK_THREAD;
|
||||
cRecording *recording = GetByName(FileName);
|
||||
if (recording)
|
||||
if (recording) {
|
||||
Del(recording);
|
||||
ChangeState();
|
||||
}
|
||||
}
|
||||
|
||||
// --- cMark -----------------------------------------------------------------
|
||||
|
28
recording.h
28
recording.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recording.h 1.40 2005/09/03 13:04:41 kls Exp $
|
||||
* $Id: recording.h 1.41 2005/09/25 10:07:40 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __RECORDING_H
|
||||
@ -71,7 +71,7 @@ public:
|
||||
int lifetime;
|
||||
cRecording(cTimer *Timer, const cEvent *Event);
|
||||
cRecording(const char *FileName);
|
||||
~cRecording();
|
||||
virtual ~cRecording();
|
||||
virtual int Compare(const cListObject &ListObject) const;
|
||||
const char *Name(void) const { return name; }
|
||||
const char *FileName(void) const;
|
||||
@ -90,16 +90,32 @@ public:
|
||||
// Returns false in case of error
|
||||
};
|
||||
|
||||
class cRecordings : public cList<cRecording> {
|
||||
class cRecordings : public cList<cRecording>, public cThread {
|
||||
private:
|
||||
bool deleted;
|
||||
time_t lastUpdate;
|
||||
void ScanVideoDir(const char *DirName);
|
||||
int state;
|
||||
void Refresh(bool Foreground = false);
|
||||
void ScanVideoDir(const char *DirName, bool Foreground = false);
|
||||
protected:
|
||||
void Action(void);
|
||||
public:
|
||||
cRecordings(bool Deleted = false);
|
||||
bool Load(void);
|
||||
virtual ~cRecordings();
|
||||
bool Load(void) { return Update(true); }
|
||||
///< Loads the current list of recordings and returns true if there
|
||||
///< is anything in it (for compatibility with older plugins - use
|
||||
///< Update(true) instead).
|
||||
bool Update(bool Wait = false);
|
||||
///< Triggers an update of the list of recordings, which will run
|
||||
///< as a separate thread if Wait is false. If Wait is true, the
|
||||
///< function returns only after the update has completed.
|
||||
///< Returns true if Wait is true and there is anyting in the list
|
||||
///< of recordings, false otherwise.
|
||||
void TriggerUpdate(void) { lastUpdate = 0; }
|
||||
bool NeedsUpdate(void);
|
||||
void ChangeState(void) { state++; }
|
||||
bool StateChanged(int &State);
|
||||
cRecording *GetByName(const char *FileName);
|
||||
void AddByName(const char *FileName);
|
||||
void DelByName(const char *FileName);
|
||||
@ -112,7 +128,7 @@ public:
|
||||
int position;
|
||||
char *comment;
|
||||
cMark(int Position = 0, const char *Comment = NULL);
|
||||
~cMark();
|
||||
virtual ~cMark();
|
||||
cString ToText(void);
|
||||
bool Parse(const char *s);
|
||||
bool Save(FILE *f);
|
||||
|
4
svdrp.c
4
svdrp.c
@ -10,7 +10,7 @@
|
||||
* and interact with the Video Disk Recorder - or write a full featured
|
||||
* graphical interface that sits on top of an SVDRP connection.
|
||||
*
|
||||
* $Id: svdrp.c 1.81 2005/09/18 10:50:08 kls Exp $
|
||||
* $Id: svdrp.c 1.82 2005/09/25 10:36:59 kls Exp $
|
||||
*/
|
||||
|
||||
#include "svdrp.h"
|
||||
@ -890,7 +890,7 @@ void cSVDRP::CmdLSTE(const char *Option)
|
||||
|
||||
void cSVDRP::CmdLSTR(const char *Option)
|
||||
{
|
||||
bool recordings = Recordings.Load();
|
||||
bool recordings = Recordings.Update(true);
|
||||
if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
|
||||
|
12
vdr.c
12
vdr.c
@ -22,7 +22,7 @@
|
||||
*
|
||||
* The project's page is at http://www.cadsoft.de/vdr
|
||||
*
|
||||
* $Id: vdr.c 1.216 2005/09/04 08:57:15 kls Exp $
|
||||
* $Id: vdr.c 1.217 2005/09/24 13:27:26 kls Exp $
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
@ -437,6 +437,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
cFont::SetCode(I18nCharSets()[Setup.OSDLanguage]);
|
||||
|
||||
// Recordings:
|
||||
|
||||
Recordings.Update();
|
||||
|
||||
// EPG data:
|
||||
|
||||
if (EpgDataFileName) {
|
||||
@ -539,10 +543,6 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
cDevice::PrimaryDevice()->SetVolume(Setup.CurrentVolume, true);
|
||||
|
||||
// Recordings:
|
||||
|
||||
Recordings.Load();
|
||||
|
||||
// Signal handlers:
|
||||
|
||||
if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN);
|
||||
@ -674,7 +674,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
if (!Menu && Recordings.NeedsUpdate())
|
||||
Recordings.Load();
|
||||
Recordings.Update();
|
||||
// CAM control:
|
||||
if (!Menu && !cOsd::IsOpen()) {
|
||||
Menu = CamControl();
|
||||
|
Loading…
Reference in New Issue
Block a user