diff options
| author | Klaus Schmidinger <vdr@tvdr.de> | 2003-01-19 15:43:58 +0100 | 
|---|---|---|
| committer | Klaus Schmidinger <vdr@tvdr.de> | 2003-01-19 15:43:58 +0100 | 
| commit | cdcf28b051c71a84632384346bb7a2a481a95373 (patch) | |
| tree | 81fff0617b64c3aeb98f99c71d875075536669ae | |
| parent | 413b22dc636dbd78f7acd0dd816cde933fe6a78d (diff) | |
| download | vdr-cdcf28b051c71a84632384346bb7a2a481a95373.tar.gz vdr-cdcf28b051c71a84632384346bb7a2a481a95373.tar.bz2 | |
Implemented non blocking file reader for cDvbPlayer
| -rw-r--r-- | HISTORY | 3 | ||||
| -rw-r--r-- | dvbplayer.c | 199 | ||||
| -rw-r--r-- | ringbuffer.c | 18 | ||||
| -rw-r--r-- | ringbuffer.h | 5 | 
4 files changed, 178 insertions, 47 deletions
| @@ -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. diff --git a/dvbplayer.c b/dvbplayer.c index 2d9cc1ce..1d140ea9 100644 --- a/dvbplayer.c +++ b/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 if (index) { -                    uchar FileNumber; -                    int FileOffset, Length; -                    readIndex++; -                    if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) { -                       readIndex = -1; -                       eof = true; -                       continue; +                    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;                         } -                    r = ReadFrame(replayFile, b, Length, sizeof(b)); -                    StripAudioPackets(b, r, audioTrack); +                    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)                      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());  } diff --git a/ringbuffer.c b/ringbuffer.c index b57b23e2..ed92be59 100644 --- a/ringbuffer.c +++ b/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;  } diff --git a/ringbuffer.h b/ringbuffer.h index 660a08b0..f96143c9 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -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; } | 
