📄 cam.c
字号:
/* * Softcam plugin to VDR (C++) * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Or, point your browser to http://www.gnu.org/copyleft/gpl.html */#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <errno.h>#include <sys/ioctl.h>#include <sys/time.h>#include <dlfcn.h>#include <linux/dvb/ca.h>#include <vdr/ci.h>#include <vdr/dvbdevice.h>#if APIVERSNUM >= 10500#include <vdr/dvbci.h>#endif#include <vdr/channels.h>#include <vdr/thread.h>#include "FFdecsa/FFdecsa.h"#include "cam.h"#include "scsetup.h"#include "filter.h"#include "system.h"#include "data.h"#include "misc.h"#include "log-core.h"#define IDLE_SLEEP 0 // idleTime when sleeping#define IDLE_GETCA 200 // idleTime when waiting for ca descriptors#define IDLE_GETCA_SLOW 20000 // idleTime if no enc. system#define IDLE_NO_SYNC 800 // idleTime when not in sync#define IDLE_SYNC 2000 // idleTime when in sync#define CW_REPEAT_TIME 2000 // rewrite CW after X ms#define LOG_COUNT 3 // stop logging after X complete ECM cycles#define CHAIN_HOLD 120000 // min. time to hold a logger chain#define ECM_DATA_TIME 6000 // time to wait for ECM data updates#define ECM_UPD_TIME 120000 // delay between ECM data updates#define MAX_ECM_IDLE 300000 // delay before an idle handler can be removed#define MAX_ECM_HOLD 15000 // delay before an idle handler stops processing#define CAID_TIME 300000 // time between caid scans#define L_HEX 2#define L_HEX_ECM LCLASS(L_HEX,2)#define L_HEX_EMM LCLASS(L_HEX,4)#define L_HEX_CAT LCLASS(L_HEX,8)#define L_HEX_PMT LCLASS(L_HEX,16)#define L_HEX_HOOK LCLASS(L_HEX,32)#define L_HEX_ALL LALL(L_HEX_HOOK)static const struct LogModule lm_hex = { (LMOD_ENABLE|L_HEX_ALL)&LOPT_MASK, (LMOD_ENABLE)&LOPT_MASK, "hexdata", { "ecm","emm","cat","pmt","hook" } };ADD_MODULE(L_HEX,lm_hex)static const char *typeNames[] = { "typ0","typ1","VIDEO","typ3","AUDIO","typ5","DOLBY","typ6+" };#define TYPENAME(type) (typeNames[(type)<=7?(type):7])// -- cLogStats ---------------------------------------------------------------#define COUNTS 20#define SAMPLE (30*1000)#define AVR1 (60*1000)#define AVR2 (4*60*1000)#define AVR3 (10*60*1000)#define REPORT (60*1000)class cLogStats : public cThread {private: cTimeMs sTime, repTime; int sCount, sIdx, sCounts[COUNTS][2];protected: virtual void Action(void);public: cLogStats(void); ~cLogStats(); void Count(void); };static cMutex logstatsMutex;static cLogStats *logstats=0;void LogStatsUp(void){ logstatsMutex.Lock(); if(LOG(L_CORE_AUSTATS) && !logstats) logstats=new cLogStats; logstatsMutex.Unlock();}void LogStatsDown(void){ logstatsMutex.Lock(); if(logstats) { delete logstats; logstats=0; } logstatsMutex.Unlock();}cLogStats::cLogStats(void){ sCount=sIdx=0; for(int i=0; i<COUNTS; i++) { sCounts[i][0]=0; sCounts[i][1]=SAMPLE; } SetDescription("logger stats"); Start();}cLogStats::~cLogStats(){ Cancel(2);}void cLogStats::Count(void){ sCount++;}void cLogStats::Action(void){ while(Running()) { cCondWait::SleepMs(50); if(sTime.Elapsed()>SAMPLE) { sCounts[sIdx][0]=sCount; sCount=0; sCounts[sIdx][1]=sTime.Elapsed(); sTime.Set(); if(++sIdx >= COUNTS) sIdx=0; } if(repTime.Elapsed()>REPORT) { repTime.Set(); if(sCounts[(sIdx+COUNTS-1)%COUNTS][0]>0) { LBSTART(L_CORE_AUSTATS); LBPUT("EMM packet load average (%d/%d/%dmin)",AVR1/60000,AVR2/60000,AVR3/60000); int s=0, t=0; for(int i=1; i<=COUNTS; i++) { s+=sCounts[(sIdx+COUNTS-i)%COUNTS][0]; t+=sCounts[(sIdx+COUNTS-i)%COUNTS][1]; if(i==(AVR1/SAMPLE) || i==(AVR2/SAMPLE) || i==(AVR3/SAMPLE)) LBPUT(" %4d",(int)((float)s/(float)t*1000.0)); } LBPUT(" pks/s"); LBEND(); } } }}// -- cHookManager -------------------------------------------------------------class cHookManager : public cAction { int cardNum; cSimpleList<cLogHook> hooks; // cPidFilter *AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc); void ClearHooks(void); void DelHook(cLogHook *hook);protected: virtual void Process(cPidFilter *filter, unsigned char *data, int len);public: cHookManager(int CardNum); virtual ~cHookManager(); void AddHook(cLogHook *hook); bool TriggerHook(int id); void Down(void); };cHookManager::cHookManager(int CardNum):cAction("hookmanager",CardNum){ cardNum=CardNum; Priority(10);}cHookManager::~cHookManager(){ Down();}void cHookManager::Down(void){ Lock(); while(cLogHook *hook=hooks.First()) DelHook(hook); DelAllFilter(); Unlock();}bool cHookManager::TriggerHook(int id){ Lock(); for(cLogHook *hook=hooks.First(); hook; hook=hooks.Next(hook)) if(hook->id==id) { hook->delay.Set(CHAIN_HOLD); Unlock(); return true; } Unlock(); return false;}void cHookManager::AddHook(cLogHook *hook){ Lock(); PRINTF(L_CORE_HOOK,"%d: starting hook '%s' (%04x)",cardNum,hook->name,hook->id); hook->delay.Set(CHAIN_HOLD); hook->cardNum=cardNum; hooks.Add(hook); for(cPid *pid=hook->pids.First(); pid; pid=hook->pids.Next(pid)) { cPidFilter *filter=AddFilter(pid->pid,pid->sct,pid->mask,pid->mode,CHAIN_HOLD/8,false); if(filter) { filter->userData=(void *)hook; pid->filter=filter; } } Unlock();}void cHookManager::DelHook(cLogHook *hook){ PRINTF(L_CORE_HOOK,"%d: stopping hook '%s' (%04x)",cardNum,hook->name,hook->id); for(cPid *pid=hook->pids.First(); pid; pid=hook->pids.Next(pid)) { cPidFilter *filter=pid->filter; if(filter) { DelFilter(filter); pid->filter=0; } } hooks.Del(hook);}cPidFilter *cHookManager::AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc){ cPidFilter *filter=NewFilter(IdleTime); if(filter) { filter->SetBuffSize(32768); filter->Start(Pid,Section,Mask,Mode,Crc); PRINTF(L_CORE_HOOK,"%d: added filter pid=0x%.4x sct=0x%.2x/0x%.2x/0x%.2x idle=%d crc=%d",cardNum,Pid,Section,Mask,Mode,IdleTime,Crc); } else PRINTF(L_GEN_ERROR,"no free slot or filter failed to open for hookmanager %d",cardNum); return filter;}void cHookManager::Process(cPidFilter *filter, unsigned char *data, int len){ if(data && len>0) { HEXDUMP(L_HEX_HOOK,data,len,"HOOK pid 0x%04x",filter->Pid()); if(SCT_LEN(data)==len) { cLogHook *hook=(cLogHook *)(filter->userData); if(hook) { hook->Process(filter->Pid(),data); if(hook->bailOut || hook->delay.TimedOut()) DelHook(hook); } } else PRINTF(L_CORE_HOOK,"%d: incomplete section %d != %d",cardNum,len,SCT_LEN(data)); } else { cLogHook *hook=(cLogHook *)(filter->userData); if(hook && (hook->bailOut || hook->delay.TimedOut())) DelHook(hook); }}// -- cLogChain ----------------------------------------------------------------class cLogChain : public cSimpleItem {public: int cardNum, caid; bool softCSA, active, delayed; cTimeMs delay; cPids pids; cSimpleList<cSystem> systems; // cLogChain(int CardNum, bool soft); void Process(int pid, unsigned char *data); bool Parse(const unsigned char *cat); };cLogChain::cLogChain(int CardNum, bool soft){ cardNum=CardNum; softCSA=soft; active=delayed=false;}void cLogChain::Process(int pid, unsigned char *data){ if(active) { for(cSystem *sys=systems.First(); sys; sys=systems.Next(sys)) sys->ProcessEMM(pid,caid,data); }}bool cLogChain::Parse(const unsigned char *cat){ if(cat[0]==0x09) { caid=WORD(cat,2,0xFFFF); LBSTARTF(L_CORE_AU); LBPUT("%d: chain caid %04x",cardNum,caid); cSystem *sys; if(systems.Count()>0) { LBPUT(" ++"); for(sys=systems.First(); sys; sys=systems.Next(sys)) sys->ParseCAT(&pids,cat); } else { LBPUT(" ->"); int Pri=0; while((sys=cSystems::FindBySysId(caid,!softCSA,Pri))) { Pri=sys->Pri(); if(sys->HasLogger()) { sys->CardNum(cardNum); sys->ParseCAT(&pids,cat); systems.Add(sys); LBPUT(" %s(%d)",sys->Name(),sys->Pri()); } else delete sys; } } if(systems.Count()==0) LBPUT(" none available"); for(cPid *pid=pids.First(); pid; pid=pids.Next(pid)) LBPUT(" [%04x-%02x/%02x/%02x]",pid->pid,pid->sct,pid->mask,pid->mode); LBEND(); if(systems.Count()>0 && pids.Count()>0) return true; } return false;}// -- cLogger ------------------------------------------------------------------class cLogger : public cAction {private: int cardNum; bool softCSA, up; cSimpleList<cLogChain> chains; cSimpleList<cEcmInfo> active; // cPidFilter *catfilt; int catVers; // cPidFilter *AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc); void SetChains(void); void ClearChains(void); void StartChain(cLogChain *chain); void StopChain(cLogChain *chain, bool force);protected: virtual void Process(cPidFilter *filter, unsigned char *data, int len);public: cLogger(int CardNum, bool soft); virtual ~cLogger(); void EcmStatus(const cEcmInfo *ecm, bool on); void Up(void); void Down(void); };cLogger::cLogger(int CardNum, bool soft):cAction("logger",CardNum){ cardNum=CardNum; softCSA=soft; catfilt=0; up=false; Priority(10);}cLogger::~cLogger(){ Down();}void cLogger::Up(void){ Lock(); if(!up) { PRINTF(L_CORE_AUEXTRA,"%d: UP",cardNum); catVers=-1; catfilt=AddFilter(1,0x01,0xFF,0,0,true); up=true; } Unlock();}void cLogger::Down(void){ Lock(); if(up) { PRINTF(L_CORE_AUEXTRA,"%d: DOWN",cardNum); ClearChains(); DelAllFilter(); catfilt=0; up=false; } Unlock();}void cLogger::EcmStatus(const cEcmInfo *ecm, bool on){ Lock(); PRINTF(L_CORE_AUEXTRA,"%d: ecm prgid=%d caid=%04x prov=%.4x %s",cardNum,ecm->prgId,ecm->caId,ecm->provId,on ? "active":"inactive"); cEcmInfo *e; if(on) { e=new cEcmInfo(ecm); active.Add(e); if(!up) Up(); } else { for(e=active.First(); e; e=active.Next(e)) if(e->Compare(ecm)) { active.Del(e); break; } } SetChains(); Unlock();}void cLogger::SetChains(void){ for(cLogChain *chain=chains.First(); chain; chain=chains.Next(chain)) { bool act=false; if(ScSetup.AutoUpdate>1) act=true; else if(ScSetup.AutoUpdate==1) { for(cEcmInfo *e=active.First(); e; e=active.Next(e)) if((e->emmCaId && chain->caid==e->emmCaId) || chain->caid==e->caId) { act=true; break; } } if(act) StartChain(chain); else StopChain(chain,false); }}void cLogger::ClearChains(void){ for(cLogChain *chain=chains.First(); chain; chain=chains.Next(chain)) StopChain(chain,true); chains.Clear();}void cLogger::StartChain(cLogChain *chain){ if(chain->delayed) PRINTF(L_CORE_AUEXTRA,"%d: restarting delayed chain %04x",cardNum,chain->caid); chain->delayed=false; if(!chain->active) { PRINTF(L_CORE_AU,"%d: starting chain %04x",cardNum,chain->caid); chain->active=true; for(cPid *pid=chain->pids.First(); pid; pid=chain->pids.Next(pid)) { cPidFilter *filter=AddFilter(pid->pid,pid->sct,pid->mask,pid->mode,CHAIN_HOLD/8,false); if(filter) { filter->userData=(void *)chain; pid->filter=filter; } } }}void cLogger::StopChain(cLogChain *chain, bool force){ if(chain->active) { if(force || (chain->delayed && chain->delay.TimedOut())) { PRINTF(L_CORE_AU,"%d: stopping chain %04x",cardNum,chain->caid); chain->active=false; for(cPid *pid=chain->pids.First(); pid; pid=chain->pids.Next(pid)) { cPidFilter *filter=pid->filter; if(filter) { DelFilter(filter); pid->filter=0; } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -