From c792c552979366bf781c9d9c817487bdd2926189 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 8 Sep 2002 09:03:10 +0200 Subject: [PATCH] Implemented cTSBuffer --- HISTORY | 8 +++- PLUGINS.html | 6 ++- device.c | 104 +++++++++++++++++++++++++++++++++++++++++++++------ device.h | 40 ++++++++++++++++---- dvbdevice.c | 38 ++++++++++--------- dvbdevice.h | 6 ++- recorder.c | 4 +- 7 files changed, 162 insertions(+), 44 deletions(-) diff --git a/HISTORY b/HISTORY index 146c1d33..88998b3a 100644 --- a/HISTORY +++ b/HISTORY @@ -1431,7 +1431,7 @@ Video Disk Recorder Revision History time changed into the future (thanks to Matthias Schniedermeyer for reporting this one). -2002-09-04: Version 1.1.9 +2002-09-08: Version 1.1.9 - Fixed the 'newplugin' script to make it name the target for creating the distribution package 'dist', as stated in the PLUGINS.html documentation. @@ -1444,3 +1444,9 @@ Video Disk Recorder Revision History cDevice::ProvidesChannel() is now used to determine whether a device can receive a given channel, and by default this function returns false. So a device that is a pure replaying device doesn't need to do anything here. +- Increased the recorder buffer size to 5MB in order to be able to better handle + multiple recordings. +- Implemented cTSBuffer for better handling TS packet buffering in derived + cDevice classes. +- Changed the interface if cDevice::GetTSPacket() to avoid unnecessary copying + of data. diff --git a/PLUGINS.html b/PLUGINS.html index f67ddbed..5df0cd99 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -1206,10 +1206,12 @@ A device that can be used for recording must implement the functions virtual bool SetPid(cPidHandle *Handle, int Type, bool On); virtual bool OpenDvr(void); virtual void CloseDvr(void); -virtual int GetTSPacket(uchar *Data); +
  +virtual bool GetTSPacket(uchar *&Data); +

-which allow VDR to set the PIDs that shall be recorded, set up the device fro +which allow VDR to set the PIDs that shall be recorded, set up the device for recording (and shut it down again), and receive the MPEG data stream. The data must be delivered in the form of a Transport Stream (TS), which consists of packets that are all 188 bytes in size. Each call to GetTSPacket() diff --git a/device.c b/device.c index 90b11f57..ef662ce8 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 1.15 2002/09/06 14:02:19 kls Exp $ + * $Id: device.c 1.16 2002/09/08 09:03:10 kls Exp $ */ #include "device.h" @@ -18,6 +18,8 @@ #include "status.h" #include "transfer.h" +// --- cDevice --------------------------------------------------------------- + // The default priority for non-primary devices: #define DEFAULTPRIORITY -2 @@ -520,15 +522,13 @@ void cDevice::Action(void) dsyslog("receiver thread started on device %d (pid=%d)", CardIndex() + 1, getpid()); if (OpenDvr()) { - uchar b[TS_SIZE]; time_t t = time(NULL); active = true; for (; active;) { // Read data from the DVR device: - int r = GetTSPacket(b); - if (r == TS_SIZE) { - if (*b == TS_SYNC_BYTE) { - // We're locked on to a TS packet + uchar *b = NULL; + if (GetTSPacket(b)) { + if (b) { int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2]; // Distribute the packet to all attached receivers: Lock(); @@ -537,12 +537,10 @@ void cDevice::Action(void) receiver[i]->Receive(b, TS_SIZE); } Unlock(); + t = time(NULL); } - t = time(NULL); } - else if (r > 0) - esyslog("ERROR: got incomplete TS packet (%d bytes) on device %d", r, CardIndex() + 1);//XXX+ TODO do we have to read the rest??? - else if (r < 0) + else break; //XXX+ put this into the recorder??? or give the receiver a flag whether it wants this? @@ -567,9 +565,9 @@ void cDevice::CloseDvr(void) { } -int cDevice::GetTSPacket(uchar *Data) +bool cDevice::GetTSPacket(uchar *&Data) { - return -1; + return false; } bool cDevice::AttachReceiver(cReceiver *Receiver) @@ -618,3 +616,85 @@ void cDevice::Detach(cReceiver *Receiver) Cancel(3); } } + +// --- cTSBuffer ------------------------------------------------------------- + +cTSBuffer::cTSBuffer(int File, int Size, int 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; +} + +cTSBuffer::~cTSBuffer() +{ + free(buf); +} + +int cTSBuffer::Read(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; + } + return -1; +} + +uchar *cTSBuffer::Get(void) +{ + if (Used() >= TS_SIZE) { + uchar *p = buf + tsRead; + 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; + } + } + if ((tsRead = tsMax) >= size) + tsRead = 0; + 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; + return p; + } + return NULL; +} diff --git a/device.h b/device.h index 16e831bc..d1fce5ca 100644 --- a/device.h +++ b/device.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.h 1.12 2002/09/06 14:04:52 kls Exp $ + * $Id: device.h 1.13 2002/09/08 08:56:46 kls Exp $ */ #ifndef __DEVICE_H @@ -286,12 +286,13 @@ protected: // Stream for use in a cReceiver. virtual void CloseDvr(void); // Shuts down the DVR. - virtual int GetTSPacket(uchar *Data); - // Gets exactly one TS packet from the DVR of this device and copies it - // into the given memory area (which is exactly 188 bytes in size). - // Returns the number of bytes copied into Data (which must be 188). - // If there is currently no TS packet available, 0 should be returned. - // In case of a non recoverable error, returns -1. + virtual bool GetTSPacket(uchar *&Data); + // Gets exactly one TS packet from the DVR of this device and returns + // a pointer to it in Data. Only the first 188 bytes (TS_SIZE) Data + // points to are valid and may be accessed. If there is currently no + // new data available, Data will be set to NULL. The function returns + // false in case of a non recoverable error, otherwise it returns true, + // even if Data is NULL. public: int Ca(void) { return ca; } // Returns the ca of the current receiving session. @@ -303,4 +304,29 @@ public: // Detaches the given receiver from this device. }; +// Derived cDevice classes that can receive channels will have to provide +// Transport Stream (TS) packets one at a time. cTSBuffer implements a +// simple buffer that allows the device to read a larger amount of data +// from the driver with each call to Read(), thus avoiding the overhead +// of getting each TS packet separately from the driver. It also makes +// sure the returned data points to a TS packet and automatically +// re-synchronizes after broken packet. + +class cTSBuffer { +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; } +public: + cTSBuffer(int File, int Size, int CardIndex); + ~cTSBuffer(); + int Read(void); + uchar *Get(void); + }; + #endif //__DEVICE_H diff --git a/dvbdevice.c b/dvbdevice.c index 83613358..3d971b59 100644 --- a/dvbdevice.c +++ b/dvbdevice.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.c 1.10 2002/09/06 14:09:55 kls Exp $ + * $Id: dvbdevice.c 1.11 2002/09/07 13:39:49 kls Exp $ */ #include "dvbdevice.h" @@ -773,6 +773,8 @@ bool cDvbDevice::OpenDvr(void) { CloseDvr(); fd_dvr = DvbOpen(DEV_DVB_DVR, CardIndex(), O_RDONLY | O_NONBLOCK, true); + if (fd_dvr >= 0) + tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(2), CardIndex() + 1); return fd_dvr >= 0; } @@ -781,28 +783,28 @@ void cDvbDevice::CloseDvr(void) if (fd_dvr >= 0) { close(fd_dvr); fd_dvr = -1; + delete tsBuffer; + tsBuffer = NULL; } } -int cDvbDevice::GetTSPacket(uchar *Data) +bool cDvbDevice::GetTSPacket(uchar *&Data) { - if (fd_dvr >= 0) { - cPoller Poller(fd_dvr, false); - if (Poller.Poll(100)) { - int r = read(fd_dvr, Data, TS_SIZE); - if (r >= 0) - return r; - else if (FATALERRNO) { - if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library - esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1); - else { - LOG_ERROR; - return -1; - } + if (tsBuffer) { + int r = tsBuffer->Read(); + if (r >= 0) { + Data = tsBuffer->Get(); + return true; + } + else if (FATALERRNO) { + if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library + esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1); + else { + LOG_ERROR; + return false; } } - return 0; + return true; } - else - return -1; + return false; } diff --git a/dvbdevice.h b/dvbdevice.h index 05ac4437..7de89665 100644 --- a/dvbdevice.h +++ b/dvbdevice.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.h 1.8 2002/09/06 14:10:17 kls Exp $ + * $Id: dvbdevice.h 1.9 2002/09/07 09:06:40 kls Exp $ */ #ifndef __DVBDEVICE_H @@ -106,10 +106,12 @@ public: // Receiver facilities +private: + cTSBuffer *tsBuffer; protected: virtual bool OpenDvr(void); virtual void CloseDvr(void); - virtual int GetTSPacket(uchar *Data); + virtual bool GetTSPacket(uchar *&Data); }; #endif //__DVBDEVICE_H diff --git a/recorder.c b/recorder.c index 3a0941bb..ba7dc06d 100644 --- a/recorder.c +++ b/recorder.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recorder.c 1.1 2002/06/16 10:03:25 kls Exp $ + * $Id: recorder.c 1.2 2002/09/07 13:40:23 kls Exp $ */ #include @@ -14,7 +14,7 @@ // The size of the array used to buffer video data: // (must be larger than MINVIDEODATA - see remux.h) -#define VIDEOBUFSIZE MEGABYTE(1) +#define VIDEOBUFSIZE MEGABYTE(5) #define MINFREEDISKSPACE (512) // MB #define DISKCHECKINTERVAL 100 // seconds