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
|
- 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.
|
||||||
|
153
dvbplayer.c
153
dvbplayer.c
@ -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,10 +430,10 @@ 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))
|
||||||
@ -345,27 +449,40 @@ void cDvbPlayer::Action(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
readIndex = Index;
|
readIndex = Index;
|
||||||
r = ReadFrame(replayFile, b, Length, sizeof(b));
|
AudioTrack = 0;
|
||||||
// must call StripAudioPackets() here because the buffer is not emptied
|
// must clear all audio packets because the buffer is not emptied
|
||||||
// when falling back from "fast forward" to "play" (see above)
|
// when falling back from "fast forward" to "play" (see above)
|
||||||
StripAudioPackets(b, r);
|
|
||||||
}
|
}
|
||||||
else if (index) {
|
else if (index) {
|
||||||
uchar FileNumber;
|
uchar FileNumber;
|
||||||
int FileOffset, Length;
|
int FileOffset;
|
||||||
readIndex++;
|
readIndex++;
|
||||||
if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) {
|
if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) {
|
||||||
readIndex = -1;
|
readIndex = -1;
|
||||||
eof = true;
|
eof = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
r = ReadFrame(replayFile, b, Length, sizeof(b));
|
AudioTrack = audioTrack;
|
||||||
StripAudioPackets(b, r, audioTrack);
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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)
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
if (Count < 0)
|
||||||
|
data = (uchar *)Data;
|
||||||
|
else {
|
||||||
data = new uchar[count];
|
data = new uchar[count];
|
||||||
if (data)
|
if (data)
|
||||||
memcpy(data, Data, count);
|
memcpy(data, Data, count);
|
||||||
else
|
else
|
||||||
esyslog("ERROR: can't allocate frame buffer (count=%d)", count);
|
esyslog("ERROR: can't allocate frame buffer (count=%d)", count);
|
||||||
|
}
|
||||||
next = NULL;
|
next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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; }
|
||||||
|
Loading…
Reference in New Issue
Block a user