trunk 20080912
[gitenigma.git] / lib / dvb / cahandler.cpp
blob71408c2da3a54a6218e8ee4edfebeb8dc1a54fec
1 #include <unistd.h>
2 #include <string.h>
4 #include <lib/dvb/cahandler.h>
5 #include <lib/base/eerror.h>
6 #include <lib/system/init.h>
7 #include <lib/system/init_num.h>
8 #include <lib/system/info.h>
10 ePMTClient::ePMTClient(eDVBCAHandler *handler, int socket)
11 : eUnixDomainSocket(socket, 1, eApp), parent(handler)
13 CONNECT(connectionClosed_, ePMTClient::connectionLost);
16 void ePMTClient::connectionLost()
18 if (parent) parent->connectionLost(this);
21 eDVBCAHandler::eDVBCAHandler()
22 : eServerSocket(PMT_SERVER_SOCKET, eApp), serviceLeft(eApp)
24 services.setAutoDelete(true);
25 clients.setAutoDelete(true);
26 CONNECT( eDVB::getInstance()->leaveTransponder, eDVBCAHandler::leaveTransponder );
27 CONNECT(serviceLeft.timeout, eDVBCAHandler::serviceGone);
28 eDVBCaPMTClientHandler::registerCaPMTClient(this); // static method...
31 eDVBCAHandler::~eDVBCAHandler()
33 eDVBCaPMTClientHandler::unregisterCaPMTClient(this); // static method...
36 void eDVBCAHandler::newConnection(int socket)
38 ePMTClient *client = new ePMTClient(this, socket);
39 clients.push_back(client);
41 /* inform the new client about our current services, if we have any */
42 distributeCAPMT();
45 void eDVBCAHandler::connectionLost(ePMTClient *client)
47 ePtrList<ePMTClient>::iterator it = std::find(clients.begin(), clients.end(), client);
48 if (it != clients.end())
50 clients.erase(it);
54 void eDVBCAHandler::leaveTransponder( eTransponder* t )
56 if ( t )
58 const char *msg = "\x9f\x80\x3f\x04\x83\x02\x03\x01";
60 /* send msg to the listening client */
61 eUnixDomainSocket socket(eApp);
62 socket.connectToPath(PMT_CLIENT_SOCKET);
63 if (socket.state() == eSocket::Connection) socket.writeBlock(msg, strlen(msg));
67 void eDVBCAHandler::enterService( const eServiceReferenceDVB &service )
69 ePtrList<CAService>::iterator it =
70 std::find(services.begin(), services.end(), service );
71 if ( it == services.end() )
73 serviceLeft.stop();
74 services.push_back(new CAService( service ));
78 * our servicelist has changed, but we have to wait till we receive PMT data
79 * for this service, before we distribute a new list of CAPMT objects to our clients.
83 void eDVBCAHandler::leaveService( const eServiceReferenceDVB &service )
85 ePtrList<CAService>::iterator it =
86 std::find(services.begin(), services.end(), service );
87 if ( it != services.end() )
89 serviceLeft.startLongTimer(2);
90 services.erase(it);
93 /* our servicelist has changed, distribute the list of CAPMT objects to all our clients */
94 distributeCAPMT();
97 void eDVBCAHandler::serviceGone()
99 if (!services.size())
101 clients.clear();
105 void eDVBCAHandler::distributeCAPMT()
108 * write the list of CAPMT objects to each connected client, if it's not empty
110 if (services.empty()) return;
112 ePtrList<ePMTClient>::iterator client_it = clients.begin();
113 for ( ; client_it != clients.end(); ++client_it)
115 if (client_it->state() == eSocket::Connection)
117 unsigned char list_management = LIST_FIRST;
118 ePtrList<CAService>::iterator it = services.begin();
119 for ( ; it != services.end(); )
121 CAService *current = it;
122 ++it;
123 if (it == services.end()) list_management |= LIST_LAST;
124 current->writeCAPMTObject(*client_it, list_management);
125 list_management = LIST_MORE;
131 void eDVBCAHandler::handlePMT( const eServiceReferenceDVB &service, PMT *pmt )
133 ePtrList<CAService>::iterator it = std::find(services.begin(), services.end(), service);
134 if (it != services.end())
136 /* we found the service in our list */
137 if (it->getCAPMTVersion() == pmt->version)
139 eDebug("[eDVBCAHandler] dont send the self pmt version");
140 return;
143 bool isUpdate = (it->getCAPMTVersion() >= 0);
145 /* prepare the data */
146 it->buildCAPMT(pmt);
148 /* send the data to the listening client */
149 it->sendCAPMT();
151 if (isUpdate)
153 /* this is a PMT update, we should distribute the new CAPMT object to all our connected clients */
154 ePtrList<ePMTClient>::iterator client_it = clients.begin();
155 for ( ; client_it != clients.end(); ++client_it)
157 if (client_it->state() == eSocket::Connection)
159 it->writeCAPMTObject(*client_it, LIST_UPDATE);
163 else
166 * this is PMT information for a new service, so we can now distribute
167 * the CAPMT objects to all our connected clients
169 distributeCAPMT();
174 CAService::CAService( const eServiceReferenceDVB &service )
175 : eUnixDomainSocket(eApp), lastPMTVersion(-1), me(service), capmt(NULL), retry(eApp)
177 int socketReconnect = 0;
178 eConfig::getInstance()->getKey("/elitedvb/extra/cahandlerReconnect", socketReconnect);
179 if (socketReconnect)
181 CONNECT(connectionClosed_, CAService::connectionLost);
183 CONNECT(retry.timeout, CAService::sendCAPMT);
184 // eDebug("[eDVBCAHandler] new service %s", service.toString().c_str() );
187 void CAService::connectionLost()
189 /* reconnect in 1s */
190 retry.startLongTimer(1);
193 void CAService::buildCAPMT( PMT *pmt )
195 if ( !capmt )
196 capmt = new unsigned char[1024];
198 memcpy(capmt,"\x9f\x80\x32\x82\x00\x00", 6);
200 capmt[6]=lastPMTVersion==-1 ? LIST_ONLY : LIST_UPDATE;
201 capmt[7]=(unsigned char)((pmt->program_number>>8) & 0xff); //prg-nr
202 capmt[8]=(unsigned char)(pmt->program_number & 0xff); //prg-nr
204 capmt[9]=pmt->version; //reserved - version - current/next
205 capmt[10]=0x00; //reserved - prg-info len
206 capmt[11]=0x00; //prg-info len
208 capmt[12]=CMD_OK_DESCRAMBLING; // ca pmt command id
209 capmt[13]=0x81; // private descr.. dvbnamespace
210 capmt[14]=0x08;
211 capmt[15]=me.getDVBNamespace().get()>>24;
212 capmt[16]=(me.getDVBNamespace().get()>>16)&0xFF;
213 capmt[17]=(me.getDVBNamespace().get()>>8)&0xFF;
214 capmt[18]=me.getDVBNamespace().get()&0xFF;
215 capmt[19]=me.getTransportStreamID().get()>>8;
216 capmt[20]=me.getTransportStreamID().get()&0xFF;
217 capmt[21]=me.getOriginalNetworkID().get()>>8;
218 capmt[22]=me.getOriginalNetworkID().get()&0xFF;
220 capmt[23]=0x82; // demuxer kram..
221 capmt[24]=0x02;
223 switch(eSystemInfo::getInstance()->getHwType())
225 case eSystemInfo::DM7000:
226 case eSystemInfo::DM7020:
227 capmt[25]=0x03; // descramble on demux0 and demux1
228 capmt[26]=0x01; // get section data from demux1
229 break;
230 case eSystemInfo::DM500PLUS:
231 case eSystemInfo::DM600PVR:
232 capmt[25]=0x01; // descramble on demux0 // demux 1 is just a fake demux 0
233 capmt[26]=0x01; // get section data from demux1
234 break;
235 default:
236 capmt[25]=0x01; // only descramble on demux0
237 capmt[26]=0x00; // get section data from demux0
238 break;
241 capmt[27]=0x84; // pmt pid
242 capmt[28]=0x02;
243 capmt[29]=pmt->pid>>8;
244 capmt[30]=pmt->pid&0xFF;
246 lastPMTVersion=pmt->version;
247 int lenpos=10;
248 int len=19;
249 int first=0;
250 int wp=31;
252 // program_info
253 for (ePtrList<Descriptor>::const_iterator i(pmt->program_info);
254 i != pmt->program_info.end(); ++i)
256 if (i->Tag()==9) // CADescriptor
258 CADescriptor *ca=(CADescriptor*)*i;
259 memcpy(capmt+wp, ca->data, ca->data[1]+2);
260 wp+=ca->data[1]+2;
261 len+=ca->data[1]+2;
265 for (ePtrList<PMTEntry>::iterator i(pmt->streams); i != pmt->streams.end(); ++i)
267 PMTEntry *pe=*i;
269 first=1;
270 capmt[lenpos]=((len & 0xf00)>>8);
271 capmt[lenpos+1]=(len & 0xff);
272 len=0;
273 lenpos=wp+3;
274 first=1;
275 capmt[wp++]=(pe->stream_type & 0xffff);
276 capmt[wp++]=((pe->elementary_PID >> 8) & 0xff);
277 capmt[wp++]=(pe->elementary_PID & 0xff);
278 wp+=2;
280 switch (pe->stream_type)
282 case 1: // ISO/IEC 11172 Video
283 case 2: // ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream
284 case 3: // ISO/IEC 11172 Audio
285 case 4: // ISO/IEC 13818-3 Audio
286 case 6: // private stream ( ttx or AC3 or DTS )
287 for (ePtrList<Descriptor>::const_iterator i(pe->ES_info);
288 i != pe->ES_info.end(); ++i)
290 if (i->Tag()==9) // CADescriptor
292 CADescriptor *ca=(CADescriptor*)*i;
293 if(first)
295 first=0;
296 capmt[wp++]=0x01; //ca_pmt_command_id
297 len++;
299 memcpy(capmt+wp, ca->data, ca->data[1]+2);
300 wp+=ca->data[1]+2;
301 len+=ca->data[1]+2;
304 default:
305 break;
308 capmt[lenpos]=((len & 0xf00)>>8);
309 capmt[lenpos+1]=(len & 0xff);
311 capmt[4]=((wp-6)>>8) & 0xff;
312 capmt[5]=(wp-6) & 0xff;
315 void CAService::sendCAPMT()
317 if (state() == Idle || state() == Invalid)
319 /* we're not connected yet */
320 connectToPath(PMT_CLIENT_SOCKET);
323 if (state() == Connection)
326 * Send the CAPMT object which we just constructed, with unmodified list_management field.
327 * This should work in case of a new service, as well as for an updated service.
329 writeCAPMTObject(this, -1);
331 else
333 /* we're not connected, try again in 5s */
334 retry.startLongTimer(5);
338 int CAService::writeCAPMTObject(eSocket *socket, int list_management)
340 int wp;
342 if (!capmt) return 0;
344 if (list_management >= 0) capmt[6] = (unsigned char)list_management;
346 wp = capmt[4] << 8;
347 wp |= capmt[5];
348 wp += 6;
350 return socket->writeBlock((const char*)capmt, wp);
353 eAutoInitP0<eDVBCAHandler> init_eDVBCAHandler(eAutoInitNumbers::osd-2, "eDVBCAHandler");