vdr/sections.c
Klaus Schmidinger ddd1e13e53 Version 1.3.29
- Fixed a race condition in cTransfer (thanks to Klaus Heppenheimer for reporting this one).
  In doing so, the 'active' variables used by the actual derived cThread classes
  have been replaced by the cThread::Running() function.
  Plugin authors may want to check their derived cThread classes and replace any 'active'
  variables the same way as, for instance, done in transfer.c.
- Fixed handling EPG data for time shifted events (thanks to Marco Schlüßler).
- Increased the default value for 'Min. user inactivity' to 300 minutes (suggested
  by Helmut Auer).
- Now storing the channel id in the info.vdr file even if there is no EPG info
  available (thanks to Andreas Brachold for reporting that there are empty info.vdr
  files created in that case).
- Added some 'mkdir -p' to the Makefile's 'install' target (thanks to Wayne Keer).
- Changed the title of the recording info menu (thanks to Rolf Ahrenberg).
- Fixed handling the frame number display if '7' is pressed before the first editing
  mark, or '9' after the last one (thanks to Thomas Günther).
- Now discarding any previous numerical input to switch channels if Up, Down, Channel+,
  Channel-, Left or Right is pressed (thanks to Wolfgang Rohdewald for reporting a
  problem with this).
- Pressing Ok while entering a channel number now immediately switches to that
  channel, without waiting for further input.
- Avoiding unnecessary OSD draw operations caused by the audio track description
  display in the ST:TNG skin's channel display (thanks to Oliver Endriss for reporting
  this).
- Removed the VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES stuff from
  cDvbDevice::StillPicture(), since apparently the VIDEO_STILLPICTURE call works.
2005-08-15 18:00:00 +02:00

226 lines
5.9 KiB
C

/*
* sections.c: Section data handling
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: sections.c 1.13 2005/08/14 10:54:39 kls Exp $
*/
#include "sections.h"
#include <unistd.h>
#include "channels.h"
#include "device.h"
#include "thread.h"
// --- cFilterHandle----------------------------------------------------------
class cFilterHandle : public cListObject {
public:
cFilterData filterData;
int handle;
int used;
cFilterHandle(const cFilterData &FilterData);
};
cFilterHandle::cFilterHandle(const cFilterData &FilterData)
{
filterData = FilterData;
handle = -1;
used = 0;
}
// --- cSectionHandlerPrivate ------------------------------------------------
class cSectionHandlerPrivate {
public:
cChannel channel;
};
// --- cSectionHandler -------------------------------------------------------
cSectionHandler::cSectionHandler(cDevice *Device)
:cThread("Section handler")
{
shp = new cSectionHandlerPrivate;
device = Device;
statusCount = 0;
on = false;
waitForLock = false;
lastIncompleteSection = 0;
Start();
}
cSectionHandler::~cSectionHandler()
{
Cancel(3);
cFilter *fi;
while ((fi = filters.First()) != NULL)
Detach(fi);
delete shp;
}
int cSectionHandler::Source(void)
{
return shp->channel.Source();
}
int cSectionHandler::Transponder(void)
{
return shp->channel.Transponder();
}
const cChannel *cSectionHandler::Channel(void)
{
return &shp->channel;
}
void cSectionHandler::Add(const cFilterData *FilterData)
{
Lock();
statusCount++;
cFilterHandle *fh;
for (fh = filterHandles.First(); fh; fh = filterHandles.Next(fh)) {
if (fh->filterData.Is(FilterData->pid, FilterData->tid, FilterData->mask))
break;
}
if (!fh) {
int handle = device->OpenFilter(FilterData->pid, FilterData->tid, FilterData->mask);
if (handle >= 0) {
fh = new cFilterHandle(*FilterData);
fh->handle = handle;
filterHandles.Add(fh);
}
}
if (fh)
fh->used++;
Unlock();
}
void cSectionHandler::Del(const cFilterData *FilterData)
{
Lock();
statusCount++;
cFilterHandle *fh;
for (fh = filterHandles.First(); fh; fh = filterHandles.Next(fh)) {
if (fh->filterData.Is(FilterData->pid, FilterData->tid, FilterData->mask)) {
if (--fh->used <= 0) {
close(fh->handle);
filterHandles.Del(fh);
break;
}
}
}
Unlock();
}
void cSectionHandler::Attach(cFilter *Filter)
{
Lock();
statusCount++;
filters.Add(Filter);
Filter->sectionHandler = this;
if (on)
Filter->SetStatus(true);
Unlock();
}
void cSectionHandler::Detach(cFilter *Filter)
{
Lock();
statusCount++;
Filter->SetStatus(false);
Filter->sectionHandler = NULL;
filters.Del(Filter, false);
Unlock();
}
void cSectionHandler::SetChannel(const cChannel *Channel)
{
Lock();
shp->channel = Channel ? *Channel : cChannel();
Unlock();
}
void cSectionHandler::SetStatus(bool On)
{
Lock();
if (on != On) {
if (!On || device->HasLock()) {
statusCount++;
for (cFilter *fi = filters.First(); fi; fi = filters.Next(fi)) {
fi->SetStatus(false);
if (On)
fi->SetStatus(true);
}
on = On;
waitForLock = false;
}
else
waitForLock = On;
}
Unlock();
}
void cSectionHandler::Action(void)
{
SetPriority(19);
while (Running()) {
Lock();
if (waitForLock)
SetStatus(true);
int NumFilters = filterHandles.Count();
pollfd pfd[NumFilters];
for (cFilterHandle *fh = filterHandles.First(); fh; fh = filterHandles.Next(fh)) {
int i = fh->Index();
pfd[i].fd = fh->handle;
pfd[i].events = POLLIN;
pfd[i].revents = 0;
}
int oldStatusCount = statusCount;
Unlock();
if (poll(pfd, NumFilters, 1000) > 0) {
bool DeviceHasLock = device->HasLock();
if (!DeviceHasLock)
cCondWait::SleepMs(100);
for (int i = 0; i < NumFilters; i++) {
if (pfd[i].revents & POLLIN) {
cFilterHandle *fh = NULL;
LOCK_THREAD;
if (statusCount != oldStatusCount)
break;
for (fh = filterHandles.First(); fh; fh = filterHandles.Next(fh)) {
if (pfd[i].fd == fh->handle)
break;
}
if (fh) {
// Read section data:
unsigned char buf[4096]; // max. allowed size for any EIT section
int r = safe_read(fh->handle, buf, sizeof(buf));
if (!DeviceHasLock)
continue; // we do the read anyway, to flush any data that might have come from a different transponder
if (r > 3) { // minimum number of bytes necessary to get section length
int len = (((buf[1] & 0x0F) << 8) | (buf[2] & 0xFF)) + 3;
if (len == r) {
// Distribute data to all attached filters:
int pid = fh->filterData.pid;
int tid = buf[0];
for (cFilter *fi = filters.First(); fi; fi = filters.Next(fi)) {
if (fi->Matches(pid, tid))
fi->Process(pid, tid, buf, len);
}
}
else if (time(NULL) - lastIncompleteSection > 10) { // log them only every 10 seconds
dsyslog("read incomplete section - len = %d, r = %d", len, r);
lastIncompleteSection = time(NULL);
}
}
}
}
}
}
}
}