vdr-plugin-iptv/sectionfilter.c

428 lines
12 KiB
C
Raw Permalink Normal View History

/*
* sectionfilter.c: IPTV plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
*/
#include "config.h"
2015-03-08 13:33:18 +01:00
#include "log.h"
#include "sectionfilter.h"
2013-02-23 14:31:11 +01:00
cIptvSectionFilter::cIptvSectionFilter(int deviceIndexP, uint16_t pidP, uint8_t tidP, uint8_t maskP)
: pusiSeenM(0),
feedCcM(0),
doneqM(0),
secBufM(NULL),
secBufpM(0),
secLenM(0),
tsFeedpM(0),
pidM(pidP),
2014-04-05 17:43:23 +02:00
ringBufferM(new cRingBufferFrame(eDmxMaxSectionCount * eDmxMaxSectionSize)),
deviceIndexM(deviceIndexP)
{
2015-03-08 13:33:18 +01:00
debug16("%s (%d, %d)", __PRETTY_FUNCTION__, deviceIndexM, pidM);
2009-02-27 15:05:19 +01:00
int i;
2013-02-23 14:31:11 +01:00
memset(secBufBaseM, 0, sizeof(secBufBaseM));
memset(filterValueM, 0, sizeof(filterValueM));
memset(filterMaskM, 0, sizeof(filterMaskM));
memset(filterModeM, 0, sizeof(filterModeM));
memset(maskAndModeM, 0, sizeof(maskAndModeM));
memset(maskAndNotModeM, 0, sizeof(maskAndNotModeM));
2013-02-23 14:31:11 +01:00
filterValueM[0] = tidP;
filterMaskM[0] = maskP;
// Invert the filter
2014-04-05 17:43:23 +02:00
for (i = 0; i < eDmxMaxFilterSize; ++i)
2013-02-23 14:31:11 +01:00
filterValueM[i] ^= 0xFF;
2013-02-23 14:31:11 +01:00
uint8_t mask, mode, doneq = 0;
2014-04-05 17:43:23 +02:00
for (i = 0; i < eDmxMaxFilterSize; ++i) {
2013-02-23 14:31:11 +01:00
mode = filterModeM[i];
mask = filterMaskM[i];
maskAndModeM[i] = (uint8_t)(mask & mode);
maskAndNotModeM[i] = (uint8_t)(mask & ~mode);
doneq |= maskAndNotModeM[i];
2008-01-30 22:57:33 +01:00
}
2013-02-23 14:31:11 +01:00
doneqM = doneq ? 1 : 0;
// Create sockets
socketM[0] = socketM[1] = -1;
2014-04-05 17:43:23 +02:00
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socketM) != 0) {
char tmp[64];
error("Opening section filter sockets failed (device=%d pid=%d): %s", deviceIndexM, pidM, strerror_r(errno, tmp, sizeof(tmp)));
}
else if ((fcntl(socketM[0], F_SETFL, O_NONBLOCK) != 0) || (fcntl(socketM[1], F_SETFL, O_NONBLOCK) != 0)) {
char tmp[64];
error("Setting section filter socket to non-blocking mode failed (device=%d pid=%d): %s", deviceIndexM, pidM, strerror_r(errno, tmp, sizeof(tmp)));
}
}
cIptvSectionFilter::~cIptvSectionFilter()
{
2015-03-08 13:33:18 +01:00
debug16("%s (%d, %d)", __PRETTY_FUNCTION__, deviceIndexM, pidM);
int tmp = socketM[1];
socketM[1] = -1;
if (tmp >= 0)
close(tmp);
tmp = socketM[0];
socketM[0] = -1;
if (tmp >= 0)
close(tmp);
2013-02-23 14:31:11 +01:00
secBufM = NULL;
2014-04-05 17:43:23 +02:00
DELETENULL(ringBufferM);
}
2013-02-23 14:31:11 +01:00
inline uint16_t cIptvSectionFilter::GetLength(const uint8_t *dataP)
{
2013-02-23 14:31:11 +01:00
return (uint16_t)(3 + ((dataP[1] & 0x0f) << 8) + dataP[2]);
}
2009-02-27 15:05:19 +01:00
void cIptvSectionFilter::New(void)
{
2013-02-23 14:31:11 +01:00
tsFeedpM = secBufpM = secLenM = 0;
secBufM = secBufBaseM;
}
2009-02-27 15:05:19 +01:00
int cIptvSectionFilter::Filter(void)
{
2013-02-23 14:31:11 +01:00
if (secBufM) {
2011-06-15 16:49:15 +02:00
int i;
uint8_t neq = 0;
2014-04-05 17:43:23 +02:00
for (i = 0; i < eDmxMaxFilterSize; ++i) {
2013-02-23 14:31:11 +01:00
uint8_t calcxor = (uint8_t)(filterValueM[i] ^ secBufM[i]);
if (maskAndModeM[i] & calcxor)
2009-03-20 16:43:31 +01:00
return 0;
2013-02-23 14:31:11 +01:00
neq |= (maskAndNotModeM[i] & calcxor);
2009-03-20 16:43:31 +01:00
}
2013-02-23 14:31:11 +01:00
if (doneqM && !neq)
2009-03-20 16:43:31 +01:00
return 0;
2014-04-05 17:43:23 +02:00
if (ringBufferM && (secLenM > 0))
ringBufferM->Put(new cFrame(secBufM, secLenM));
2007-09-24 19:20:58 +02:00
}
2009-02-27 15:05:19 +01:00
return 0;
}
2009-02-27 15:05:19 +01:00
inline int cIptvSectionFilter::Feed(void)
{
2009-02-27 15:05:19 +01:00
if (Filter() < 0)
2007-09-24 19:20:58 +02:00
return -1;
2013-02-23 14:31:11 +01:00
secLenM = 0;
return 0;
}
2013-02-23 14:31:11 +01:00
int cIptvSectionFilter::CopyDump(const uint8_t *bufP, uint8_t lenP)
{
2013-02-23 14:31:11 +01:00
uint16_t limit, seclen, n;
2014-04-05 17:43:23 +02:00
if (tsFeedpM >= eDmxMaxSectionFeedSize)
2007-09-24 19:20:58 +02:00
return 0;
2014-04-05 17:43:23 +02:00
if (tsFeedpM + lenP > eDmxMaxSectionFeedSize)
lenP = (uint8_t)(eDmxMaxSectionFeedSize - tsFeedpM);
2013-02-23 14:31:11 +01:00
if (lenP <= 0)
2007-09-24 19:20:58 +02:00
return 0;
2013-02-23 14:31:11 +01:00
memcpy(secBufBaseM + tsFeedpM, bufP, lenP);
tsFeedpM = uint16_t(tsFeedpM + lenP);
2013-02-23 14:31:11 +01:00
limit = tsFeedpM;
2014-04-05 17:43:23 +02:00
if (limit > eDmxMaxSectionFeedSize)
2009-03-20 17:00:17 +01:00
return -1; // internal error should never happen
2009-02-27 15:05:19 +01:00
// Always set secbuf
2013-02-23 14:31:11 +01:00
secBufM = secBufBaseM + secBufpM;
2013-02-23 14:31:11 +01:00
for (n = 0; secBufpM + 2 < limit; ++n) {
seclen = GetLength(secBufM);
2014-04-05 17:43:23 +02:00
if ((seclen <= 0) || (seclen > eDmxMaxSectionSize) || ((seclen + secBufpM) > limit))
2007-09-24 19:20:58 +02:00
return 0;
2013-02-23 14:31:11 +01:00
secLenM = seclen;
if (pusiSeenM)
2009-02-27 15:05:19 +01:00
Feed();
2013-02-23 14:31:11 +01:00
secBufpM = uint16_t(secBufpM + seclen);
secBufM += seclen;
2007-09-24 19:20:58 +02:00
}
return 0;
}
2013-02-23 14:31:11 +01:00
void cIptvSectionFilter::Process(const uint8_t* dataP)
{
2013-02-23 14:31:11 +01:00
if (dataP[0] != TS_SYNC_BYTE)
2007-09-24 19:20:58 +02:00
return;
// Stop if not the PID this filter is looking for
2013-02-23 14:31:11 +01:00
if (ts_pid(dataP) != pidM)
2007-09-24 19:20:58 +02:00
return;
2013-02-23 14:31:11 +01:00
uint8_t count = payload(dataP);
2009-02-27 15:05:19 +01:00
// Check if no payload or out of range
if (count == 0)
2007-09-24 19:20:58 +02:00
return;
2009-02-27 15:05:19 +01:00
// Payload start
2009-06-19 12:28:53 +02:00
uint8_t p = (uint8_t)(TS_SIZE - count);
2013-02-23 14:31:11 +01:00
uint8_t cc = (uint8_t)(dataP[3] & 0x0f);
int ccok = ((feedCcM + 1) & 0x0f) == cc;
feedCcM = cc;
int dc_i = 0;
2013-02-23 14:31:11 +01:00
if (dataP[3] & 0x20) {
2009-02-27 15:05:19 +01:00
// Adaption field present, check for discontinuity_indicator
2013-02-23 14:31:11 +01:00
if ((dataP[4] > 0) && (dataP[5] & 0x80))
2007-09-24 19:20:58 +02:00
dc_i = 1;
}
if (!ccok || dc_i) {
2013-02-23 14:31:11 +01:00
// Discontinuity detected. Reset pusiSeenM = 0 to
2009-02-27 15:05:19 +01:00
// stop feeding of suspicious data until next PUSI=1 arrives
2013-02-23 14:31:11 +01:00
pusiSeenM = 0;
2009-02-27 15:05:19 +01:00
New();
2007-09-24 19:20:58 +02:00
}
2013-02-23 14:31:11 +01:00
if (dataP[1] & 0x40) {
2009-02-27 15:05:19 +01:00
// PUSI=1 (is set), section boundary is here
2013-02-23 14:31:11 +01:00
if (count > 1 && dataP[p] < count) {
const uint8_t *before = &dataP[p + 1];
uint8_t before_len = dataP[p];
2007-09-24 19:20:58 +02:00
const uint8_t *after = &before[before_len];
2009-06-19 12:28:53 +02:00
uint8_t after_len = (uint8_t)(count - 1 - before_len);
2009-02-27 15:05:19 +01:00
CopyDump(before, before_len);
2007-09-24 19:20:58 +02:00
2013-02-23 14:31:11 +01:00
// Before start of new section, set pusiSeenM = 1
pusiSeenM = 1;
2009-02-27 15:05:19 +01:00
New();
CopyDump(after, after_len);
2007-09-24 19:20:58 +02:00
}
}
else {
2009-02-27 15:05:19 +01:00
// PUSI=0 (is not set), no section boundary
2013-02-23 14:31:11 +01:00
CopyDump(&dataP[p], count);
2007-09-24 19:20:58 +02:00
}
}
2014-04-05 17:43:23 +02:00
bool cIptvSectionFilter::Send(void)
{
bool result = false;
cFrame *section = ringBufferM->Get();
if (section) {
uchar *data = section->Data();
int count = section->Count();
if (data && (count > 0) && (socketM[1] >= 0) && (socketM[0] >= 0)) {
ssize_t len = send(socketM[1], data, count, MSG_EOR);
ERROR_IF(len < 0 && errno != EAGAIN, "send()");
if (len > 0) {
ringBufferM->Drop(section);
result = !!ringBufferM->Available();
// Update statistics
AddSectionStatistic(len, 1);
}
}
}
return result;
}
cIptvSectionFilterHandler::cIptvSectionFilterHandler(int deviceIndexP, unsigned int bufferLenP)
2014-04-05 17:43:23 +02:00
: cThread("IPTV section handler"),
ringBufferM(new cRingBufferLinear(bufferLenP, TS_SIZE, false, *cString::sprintf("IPTV SECTION HANDLER %d", deviceIndexP))),
mutexM(),
2014-04-05 17:43:23 +02:00
deviceIndexM(deviceIndexP)
{
2015-03-08 13:33:18 +01:00
debug1("%s (%d, %d) [device %d]", __PRETTY_FUNCTION__, deviceIndexP, bufferLenP, deviceIndexM);
// Initialize filter pointers
memset(filtersM, 0, sizeof(filtersM));
// Create input buffer
if (ringBufferM) {
ringBufferM->SetTimeouts(100, 100);
ringBufferM->SetIoThrottle();
}
else
error("Failed to allocate buffer for section filter handler (device=%d): ", deviceIndexM);
Start();
}
cIptvSectionFilterHandler::~cIptvSectionFilterHandler()
{
2015-03-08 13:33:18 +01:00
debug1("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2014-04-05 17:43:23 +02:00
// Stop thread
if (Running())
Cancel(3);
DELETE_POINTER(ringBufferM);
// Destroy all filters
cMutexLock MutexLock(&mutexM);
for (int i = 0; i < eMaxSecFilterCount; ++i)
Delete(i);
}
void cIptvSectionFilterHandler::Action(void)
{
2015-03-08 13:33:18 +01:00
debug1("%s Entering [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
2014-04-05 17:43:23 +02:00
bool processed = false;
// Do the thread loop
while (Running()) {
2014-04-05 17:43:23 +02:00
// Send demuxed section packets through all filters
bool retry = false;
mutexM.Lock();
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (filtersM[i] && filtersM[i]->Send())
retry = true;
}
mutexM.Unlock();
if (retry)
continue;
// Read one TS packet
if (ringBufferM) {
int len = 0;
2014-04-05 17:43:23 +02:00
if (processed) {
ringBufferM->Del(TS_SIZE);
2014-04-05 17:43:23 +02:00
processed = false;
}
uchar *p = ringBufferM->Get(len);
if (p && (len >= TS_SIZE)) {
if (*p != TS_SYNC_BYTE) {
for (int i = 1; i < len; ++i) {
if (p[i] == TS_SYNC_BYTE) {
len = i;
break;
}
}
ringBufferM->Del(len);
2015-03-08 13:33:18 +01:00
debug1("%s Skipped %d bytes to sync on TS packet [device %d]]", __PRETTY_FUNCTION__, len, deviceIndexM);
continue;
}
// Process TS packet through all filters
mutexM.Lock();
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (filtersM[i])
filtersM[i]->Process(p);
}
mutexM.Unlock();
2014-04-05 17:43:23 +02:00
processed = true;
continue;
}
}
cCondWait::SleepMs(10); // to avoid busy loop and reduce cpu load
}
2015-03-08 13:33:18 +01:00
debug1("%s Exiting [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
}
cString cIptvSectionFilterHandler::GetInformation(void)
{
2015-03-08 13:33:18 +01:00
debug16("%s [device %d]", __PRETTY_FUNCTION__, deviceIndexM);
// loop through active section filters
cMutexLock MutexLock(&mutexM);
cString s = "";
unsigned int count = 0;
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (filtersM[i]) {
s = cString::sprintf("%sFilter %d: %s Pid=0x%02X (%s)\n", *s, i,
*filtersM[i]->GetSectionStatistic(), filtersM[i]->GetPid(),
id_pid(filtersM[i]->GetPid()));
if (++count > IPTV_STATS_ACTIVE_FILTERS_COUNT)
break;
}
}
return s;
}
bool cIptvSectionFilterHandler::Delete(unsigned int indexP)
{
2015-03-08 13:33:18 +01:00
debug16("%s (%d) [device %d]", __PRETTY_FUNCTION__, indexP, deviceIndexM);
if ((indexP < eMaxSecFilterCount) && filtersM[indexP]) {
2015-03-08 13:33:18 +01:00
debug16("%s (%d) Found [device %d]", __PRETTY_FUNCTION__, indexP, deviceIndexM);
cIptvSectionFilter *tmp = filtersM[indexP];
filtersM[indexP] = NULL;
delete tmp;
return true;
}
return false;
}
bool cIptvSectionFilterHandler::IsBlackListed(u_short pidP, u_char tidP, u_char maskP) const
{
2015-03-08 13:33:18 +01:00
debug16("%s (%d, %02X, %02X) [device %d]", __PRETTY_FUNCTION__, pidP, tidP, maskP, deviceIndexM);
// loop through section filter table
for (int i = 0; i < SECTION_FILTER_TABLE_SIZE; ++i) {
int index = IptvConfig.GetDisabledFilters(i);
// Check if matches
if ((index >= 0) && (index < SECTION_FILTER_TABLE_SIZE) &&
(section_filter_table[index].pid == pidP) && (section_filter_table[index].tid == tidP) &&
(section_filter_table[index].mask == maskP)) {
2015-03-08 13:33:18 +01:00
debug16("%s (%d, %02X, %02X) Found %s [device %d]", __PRETTY_FUNCTION__, pidP, tidP, maskP, section_filter_table[index].description, deviceIndexM);
return true;
}
}
return false;
}
int cIptvSectionFilterHandler::Open(u_short pidP, u_char tidP, u_char maskP)
{
// Lock
cMutexLock MutexLock(&mutexM);
// Blacklist check, refuse certain filters
if (IsBlackListed(pidP, tidP, maskP))
return -1;
// Search the next free filter slot
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (!filtersM[i]) {
filtersM[i] = new cIptvSectionFilter(deviceIndexM, pidP, tidP, maskP);
2015-03-08 13:33:18 +01:00
debug16("%s (%d, %02X, %02X) handle=%d index=%u [device %d]", __PRETTY_FUNCTION__, pidP, tidP, maskP, filtersM[i]->GetFd(), i, deviceIndexM);
return filtersM[i]->GetFd();
}
}
// No free filter slot found
return -1;
}
void cIptvSectionFilterHandler::Close(int handleP)
{
// Lock
cMutexLock MutexLock(&mutexM);
// Search the filter for deletion
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (filtersM[i] && (handleP == filtersM[i]->GetFd())) {
2015-03-08 13:33:18 +01:00
debug1("%s (%d) pid=%d handle=%d index=%d [device %d]", __PRETTY_FUNCTION__, handleP, filtersM[i]->GetPid(), filtersM[i]->GetFd(), i, deviceIndexM);
Delete(i);
break;
}
}
}
2014-02-09 18:22:02 +01:00
int cIptvSectionFilterHandler::GetPid(int handleP)
{
// Lock
cMutexLock MutexLock(&mutexM);
// Search the filter for data
for (unsigned int i = 0; i < eMaxSecFilterCount; ++i) {
if (filtersM[i] && (handleP == filtersM[i]->GetFd())) {
2015-03-08 13:33:18 +01:00
debug1("%s (%d) pid=%d handle=%d index=%d [device %d]", __PRETTY_FUNCTION__, handleP, filtersM[i]->GetPid(), filtersM[i]->GetFd(), i, deviceIndexM);
2014-02-09 18:22:02 +01:00
return filtersM[i]->GetPid();
}
}
return -1;
}
void cIptvSectionFilterHandler::Write(uchar *bufferP, int lengthP)
{
2015-03-08 13:33:18 +01:00
debug16("%s (, %d) [device %d]", __PRETTY_FUNCTION__, lengthP, deviceIndexM);
// Fill up the buffer
if (ringBufferM) {
int len = ringBufferM->Put(bufferP, lengthP);
if (len != lengthP)
ringBufferM->ReportOverflow(lengthP - len);
}
}