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
Paul Gohn).
- 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
* 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"
@ -69,6 +69,103 @@ int cBackTrace::Get(bool Forward)
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 ------------------------------------------------------------
//XXX+ also used in recorder.c - find a better place???
@ -84,6 +181,7 @@ private:
enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
enum ePlayDirs { pdForward, pdBackward };
static int Speeds[];
cNonBlockingFileReader *nonBlockingFileReader;
cRingBufferFrame *ringBuffer;
cBackTrace *backTrace;
cFileName *fileName;
@ -135,6 +233,7 @@ int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
cDvbPlayer::cDvbPlayer(const char *FileName)
{
nonBlockingFileReader = NULL;
ringBuffer = NULL;
backTrace = NULL;
index = NULL;
@ -198,7 +297,9 @@ void cDvbPlayer::TrickSpeed(int Increment)
void cDvbPlayer::Empty(void)
{
Lock();
LOCK_THREAD;
if (nonBlockingFileReader)
nonBlockingFileReader->Clear();
if ((readIndex = backTrace->Get(playDir == pdForward)) < 0)
readIndex = writeIndex;
readFrame = NULL;
@ -206,7 +307,6 @@ void cDvbPlayer::Empty(void)
ringBuffer->Clear();
backTrace->Clear();
DeviceClear();
Unlock();
}
void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except)
@ -305,7 +405,7 @@ void cDvbPlayer::Action(void)
{
dsyslog("dvbplayer thread started (pid=%d)", getpid());
uchar b[MAXFRAMESIZE];
uchar *b = NULL;
const uchar *p = NULL;
int pc = 0;
@ -313,6 +413,10 @@ void cDvbPlayer::Action(void)
if (readIndex >= 0)
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;
while (running && (NextFile() || readIndex >= 0 || ringBuffer->Available())) {
cPoller Poller;
@ -326,46 +430,59 @@ void cDvbPlayer::Action(void)
if (!readFrame && (replayFile >= 0 || readIndex >= 0)) {
if (playMode != pmStill) {
int r = 0;
if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
uchar FileNumber;
int FileOffset, Length;
int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
if (Index >= 0) {
if (!NextFile(FileNumber, FileOffset))
if (!nonBlockingFileReader->Reading()) {
if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
uchar FileNumber;
int FileOffset;
int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
if (Index >= 0) {
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;
}
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 {
// 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;
else if (index) {
uchar FileNumber;
int FileOffset;
readIndex++;
if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) {
readIndex = -1;
eof = true;
continue;
}
AudioTrack = audioTrack;
}
readIndex = Index;
r = ReadFrame(replayFile, b, Length, sizeof(b));
// must call StripAudioPackets() here because the buffer is not emptied
// when falling back from "fast forward" to "play" (see above)
StripAudioPackets(b, r);
else { // allows replay even if the index file is missing
Length = MAXFRAMESIZE;
AudioTrack = -1;
}
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) {
uchar FileNumber;
int FileOffset, Length;
readIndex++;
if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) {
readIndex = -1;
eof = true;
continue;
}
r = ReadFrame(replayFile, b, Length, sizeof(b));
StripAudioPackets(b, r, audioTrack);
int r = nonBlockingFileReader->Read(replayFile, b, Length);
if (r > 0) {
if (AudioTrack >= 0)
StripAudioPackets(b, r, AudioTrack);
readFrame = new cFrame(b, -r, ftUnknown, readIndex); // hands over b to the ringBuffer
b = NULL;
}
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)
eof = true;
else if (r < 0 && FATALERRNO) {
@ -422,6 +539,10 @@ void cDvbPlayer::Action(void)
}
active = running = false;
cNonBlockingFileReader *nbfr = nonBlockingFileReader;
nonBlockingFileReader = NULL;
delete nbfr;
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
* 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"
@ -174,14 +174,18 @@ int cRingBufferLinear::Get(uchar *Data, int Count)
cFrame::cFrame(const uchar *Data, int Count, eFrameType Type, int Index)
{
count = Count;
count = abs(Count);
type = Type;
index = Index;
data = new uchar[count];
if (data)
memcpy(data, Data, count);
else
esyslog("ERROR: can't allocate frame buffer (count=%d)", count);
if (Count < 0)
data = (uchar *)Data;
else {
data = new uchar[count];
if (data)
memcpy(data, Data, count);
else
esyslog("ERROR: can't allocate frame buffer (count=%d)", count);
}
next = NULL;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* 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
@ -69,6 +69,9 @@ private:
int index;
public:
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();
const uchar *Data(void) const { return data; }
int Count(void) const { return count; }