1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Implemented cTSBuffer

This commit is contained in:
Klaus Schmidinger 2002-09-08 09:03:10 +02:00
parent f24f820e7d
commit c792c55297
7 changed files with 162 additions and 44 deletions

View File

@ -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.

View File

@ -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);
<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
virtual bool GetTSPacket(uchar *&amp;Data);
<!--X1.1.9--></td></tr></table>
</pre></td></tr></table><p>
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 <tt>GetTSPacket()</tt>

104
device.c
View File

@ -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);
}
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;
}

View File

@ -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

View File

@ -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;
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 -1;
return false;
}
}
return true;
}
return 0;
}
else
return -1;
return false;
}

View File

@ -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

View File

@ -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 <stdarg.h>
@ -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