summaryrefslogtreecommitdiff
path: root/filter.c
diff options
context:
space:
mode:
authorLars Heer <l.heer@gmx.de>2013-09-18 05:50:03 +0200
committerLars Heer <l.heer@gmx.de>2013-09-18 05:50:03 +0200
commitccf6e0f9c6b0481ed13e0f4794e3fbead750f385 (patch)
treeed86efb54f7ee41edfba5c89ca519b5fd10aa0d5 /filter.c
downloadvdr-plugin-mcli-ccf6e0f9c6b0481ed13e0f4794e3fbead750f385.tar.gz
vdr-plugin-mcli-ccf6e0f9c6b0481ed13e0f4794e3fbead750f385.tar.bz2
added vdr-plugin-mcli-0.0.1+svn20120927
Diffstat (limited to 'filter.c')
-rw-r--r--filter.c463
1 files changed, 463 insertions, 0 deletions
diff --git a/filter.c b/filter.c
new file mode 100644
index 0000000..fdcbe7a
--- /dev/null
+++ b/filter.c
@@ -0,0 +1,463 @@
+/*
+ * (c) BayCom GmbH, http://www.baycom.de, info@baycom.de
+ *
+ * See the COPYING file for copyright information and
+ * how to reach the author.
+ *
+ */
+
+#include "filter.h"
+#include "device.h"
+
+#include <vdr/device.h>
+
+#define PID_MASK_HI 0x1F
+
+class cMcliPid:public cListObject
+{
+ private:
+ int m_Pid;
+ int m_Tid;
+
+ public:
+ cMcliPid (int Pid, int Tid)
+ {
+ m_Pid = Pid;
+ m_Tid = Tid;
+ }
+ ~cMcliPid ()
+ {
+ }
+
+ int Tid (void)
+ {
+ return m_Tid;
+ }
+ int Pid (void)
+ {
+ return m_Pid;
+ }
+ void SetTid (int Tid)
+ {
+ m_Tid = Tid;
+ }
+};
+
+// --- cMcliFilter ------------------------------------------------------
+
+class cMcliFilter:public cListObject
+{
+ private:
+ uchar m_Buffer[65536];
+ int m_Used;
+ bool m_closed;
+ int m_Pipe[2];
+ u_short m_Pid;
+ u_char m_Tid;
+ u_char m_Mask;
+
+ public:
+ cMcliFilter (u_short Pid, u_char Tid, u_char Mask);
+ virtual ~ cMcliFilter ();
+
+ bool Matches (u_short Pid, u_char Tid);
+ bool PutSection (const uchar * Data, int Length, bool Pusi);
+ int ReadPipe (void) const
+ {
+ return m_Pipe[0];
+ }
+
+ bool IsClosed (void);
+ void Close (void);
+ void Reset (void);
+
+ u_short Pid (void) const
+ {
+ return m_Pid;
+ }
+ u_char Tid (void) const
+ {
+ return m_Tid;
+ }
+ u_char Mask (void) const
+ {
+ return m_Mask;
+ }
+};
+
+inline bool cMcliFilter::Matches (u_short Pid, u_char Tid)
+{
+// printf("Match: %d == %d m_Tid %d == %d %02x\n", m_Pid, Pid, m_Tid, Tid & m_Mask, m_Mask);
+ return m_Pid == Pid && m_Tid == (Tid & m_Mask);
+}
+
+cMcliFilter::cMcliFilter (u_short Pid, u_char Tid, u_char Mask)
+{
+ m_Used = 0;
+ m_Pid = Pid;
+ m_Tid = Tid;
+ m_Mask = Mask;
+ m_Pipe[0] = m_Pipe[1] = -1;
+ m_closed = false;
+
+#ifdef SOCK_SEQPACKET
+ // SOCK_SEQPACKET (since kernel 2.6.4)
+ if (socketpair (AF_UNIX, SOCK_SEQPACKET, 0, m_Pipe) != 0) {
+ esyslog ("mcli: socketpair(SOCK_SEQPACKET) failed: %m, trying SOCK_DGRAM");
+ }
+#endif
+ if (m_Pipe[0] < 0 && socketpair (AF_UNIX, SOCK_DGRAM, 0, m_Pipe) != 0) {
+ esyslog ("mcli: couldn't open section filter socket: %m");
+ }
+
+ else if (fcntl (m_Pipe[0], F_SETFL, O_NONBLOCK) != 0 || fcntl (m_Pipe[1], F_SETFL, O_NONBLOCK) != 0) {
+ esyslog ("mcli: couldn't set section filter socket to non-blocking mode: %m");
+ }
+}
+
+cMcliFilter::~cMcliFilter ()
+{
+// printf ("~cMcliFilter %p\n", this);
+ Close();
+}
+
+
+bool cMcliFilter::PutSection (const uchar * Data, int Length, bool Pusi)
+{
+
+ if (!m_Used && !Pusi) { /* wait for payload unit start indicator */
+// printf("Mittendrin pid %d tid %d mask %02x \n", Pid(), Tid(), Mask());
+ return true;
+ }
+ if (m_Used && Pusi) { /* reset at payload unit start */
+// int length = (((m_Buffer[1] & 0x0F) << 8) | m_Buffer[2]) + 3;
+// printf("RESET expect %d got %d for pid %d tid %d mask %02x \n",length, m_Used, Pid(), Tid(), Mask());
+ Reset ();
+ }
+ if (m_Used + Length >= (int) sizeof (m_Buffer)) {
+ esyslog ("ERROR: Mcli: Section handler buffer overflow (%d bytes lost)", Length);
+ Reset ();
+ return true;
+ }
+
+ memcpy (m_Buffer + m_Used, Data, Length);
+ m_Used += Length;
+ if (m_Used > 3) {
+ int length = (((m_Buffer[1] & 0x0F) << 8) | m_Buffer[2]) + 3;
+// printf("-> pid %d Tid %d Mask %02x expect %d got %d\n",Pid(), Tid(), Mask(), length, m_Used);
+ if (m_Used >= length) {
+// printf("Section complete\n");
+ m_Used = 0;
+ if (write (m_Pipe[1], m_Buffer, length) < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ //dsyslog ("cMcliFilter::PutSection socket overflow, " "Pid %4d Tid %3d", m_Pid, m_Tid);
+ ;
+ else {
+ m_closed=true;
+ return false;
+ }
+ }
+ }
+
+ if (m_Used > length) {
+ dsyslog ("cMcliFilter::PutSection: m_Used > length ! Pid %2d, Tid%2d " "(len %3d, got %d/%d)", m_Pid, m_Tid, Length, m_Used, length);
+ if (Length < TS_SIZE - 5) {
+ // TS packet not full -> this must be last TS packet of section data -> safe to reset now
+ Reset ();
+ }
+ }
+ }
+ return true;
+}
+
+void cMcliFilter::Reset (void)
+{
+#ifdef DEBUG_FILTER
+ if (m_Used)
+ dsyslog ("cMcliFilter::Reset skipping %d bytes", m_Used);
+#endif
+ m_Used = 0;
+}
+
+bool cMcliFilter::IsClosed (void)
+{
+ char m_Buffer[3] = { 0, 0, 0 }; /* tid 0, 0 bytes */
+ if(m_closed) {
+ return m_closed;
+ }
+
+ // Test if pipe/socket has been closed by writing empty section
+ if ((write (m_Pipe[1], m_Buffer, 3) < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) {
+ if (errno != ECONNREFUSED && errno != ECONNRESET && errno != EPIPE)
+ esyslog ("cMcliFilter::IsClosed failed: %m");
+ m_closed = true;
+ return true;
+ }
+
+ return false;
+}
+
+void cMcliFilter::Close (void)
+{
+ if(m_Pipe[0]>=0) {
+ close (m_Pipe[0]);
+ m_Pipe[0]=-1;
+ }
+ if(m_Pipe[1]>=0) {
+ close (m_Pipe[1]);
+ m_Pipe[1]=-1;
+ }
+ m_closed=true;
+}
+
+// --- cMcliFilters -----------------------------------------------------
+
+cMcliFilters::cMcliFilters (void):
+cThread ("mcli: sections assembler")
+{
+ m_PB = NULL;
+}
+
+cMcliFilters::~cMcliFilters ()
+{
+}
+
+int cMcliFilters::PutTS (const uchar * data, int len)
+{
+ u_short pid = (((u_short) data[1] & PID_MASK_HI) << 8) | data[2];
+ if (m_PB && WantPid (pid)) {
+ int i;
+ for (i = 0; i < len; i += TS_SIZE) {
+ unsigned char *ptr = m_PB->PutStart (TS_SIZE);
+ if (ptr) {
+ memcpy (ptr, data + i, TS_SIZE);
+ m_PB->PutEnd (TS_SIZE, 0, 0);
+ }
+ }
+ }
+ return 0;
+}
+
+int cMcliFilters::CloseFilter (int Handle)
+{
+// printf("cMcliFilters::CloseFilter: %d\n", Handle);
+ GarbageCollect ();
+
+ int pid = GetPid (Handle);
+ if (pid != -1) {
+ m_pl.SetPid (pid, -1);
+ }
+ cMcliFilter *f=GetFilter(Handle);
+ if(f) {
+ LOCK_THREAD;
+ f->Close();
+ Del(f);
+ }
+ return pid;
+}
+
+int cMcliFilters::OpenFilter (u_short Pid, u_char Tid, u_char Mask)
+{
+// printf("cMcliFilters::OpenFilter: %d %d %02x\n", Pid, Tid, Mask);
+ GarbageCollect ();
+
+ if (!WantPid (Pid)) {
+ m_pl.SetPid (Pid, 0xffff);
+ }
+
+ if (!m_PB) {
+ m_PB = new cMyPacketBuffer (10000 * TS_SIZE, 10000);
+ m_PB->SetTimeouts (0, CLOCKS_PER_SEC * 20 / 1000);
+ }
+ Start ();
+
+ cMcliFilter *f = new cMcliFilter (Pid, Tid, Mask);
+ int fh = f->ReadPipe ();
+
+ Lock ();
+ Add (f);
+ Unlock ();
+
+ return fh;
+}
+
+int cMcliPidList::GetTidFromPid (int pid)
+{
+ for (cMcliPid * p = First (); p; p = Next (p)) {
+ if (p->Pid () == pid) {
+// printf("Found pid %d -> tid %d\n",pid, p->Tid());
+ return p->Tid ();
+ }
+ }
+ return -1;
+}
+
+void cMcliPidList::SetPid (int Pid, int Tid)
+{
+ if (Tid >= 0) {
+ for (cMcliPid * p = First (); p; p = Next (p)) {
+ if (p->Pid () == Pid) {
+// printf("Change pid %d -> tid %d\n", Pid, Tid);
+ if (Tid != 0xffff) {
+ p->SetTid (Tid);
+ }
+ return;
+ }
+ }
+ cMcliPid *pid = new cMcliPid (Pid, Tid);
+ Add (pid);
+// printf("Add pid %d -> tid %d\n", Pid, Tid);
+ } else {
+ for (cMcliPid * p = First (); p; p = Next (p)) {
+ if (p->Pid () == Pid) {
+// printf("Del pid %d\n", Pid);
+ Del (p);
+ return;
+ }
+ }
+ }
+}
+
+int cMcliFilters::GetPid (int Handle)
+{
+ int used = 0;
+ int pid = -1;
+
+ LOCK_THREAD;
+ for (cMcliFilter * fi = First (); fi; fi = Next (fi)) {
+ if (fi->ReadPipe () == Handle) {
+ pid = fi->Pid ();
+ break;
+ }
+ }
+ if(pid != -1) {
+ for (cMcliFilter * fi = First (); fi; fi = Next (fi)) {
+ if (pid == fi->Pid ()) {
+ used++;
+ }
+ }
+ }
+// printf("Pid %d used %dx\n", pid, used);
+ if (used==1) {
+ return pid;
+ }
+ return -1;
+}
+
+cMcliFilter *cMcliFilters::GetFilter (int Handle)
+{
+ LOCK_THREAD;
+ for (cMcliFilter * fi = First (); fi; fi = Next (fi)) {
+ if (fi->ReadPipe () == Handle) {
+ return fi;
+ }
+ }
+ return NULL;
+}
+
+bool cMcliFilters::WantPid (int pid)
+{
+ LOCK_THREAD;
+ for (cMcliFilter * fi = First (); fi; fi = Next (fi)) {
+ if (pid == fi->Pid ()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void cMcliFilters::GarbageCollect (void)
+{
+ LOCK_THREAD;
+ for (cMcliFilter * fi = First (); fi;) {
+ if (fi->IsClosed ()) {
+ if (errno == ECONNREFUSED || errno == ECONNRESET || errno == EPIPE) {
+// printf ("cMcliFilters::GarbageCollector: filter closed: Pid %4d, Tid %3d, Mask %2x (%d filters left)\n", (int) fi->Pid (), (int) fi->Tid (), fi->Mask (), Count () - 1);
+
+ cMcliFilter *next = Prev (fi);
+ Del (fi);
+ fi = next ? Next (next) : First ();
+ } else {
+ esyslog ("cMcliFilters::GarbageCollector() error: " "Pid %4d, Tid %3d, Mask %2x (%d filters left) failed", (int) fi->Pid (), (int) fi->Tid (), fi->Mask (), Count () - 1);
+ LOG_ERROR;
+ fi = Next (fi);
+ }
+ } else {
+ fi = Next (fi);
+ }
+ }
+}
+
+void cMcliFilters::Action (void)
+{
+
+ while (Running ()) {
+ m_PB->GetEnd ();
+ int size;
+ const uchar *block = m_PB->GetStart (&size, 0, 0);
+ if (block) {
+ u_short pid = (((u_short) block[1] & PID_MASK_HI) << 8) | block[2];
+ int offset = ((block[3] & 0x20) >> 5) * (block[4] + 1); // offset is the length of the adaption field (see vdr/remux.c).
+ bool Pusi = (block[1] & 0x40) >> 6;
+ int len=188-4-offset; //payload len
+ block=block+4+offset; //point to payload
+ if ( Pusi ) {
+ if ( len>(block[0]) ) { //process last section chunk
+ ProcessChunk(pid,block+1,block[0],0);
+ len -= block[0]+1;
+ block += block[0]+1;
+ } else {
+ len=0; //error!?!
+ }
+ } else {
+ if ( len>0 ) { //process section chunk
+ ProcessChunk(pid,block,len,0);
+ block +=len;
+ len = 0;
+ }
+ }
+ while ( len>0 ) { //process complete section or initial chunk
+ int chunklen= ( ( (block[1]<<8) | block[2] ) & 0xFFF ) + 3 ;
+ if ( chunklen>len ) {
+ chunklen=len;
+ }
+ ProcessChunk(pid,block,chunklen,1);
+ block +=chunklen;
+ len -= chunklen;
+ }
+ }
+ }
+ DELETENULL (m_PB);
+ dsyslog ("McliFilters::Action() ended");
+}
+
+void cMcliFilters::ProcessChunk(u_short pid, const uchar *block, int len, bool Pusi) {
+ int tid = -1;
+ if (Pusi) {
+ tid = (int) block[0];
+ m_pl.SetPid (pid, tid);
+ } else {
+ tid = m_pl.GetTidFromPid (pid);
+ if (tid == -1) {
+ //printf("Failed to get tid for pid %d\n", pid);
+ }
+ }
+ //printf("pid:%d tid:%d Pusi: %d len: %d\n", pid, tid, Pusi, len);
+ LOCK_THREAD;
+ cMcliFilter *f = First ();
+ while (f) {
+ cMcliFilter *next = Next (f);
+ if (tid != -1 && f->Matches (pid, tid)) {
+ //printf("Match!!!!");
+ if (!f->PutSection (block, len, Pusi)) {
+ if (errno != ECONNREFUSED && errno != ECONNRESET && errno != EPIPE) {
+ esyslog ("mcli: couldn't send section packet");
+ }
+ Del (f);
+ // Filter was closed.
+ // - need to check remaining filters for another match
+ } // if
+ }
+ f = next;
+ }
+}