/* * 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) : 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); 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 for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i) filter_value[i] ^= 0xff; uint8_t mask, mode, local_doneq = 0; for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i) { mode = filter_mode[i]; mask = filter_mask[i]; maskandmode[i] = (uint8_t)(mask & mode); maskandnotmode[i] = (uint8_t)(mask & ~mode); local_doneq |= maskandnotmode[i]; } 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); 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; } inline uint16_t cIptvSectionFilter::GetLength(const uint8_t *Data) { return (uint16_t)(3 + ((Data[1] & 0x0f) << 8) + Data[2]); } void cIptvSectionFilter::New(void) { tsfeedp = secbufp = seclen = 0; secbuf = secbuf_base; } int cIptvSectionFilter::Filter(void) { if (secbuf) { int i; uint8_t neq = 0; for (i = 0; i < DMX_MAX_FILTER_SIZE; ++i) { uint8_t local_xor = (uint8_t)(filter_value[i] ^ secbuf[i]); if (maskandmode[i] & local_xor) return 0; neq |= (maskandnotmode[i] & local_xor); } if (doneq && !neq) return 0; if (ringbuffer) { int len = ringbuffer->Put(secbuf, seclen); if (len != seclen) ringbuffer->ReportOverflow(seclen - len); // Update statistics AddSectionStatistic(len, 1); } } return 0; } inline int cIptvSectionFilter::Feed(void) { if (Filter() < 0) return -1; seclen = 0; return 0; } int cIptvSectionFilter::CopyDump(const uint8_t *buf, uint8_t len) { uint16_t limit, seclen_local, n; if (tsfeedp >= DMX_MAX_SECFEED_SIZE) return 0; if (tsfeedp + len > DMX_MAX_SECFEED_SIZE) len = (uint8_t)(DMX_MAX_SECFEED_SIZE - tsfeedp); if (len <= 0) return 0; memcpy(secbuf_base + tsfeedp, buf, len); tsfeedp = uint16_t(tsfeedp + len); limit = tsfeedp; if (limit > DMX_MAX_SECFEED_SIZE) return -1; // internal error should never happen // Always set secbuf secbuf = secbuf_base + secbufp; for (n = 0; secbufp + 2 < limit; ++n) { seclen_local = GetLength(secbuf); if ((seclen_local <= 0) || (seclen_local > DMX_MAX_SECTION_SIZE) || ((seclen_local + secbufp) > limit)) return 0; seclen = seclen_local; if (pusi_seen) Feed(); secbufp = uint16_t(secbufp + seclen_local); secbuf += seclen_local; } return 0; } void cIptvSectionFilter::Process(const uint8_t* Data) { if (Data[0] != TS_SYNC_BYTE) return; // Stop if not the PID this filter is looking for if (ts_pid(Data) != pid) return; uint8_t count = payload(Data); // Check if no payload or out of range if (count == 0) return; // Payload start uint8_t p = (uint8_t)(TS_SIZE - count); uint8_t cc = (uint8_t)(Data[3] & 0x0f); int ccok = ((feedcc + 1) & 0x0f) == cc; feedcc = cc; int dc_i = 0; if (Data[3] & 0x20) { // Adaption field present, check for discontinuity_indicator if ((Data[4] > 0) && (Data[5] & 0x80)) dc_i = 1; } if (!ccok || dc_i) { // Discontinuity detected. Reset pusi_seen = 0 to // stop feeding of suspicious data until next PUSI=1 arrives pusi_seen = 0; New(); } 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]; const uint8_t *after = &before[before_len]; uint8_t after_len = (uint8_t)(count - 1 - before_len); CopyDump(before, before_len); // Before start of new section, set pusi_seen = 1 pusi_seen = 1; New(); CopyDump(after, after_len); } } else { // PUSI=0 (is not set), no section boundary CopyDump(&Data[p], count); } }