===================================================================
RCS file: ./RCS/device.c
retrieving revision 1.56
diff -u -r1.56 ./device.c
--- ./device.c	2004/06/19 08:51:05	1.56
+++ ./device.c	2004/10/09 12:53:02
@@ -801,79 +801,67 @@
 
 cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
 {
+  SetDescription("TS buffer on device %d", CardIndex);
   f = File;
-  size = Size / TS_SIZE * TS_SIZE;
   cardIndex = CardIndex;
-  tsRead = tsWrite = 0;
-  buf = (f >= 0 && size >= TS_SIZE) ? MALLOC(uchar, size + TS_SIZE) : NULL;
-  // the '+ TS_SIZE' allocates some extra space for handling packets that got split by a buffer roll-over
-  firstRead = true;
+  active = false;
+  delivered = false;
+  ringBuffer = new cRingBufferLinear(Size, TS_SIZE, true, "TS");
+  ringBuffer->SetTimeouts(100, 100);
+  Start();
 }
 
 cTSBuffer::~cTSBuffer()
 {
-  free(buf);
+  active = false;
+  Cancel(3);
+  delete ringBuffer;
 }
 
-int cTSBuffer::Read(void)
+void cTSBuffer::Action(void)
 {
-  if (buf) {
-     cPoller Poller(f, false);
-     bool repeat;
-     int total = 0;
-     do {
-        repeat = false;
-        if (firstRead || Used() > TS_SIZE || Poller.Poll(100)) { // only wait if there's not enough data in the buffer
-           firstRead = false;
-           if (tsRead == tsWrite)
-              tsRead = tsWrite = 0; // keep the maximum buffer space available
-           if (tsWrite >= size && tsRead > 0)
-              tsWrite = 0;
-           int free = tsRead <= tsWrite ? size - tsWrite : tsRead - tsWrite - 1;
-           if (free > 0) {
-              int r = read(f, buf + tsWrite, free);
-              if (r > 0) {
-                 total += r;
-                 tsWrite += r;
-                 if (tsWrite >= size && tsRead > 0) {
-                    tsWrite = 0;
-                    repeat = true; // read again after a boundary roll-over
-                    }
-                 }
-              }
-           }
-        } while (repeat);
-     return total;
+  if (ringBuffer) {
+     bool firstRead = true;
+     cPoller Poller(f);
+     active = true;
+     for (; active;) {
+         if (firstRead || Poller.Poll(100)) {
+            firstRead = false;
+            int r = ringBuffer->Read(f);
+            if (r < 0 && FATALERRNO) {
+               if (errno == EOVERFLOW)
+                  esyslog("ERROR: driver buffer overflow on device %d", cardIndex);
+               else {
+                  LOG_ERROR;
+                  break;
+                  }
+               }
+            }
+         }
      }
-  return -1;
 }
 
 uchar *cTSBuffer::Get(void)
 {
-  if (Used() >= TS_SIZE) {
-     uchar *p = buf + tsRead;
+  int Count = 0;
+  if (delivered) {
+     ringBuffer->Del(TS_SIZE);
+     delivered = false;
+     }
+  uchar *p = ringBuffer->Get(Count);
+  if (p && Count >= TS_SIZE) {
      if (*p != TS_SYNC_BYTE) {
-        esyslog("ERROR: not sync'ed to TS packet on device %d", cardIndex);
-        int tsMax = tsRead < tsWrite ? tsWrite : size;
-        for (int i = tsRead; i < tsMax; i++) {
-            if (buf[i] == TS_SYNC_BYTE) {
-               esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d", i - tsRead, cardIndex);
-               tsRead = i;
-               return NULL;
+        for (int i = 1; i < Count; i++) {
+            if (p[i] == TS_SYNC_BYTE) {
+               Count = i;
+               break;
                }
             }
-        if ((tsRead = tsMax) >= size)
-           tsRead = 0;
+        ringBuffer->Del(Count);
+        esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d", Count, cardIndex);
         return NULL;
         }
-     if (tsRead + TS_SIZE > size) {
-        // the packet rolled over the buffer boundary, so let's fetch the rest from the beginning (which MUST be there, since Used() >= TS_SIZE)
-        int rest = TS_SIZE - (size - tsRead);
-        memcpy(buf + size, buf, rest);
-        tsRead = rest;
-        }
-     else if ((tsRead += TS_SIZE) >= size)
-        tsRead = 0;
+     delivered = true;
      return p;
      }
   return NULL;
===================================================================
RCS file: ./RCS/device.h
retrieving revision 1.44
diff -u -r1.44 ./device.h
--- ./device.h	2004/06/19 08:50:37	1.44
+++ ./device.h	2004/09/24 14:07:22
@@ -15,6 +15,7 @@
 #include "filter.h"
 #include "nit.h"
 #include "pat.h"
+#include "ringbuffer.h"
 #include "sdt.h"
 #include "sections.h"
 #include "thread.h"
@@ -437,20 +438,17 @@
 /// sure the returned data points to a TS packet and automatically
 /// re-synchronizes after broken packets.
 
-class cTSBuffer {
+class cTSBuffer : public cThread {
 private:
   int f;
-  int size;
   int cardIndex;
-  int tsRead;
-  int tsWrite;
-  uchar *buf;
-  bool firstRead;
-  int Used(void) { return tsRead <= tsWrite ? tsWrite - tsRead : size - tsRead + tsWrite; }
+  bool active;
+  bool delivered;
+  cRingBufferLinear *ringBuffer;
+  virtual void Action(void);
 public:
   cTSBuffer(int File, int Size, int CardIndex);
   ~cTSBuffer();
-  int Read(void);
   uchar *Get(void);
   };
 
===================================================================
RCS file: ./RCS/dvbdevice.c
retrieving revision 1.93
diff -u -r1.93 ./dvbdevice.c
--- ./dvbdevice.c	2004/06/19 09:33:42	1.93
+++ ./dvbdevice.c	2004/10/10 12:58:48
@@ -78,7 +78,6 @@
   bool useCa;
   time_t startTime;
   eTunerStatus tunerStatus;
-  cMutex mutex;
   cCondVar newSet;
   bool SetFrontend(void);
   virtual void Action(void);
@@ -111,7 +110,7 @@
 {
   active = false;
   tunerStatus = tsIdle;
-  newSet.Broadcast();
+  newSet.Signal();
   Cancel(3);
 }
 
@@ -122,7 +121,7 @@
 
 void cDvbTuner::Set(const cChannel *Channel, bool Tune, bool UseCa)
 {
-  cMutexLock MutexLock(&mutex);
+  Lock();
   if (Tune)
      tunerStatus = tsSet;
   else if (tunerStatus == tsCam)
@@ -131,7 +130,8 @@
   if (Channel->Ca() && tunerStatus != tsCam)
      startTime = time(NULL);
   channel = *Channel;
-  newSet.Broadcast();
+  Unlock();
+  newSet.Signal();
 }
 
 static unsigned int FrequencyToHz(unsigned int f)
@@ -252,7 +252,7 @@
 {
   active = true;
   while (active) {
-        cMutexLock MutexLock(&mutex);
+        Lock();
         if (tunerStatus == tsSet)
            tunerStatus = SetFrontend() ? tsTuned : tsIdle;
         if (tunerStatus == tsTuned) {
@@ -267,7 +267,6 @@
               if (event.status & FE_REINIT) {
                  tunerStatus = tsSet;
                  esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
-                 continue;
                  }
               }
            }
@@ -292,8 +291,9 @@
            else if (tunerStatus > tsLocked)
               tunerStatus = tsLocked;
            }
+        Unlock();
         // in the beginning we loop more often to let the CAM connection start up fast
-        newSet.TimedWait(mutex, (ciHandler && (time(NULL) - startTime < 20)) ? 100 : 1000);
+        newSet.Wait((ciHandler && (time(NULL) - startTime < 20)) ? 100 : 1000);
         }
 }
 
@@ -1101,29 +1101,17 @@
 void cDvbDevice::CloseDvr(void)
 {
   if (fd_dvr >= 0) {
-     close(fd_dvr);
-     fd_dvr = -1;
      delete tsBuffer;
      tsBuffer = NULL;
+     close(fd_dvr);
+     fd_dvr = -1;
      }
 }
 
 bool cDvbDevice::GetTSPacket(uchar *&Data)
 {
   if (tsBuffer) {
-     int r = tsBuffer->Read();
-     if (r >= 0) {
-        Data = tsBuffer->Get();
-        return true;
-        }
-     else if (FATALERRNO) {
-        if (errno == EOVERFLOW)
-           esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1);
-        else {
-           LOG_ERROR;
-           return false;
-           }
-        }
+     Data = tsBuffer->Get();
      return true;
      }
   return false;
===================================================================
RCS file: ./RCS/dvbplayer.c
retrieving revision 1.24
diff -u -r1.24 ./dvbplayer.c
--- ./dvbplayer.c	2004/06/19 08:55:49	1.24
+++ ./dvbplayer.c	2004/10/10 12:59:46
@@ -80,7 +80,6 @@
   int length;
   bool hasData;
   bool active;
-  cMutex mutex; 
   cCondVar newSet;
 protected:
   void Action(void);
@@ -106,20 +105,21 @@
 cNonBlockingFileReader::~cNonBlockingFileReader()
 {
   active = false;
-  newSet.Broadcast();
+  newSet.Signal();
   Cancel(3);
   free(buffer);
 }
 
 void cNonBlockingFileReader::Clear(void)
 {
-  cMutexLock MutexLock(&mutex);
+  Lock();
   f = -1;
   free(buffer);
   buffer = NULL;
   wanted = length = 0;
   hasData = false;
-  newSet.Broadcast();
+  Unlock();
+  newSet.Signal();
 }
 
 int cNonBlockingFileReader::Read(int FileHandle, uchar *Buffer, int Length)
@@ -139,7 +139,7 @@
      wanted = Length;
      length = 0;
      hasData = false;
-     newSet.Broadcast();
+     newSet.Signal();
      }
   errno = EAGAIN;
   return -1;
@@ -149,7 +149,7 @@
 {
   active = true;
   while (active) {
-        cMutexLock MutexLock(&mutex);
+        Lock();
         if (!hasData && f >= 0 && buffer) {
            int r = safe_read(f, buffer + length, wanted - length);
            if (r >= 0) {
@@ -163,16 +163,14 @@
               hasData = true;
               }
            }
-        newSet.TimedWait(mutex, 1000);
+        Unlock();
+        newSet.Wait(1000);
         }
 }
 
 // --- cDvbPlayer ------------------------------------------------------------
 
-//XXX+ also used in recorder.c - find a better place???
-// The size of the array used to buffer video data:
-// (must be larger than MINVIDEODATA - see remux.h)
-#define VIDEOBUFSIZE  MEGABYTE(1)
+#define PLAYERBUFSIZE  MEGABYTE(1)
 
 // The number of frames to back up when resuming an interrupted replay session:
 #define RESUMEBACKUP (10 * FRAMESPERSEC)
@@ -257,7 +255,7 @@
   replayFile = fileName->Open();
   if (replayFile < 0)
      return;
-  ringBuffer = new cRingBufferFrame(VIDEOBUFSIZE);
+  ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
   // Create the index file:
   index = new cIndexFile(FileName, false);
   if (!index)
===================================================================
RCS file: ./RCS/recorder.c
retrieving revision 1.10
diff -u -r1.10 ./recorder.c
--- ./recorder.c	2004/03/20 10:33:21	1.10
+++ ./recorder.c	2004/10/10 11:23:10
@@ -12,9 +12,7 @@
 #include <unistd.h>
 #include "recorder.h"
 
-// The size of the array used to buffer video data:
-// (must be larger than MINVIDEODATA - see remux.h)
-#define VIDEOBUFSIZE  MEGABYTE(5)
+#define RECORDERBUFSIZE  MEGABYTE(5)
 
 // The maximum time we wait before assuming that a recorded video data stream
 // is broken:
@@ -23,25 +21,35 @@
 #define MINFREEDISKSPACE    (512) // MB
 #define DISKCHECKINTERVAL   100 // seconds
 
-cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2)
-:cReceiver(Ca, Priority, Setup.RecordDolbyDigital ? 5 : 3, VPid, APid1, APid2, DPid1, DPid2)
-,cThread("recording")
+class cFileWriter : public cThread {
+private:
+  cRemux *remux;
+  cFileName *fileName;
+  cIndexFile *index;
+  uchar pictureType;
+  int fileSize;
+  int recordFile;
+  bool active;
+  time_t lastDiskSpaceCheck;
+  bool RunningLowOnDiskSpace(void);
+  bool NextFile(void);
+protected:
+  virtual void Action(void);
+public:
+  cFileWriter(const char *FileName, cRemux *Remux);
+  virtual ~cFileWriter();
+  };
+
+cFileWriter::cFileWriter(const char *FileName, cRemux *Remux)
+:cThread("file writer")
 {
-  ringBuffer = NULL;
-  remux = NULL;
+  active = false;
   fileName = NULL;
+  remux = Remux;
   index = NULL;
   pictureType = NO_PICTURE;
   fileSize = 0;
-  active = false;
   lastDiskSpaceCheck = time(NULL);
-
-  // Make sure the disk is up and running:
-
-  SpinUpDisk(FileName);
-
-  ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, TS_SIZE * 2, true);
-  remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2, true);
   fileName = new cFileName(FileName, true);
   recordFile = fileName->Open();
   if (recordFile < 0)
@@ -53,28 +61,15 @@
      // let's continue without index, so we'll at least have the recording
 }
 
-cRecorder::~cRecorder()
+cFileWriter::~cFileWriter()
 {
-  Detach();
+  active = false;
+  Cancel(3);
   delete index;
   delete fileName;
-  delete remux;
-  delete ringBuffer;
-}
-
-void cRecorder::Activate(bool On)
-{
-  if (On) {
-     if (recordFile >= 0)
-        Start();
-     }
-  else if (active) {
-     active = false;
-     Cancel(3);
-     }
 }
 
-bool cRecorder::RunningLowOnDiskSpace(void)
+bool cFileWriter::RunningLowOnDiskSpace(void)
 {
   if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
      int Free = FreeDiskSpaceMB(fileName->Name());
@@ -87,7 +82,7 @@
   return false;
 }
 
-bool cRecorder::NextFile(void)
+bool cFileWriter::NextFile(void)
 {
   if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
      if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
@@ -98,6 +93,76 @@
   return recordFile >= 0;
 }
 
+void cFileWriter::Action(void)
+{
+  time_t t = time(NULL);
+  active = true;
+  while (active) {
+        int Count;
+        uchar *p = remux->Get(Count, &pictureType);
+        if (p) {
+           //XXX+ active??? see old version (Busy)
+           if (!active && pictureType == I_FRAME) // finish the recording before the next 'I' frame
+              break;
+           if (NextFile()) {
+              if (index && pictureType != NO_PICTURE)
+                 index->Write(pictureType, fileName->Number(), fileSize);
+              if (safe_write(recordFile, p, Count) < 0) {
+                 LOG_ERROR_STR(fileName->Name());
+                 break;
+                 }
+              fileSize += Count;
+              remux->Del(Count);
+              }
+           else
+              break;
+           t = time(NULL);
+           continue;
+           }
+        if (time(NULL) - t > MAXBROKENTIMEOUT) {
+           esyslog("ERROR: video data stream broken");
+           cThread::EmergencyExit(true);
+           t = time(NULL);
+           }
+        }
+}
+
+cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2)
+:cReceiver(Ca, Priority, Setup.RecordDolbyDigital ? 5 : 3, VPid, APid1, APid2, DPid1, DPid2)
+,cThread("recording")
+{
+  active = false;
+
+  // Make sure the disk is up and running:
+
+  SpinUpDisk(FileName);
+
+  ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, TS_SIZE * 2, true, "Recorder");
+  ringBuffer->SetTimeouts(0, 100);
+  remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2, true);
+  writer = new cFileWriter(FileName, remux);
+}
+
+cRecorder::~cRecorder()
+{
+  Detach();
+  delete writer;
+  delete remux;
+  delete ringBuffer;
+}
+
+void cRecorder::Activate(bool On)
+{
+  if (On) {
+     writer->Start();
+     Start();
+     }
+  else if (active) {
+     active = false;
+     Cancel(3);
+     }
+}
+
 void cRecorder::Receive(uchar *Data, int Length)
 {
   int p = ringBuffer->Put(Data, Length);
@@ -107,39 +172,14 @@
 
 void cRecorder::Action(void)
 {
-  time_t t = time(NULL);
   active = true;
   while (active) {
         int r;
-        const uchar *b = ringBuffer->Get(r);
+        uchar *b = ringBuffer->Get(r);
         if (b) {
-           int Count = r, Result;
-           uchar *p = remux->Process(b, Count, Result, &pictureType);
-           ringBuffer->Del(Count);
-           if (p) {
-              //XXX+ active??? see old version (Busy)
-              if (!active && pictureType == I_FRAME) // finish the recording before the next 'I' frame
-                 break;
-              if (NextFile()) {
-                 if (index && pictureType != NO_PICTURE)
-                    index->Write(pictureType, fileName->Number(), fileSize);
-                 if (safe_write(recordFile, p, Result) < 0) {
-                    LOG_ERROR_STR(fileName->Name());
-                    break;
-                    }
-                 fileSize += Result;
-                 }
-              else
-                 break;
-              }
-           t = time(NULL);
-           }
-        else if (time(NULL) - t > MAXBROKENTIMEOUT) {
-           esyslog("ERROR: video data stream broken");
-           cThread::EmergencyExit(true);
-           t = time(NULL);
+           int Count = remux->Put(b, r);
+           if (Count)
+              ringBuffer->Del(Count);
            }
-        else
-           usleep(1); // this keeps the CPU load low
         }
 }
===================================================================
RCS file: ./RCS/recorder.h
retrieving revision 1.1
diff -u -r1.1 ./recorder.h
--- ./recorder.h	2002/06/08 09:35:03	1.1
+++ ./recorder.h	2004/10/10 11:22:38
@@ -16,19 +16,14 @@
 #include "ringbuffer.h"
 #include "thread.h"
 
+class cFileWriter;
+
 class cRecorder : public cReceiver, cThread {
 private:
   cRingBufferLinear *ringBuffer;
   cRemux *remux;
-  cFileName *fileName;
-  cIndexFile *index;
-  uchar pictureType;
-  int fileSize;
-  int recordFile;
+  cFileWriter *writer;
   bool active;
-  time_t lastDiskSpaceCheck;
-  bool RunningLowOnDiskSpace(void);
-  bool NextFile(void);
 protected:
   virtual void Activate(bool On);
   virtual void Receive(uchar *Data, int Length);
===================================================================
RCS file: ./RCS/remote.c
retrieving revision 1.40
diff -u -r1.40 ./remote.c
--- ./remote.c	2004/05/28 14:19:52	1.40
+++ ./remote.c	2004/10/10 12:59:29
@@ -25,7 +25,7 @@
 int cRemote::out = 0;
 cRemote *cRemote::learning = NULL;
 char *cRemote::unknownCode = NULL;
-cMutex cRemote::mutex;
+cMutex cRemote::mutex; //XXX try to avoid mutex completely!!!
 cCondVar cRemote::keyPressed;
 const char *cRemote::plugin = NULL;
 
@@ -74,7 +74,7 @@
 bool cRemote::Put(eKeys Key, bool AtFront)
 {
   if (Key != kNone) {
-     cMutexLock MutexLock(&mutex);
+     mutex.Lock();
      if (in != out && (keys[out] & k_Repeat) && (Key & k_Release))
         Clear();
      int d = out - in;
@@ -91,9 +91,11 @@
            if (++in >= MaxKeys)
               in = 0;
            }
-        keyPressed.Broadcast();
+        mutex.Unlock();
+        keyPressed.Signal();
         return true;
         }
+     mutex.Unlock();
      return false;
      }
   return true; // only a real key shall report an overflow!
@@ -138,7 +140,7 @@
   if (learning) {
      free(unknownCode);
      unknownCode = strdup(Code);
-     keyPressed.Broadcast();
+     keyPressed.Signal();
      }
   return false;
 }
@@ -151,6 +153,7 @@
 eKeys cRemote::Get(int WaitMs, char **UnknownCode)
 {
   for (;;) {
+      {//XXX
       cMutexLock MutexLock(&mutex);
       if (in != out) {
          eKeys k = keys[out];
@@ -158,7 +161,9 @@
             out = 0;
          return k;
          }
-      else if (!WaitMs || !keyPressed.TimedWait(mutex, WaitMs)) {
+      }//XXX
+      if (!WaitMs || !keyPressed.Wait(WaitMs)) {
+         cMutexLock MutexLock(&mutex);//XXX
          if (learning && UnknownCode) {
             *UnknownCode = unknownCode;
             unknownCode = NULL;
===================================================================
RCS file: ./RCS/remux.c
retrieving revision 1.18
diff -u -r1.18 ./remux.c
--- ./remux.c	2004/02/14 10:40:37	1.18
+++ ./remux.c	2004/10/10 11:36:03
@@ -11,60 +11,6 @@
  * $Id: remux.c 1.18 2004/02/14 10:40:37 kls Exp kls $
  */
 
-/* The calling interface of the 'cRemux::Process()' function is defined
-   as follows:
-
-   'Data' points to a chunk of data that consists of 'Count' bytes.
-   The 'Process' function shall try to remultiplex as much of the
-   data as possible and return a pointer to the resulting buffer.
-   That buffer typically is different from the incoming 'Data',
-   but in the simplest case (when 'Process' does nothing) might
-   as well point to the original 'Data'. When returning, 'Count'
-   shall be set to the number of bytes that have been processed
-   (i.e. have been taken from 'Data'), while 'Result' indicates
-   how many bytes the returned buffer contains. 'PictureType' shall
-   be set to NO_PICTURE if the returned data does not start a new
-   picture, or one of I_FRAME, P_FRAME or B_FRAME if a new picture
-   starting point has been found. This also means that the returned
-   data buffer may contain at most one entire video frame, because
-   the next frame must be returned with its own value for 'PictureType'.
-
-   'Process' shall do it's best to keep the latency time as short
-   as possible in order to allow a quick start of VDR's "Transfer
-   mode" (displaying the signal of one DVB card on another card).
-   In order to do that, this function may decide to first pass
-   through the incoming data (almost) unprocessed, and make
-   actual processing kick in after a few seconds (if that is at
-   all possible for the algorithm). This may result in a non-
-   optimal stream at the beginning, which won't matter for normal
-   recordings but may make switching through encrypted channels
-   in "Transfer mode" faster.
-
-   In the resulting data stream, a new packet shall always be started
-   when a frame border is encountered. VDR needs this in order to
-   be able to detect and store the frame indexes, and to easily
-   display single frames in fast forward/back mode. The very first
-   data block returned shall be the starting point of an I_FRAME.
-   Everything before that shall be silently dropped.
-
-   If the incoming data is not enough to do remultiplexing, a value
-   of NULL shall be returned ('Result' has no meaning then). This
-   will tell the caller to wait for more data to be presented in
-   the next call. If NULL is returned and 'Count' is not 0, the
-   caller shall remove 'Count' bytes from the beginning of 'Data'
-   before the next call. This is the way 'Process' indicates that
-   it must skip that data.
-
-   Any data that is not used during this call will appear at the
-   beginning of the incoming 'Data' buffer at the next call, plus
-   any new data that has become available.
-
-   It is guaranteed that the caller will completely process any
-   returned data before the next call to 'Process'. That way, 'Process'
-   can dynamically allocate its return buffer and be sure the caller
-   doesn't keep any pointers into that buffer.
-*/
-
 #include "remux.h"
 #include <stdlib.h>
 #include "thread.h"
@@ -133,8 +79,7 @@
   uint8_t check;
   int which;
   bool done;
-  uint8_t *resultBuffer;
-  int *resultCount;
+  cRingBufferLinear *resultBuffer;
   int tsErrors;
   int ccErrors;
   int ccCounter;
@@ -145,7 +90,7 @@
   void write_ipack(const uint8_t *Data, int Count);
   void instant_repack(const uint8_t *Buf, int Count);
 public:
-  cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size, uint8_t AudioCid = 0x00);
+  cTS2PES(cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid = 0x00);
   ~cTS2PES();
   void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
   void Clear(void);
@@ -153,10 +98,9 @@
 
 uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
 
-cTS2PES::cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size, uint8_t AudioCid)
+cTS2PES::cTS2PES(cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid)
 {
   resultBuffer = ResultBuffer;
-  resultCount = ResultCount;
   size = Size;
   audioCid = AudioCid;
 
@@ -184,12 +128,9 @@
 
 void cTS2PES::store(uint8_t *Data, int Count)
 {
-  if (*resultCount + Count > RESULTBUFFERSIZE) {
-     esyslog("ERROR: result buffer overflow (%d + %d > %d)", *resultCount, Count, RESULTBUFFERSIZE);
-     Count = RESULTBUFFERSIZE - *resultCount;
-     }
-  memcpy(resultBuffer + *resultCount, Data, Count);
-  *resultCount += Count;
+  int n = resultBuffer->Put(Data, Count);
+  if (n != Count)
+     esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
 }
 
 void cTS2PES::reset_ipack(void)
@@ -452,6 +393,8 @@
 
 // --- cRemux ----------------------------------------------------------------
 
+#define RESULTBUFFERSIZE KILOBYTE(256)
+
 cRemux::cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure)
 {
   vPid = VPid;
@@ -463,13 +406,15 @@
   numUPTerrors = 0;
   synced = false;
   skipped = 0;
-  resultCount = resultDelivered = 0;
-  vTS2PES  =         new cTS2PES(resultBuffer, &resultCount, IPACKS);
-  aTS2PES1 =         new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC0);
-  aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC1) : NULL;
-  dTS2PES1 = dPid1 ? new cTS2PES(resultBuffer, &resultCount, IPACKS)       : NULL;
+  resultSkipped = 0;
+  resultBuffer = new cRingBufferLinear(RESULTBUFFERSIZE, IPACKS, false, "Result");
+  resultBuffer->SetTimeouts(0, 100);
+  vTS2PES  =         new cTS2PES(resultBuffer, IPACKS);
+  aTS2PES1 =         new cTS2PES(resultBuffer, IPACKS, 0xC0);
+  aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, IPACKS, 0xC1) : NULL;
+  dTS2PES1 = dPid1 ? new cTS2PES(resultBuffer, IPACKS)       : NULL;
   //XXX don't yet know how to tell apart primary and secondary DD data...
-  dTS2PES2 = /*XXX dPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS) : XXX*/ NULL;
+  dTS2PES2 = /*XXX dPid2 ? new cTS2PES(resultBuffer, IPACKS) : XXX*/ NULL;
 }
 
 cRemux::~cRemux()
@@ -479,6 +424,7 @@
   delete aTS2PES2;
   delete dTS2PES1;
   delete dTS2PES2;
+  delete resultBuffer;
 }
 
 int cRemux::GetPid(const uchar *Data)
@@ -488,27 +434,32 @@
 
 int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
 {
-  // Returns the entire length of the packet starting at offset, or -1 in case of error.
-  return (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
+  // Returns the length of the packet starting at Offset, or -1 if Count is
+  // too small to contain the entire packet.
+  int Length = (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
+  if (Length > 0 && Offset + Length <= Count)
+     return Length;
+  return -1;
 }
 
 int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
 {
   // Scans the video packet starting at Offset and returns its length.
   // If the return value is -1 the packet was not completely in the buffer.
-
   int Length = GetPacketLength(Data, Count, Offset);
-  if (Length > 0 && Offset + Length <= Count) {
-     int i = Offset + 8; // the minimum length of the video packet header
-     i += Data[i] + 1;   // possible additional header bytes
-     for (; i < Offset + Length; i++) {
-         if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
-            switch (Data[i + 3]) {
-              case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
-                               return Length;
-              }
+  if (Length > 0) {
+     if (Length >= 8) {
+        int i = Offset + 8; // the minimum length of the video packet header
+        i += Data[i] + 1;   // possible additional header bytes
+        for (; i < Offset + Length; i++) {
+            if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
+               switch (Data[i + 3]) {
+                 case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
+                                  return Length;
+                 }
+               }
             }
-         }
+        }
      PictureType = NO_PICTURE;
      return Length;
      }
@@ -517,28 +468,8 @@
 
 #define TS_SYNC_BYTE 0x47
 
-uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar *PictureType)
+int cRemux::Put(const uchar *Data, int Count)
 {
-  uchar dummyPictureType;
-  if (!PictureType)
-     PictureType = &dummyPictureType;
-
-/*XXX
-  // test recording the raw TS:
-  Result = Count;
-  *PictureType = I_FRAME;
-  return Data;
-XXX*/
-
-  // Remove any previously delivered data from the result buffer:
-
-  if (resultDelivered) {
-     if (resultDelivered < resultCount)
-        memmove(resultBuffer, resultBuffer + resultDelivered, resultCount - resultDelivered);
-     resultCount -= resultDelivered;
-     resultDelivered = 0;
-     }
-
   int used = 0;
 
   // Make sure we are looking at a TS packet:
@@ -560,6 +491,8 @@
          break;
       if (Data[i] != TS_SYNC_BYTE)
          break;
+      if (resultBuffer->Free() < IPACKS)
+         break;
       int pid = GetPid(Data + i + 1);
       if (Data[i + 3] & 0x10) { // got payload
          if      (pid == vPid)              vTS2PES->ts_to_pes(Data + i);
@@ -569,17 +502,18 @@
          else if (pid == dPid2 && dTS2PES2) dTS2PES2->ts_to_pes(Data + i);
          }
       used += TS_SIZE;
-      if (resultCount > (int)sizeof(resultBuffer) / 2)
-         break;
       }
-  Count = used;
+  return used;
+}
 
-/*XXX
-  // test recording without determining the real frame borders:
-  *PictureType = I_FRAME;
-  Result = resultDelivered = resultCount;
-  return Result ? resultBuffer : NULL;
-XXX*/
+uchar *cRemux::Get(int &Count, uchar *PictureType)
+{
+#if 0
+  // Test recording without determining the real frame borders:
+  if (PictureType)
+     *PictureType = I_FRAME;
+  return resultBuffer->Get(Count);
+#endif
 
   // Special VPID case to enable recording radio channels:
 
@@ -587,9 +521,9 @@
      // XXX actually '0' should be enough, but '1' must be used with encrypted channels (driver bug?)
      // XXX also allowing 0x1FFF to not break Michael Paar's original patch,
      // XXX but it would probably be best to only use '0'
-     *PictureType = I_FRAME;
-     Result = resultDelivered = resultCount;
-     return Result ? resultBuffer : NULL;
+     if (PictureType)
+        *PictureType = I_FRAME;
+     return resultBuffer->Get(Count);
      }
 
   // Check if we're getting anywhere here:
@@ -607,72 +541,69 @@
 
   // Check for frame borders:
 
-  *PictureType = NO_PICTURE;
+  if (PictureType)
+     *PictureType = NO_PICTURE;
 
-  if (resultCount >= MINVIDEODATA) {
-     for (int i = 0; i < resultCount; i++) {
-         if (resultBuffer[i] == 0 && resultBuffer[i + 1] == 0 && resultBuffer[i + 2] == 1) {
-            switch (resultBuffer[i + 3]) {
-              case VIDEO_STREAM_S ... VIDEO_STREAM_E:
-                   {
-                     uchar pt = NO_PICTURE;
-                     int l = ScanVideoPacket(resultBuffer, resultCount, i, pt);
-                     if (l < 0)
-                        return NULL; // no useful data found, wait for more
-                     if (pt != NO_PICTURE) {
-                        if (pt < I_FRAME || B_FRAME < pt) {
-                           esyslog("ERROR: unknown picture type '%d'", pt);
-                           if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure)
-                              cThread::EmergencyExit(true);
-                           }
-                        else if (!synced) {
-                           if (pt == I_FRAME) {
-                              resultDelivered = i; // will drop everything before this position
-                              SetBrokenLink(resultBuffer + i, l);
-                              synced = true;
-                              }
-                           else {
-                              resultDelivered = i + l; // will drop everything before and including this packet
-                              return NULL;
-                              }
-                           }
-                        }
-                     if (synced) {
-                        *PictureType = pt;
-                        Result = l;
-                        uchar *p = resultBuffer + resultDelivered;
-                        resultDelivered += l;
-                        return p;
-                        }
-                     else {
-                        resultDelivered = i + l; // will drop everything before and including this packet
-                        return NULL;
-                        }
-                   }
-                   break;
-              case PRIVATE_STREAM1:
-              case AUDIO_STREAM_S ... AUDIO_STREAM_E:
-                   {
-                     int l = GetPacketLength(resultBuffer, resultCount, i);
-                     if (l < 0)
-                        return NULL; // no useful data found, wait for more
-                     if (synced) {
-                        Result = l;
-                        uchar *p = resultBuffer + resultDelivered;
-                        resultDelivered += l;
-                        return p;
-                        }
-                     else {
-                        resultDelivered = i + l; // will drop everything before and including this packet
-                        return NULL;
+  Count = 0;
+  uchar *resultData = NULL;
+  int resultCount = 0;
+  uchar *data = resultBuffer->Get(resultCount);
+  if (data) {
+     for (int i = 0; i < resultCount - 3; i++) {
+         if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
+            int l = 0;
+            uchar StreamType = data[i + 3];
+            if (VIDEO_STREAM_S <= StreamType && StreamType <= VIDEO_STREAM_E) {
+               uchar pt = NO_PICTURE;
+               l = ScanVideoPacket(data, resultCount, i, pt);
+               if (l < 0)
+                  return resultData;
+               if (pt != NO_PICTURE) {
+                  if (pt < I_FRAME || B_FRAME < pt) {
+                     esyslog("ERROR: unknown picture type '%d'", pt);
+                     if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure)
+                        cThread::EmergencyExit(true);
+                     }
+                  else if (!synced) {
+                     if (pt == I_FRAME) {
+                        if (PictureType)
+                           *PictureType = pt;
+                        resultSkipped = i; // will drop everything before this position
+                        SetBrokenLink(data + i, l);
+                        synced = true;
                         }
-                   }
-                   break;
-              }
+                     }
+                  else if (Count)
+                     return resultData;
+                  else if (PictureType)
+                     *PictureType = pt;
+                  }
+               }
+            //XXX make this a general else branch???
+            else if (AUDIO_STREAM_S <= StreamType && StreamType <= AUDIO_STREAM_E || StreamType == PRIVATE_STREAM1) {
+               l = GetPacketLength(data, resultCount, i);
+               if (l < 0)
+                  return resultData;
+               }
+            if (synced) {
+               if (!Count)
+                  resultData = data + i;
+               Count += l;
+               }
+            else
+               resultSkipped = i;
+            if (l > 0)
+               i += l - 1; // the loop increments, too
             }
          }
      }
-  return NULL; // no useful data found, wait for more
+  return resultData;
+}
+
+void cRemux::Del(int Count)
+{
+  resultBuffer->Del(resultSkipped + Count);
+  resultSkipped = 0;
 }
 
 void cRemux::SetBrokenLink(uchar *Data, int Length)
===================================================================
RCS file: ./RCS/remux.h
retrieving revision 1.11
diff -u -r1.11 ./remux.h
--- ./remux.h	2004/02/14 10:40:41	1.11
+++ ./remux.h	2004/10/10 11:34:38
@@ -12,6 +12,7 @@
 
 #include <time.h> //XXX FIXME: DVB/linux/dvb/dmx.h should include <time.h> itself!!!
 #include <linux/dvb/dmx.h>
+#include "ringbuffer.h"
 #include "tools.h"
 
 // Picture types:
@@ -20,11 +21,6 @@
 #define P_FRAME    2
 #define B_FRAME    3
 
-// The minimum amount of video data necessary to identify frames:
-#define MINVIDEODATA (16*1024) // just a safe guess (max. size of any frame block, plus some safety)
-
-#define RESULTBUFFERSIZE (MINVIDEODATA * 4)
-
 class cTS2PES;
 
 class cRemux {
@@ -35,16 +31,27 @@
   int skipped;
   int vPid, aPid1, aPid2, dPid1, dPid2;
   cTS2PES *vTS2PES, *aTS2PES1, *aTS2PES2, *dTS2PES1, *dTS2PES2;
-  uchar resultBuffer[RESULTBUFFERSIZE];
-  int resultCount;
-  int resultDelivered;
+  cRingBufferLinear *resultBuffer;
+  int resultSkipped;
   int GetPid(const uchar *Data);
   int GetPacketLength(const uchar *Data, int Count, int Offset);
   int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
 public:
   cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure = false);
   ~cRemux();
-  uchar *Process(const uchar *Data, int &Count, int &Result, uchar *PictureType = NULL);
+  int Put(const uchar *Data, int Count);
+       ///< Puts at most Count bytes of Data into the remuxer.
+       ///< \return Returns the number of bytes actually consumed from Data.
+  uchar *Get(int &Count, uchar *PictureType = NULL);
+       ///< Gets all currently available data from the remuxer.
+       ///< \return Count contains the number of bytes the result points to, and
+       ///< PictureType (if not NULL) will contain one of NO_PICTURE, I_FRAME, P_FRAME
+       ///< or B_FRAME.
+  void Del(int Count);
+       ///< Deletes Count bytes from the remuxer. Count must be the number returned
+       ///< from a previous call to Get(). Several calls to Del() with fractions of
+       ///< a previously returned Count may be made, but the total sum of all Count
+       ///< values must be exactly what the previous Get() has returned.
   static void SetBrokenLink(uchar *Data, int Length);
   };
 
===================================================================
RCS file: ./RCS/ringbuffer.c
retrieving revision 1.20
diff -u -r1.20 ./ringbuffer.c
--- ./ringbuffer.c	2004/06/19 12:27:56	1.20
+++ ./ringbuffer.c	2004/10/10 12:59:12
@@ -18,11 +18,14 @@
 // --- cRingBuffer -----------------------------------------------------------
 
 #define OVERFLOWREPORTDELTA 5 // seconds between reports
+#define PERCENTAGEDELTA     10
+#define PERCENTAGETHRESHOLD 70
 
 cRingBuffer::cRingBuffer(int Size, bool Statistics)
 {
   size = Size;
   statistics = Statistics;
+  getThreadTid = 0;
   maxFill = 0;
   lastPercent = 0;
   putTimeout = getTimeout = 0;
@@ -36,34 +39,41 @@
      dsyslog("buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
 }
 
-void cRingBuffer::WaitForPut(void)
+void cRingBuffer::UpdatePercentage(int Fill)
 {
-  if (putTimeout) {
-     putMutex.Lock();
-     readyForPut.TimedWait(putMutex, putTimeout);
-     putMutex.Unlock();
+  if (Fill > maxFill)
+     maxFill = Fill;
+  int percent = Fill * 100 / (Size() - 1) / PERCENTAGEDELTA * PERCENTAGEDELTA;
+  if (percent != lastPercent) {
+     if (percent >= PERCENTAGETHRESHOLD && percent > lastPercent || percent < PERCENTAGETHRESHOLD && lastPercent >= PERCENTAGETHRESHOLD) {
+        dsyslog("buffer usage: %d%% (tid=%ld)", percent, getThreadTid);
+        lastPercent = percent;
+        }
      }
 }
 
+void cRingBuffer::WaitForPut(void)
+{
+  if (putTimeout)
+     readyForPut.Wait(putTimeout);
+}
+
 void cRingBuffer::WaitForGet(void)
 {
-  if (getTimeout) {
-     getMutex.Lock();
-     readyForGet.TimedWait(getMutex, getTimeout);
-     getMutex.Unlock();
-     }
+  if (getTimeout)
+     readyForGet.Wait(getTimeout);
 }
 
 void cRingBuffer::EnablePut(void)
 {
-  if (putTimeout)
-     readyForPut.Broadcast();
+  if (putTimeout && Free() > Size() / 3)
+     readyForPut.Signal();
 }
 
 void cRingBuffer::EnableGet(void)
 {
-  if (getTimeout)
-     readyForGet.Broadcast();
+  if (getTimeout && Available() > Size() / 3)
+     readyForGet.Signal();
 }
 
 void cRingBuffer::SetTimeouts(int PutTimeout, int GetTimeout)
@@ -85,70 +95,169 @@
 
 // --- cRingBufferLinear -----------------------------------------------------
 
-cRingBufferLinear::cRingBufferLinear(int Size, int Margin, bool Statistics)
+#ifdef DEBUGRINGBUFFERS
+#define MAXRBLS 30
+#define DEBUGRBLWIDTH 45
+
+cRingBufferLinear *cRingBufferLinear::RBLS[MAXRBLS] = { NULL };
+
+void cRingBufferLinear::AddDebugRBL(cRingBufferLinear *RBL)
+{
+  for (int i = 0; i < MAXRBLS; i++) {
+      if (!RBLS[i]) {
+         RBLS[i] = RBL;
+         break;
+         }
+      }
+}
+
+void cRingBufferLinear::DelDebugRBL(cRingBufferLinear *RBL)
+{
+  for (int i = 0; i < MAXRBLS; i++) {
+      if (RBLS[i] == RBL) {
+         RBLS[i] = NULL;
+         break;
+         }
+      }
+}
+
+void cRingBufferLinear::PrintDebugRBL(void)
+{
+  bool printed = false;
+  for (int i = 0; i < MAXRBLS; i++) {
+      cRingBufferLinear *p = RBLS[i];
+      if (p) {
+         printed = true;
+         int lh = p->lastHead;
+         int lt = p->lastTail;
+         int h = lh * DEBUGRBLWIDTH / p->Size();
+         int t = lt * DEBUGRBLWIDTH / p->Size();
+         char buf[DEBUGRBLWIDTH + 10];
+         memset(buf, '-', DEBUGRBLWIDTH);
+         if (lt <= lh)
+            memset(buf + t, '*', max(h - t, 1));
+         else {
+            memset(buf, '*', h);
+            memset(buf + t, '*', DEBUGRBLWIDTH - t);
+            }
+         buf[t] = '<';
+         buf[h] = '>';
+         buf[DEBUGRBLWIDTH] = 0;
+         printf("%2d %s %8d %8d %s\n", i, buf, p->lastPut, p->lastGet, p->description);
+         }
+      }
+  if (printed)
+     printf("\n");
+  }
+#endif
+
+cRingBufferLinear::cRingBufferLinear(int Size, int Margin, bool Statistics, const char *Description)
 :cRingBuffer(Size, Statistics)
 {
-  margin = Margin;
+  description = Description ? strdup(Description) : NULL;
+  tail = head = margin = Margin;
   buffer = NULL;
-  getThreadTid = 0;
   if (Size > 1) { // 'Size - 1' must not be 0!
-     buffer = MALLOC(uchar, Size);
-     if (!buffer)
-        esyslog("ERROR: can't allocate ring buffer (size=%d)", Size);
-     Clear();
+     if (Margin <= Size / 2) {
+        buffer = MALLOC(uchar, Size);
+        if (!buffer)
+           esyslog("ERROR: can't allocate ring buffer (size=%d)", Size);
+        Clear();
+        }
+     else
+        esyslog("ERROR: illegal margin for ring buffer (%d > %d)", Margin, Size / 2);
      }
   else
      esyslog("ERROR: illegal size for ring buffer (%d)", Size);
+#ifdef DEBUGRINGBUFFERS
+  lastHead = head;
+  lastTail = tail;
+  lastPut = lastGet = -1;
+  AddDebugRBL(this);
+#endif
 }
 
 cRingBufferLinear::~cRingBufferLinear()
 {
+#ifdef DEBUGRINGBUFFERS
+  DelDebugRBL(this);
+#endif
   free(buffer);
+  free(description);
 }
 
 int cRingBufferLinear::Available(void)
 {
-  Lock();
   int diff = head - tail;
-  Unlock();
   return (diff >= 0) ? diff : Size() + diff - margin;
 }
 
 void cRingBufferLinear::Clear(void)
 {
-  Lock();
-  head = tail = margin;
-  lastGet = -1;
-  Unlock();
+  //XXX head = tail; if called by writing thread???
+  tail = head;
+#ifdef DEBUGRINGBUFFERS
+  lastHead = head;
+  lastTail = tail;
+  lastPut = lastGet = -1;
+#endif
+  maxFill = 0;
   EnablePut();
+}
+
+int cRingBufferLinear::Read(int FileHandle, int Max)
+{
+  int Tail = tail;
+  int diff = Tail - head;
+  int free = (diff > 0) ? diff - 1 : Size() - head;
+  if (Tail <= margin)
+     free--;
+  int Count = 0;
+  if (free > 0) {
+     if (0 < Max && Max < free)
+        free = Max;
+     Count = safe_read(FileHandle, buffer + head, free);
+     if (Count > 0) {
+        int Head = head + Count;
+        if (Head >= Size())
+           Head = margin;
+        head = Head;
+        if (statistics) {
+           int fill = head - Tail;
+           if (fill < 0)
+              fill = Size() + fill;
+           else if (fill >= Size())
+              fill = Size() - 1;
+           UpdatePercentage(fill);
+           }
+        }
+     }
+#ifdef DEBUGRINGBUFFERS
+  lastHead = head;
+  lastPut = Count;
+#endif
   EnableGet();
+  if (free == 0)
+     WaitForPut();
+  return Count;
 }
 
 int cRingBufferLinear::Put(const uchar *Data, int Count)
 {
   if (Count > 0) {
-     Lock();
+     int Tail = tail;
      int rest = Size() - head;
-     int diff = tail - head;
-     int free = ((tail < margin) ? rest : (diff > 0) ? diff : Size() + diff - margin) - 1;
+     int diff = Tail - head;
+     int free = ((Tail < margin) ? rest : (diff > 0) ? diff : Size() + diff - margin) - 1;
      if (statistics) {
         int fill = Size() - free - 1 + Count;
         if (fill >= Size())
            fill = Size() - 1;
-        if (fill > maxFill)
-           maxFill = fill;
-        int percent = maxFill * 100 / (Size() - 1) / 5 * 5;
-        if (abs(lastPercent - percent) >= 5) {
-           if (percent > 75)
-              dsyslog("buffer usage: %d%% (tid=%ld)", percent, getThreadTid);
-           lastPercent = percent;
-           }
+        UpdatePercentage(fill);
         }
      if (free > 0) {
         if (free < Count)
            Count = free;
-        if (Count > maxFill)
-           maxFill = Count;
         if (Count >= rest) {
            memcpy(buffer + head, Data, rest);
            if (Count - rest)
@@ -162,7 +271,10 @@
         }
      else
         Count = 0;
-     Unlock();
+#ifdef DEBUGRINGBUFFERS
+     lastHead = head;
+     lastPut = Count;
+#endif
      EnableGet();
      if (Count == 0)
         WaitForPut();
@@ -173,25 +285,24 @@
 uchar *cRingBufferLinear::Get(int &Count)
 {
   uchar *p = NULL;
-  Lock();
+  int Head = head;
   if (getThreadTid <= 0)
      getThreadTid = pthread_self();
   int rest = Size() - tail;
-  if (rest < margin && head < tail) {
+  if (rest < margin && Head < tail) {
      int t = margin - rest;
      memcpy(buffer + t, buffer + tail, rest);
      tail = t;
-     rest = head - tail;
+     rest = Head - tail;
      }
-  int diff = head - tail;
+  int diff = Head - tail;
   int cont = (diff >= 0) ? diff : Size() + diff - margin;
   if (cont > rest)
      cont = rest;
   if (cont >= margin) {
      p = buffer + tail;
-     Count = lastGet = cont;
+     Count = gotten = cont;
      }
-  Unlock();
   if (!p)
      WaitForGet();
   return p;
@@ -199,17 +310,23 @@
 
 void cRingBufferLinear::Del(int Count)
 {
-  if (Count > 0 && Count <= lastGet) {
-     Lock();
-     tail += Count;
-     lastGet -= Count;
-     if (tail >= Size())
-        tail = margin;
-     Unlock();
+  if (Count > gotten) {
+     esyslog("ERROR: invalid Count in cRingBufferLinear::Del: %d (limited to %d)", Count, gotten);
+     Count = gotten;
+     }
+  if (Count > 0) {
+     int Tail = tail;
+     Tail += Count;
+     gotten -= Count;
+     if (Tail >= Size())
+        Tail = margin;
+     tail = Tail;
      EnablePut();
      }
-  else
-     esyslog("ERROR: invalid Count in cRingBufferLinear::Del: %d", Count);
+#ifdef DEBUGRINGBUFFERS
+  lastTail = tail;
+  lastGet = Count;
+#endif
 }
 
 // --- cFrame ----------------------------------------------------------------
===================================================================
RCS file: ./RCS/ringbuffer.h
retrieving revision 1.15
diff -u -r1.15 ./ringbuffer.h
--- ./ringbuffer.h	2004/06/19 10:32:15	1.15
+++ ./ringbuffer.h	2004/10/10 12:43:18
@@ -15,9 +15,7 @@
 
 class cRingBuffer {
 private:
-  cMutex mutex;
   cCondVar readyForPut, readyForGet;
-  cMutex putMutex, getMutex;
   int putTimeout;
   int getTimeout;
   int size;
@@ -25,18 +23,18 @@
   int overflowCount;
   int overflowBytes;
 protected:
+  pthread_t getThreadTid;
   int maxFill;//XXX
   int lastPercent;
   bool statistics;//XXX
+  void UpdatePercentage(int Fill);
   void WaitForPut(void);
   void WaitForGet(void);
   void EnablePut(void);
   void EnableGet(void);
   virtual void Clear(void) = 0;
   virtual int Available(void) = 0;
-  int Free(void) { return size - Available() - 1; }
-  void Lock(void) { mutex.Lock(); }
-  void Unlock(void) { mutex.Unlock(); }
+  virtual int Free(void) { return Size() - Available() - 1; }
   int Size(void) { return size; }
 public:
   cRingBuffer(int Size, bool Statistics = false);
@@ -46,20 +44,39 @@
   };
 
 class cRingBufferLinear : public cRingBuffer {
+#define DEBUGRINGBUFFERS//XXX
+#ifdef DEBUGRINGBUFFERS
+private:
+  int lastHead, lastTail;
+  int lastPut, lastGet;
+  static cRingBufferLinear *RBLS[];
+  static void AddDebugRBL(cRingBufferLinear *RBL);
+  static void DelDebugRBL(cRingBufferLinear *RBL);
+public:
+  static void PrintDebugRBL(void);
+#endif
 private:
   int margin, head, tail;
-  int lastGet;
+  int gotten;
   uchar *buffer;
-  pthread_t getThreadTid;
+  char *description;
 public:
-  cRingBufferLinear(int Size, int Margin = 0, bool Statistics = false);
+  cRingBufferLinear(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL);
     ///< Creates a linear ring buffer.
     ///< The buffer will be able to hold at most Size-Margin-1 bytes of data, and will
     ///< be guaranteed to return at least Margin bytes in one consecutive block.
+    ///< The optional Description is used for debugging only.
   virtual ~cRingBufferLinear();
   virtual int Available(void);
+  virtual int Free(void) { return Size() - Available() - 1 - margin; }
   virtual void Clear(void);
     ///< Immediately clears the ring buffer.
+  int Read(int FileHandle, int Max = 0);
+    ///< Reads at most Max bytes from FileHandle and stores them in the
+    ///< ring buffer. If Max is 0, reads as many bytes as possible.
+    ///< Only one actual read() call is done.
+    ///< \return Returns the number of bytes actually read and stored, or
+    ///< an error value from the actual read() call.
   int Put(const uchar *Data, int Count);
     ///< Puts at most Count bytes of Data into the ring buffer.
     ///< \return Returns the number of bytes actually stored.
@@ -67,7 +84,7 @@
     ///< Gets data from the ring buffer.
     ///< The data will remain in the buffer until a call to Del() deletes it.
     ///< \return Returns a pointer to the data, and stores the number of bytes
-    ///< actually retrieved in Count. If the returned pointer is NULL, Count has no meaning.
+    ///< actually available in Count. If the returned pointer is NULL, Count has no meaning.
   void Del(int Count);
     ///< Deletes at most Count bytes from the ring buffer.
     ///< Count must be less or equal to the number that was returned by a previous
@@ -98,9 +115,12 @@
 
 class cRingBufferFrame : public cRingBuffer {
 private:
+  cMutex mutex;
   cFrame *head;
   int currentFill;
   void Delete(cFrame *Frame);
+  void Lock(void) { mutex.Lock(); }
+  void Unlock(void) { mutex.Unlock(); }
 public:
   cRingBufferFrame(int Size, bool Statistics = false);
   virtual ~cRingBufferFrame();
===================================================================
RCS file: ./RCS/thread.c
retrieving revision 1.31
diff -u -r1.31 ./thread.c
--- ./thread.c	2004/03/14 16:48:30	1.31
+++ ./thread.c	2004/10/10 12:56:57
@@ -22,63 +22,53 @@
 
 cCondVar::cCondVar(void)
 {
-  pthread_cond_init(&cond, 0);
+  signaled = false;
+  pthread_mutex_init(&mutex, NULL);
+  pthread_cond_init(&cond, NULL);
 }
 
 cCondVar::~cCondVar()
 {
   pthread_cond_destroy(&cond);
+  pthread_mutex_destroy(&mutex);
 }
 
-void cCondVar::Wait(cMutex &Mutex)
-{
-  if (Mutex.locked) {
-     int locked = Mutex.locked;
-     Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
-                       // does an implizit unlock of the mutex
-     pthread_cond_wait(&cond, &Mutex.mutex);
-     Mutex.locked = locked;
-     }
-}
-
-bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
+bool cCondVar::Wait(int TimeoutMs)
 {
-  bool r = true; // true = condition signaled false = timeout
-
-  if (Mutex.locked) {
-     struct timeval now;                   // unfortunately timedwait needs the absolute time, not the delta :-(
-     if (gettimeofday(&now, NULL) == 0) {  // get current time
-        now.tv_usec += TimeoutMs * 1000;   // add the timeout
-        while (now.tv_usec >= 1000000) {   // take care of an overflow
-              now.tv_sec++;
-              now.tv_usec -= 1000000;
-              }
-        struct timespec abstime;              // build timespec for timedwait
-        abstime.tv_sec = now.tv_sec;          // seconds
-        abstime.tv_nsec = now.tv_usec * 1000; // nano seconds
-
-        int locked = Mutex.locked;
-        Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
-                          // does an implizit unlock of the mutex.
-        if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
-           r = false;
-        Mutex.locked = locked;
+  pthread_mutex_lock(&mutex);
+  if (!signaled) {
+     if (TimeoutMs) {
+        struct timeval now;
+        if (gettimeofday(&now, NULL) == 0) {  // get current time
+           now.tv_usec += TimeoutMs * 1000;   // add the timeout
+           int sec = now.tv_usec / 1000000;
+           now.tv_sec += sec;
+           now.tv_usec -= sec * 1000000;
+           struct timespec abstime;              // build timespec for timedwait
+           abstime.tv_sec = now.tv_sec;          // seconds
+           abstime.tv_nsec = now.tv_usec * 1000; // nano seconds
+           while (!signaled) {
+                 if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
+                    break;
+                 }
+           }
         }
+     else
+        pthread_cond_wait(&cond, &mutex);
      }
+  bool r = signaled;
+  signaled = false;
+  pthread_mutex_unlock(&mutex);
   return r;
 }
 
-void cCondVar::Broadcast(void)
-{
-  pthread_cond_broadcast(&cond);
-}
-
-/*
 void cCondVar::Signal(void)
 {
+  pthread_mutex_lock(&mutex);
+  signaled = true;
   pthread_cond_signal(&cond);
+  pthread_mutex_unlock(&mutex);
 }
-*/
 
 // --- cRwLock ---------------------------------------------------------------
 
===================================================================
RCS file: ./RCS/thread.h
retrieving revision 1.20
diff -u -r1.20 ./thread.h
--- ./thread.h	2004/01/03 16:58:50	1.20
+++ ./thread.h	2004/10/10 13:02:21
@@ -14,18 +14,21 @@
 #include <stdio.h>
 #include <sys/types.h>
 
-class cMutex;
-
 class cCondVar {
 private:
+  pthread_mutex_t mutex;
   pthread_cond_t cond;
+  bool signaled;
 public:
   cCondVar(void);
   ~cCondVar();
-  void Wait(cMutex &Mutex);
-  bool TimedWait(cMutex &Mutex, int TimeoutMs);
-  void Broadcast(void);
-  //void Signal(void);
+  bool Wait(int TimeoutMs = 0);
+       ///< Waits at most TimeoutMs milliseconds for a call to Signal(), or
+       ///< forever if TimeoutMs is 0.
+       ///< \return Returns true if Signal() has been called, false it the given
+       ///< timeout has expired.
+  void Signal(void);
+       ///< Signals a caller of Wait() that the condition it is waiting for is met.
   };
 
 class cRwLock {
===================================================================
RCS file: ./RCS/transfer.c
retrieving revision 1.16
diff -u -r1.16 ./transfer.c
--- ./transfer.c	2004/03/07 14:40:15	1.16
+++ ./transfer.c	2004/10/10 11:23:18
@@ -9,10 +9,7 @@
 
 #include "transfer.h"
 
-//XXX+ also used in recorder.c - find a better place???
-// The size of the array used to buffer video data:
-// (must be larger than MINVIDEODATA - see remux.h)
-#define VIDEOBUFSIZE  MEGABYTE(1)
+#define TRANSFERBUFSIZE  MEGABYTE(1)
 #define POLLTIMEOUTS_BEFORE_DEVICECLEAR 3
 
 // --- cTransfer -------------------------------------------------------------
@@ -21,7 +18,8 @@
 :cReceiver(0, -1, 5, VPid, APid1, APid2, DPid1, DPid2)
 ,cThread("transfer")
 {
-  ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, TS_SIZE * 2, true);
+  ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
+  ringBuffer->SetTimeouts(0, 100);
   remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2);
   canToggleAudioTrack = false;
   audioTrack = 0xC0;
@@ -52,16 +50,10 @@
 void cTransfer::Receive(uchar *Data, int Length)
 {
   if (IsAttached()) {
-     int i = 0;
-     while (active && Length > 0) {
-           if (i++ > 10) {
-              ringBuffer->ReportOverflow(Length);
-              break;
-              }
-           int p = ringBuffer->Put(Data, Length);
-           Length -= p;
-           Data += p;
-           }
+     int p = ringBuffer->Put(Data, Length);
+     if (p != Length && active)
+        ringBuffer->ReportOverflow(Length - p);
+     return;
      }
 }
 
@@ -84,27 +76,32 @@
            }
            */
 
-        // Get data from the buffer:
-
-        int r;
-        const uchar *b = ringBuffer->Get(r);
-
-        // Play the data:
-
+        int r;//XXX use Count all the way???
+        uchar *b = ringBuffer->Get(r);
         if (b) {
-           int Count = r, Result;
-           uchar *p = remux->Process(b, Count, Result);
-           ringBuffer->Del(Count);
+           int Count = remux->Put(b, r);
+           if (Count)
+              ringBuffer->Del(Count);
+           uchar *p = remux->Get(Count);
            if (p) {
-              StripAudioPackets(p, Result, audioTrack);
-              while (Result > 0 && active) {
+              //XXX testing...
+              if (!gotBufferReserve) {
+                 if (ringBuffer->Available() < TRANSFERBUFSIZE / 3) {
+                    usleep(100000); // allow the buffer to collect some reserve
+                    }
+                 else
+                    gotBufferReserve = true;
+                 }
+              StripAudioPackets(p, Count, audioTrack);
+              while (Count > 0 && active) {
                     cPoller Poller;
                     if (DevicePoll(Poller, 100)) {
                        PollTimeouts = 0;
-                       int w = PlayVideo(p, Result);
+                       int w = PlayVideo(p, Count);
                        if (w > 0) {
                           p += w;
-                          Result -= w;
+                          Count -= w;
+                          remux->Del(w);
                           }
                        else if (w < 0 && FATALERRNO) {
                           LOG_ERROR;
@@ -116,13 +113,12 @@
                        if (PollTimeouts == POLLTIMEOUTS_BEFORE_DEVICECLEAR) {
                           dsyslog("clearing device because of consecutive poll timeouts");
                           DeviceClear();
+                          ringBuffer->Clear();
                           }
                        }
                     }
               }
            }
-        else
-           usleep(1); // this keeps the CPU load low
         }
 }
 
===================================================================
RCS file: ./RCS/vdr.c
retrieving revision 1.185
diff -u -r1.185 ./vdr.c
--- ./vdr.c	2004/07/27 07:21:22	1.185
+++ ./vdr.c	2004/10/10 12:47:56
@@ -516,6 +516,9 @@
            esyslog("emergency exit requested - shutting down");
            break;
            }
+#ifdef DEBUGRINGBUFFERS
+        cRingBufferLinear::PrintDebugRBL();
+#endif
         // Attach launched player control:
         cControl::Attach();
         // Make sure we have a visible programme in case device usage has changed:
