vdr-plugin-iptv/sectionfilter.c

213 lines
5.2 KiB
C
Raw 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"
#include "sectionfilter.h"
cIptvSectionFilter::cIptvSectionFilter(int DeviceIndex, uint16_t Pid, uint8_t Tid, uint8_t Mask)
2007-09-24 19:20:58 +02:00
: pusi_seen(0),
feedcc(0),
doneq(0),
secbuf(NULL),
secbufp(0),
seclen(0),
tsfeedp(0),
pid(Pid),
devid(DeviceIndex)
{
//debug("cIptvSectionFilter::cIptvSectionFilter(%d, %d)\n", devid, pid);
2009-02-27 15:05:19 +01:00
int i;
memset(secbuf_base, '\0', sizeof(secbuf_base));
memset(filter_value, '\0', sizeof(filter_value));
memset(filter_mask, '\0', sizeof(filter_mask));
memset(filter_mode, '\0', sizeof(filter_mode));
memset(maskandmode, '\0', sizeof(maskandmode));
memset(maskandnotmode, '\0', sizeof(maskandnotmode));
filter_value[0] = Tid;
filter_mask[0] = Mask;
// Invert the filter
2009-02-27 15:05:19 +01:00
for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i)
filter_value[i] ^= 0xff;
uint8_t mask, mode, local_doneq = 0;
2009-02-27 15:05:19 +01:00
for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i) {
2008-01-30 22:57:33 +01:00
mode = filter_mode[i];
mask = filter_mask[i];
2009-06-19 12:28:53 +02:00
maskandmode[i] = (uint8_t)(mask & mode);
maskandnotmode[i] = (uint8_t)(mask & ~mode);
local_doneq |= maskandnotmode[i];
2008-01-30 22:57:33 +01:00
}
doneq = local_doneq ? 1 : 0;
// Create filtering buffer
ringbuffer = new cRingBufferLinear(KILOBYTE(128), 0, false, *cString::sprintf("IPTV SECTION %d/%d", devid, pid));
if (ringbuffer)
ringbuffer->SetTimeouts(10, 10);
else
error("Failed to allocate buffer for section filter (device=%d pid=%d): ", devid, pid);
}
cIptvSectionFilter::~cIptvSectionFilter()
{
//debug("cIptvSectionFilter::~cIptvSectionfilter(%d, %d)\n", devid, pid);
DELETE_POINTER(ringbuffer);
2009-03-20 16:43:31 +01:00
secbuf = NULL;
}
int cIptvSectionFilter::Read(void *Data, size_t Length)
{
int count = 0;
uchar *p = ringbuffer->Get(count);
if (p && count > 0) {
memcpy(Data, p, count);
ringbuffer->Del(count);
}
return count;
}
2009-02-27 15:05:19 +01:00
inline uint16_t cIptvSectionFilter::GetLength(const uint8_t *Data)
{
2009-06-19 12:28:53 +02:00
return (uint16_t)(3 + ((Data[1] & 0x0f) << 8) + Data[2]);
}
2009-02-27 15:05:19 +01:00
void cIptvSectionFilter::New(void)
{
tsfeedp = secbufp = seclen = 0;
secbuf = secbuf_base;
}
2009-02-27 15:05:19 +01:00
int cIptvSectionFilter::Filter(void)
{
2009-03-20 16:43:31 +01:00
if (secbuf) {
2011-06-15 16:49:15 +02:00
int i;
uint8_t neq = 0;
2009-03-20 16:43:31 +01:00
for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i) {
2009-06-19 12:28:53 +02:00
uint8_t local_xor = (uint8_t)(filter_value[i] ^ secbuf[i]);
2009-03-20 16:43:31 +01:00
if (maskandmode[i] & local_xor)
return 0;
2009-06-19 12:28:53 +02:00
neq |= (maskandnotmode[i] & local_xor);
2009-03-20 16:43:31 +01:00
}
if (doneq && !neq)
return 0;
if (ringbuffer) {
int len = ringbuffer->Put(secbuf, seclen);
if (len != seclen)
ringbuffer->ReportOverflow(seclen - len);
2009-03-20 16:43:31 +01:00
// Update statistics
2009-06-19 12:28:53 +02:00
AddSectionStatistic(len, 1);
2009-03-20 16:43:31 +01:00
}
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;
seclen = 0;
return 0;
}
2009-02-27 15:05:19 +01:00
int cIptvSectionFilter::CopyDump(const uint8_t *buf, uint8_t len)
{
uint16_t limit, seclen_local, n;
2007-09-24 19:20:58 +02:00
if (tsfeedp >= DMX_MAX_SECFEED_SIZE)
return 0;
2009-02-27 15:05:19 +01:00
if (tsfeedp + len > DMX_MAX_SECFEED_SIZE)
2009-06-19 12:28:53 +02:00
len = (uint8_t)(DMX_MAX_SECFEED_SIZE - tsfeedp);
if (len <= 0)
2007-09-24 19:20:58 +02:00
return 0;
memcpy(secbuf_base + tsfeedp, buf, len);
2012-02-19 18:14:10 +01:00
tsfeedp = uint16_t(tsfeedp + len);
limit = tsfeedp;
2007-09-24 19:20:58 +02:00
if (limit > DMX_MAX_SECFEED_SIZE)
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
secbuf = secbuf_base + secbufp;
2008-01-30 22:57:33 +01:00
for (n = 0; secbufp + 2 < limit; ++n) {
2009-02-27 15:05:19 +01:00
seclen_local = GetLength(secbuf);
if ((seclen_local <= 0) || (seclen_local > DMX_MAX_SECTION_SIZE) || ((seclen_local + secbufp) > limit))
2007-09-24 19:20:58 +02:00
return 0;
seclen = seclen_local;
if (pusi_seen)
2009-02-27 15:05:19 +01:00
Feed();
2012-02-19 18:14:10 +01:00
secbufp = uint16_t(secbufp + seclen_local);
2009-02-27 15:05:19 +01:00
secbuf += seclen_local;
2007-09-24 19:20:58 +02:00
}
return 0;
}
2009-02-27 15:05:19 +01:00
void cIptvSectionFilter::Process(const uint8_t* Data)
{
2009-02-27 15:05:19 +01:00
if (Data[0] != TS_SYNC_BYTE)
2007-09-24 19:20:58 +02:00
return;
// Stop if not the PID this filter is looking for
2009-02-27 15:05:19 +01:00
if (ts_pid(Data) != pid)
2007-09-24 19:20:58 +02:00
return;
2009-02-27 15:05:19 +01:00
uint8_t count = payload(Data);
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);
2009-06-19 12:28:53 +02:00
uint8_t cc = (uint8_t)(Data[3] & 0x0f);
int ccok = ((feedcc + 1) & 0x0f) == cc;
feedcc = cc;
int dc_i = 0;
2009-02-27 15:05:19 +01:00
if (Data[3] & 0x20) {
// Adaption field present, check for discontinuity_indicator
if ((Data[4] > 0) && (Data[5] & 0x80))
2007-09-24 19:20:58 +02:00
dc_i = 1;
}
if (!ccok || dc_i) {
2009-02-27 15:05:19 +01:00
// Discontinuity detected. Reset pusi_seen = 0 to
// stop feeding of suspicious data until next PUSI=1 arrives
2007-09-24 19:20:58 +02:00
pusi_seen = 0;
2009-02-27 15:05:19 +01:00
New();
2007-09-24 19:20:58 +02:00
}
2009-02-27 15:05:19 +01:00
if (Data[1] & 0x40) {
// PUSI=1 (is set), section boundary is here
if (count > 1 && Data[p] < count) {
const uint8_t *before = &Data[p + 1];
uint8_t before_len = Data[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
2009-02-27 15:05:19 +01:00
// Before start of new section, set pusi_seen = 1
2007-09-24 19:20:58 +02:00
pusi_seen = 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
CopyDump(&Data[p], count);
2007-09-24 19:20:58 +02:00
}
}