mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Implemented non blocking file reader for cDvbPlayer
This commit is contained in:
parent
413b22dc63
commit
cdcf28b051
3
HISTORY
3
HISTORY
@ -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.
|
||||
|
199
dvbplayer.c
199
dvbplayer.c
@ -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());
|
||||
}
|
||||
|
||||
|
18
ringbuffer.c
18
ringbuffer.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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; }
|
||||
|
Loading…
Reference in New Issue
Block a user