diff options
author | Lars Heer <l.heer@gmx.de> | 2013-09-18 05:50:03 +0200 |
---|---|---|
committer | Lars Heer <l.heer@gmx.de> | 2013-09-18 05:50:03 +0200 |
commit | ccf6e0f9c6b0481ed13e0f4794e3fbead750f385 (patch) | |
tree | ed86efb54f7ee41edfba5c89ca519b5fd10aa0d5 /filter.c | |
download | vdr-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.c | 463 |
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; + } +} |