The list of recordings is now read in a separate thread

This commit is contained in:
Klaus Schmidinger 2005-09-25 11:00:57 +02:00
parent 83985eff47
commit 9e8aac3882
8 changed files with 178 additions and 63 deletions

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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);

View File

@ -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 -----------------------------------------------------------------

View File

@ -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);

View File

@ -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
View File

@ -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();