mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Implemented cTSBuffer
This commit is contained in:
parent
f24f820e7d
commit
c792c55297
8
HISTORY
8
HISTORY
@ -1431,7 +1431,7 @@ Video Disk Recorder Revision History
|
|||||||
time changed into the future (thanks to Matthias Schniedermeyer for reporting
|
time changed into the future (thanks to Matthias Schniedermeyer for reporting
|
||||||
this one).
|
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
|
- Fixed the 'newplugin' script to make it name the target for creating the
|
||||||
distribution package 'dist', as stated in the PLUGINS.html documentation.
|
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
|
cDevice::ProvidesChannel() is now used to determine whether a device can
|
||||||
receive a given channel, and by default this function returns false. So a
|
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.
|
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.
|
||||||
|
@ -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 SetPid(cPidHandle *Handle, int Type, bool On);
|
||||||
virtual bool OpenDvr(void);
|
virtual bool OpenDvr(void);
|
||||||
virtual void CloseDvr(void);
|
virtual void CloseDvr(void);
|
||||||
virtual int GetTSPacket(uchar *Data);
|
<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%>
|
||||||
|
virtual bool GetTSPacket(uchar *&Data);
|
||||||
|
<!--X1.1.9--></td></tr></table>
|
||||||
</pre></td></tr></table><p>
|
</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
|
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
|
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>
|
packets that are all 188 bytes in size. Each call to <tt>GetTSPacket()</tt>
|
||||||
|
104
device.c
104
device.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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"
|
#include "device.h"
|
||||||
@ -18,6 +18,8 @@
|
|||||||
#include "status.h"
|
#include "status.h"
|
||||||
#include "transfer.h"
|
#include "transfer.h"
|
||||||
|
|
||||||
|
// --- cDevice ---------------------------------------------------------------
|
||||||
|
|
||||||
// The default priority for non-primary devices:
|
// The default priority for non-primary devices:
|
||||||
#define DEFAULTPRIORITY -2
|
#define DEFAULTPRIORITY -2
|
||||||
|
|
||||||
@ -520,15 +522,13 @@ void cDevice::Action(void)
|
|||||||
dsyslog("receiver thread started on device %d (pid=%d)", CardIndex() + 1, getpid());
|
dsyslog("receiver thread started on device %d (pid=%d)", CardIndex() + 1, getpid());
|
||||||
|
|
||||||
if (OpenDvr()) {
|
if (OpenDvr()) {
|
||||||
uchar b[TS_SIZE];
|
|
||||||
time_t t = time(NULL);
|
time_t t = time(NULL);
|
||||||
active = true;
|
active = true;
|
||||||
for (; active;) {
|
for (; active;) {
|
||||||
// Read data from the DVR device:
|
// Read data from the DVR device:
|
||||||
int r = GetTSPacket(b);
|
uchar *b = NULL;
|
||||||
if (r == TS_SIZE) {
|
if (GetTSPacket(b)) {
|
||||||
if (*b == TS_SYNC_BYTE) {
|
if (b) {
|
||||||
// We're locked on to a TS packet
|
|
||||||
int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
|
int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
|
||||||
// Distribute the packet to all attached receivers:
|
// Distribute the packet to all attached receivers:
|
||||||
Lock();
|
Lock();
|
||||||
@ -537,12 +537,10 @@ void cDevice::Action(void)
|
|||||||
receiver[i]->Receive(b, TS_SIZE);
|
receiver[i]->Receive(b, TS_SIZE);
|
||||||
}
|
}
|
||||||
Unlock();
|
Unlock();
|
||||||
|
t = time(NULL);
|
||||||
}
|
}
|
||||||
t = time(NULL);
|
|
||||||
}
|
}
|
||||||
else if (r > 0)
|
else
|
||||||
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)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//XXX+ put this into the recorder??? or give the receiver a flag whether it wants this?
|
//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)
|
bool cDevice::AttachReceiver(cReceiver *Receiver)
|
||||||
@ -618,3 +616,85 @@ void cDevice::Detach(cReceiver *Receiver)
|
|||||||
Cancel(3);
|
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;
|
||||||
|
}
|
||||||
|
40
device.h
40
device.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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
|
#ifndef __DEVICE_H
|
||||||
@ -286,12 +286,13 @@ protected:
|
|||||||
// Stream for use in a cReceiver.
|
// Stream for use in a cReceiver.
|
||||||
virtual void CloseDvr(void);
|
virtual void CloseDvr(void);
|
||||||
// Shuts down the DVR.
|
// Shuts down the DVR.
|
||||||
virtual int GetTSPacket(uchar *Data);
|
virtual bool GetTSPacket(uchar *&Data);
|
||||||
// Gets exactly one TS packet from the DVR of this device and copies it
|
// Gets exactly one TS packet from the DVR of this device and returns
|
||||||
// into the given memory area (which is exactly 188 bytes in size).
|
// a pointer to it in Data. Only the first 188 bytes (TS_SIZE) Data
|
||||||
// Returns the number of bytes copied into Data (which must be 188).
|
// points to are valid and may be accessed. If there is currently no
|
||||||
// If there is currently no TS packet available, 0 should be returned.
|
// new data available, Data will be set to NULL. The function returns
|
||||||
// In case of a non recoverable error, returns -1.
|
// false in case of a non recoverable error, otherwise it returns true,
|
||||||
|
// even if Data is NULL.
|
||||||
public:
|
public:
|
||||||
int Ca(void) { return ca; }
|
int Ca(void) { return ca; }
|
||||||
// Returns the ca of the current receiving session.
|
// Returns the ca of the current receiving session.
|
||||||
@ -303,4 +304,29 @@ public:
|
|||||||
// Detaches the given receiver from this device.
|
// 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
|
#endif //__DEVICE_H
|
||||||
|
38
dvbdevice.c
38
dvbdevice.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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"
|
#include "dvbdevice.h"
|
||||||
@ -773,6 +773,8 @@ bool cDvbDevice::OpenDvr(void)
|
|||||||
{
|
{
|
||||||
CloseDvr();
|
CloseDvr();
|
||||||
fd_dvr = DvbOpen(DEV_DVB_DVR, CardIndex(), O_RDONLY | O_NONBLOCK, true);
|
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;
|
return fd_dvr >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -781,28 +783,28 @@ void cDvbDevice::CloseDvr(void)
|
|||||||
if (fd_dvr >= 0) {
|
if (fd_dvr >= 0) {
|
||||||
close(fd_dvr);
|
close(fd_dvr);
|
||||||
fd_dvr = -1;
|
fd_dvr = -1;
|
||||||
|
delete tsBuffer;
|
||||||
|
tsBuffer = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int cDvbDevice::GetTSPacket(uchar *Data)
|
bool cDvbDevice::GetTSPacket(uchar *&Data)
|
||||||
{
|
{
|
||||||
if (fd_dvr >= 0) {
|
if (tsBuffer) {
|
||||||
cPoller Poller(fd_dvr, false);
|
int r = tsBuffer->Read();
|
||||||
if (Poller.Poll(100)) {
|
if (r >= 0) {
|
||||||
int r = read(fd_dvr, Data, TS_SIZE);
|
Data = tsBuffer->Get();
|
||||||
if (r >= 0)
|
return true;
|
||||||
return r;
|
}
|
||||||
else if (FATALERRNO) {
|
else if (FATALERRNO) {
|
||||||
if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
|
if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
|
||||||
esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1);
|
esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1);
|
||||||
else {
|
else {
|
||||||
LOG_ERROR;
|
LOG_ERROR;
|
||||||
return -1;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
return false;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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
|
#ifndef __DVBDEVICE_H
|
||||||
@ -106,10 +106,12 @@ public:
|
|||||||
|
|
||||||
// Receiver facilities
|
// Receiver facilities
|
||||||
|
|
||||||
|
private:
|
||||||
|
cTSBuffer *tsBuffer;
|
||||||
protected:
|
protected:
|
||||||
virtual bool OpenDvr(void);
|
virtual bool OpenDvr(void);
|
||||||
virtual void CloseDvr(void);
|
virtual void CloseDvr(void);
|
||||||
virtual int GetTSPacket(uchar *Data);
|
virtual bool GetTSPacket(uchar *&Data);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //__DVBDEVICE_H
|
#endif //__DVBDEVICE_H
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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>
|
#include <stdarg.h>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
// The size of the array used to buffer video data:
|
// The size of the array used to buffer video data:
|
||||||
// (must be larger than MINVIDEODATA - see remux.h)
|
// (must be larger than MINVIDEODATA - see remux.h)
|
||||||
#define VIDEOBUFSIZE MEGABYTE(1)
|
#define VIDEOBUFSIZE MEGABYTE(5)
|
||||||
|
|
||||||
#define MINFREEDISKSPACE (512) // MB
|
#define MINFREEDISKSPACE (512) // MB
|
||||||
#define DISKCHECKINTERVAL 100 // seconds
|
#define DISKCHECKINTERVAL 100 // seconds
|
||||||
|
Loading…
Reference in New Issue
Block a user