mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
Version 1.7.10
- Updated the Italian OSD texts (thanks to Diego Pierotto). - Fixed wrong bracketing in cChannel::SubtitlingType() etc. (thanks to Rolf Ahrenberg). - Fixed not logging changes for channels that have no number (reported by Timothy D. Lenz). - Changed the project's URLs and email to tvdr.de. - Added Lithuanian language translations (thanks to Valdemaras Pipiras). - Updated Chinese language texts (thanks to Nan Feng). - Only checking DVB_API_VERSION to be >=5 in order to stay compileable in case the DVB API version number is increased (the API claims to always be backward compatible). - Fixed saving terminal settings when running in background (thanks to Manuel Reimer). - Fixed cFrameDetector::Analyze() to handle video streams where the frame type is not detectable from the first TS packet of a frame. - Fixed writing the PCR pid into the PMT in cPatPmtGenerator::GeneratePmt() (reported by Rene van den Braken). - Added Slovakian language texts (thanks to Milan Hrala). - Fixed EntriesOnSameFileSystem() to avoid using f_fsid, which may be 0 (thanks to Frank Schmirler). - Fixed starting a recording at an I-frame. - Fixed generating the index for recordings from channels that put a whole GOP into one payload unit. - The index file for TS recordings is now regenerated on-the-fly if a recording is replayed that has no index. This can also be used to re-create a broken index file by manually deleting the index file and then replaying the recording (at least until the index file has been generated). - The cRingBufferLinear::Read() function now returns -1 and sets errno to EAGAIN if the buffer is already full. - Fixed handling DVB subtitles for PES recordings (thanks to Rolf Ahrenberg). - Added the audio id to the call of PlayAudio() in cDevice::PlayTsAudio() (thanks to Andreas Schaefers). - Fixed references to old *.vdr file names in MANUAL (reported by Arthur Konovalov). - Reverted "Removed limitation to PAL resolution from SPU handling" because it cause nothing but trouble. Besides, the core VDR doesn't use this, anyway. - Fixed the default value for "Pause key handling" in the MANUAL (reported by Diego Pierotto).
This commit is contained in:
150
recording.c
150
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 2.17 2009/08/16 10:39:43 kls Exp $
|
||||
* $Id: recording.c 2.18 2009/11/22 11:20:53 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recording.h"
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "i18n.h"
|
||||
#include "interface.h"
|
||||
#include "remux.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "skins.h"
|
||||
#include "tools.h"
|
||||
#include "videodir.h"
|
||||
@@ -1309,6 +1310,124 @@ void cRecordingUserCommand::InvokeCommand(const char *State, const char *Recordi
|
||||
}
|
||||
}
|
||||
|
||||
// --- cIndexFileGenerator ---------------------------------------------------
|
||||
|
||||
#define IFG_BUFFER_SIZE KILOBYTE(100)
|
||||
|
||||
class cIndexFileGenerator : public cThread {
|
||||
private:
|
||||
cString recordingName;
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cIndexFileGenerator(const char *RecordingName);
|
||||
~cIndexFileGenerator();
|
||||
};
|
||||
|
||||
cIndexFileGenerator::cIndexFileGenerator(const char *RecordingName)
|
||||
:cThread("index file generator")
|
||||
,recordingName(RecordingName)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
|
||||
cIndexFileGenerator::~cIndexFileGenerator()
|
||||
{
|
||||
Cancel(3);
|
||||
}
|
||||
|
||||
void cIndexFileGenerator::Action(void)
|
||||
{
|
||||
bool IndexFileComplete = false;
|
||||
bool Rewind = false;
|
||||
cFileName FileName(recordingName, false);
|
||||
cUnbufferedFile *ReplayFile = FileName.Open();
|
||||
cRingBufferLinear Buffer(IFG_BUFFER_SIZE, MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE);
|
||||
cPatPmtParser PatPmtParser;
|
||||
cFrameDetector FrameDetector;
|
||||
cIndexFile IndexFile(recordingName, true);
|
||||
int BufferChunks = KILOBYTE(1); // no need to read a lot at the beginning when parsing PAT/PMT
|
||||
off_t FileSize = 0;
|
||||
off_t FrameOffset = -1;
|
||||
Skins.QueueMessage(mtInfo, tr("Regenerating index file"));
|
||||
while (Running()) {
|
||||
// Rewind input file:
|
||||
if (Rewind) {
|
||||
ReplayFile = FileName.SetOffset(1);
|
||||
Buffer.Clear();
|
||||
Rewind = false;
|
||||
}
|
||||
// Process data:
|
||||
int Length;
|
||||
uchar *Data = Buffer.Get(Length);
|
||||
if (Data) {
|
||||
if (FrameDetector.Synced()) {
|
||||
// Step 3 - generate the index:
|
||||
if (TsPid(Data) == PATPID)
|
||||
FrameOffset = FileSize; // the PAT/PMT is at the beginning of an I-frame
|
||||
int Processed = FrameDetector.Analyze(Data, Length);
|
||||
if (Processed > 0) {
|
||||
if (FrameDetector.NewFrame()) {
|
||||
IndexFile.Write(FrameDetector.IndependentFrame(), FileName.Number(), FrameOffset >= 0 ? FrameOffset : FileSize);
|
||||
FrameOffset = -1;
|
||||
}
|
||||
FileSize += Processed;
|
||||
Buffer.Del(Processed);
|
||||
}
|
||||
}
|
||||
else if (PatPmtParser.Vpid()) {
|
||||
// Step 2 - sync FrameDetector:
|
||||
int Processed = FrameDetector.Analyze(Data, Length);
|
||||
if (Processed > 0) {
|
||||
if (FrameDetector.Synced()) {
|
||||
// Synced FrameDetector, so rewind for actual processing:
|
||||
FrameDetector.Reset();
|
||||
Rewind = true;
|
||||
}
|
||||
Buffer.Del(Processed);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Step 1 - parse PAT/PMT:
|
||||
uchar *p = Data;
|
||||
while (Length >= TS_SIZE) {
|
||||
int Pid = TsPid(p);
|
||||
if (Pid == 0)
|
||||
PatPmtParser.ParsePat(p, TS_SIZE);
|
||||
else if (Pid == PatPmtParser.PmtPid())
|
||||
PatPmtParser.ParsePmt(p, TS_SIZE);
|
||||
Length -= TS_SIZE;
|
||||
p += TS_SIZE;
|
||||
if (PatPmtParser.Vpid()) {
|
||||
// Found Vpid, so rewind to sync FrameDetector:
|
||||
FrameDetector.SetPid(PatPmtParser.Vpid(), PatPmtParser.Vtype());
|
||||
BufferChunks = IFG_BUFFER_SIZE;
|
||||
Rewind = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Buffer.Del(p - Data);
|
||||
}
|
||||
}
|
||||
// Read data:
|
||||
else if (ReplayFile) {
|
||||
int Result = Buffer.Read(ReplayFile, BufferChunks);
|
||||
if (Result == 0) // EOF
|
||||
ReplayFile = FileName.NextFile();
|
||||
}
|
||||
// Recording has been processed:
|
||||
else {
|
||||
IndexFileComplete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Delete the index file if the recording has not been processed entirely:
|
||||
if (IndexFileComplete)
|
||||
Skins.QueueMessage(mtInfo, tr("Index file regeneration complete"));
|
||||
else
|
||||
IndexFile.Delete();
|
||||
}
|
||||
|
||||
// --- cIndexFile ------------------------------------------------------------
|
||||
|
||||
#define INDEXFILESUFFIX "/index"
|
||||
@@ -1343,6 +1462,9 @@ struct tIndexTs {
|
||||
}
|
||||
};
|
||||
|
||||
#define MAXWAITFORINDEXFILE 10 // max. time to wait for the regenerated index file (seconds)
|
||||
#define INDEXFILECHECKINTERVAL 500 // ms between checks for existence of the regenerated index file
|
||||
|
||||
cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording)
|
||||
:resumeFile(FileName, IsPesRecording)
|
||||
{
|
||||
@@ -1352,6 +1474,7 @@ cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording)
|
||||
last = -1;
|
||||
index = NULL;
|
||||
isPesRecording = IsPesRecording;
|
||||
indexFileGenerator = NULL;
|
||||
if (FileName) {
|
||||
const char *Suffix = isPesRecording ? INDEXFILESUFFIX ".vdr" : INDEXFILESUFFIX;
|
||||
fileName = MALLOC(char, strlen(FileName) + strlen(Suffix) + 1);
|
||||
@@ -1360,6 +1483,18 @@ cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording)
|
||||
char *pFileExt = fileName + strlen(fileName);
|
||||
strcpy(pFileExt, Suffix);
|
||||
int delta = 0;
|
||||
if (!Record && access(fileName, R_OK) != 0) {
|
||||
// Index file doesn't exist, so try to regenerate it:
|
||||
if (!isPesRecording) { // sorry, can only do this for TS recordings
|
||||
resumeFile.Delete(); // just in case
|
||||
indexFileGenerator = new cIndexFileGenerator(FileName);
|
||||
// Wait until the index file exists:
|
||||
time_t tmax = time(NULL) + MAXWAITFORINDEXFILE;
|
||||
do {
|
||||
cCondWait::SleepMs(INDEXFILECHECKINTERVAL); // start with a sleep, to give it a head start
|
||||
} while (access(fileName, R_OK) != 0 && time(NULL) < tmax);
|
||||
}
|
||||
}
|
||||
if (access(fileName, R_OK) == 0) {
|
||||
struct stat buf;
|
||||
if (stat(fileName, &buf) == 0) {
|
||||
@@ -1421,6 +1556,7 @@ cIndexFile::~cIndexFile()
|
||||
close(f);
|
||||
free(fileName);
|
||||
free(index);
|
||||
delete indexFileGenerator;
|
||||
}
|
||||
|
||||
void cIndexFile::ConvertFromPes(tIndexTs *IndexTs, int Count)
|
||||
@@ -1598,6 +1734,18 @@ bool cIndexFile::IsStillRecording()
|
||||
return f >= 0;
|
||||
}
|
||||
|
||||
void cIndexFile::Delete(void)
|
||||
{
|
||||
if (fileName) {
|
||||
dsyslog("deleting index file '%s'", fileName);
|
||||
if (f >= 0) {
|
||||
close(f);
|
||||
f = -1;
|
||||
}
|
||||
unlink(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
// --- cFileName -------------------------------------------------------------
|
||||
|
||||
#define MAXFILESPERRECORDINGPES 255
|
||||
|
||||
Reference in New Issue
Block a user