Implemented non blocking file reader for cDvbPlayer

This commit is contained in:
Klaus Schmidinger 2003-01-19 15:43:58 +01:00
parent 413b22dc63
commit cdcf28b051
4 changed files with 178 additions and 47 deletions

View File

@ -1922,3 +1922,6 @@ Video Disk Recorder Revision History
- Added 'Hrvatska radiotelevizija' and 'RTV Slovenija' to ca.conf (thanks to - Added 'Hrvatska radiotelevizija' and 'RTV Slovenija' to ca.conf (thanks to
Paul Gohn). Paul Gohn).
- Implemented actual user input for CAM enquiry menus. - Implemented actual user input for CAM enquiry menus.
- Since disk file systems apparently don't honor the O_NONBLOCK flag to read from
a file in non-blocking mode the cDvbPlayer now uses a non blocking file reader
class to make sure replay remains smooth even under heavy system load.

View File

@ -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: dvbplayer.c 1.16 2002/11/03 11:23:47 kls Exp $ * $Id: dvbplayer.c 1.17 2003/01/19 15:43:58 kls Exp $
*/ */
#include "dvbplayer.h" #include "dvbplayer.h"
@ -69,6 +69,103 @@ int cBackTrace::Get(bool Forward)
return i; return i;
} }
// --- cNonBlockingFileReader ------------------------------------------------
class cNonBlockingFileReader : public cThread {
private:
int f;
uchar *buffer;
int wanted;
int length;
bool hasData;
bool active;
cMutex mutex;
cCondVar newSet;
protected:
void Action(void);
public:
cNonBlockingFileReader(void);
~cNonBlockingFileReader();
void Clear(void);
int Read(int FileHandle, uchar *Buffer, int Length);
bool Reading(void) { return buffer; }
};
cNonBlockingFileReader::cNonBlockingFileReader(void)
{
f = -1;
buffer = NULL;
wanted = length = 0;
hasData = false;
active = false;
Start();
}
cNonBlockingFileReader::~cNonBlockingFileReader()
{
active = false;
newSet.Broadcast();
Cancel(3);
free(buffer);
}
void cNonBlockingFileReader::Clear(void)
{
cMutexLock MutexLock(&mutex);
f = -1;
buffer = NULL;
wanted = length = 0;
hasData = false;
newSet.Broadcast();
}
int cNonBlockingFileReader::Read(int FileHandle, uchar *Buffer, int Length)
{
if (hasData && buffer) {
if (buffer != Buffer) {
esyslog("ERROR: cNonBlockingFileReader::Read() called with different buffer!");
errno = EINVAL;
return -1;
}
buffer = NULL;
return length;
}
if (!buffer) {
f = FileHandle;
buffer = Buffer;
wanted = Length;
length = 0;
hasData = false;
newSet.Broadcast();
}
errno = EAGAIN;
return -1;
}
void cNonBlockingFileReader::Action(void)
{
dsyslog("non blocking file reader thread started (pid=%d)", getpid());
active = true;
while (active) {
cMutexLock MutexLock(&mutex);
if (!hasData && f >= 0 && buffer) {
int r = safe_read(f, buffer + length, wanted - length);
if (r >= 0) {
length += r;
if (!r || length == wanted) // r == 0 means EOF
hasData = true;
}
else if (r < 0 && FATALERRNO) {
LOG_ERROR;
length = r; // this will forward the error status to the caller
hasData = true;
}
}
newSet.TimedWait(mutex, 1000);
}
dsyslog("non blocking file reader thread ended (pid=%d)", getpid());
}
// --- cDvbPlayer ------------------------------------------------------------ // --- cDvbPlayer ------------------------------------------------------------
//XXX+ also used in recorder.c - find a better place??? //XXX+ also used in recorder.c - find a better place???
@ -84,6 +181,7 @@ private:
enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill }; enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
enum ePlayDirs { pdForward, pdBackward }; enum ePlayDirs { pdForward, pdBackward };
static int Speeds[]; static int Speeds[];
cNonBlockingFileReader *nonBlockingFileReader;
cRingBufferFrame *ringBuffer; cRingBufferFrame *ringBuffer;
cBackTrace *backTrace; cBackTrace *backTrace;
cFileName *fileName; cFileName *fileName;
@ -135,6 +233,7 @@ int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
cDvbPlayer::cDvbPlayer(const char *FileName) cDvbPlayer::cDvbPlayer(const char *FileName)
{ {
nonBlockingFileReader = NULL;
ringBuffer = NULL; ringBuffer = NULL;
backTrace = NULL; backTrace = NULL;
index = NULL; index = NULL;
@ -198,7 +297,9 @@ void cDvbPlayer::TrickSpeed(int Increment)
void cDvbPlayer::Empty(void) void cDvbPlayer::Empty(void)
{ {
Lock(); LOCK_THREAD;
if (nonBlockingFileReader)
nonBlockingFileReader->Clear();
if ((readIndex = backTrace->Get(playDir == pdForward)) < 0) if ((readIndex = backTrace->Get(playDir == pdForward)) < 0)
readIndex = writeIndex; readIndex = writeIndex;
readFrame = NULL; readFrame = NULL;
@ -206,7 +307,6 @@ void cDvbPlayer::Empty(void)
ringBuffer->Clear(); ringBuffer->Clear();
backTrace->Clear(); backTrace->Clear();
DeviceClear(); DeviceClear();
Unlock();
} }
void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except) void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except)
@ -305,7 +405,7 @@ void cDvbPlayer::Action(void)
{ {
dsyslog("dvbplayer thread started (pid=%d)", getpid()); dsyslog("dvbplayer thread started (pid=%d)", getpid());
uchar b[MAXFRAMESIZE]; uchar *b = NULL;
const uchar *p = NULL; const uchar *p = NULL;
int pc = 0; int pc = 0;
@ -313,6 +413,10 @@ void cDvbPlayer::Action(void)
if (readIndex >= 0) if (readIndex >= 0)
isyslog("resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true)); isyslog("resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true));
nonBlockingFileReader = new cNonBlockingFileReader;
int Length = 0;
int AudioTrack = 0; // -1 = any, 0 = none, >0 = audioTrack
running = true; running = true;
while (running && (NextFile() || readIndex >= 0 || ringBuffer->Available())) { while (running && (NextFile() || readIndex >= 0 || ringBuffer->Available())) {
cPoller Poller; cPoller Poller;
@ -326,46 +430,59 @@ void cDvbPlayer::Action(void)
if (!readFrame && (replayFile >= 0 || readIndex >= 0)) { if (!readFrame && (replayFile >= 0 || readIndex >= 0)) {
if (playMode != pmStill) { if (playMode != pmStill) {
int r = 0; if (!nonBlockingFileReader->Reading()) {
if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) { if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
uchar FileNumber; uchar FileNumber;
int FileOffset, Length; int FileOffset;
int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true); int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
if (Index >= 0) { if (Index >= 0) {
if (!NextFile(FileNumber, FileOffset)) if (!NextFile(FileNumber, FileOffset))
continue;
}
else {
// can't call Play() here, because those functions may only be
// called from the foreground thread - and we also don't need
// to empty the buffer here
DevicePlay();
playMode = pmPlay;
playDir = pdForward;
continue; continue;
}
readIndex = Index;
AudioTrack = 0;
// must clear all audio packets because the buffer is not emptied
// when falling back from "fast forward" to "play" (see above)
} }
else { else if (index) {
// can't call Play() here, because those functions may only be uchar FileNumber;
// called from the foreground thread - and we also don't need int FileOffset;
// to empty the buffer here readIndex++;
DevicePlay(); if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) {
playMode = pmPlay; readIndex = -1;
playDir = pdForward; eof = true;
continue; continue;
}
AudioTrack = audioTrack;
} }
readIndex = Index; else { // allows replay even if the index file is missing
r = ReadFrame(replayFile, b, Length, sizeof(b)); Length = MAXFRAMESIZE;
// must call StripAudioPackets() here because the buffer is not emptied AudioTrack = -1;
// when falling back from "fast forward" to "play" (see above) }
StripAudioPackets(b, r); if (Length == -1)
Length = MAXFRAMESIZE; // this means we read up to EOF (see cIndex)
else if (Length > MAXFRAMESIZE) {
esyslog("ERROR: frame larger than buffer (%d > %d)", Length, MAXFRAMESIZE);
Length = MAXFRAMESIZE;
}
b = MALLOC(uchar, Length);
} }
else if (index) { int r = nonBlockingFileReader->Read(replayFile, b, Length);
uchar FileNumber; if (r > 0) {
int FileOffset, Length; if (AudioTrack >= 0)
readIndex++; StripAudioPackets(b, r, AudioTrack);
if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) { readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer
readIndex = -1; b = NULL;
eof = true;
continue;
}
r = ReadFrame(replayFile, b, Length, sizeof(b));
StripAudioPackets(b, r, audioTrack);
} }
else // allows replay even if the index file is missing
r = read(replayFile, b, sizeof(b));
if (r > 0)
readFrame = new cFrame(b, r, ftUnknown, readIndex);
else if (r == 0) else if (r == 0)
eof = true; eof = true;
else if (r < 0 && FATALERRNO) { else if (r < 0 && FATALERRNO) {
@ -422,6 +539,10 @@ void cDvbPlayer::Action(void)
} }
active = running = false; active = running = false;
cNonBlockingFileReader *nbfr = nonBlockingFileReader;
nonBlockingFileReader = NULL;
delete nbfr;
dsyslog("dvbplayer thread ended (pid=%d)", getpid()); dsyslog("dvbplayer thread ended (pid=%d)", getpid());
} }

View File

@ -7,7 +7,7 @@
* Parts of this file were inspired by the 'ringbuffy.c' from the * Parts of this file were inspired by the 'ringbuffy.c' from the
* LinuxDVB driver (see linuxtv.org). * LinuxDVB driver (see linuxtv.org).
* *
* $Id: ringbuffer.c 1.10 2002/07/28 12:47:32 kls Exp $ * $Id: ringbuffer.c 1.11 2003/01/19 15:03:00 kls Exp $
*/ */
#include "ringbuffer.h" #include "ringbuffer.h"
@ -174,14 +174,18 @@ int cRingBufferLinear::Get(uchar *Data, int Count)
cFrame::cFrame(const uchar *Data, int Count, eFrameType Type, int Index) cFrame::cFrame(const uchar *Data, int Count, eFrameType Type, int Index)
{ {
count = Count; count = abs(Count);
type = Type; type = Type;
index = Index; index = Index;
data = new uchar[count]; if (Count < 0)
if (data) data = (uchar *)Data;
memcpy(data, Data, count); else {
else data = new uchar[count];
esyslog("ERROR: can't allocate frame buffer (count=%d)", count); if (data)
memcpy(data, Data, count);
else
esyslog("ERROR: can't allocate frame buffer (count=%d)", count);
}
next = NULL; next = NULL;
} }

View File

@ -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: ringbuffer.h 1.7 2002/08/04 10:27:30 kls Exp $ * $Id: ringbuffer.h 1.8 2003/01/19 15:03:00 kls Exp $
*/ */
#ifndef __RINGBUFFER_H #ifndef __RINGBUFFER_H
@ -69,6 +69,9 @@ private:
int index; int index;
public: public:
cFrame(const uchar *Data, int Count, eFrameType = ftUnknown, int Index = -1); cFrame(const uchar *Data, int Count, eFrameType = ftUnknown, int Index = -1);
///< Creates a new cFrame object.
///< If Count is negative, the cFrame object will take ownership of the given
///< Data. Otherwise it will allocate Count bytes of memory and copy Data.
~cFrame(); ~cFrame();
const uchar *Data(void) const { return data; } const uchar *Data(void) const { return data; }
int Count(void) const { return count; } int Count(void) const { return count; }