mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
The info file of an edited recording now contains the number of errors in the edited version
This commit is contained in:
parent
3d6b31b115
commit
292af5d4f4
5
HISTORY
5
HISTORY
@ -10009,3 +10009,8 @@ Video Disk Recorder Revision History
|
|||||||
APIVERSNUM is now 30004.
|
APIVERSNUM is now 30004.
|
||||||
- When the index file of a recording is regenerated, errors in the recording are now
|
- When the index file of a recording is regenerated, errors in the recording are now
|
||||||
stored in the index file.
|
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.
|
||||||
|
6
config.h
6
config.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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
|
#ifndef __CONFIG_H
|
||||||
@ -27,8 +27,8 @@
|
|||||||
|
|
||||||
// The plugin API's version number:
|
// The plugin API's version number:
|
||||||
|
|
||||||
#define APIVERSION "4"
|
#define APIVERSION "5"
|
||||||
#define APIVERSNUM 30004
|
#define APIVERSNUM 30005
|
||||||
|
|
||||||
// When loading plugins, VDR searches files by their APIVERSION, which
|
// When loading plugins, VDR searches files by their APIVERSION, which
|
||||||
// is different from VDRVERSION. APIVERSION is a plain number, incremented
|
// is different from VDRVERSION. APIVERSION is a plain number, incremented
|
||||||
|
53
cutter.c
53
cutter.c
@ -4,12 +4,11 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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 "cutter.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
#include "recording.h"
|
|
||||||
#include "remux.h"
|
#include "remux.h"
|
||||||
#include "videodir.h"
|
#include "videodir.h"
|
||||||
|
|
||||||
@ -232,6 +231,10 @@ private:
|
|||||||
int numSequences;
|
int numSequences;
|
||||||
off_t maxVideoFileSize;
|
off_t maxVideoFileSize;
|
||||||
off_t fileSize;
|
off_t fileSize;
|
||||||
|
int frameErrors;
|
||||||
|
time_t lastErrorHandling;
|
||||||
|
cString editedRecordingName;
|
||||||
|
cRecordingInfo *recordingInfo;
|
||||||
bool suspensionLogged;
|
bool suspensionLogged;
|
||||||
int sequence; // cutting sequence
|
int sequence; // cutting sequence
|
||||||
int delta; // time between two frames (PTS ticks)
|
int delta; // time between two frames (PTS ticks)
|
||||||
@ -246,7 +249,7 @@ private:
|
|||||||
cPatPmtParser patPmtParser;
|
cPatPmtParser patPmtParser;
|
||||||
bool Throttled(void);
|
bool Throttled(void);
|
||||||
bool SwitchFile(bool Force = false);
|
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);
|
bool FramesAreEqual(int Index1, int Index2);
|
||||||
void GetPendingPackets(uchar *Buffer, int &Length, int Index);
|
void GetPendingPackets(uchar *Buffer, int &Length, int Index);
|
||||||
// Gather all non-video TS packets from Index upward that either belong to
|
// 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.
|
// 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 FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut);
|
||||||
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
|
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
|
||||||
|
void HandleErrors(bool Force = false);
|
||||||
protected:
|
protected:
|
||||||
virtual void Action(void);
|
virtual void Action(void);
|
||||||
public:
|
public:
|
||||||
cCuttingThread(const char *FromFileName, const char *ToFileName);
|
cCuttingThread(const char *FromFileName, const char *ToFileName, cRecordingInfo *RecordingInfo);
|
||||||
virtual ~cCuttingThread();
|
virtual ~cCuttingThread();
|
||||||
const char *Error(void) { return error; }
|
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)
|
:cThread("video cutting", true)
|
||||||
{
|
{
|
||||||
error = NULL;
|
error = NULL;
|
||||||
@ -274,6 +278,10 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
|||||||
framesPerSecond = Recording.FramesPerSecond();
|
framesPerSecond = Recording.FramesPerSecond();
|
||||||
suspensionLogged = false;
|
suspensionLogged = false;
|
||||||
fileSize = 0;
|
fileSize = 0;
|
||||||
|
frameErrors = 0;
|
||||||
|
lastErrorHandling = 0;
|
||||||
|
editedRecordingName = ToFileName;
|
||||||
|
recordingInfo = RecordingInfo;
|
||||||
sequence = 0;
|
sequence = 0;
|
||||||
delta = int(round(PTSTICKS / framesPerSecond));
|
delta = int(round(PTSTICKS / framesPerSecond));
|
||||||
lastVidPts = -1;
|
lastVidPts = -1;
|
||||||
@ -328,11 +336,11 @@ bool cCuttingThread::Throttled(void)
|
|||||||
return false;
|
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;
|
uint16_t FileNumber;
|
||||||
off_t FileOffset;
|
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);
|
fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
|
||||||
if (fromFile) {
|
if (fromFile) {
|
||||||
fromFile->SetReadAhead(MEGABYTE(20));
|
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++) {
|
for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) {
|
||||||
bool Independent;
|
bool Independent;
|
||||||
int Length;
|
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:
|
// Make sure there is enough disk space:
|
||||||
AssertFreeDiskSpace(-1);
|
AssertFreeDiskSpace(-1);
|
||||||
bool CutIn = !SeamlessBegin && Index == BeginIndex;
|
bool CutIn = !SeamlessBegin && Index == BeginIndex;
|
||||||
@ -572,10 +582,12 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Write index:
|
// Write index:
|
||||||
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize)) {
|
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize, Errors, Missing)) {
|
||||||
error = "toIndex";
|
error = "toIndex";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
frameErrors += Errors + Missing;
|
||||||
|
HandleErrors();
|
||||||
// Write data:
|
// Write data:
|
||||||
if (toFile->Write(Buffer, Length) < 0) {
|
if (toFile->Write(Buffer, Length) < 0) {
|
||||||
error = "safe_write";
|
error = "safe_write";
|
||||||
@ -596,6 +608,21 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
|
|||||||
return true;
|
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)
|
void cCuttingThread::Action(void)
|
||||||
{
|
{
|
||||||
if (cMark *BeginMark = fromMarks.GetNextBegin()) {
|
if (cMark *BeginMark = fromMarks.GetNextBegin()) {
|
||||||
@ -634,6 +661,7 @@ void cCuttingThread::Action(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
HandleErrors(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
esyslog("no editing marks found!");
|
esyslog("no editing marks found!");
|
||||||
@ -642,6 +670,7 @@ void cCuttingThread::Action(void)
|
|||||||
// --- cCutter ---------------------------------------------------------------
|
// --- cCutter ---------------------------------------------------------------
|
||||||
|
|
||||||
cCutter::cCutter(const char *FileName)
|
cCutter::cCutter(const char *FileName)
|
||||||
|
:recordingInfo(FileName)
|
||||||
{
|
{
|
||||||
cuttingThread = NULL;
|
cuttingThread = NULL;
|
||||||
error = false;
|
error = false;
|
||||||
@ -676,9 +705,11 @@ bool cCutter::Start(void)
|
|||||||
if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different!
|
if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different!
|
||||||
cRecordingUserCommand::InvokeCommand(RUC_EDITINGRECORDING, editedVersionName, originalVersionName);
|
cRecordingUserCommand::InvokeCommand(RUC_EDITINGRECORDING, editedVersionName, originalVersionName);
|
||||||
if (cVideoDirectory::RemoveVideoFile(editedVersionName) && MakeDirs(editedVersionName, true)) {
|
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));
|
SetRecordingTimerId(editedVersionName, cString::sprintf("%d@%s", 0, Setup.SVDRPHostName));
|
||||||
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName);
|
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName, &recordingInfo);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
cutter.h
4
cutter.h
@ -4,12 +4,13 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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
|
#ifndef __CUTTER_H
|
||||||
#define __CUTTER_H
|
#define __CUTTER_H
|
||||||
|
|
||||||
|
#include "recording.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ class cCutter {
|
|||||||
private:
|
private:
|
||||||
cString originalVersionName;
|
cString originalVersionName;
|
||||||
cString editedVersionName;
|
cString editedVersionName;
|
||||||
|
cRecordingInfo recordingInfo;
|
||||||
cCuttingThread *cuttingThread;
|
cCuttingThread *cuttingThread;
|
||||||
bool error;
|
bool error;
|
||||||
public:
|
public:
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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"
|
#include "recording.h"
|
||||||
@ -2946,7 +2946,7 @@ bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset,
|
|||||||
return f >= 0;
|
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 (CatchUp(Index)) {
|
||||||
if (Index >= 0 && Index <= last) {
|
if (Index >= 0 && Index <= last) {
|
||||||
@ -2966,6 +2966,10 @@ bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *I
|
|||||||
else
|
else
|
||||||
*Length = -1;
|
*Length = -1;
|
||||||
}
|
}
|
||||||
|
if (Errors)
|
||||||
|
*Errors = index[Index].errors;
|
||||||
|
if (Missing)
|
||||||
|
*Missing = index[Index].missing;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* 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
|
#ifndef __RECORDING_H
|
||||||
@ -504,7 +504,7 @@ public:
|
|||||||
~cIndexFile();
|
~cIndexFile();
|
||||||
bool Ok(void) { return index != NULL; }
|
bool Ok(void) { return index != NULL; }
|
||||||
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset, bool Errors = false, bool Missing = false);
|
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);
|
const cErrors *GetErrors(void);
|
||||||
///< Returns the frame indexes of errors in the recording (if any).
|
///< 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);
|
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL);
|
||||||
|
4
vdr.5
4
vdr.5
@ -8,7 +8,7 @@
|
|||||||
.\" License as specified in the file COPYING that comes with the
|
.\" License as specified in the file COPYING that comes with the
|
||||||
.\" vdr distribution.
|
.\" 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"
|
.TH vdr 5 "27 Dec 2021" "2.7" "Video Disk Recorder Files"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
@ -824,7 +824,7 @@ l l.
|
|||||||
The 'O' tag contains the number of errors that occurred during recording.
|
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,
|
If it is zero, the recording can be safely considered error free. The higher the value,
|
||||||
the more damaged the recording is.
|
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.
|
recording.
|
||||||
.SS RESUME
|
.SS RESUME
|
||||||
The file \fIresume\fR (if present in a recording directory) contains
|
The file \fIresume\fR (if present in a recording directory) contains
|
||||||
|
Loading…
x
Reference in New Issue
Block a user