mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Removed all error messages from cRecordings::ScanVideoDir() and just skipping entries that cause errors in order to avoid failure in case of things like broken links etc. - The function cTimers::SetEvents() now immediately returns if there is some user input. - Fixed handling menu status messages when the list contents is scrolled (thanks to Alfred Zastrow for reporting this one). - Fixed checking the last area for misalignment in cOsd::CanHandleAreas() (thanks to Reinhard Nissl for reporting this one). - No longer adding section filters to the list of filters if they can't be opened (thanks to Marco Schlüßler for pointing this out). - Fixed handling error case '-1' when polling section filters (thanks to Marco Schlüßler). - Fixed handling error case '-1' when polling CAM connections. - Making sure the OSD reports oeWrongAlignment errors before any oeAreasOverlap error (suggested by Reinhard Nissl). - Avoiding flashing effects in the OSD of full featured DVB cards by explicitly clearing the OSD windows before opening them (suggested by Marco Schlüßler). - Experimental support for NVOD channels. Currently these channels are detected and linked to their "base" channels using the same mechanisms as for the "linked services" (let's see if this is useful). Thanks to Mike parker for helping to test this. Also used some input from the 'autopid' patch by Andreas Schultz). - Now storing the name of the service provider (aka "bouquet") in the channel name, separated by a semicolon (see man vdr(5) for details). Explicit usage of the various parts of the channel name is yet to come. - The 'radio' channel icon is now only displayed in the ST:TNG skin if the channel actually has an APID.
217 lines
5.6 KiB
C
217 lines
5.6 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.7 2004/07/17 14:26:32 kls Exp $
|
|
*/
|
|
|
|
#include "sections.h"
|
|
#include <unistd.h>
|
|
#include "channels.h"
|
|
#include "device.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;
|
|
active = false;
|
|
statusCount = 0;
|
|
on = false;
|
|
lastIncompleteSection = 0;
|
|
Start();
|
|
}
|
|
|
|
cSectionHandler::~cSectionHandler()
|
|
{
|
|
active = false;
|
|
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;
|
|
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) {
|
|
statusCount++;
|
|
for (cFilter *fi = filters.First(); fi; fi = filters.Next(fi)) {
|
|
fi->SetStatus(false);
|
|
if (On)
|
|
fi->SetStatus(true);
|
|
}
|
|
on = On;
|
|
}
|
|
Unlock();
|
|
}
|
|
|
|
void cSectionHandler::Action(void)
|
|
{
|
|
active = true;
|
|
while (active) {
|
|
|
|
Lock();
|
|
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;
|
|
}
|
|
int oldStatusCount = statusCount;
|
|
Unlock();
|
|
|
|
if (poll(pfd, NumFilters, 1000) > 0) {
|
|
bool DeviceHasLock = device->HasLock();
|
|
if (!DeviceHasLock)
|
|
usleep(100000);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|