Implemented strict locking of global lists

This commit is contained in:
Klaus Schmidinger
2015-09-01 11:14:27 +02:00
parent 8a7bc6a0bb
commit 3cd5294d8a
41 changed files with 3512 additions and 2402 deletions

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 4.2 2015/04/18 13:14:55 kls Exp $
* $Id: recording.c 4.3 2015/08/29 14:42:53 kls Exp $
*/
#include "recording.h"
@@ -76,9 +76,6 @@ int DirectoryNameMax = NAME_MAX;
bool DirectoryEncoding = false;
int InstanceId = 0;
cRecordings DeletedRecordings(true);
static cRecordings VanishedRecordings;
// --- cRemoveDeletedRecordingsThread ----------------------------------------
class cRemoveDeletedRecordingsThread : public cThread {
@@ -100,8 +97,8 @@ void cRemoveDeletedRecordingsThread::Action(void)
if (LockFile.Lock()) {
time_t StartTime = time(NULL);
bool deleted = false;
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
for (cRecording *r = DeletedRecordings.First(); r; ) {
LOCK_DELETEDRECORDINGS_WRITE;
for (cRecording *r = DeletedRecordings->First(); r; ) {
if (cIoThrottle::Engaged())
return;
if (time(NULL) - StartTime > MAXREMOVETIME)
@@ -109,14 +106,14 @@ void cRemoveDeletedRecordingsThread::Action(void)
if (cRemote::HasKeys())
return; // react immediately on user input
if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) {
cRecording *next = DeletedRecordings.Next(r);
cRecording *next = DeletedRecordings->Next(r);
r->Remove();
DeletedRecordings.Del(r);
DeletedRecordings->Del(r);
r = next;
deleted = true;
continue;
}
r = DeletedRecordings.Next(r);
r = DeletedRecordings->Next(r);
}
if (deleted) {
const char *IgnoreFiles[] = { SORTMODEFILE, NULL };
@@ -134,8 +131,8 @@ void RemoveDeletedRecordings(void)
static time_t LastRemoveCheck = 0;
if (time(NULL) - LastRemoveCheck > REMOVECHECKDELTA) {
if (!RemoveDeletedRecordingsThread.Active()) {
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
for (cRecording *r = DeletedRecordings.First(); r; r = DeletedRecordings.Next(r)) {
LOCK_DELETEDRECORDINGS_READ;
for (const cRecording *r = DeletedRecordings->First(); r; r = DeletedRecordings->Next(r)) {
if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) {
RemoveDeletedRecordingsThread.Start();
break;
@@ -163,37 +160,43 @@ void AssertFreeDiskSpace(int Priority, bool Force)
return;
// Remove the oldest file that has been "deleted":
isyslog("low disk space while recording, trying to remove a deleted recording...");
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
if (DeletedRecordings.Count()) {
cRecording *r = DeletedRecordings.First();
cRecording *r0 = NULL;
while (r) {
if (r->IsOnVideoDirectoryFileSystem()) { // only remove recordings that will actually increase the free video disk space
if (!r0 || r->Start() < r0->Start())
r0 = r;
}
r = DeletedRecordings.Next(r);
}
if (r0) {
if (r0->Remove())
LastFreeDiskCheck += REMOVELATENCY / Factor;
DeletedRecordings.Del(r0);
return;
}
}
else {
int NumDeletedRecordings = 0;
{
LOCK_DELETEDRECORDINGS_WRITE;
NumDeletedRecordings = DeletedRecordings->Count();
if (NumDeletedRecordings) {
cRecording *r = DeletedRecordings->First();
cRecording *r0 = NULL;
while (r) {
if (r->IsOnVideoDirectoryFileSystem()) { // only remove recordings that will actually increase the free video disk space
if (!r0 || r->Start() < r0->Start())
r0 = r;
}
r = DeletedRecordings->Next(r);
}
if (r0) {
if (r0->Remove())
LastFreeDiskCheck += REMOVELATENCY / Factor;
DeletedRecordings->Del(r0);
return;
}
}
}
if (NumDeletedRecordings == 0) {
// DeletedRecordings was empty, so to be absolutely sure there are no
// deleted recordings we need to double check:
DeletedRecordings.Update(true);
if (DeletedRecordings.Count())
cRecordings::Update(true);
LOCK_DELETEDRECORDINGS_READ;
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:
if (Priority > 0) {
isyslog("...no deleted recording found, trying to delete an old recording...");
cThreadLock RecordingsLock(&Recordings);
if (Recordings.Count()) {
cRecording *r = Recordings.First();
LOCK_RECORDINGS_WRITE;
Recordings->SetExplicitModify();
if (Recordings->Count()) {
cRecording *r = Recordings->First();
cRecording *r0 = NULL;
while (r) {
if (r->IsOnVideoDirectoryFileSystem()) { // only delete recordings that will actually increase the free video disk space
@@ -209,10 +212,11 @@ void AssertFreeDiskSpace(int Priority, bool Force)
}
}
}
r = Recordings.Next(r);
r = Recordings->Next(r);
}
if (r0 && r0->Delete()) {
Recordings.Del(r0);
Recordings->Del(r0);
Recordings->SetModified();
return;
}
}
@@ -227,14 +231,6 @@ void AssertFreeDiskSpace(int Priority, bool Force)
}
}
// --- Clear vanished recordings ---------------------------------------------
void ClearVanishedRecordings(void)
{
cThreadLock RecordingsLock(&Recordings); // yes, it *is* Recordings!
VanishedRecordings.Clear();
}
// --- cResumeFile -----------------------------------------------------------
cResumeFile::cResumeFile(const char *FileName, bool IsPesRecording)
@@ -309,7 +305,8 @@ bool cResumeFile::Save(int Index)
if (safe_write(f, &Index, sizeof(Index)) < 0)
LOG_ERROR_STR(fileName);
close(f);
Recordings.ResetResume(fileName);
LOCK_RECORDINGS_WRITE;
Recordings->ResetResume(fileName);
return true;
}
}
@@ -318,7 +315,8 @@ bool cResumeFile::Save(int Index)
if (f) {
fprintf(f, "I %d\n", Index);
fclose(f);
Recordings.ResetResume(fileName);
LOCK_RECORDINGS_WRITE;
Recordings->ResetResume(fileName);
}
else
LOG_ERROR_STR(fileName);
@@ -331,8 +329,10 @@ bool cResumeFile::Save(int Index)
void cResumeFile::Delete(void)
{
if (fileName) {
if (remove(fileName) == 0)
Recordings.ResetResume(fileName);
if (remove(fileName) == 0) {
LOCK_RECORDINGS_WRITE;
Recordings->ResetResume(fileName);
}
else if (errno != ENOENT)
LOG_ERROR_STR(fileName);
}
@@ -787,10 +787,8 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event)
else
break;
}
if (Timer->IsSingleEvent()) {
if (Timer->IsSingleEvent())
Timer->SetFile(name); // this was an instant recording, so let's set the actual data
Timers.SetModified();
}
}
else if (Timer->IsSingleEvent() || !Setup.UseSubtitle)
name = strdup(Timer->File());
@@ -1017,7 +1015,7 @@ int cRecording::Compare(const cListObject &ListObject) const
return strcasecmp(SortName(), r->SortName());
}
bool cRecording::IsInPath(const char *Path)
bool cRecording::IsInPath(const char *Path) const
{
if (isempty(Path))
return true;
@@ -1154,20 +1152,14 @@ bool cRecording::IsOnVideoDirectoryFileSystem(void) const
return isOnVideoDirectoryFileSystem;
}
bool cRecording::HasMarks(void)
bool cRecording::HasMarks(void) const
{
return access(cMarks::MarksFileName(this), F_OK) == 0;
}
bool cRecording::DeleteMarks(void)
{
if (remove(cMarks::MarksFileName(this)) < 0) {
if (errno != ENOENT) {
LOG_ERROR_STR(fileName);
return false;
}
}
return true;
return cMarks::DeleteMarksFile(this);
}
void cRecording::ReadInfo(void)
@@ -1219,8 +1211,6 @@ bool cRecording::ChangePriorityLifetime(int NewPriority, int NewLifetime)
if (!WriteInfo())
return false;
}
Recordings.ChangeState();
Recordings.TouchUpdate();
}
return true;
}
@@ -1245,8 +1235,6 @@ bool cRecording::ChangeName(const char *NewName)
}
isOnVideoDirectoryFileSystem = -1; // it might have been moved to a different file system
ClearSortName();
Recordings.ChangeState();
Recordings.TouchUpdate();
}
return true;
}
@@ -1360,59 +1348,53 @@ int cRecording::FileSizeMB(void) const
return fileSizeMB;
}
// --- cRecordings -----------------------------------------------------------
// --- cVideoDirectoryScannerThread ------------------------------------------
cRecordings Recordings;
class cVideoDirectoryScannerThread : public cThread {
private:
cRecordings *recordings;
cRecordings *deletedRecordings;
bool initial;
void ScanVideoDir(const char *DirName, int LinkLevel = 0, int DirLevel = 0);
protected:
virtual void Action(void);
public:
cVideoDirectoryScannerThread(cRecordings *Recordings, cRecordings *DeletedRecordings);
~cVideoDirectoryScannerThread();
};
char *cRecordings::updateFileName = NULL;
cRecordings::cRecordings(bool Deleted)
cVideoDirectoryScannerThread::cVideoDirectoryScannerThread(cRecordings *Recordings, cRecordings *DeletedRecordings)
:cThread("video directory scanner", true)
{
deleted = Deleted;
recordings = Recordings;
deletedRecordings = DeletedRecordings;
initial = true;
lastUpdate = 0;
state = 0;
}
cRecordings::~cRecordings()
cVideoDirectoryScannerThread::~cVideoDirectoryScannerThread()
{
Cancel(3);
}
void cRecordings::Action(void)
void cVideoDirectoryScannerThread::Action(void)
{
Refresh();
cStateKey StateKey;
recordings->Lock(StateKey);
initial = recordings->Count() == 0; // no name checking if the list is initially empty
StateKey.Remove();
deletedRecordings->Lock(StateKey, true);
deletedRecordings->Clear();
StateKey.Remove();
ScanVideoDir(cVideoDirectory::Name());
}
const char *cRecordings::UpdateFileName(void)
void cVideoDirectoryScannerThread::ScanVideoDir(const char *DirName, int LinkLevel, int DirLevel)
{
if (!updateFileName)
updateFileName = strdup(AddDirectory(cVideoDirectory::Name(), ".update"));
return updateFileName;
}
void cRecordings::Refresh(bool Foreground)
{
lastUpdate = time(NULL); // doing this first to make sure we don't miss anything
initial = Count() == 0; // no name checking if the list is initially empty
if (deleted) {
Lock();
Clear();
ChangeState();
Unlock();
}
ScanVideoDir(cVideoDirectory::Name(), Foreground);
}
bool cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLevel, int DirLevel)
{
bool DoChangeState = false;
// Find any new recordings:
cReadDir d(DirName);
struct dirent *e;
while ((Foreground || Running()) && (e = d.Next()) != NULL) {
if (!Foreground && cIoThrottle::Engaged())
while (Running() && (e = d.Next()) != NULL) {
if (cIoThrottle::Engaged())
cCondWait::SleepMs(100);
cString buffer = AddDirectory(DirName, e->d_name);
struct stat st;
@@ -1428,57 +1410,73 @@ bool cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLev
continue;
}
if (S_ISDIR(st.st_mode)) {
if (endswith(buffer, deleted ? DELEXT : RECEXT)) {
if (deleted || initial || !GetByName(buffer)) {
cRecordings *Recordings = NULL;
if (endswith(buffer, RECEXT))
Recordings = recordings;
else if (endswith(buffer, DELEXT))
Recordings = deletedRecordings;
if (Recordings) {
cStateKey StateKey;
Recordings->Lock(StateKey, true);
if (Recordings == deletedRecordings || initial || !Recordings->GetByName(buffer)) {
cRecording *r = new cRecording(buffer);
if (r->Name()) {
r->NumFrames(); // initializes the numFrames member
r->FileSizeMB(); // initializes the fileSizeMB member
r->IsOnVideoDirectoryFileSystem(); // initializes the isOnVideoDirectoryFileSystem member
if (deleted)
r->deleted = time(NULL);
Lock();
Add(r);
if (initial)
ChangeState();
else
DoChangeState = true;
Unlock();
if (Recordings == deletedRecordings)
r->SetDeleted();
Recordings->Add(r);
}
else
delete r;
}
StateKey.Remove();
}
else
DoChangeState |= ScanVideoDir(buffer, Foreground, LinkLevel + Link, DirLevel + 1);
ScanVideoDir(buffer, LinkLevel + Link, DirLevel + 1);
}
}
}
// Handle any vanished recordings:
if (!deleted && !initial && DirLevel == 0) {
for (cRecording *recording = First(); recording; ) {
cRecording *r = recording;
recording = Next(recording);
if (access(r->FileName(), F_OK) != 0) {
Lock();
Del(r, false);
VanishedRecordings.Add(r);
DoChangeState = true;
Unlock();
}
if (!initial && DirLevel == 0) {
cStateKey StateKey;
recordings->Lock(StateKey, true);
for (cRecording *Recording = recordings->First(); Recording; ) {
cRecording *r = Recording;
Recording = recordings->Next(Recording);
if (access(r->FileName(), F_OK) != 0)
recordings->Del(r);
}
StateKey.Remove();
}
if (DoChangeState && DirLevel == 0)
ChangeState();
return DoChangeState;
}
bool cRecordings::StateChanged(int &State)
// --- cRecordings -----------------------------------------------------------
cRecordings cRecordings::recordings;
cRecordings cRecordings::deletedRecordings(true);
char *cRecordings::updateFileName = NULL;
cVideoDirectoryScannerThread *cRecordings::videoDirectoryScannerThread = NULL;
time_t cRecordings::lastUpdate = 0;
cRecordings::cRecordings(bool Deleted)
:cList<cRecording>(Deleted ? "DelRecs" : "Recordings")
{
int NewState = state;
bool Result = State != NewState;
State = state;
return Result;
}
cRecordings::~cRecordings()
{
// The first one to be destructed deletes it:
delete videoDirectoryScannerThread;
videoDirectoryScannerThread = NULL;
}
const char *cRecordings::UpdateFileName(void)
{
if (!updateFileName)
updateFileName = strdup(AddDirectory(cVideoDirectory::Name(), ".update"));
return updateFileName;
}
void cRecordings::TouchUpdate(void)
@@ -1497,24 +1495,24 @@ bool cRecordings::NeedsUpdate(void)
return lastUpdate < lastModified;
}
bool cRecordings::Update(bool Wait)
void cRecordings::Update(bool Wait)
{
if (!videoDirectoryScannerThread)
videoDirectoryScannerThread = new cVideoDirectoryScannerThread(&recordings, &deletedRecordings);
lastUpdate = time(NULL); // doing this first to make sure we don't miss anything
videoDirectoryScannerThread->Start();
if (Wait) {
Refresh(true);
return Count() > 0;
while (videoDirectoryScannerThread->Active())
cCondWait::SleepMs(100);
}
else
Start();
return false;
}
cRecording *cRecordings::GetByName(const char *FileName)
const cRecording *cRecordings::GetByName(const char *FileName) const
{
if (FileName) {
LOCK_THREAD;
for (cRecording *recording = First(); recording; recording = Next(recording)) {
if (strcmp(recording->FileName(), FileName) == 0)
return recording;
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
if (strcmp(Recording->FileName(), FileName) == 0)
return Recording;
}
}
return NULL;
@@ -1522,12 +1520,8 @@ cRecording *cRecordings::GetByName(const char *FileName)
void cRecordings::AddByName(const char *FileName, bool TriggerUpdate)
{
LOCK_THREAD;
cRecording *recording = GetByName(FileName);
if (!recording) {
recording = new cRecording(FileName);
Add(recording);
ChangeState();
if (!GetByName(FileName)) {
Add(new cRecording(FileName));
if (TriggerUpdate)
TouchUpdate();
}
@@ -1535,58 +1529,52 @@ void cRecordings::AddByName(const char *FileName, bool TriggerUpdate)
void cRecordings::DelByName(const char *FileName)
{
LOCK_THREAD;
cRecording *recording = GetByName(FileName);
cRecording *Recording = GetByName(FileName);
cRecording *dummy = NULL;
if (!recording)
recording = dummy = new cRecording(FileName); // allows us to use a FileName that is not in the Recordings list
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
if (!Recording)
Recording = dummy = new cRecording(FileName); // allows us to use a FileName that is not in the Recordings list
LOCK_DELETEDRECORDINGS_WRITE;
if (!dummy)
Del(recording, false);
char *ext = strrchr(recording->fileName, '.');
Del(Recording, false);
char *ext = strrchr(Recording->fileName, '.');
if (ext) {
strncpy(ext, DELEXT, strlen(ext));
if (access(recording->FileName(), F_OK) == 0) {
recording->deleted = time(NULL);
DeletedRecordings.Add(recording);
recording = NULL; // to prevent it from being deleted below
if (access(Recording->FileName(), F_OK) == 0) {
Recording->SetDeleted();
DeletedRecordings->Add(Recording);
Recording = NULL; // to prevent it from being deleted below
}
}
delete recording;
ChangeState();
delete Recording;
TouchUpdate();
}
void cRecordings::UpdateByName(const char *FileName)
{
LOCK_THREAD;
cRecording *recording = GetByName(FileName);
if (recording)
recording->ReadInfo();
if (cRecording *Recording = GetByName(FileName))
Recording->ReadInfo();
}
int cRecordings::TotalFileSizeMB(void)
int cRecordings::TotalFileSizeMB(void) const
{
int size = 0;
LOCK_THREAD;
for (cRecording *recording = First(); recording; recording = Next(recording)) {
int FileSizeMB = recording->FileSizeMB();
if (FileSizeMB > 0 && recording->IsOnVideoDirectoryFileSystem())
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
int FileSizeMB = Recording->FileSizeMB();
if (FileSizeMB > 0 && Recording->IsOnVideoDirectoryFileSystem())
size += FileSizeMB;
}
return size;
}
double cRecordings::MBperMinute(void)
double cRecordings::MBperMinute(void) const
{
int size = 0;
int length = 0;
LOCK_THREAD;
for (cRecording *recording = First(); recording; recording = Next(recording)) {
if (recording->IsOnVideoDirectoryFileSystem()) {
int FileSizeMB = recording->FileSizeMB();
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
if (Recording->IsOnVideoDirectoryFileSystem()) {
int FileSizeMB = Recording->FileSizeMB();
if (FileSizeMB > 0) {
int LengthInSeconds = recording->LengthInSeconds();
int LengthInSeconds = Recording->LengthInSeconds();
if (LengthInSeconds > 0) {
if (LengthInSeconds / FileSizeMB < LIMIT_SECS_PER_MB_RADIO) { // don't count radio recordings
size += FileSizeMB;
@@ -1599,23 +1587,21 @@ double cRecordings::MBperMinute(void)
return (size && length) ? double(size) * 60 / length : -1;
}
int cRecordings::PathIsInUse(const char *Path)
int cRecordings::PathIsInUse(const char *Path) const
{
LOCK_THREAD;
int Use = ruNone;
for (cRecording *recording = First(); recording; recording = Next(recording)) {
if (recording->IsInPath(Path))
Use |= recording->IsInUse();
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
if (Recording->IsInPath(Path))
Use |= Recording->IsInUse();
}
return Use;
}
int cRecordings::GetNumRecordingsInPath(const char *Path)
int cRecordings::GetNumRecordingsInPath(const char *Path) const
{
LOCK_THREAD;
int n = 0;
for (cRecording *recording = First(); recording; recording = Next(recording)) {
if (recording->IsInPath(Path))
for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
if (Recording->IsInPath(Path))
n++;
}
return n;
@@ -1624,36 +1610,35 @@ int cRecordings::GetNumRecordingsInPath(const char *Path)
bool cRecordings::MoveRecordings(const char *OldPath, const char *NewPath)
{
if (OldPath && NewPath && strcmp(OldPath, NewPath)) {
LOCK_THREAD;
dsyslog("moving '%s' to '%s'", OldPath, NewPath);
for (cRecording *recording = First(); recording; recording = Next(recording)) {
if (recording->IsInPath(OldPath)) {
const char *p = recording->Name() + strlen(OldPath);
bool Moved = false;
for (cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
if (Recording->IsInPath(OldPath)) {
const char *p = Recording->Name() + strlen(OldPath);
cString NewName = cString::sprintf("%s%s", NewPath, p);
if (!recording->ChangeName(NewName))
if (!Recording->ChangeName(NewName))
return false;
ChangeState();
Moved = true;
}
}
if (Moved)
TouchUpdate();
}
return true;
}
void cRecordings::ResetResume(const char *ResumeFileName)
{
LOCK_THREAD;
for (cRecording *recording = First(); recording; recording = Next(recording)) {
if (!ResumeFileName || strncmp(ResumeFileName, recording->FileName(), strlen(recording->FileName())) == 0)
recording->ResetResume();
for (cRecording *Recording = First(); Recording; Recording = Next(Recording)) {
if (!ResumeFileName || strncmp(ResumeFileName, Recording->FileName(), strlen(Recording->FileName())) == 0)
Recording->ResetResume();
}
ChangeState();
}
void cRecordings::ClearSortNames(void)
{
LOCK_THREAD;
for (cRecording *recording = First(); recording; recording = Next(recording))
recording->ClearSortName();
for (cRecording *Recording = First(); Recording; Recording = Next(Recording))
Recording->ClearSortName();
}
// --- cDirCopier ------------------------------------------------------------
@@ -1812,8 +1797,9 @@ void cDirCopier::Stop(void)
Cancel(3);
if (error) {
cVideoDirectory::RemoveVideoFile(dirNameDst);
Recordings.AddByName(dirNameSrc);
Recordings.DelByName(dirNameDst);
LOCK_RECORDINGS_WRITE;
Recordings->AddByName(dirNameSrc);
Recordings->DelByName(dirNameDst);
}
}
@@ -1893,17 +1879,19 @@ bool cRecordingsHandlerEntry::Active(bool &Error)
copier->Start();
}
ClearPending();
Recordings.ChangeState();
LOCK_RECORDINGS_WRITE; // to trigger a state change
return true;
}
// Clean up:
if (CopierFinishedOk && (Usage() & ruMove) != 0) {
cRecording Recording(FileNameSrc());
if (Recording.Delete())
Recordings.DelByName(Recording.FileName());
if (Recording.Delete()) {
LOCK_RECORDINGS_WRITE;
Recordings->DelByName(Recording.FileName());
}
}
Recordings.ChangeState();
Recordings.TouchUpdate();
LOCK_RECORDINGS_WRITE; // to trigger a state change
Recordings->TouchUpdate();
return false;
}
@@ -1947,7 +1935,7 @@ bool cRecordingsHandler::Add(int Usage, const char *FileNameSrc, const char *Fil
operations.Add(new cRecordingsHandlerEntry(Usage, FileNameSrc, FileNameDst));
finished = false;
Active(); // start it right away if possible
Recordings.ChangeState();
LOCK_RECORDINGS_WRITE; // to trigger a state change
return true;
}
else
@@ -1969,7 +1957,7 @@ void cRecordingsHandler::Del(const char *FileName)
cMutexLock MutexLock(&mutex);
if (cRecordingsHandlerEntry *r = Get(FileName)) {
operations.Del(r);
Recordings.ChangeState();
LOCK_RECORDINGS_WRITE; // to trigger a state change
}
}
@@ -1977,7 +1965,7 @@ void cRecordingsHandler::DelAll(void)
{
cMutexLock MutexLock(&mutex);
operations.Clear();
Recordings.ChangeState();
LOCK_RECORDINGS_WRITE; // to trigger a state change
}
int cRecordingsHandler::GetUsage(const char *FileName)
@@ -2059,9 +2047,19 @@ cString cMarks::MarksFileName(const cRecording *Recording)
return AddDirectory(Recording->FileName(), Recording->IsPesRecording() ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX);
}
bool cMarks::DeleteMarksFile(const cRecording *Recording)
{
if (remove(cMarks::MarksFileName(Recording)) < 0) {
if (errno != ENOENT) {
LOG_ERROR_STR(Recording->FileName());
return false;
}
}
return true;
}
bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool IsPesRecording)
{
cMutexLock MutexLock(this);
recordingFileName = RecordingFileName;
fileName = AddDirectory(RecordingFileName, IsPesRecording ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX);
framesPerSecond = FramesPerSecond;
@@ -2074,7 +2072,6 @@ bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool Is
bool cMarks::Update(void)
{
cMutexLock MutexLock(this);
time_t t = time(NULL);
if (t > nextUpdate && *fileName) {
time_t LastModified = LastModifiedTime(fileName);
@@ -2106,7 +2103,6 @@ bool cMarks::Update(void)
bool cMarks::Save(void)
{
cMutexLock MutexLock(this);
if (cConfig<cMark>::Save()) {
lastFileTime = LastModifiedTime(fileName);
return true;
@@ -2116,7 +2112,6 @@ bool cMarks::Save(void)
void cMarks::Align(void)
{
cMutexLock MutexLock(this);
cIndexFile IndexFile(recordingFileName, false, isPesRecording);
for (cMark *m = First(); m; m = Next(m)) {
int p = IndexFile.GetClosestIFrame(m->Position());
@@ -2129,7 +2124,6 @@ void cMarks::Align(void)
void cMarks::Sort(void)
{
cMutexLock MutexLock(this);
for (cMark *m1 = First(); m1; m1 = Next(m1)) {
for (cMark *m2 = Next(m1); m2; m2 = Next(m2)) {
if (m2->Position() < m1->Position()) {
@@ -2142,43 +2136,42 @@ void cMarks::Sort(void)
void cMarks::Add(int Position)
{
cMutexLock MutexLock(this);
cConfig<cMark>::Add(new cMark(Position, NULL, framesPerSecond));
Sort();
}
cMark *cMarks::Get(int Position)
const cMark *cMarks::Get(int Position) const
{
for (cMark *mi = First(); mi; mi = Next(mi)) {
for (const cMark *mi = First(); mi; mi = Next(mi)) {
if (mi->Position() == Position)
return mi;
}
return NULL;
}
cMark *cMarks::GetPrev(int Position)
const cMark *cMarks::GetPrev(int Position) const
{
for (cMark *mi = Last(); mi; mi = Prev(mi)) {
for (const cMark *mi = Last(); mi; mi = Prev(mi)) {
if (mi->Position() < Position)
return mi;
}
return NULL;
}
cMark *cMarks::GetNext(int Position)
const cMark *cMarks::GetNext(int Position) const
{
for (cMark *mi = First(); mi; mi = Next(mi)) {
for (const cMark *mi = First(); mi; mi = Next(mi)) {
if (mi->Position() > Position)
return mi;
}
return NULL;
}
cMark *cMarks::GetNextBegin(cMark *EndMark)
const cMark *cMarks::GetNextBegin(const cMark *EndMark) const
{
cMark *BeginMark = EndMark ? Next(EndMark) : First();
const cMark *BeginMark = EndMark ? Next(EndMark) : First();
if (BeginMark && EndMark && BeginMark->Position() == EndMark->Position()) {
while (cMark *NextMark = Next(BeginMark)) {
while (const cMark *NextMark = Next(BeginMark)) {
if (BeginMark->Position() == NextMark->Position()) { // skip Begin/End at the same position
if (!(BeginMark = Next(NextMark)))
break;
@@ -2190,13 +2183,13 @@ cMark *cMarks::GetNextBegin(cMark *EndMark)
return BeginMark;
}
cMark *cMarks::GetNextEnd(cMark *BeginMark)
const cMark *cMarks::GetNextEnd(const cMark *BeginMark) const
{
if (!BeginMark)
return NULL;
cMark *EndMark = Next(BeginMark);
const cMark *EndMark = Next(BeginMark);
if (EndMark && BeginMark && BeginMark->Position() == EndMark->Position()) {
while (cMark *NextMark = Next(EndMark)) {
while (const cMark *NextMark = Next(EndMark)) {
if (EndMark->Position() == NextMark->Position()) { // skip End/Begin at the same position
if (!(EndMark = Next(NextMark)))
break;
@@ -2208,12 +2201,11 @@ cMark *cMarks::GetNextEnd(cMark *BeginMark)
return EndMark;
}
int cMarks::GetNumSequences(void)
int cMarks::GetNumSequences(void) const
{
cMutexLock MutexLock(this);
int NumSequences = 0;
if (cMark *BeginMark = GetNextBegin()) {
while (cMark *EndMark = GetNextEnd(BeginMark)) {
if (const cMark *BeginMark = GetNextBegin()) {
while (const cMark *EndMark = GetNextEnd(BeginMark)) {
NumSequences++;
BeginMark = GetNextBegin(EndMark);
}
@@ -2403,7 +2395,8 @@ void cIndexFileGenerator::Action(void)
if (FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) {
RecordingInfo.SetFramesPerSecond(FrameDetector.FramesPerSecond());
RecordingInfo.Write();
Recordings.UpdateByName(recordingName);
LOCK_RECORDINGS_WRITE;
Recordings->UpdateByName(recordingName);
}
}
Skins.QueueMessage(mtInfo, tr("Index file regeneration complete"));