mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Improved buffer handling
This commit is contained in:
parent
15030f6ace
commit
6415cc900d
@ -723,6 +723,7 @@ Sascha Volkenandt <sascha@akv-soft.de>
|
|||||||
for suggesting to ignore unused "none" color entries in XPM files written by
|
for suggesting to ignore unused "none" color entries in XPM files written by
|
||||||
some broken graphics tools
|
some broken graphics tools
|
||||||
for fixing a memory leak in theme description handling
|
for fixing a memory leak in theme description handling
|
||||||
|
for pointing out a "near miss" condition in cCondVar
|
||||||
|
|
||||||
Malcolm Caldwell <malcolm.caldwell@ntu.edu.au>
|
Malcolm Caldwell <malcolm.caldwell@ntu.edu.au>
|
||||||
for modifying LOF handling to allow for C-band reception
|
for modifying LOF handling to allow for C-band reception
|
||||||
@ -1037,6 +1038,9 @@ Marco Schl
|
|||||||
for suggesting to avoiding flashing effects in the OSD of full featured DVB cards
|
for suggesting to avoiding flashing effects in the OSD of full featured DVB cards
|
||||||
by explicitly clearing the OSD windows before opening them
|
by explicitly clearing the OSD windows before opening them
|
||||||
for fixing a possible NULL pointer assignment in cMenuText::SetText()
|
for fixing a possible NULL pointer assignment in cMenuText::SetText()
|
||||||
|
for doing some testing regarding buffer performance and giving me some hints that
|
||||||
|
finally lead to finding out that the basic problem causing buffer overflows was in
|
||||||
|
EnableGet()/EnablePut() being called too often
|
||||||
|
|
||||||
Jürgen Schmitz <j.schmitz@web.de>
|
Jürgen Schmitz <j.schmitz@web.de>
|
||||||
for reporting a bug in displaying the current channel when switching via the SVDRP
|
for reporting a bug in displaying the current channel when switching via the SVDRP
|
||||||
|
32
HISTORY
32
HISTORY
@ -2967,7 +2967,7 @@ Video Disk Recorder Revision History
|
|||||||
- The 'radio' channel icon is now only displayed in the ST:TNG skin if the channel
|
- The 'radio' channel icon is now only displayed in the ST:TNG skin if the channel
|
||||||
actually has an APID.
|
actually has an APID.
|
||||||
|
|
||||||
2004-08-08: Version 1.3.13
|
2004-10-16: Version 1.3.13
|
||||||
|
|
||||||
- Fixed checking for the presence of NPTL (thanks to Jouni Karvo).
|
- Fixed checking for the presence of NPTL (thanks to Jouni Karvo).
|
||||||
- Making sure section filters are only set if the device actually has a lock
|
- Making sure section filters are only set if the device actually has a lock
|
||||||
@ -2976,3 +2976,33 @@ Video Disk Recorder Revision History
|
|||||||
Marco Schlüssler).
|
Marco Schlüssler).
|
||||||
- Fixed a crash in case the last line in channels.conf is a group separator and
|
- Fixed a crash in case the last line in channels.conf is a group separator and
|
||||||
that group is selected in the channel display (thanks to Dick Streefland).
|
that group is selected in the channel display (thanks to Dick Streefland).
|
||||||
|
- Added cRingBufferLinear::Read() to read directly from a file handle into the
|
||||||
|
ring buffer.
|
||||||
|
- Using timeouts in ring buffers to avoid 'usleep()'.
|
||||||
|
- Clearing the 'Transfer Mode' ring buffer after clearing the device to avoid
|
||||||
|
an "almost full" ring buffer.
|
||||||
|
- Removed locking from cRingBufferLinear for better performance under high load.
|
||||||
|
- Using a cRingBufferLinear in cRemux to avoid unnecessary copying of data.
|
||||||
|
- Using a cRingBufferLinear in cTSBuffer and filling it in a separate thread
|
||||||
|
to avoid buffer overflows. Plugins using cTSBuffer will need to remove the
|
||||||
|
call to the now obsolete Read() function (see cDvbDevice::GetTSPacket() for
|
||||||
|
the new usage of cTSBuffer).
|
||||||
|
- cRemux::Process() has been split into Put(), Get() and Del() to allow for a
|
||||||
|
better decoupling of the remuxing and disk writing process. Plugins using
|
||||||
|
cRemux will need to be modified accordingly.
|
||||||
|
- The actual disk writing in recordings is now done in a separate thread to
|
||||||
|
improve the overall throughput.
|
||||||
|
- Changed cRemux so that it returns the maximum available amount of data with
|
||||||
|
each call, not just 2048 byte.
|
||||||
|
- Added a visual display of all cRingBufferLinear buffers for debugging. To
|
||||||
|
activate it, define DEBUGRINGBUFFERS in ringbuffer.h.
|
||||||
|
- Instead of cCondVar now using the new cCondWait (which also avoids a possible
|
||||||
|
"near miss" condition; thanks to Sascha Volkenandt for pointing out this one).
|
||||||
|
cCondVar is still present for plugins that use it (and VDR itself also still
|
||||||
|
uses it in cRemote).
|
||||||
|
- The cRingBuffer now does EnableGet()/EnablePut() only if the buffer is more than
|
||||||
|
one third full or empty, respectively. This dramatically improves recording
|
||||||
|
performance and reduces system load (thanks to Marco Schlüßler for doing some
|
||||||
|
testing regarding buffer performance and giving me some hints that finally led
|
||||||
|
to finding out that this was the basic problem causing buffer overflows).
|
||||||
|
- Improved Transfer Mode (thanks to Marco Schlüßler for suggestions and testing).
|
||||||
|
@ -24,3 +24,7 @@ VDR Plugin 'sky' Revision History
|
|||||||
- Added automatic DST detection to getskyepg.pl.
|
- Added automatic DST detection to getskyepg.pl.
|
||||||
- Fixed handling receivers, so that a recording on the same channel
|
- Fixed handling receivers, so that a recording on the same channel
|
||||||
won't interrupt an ongoing Transfer mode.
|
won't interrupt an ongoing Transfer mode.
|
||||||
|
|
||||||
|
2004-10-16: Version 0.3.1
|
||||||
|
|
||||||
|
- Improved buffer handling.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: sky.c 1.6 2004/02/15 14:59:46 kls Exp $
|
* $Id: sky.c 1.7 2004/10/16 09:10:06 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
#include <vdr/plugin.h>
|
#include <vdr/plugin.h>
|
||||||
#include <vdr/sources.h>
|
#include <vdr/sources.h>
|
||||||
|
|
||||||
static const char *VERSION = "0.3.0";
|
static const char *VERSION = "0.3.1";
|
||||||
static const char *DESCRIPTION = "Sky Digibox interface";
|
static const char *DESCRIPTION = "Sky Digibox interface";
|
||||||
|
|
||||||
// --- cDigiboxDevice --------------------------------------------------------
|
// --- cDigiboxDevice --------------------------------------------------------
|
||||||
@ -140,7 +140,7 @@ bool cDigiboxDevice::OpenDvr(void)
|
|||||||
CloseDvr();
|
CloseDvr();
|
||||||
fd_dvr = open("/dev/video2", O_RDONLY | O_NONBLOCK);//XXX parameter???
|
fd_dvr = open("/dev/video2", O_RDONLY | O_NONBLOCK);//XXX parameter???
|
||||||
if (fd_dvr >= 0)
|
if (fd_dvr >= 0)
|
||||||
tsBuffer = new cTSBuffer(fd_dvr, KILOBYTE(256), CardIndex() + 1);
|
tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(2), CardIndex() + 1);
|
||||||
return fd_dvr >= 0;
|
return fd_dvr >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,24 +157,16 @@ void cDigiboxDevice::CloseDvr(void)
|
|||||||
bool cDigiboxDevice::GetTSPacket(uchar *&Data)
|
bool cDigiboxDevice::GetTSPacket(uchar *&Data)
|
||||||
{
|
{
|
||||||
if (tsBuffer) {
|
if (tsBuffer) {
|
||||||
int r = tsBuffer->Read();
|
Data = tsBuffer->Get();
|
||||||
if (r >= 0) {
|
if (Data) {
|
||||||
Data = tsBuffer->Get();
|
// insert the actual PIDs:
|
||||||
if (Data) {
|
int Pid = (((uint16_t)Data[1] & PID_MASK_HI) << 8) | Data[2];
|
||||||
// insert the actual PIDs:
|
if (Pid == DUMMYAPID)
|
||||||
int Pid = (((uint16_t)Data[1] & PID_MASK_HI) << 8) | Data[2];
|
Pid = apid;
|
||||||
if (Pid == DUMMYAPID)
|
else if (Pid == DUMMYVPID)
|
||||||
Pid = apid;
|
Pid = vpid;
|
||||||
else if (Pid == DUMMYVPID)
|
Data[1] = ((Pid >> 8) & 0xFF) | (Data[1] & ~PID_MASK_HI);
|
||||||
Pid = vpid;
|
Data[2] = Pid & 0xFF;
|
||||||
Data[1] = ((Pid >> 8) & 0xFF) | (Data[1] & ~PID_MASK_HI);
|
|
||||||
Data[2] = Pid & 0xFF;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (FATALERRNO) {
|
|
||||||
LOG_ERROR;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
98
device.c
98
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.56 2004/06/19 08:51:05 kls Exp $
|
* $Id: device.c 1.57 2004/10/09 12:53:02 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
@ -801,79 +801,67 @@ void cDevice::Detach(cReceiver *Receiver)
|
|||||||
|
|
||||||
cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
|
cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
|
||||||
{
|
{
|
||||||
|
SetDescription("TS buffer on device %d", CardIndex);
|
||||||
f = File;
|
f = File;
|
||||||
size = Size / TS_SIZE * TS_SIZE;
|
|
||||||
cardIndex = CardIndex;
|
cardIndex = CardIndex;
|
||||||
tsRead = tsWrite = 0;
|
active = false;
|
||||||
buf = (f >= 0 && size >= TS_SIZE) ? MALLOC(uchar, size + TS_SIZE) : NULL;
|
delivered = false;
|
||||||
// the '+ TS_SIZE' allocates some extra space for handling packets that got split by a buffer roll-over
|
ringBuffer = new cRingBufferLinear(Size, TS_SIZE, true, "TS");
|
||||||
firstRead = true;
|
ringBuffer->SetTimeouts(100, 100);
|
||||||
|
Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
cTSBuffer::~cTSBuffer()
|
cTSBuffer::~cTSBuffer()
|
||||||
{
|
{
|
||||||
free(buf);
|
active = false;
|
||||||
|
Cancel(3);
|
||||||
|
delete ringBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cTSBuffer::Read(void)
|
void cTSBuffer::Action(void)
|
||||||
{
|
{
|
||||||
if (buf) {
|
if (ringBuffer) {
|
||||||
cPoller Poller(f, false);
|
bool firstRead = true;
|
||||||
bool repeat;
|
cPoller Poller(f);
|
||||||
int total = 0;
|
active = true;
|
||||||
do {
|
for (; active;) {
|
||||||
repeat = false;
|
if (firstRead || Poller.Poll(100)) {
|
||||||
if (firstRead || Used() > TS_SIZE || Poller.Poll(100)) { // only wait if there's not enough data in the buffer
|
firstRead = false;
|
||||||
firstRead = false;
|
int r = ringBuffer->Read(f);
|
||||||
if (tsRead == tsWrite)
|
if (r < 0 && FATALERRNO) {
|
||||||
tsRead = tsWrite = 0; // keep the maximum buffer space available
|
if (errno == EOVERFLOW)
|
||||||
if (tsWrite >= size && tsRead > 0)
|
esyslog("ERROR: driver buffer overflow on device %d", cardIndex);
|
||||||
tsWrite = 0;
|
else {
|
||||||
int free = tsRead <= tsWrite ? size - tsWrite : tsRead - tsWrite - 1;
|
LOG_ERROR;
|
||||||
if (free > 0) {
|
break;
|
||||||
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)
|
uchar *cTSBuffer::Get(void)
|
||||||
{
|
{
|
||||||
if (Used() >= TS_SIZE) {
|
int Count = 0;
|
||||||
uchar *p = buf + tsRead;
|
if (delivered) {
|
||||||
|
ringBuffer->Del(TS_SIZE);
|
||||||
|
delivered = false;
|
||||||
|
}
|
||||||
|
uchar *p = ringBuffer->Get(Count);
|
||||||
|
if (p && Count >= TS_SIZE) {
|
||||||
if (*p != TS_SYNC_BYTE) {
|
if (*p != TS_SYNC_BYTE) {
|
||||||
esyslog("ERROR: not sync'ed to TS packet on device %d", cardIndex);
|
for (int i = 1; i < Count; i++) {
|
||||||
int tsMax = tsRead < tsWrite ? tsWrite : size;
|
if (p[i] == TS_SYNC_BYTE) {
|
||||||
for (int i = tsRead; i < tsMax; i++) {
|
Count = i;
|
||||||
if (buf[i] == TS_SYNC_BYTE) {
|
break;
|
||||||
esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d", i - tsRead, cardIndex);
|
|
||||||
tsRead = i;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((tsRead = tsMax) >= size)
|
ringBuffer->Del(Count);
|
||||||
tsRead = 0;
|
esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d", Count, cardIndex);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (tsRead + TS_SIZE > size) {
|
delivered = true;
|
||||||
// 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 p;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
16
device.h
16
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.44 2004/06/19 08:50:37 kls Exp $
|
* $Id: device.h 1.45 2004/09/24 14:07:22 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __DEVICE_H
|
#ifndef __DEVICE_H
|
||||||
@ -15,6 +15,7 @@
|
|||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
#include "nit.h"
|
#include "nit.h"
|
||||||
#include "pat.h"
|
#include "pat.h"
|
||||||
|
#include "ringbuffer.h"
|
||||||
#include "sdt.h"
|
#include "sdt.h"
|
||||||
#include "sections.h"
|
#include "sections.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
@ -437,20 +438,17 @@ public:
|
|||||||
/// sure the returned data points to a TS packet and automatically
|
/// sure the returned data points to a TS packet and automatically
|
||||||
/// re-synchronizes after broken packets.
|
/// re-synchronizes after broken packets.
|
||||||
|
|
||||||
class cTSBuffer {
|
class cTSBuffer : public cThread {
|
||||||
private:
|
private:
|
||||||
int f;
|
int f;
|
||||||
int size;
|
|
||||||
int cardIndex;
|
int cardIndex;
|
||||||
int tsRead;
|
bool active;
|
||||||
int tsWrite;
|
bool delivered;
|
||||||
uchar *buf;
|
cRingBufferLinear *ringBuffer;
|
||||||
bool firstRead;
|
virtual void Action(void);
|
||||||
int Used(void) { return tsRead <= tsWrite ? tsWrite - tsRead : size - tsRead + tsWrite; }
|
|
||||||
public:
|
public:
|
||||||
cTSBuffer(int File, int Size, int CardIndex);
|
cTSBuffer(int File, int Size, int CardIndex);
|
||||||
~cTSBuffer();
|
~cTSBuffer();
|
||||||
int Read(void);
|
|
||||||
uchar *Get(void);
|
uchar *Get(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
36
dvbdevice.c
36
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.93 2004/06/19 13:48:00 kls Exp $
|
* $Id: dvbdevice.c 1.94 2004/10/15 13:07:52 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dvbdevice.h"
|
#include "dvbdevice.h"
|
||||||
@ -78,8 +78,7 @@ private:
|
|||||||
bool useCa;
|
bool useCa;
|
||||||
time_t startTime;
|
time_t startTime;
|
||||||
eTunerStatus tunerStatus;
|
eTunerStatus tunerStatus;
|
||||||
cMutex mutex;
|
cCondWait newSet;
|
||||||
cCondVar newSet;
|
|
||||||
bool SetFrontend(void);
|
bool SetFrontend(void);
|
||||||
virtual void Action(void);
|
virtual void Action(void);
|
||||||
public:
|
public:
|
||||||
@ -111,7 +110,7 @@ cDvbTuner::~cDvbTuner()
|
|||||||
{
|
{
|
||||||
active = false;
|
active = false;
|
||||||
tunerStatus = tsIdle;
|
tunerStatus = tsIdle;
|
||||||
newSet.Broadcast();
|
newSet.Signal();
|
||||||
Cancel(3);
|
Cancel(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +121,7 @@ bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
|
|||||||
|
|
||||||
void cDvbTuner::Set(const cChannel *Channel, bool Tune, bool UseCa)
|
void cDvbTuner::Set(const cChannel *Channel, bool Tune, bool UseCa)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutex);
|
Lock();
|
||||||
if (Tune)
|
if (Tune)
|
||||||
tunerStatus = tsSet;
|
tunerStatus = tsSet;
|
||||||
else if (tunerStatus == tsCam)
|
else if (tunerStatus == tsCam)
|
||||||
@ -131,7 +130,8 @@ void cDvbTuner::Set(const cChannel *Channel, bool Tune, bool UseCa)
|
|||||||
if (Channel->Ca() && tunerStatus != tsCam)
|
if (Channel->Ca() && tunerStatus != tsCam)
|
||||||
startTime = time(NULL);
|
startTime = time(NULL);
|
||||||
channel = *Channel;
|
channel = *Channel;
|
||||||
newSet.Broadcast();
|
Unlock();
|
||||||
|
newSet.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int FrequencyToHz(unsigned int f)
|
static unsigned int FrequencyToHz(unsigned int f)
|
||||||
@ -252,7 +252,7 @@ void cDvbTuner::Action(void)
|
|||||||
{
|
{
|
||||||
active = true;
|
active = true;
|
||||||
while (active) {
|
while (active) {
|
||||||
cMutexLock MutexLock(&mutex);
|
Lock();
|
||||||
if (tunerStatus == tsSet)
|
if (tunerStatus == tsSet)
|
||||||
tunerStatus = SetFrontend() ? tsTuned : tsIdle;
|
tunerStatus = SetFrontend() ? tsTuned : tsIdle;
|
||||||
if (tunerStatus == tsTuned) {
|
if (tunerStatus == tsTuned) {
|
||||||
@ -267,7 +267,6 @@ void cDvbTuner::Action(void)
|
|||||||
if (event.status & FE_REINIT) {
|
if (event.status & FE_REINIT) {
|
||||||
tunerStatus = tsSet;
|
tunerStatus = tsSet;
|
||||||
esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
|
esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,8 +291,9 @@ void cDvbTuner::Action(void)
|
|||||||
else if (tunerStatus > tsLocked)
|
else if (tunerStatus > tsLocked)
|
||||||
tunerStatus = tsLocked;
|
tunerStatus = tsLocked;
|
||||||
}
|
}
|
||||||
|
Unlock();
|
||||||
// in the beginning we loop more often to let the CAM connection start up fast
|
// 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 @@ bool cDvbDevice::OpenDvr(void)
|
|||||||
void cDvbDevice::CloseDvr(void)
|
void cDvbDevice::CloseDvr(void)
|
||||||
{
|
{
|
||||||
if (fd_dvr >= 0) {
|
if (fd_dvr >= 0) {
|
||||||
close(fd_dvr);
|
|
||||||
fd_dvr = -1;
|
|
||||||
delete tsBuffer;
|
delete tsBuffer;
|
||||||
tsBuffer = NULL;
|
tsBuffer = NULL;
|
||||||
|
close(fd_dvr);
|
||||||
|
fd_dvr = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cDvbDevice::GetTSPacket(uchar *&Data)
|
bool cDvbDevice::GetTSPacket(uchar *&Data)
|
||||||
{
|
{
|
||||||
if (tsBuffer) {
|
if (tsBuffer) {
|
||||||
int r = tsBuffer->Read();
|
Data = tsBuffer->Get();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
26
dvbplayer.c
26
dvbplayer.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: dvbplayer.c 1.24 2004/06/19 08:55:49 kls Exp $
|
* $Id: dvbplayer.c 1.25 2004/10/15 13:07:55 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dvbplayer.h"
|
#include "dvbplayer.h"
|
||||||
@ -80,8 +80,7 @@ private:
|
|||||||
int length;
|
int length;
|
||||||
bool hasData;
|
bool hasData;
|
||||||
bool active;
|
bool active;
|
||||||
cMutex mutex;
|
cCondWait newSet;
|
||||||
cCondVar newSet;
|
|
||||||
protected:
|
protected:
|
||||||
void Action(void);
|
void Action(void);
|
||||||
public:
|
public:
|
||||||
@ -106,20 +105,21 @@ cNonBlockingFileReader::cNonBlockingFileReader(void)
|
|||||||
cNonBlockingFileReader::~cNonBlockingFileReader()
|
cNonBlockingFileReader::~cNonBlockingFileReader()
|
||||||
{
|
{
|
||||||
active = false;
|
active = false;
|
||||||
newSet.Broadcast();
|
newSet.Signal();
|
||||||
Cancel(3);
|
Cancel(3);
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cNonBlockingFileReader::Clear(void)
|
void cNonBlockingFileReader::Clear(void)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&mutex);
|
Lock();
|
||||||
f = -1;
|
f = -1;
|
||||||
free(buffer);
|
free(buffer);
|
||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
wanted = length = 0;
|
wanted = length = 0;
|
||||||
hasData = false;
|
hasData = false;
|
||||||
newSet.Broadcast();
|
Unlock();
|
||||||
|
newSet.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
int cNonBlockingFileReader::Read(int FileHandle, uchar *Buffer, int Length)
|
int cNonBlockingFileReader::Read(int FileHandle, uchar *Buffer, int Length)
|
||||||
@ -139,7 +139,7 @@ int cNonBlockingFileReader::Read(int FileHandle, uchar *Buffer, int Length)
|
|||||||
wanted = Length;
|
wanted = Length;
|
||||||
length = 0;
|
length = 0;
|
||||||
hasData = false;
|
hasData = false;
|
||||||
newSet.Broadcast();
|
newSet.Signal();
|
||||||
}
|
}
|
||||||
errno = EAGAIN;
|
errno = EAGAIN;
|
||||||
return -1;
|
return -1;
|
||||||
@ -149,7 +149,7 @@ void cNonBlockingFileReader::Action(void)
|
|||||||
{
|
{
|
||||||
active = true;
|
active = true;
|
||||||
while (active) {
|
while (active) {
|
||||||
cMutexLock MutexLock(&mutex);
|
Lock();
|
||||||
if (!hasData && f >= 0 && buffer) {
|
if (!hasData && f >= 0 && buffer) {
|
||||||
int r = safe_read(f, buffer + length, wanted - length);
|
int r = safe_read(f, buffer + length, wanted - length);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
@ -163,16 +163,14 @@ void cNonBlockingFileReader::Action(void)
|
|||||||
hasData = true;
|
hasData = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newSet.TimedWait(mutex, 1000);
|
Unlock();
|
||||||
|
newSet.Wait(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cDvbPlayer ------------------------------------------------------------
|
// --- cDvbPlayer ------------------------------------------------------------
|
||||||
|
|
||||||
//XXX+ also used in recorder.c - find a better place???
|
#define PLAYERBUFSIZE MEGABYTE(1)
|
||||||
// The size of the array used to buffer video data:
|
|
||||||
// (must be larger than MINVIDEODATA - see remux.h)
|
|
||||||
#define VIDEOBUFSIZE MEGABYTE(1)
|
|
||||||
|
|
||||||
// The number of frames to back up when resuming an interrupted replay session:
|
// The number of frames to back up when resuming an interrupted replay session:
|
||||||
#define RESUMEBACKUP (10 * FRAMESPERSEC)
|
#define RESUMEBACKUP (10 * FRAMESPERSEC)
|
||||||
@ -257,7 +255,7 @@ cDvbPlayer::cDvbPlayer(const char *FileName)
|
|||||||
replayFile = fileName->Open();
|
replayFile = fileName->Open();
|
||||||
if (replayFile < 0)
|
if (replayFile < 0)
|
||||||
return;
|
return;
|
||||||
ringBuffer = new cRingBufferFrame(VIDEOBUFSIZE);
|
ringBuffer = new cRingBufferFrame(PLAYERBUFSIZE);
|
||||||
// Create the index file:
|
// Create the index file:
|
||||||
index = new cIndexFile(FileName, false);
|
index = new cIndexFile(FileName, false);
|
||||||
if (!index)
|
if (!index)
|
||||||
|
170
recorder.c
170
recorder.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: recorder.c 1.10 2004/03/20 10:33:21 kls Exp $
|
* $Id: recorder.c 1.11 2004/10/16 09:23:01 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@ -12,9 +12,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "recorder.h"
|
#include "recorder.h"
|
||||||
|
|
||||||
// The size of the array used to buffer video data:
|
#define RECORDERBUFSIZE MEGABYTE(5)
|
||||||
// (must be larger than MINVIDEODATA - see remux.h)
|
|
||||||
#define VIDEOBUFSIZE MEGABYTE(5)
|
|
||||||
|
|
||||||
// The maximum time we wait before assuming that a recorded video data stream
|
// The maximum time we wait before assuming that a recorded video data stream
|
||||||
// is broken:
|
// is broken:
|
||||||
@ -23,25 +21,35 @@
|
|||||||
#define MINFREEDISKSPACE (512) // MB
|
#define MINFREEDISKSPACE (512) // MB
|
||||||
#define DISKCHECKINTERVAL 100 // seconds
|
#define DISKCHECKINTERVAL 100 // seconds
|
||||||
|
|
||||||
cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2)
|
class cFileWriter : public cThread {
|
||||||
:cReceiver(Ca, Priority, Setup.RecordDolbyDigital ? 5 : 3, VPid, APid1, APid2, DPid1, DPid2)
|
private:
|
||||||
,cThread("recording")
|
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;
|
active = false;
|
||||||
remux = NULL;
|
|
||||||
fileName = NULL;
|
fileName = NULL;
|
||||||
|
remux = Remux;
|
||||||
index = NULL;
|
index = NULL;
|
||||||
pictureType = NO_PICTURE;
|
pictureType = NO_PICTURE;
|
||||||
fileSize = 0;
|
fileSize = 0;
|
||||||
active = false;
|
|
||||||
lastDiskSpaceCheck = time(NULL);
|
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);
|
fileName = new cFileName(FileName, true);
|
||||||
recordFile = fileName->Open();
|
recordFile = fileName->Open();
|
||||||
if (recordFile < 0)
|
if (recordFile < 0)
|
||||||
@ -53,28 +61,15 @@ cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, int A
|
|||||||
// let's continue without index, so we'll at least have the recording
|
// 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 index;
|
||||||
delete fileName;
|
delete fileName;
|
||||||
delete remux;
|
|
||||||
delete ringBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRecorder::Activate(bool On)
|
bool cFileWriter::RunningLowOnDiskSpace(void)
|
||||||
{
|
|
||||||
if (On) {
|
|
||||||
if (recordFile >= 0)
|
|
||||||
Start();
|
|
||||||
}
|
|
||||||
else if (active) {
|
|
||||||
active = false;
|
|
||||||
Cancel(3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cRecorder::RunningLowOnDiskSpace(void)
|
|
||||||
{
|
{
|
||||||
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
||||||
int Free = FreeDiskSpaceMB(fileName->Name());
|
int Free = FreeDiskSpaceMB(fileName->Name());
|
||||||
@ -87,7 +82,7 @@ bool cRecorder::RunningLowOnDiskSpace(void)
|
|||||||
return false;
|
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 (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
|
||||||
if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
|
if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
|
||||||
@ -98,40 +93,29 @@ bool cRecorder::NextFile(void)
|
|||||||
return recordFile >= 0;
|
return recordFile >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRecorder::Receive(uchar *Data, int Length)
|
void cFileWriter::Action(void)
|
||||||
{
|
|
||||||
int p = ringBuffer->Put(Data, Length);
|
|
||||||
if (p != Length && active)
|
|
||||||
ringBuffer->ReportOverflow(Length - p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cRecorder::Action(void)
|
|
||||||
{
|
{
|
||||||
time_t t = time(NULL);
|
time_t t = time(NULL);
|
||||||
active = true;
|
active = true;
|
||||||
while (active) {
|
while (active) {
|
||||||
int r;
|
int Count;
|
||||||
const uchar *b = ringBuffer->Get(r);
|
uchar *p = remux->Get(Count, &pictureType);
|
||||||
if (b) {
|
if (p) {
|
||||||
int Count = r, Result;
|
//XXX+ active??? see old version (Busy)
|
||||||
uchar *p = remux->Process(b, Count, Result, &pictureType);
|
if (!active && pictureType == I_FRAME) // finish the recording before the next 'I' frame
|
||||||
ringBuffer->Del(Count);
|
break;
|
||||||
if (p) {
|
if (NextFile()) {
|
||||||
//XXX+ active??? see old version (Busy)
|
if (index && pictureType != NO_PICTURE)
|
||||||
if (!active && pictureType == I_FRAME) // finish the recording before the next 'I' frame
|
index->Write(pictureType, fileName->Number(), fileSize);
|
||||||
|
if (safe_write(recordFile, p, Count) < 0) {
|
||||||
|
LOG_ERROR_STR(fileName->Name());
|
||||||
break;
|
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
|
fileSize += Count;
|
||||||
break;
|
remux->Del(Count);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
t = time(NULL);
|
t = time(NULL);
|
||||||
}
|
}
|
||||||
else if (time(NULL) - t > MAXBROKENTIMEOUT) {
|
else if (time(NULL) - t > MAXBROKENTIMEOUT) {
|
||||||
@ -139,7 +123,65 @@ void cRecorder::Action(void)
|
|||||||
cThread::EmergencyExit(true);
|
cThread::EmergencyExit(true);
|
||||||
t = time(NULL);
|
t = time(NULL);
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
usleep(1); // this keeps the CPU load low
|
active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (active) {
|
||||||
|
int p = ringBuffer->Put(Data, Length);
|
||||||
|
if (p != Length && active)
|
||||||
|
ringBuffer->ReportOverflow(Length - p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cRecorder::Action(void)
|
||||||
|
{
|
||||||
|
active = true;
|
||||||
|
while (active) {
|
||||||
|
int r;
|
||||||
|
uchar *b = ringBuffer->Get(r);
|
||||||
|
if (b) {
|
||||||
|
int Count = remux->Put(b, r);
|
||||||
|
if (Count)
|
||||||
|
ringBuffer->Del(Count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
recorder.h
13
recorder.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.h 1.1 2002/06/10 16:30:00 kls Exp $
|
* $Id: recorder.h 1.2 2004/10/10 11:22:38 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __RECORDER_H
|
#ifndef __RECORDER_H
|
||||||
@ -16,19 +16,14 @@
|
|||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
|
||||||
|
class cFileWriter;
|
||||||
|
|
||||||
class cRecorder : public cReceiver, cThread {
|
class cRecorder : public cReceiver, cThread {
|
||||||
private:
|
private:
|
||||||
cRingBufferLinear *ringBuffer;
|
cRingBufferLinear *ringBuffer;
|
||||||
cRemux *remux;
|
cRemux *remux;
|
||||||
cFileName *fileName;
|
cFileWriter *writer;
|
||||||
cIndexFile *index;
|
|
||||||
uchar pictureType;
|
|
||||||
int fileSize;
|
|
||||||
int recordFile;
|
|
||||||
bool active;
|
bool active;
|
||||||
time_t lastDiskSpaceCheck;
|
|
||||||
bool RunningLowOnDiskSpace(void);
|
|
||||||
bool NextFile(void);
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Activate(bool On);
|
virtual void Activate(bool On);
|
||||||
virtual void Receive(uchar *Data, int Length);
|
virtual void Receive(uchar *Data, int Length);
|
||||||
|
326
remux.c
326
remux.c
@ -8,63 +8,9 @@
|
|||||||
* the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
|
* the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
|
||||||
* VDR's needs.
|
* VDR's needs.
|
||||||
*
|
*
|
||||||
* $Id: remux.c 1.18 2004/02/14 10:40:37 kls Exp $
|
* $Id: remux.c 1.19 2004/10/16 09:11:52 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 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 "remux.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
@ -133,8 +79,7 @@ private:
|
|||||||
uint8_t check;
|
uint8_t check;
|
||||||
int which;
|
int which;
|
||||||
bool done;
|
bool done;
|
||||||
uint8_t *resultBuffer;
|
cRingBufferLinear *resultBuffer;
|
||||||
int *resultCount;
|
|
||||||
int tsErrors;
|
int tsErrors;
|
||||||
int ccErrors;
|
int ccErrors;
|
||||||
int ccCounter;
|
int ccCounter;
|
||||||
@ -145,7 +90,7 @@ private:
|
|||||||
void write_ipack(const uint8_t *Data, int Count);
|
void write_ipack(const uint8_t *Data, int Count);
|
||||||
void instant_repack(const uint8_t *Buf, int Count);
|
void instant_repack(const uint8_t *Buf, int Count);
|
||||||
public:
|
public:
|
||||||
cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size, uint8_t AudioCid = 0x00);
|
cTS2PES(cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid = 0x00);
|
||||||
~cTS2PES();
|
~cTS2PES();
|
||||||
void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
|
void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
|
||||||
void Clear(void);
|
void Clear(void);
|
||||||
@ -153,10 +98,9 @@ public:
|
|||||||
|
|
||||||
uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
|
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;
|
resultBuffer = ResultBuffer;
|
||||||
resultCount = ResultCount;
|
|
||||||
size = Size;
|
size = Size;
|
||||||
audioCid = AudioCid;
|
audioCid = AudioCid;
|
||||||
|
|
||||||
@ -184,12 +128,9 @@ void cTS2PES::Clear(void)
|
|||||||
|
|
||||||
void cTS2PES::store(uint8_t *Data, int Count)
|
void cTS2PES::store(uint8_t *Data, int Count)
|
||||||
{
|
{
|
||||||
if (*resultCount + Count > RESULTBUFFERSIZE) {
|
int n = resultBuffer->Put(Data, Count);
|
||||||
esyslog("ERROR: result buffer overflow (%d + %d > %d)", *resultCount, Count, RESULTBUFFERSIZE);
|
if (n != Count)
|
||||||
Count = RESULTBUFFERSIZE - *resultCount;
|
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
|
||||||
}
|
|
||||||
memcpy(resultBuffer + *resultCount, Data, Count);
|
|
||||||
*resultCount += Count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cTS2PES::reset_ipack(void)
|
void cTS2PES::reset_ipack(void)
|
||||||
@ -452,6 +393,8 @@ void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188)
|
|||||||
|
|
||||||
// --- cRemux ----------------------------------------------------------------
|
// --- cRemux ----------------------------------------------------------------
|
||||||
|
|
||||||
|
#define RESULTBUFFERSIZE KILOBYTE(256)
|
||||||
|
|
||||||
cRemux::cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure)
|
cRemux::cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure)
|
||||||
{
|
{
|
||||||
vPid = VPid;
|
vPid = VPid;
|
||||||
@ -463,13 +406,15 @@ cRemux::cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOn
|
|||||||
numUPTerrors = 0;
|
numUPTerrors = 0;
|
||||||
synced = false;
|
synced = false;
|
||||||
skipped = 0;
|
skipped = 0;
|
||||||
resultCount = resultDelivered = 0;
|
resultSkipped = 0;
|
||||||
vTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS);
|
resultBuffer = new cRingBufferLinear(RESULTBUFFERSIZE, IPACKS, false, "Result");
|
||||||
aTS2PES1 = new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC0);
|
resultBuffer->SetTimeouts(0, 100);
|
||||||
aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC1) : NULL;
|
vTS2PES = new cTS2PES(resultBuffer, IPACKS);
|
||||||
dTS2PES1 = dPid1 ? new cTS2PES(resultBuffer, &resultCount, IPACKS) : NULL;
|
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...
|
//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()
|
cRemux::~cRemux()
|
||||||
@ -479,6 +424,7 @@ cRemux::~cRemux()
|
|||||||
delete aTS2PES2;
|
delete aTS2PES2;
|
||||||
delete dTS2PES1;
|
delete dTS2PES1;
|
||||||
delete dTS2PES2;
|
delete dTS2PES2;
|
||||||
|
delete resultBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRemux::GetPid(const uchar *Data)
|
int cRemux::GetPid(const uchar *Data)
|
||||||
@ -488,27 +434,32 @@ int cRemux::GetPid(const uchar *Data)
|
|||||||
|
|
||||||
int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
|
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.
|
// Returns the length of the packet starting at Offset, or -1 if Count is
|
||||||
return (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
|
// 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)
|
int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
|
||||||
{
|
{
|
||||||
// Scans the video packet starting at Offset and returns its length.
|
// 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.
|
// If the return value is -1 the packet was not completely in the buffer.
|
||||||
|
|
||||||
int Length = GetPacketLength(Data, Count, Offset);
|
int Length = GetPacketLength(Data, Count, Offset);
|
||||||
if (Length > 0 && Offset + Length <= Count) {
|
if (Length > 0) {
|
||||||
int i = Offset + 8; // the minimum length of the video packet header
|
if (Length >= 8) {
|
||||||
i += Data[i] + 1; // possible additional header bytes
|
int i = Offset + 8; // the minimum length of the video packet header
|
||||||
for (; i < Offset + Length; i++) {
|
i += Data[i] + 1; // possible additional header bytes
|
||||||
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
|
for (; i < Offset + Length; i++) {
|
||||||
switch (Data[i + 3]) {
|
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
|
||||||
case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
|
switch (Data[i + 3]) {
|
||||||
return Length;
|
case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
|
||||||
}
|
return Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PictureType = NO_PICTURE;
|
PictureType = NO_PICTURE;
|
||||||
return Length;
|
return Length;
|
||||||
}
|
}
|
||||||
@ -517,28 +468,8 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
|
|||||||
|
|
||||||
#define TS_SYNC_BYTE 0x47
|
#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;
|
int used = 0;
|
||||||
|
|
||||||
// Make sure we are looking at a TS packet:
|
// Make sure we are looking at a TS packet:
|
||||||
@ -560,6 +491,8 @@ XXX*/
|
|||||||
break;
|
break;
|
||||||
if (Data[i] != TS_SYNC_BYTE)
|
if (Data[i] != TS_SYNC_BYTE)
|
||||||
break;
|
break;
|
||||||
|
if (resultBuffer->Free() < IPACKS)
|
||||||
|
break;
|
||||||
int pid = GetPid(Data + i + 1);
|
int pid = GetPid(Data + i + 1);
|
||||||
if (Data[i + 3] & 0x10) { // got payload
|
if (Data[i + 3] & 0x10) { // got payload
|
||||||
if (pid == vPid) vTS2PES->ts_to_pes(Data + i);
|
if (pid == vPid) vTS2PES->ts_to_pes(Data + i);
|
||||||
@ -569,31 +502,9 @@ XXX*/
|
|||||||
else if (pid == dPid2 && dTS2PES2) dTS2PES2->ts_to_pes(Data + i);
|
else if (pid == dPid2 && dTS2PES2) dTS2PES2->ts_to_pes(Data + i);
|
||||||
}
|
}
|
||||||
used += TS_SIZE;
|
used += TS_SIZE;
|
||||||
if (resultCount > (int)sizeof(resultBuffer) / 2)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
Count = used;
|
|
||||||
|
|
||||||
/*XXX
|
|
||||||
// test recording without determining the real frame borders:
|
|
||||||
*PictureType = I_FRAME;
|
|
||||||
Result = resultDelivered = resultCount;
|
|
||||||
return Result ? resultBuffer : NULL;
|
|
||||||
XXX*/
|
|
||||||
|
|
||||||
// Special VPID case to enable recording radio channels:
|
|
||||||
|
|
||||||
if (vPid == 0 || vPid == 1 || vPid == 0x1FFF) {
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we're getting anywhere here:
|
// Check if we're getting anywhere here:
|
||||||
|
|
||||||
if (!synced && skipped >= 0) {
|
if (!synced && skipped >= 0) {
|
||||||
if (skipped > MAXNONUSEFULDATA) {
|
if (skipped > MAXNONUSEFULDATA) {
|
||||||
esyslog("ERROR: no useful data seen within %d byte of video stream", skipped);
|
esyslog("ERROR: no useful data seen within %d byte of video stream", skipped);
|
||||||
@ -602,77 +513,112 @@ XXX*/
|
|||||||
cThread::EmergencyExit(true);
|
cThread::EmergencyExit(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
skipped += Count;
|
skipped += used;
|
||||||
|
}
|
||||||
|
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar *cRemux::Get(int &Count, uchar *PictureType)
|
||||||
|
{
|
||||||
|
// Remove any previously skipped data from the result buffer:
|
||||||
|
|
||||||
|
if (resultSkipped > 0) {
|
||||||
|
resultBuffer->Del(resultSkipped);
|
||||||
|
resultSkipped = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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:
|
||||||
|
|
||||||
|
if (vPid == 0 || vPid == 1 || vPid == 0x1FFF) {
|
||||||
|
// 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'
|
||||||
|
if (PictureType)
|
||||||
|
*PictureType = I_FRAME;
|
||||||
|
return resultBuffer->Get(Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for frame borders:
|
// Check for frame borders:
|
||||||
|
|
||||||
*PictureType = NO_PICTURE;
|
if (PictureType)
|
||||||
|
*PictureType = NO_PICTURE;
|
||||||
|
|
||||||
if (resultCount >= MINVIDEODATA) {
|
Count = 0;
|
||||||
for (int i = 0; i < resultCount; i++) {
|
uchar *resultData = NULL;
|
||||||
if (resultBuffer[i] == 0 && resultBuffer[i + 1] == 0 && resultBuffer[i + 2] == 1) {
|
int resultCount = 0;
|
||||||
switch (resultBuffer[i + 3]) {
|
uchar *data = resultBuffer->Get(resultCount);
|
||||||
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
if (data) {
|
||||||
{
|
for (int i = 0; i < resultCount - 3; i++) {
|
||||||
uchar pt = NO_PICTURE;
|
if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
|
||||||
int l = ScanVideoPacket(resultBuffer, resultCount, i, pt);
|
int l = 0;
|
||||||
if (l < 0)
|
uchar StreamType = data[i + 3];
|
||||||
return NULL; // no useful data found, wait for more
|
if (VIDEO_STREAM_S <= StreamType && StreamType <= VIDEO_STREAM_E) {
|
||||||
if (pt != NO_PICTURE) {
|
uchar pt = NO_PICTURE;
|
||||||
if (pt < I_FRAME || B_FRAME < pt) {
|
l = ScanVideoPacket(data, resultCount, i, pt);
|
||||||
esyslog("ERROR: unknown picture type '%d'", pt);
|
if (l < 0)
|
||||||
if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure)
|
return resultData;
|
||||||
cThread::EmergencyExit(true);
|
if (pt != NO_PICTURE) {
|
||||||
}
|
if (pt < I_FRAME || B_FRAME < pt) {
|
||||||
else if (!synced) {
|
esyslog("ERROR: unknown picture type '%d'", pt);
|
||||||
if (pt == I_FRAME) {
|
if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure)
|
||||||
resultDelivered = i; // will drop everything before this position
|
cThread::EmergencyExit(true);
|
||||||
SetBrokenLink(resultBuffer + i, l);
|
}
|
||||||
synced = true;
|
else if (!synced) {
|
||||||
}
|
if (pt == I_FRAME) {
|
||||||
else {
|
if (PictureType)
|
||||||
resultDelivered = i + l; // will drop everything before and including this packet
|
*PictureType = pt;
|
||||||
return NULL;
|
resultSkipped = i; // will drop everything before this position
|
||||||
}
|
SetBrokenLink(data + i, l);
|
||||||
}
|
synced = true;
|
||||||
}
|
}
|
||||||
if (synced) {
|
}
|
||||||
*PictureType = pt;
|
else if (Count)
|
||||||
Result = l;
|
return resultData;
|
||||||
uchar *p = resultBuffer + resultDelivered;
|
else if (PictureType)
|
||||||
resultDelivered += l;
|
*PictureType = pt;
|
||||||
return p;
|
}
|
||||||
}
|
}
|
||||||
else {
|
else { //if (AUDIO_STREAM_S <= StreamType && StreamType <= AUDIO_STREAM_E || StreamType == PRIVATE_STREAM1) {
|
||||||
resultDelivered = i + l; // will drop everything before and including this packet
|
l = GetPacketLength(data, resultCount, i);
|
||||||
return NULL;
|
if (l < 0)
|
||||||
}
|
return resultData;
|
||||||
}
|
}
|
||||||
break;
|
if (synced) {
|
||||||
case PRIVATE_STREAM1:
|
if (!Count)
|
||||||
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
resultData = data + i;
|
||||||
{
|
Count += l;
|
||||||
int l = GetPacketLength(resultBuffer, resultCount, i);
|
}
|
||||||
if (l < 0)
|
else
|
||||||
return NULL; // no useful data found, wait for more
|
resultSkipped = i;
|
||||||
if (synced) {
|
if (l > 0)
|
||||||
Result = l;
|
i += l - 1; // the loop increments, too
|
||||||
uchar *p = resultBuffer + resultDelivered;
|
|
||||||
resultDelivered += l;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resultDelivered = i + l; // will drop everything before and including this packet
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL; // no useful data found, wait for more
|
return resultData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cRemux::Del(int Count)
|
||||||
|
{
|
||||||
|
resultBuffer->Del(Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cRemux::Clear(void)
|
||||||
|
{
|
||||||
|
if (vTS2PES) vTS2PES->Clear();
|
||||||
|
if (aTS2PES1) aTS2PES1->Clear();
|
||||||
|
if (aTS2PES2) aTS2PES2->Clear();
|
||||||
|
if (dTS2PES1) dTS2PES1->Clear();
|
||||||
|
if (dTS2PES2) dTS2PES2->Clear();
|
||||||
|
resultBuffer->Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRemux::SetBrokenLink(uchar *Data, int Length)
|
void cRemux::SetBrokenLink(uchar *Data, int Length)
|
||||||
|
30
remux.h
30
remux.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: remux.h 1.11 2004/02/14 10:40:41 kls Exp $
|
* $Id: remux.h 1.12 2004/10/15 12:31:16 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __REMUX_H
|
#ifndef __REMUX_H
|
||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <time.h> //XXX FIXME: DVB/linux/dvb/dmx.h should include <time.h> itself!!!
|
#include <time.h> //XXX FIXME: DVB/linux/dvb/dmx.h should include <time.h> itself!!!
|
||||||
#include <linux/dvb/dmx.h>
|
#include <linux/dvb/dmx.h>
|
||||||
|
#include "ringbuffer.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
// Picture types:
|
// Picture types:
|
||||||
@ -20,11 +21,6 @@
|
|||||||
#define P_FRAME 2
|
#define P_FRAME 2
|
||||||
#define B_FRAME 3
|
#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 cTS2PES;
|
||||||
|
|
||||||
class cRemux {
|
class cRemux {
|
||||||
@ -35,16 +31,30 @@ private:
|
|||||||
int skipped;
|
int skipped;
|
||||||
int vPid, aPid1, aPid2, dPid1, dPid2;
|
int vPid, aPid1, aPid2, dPid1, dPid2;
|
||||||
cTS2PES *vTS2PES, *aTS2PES1, *aTS2PES2, *dTS2PES1, *dTS2PES2;
|
cTS2PES *vTS2PES, *aTS2PES1, *aTS2PES2, *dTS2PES1, *dTS2PES2;
|
||||||
uchar resultBuffer[RESULTBUFFERSIZE];
|
cRingBufferLinear *resultBuffer;
|
||||||
int resultCount;
|
int resultSkipped;
|
||||||
int resultDelivered;
|
|
||||||
int GetPid(const uchar *Data);
|
int GetPid(const uchar *Data);
|
||||||
int GetPacketLength(const uchar *Data, int Count, int Offset);
|
int GetPacketLength(const uchar *Data, int Count, int Offset);
|
||||||
int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
|
int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
|
||||||
public:
|
public:
|
||||||
cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure = false);
|
cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure = false);
|
||||||
~cRemux();
|
~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.
|
||||||
|
void Clear(void);
|
||||||
|
///< Clears the remuxer of all data it might still contain, keeping the PID
|
||||||
|
///< settings as they are.
|
||||||
static void SetBrokenLink(uchar *Data, int Length);
|
static void SetBrokenLink(uchar *Data, int Length);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
230
ringbuffer.c
230
ringbuffer.c
@ -7,7 +7,7 @@
|
|||||||
* Parts of this file were inspired by the 'ringbuffy.c' from the
|
* Parts of this file were inspired by the 'ringbuffy.c' from the
|
||||||
* LinuxDVB driver (see linuxtv.org).
|
* LinuxDVB driver (see linuxtv.org).
|
||||||
*
|
*
|
||||||
* $Id: ringbuffer.c 1.20 2004/06/19 12:27:56 kls Exp $
|
* $Id: ringbuffer.c 1.21 2004/10/15 13:49:25 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
@ -18,11 +18,14 @@
|
|||||||
// --- cRingBuffer -----------------------------------------------------------
|
// --- cRingBuffer -----------------------------------------------------------
|
||||||
|
|
||||||
#define OVERFLOWREPORTDELTA 5 // seconds between reports
|
#define OVERFLOWREPORTDELTA 5 // seconds between reports
|
||||||
|
#define PERCENTAGEDELTA 10
|
||||||
|
#define PERCENTAGETHRESHOLD 70
|
||||||
|
|
||||||
cRingBuffer::cRingBuffer(int Size, bool Statistics)
|
cRingBuffer::cRingBuffer(int Size, bool Statistics)
|
||||||
{
|
{
|
||||||
size = Size;
|
size = Size;
|
||||||
statistics = Statistics;
|
statistics = Statistics;
|
||||||
|
getThreadTid = 0;
|
||||||
maxFill = 0;
|
maxFill = 0;
|
||||||
lastPercent = 0;
|
lastPercent = 0;
|
||||||
putTimeout = getTimeout = 0;
|
putTimeout = getTimeout = 0;
|
||||||
@ -36,34 +39,41 @@ cRingBuffer::~cRingBuffer()
|
|||||||
dsyslog("buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
|
dsyslog("buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cRingBuffer::UpdatePercentage(int Fill)
|
||||||
|
{
|
||||||
|
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)
|
void cRingBuffer::WaitForPut(void)
|
||||||
{
|
{
|
||||||
if (putTimeout) {
|
if (putTimeout)
|
||||||
putMutex.Lock();
|
readyForPut.Wait(putTimeout);
|
||||||
readyForPut.TimedWait(putMutex, putTimeout);
|
|
||||||
putMutex.Unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRingBuffer::WaitForGet(void)
|
void cRingBuffer::WaitForGet(void)
|
||||||
{
|
{
|
||||||
if (getTimeout) {
|
if (getTimeout)
|
||||||
getMutex.Lock();
|
readyForGet.Wait(getTimeout);
|
||||||
readyForGet.TimedWait(getMutex, getTimeout);
|
|
||||||
getMutex.Unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRingBuffer::EnablePut(void)
|
void cRingBuffer::EnablePut(void)
|
||||||
{
|
{
|
||||||
if (putTimeout)
|
if (putTimeout && Free() > Size() / 3)
|
||||||
readyForPut.Broadcast();
|
readyForPut.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRingBuffer::EnableGet(void)
|
void cRingBuffer::EnableGet(void)
|
||||||
{
|
{
|
||||||
if (getTimeout)
|
if (getTimeout && Available() > Size() / 3)
|
||||||
readyForGet.Broadcast();
|
readyForGet.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRingBuffer::SetTimeouts(int PutTimeout, int GetTimeout)
|
void cRingBuffer::SetTimeouts(int PutTimeout, int GetTimeout)
|
||||||
@ -85,70 +95,168 @@ void cRingBuffer::ReportOverflow(int Bytes)
|
|||||||
|
|
||||||
// --- cRingBufferLinear -----------------------------------------------------
|
// --- 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)
|
:cRingBuffer(Size, Statistics)
|
||||||
{
|
{
|
||||||
margin = Margin;
|
description = Description ? strdup(Description) : NULL;
|
||||||
|
tail = head = margin = Margin;
|
||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
getThreadTid = 0;
|
|
||||||
if (Size > 1) { // 'Size - 1' must not be 0!
|
if (Size > 1) { // 'Size - 1' must not be 0!
|
||||||
buffer = MALLOC(uchar, Size);
|
if (Margin <= Size / 2) {
|
||||||
if (!buffer)
|
buffer = MALLOC(uchar, Size);
|
||||||
esyslog("ERROR: can't allocate ring buffer (size=%d)", Size);
|
if (!buffer)
|
||||||
Clear();
|
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
|
else
|
||||||
esyslog("ERROR: illegal size for ring buffer (%d)", Size);
|
esyslog("ERROR: illegal size for ring buffer (%d)", Size);
|
||||||
|
#ifdef DEBUGRINGBUFFERS
|
||||||
|
lastHead = head;
|
||||||
|
lastTail = tail;
|
||||||
|
lastPut = lastGet = -1;
|
||||||
|
AddDebugRBL(this);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
cRingBufferLinear::~cRingBufferLinear()
|
cRingBufferLinear::~cRingBufferLinear()
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUGRINGBUFFERS
|
||||||
|
DelDebugRBL(this);
|
||||||
|
#endif
|
||||||
free(buffer);
|
free(buffer);
|
||||||
|
free(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRingBufferLinear::Available(void)
|
int cRingBufferLinear::Available(void)
|
||||||
{
|
{
|
||||||
Lock();
|
|
||||||
int diff = head - tail;
|
int diff = head - tail;
|
||||||
Unlock();
|
|
||||||
return (diff >= 0) ? diff : Size() + diff - margin;
|
return (diff >= 0) ? diff : Size() + diff - margin;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRingBufferLinear::Clear(void)
|
void cRingBufferLinear::Clear(void)
|
||||||
{
|
{
|
||||||
Lock();
|
tail = head;
|
||||||
head = tail = margin;
|
#ifdef DEBUGRINGBUFFERS
|
||||||
lastGet = -1;
|
lastHead = head;
|
||||||
Unlock();
|
lastTail = tail;
|
||||||
|
lastPut = lastGet = -1;
|
||||||
|
#endif
|
||||||
|
maxFill = 0;
|
||||||
EnablePut();
|
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();
|
EnableGet();
|
||||||
|
if (free == 0)
|
||||||
|
WaitForPut();
|
||||||
|
return Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRingBufferLinear::Put(const uchar *Data, int Count)
|
int cRingBufferLinear::Put(const uchar *Data, int Count)
|
||||||
{
|
{
|
||||||
if (Count > 0) {
|
if (Count > 0) {
|
||||||
Lock();
|
int Tail = tail;
|
||||||
int rest = Size() - head;
|
int rest = Size() - head;
|
||||||
int diff = tail - head;
|
int diff = Tail - head;
|
||||||
int free = ((tail < margin) ? rest : (diff > 0) ? diff : Size() + diff - margin) - 1;
|
int free = ((Tail < margin) ? rest : (diff > 0) ? diff : Size() + diff - margin) - 1;
|
||||||
if (statistics) {
|
if (statistics) {
|
||||||
int fill = Size() - free - 1 + Count;
|
int fill = Size() - free - 1 + Count;
|
||||||
if (fill >= Size())
|
if (fill >= Size())
|
||||||
fill = Size() - 1;
|
fill = Size() - 1;
|
||||||
if (fill > maxFill)
|
UpdatePercentage(fill);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (free > 0) {
|
if (free > 0) {
|
||||||
if (free < Count)
|
if (free < Count)
|
||||||
Count = free;
|
Count = free;
|
||||||
if (Count > maxFill)
|
|
||||||
maxFill = Count;
|
|
||||||
if (Count >= rest) {
|
if (Count >= rest) {
|
||||||
memcpy(buffer + head, Data, rest);
|
memcpy(buffer + head, Data, rest);
|
||||||
if (Count - rest)
|
if (Count - rest)
|
||||||
@ -162,7 +270,10 @@ int cRingBufferLinear::Put(const uchar *Data, int Count)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
Count = 0;
|
Count = 0;
|
||||||
Unlock();
|
#ifdef DEBUGRINGBUFFERS
|
||||||
|
lastHead = head;
|
||||||
|
lastPut = Count;
|
||||||
|
#endif
|
||||||
EnableGet();
|
EnableGet();
|
||||||
if (Count == 0)
|
if (Count == 0)
|
||||||
WaitForPut();
|
WaitForPut();
|
||||||
@ -173,25 +284,24 @@ int cRingBufferLinear::Put(const uchar *Data, int Count)
|
|||||||
uchar *cRingBufferLinear::Get(int &Count)
|
uchar *cRingBufferLinear::Get(int &Count)
|
||||||
{
|
{
|
||||||
uchar *p = NULL;
|
uchar *p = NULL;
|
||||||
Lock();
|
int Head = head;
|
||||||
if (getThreadTid <= 0)
|
if (getThreadTid <= 0)
|
||||||
getThreadTid = pthread_self();
|
getThreadTid = pthread_self();
|
||||||
int rest = Size() - tail;
|
int rest = Size() - tail;
|
||||||
if (rest < margin && head < tail) {
|
if (rest < margin && Head < tail) {
|
||||||
int t = margin - rest;
|
int t = margin - rest;
|
||||||
memcpy(buffer + t, buffer + tail, rest);
|
memcpy(buffer + t, buffer + tail, rest);
|
||||||
tail = t;
|
tail = t;
|
||||||
rest = head - tail;
|
rest = Head - tail;
|
||||||
}
|
}
|
||||||
int diff = head - tail;
|
int diff = Head - tail;
|
||||||
int cont = (diff >= 0) ? diff : Size() + diff - margin;
|
int cont = (diff >= 0) ? diff : Size() + diff - margin;
|
||||||
if (cont > rest)
|
if (cont > rest)
|
||||||
cont = rest;
|
cont = rest;
|
||||||
if (cont >= margin) {
|
if (cont >= margin) {
|
||||||
p = buffer + tail;
|
p = buffer + tail;
|
||||||
Count = lastGet = cont;
|
Count = gotten = cont;
|
||||||
}
|
}
|
||||||
Unlock();
|
|
||||||
if (!p)
|
if (!p)
|
||||||
WaitForGet();
|
WaitForGet();
|
||||||
return p;
|
return p;
|
||||||
@ -199,17 +309,23 @@ uchar *cRingBufferLinear::Get(int &Count)
|
|||||||
|
|
||||||
void cRingBufferLinear::Del(int Count)
|
void cRingBufferLinear::Del(int Count)
|
||||||
{
|
{
|
||||||
if (Count > 0 && Count <= lastGet) {
|
if (Count > gotten) {
|
||||||
Lock();
|
esyslog("ERROR: invalid Count in cRingBufferLinear::Del: %d (limited to %d)", Count, gotten);
|
||||||
tail += Count;
|
Count = gotten;
|
||||||
lastGet -= Count;
|
}
|
||||||
if (tail >= Size())
|
if (Count > 0) {
|
||||||
tail = margin;
|
int Tail = tail;
|
||||||
Unlock();
|
Tail += Count;
|
||||||
|
gotten -= Count;
|
||||||
|
if (Tail >= Size())
|
||||||
|
Tail = margin;
|
||||||
|
tail = Tail;
|
||||||
EnablePut();
|
EnablePut();
|
||||||
}
|
}
|
||||||
else
|
#ifdef DEBUGRINGBUFFERS
|
||||||
esyslog("ERROR: invalid Count in cRingBufferLinear::Del: %d", Count);
|
lastTail = tail;
|
||||||
|
lastGet = Count;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cFrame ----------------------------------------------------------------
|
// --- cFrame ----------------------------------------------------------------
|
||||||
|
42
ringbuffer.h
42
ringbuffer.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: ringbuffer.h 1.15 2004/06/19 10:32:15 kls Exp $
|
* $Id: ringbuffer.h 1.16 2004/10/15 13:50:46 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __RINGBUFFER_H
|
#ifndef __RINGBUFFER_H
|
||||||
@ -15,9 +15,7 @@
|
|||||||
|
|
||||||
class cRingBuffer {
|
class cRingBuffer {
|
||||||
private:
|
private:
|
||||||
cMutex mutex;
|
cCondWait readyForPut, readyForGet;
|
||||||
cCondVar readyForPut, readyForGet;
|
|
||||||
cMutex putMutex, getMutex;
|
|
||||||
int putTimeout;
|
int putTimeout;
|
||||||
int getTimeout;
|
int getTimeout;
|
||||||
int size;
|
int size;
|
||||||
@ -25,18 +23,18 @@ private:
|
|||||||
int overflowCount;
|
int overflowCount;
|
||||||
int overflowBytes;
|
int overflowBytes;
|
||||||
protected:
|
protected:
|
||||||
|
pthread_t getThreadTid;
|
||||||
int maxFill;//XXX
|
int maxFill;//XXX
|
||||||
int lastPercent;
|
int lastPercent;
|
||||||
bool statistics;//XXX
|
bool statistics;//XXX
|
||||||
|
void UpdatePercentage(int Fill);
|
||||||
void WaitForPut(void);
|
void WaitForPut(void);
|
||||||
void WaitForGet(void);
|
void WaitForGet(void);
|
||||||
void EnablePut(void);
|
void EnablePut(void);
|
||||||
void EnableGet(void);
|
void EnableGet(void);
|
||||||
virtual void Clear(void) = 0;
|
virtual void Clear(void) = 0;
|
||||||
virtual int Available(void) = 0;
|
virtual int Available(void) = 0;
|
||||||
int Free(void) { return size - Available() - 1; }
|
virtual int Free(void) { return Size() - Available() - 1; }
|
||||||
void Lock(void) { mutex.Lock(); }
|
|
||||||
void Unlock(void) { mutex.Unlock(); }
|
|
||||||
int Size(void) { return size; }
|
int Size(void) { return size; }
|
||||||
public:
|
public:
|
||||||
cRingBuffer(int Size, bool Statistics = false);
|
cRingBuffer(int Size, bool Statistics = false);
|
||||||
@ -46,20 +44,39 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class cRingBufferLinear : public cRingBuffer {
|
class cRingBufferLinear : public cRingBuffer {
|
||||||
|
//#define DEBUGRINGBUFFERS
|
||||||
|
#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:
|
private:
|
||||||
int margin, head, tail;
|
int margin, head, tail;
|
||||||
int lastGet;
|
int gotten;
|
||||||
uchar *buffer;
|
uchar *buffer;
|
||||||
pthread_t getThreadTid;
|
char *description;
|
||||||
public:
|
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.
|
///< Creates a linear ring buffer.
|
||||||
///< The buffer will be able to hold at most Size-Margin-1 bytes of data, and will
|
///< 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.
|
///< be guaranteed to return at least Margin bytes in one consecutive block.
|
||||||
|
///< The optional Description is used for debugging only.
|
||||||
virtual ~cRingBufferLinear();
|
virtual ~cRingBufferLinear();
|
||||||
virtual int Available(void);
|
virtual int Available(void);
|
||||||
|
virtual int Free(void) { return Size() - Available() - 1 - margin; }
|
||||||
virtual void Clear(void);
|
virtual void Clear(void);
|
||||||
///< Immediately clears the ring buffer.
|
///< 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);
|
int Put(const uchar *Data, int Count);
|
||||||
///< Puts at most Count bytes of Data into the ring buffer.
|
///< Puts at most Count bytes of Data into the ring buffer.
|
||||||
///< \return Returns the number of bytes actually stored.
|
///< \return Returns the number of bytes actually stored.
|
||||||
@ -67,7 +84,7 @@ public:
|
|||||||
///< Gets data from the ring buffer.
|
///< Gets data from the ring buffer.
|
||||||
///< The data will remain in the buffer until a call to Del() deletes it.
|
///< 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
|
///< \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);
|
void Del(int Count);
|
||||||
///< Deletes at most Count bytes from the ring buffer.
|
///< Deletes at most Count bytes from the ring buffer.
|
||||||
///< Count must be less or equal to the number that was returned by a previous
|
///< Count must be less or equal to the number that was returned by a previous
|
||||||
@ -98,9 +115,12 @@ public:
|
|||||||
|
|
||||||
class cRingBufferFrame : public cRingBuffer {
|
class cRingBufferFrame : public cRingBuffer {
|
||||||
private:
|
private:
|
||||||
|
cMutex mutex;
|
||||||
cFrame *head;
|
cFrame *head;
|
||||||
int currentFill;
|
int currentFill;
|
||||||
void Delete(cFrame *Frame);
|
void Delete(cFrame *Frame);
|
||||||
|
void Lock(void) { mutex.Lock(); }
|
||||||
|
void Unlock(void) { mutex.Unlock(); }
|
||||||
public:
|
public:
|
||||||
cRingBufferFrame(int Size, bool Statistics = false);
|
cRingBufferFrame(int Size, bool Statistics = false);
|
||||||
virtual ~cRingBufferFrame();
|
virtual ~cRingBufferFrame();
|
||||||
|
61
thread.c
61
thread.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: thread.c 1.31 2004/03/14 16:48:30 kls Exp $
|
* $Id: thread.c 1.32 2004/10/15 13:15:02 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
@ -18,6 +18,58 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
|
// --- cCondWait -------------------------------------------------------------
|
||||||
|
|
||||||
|
cCondWait::cCondWait(void)
|
||||||
|
{
|
||||||
|
signaled = false;
|
||||||
|
pthread_mutex_init(&mutex, NULL);
|
||||||
|
pthread_cond_init(&cond, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
cCondWait::~cCondWait()
|
||||||
|
{
|
||||||
|
pthread_cond_destroy(&cond);
|
||||||
|
pthread_mutex_destroy(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cCondWait::Wait(int TimeoutMs)
|
||||||
|
{
|
||||||
|
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 cCondWait::Signal(void)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&mutex);
|
||||||
|
signaled = true;
|
||||||
|
pthread_cond_signal(&cond);
|
||||||
|
pthread_mutex_unlock(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
// --- cCondVar --------------------------------------------------------------
|
// --- cCondVar --------------------------------------------------------------
|
||||||
|
|
||||||
cCondVar::cCondVar(void)
|
cCondVar::cCondVar(void)
|
||||||
@ -73,13 +125,6 @@ void cCondVar::Broadcast(void)
|
|||||||
pthread_cond_broadcast(&cond);
|
pthread_cond_broadcast(&cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void cCondVar::Signal(void)
|
|
||||||
{
|
|
||||||
pthread_cond_signal(&cond);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// --- cRwLock ---------------------------------------------------------------
|
// --- cRwLock ---------------------------------------------------------------
|
||||||
|
|
||||||
cRwLock::cRwLock(bool PreferWriter)
|
cRwLock::cRwLock(bool PreferWriter)
|
||||||
|
20
thread.h
20
thread.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: thread.h 1.20 2004/01/03 16:58:50 kls Exp $
|
* $Id: thread.h 1.21 2004/10/15 13:16:39 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __THREAD_H
|
#ifndef __THREAD_H
|
||||||
@ -14,6 +14,23 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
class cCondWait {
|
||||||
|
private:
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
pthread_cond_t cond;
|
||||||
|
bool signaled;
|
||||||
|
public:
|
||||||
|
cCondWait(void);
|
||||||
|
~cCondWait();
|
||||||
|
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 cMutex;
|
class cMutex;
|
||||||
|
|
||||||
class cCondVar {
|
class cCondVar {
|
||||||
@ -25,7 +42,6 @@ public:
|
|||||||
void Wait(cMutex &Mutex);
|
void Wait(cMutex &Mutex);
|
||||||
bool TimedWait(cMutex &Mutex, int TimeoutMs);
|
bool TimedWait(cMutex &Mutex, int TimeoutMs);
|
||||||
void Broadcast(void);
|
void Broadcast(void);
|
||||||
//void Signal(void);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class cRwLock {
|
class cRwLock {
|
||||||
|
118
transfer.c
118
transfer.c
@ -4,15 +4,12 @@
|
|||||||
* 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: transfer.c 1.16 2004/03/07 14:40:15 kls Exp $
|
* $Id: transfer.c 1.17 2004/10/16 09:22:58 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "transfer.h"
|
#include "transfer.h"
|
||||||
|
|
||||||
//XXX+ also used in recorder.c - find a better place???
|
#define TRANSFERBUFSIZE MEGABYTE(2)
|
||||||
// The size of the array used to buffer video data:
|
|
||||||
// (must be larger than MINVIDEODATA - see remux.h)
|
|
||||||
#define VIDEOBUFSIZE MEGABYTE(1)
|
|
||||||
#define POLLTIMEOUTS_BEFORE_DEVICECLEAR 3
|
#define POLLTIMEOUTS_BEFORE_DEVICECLEAR 3
|
||||||
|
|
||||||
// --- cTransfer -------------------------------------------------------------
|
// --- cTransfer -------------------------------------------------------------
|
||||||
@ -21,11 +18,10 @@ cTransfer::cTransfer(int VPid, int APid1, int APid2, int DPid1, int DPid2)
|
|||||||
:cReceiver(0, -1, 5, VPid, APid1, APid2, DPid1, DPid2)
|
:cReceiver(0, -1, 5, VPid, APid1, APid2, DPid1, DPid2)
|
||||||
,cThread("transfer")
|
,cThread("transfer")
|
||||||
{
|
{
|
||||||
ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, TS_SIZE * 2, true);
|
ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer");
|
||||||
remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2);
|
remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2);
|
||||||
canToggleAudioTrack = false;
|
canToggleAudioTrack = false;
|
||||||
audioTrack = 0xC0;
|
audioTrack = 0xC0;
|
||||||
gotBufferReserve = false;
|
|
||||||
active = false;
|
active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,79 +47,69 @@ void cTransfer::Activate(bool On)
|
|||||||
|
|
||||||
void cTransfer::Receive(uchar *Data, int Length)
|
void cTransfer::Receive(uchar *Data, int Length)
|
||||||
{
|
{
|
||||||
if (IsAttached()) {
|
if (IsAttached() && active) {
|
||||||
int i = 0;
|
int p = ringBuffer->Put(Data, Length);
|
||||||
while (active && Length > 0) {
|
if (p != Length && active)
|
||||||
if (i++ > 10) {
|
ringBuffer->ReportOverflow(Length - p);
|
||||||
ringBuffer->ReportOverflow(Length);
|
return;
|
||||||
break;
|
|
||||||
}
|
|
||||||
int p = ringBuffer->Put(Data, Length);
|
|
||||||
Length -= p;
|
|
||||||
Data += p;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cTransfer::Action(void)
|
void cTransfer::Action(void)
|
||||||
{
|
{
|
||||||
int PollTimeouts = 0;
|
int PollTimeouts = 0;
|
||||||
|
uchar *p = NULL;
|
||||||
|
int Result = 0;
|
||||||
active = true;
|
active = true;
|
||||||
while (active) {
|
while (active) {
|
||||||
|
int Count;
|
||||||
//XXX+ Maybe we need this to avoid buffer underruns in driver.
|
uchar *b = ringBuffer->Get(Count);
|
||||||
//XXX+ But then again, it appears to play just fine without this...
|
if (b) {
|
||||||
/*
|
if (Count > TRANSFERBUFSIZE * 2 / 3) {
|
||||||
if (!gotBufferReserve) {
|
// If the buffer runs full, we have no chance of ever catching up
|
||||||
if (ringBuffer->Available() < 4 * MAXFRAMESIZE) {
|
// since the data comes in at the same rate as it goes out (it's "live").
|
||||||
usleep(100000); // allow the buffer to collect some reserve
|
// So let's clear the buffer instead of suffering from permanent
|
||||||
|
// overflows.
|
||||||
|
dsyslog("clearing transfer buffer to avoid overflows");
|
||||||
|
ringBuffer->Clear();
|
||||||
|
remux->Clear();
|
||||||
|
p = NULL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
Count = remux->Put(b, Count);
|
||||||
gotBufferReserve = true;
|
if (Count)
|
||||||
|
ringBuffer->Del(Count);
|
||||||
}
|
}
|
||||||
*/
|
if (!p && (p = remux->Get(Result)) != NULL)
|
||||||
|
StripAudioPackets(p, Result, audioTrack);
|
||||||
// Get data from the buffer:
|
if (p) {
|
||||||
|
cPoller Poller;
|
||||||
int r;
|
if (DevicePoll(Poller, 100)) {
|
||||||
const uchar *b = ringBuffer->Get(r);
|
PollTimeouts = 0;
|
||||||
|
int w = PlayVideo(p, Result);
|
||||||
// Play the data:
|
if (w > 0) {
|
||||||
|
p += w;
|
||||||
if (b) {
|
Result -= w;
|
||||||
int Count = r, Result;
|
remux->Del(w);
|
||||||
uchar *p = remux->Process(b, Count, Result);
|
if (Result <= 0)
|
||||||
ringBuffer->Del(Count);
|
p = NULL;
|
||||||
if (p) {
|
}
|
||||||
StripAudioPackets(p, Result, audioTrack);
|
else if (w < 0 && FATALERRNO)
|
||||||
while (Result > 0 && active) {
|
LOG_ERROR;
|
||||||
cPoller Poller;
|
}
|
||||||
if (DevicePoll(Poller, 100)) {
|
else {
|
||||||
PollTimeouts = 0;
|
PollTimeouts++;
|
||||||
int w = PlayVideo(p, Result);
|
if (PollTimeouts == POLLTIMEOUTS_BEFORE_DEVICECLEAR) {
|
||||||
if (w > 0) {
|
dsyslog("clearing device because of consecutive poll timeouts");
|
||||||
p += w;
|
DeviceClear();
|
||||||
Result -= w;
|
ringBuffer->Clear();
|
||||||
}
|
remux->Clear();
|
||||||
else if (w < 0 && FATALERRNO) {
|
p = NULL;
|
||||||
LOG_ERROR;
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
PollTimeouts++;
|
|
||||||
if (PollTimeouts == POLLTIMEOUTS_BEFORE_DEVICECLEAR) {
|
|
||||||
dsyslog("clearing device because of consecutive poll timeouts");
|
|
||||||
DeviceClear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
usleep(1); // this keeps the CPU load low
|
|
||||||
}
|
}
|
||||||
|
active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cTransfer::StripAudioPackets(uchar *b, int Length, uchar Except)
|
void cTransfer::StripAudioPackets(uchar *b, int Length, uchar Except)
|
||||||
|
@ -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: transfer.h 1.4 2003/05/11 08:48:36 kls Exp $
|
* $Id: transfer.h 1.5 2004/10/15 12:39:54 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __TRANSFER_H
|
#ifndef __TRANSFER_H
|
||||||
@ -22,7 +22,6 @@ private:
|
|||||||
cRemux *remux;
|
cRemux *remux;
|
||||||
bool canToggleAudioTrack;
|
bool canToggleAudioTrack;
|
||||||
uchar audioTrack;
|
uchar audioTrack;
|
||||||
bool gotBufferReserve;
|
|
||||||
bool active;
|
bool active;
|
||||||
void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
|
void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
|
||||||
protected:
|
protected:
|
||||||
|
5
vdr.c
5
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.cadsoft.de/vdr
|
* The project's page is at http://www.cadsoft.de/vdr
|
||||||
*
|
*
|
||||||
* $Id: vdr.c 1.185 2004/07/27 07:21:22 kls Exp $
|
* $Id: vdr.c 1.186 2004/10/10 12:47:56 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -516,6 +516,9 @@ int main(int argc, char *argv[])
|
|||||||
esyslog("emergency exit requested - shutting down");
|
esyslog("emergency exit requested - shutting down");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUGRINGBUFFERS
|
||||||
|
cRingBufferLinear::PrintDebugRBL();
|
||||||
|
#endif
|
||||||
// Attach launched player control:
|
// Attach launched player control:
|
||||||
cControl::Attach();
|
cControl::Attach();
|
||||||
// Make sure we have a visible programme in case device usage has changed:
|
// Make sure we have a visible programme in case device usage has changed:
|
||||||
|
Loading…
Reference in New Issue
Block a user