The info file of an edited recording now contains the number of errors in the edited version

This commit is contained in:
Klaus Schmidinger 2024-09-19 20:21:58 +02:00
parent 3d6b31b115
commit 292af5d4f4
7 changed files with 63 additions and 21 deletions

View File

@ -10009,3 +10009,8 @@ Video Disk Recorder Revision History
APIVERSNUM is now 30004.
- When the index file of a recording is regenerated, errors in the recording are now
stored in the index file.
- The info file of an edited recording now contains the number of errors in the edited
version. Note that this applies only to recordings that have errors stored in their
index file. If errors are not stored in the index file, the edited version will have
its number of errors set to zero.
APIVERSNUM is now 30005.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.h 5.21 2024/09/19 09:49:02 kls Exp $
* $Id: config.h 5.22 2024/09/19 20:21:58 kls Exp $
*/
#ifndef __CONFIG_H
@ -27,8 +27,8 @@
// The plugin API's version number:
#define APIVERSION "4"
#define APIVERSNUM 30004
#define APIVERSION "5"
#define APIVERSNUM 30005
// When loading plugins, VDR searches files by their APIVERSION, which
// is different from VDRVERSION. APIVERSION is a plain number, incremented

View File

@ -4,12 +4,11 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: cutter.c 5.1 2022/11/06 11:25:13 kls Exp $
* $Id: cutter.c 5.2 2024/09/19 20:21:58 kls Exp $
*/
#include "cutter.h"
#include "menu.h"
#include "recording.h"
#include "remux.h"
#include "videodir.h"
@ -232,6 +231,10 @@ private:
int numSequences;
off_t maxVideoFileSize;
off_t fileSize;
int frameErrors;
time_t lastErrorHandling;
cString editedRecordingName;
cRecordingInfo *recordingInfo;
bool suspensionLogged;
int sequence; // cutting sequence
int delta; // time between two frames (PTS ticks)
@ -246,7 +249,7 @@ private:
cPatPmtParser patPmtParser;
bool Throttled(void);
bool SwitchFile(bool Force = false);
bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length);
bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length, bool *Errors = NULL, bool *Missing = NULL);
bool FramesAreEqual(int Index1, int Index2);
void GetPendingPackets(uchar *Buffer, int &Length, int Index);
// Gather all non-video TS packets from Index upward that either belong to
@ -254,15 +257,16 @@ private:
// and add them to the end of the given Data.
bool FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut);
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
void HandleErrors(bool Force = false);
protected:
virtual void Action(void);
public:
cCuttingThread(const char *FromFileName, const char *ToFileName);
cCuttingThread(const char *FromFileName, const char *ToFileName, cRecordingInfo *RecordingInfo);
virtual ~cCuttingThread();
const char *Error(void) { return error; }
};
cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName, cRecordingInfo *RecordingInfo)
:cThread("video cutting", true)
{
error = NULL;
@ -274,6 +278,10 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
framesPerSecond = Recording.FramesPerSecond();
suspensionLogged = false;
fileSize = 0;
frameErrors = 0;
lastErrorHandling = 0;
editedRecordingName = ToFileName;
recordingInfo = RecordingInfo;
sequence = 0;
delta = int(round(PTSTICKS / framesPerSecond));
lastVidPts = -1;
@ -328,11 +336,11 @@ bool cCuttingThread::Throttled(void)
return false;
}
bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length)
bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length, bool *Errors, bool *Missing)
{
uint16_t FileNumber;
off_t FileOffset;
if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) {
if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length, Errors, Missing)) {
fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
if (fromFile) {
fromFile->SetReadAhead(MEGABYTE(20));
@ -555,7 +563,9 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) {
bool Independent;
int Length;
if (LoadFrame(Index, Buffer, Independent, Length)) {
bool Errors;
bool Missing;
if (LoadFrame(Index, Buffer, Independent, Length, &Errors, &Missing)) {
// Make sure there is enough disk space:
AssertFreeDiskSpace(-1);
bool CutIn = !SeamlessBegin && Index == BeginIndex;
@ -572,10 +582,12 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
return false;
}
// Write index:
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize)) {
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize, Errors, Missing)) {
error = "toIndex";
return false;
}
frameErrors += Errors + Missing;
HandleErrors();
// Write data:
if (toFile->Write(Buffer, Length) < 0) {
error = "safe_write";
@ -596,6 +608,21 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
return true;
}
#define ERROR_HANDLING_DELTA 1 // seconds between handling errors
void cCuttingThread::HandleErrors(bool Force)
{
if (Force || time(NULL) - lastErrorHandling >= ERROR_HANDLING_DELTA) {
if (frameErrors > recordingInfo->Errors()) {
recordingInfo->SetErrors(frameErrors);
recordingInfo->Write();
LOCK_RECORDINGS_WRITE;
Recordings->UpdateByName(editedRecordingName);
}
lastErrorHandling = time(NULL);
}
}
void cCuttingThread::Action(void)
{
if (cMark *BeginMark = fromMarks.GetNextBegin()) {
@ -634,6 +661,7 @@ void cCuttingThread::Action(void)
}
}
}
HandleErrors(true);
}
else
esyslog("no editing marks found!");
@ -642,6 +670,7 @@ void cCuttingThread::Action(void)
// --- cCutter ---------------------------------------------------------------
cCutter::cCutter(const char *FileName)
:recordingInfo(FileName)
{
cuttingThread = NULL;
error = false;
@ -676,9 +705,11 @@ bool cCutter::Start(void)
if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different!
cRecordingUserCommand::InvokeCommand(RUC_EDITINGRECORDING, editedVersionName, originalVersionName);
if (cVideoDirectory::RemoveVideoFile(editedVersionName) && MakeDirs(editedVersionName, true)) {
Recording.WriteInfo(editedVersionName);
recordingInfo.Read();
recordingInfo.SetErrors(0);
recordingInfo.SetFileName(editedVersionName);
SetRecordingTimerId(editedVersionName, cString::sprintf("%d@%s", 0, Setup.SVDRPHostName));
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName);
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName, &recordingInfo);
return true;
}
}

View File

@ -4,12 +4,13 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: cutter.h 3.1 2013/10/05 11:34:55 kls Exp $
* $Id: cutter.h 5.1 2024/09/19 20:21:58 kls Exp $
*/
#ifndef __CUTTER_H
#define __CUTTER_H
#include "recording.h"
#include "thread.h"
#include "tools.h"
@ -19,6 +20,7 @@ class cCutter {
private:
cString originalVersionName;
cString editedVersionName;
cRecordingInfo recordingInfo;
cCuttingThread *cuttingThread;
bool error;
public:

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 5.33 2024/09/19 12:06:55 kls Exp $
* $Id: recording.c 5.34 2024/09/19 20:21:58 kls Exp $
*/
#include "recording.h"
@ -2946,7 +2946,7 @@ bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset,
return f >= 0;
}
bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent, int *Length)
bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent, int *Length, bool *Errors, bool *Missing)
{
if (CatchUp(Index)) {
if (Index >= 0 && Index <= last) {
@ -2966,6 +2966,10 @@ bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *I
else
*Length = -1;
}
if (Errors)
*Errors = index[Index].errors;
if (Missing)
*Missing = index[Index].missing;
return true;
}
}

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 5.10 2024/09/19 09:49:02 kls Exp $
* $Id: recording.h 5.11 2024/09/19 20:21:58 kls Exp $
*/
#ifndef __RECORDING_H
@ -504,7 +504,7 @@ public:
~cIndexFile();
bool Ok(void) { return index != NULL; }
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset, bool Errors = false, bool Missing = false);
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL);
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL, bool *Errors = NULL, bool *Missing = NULL);
const cErrors *GetErrors(void);
///< Returns the frame indexes of errors in the recording (if any).
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL);

4
vdr.5
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
.\" $Id: vdr.5 5.10 2024/09/09 10:58:55 kls Exp $
.\" $Id: vdr.5 5.11 2024/09/19 20:21:58 kls Exp $
.\"
.TH vdr 5 "27 Dec 2021" "2.7" "Video Disk Recorder Files"
.SH NAME
@ -824,7 +824,7 @@ l l.
The 'O' tag contains the number of errors that occurred during recording.
If it is zero, the recording can be safely considered error free. The higher the value,
the more damaged the recording is.
If this is an edited recording, the number of errors is that of the original
If this is an edited recording, the number of errors is that of the edited
recording.
.SS RESUME
The file \fIresume\fR (if present in a recording directory) contains