vdr/ringbuffer.c
Klaus Schmidinger c84022554a Version 1.1.31
- Introduced the new function cPlugin::Initialize(), in order to be able to separate
  the startup of a plugin into an "early" (Initialize()) and "late" (Start()) phase
  (suggested by Andreas Schultz). Plugin authors should please read the section
  about "Getting started" in PLUGINS.html and adapt their code if applicable.
- Implemented the CableDeliverySystemDescriptor and TerrestrialDeliverySystemDescriptor
  in libdtv (thanks to Sven Grothklags and Andreas Schultz).
- Fixed keeping live video active in case the primary device doesn't have an MPEG
  decoder (thanks to Wolfgang Goeller for reporting this one).
- Implemented cDevice::ActualDevice(), which returns the actual receiving device in
  case of 'Transfer Mode', or the primary device otherwise. This may be useful for
  plugins that want to attach a cReceiver to the device where the current live video
  is actually coming from.
- Added VDRVERSNUM to config.h, which can be used by the preprocessor to check the
  actual VDR version (suggested by Stefan Huelswitt).
- Removed the WaitForPut/WaitForGet stuff from cRingBuffer, since it appears to
  no longer be necessary due to the implementation of cNonBlockingFileReader in
  dvbplayer.c. Also, the long timeout in WaitForPut caused problems with cReceivers
  that use a ring buffer and didn't immediately return from their Receive() function
  if the buffer runs full (thanks to Sascha Volkenandt for reporting this one).
- Fixed handling EPG data where the "extended event descriptor" comes before the
  "short event" or a "time shifted event" (thanks to Jonan Santiago).
- Disabled the "Received stuffing section in EIT" log message.
- Updated 'channels.conf.terr' for Berlin (thanks to Juri Haberland).
- Avoiding short display of the "Main" menu when pressing the "Recordings" button
  or the "Back" button during replay.
- Further increased the timeout until an index file is considerd no longer to be
  written.
- Implemented separate PausePriority and PauseLifetime parameters for the recordings
  created when pausing live video (suggested by Alfred Zastrow).
- Changed C++ style comments in libdtv into C style to avoid warnings in gcc 3.x
  (thanks to Andreas Schultz).
2003-05-11 18:00:00 +02:00

261 lines
5.4 KiB
C

/*
* ringbuffer.c: A ring buffer
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* Parts of this file were inspired by the 'ringbuffy.c' from the
* LinuxDVB driver (see linuxtv.org).
*
* $Id: ringbuffer.c 1.16 2003/05/11 09:47:56 kls Exp $
*/
#include "ringbuffer.h"
#include <stdlib.h>
#include <unistd.h>
#include "tools.h"
// --- cRingBuffer -----------------------------------------------------------
cRingBuffer::cRingBuffer(int Size, bool Statistics)
{
size = Size;
statistics = Statistics;
maxFill = 0;
lastPercent = 0;
}
cRingBuffer::~cRingBuffer()
{
if (statistics)
dsyslog("buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
}
// --- cRingBufferLinear -----------------------------------------------------
cRingBufferLinear::cRingBufferLinear(int Size, int Margin, bool Statistics)
:cRingBuffer(Size, Statistics)
{
margin = Margin;
buffer = NULL;
getThreadPid = -1;
if (Size > 1) { // 'Size - 1' must not be 0!
buffer = MALLOC(uchar, Size);
if (!buffer)
esyslog("ERROR: can't allocate ring buffer (size=%d)", Size);
Clear();
}
else
esyslog("ERROR: illegal size for ring buffer (%d)", Size);
}
cRingBufferLinear::~cRingBufferLinear()
{
free(buffer);
}
int cRingBufferLinear::Available(void)
{
Lock();
int diff = head - tail;
Unlock();
return (diff >= 0) ? diff : Size() + diff - margin;
}
void cRingBufferLinear::Clear(void)
{
Lock();
head = tail = margin;
lastGet = -1;
Unlock();
}
int cRingBufferLinear::Put(const uchar *Data, int Count)
{
if (Count > 0) {
Lock();
int rest = Size() - head;
int diff = tail - head;
int free = ((tail < margin) ? rest : (diff > 0) ? diff : Size() + diff - margin) - 1;
if (statistics) {
int fill = Size() - free - 1 + Count;
if (fill >= Size())
fill = Size() - 1;
if (fill > maxFill)
maxFill = fill;
int percent = maxFill * 100 / (Size() - 1) / 5 * 5;
if (abs(lastPercent - percent) >= 5) {
if (percent > 75)
dsyslog("buffer usage: %d%% (pid=%d)", percent, getThreadPid);
lastPercent = percent;
}
}
if (free > 0) {
if (free < Count)
Count = free;
if (Count > maxFill)
maxFill = Count;
if (Count >= rest) {
memcpy(buffer + head, Data, rest);
if (Count - rest)
memcpy(buffer + margin, Data + rest, Count - rest);
head = margin + Count - rest;
}
else {
memcpy(buffer + head, Data, Count);
head += Count;
}
}
else
Count = 0;
Unlock();
}
return Count;
}
uchar *cRingBufferLinear::Get(int &Count)
{
uchar *p = NULL;
Lock();
if (getThreadPid < 0)
getThreadPid = getpid();
int rest = Size() - tail;
if (rest < margin && head < tail) {
int t = margin - rest;
memcpy(buffer + t, buffer + tail, rest);
tail = t;
}
int diff = head - tail;
int cont = (diff >= 0) ? diff : Size() + diff - margin;
if (cont > rest)
cont = rest;
if (cont >= margin) {
p = buffer + tail;
Count = lastGet = cont;
}
Unlock();
return p;
}
void cRingBufferLinear::Del(int Count)
{
if (Count > 0 && Count <= lastGet) {
Lock();
tail += Count;
lastGet -= Count;
if (tail >= Size())
tail = margin;
Unlock();
}
else
esyslog("ERROR: invalid Count in cRingBufferLinear::Del: %d", Count);
}
// --- cFrame ----------------------------------------------------------------
cFrame::cFrame(const uchar *Data, int Count, eFrameType Type, int Index)
{
count = abs(Count);
type = Type;
index = Index;
if (Count < 0)
data = (uchar *)Data;
else {
data = MALLOC(uchar, count);
if (data)
memcpy(data, Data, count);
else
esyslog("ERROR: can't allocate frame buffer (count=%d)", count);
}
next = NULL;
}
cFrame::~cFrame()
{
free(data);
}
// --- cRingBufferFrame ------------------------------------------------------
cRingBufferFrame::cRingBufferFrame(int Size, bool Statistics)
:cRingBuffer(Size, Statistics)
{
head = NULL;
currentFill = 0;
}
cRingBufferFrame::~cRingBufferFrame()
{
Clear();
}
void cRingBufferFrame::Clear(void)
{
Lock();
cFrame *p;
while ((p = Get()) != NULL)
Drop(p);
Unlock();
}
bool cRingBufferFrame::Put(cFrame *Frame)
{
if (Frame->Count() <= Free()) {
Lock();
if (head) {
Frame->next = head->next;
head->next = Frame;
head = Frame;
}
else {
head = Frame->next = Frame;
}
currentFill += Frame->Count();
Unlock();
return true;
}
return false;
}
cFrame *cRingBufferFrame::Get(void)
{
Lock();
cFrame *p = head ? head->next : NULL;
Unlock();
return p;
}
void cRingBufferFrame::Delete(cFrame *Frame)
{
currentFill -= Frame->Count();
delete Frame;
}
void cRingBufferFrame::Drop(cFrame *Frame)
{
Lock();
if (head) {
if (Frame == head->next) {
if (head->next != head) {
head->next = Frame->next;
Delete(Frame);
}
else {
Delete(head);
head = NULL;
}
}
else
esyslog("ERROR: attempt to drop wrong frame from ring buffer!");
}
Unlock();
}
int cRingBufferFrame::Available(void)
{
Lock();
int av = currentFill;
Unlock();
return av;
}