add comment for python-lists
[enigma2.git] / lib / dvb / scan.cpp
1 #include <lib/dvb/idvb.h>
2 #include <dvbsi++/service_description_section.h>
3 #include <dvbsi++/network_information_section.h>
4 #include <dvbsi++/bouquet_association_section.h>
5 #include <dvbsi++/descriptor_tag.h>
6 #include <dvbsi++/service_descriptor.h>
7 #include <dvbsi++/satellite_delivery_system_descriptor.h>
8 #include <dvbsi++/ca_identifier_descriptor.h>
9 #include <lib/dvb/specs.h>
10 #include <lib/dvb/esection.h>
11 #include <lib/dvb/scan.h>
12 #include <lib/dvb/frontend.h>
13 #include <lib/base/eerror.h>
14 #include <lib/base/estring.h>
15 #include <errno.h>
16
17 #define SCAN_eDebug(x...) eDebug(x)
18 #define SCAN_eDebugNoNewLine(x...) eDebugNoNewLine(x)
19
20 DEFINE_REF(eDVBScan);
21
22 eDVBScan::eDVBScan(iDVBChannel *channel): m_channel(channel)
23 {
24         if (m_channel->getDemux(m_demux))
25                 SCAN_eDebug("scan: failed to allocate demux!");
26         m_channel->connectStateChange(slot(*this, &eDVBScan::stateChange), m_stateChanged_connection);
27 }
28
29 eDVBScan::~eDVBScan()
30 {
31 }
32
33 int eDVBScan::isValidONIDTSID(int orbital_position, eOriginalNetworkID onid, eTransportStreamID tsid)
34 {
35         switch (onid.get())
36         {
37         case 0:
38         case 0x1111:
39                 return 0;
40         case 1:
41                 return orbital_position == 192;
42         case 0x00B1:
43                 return tsid != 0x00B0;
44         case 0x0002:
45                 return abs(orbital_position-282) < 6;
46         default:
47                 return onid.get() < 0xFF00;
48         }
49 }
50
51 eDVBNamespace eDVBScan::buildNamespace(eOriginalNetworkID onid, eTransportStreamID tsid, unsigned long hash)
52 {
53                 // on valid ONIDs, ignore frequency ("sub network") part
54         if (isValidONIDTSID((hash >> 16) & 0xFFFF, onid, tsid))
55                 hash &= ~0xFFFF;
56         return eDVBNamespace(hash);
57 }
58
59 void eDVBScan::stateChange(iDVBChannel *ch)
60 {
61         int state;
62         if (ch->getState(state))
63                 return;
64         if (m_channel_state == state)
65                 return;
66         
67         if (state == iDVBChannel::state_ok)
68         {
69                 startFilter();
70                 m_channel_state = state;
71         } else if (state == iDVBChannel::state_failed)
72         {
73                 m_ch_unavailable.push_back(m_ch_current);
74                 nextChannel();
75         }
76                         /* unavailable will timeout, anyway. */
77 }
78
79 RESULT eDVBScan::nextChannel()
80 {
81         ePtr<iDVBFrontend> fe;
82
83         m_SDT = 0; m_BAT = 0; m_NIT = 0;
84
85         m_ready = 0;
86         
87                 /* check what we need */
88         m_ready_all = readySDT;
89         
90         if (m_flags & scanNetworkSearch)
91                 m_ready_all |= readyNIT;
92         
93         if (m_flags & scanSearchBAT)
94                 m_ready_all |= readyBAT;
95         
96         if (m_ch_toScan.empty())
97         {
98                 eDebug("no channels left to scan.");
99                 eDebug("%d channels scanned, %d were unavailable.", 
100                                 m_ch_scanned.size(), m_ch_unavailable.size());
101                 eDebug("%d channels in database.", m_new_channels.size());
102                 m_event(evtFinish);
103                 return -ENOENT;
104         }
105         
106         m_ch_current = m_ch_toScan.front();
107         m_chid_current = eDVBChannelID();
108         
109         m_ch_toScan.pop_front();
110         
111         if (m_channel->getFrontend(fe))
112         {
113                 m_event(evtFail);
114                 return -ENOTSUP;
115         }
116         
117         m_channel_state = iDVBChannel::state_idle;
118         if (fe->tune(*m_ch_current))
119         {
120                 return nextChannel();
121                 m_event(evtFail);
122                 return -EINVAL;
123         }
124                 
125         m_event(evtUpdate);
126         return 0;
127 }
128
129 RESULT eDVBScan::startFilter()
130 {
131         assert(m_demux);
132         
133                         /* only start required filters filter */
134         
135         m_SDT = 0;
136
137         if (m_ready_all & readySDT)
138         {
139                 m_SDT = new eTable<ServiceDescriptionSection>();
140                 if (m_SDT->start(m_demux, eDVBSDTSpec()))
141                         return -1;
142                 CONNECT(m_SDT->tableReady, eDVBScan::SDTready);
143         }
144
145         m_NIT = 0;
146         if (m_ready_all & readyNIT)
147         {
148                 m_NIT = new eTable<NetworkInformationSection>();
149                 if (m_NIT->start(m_demux, eDVBNITSpec()))
150                         return -1;
151                 CONNECT(m_NIT->tableReady, eDVBScan::NITready);
152         }
153
154         m_BAT = 0;
155         if (m_ready_all & readyBAT)
156         {
157                 m_BAT = new eTable<BouquetAssociationSection>();
158                 if (m_BAT->start(m_demux, eDVBBATSpec()))
159                         return -1;
160                 CONNECT(m_BAT->tableReady, eDVBScan::BATready);
161         }
162         
163         return 0;
164 }
165
166 void eDVBScan::SDTready(int err)
167 {
168         SCAN_eDebug("got sdt");
169         m_ready |= readySDT;
170         if (!err)
171                 m_ready |= validSDT;
172         channelDone();
173 }
174
175 void eDVBScan::NITready(int err)
176 {
177         SCAN_eDebug("got nit, err %d", err);
178         m_ready |= readyNIT;
179         if (!err)
180                 m_ready |= validNIT;
181         channelDone();
182 }
183
184 void eDVBScan::BATready(int err)
185 {
186         SCAN_eDebug("got bat");
187         m_ready |= readyBAT;
188         if (!err)
189                 m_ready |= validBAT;
190         channelDone();
191 }
192
193 void eDVBScan::addKnownGoodChannel(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
194 {
195                 /* add it to the list of known channels. */
196         if (chid)
197                 m_new_channels.insert(std::pair<eDVBChannelID,ePtr<iDVBFrontendParameters> >(chid, feparm));
198 }
199
200 void eDVBScan::addChannelToScan(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
201 {
202                 /* check if we don't already have that channel ... */
203                 
204                 /* ... in the list of channels to scan */
205         for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_toScan.begin()); i != m_ch_toScan.end(); ++i)
206                 if (sameChannel(*i, feparm))
207                         return;
208
209                 /* ... in the list of successfully scanned channels */
210         for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_scanned.begin()); i != m_ch_scanned.end(); ++i)
211                 if (sameChannel(*i, feparm))
212                         return;
213                 
214                 /* ... in the list of unavailable channels */
215         for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_unavailable.begin()); i != m_ch_unavailable.end(); ++i)
216                 if (sameChannel(*i, feparm))
217                         return;
218
219                 /* ... on the current channel */
220         if (sameChannel(m_ch_current, feparm))
221                 return;
222
223                 /* otherwise, add it to the todo list. */
224         m_ch_toScan.push_back(feparm);
225 }
226
227 int eDVBScan::sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *ch2) const
228 {
229         int diff;
230         if (ch1->calculateDifference(ch2, diff))
231                 return 0;
232         if (diff < 4000) // more than 4mhz difference?
233                 return 1;
234         return 0;
235 }
236
237 void eDVBScan::channelDone()
238 {
239         if (m_ready & validSDT)
240         {
241                 unsigned long hash = 0;
242                 m_ch_current->getHash(hash);
243                 
244                 eDVBNamespace dvbnamespace = buildNamespace(
245                         (**m_SDT->getSections().begin()).getOriginalNetworkId(),
246                         (**m_SDT->getSections().begin()).getTransportStreamId(),
247                         hash);
248                 
249                 SCAN_eDebug("SDT: ");
250                 std::vector<ServiceDescriptionSection*>::const_iterator i;
251                 for (i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i)
252                         processSDT(dvbnamespace, **i);
253                 m_ready &= ~validSDT;
254         }
255         
256         if (m_ready & validNIT)
257         {
258                 SCAN_eDebug("dumping NIT");
259                 std::vector<NetworkInformationSection*>::const_iterator i;
260                 for (i = m_NIT->getSections().begin(); i != m_NIT->getSections().end(); ++i)
261                 {
262                         const TransportStreamInfoList &tsinfovec = *(*i)->getTsInfo();
263                         
264                         for (TransportStreamInfoConstIterator tsinfo(tsinfovec.begin()); 
265                                 tsinfo != tsinfovec.end(); ++tsinfo)
266                         {
267                                 SCAN_eDebug("TSID: %04x ONID: %04x", (*tsinfo)->getTransportStreamId(),
268                                         (*tsinfo)->getOriginalNetworkId());
269                                 
270                                 eOriginalNetworkID onid = (*tsinfo)->getOriginalNetworkId();
271                                 eTransportStreamID tsid = (*tsinfo)->getTransportStreamId();
272                                 
273                                 for (DescriptorConstIterator desc = (*tsinfo)->getDescriptors()->begin();
274                                                 desc != (*tsinfo)->getDescriptors()->end(); ++desc)
275                                 {
276                                         switch ((*desc)->getTag())
277                                         {
278 //                                      case SERVICE_LIST_DESCRIPTOR:
279                                         case SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR:
280                                         {
281                                                 SatelliteDeliverySystemDescriptor &d = (SatelliteDeliverySystemDescriptor&)**desc;
282                                                 SCAN_eDebug("%d kHz, %d%d%d.%d%c %s MOD:%d %d symb/s, fec %d", 
283                                                                 d.getFrequency(), 
284                                                                 (d.getOrbitalPosition()>>12)&0xF,
285                                                                 (d.getOrbitalPosition()>>8)&0xF,
286                                                                 (d.getOrbitalPosition()>>4)&0xF,
287                                                                 d.getOrbitalPosition()&0xF, d.getWestEastFlag()?'E':'W',
288                                                                 d.getPolarization() ? "hor" : "vert",
289                                                                 d.getModulation(), d.getSymbolRate(), d.getFecInner());
290                                                 
291                                                         /* some sanity checking: below 100MHz is invalid */
292                                                 if (d.getFrequency() < 10000)
293                                                         break;
294                                                 
295                                                 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
296                                                 eDVBFrontendParametersSatellite sat;
297                                                 sat.set(d);
298                                                 feparm->setDVBS(sat);
299                                                 unsigned long hash=0;
300                                                 feparm->getHash(hash);
301                                                 
302                                                 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
303                                                 
304                                                 if (m_chid_current && ((ns.get() ^ m_chid_current.dvbnamespace.get()) & 0xFFFF0000))
305                                                         eDebug("dropping this transponder, it's on another satellite.");
306                                                 else
307                                                 {
308                                                         addChannelToScan(
309                                                                         eDVBChannelID(ns, tsid, onid),
310                                                                         feparm);
311                                                 }
312                                                 break;
313                                         }
314                                         default:
315                                                 SCAN_eDebug("descr<%x>", (*desc)->getTag());
316                                                 break;
317                                         }
318                                 }
319                                 
320                         }
321                 }
322                 m_ready &= ~validNIT;
323         }
324         
325         if ((m_ready  & m_ready_all) != m_ready_all)
326                 return;
327         SCAN_eDebug("channel done!");
328         
329                 /* if we had services on this channel, we declare
330                    this channels as "known good". add it.
331                    
332                    (TODO: not yet implemented)
333                    a NIT entry could have possible overridden
334                    our frontend data with more exact data.
335                    
336                    (TODO: not yet implemented)
337                    the tuning process could have lead to more
338                    exact data than the user entered.
339                    
340                    The channel id was probably corrected
341                    by the data written in the SDT. this is
342                    important, as "initial transponder lists"
343                    usually don't have valid CHIDs (and that's
344                    good).
345                    
346                    These are the reasons for adding the transponder
347                    here, and not before.
348                 */
349         
350         if (!m_chid_current)
351                 eWarning("SCAN: the current channel's ID was not corrected - not adding channel.");
352         else
353                 addKnownGoodChannel(m_chid_current, m_ch_current);
354         
355         m_ch_scanned.push_back(m_ch_current);
356         nextChannel();
357 }
358
359 void eDVBScan::start(const eSmartPtrList<iDVBFrontendParameters> &known_transponders, int flags)
360 {
361         m_flags = flags;
362         m_ch_toScan.clear();
363         m_ch_scanned.clear();
364         m_ch_unavailable.clear();
365         m_new_channels.clear();
366         m_new_services.clear();
367         m_ch_toScan.insert(m_ch_toScan.end(), known_transponders.begin(), known_transponders.end());
368         nextChannel();
369 }
370
371 void eDVBScan::insertInto(iDVBChannelList *db)
372 {
373         for (std::map<eDVBChannelID, ePtr<iDVBFrontendParameters> >::const_iterator 
374                         ch(m_new_channels.begin()); ch != m_new_channels.end(); ++ch)
375                 db->addChannelToList(ch->first, ch->second);
376         for (std::map<eServiceReferenceDVB, ePtr<eDVBService> >::const_iterator
377                 service(m_new_services.begin()); service != m_new_services.end(); ++service)
378         {
379                 ePtr<eDVBService> dvb_service;
380                 if (!db->getService(service->first, dvb_service))
381                         *dvb_service = *service->second;
382                 else
383                         db->addService(service->first, service->second);
384         }
385 }
386
387 RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescriptionSection &sdt)
388 {
389         const ServiceDescriptionList &services = *sdt.getDescriptions();
390         SCAN_eDebug("ONID: %04x", sdt.getOriginalNetworkId());
391         eDVBChannelID chid(dvbnamespace, sdt.getTransportStreamId(), sdt.getOriginalNetworkId());
392         
393                 /* save correct CHID for this channel if this is an ACTUAL_SDT */
394         if (sdt.getTableId() == TID_SDT_ACTUAL)
395                 m_chid_current = chid;
396         
397         for (ServiceDescriptionConstIterator s(services.begin()); s != services.end(); ++s)
398         {
399                 SCAN_eDebugNoNewLine("SID %04x: ", (*s)->getServiceId());
400
401                 eServiceReferenceDVB ref;
402                 ePtr<eDVBService> service = new eDVBService;
403                 
404                 ref.set(chid);
405                 ref.setServiceID((*s)->getServiceId());
406
407                 for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
408                                 desc != (*s)->getDescriptors()->end(); ++desc)
409                         if ((*desc)->getTag() == SERVICE_DESCRIPTOR)
410                                 ref.setServiceType(((ServiceDescriptor&)**desc).getServiceType());
411                 
412                 for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
413                                 desc != (*s)->getDescriptors()->end(); ++desc)
414                 {
415                         switch ((*desc)->getTag())
416                         {
417                         case SERVICE_DESCRIPTOR:
418                         {
419                                 ServiceDescriptor &d = (ServiceDescriptor&)**desc;
420                                 service->m_service_name = convertDVBUTF8(d.getServiceName());
421                                 service->genSortName();
422
423                                 service->m_provider_name = convertDVBUTF8(d.getServiceProviderName());
424                                 SCAN_eDebug("name '%s', provider_name '%s'", service->m_service_name.c_str(), service->m_provider_name.c_str());
425                                 break;
426                         }
427                         case CA_IDENTIFIER_DESCRIPTOR:
428                         {
429                                 CaIdentifierDescriptor &d = (CaIdentifierDescriptor&)**desc;
430                                 const CaSystemIdList &caids = *d.getCaSystemIds();
431                                 SCAN_eDebugNoNewLine("CA ");
432                                 for (CaSystemIdList::const_iterator i(caids.begin()); i != caids.end(); ++i)
433                                 {
434                                         SCAN_eDebugNoNewLine("%04x ", *i);
435                                         service->m_ca.insert(*i);
436                                 }
437                                 SCAN_eDebug("");
438                                 break;
439                         }
440                         default:
441                                 SCAN_eDebug("descr<%x>", (*desc)->getTag());
442                                 break;
443                         }
444                 }
445                 
446                 m_new_services.insert(std::pair<eServiceReferenceDVB, ePtr<eDVBService> >(ref, service));               
447         }
448         return 0;
449 }
450
451 RESULT eDVBScan::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
452 {
453         connection = new eConnection(this, m_event.connect(event));
454         return 0;
455 }
456
457 void eDVBScan::getStats(int &transponders_done, int &transponders_total, int &services)
458 {
459         transponders_done = m_ch_scanned.size() + m_ch_unavailable.size();
460         transponders_total = m_ch_toScan.size() + transponders_done;
461         services = m_new_services.size();
462 }