17c6b5c2b170eadfc9fe67e2b0a69b71dd96f907
[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++/terrestrial_delivery_system_descriptor.h>
9 #include <dvbsi++/cable_delivery_system_descriptor.h>
10 #include <dvbsi++/ca_identifier_descriptor.h>
11 #include <lib/dvb/specs.h>
12 #include <lib/dvb/esection.h>
13 #include <lib/dvb/scan.h>
14 #include <lib/dvb/frontend.h>
15 #include <lib/base/eerror.h>
16 #include <lib/base/estring.h>
17 #include <errno.h>
18 #include <set>
19
20 #define SCAN_eDebug(x...) eDebug(x)
21 #define SCAN_eDebugNoNewLine(x...) eDebugNoNewLine(x)
22
23 DEFINE_REF(eDVBScan);
24
25 eDVBScan::eDVBScan(iDVBChannel *channel): m_channel(channel)
26 {
27         m_ready = m_flags = 0;
28         m_ready_all = readySDT;
29         m_channel_state = iDVBChannel::state_idle;
30
31         if (m_channel->getDemux(m_demux))
32                 SCAN_eDebug("scan: failed to allocate demux!");
33         m_channel->connectStateChange(slot(*this, &eDVBScan::stateChange), m_stateChanged_connection);
34 }
35
36 eDVBScan::~eDVBScan()
37 {
38 }
39
40 int eDVBScan::isValidONIDTSID(int orbital_position, eOriginalNetworkID onid, eTransportStreamID tsid)
41 {
42         switch (onid.get())
43         {
44         case 0:
45         case 0x1111:
46                 return 0;
47         case 1:
48                 return orbital_position == 192;
49         case 0x00B1:
50                 return tsid != 0x00B0;
51         case 0x0002:
52                 return abs(orbital_position-282) < 6;
53         default:
54                 return onid.get() < 0xFF00;
55         }
56 }
57
58 eDVBNamespace eDVBScan::buildNamespace(eOriginalNetworkID onid, eTransportStreamID tsid, unsigned long hash)
59 {
60                 // on valid ONIDs, ignore frequency ("sub network") part
61         if (isValidONIDTSID((hash >> 16) & 0xFFFF, onid, tsid))
62                 hash &= ~0xFFFF;
63         return eDVBNamespace(hash);
64 }
65
66 void eDVBScan::stateChange(iDVBChannel *ch)
67 {
68         int state;
69         if (ch->getState(state))
70                 return;
71         if (m_channel_state == state)
72                 return;
73         
74         if (state == iDVBChannel::state_ok)
75         {
76                 startFilter();
77                 m_channel_state = state;
78         } else if (state == iDVBChannel::state_failed)
79         {
80                 m_ch_unavailable.push_back(m_ch_current);
81                 nextChannel();
82         }
83                         /* unavailable will timeout, anyway. */
84 }
85
86 RESULT eDVBScan::nextChannel()
87 {
88         ePtr<iDVBFrontend> fe;
89
90         m_SDT = 0; m_BAT = 0; m_NIT = 0;
91
92         m_ready = 0;
93         
94                 /* check what we need */
95         m_ready_all = readySDT;
96         
97         if (m_flags & scanNetworkSearch)
98                 m_ready_all |= readyNIT;
99         
100         if (m_flags & scanSearchBAT)
101                 m_ready_all |= readyBAT;
102         
103         if (m_ch_toScan.empty())
104         {
105                 SCAN_eDebug("no channels left to scan.");
106                 SCAN_eDebug("%d channels scanned, %d were unavailable.", 
107                                 m_ch_scanned.size(), m_ch_unavailable.size());
108                 SCAN_eDebug("%d channels in database.", m_new_channels.size());
109                 m_event(evtFinish);
110                 return -ENOENT;
111         }
112         
113         m_ch_current = m_ch_toScan.front();
114         
115         m_ch_toScan.pop_front();
116         
117         if (m_channel->getFrontend(fe))
118         {
119                 m_event(evtFail);
120                 return -ENOTSUP;
121         }
122
123         int fetype;
124         fe->getFrontendType(fetype);
125         if ( fetype == iDVBFrontend::feSatellite)
126         {
127                 eDVBFrontendParametersSatellite p;
128                 m_ch_current->getDVBS(p);
129                 m_chid_current = eDVBChannelID(p.orbital_position << 16, -1, -1);
130         }
131         else
132                 m_chid_current = eDVBChannelID();
133
134         m_channel_state = iDVBChannel::state_idle;
135         if (fe->tune(*m_ch_current))
136         {
137                 return nextChannel();
138                 m_event(evtFail);
139                 return -EINVAL;
140         }
141                 
142         m_event(evtUpdate);
143         return 0;
144 }
145
146 RESULT eDVBScan::startFilter()
147 {
148         assert(m_demux);
149         
150                         /* only start required filters filter */
151         
152         m_SDT = 0;
153
154         if (m_ready_all & readySDT)
155         {
156                 m_SDT = new eTable<ServiceDescriptionSection>();
157                 if (m_SDT->start(m_demux, eDVBSDTSpec()))
158                         return -1;
159                 CONNECT(m_SDT->tableReady, eDVBScan::SDTready);
160         }
161
162         m_NIT = 0;
163         if (m_ready_all & readyNIT)
164         {
165                 m_NIT = new eTable<NetworkInformationSection>();
166                 if (m_NIT->start(m_demux, eDVBNITSpec()))
167                         return -1;
168                 CONNECT(m_NIT->tableReady, eDVBScan::NITready);
169         }
170
171         m_BAT = 0;
172         if (m_ready_all & readyBAT)
173         {
174                 m_BAT = new eTable<BouquetAssociationSection>();
175                 if (m_BAT->start(m_demux, eDVBBATSpec()))
176                         return -1;
177                 CONNECT(m_BAT->tableReady, eDVBScan::BATready);
178         }
179         
180         return 0;
181 }
182
183 void eDVBScan::SDTready(int err)
184 {
185         SCAN_eDebug("got sdt");
186         m_ready |= readySDT;
187         if (!err)
188                 m_ready |= validSDT;
189         channelDone();
190 }
191
192 void eDVBScan::NITready(int err)
193 {
194         SCAN_eDebug("got nit, err %d", err);
195         m_ready |= readyNIT;
196         if (!err)
197                 m_ready |= validNIT;
198         channelDone();
199 }
200
201 void eDVBScan::BATready(int err)
202 {
203         SCAN_eDebug("got bat");
204         m_ready |= readyBAT;
205         if (!err)
206                 m_ready |= validBAT;
207         channelDone();
208 }
209
210 void eDVBScan::addKnownGoodChannel(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
211 {
212                 /* add it to the list of known channels. */
213         if (chid)
214                 m_new_channels.insert(std::pair<eDVBChannelID,ePtr<iDVBFrontendParameters> >(chid, feparm));
215 }
216
217 void eDVBScan::addChannelToScan(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
218 {
219                 /* check if we don't already have that channel ... */
220                 
221                 /* ... in the list of channels to scan */
222         for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_toScan.begin()); i != m_ch_toScan.end(); ++i)
223                 if (sameChannel(*i, feparm))
224                         return;
225
226                 /* ... in the list of successfully scanned channels */
227         for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_scanned.begin()); i != m_ch_scanned.end(); ++i)
228                 if (sameChannel(*i, feparm))
229                         return;
230                 
231                 /* ... in the list of unavailable channels */
232         for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_unavailable.begin()); i != m_ch_unavailable.end(); ++i)
233                 if (sameChannel(*i, feparm))
234                         return;
235
236                 /* ... on the current channel */
237         if (sameChannel(m_ch_current, feparm))
238                 return;
239
240                 /* otherwise, add it to the todo list. */
241         m_ch_toScan.push_front(feparm); // better.. then the rotor not turning wild from east to west :)
242 }
243
244 int eDVBScan::sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *ch2) const
245 {
246         int diff;
247         if (ch1->calculateDifference(ch2, diff))
248                 return 0;
249         if (diff < 4000) // more than 4mhz difference?
250                 return 1;
251         return 0;
252 }
253
254 void eDVBScan::channelDone()
255 {
256         if (m_ready & validSDT)
257         {
258                 unsigned long hash = 0;
259
260                 ePtr<iDVBFrontendParameters> p = m_ch_current;
261
262                 if (!p)  // used in sdt scan
263                         m_channel->getCurrentFrontendParameters(p);
264
265                 p->getHash(hash);
266
267                 eDVBNamespace dvbnamespace = buildNamespace(
268                         (**m_SDT->getSections().begin()).getOriginalNetworkId(),
269                         (**m_SDT->getSections().begin()).getTransportStreamId(),
270                         hash);
271                 
272                 SCAN_eDebug("SDT: ");
273                 std::vector<ServiceDescriptionSection*>::const_iterator i;
274                 for (i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i)
275                         processSDT(dvbnamespace, **i);
276                 m_ready &= ~validSDT;
277         }
278         
279         if (m_ready & validNIT)
280         {
281                 SCAN_eDebug("dumping NIT");
282                 std::vector<NetworkInformationSection*>::const_iterator i;
283                 for (i = m_NIT->getSections().begin(); i != m_NIT->getSections().end(); ++i)
284                 {
285                         const TransportStreamInfoList &tsinfovec = *(*i)->getTsInfo();
286                         
287                         for (TransportStreamInfoConstIterator tsinfo(tsinfovec.begin()); 
288                                 tsinfo != tsinfovec.end(); ++tsinfo)
289                         {
290                                 SCAN_eDebug("TSID: %04x ONID: %04x", (*tsinfo)->getTransportStreamId(),
291                                         (*tsinfo)->getOriginalNetworkId());
292                                 
293                                 eOriginalNetworkID onid = (*tsinfo)->getOriginalNetworkId();
294                                 eTransportStreamID tsid = (*tsinfo)->getTransportStreamId();
295                                 
296                                 for (DescriptorConstIterator desc = (*tsinfo)->getDescriptors()->begin();
297                                                 desc != (*tsinfo)->getDescriptors()->end(); ++desc)
298                                 {
299                                         switch ((*desc)->getTag())
300                                         {
301                                         case CABLE_DELIVERY_SYSTEM_DESCRIPTOR:
302                                         {
303                                                 CableDeliverySystemDescriptor &d = (CableDeliverySystemDescriptor&)**desc;
304                                                 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
305                                                 eDVBFrontendParametersCable cable;
306                                                 cable.set(d);
307                                                 feparm->setDVBC(cable);
308
309                                                 unsigned long hash=0;
310                                                 feparm->getHash(hash);
311                                                 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
312
313                                                 addChannelToScan(
314                                                         eDVBChannelID(ns, tsid, onid),
315                                                         feparm);
316                                                 break;
317                                         }
318                                         case TERRESTRIAL_DELIVERY_SYSTEM_DESCRIPTOR:
319                                         {
320                                                 TerrestrialDeliverySystemDescriptor &d = (TerrestrialDeliverySystemDescriptor&)**desc;
321                                                 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
322                                                 eDVBFrontendParametersTerrestrial terr;
323                                                 terr.set(d);
324                                                 feparm->setDVBT(terr);
325
326                                                 unsigned long hash=0;
327                                                 feparm->getHash(hash);
328                                                 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
329
330                                                 addChannelToScan(
331                                                         eDVBChannelID(ns, tsid, onid),
332                                                         feparm);
333                                                 break;
334                                         }
335                                         case SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR:
336                                         {
337                                                 SatelliteDeliverySystemDescriptor &d = (SatelliteDeliverySystemDescriptor&)**desc;
338                                                 if (d.getFrequency() < 10000)
339                                                         break;
340                                                 
341                                                 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
342                                                 eDVBFrontendParametersSatellite sat;
343                                                 sat.set(d);
344                                                 feparm->setDVBS(sat);
345                                                 unsigned long hash=0;
346                                                 feparm->getHash(hash);
347                                                 
348                                                 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
349                                                 
350                                                 if ( m_chid_current.dvbnamespace.get() != -1 &&
351                                                         ((ns.get() ^ m_chid_current.dvbnamespace.get()) & 0xFFFF0000))
352                                                         SCAN_eDebug("dropping this transponder, it's on another satellite.");
353                                                 else
354                                                 {
355                                                         addChannelToScan(
356                                                                         eDVBChannelID(ns, tsid, onid),
357                                                                         feparm);
358                                                 }
359                                                 break;
360                                         }
361                                         default:
362                                                 SCAN_eDebug("descr<%x>", (*desc)->getTag());
363                                                 break;
364                                         }
365                                 }
366                                 
367                         }
368                 }
369                 m_ready &= ~validNIT;
370         }
371         
372         if ((m_ready  & m_ready_all) != m_ready_all)
373                 return;
374         SCAN_eDebug("channel done!");
375         
376                 /* if we had services on this channel, we declare
377                    this channels as "known good". add it.
378                    
379                    (TODO: not yet implemented)
380                    a NIT entry could have possible overridden
381                    our frontend data with more exact data.
382                    
383                    (TODO: not yet implemented)
384                    the tuning process could have lead to more
385                    exact data than the user entered.
386                    
387                    The channel id was probably corrected
388                    by the data written in the SDT. this is
389                    important, as "initial transponder lists"
390                    usually don't have valid CHIDs (and that's
391                    good).
392                    
393                    These are the reasons for adding the transponder
394                    here, and not before.
395                 */
396
397         ePtr<iDVBFrontendParameters> p = m_ch_current;
398         if (!p)
399                 m_channel->getCurrentFrontendParameters(p);
400
401         if (!m_chid_current)
402                 eWarning("SCAN: the current channel's ID was not corrected - not adding channel.");
403         else
404                 addKnownGoodChannel(m_chid_current, p);
405
406         m_ch_scanned.push_back(p);
407         nextChannel();
408 }
409
410 void eDVBScan::start(const eSmartPtrList<iDVBFrontendParameters> &known_transponders, int flags)
411 {
412         m_flags = flags;
413         m_ch_toScan.clear();
414         m_ch_scanned.clear();
415         m_ch_unavailable.clear();
416         m_new_channels.clear();
417         m_new_services.clear();
418         m_last_service = m_new_services.end();
419
420         for (eSmartPtrList<iDVBFrontendParameters>::const_iterator i(known_transponders.begin()); i != known_transponders.end(); ++i)
421         {
422                 bool exist=false;
423                 for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator ii(m_ch_toScan.begin()); ii != m_ch_toScan.end(); ++ii)
424                 {
425                         if (sameChannel(*i, *ii))
426                         {
427                                 exist=true;
428                                 break;
429                         }
430                 }
431                 if (!exist)
432                         m_ch_toScan.push_back(*i);
433         }
434
435         nextChannel();
436 }
437
438 void eDVBScan::insertInto(iDVBChannelList *db, bool dontRemoveOldFlags)
439 {
440         if (m_flags & scanRemoveServices)
441         {
442                 bool clearTerrestrial=false;
443                 bool clearCable=false;
444                 std::set<unsigned int> scanned_sat_positions;
445                 
446                 std::list<ePtr<iDVBFrontendParameters> >::iterator it(m_ch_scanned.begin());
447                 for (;it != m_ch_scanned.end(); ++it)
448                 {
449                         int system;
450                         (*it)->getSystem(system);
451                         switch(system)
452                         {
453                                 case iDVBFrontend::feSatellite:
454                                 {
455                                         eDVBFrontendParametersSatellite sat_parm;
456                                         (*it)->getDVBS(sat_parm);
457                                         scanned_sat_positions.insert(sat_parm.orbital_position);
458                                         break;
459                                 }
460                                 case iDVBFrontend::feTerrestrial:
461                                 {
462                                         clearTerrestrial=true;
463                                         break;
464                                 }
465                                 case iDVBFrontend::feCable:
466                                 {
467                                         clearCable=true;
468                                         break;
469                                 }
470                         }
471                 }
472
473                 for (it=m_ch_unavailable.begin();it != m_ch_unavailable.end(); ++it)
474                 {
475                         int system;
476                         (*it)->getSystem(system);
477                         switch(system)
478                         {
479                                 case iDVBFrontend::feSatellite:
480                                 {
481                                         eDVBFrontendParametersSatellite sat_parm;
482                                         (*it)->getDVBS(sat_parm);
483                                         scanned_sat_positions.insert(sat_parm.orbital_position);
484                                         break;
485                                 }
486                                 case iDVBFrontend::feTerrestrial:
487                                 {
488                                         clearTerrestrial=true;
489                                         break;
490                                 }
491                                 case iDVBFrontend::feCable:
492                                 {
493                                         clearCable=true;
494                                         break;
495                                 }
496                         }
497                 }
498
499                 if (clearTerrestrial)
500                 {
501                         eDVBChannelID chid;
502                         chid.dvbnamespace=0xEEEE0000;
503                         db->removeServices(chid);
504                 }
505                 if (clearCable)
506                 {
507                         eDVBChannelID chid;
508                         chid.dvbnamespace=0xFFFF0000;
509                         db->removeServices(chid);
510                 }
511                 for (std::set<unsigned int>::iterator x(scanned_sat_positions.begin()); x != scanned_sat_positions.end(); ++x)
512                 {
513                         eDVBChannelID chid;
514                         if (m_flags & scanDontRemoveFeeds)
515                                 chid.dvbnamespace = eDVBNamespace((*x)<<16);
516 //                      eDebug("remove %d %08x", *x, chid.dvbnamespace.get());
517                         db->removeServices(chid, *x);
518                 }
519         }
520
521         for (std::map<eDVBChannelID, ePtr<iDVBFrontendParameters> >::const_iterator 
522                         ch(m_new_channels.begin()); ch != m_new_channels.end(); ++ch)
523                 db->addChannelToList(ch->first, ch->second);
524         for (std::map<eServiceReferenceDVB, ePtr<eDVBService> >::const_iterator
525                 service(m_new_services.begin()); service != m_new_services.end(); ++service)
526         {
527                 ePtr<eDVBService> dvb_service;
528                 if (!db->getService(service->first, dvb_service))
529                 {
530                         if (dvb_service->m_flags & eDVBService::dxNoSDT)
531                                 continue;
532                         if (!(dvb_service->m_flags & eDVBService::dxHoldName))
533                         {
534                                 dvb_service->m_service_name = service->second->m_service_name;
535                                 dvb_service->m_service_name_sort = service->second->m_service_name_sort;
536                         }
537                         dvb_service->m_provider_name = service->second->m_provider_name;
538
539                         if (!dontRemoveOldFlags) // do not remove new found flags when not wished
540                                 dvb_service->m_flags &= ~eDVBService::dxNewFound;
541                 }
542                 else
543                 {
544                         db->addService(service->first, service->second);
545                         service->second->m_flags |= eDVBService::dxNewFound;
546                 }
547         }
548 }
549
550 RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescriptionSection &sdt)
551 {
552         const ServiceDescriptionList &services = *sdt.getDescriptions();
553         SCAN_eDebug("ONID: %04x", sdt.getOriginalNetworkId());
554         eDVBChannelID chid(dvbnamespace, sdt.getTransportStreamId(), sdt.getOriginalNetworkId());
555         
556                 /* save correct CHID for this channel if this is an ACTUAL_SDT */
557         if (sdt.getTableId() == TID_SDT_ACTUAL)
558                 m_chid_current = chid;
559         
560         for (ServiceDescriptionConstIterator s(services.begin()); s != services.end(); ++s)
561         {
562                 SCAN_eDebugNoNewLine("SID %04x: ", (*s)->getServiceId());
563
564                 eServiceReferenceDVB ref;
565                 ePtr<eDVBService> service = new eDVBService;
566                 
567                 ref.set(chid);
568                 ref.setServiceID((*s)->getServiceId());
569
570                 for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
571                                 desc != (*s)->getDescriptors()->end(); ++desc)
572                         if ((*desc)->getTag() == SERVICE_DESCRIPTOR)
573                                 ref.setServiceType(((ServiceDescriptor&)**desc).getServiceType());
574                 
575                 for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
576                                 desc != (*s)->getDescriptors()->end(); ++desc)
577                 {
578                         switch ((*desc)->getTag())
579                         {
580                         case SERVICE_DESCRIPTOR:
581                         {
582                                 ServiceDescriptor &d = (ServiceDescriptor&)**desc;
583                                 service->m_service_name = convertDVBUTF8(d.getServiceName());
584                                 service->genSortName();
585
586                                 service->m_provider_name = convertDVBUTF8(d.getServiceProviderName());
587                                 SCAN_eDebug("name '%s', provider_name '%s'", service->m_service_name.c_str(), service->m_provider_name.c_str());
588                                 break;
589                         }
590                         case CA_IDENTIFIER_DESCRIPTOR:
591                         {
592                                 CaIdentifierDescriptor &d = (CaIdentifierDescriptor&)**desc;
593                                 const CaSystemIdList &caids = *d.getCaSystemIds();
594                                 SCAN_eDebugNoNewLine("CA ");
595                                 for (CaSystemIdList::const_iterator i(caids.begin()); i != caids.end(); ++i)
596                                 {
597                                         SCAN_eDebugNoNewLine("%04x ", *i);
598                                         service->m_ca.insert(*i);
599                                 }
600                                 SCAN_eDebug("");
601                                 break;
602                         }
603                         default:
604                                 SCAN_eDebug("descr<%x>", (*desc)->getTag());
605                                 break;
606                         }
607                 }
608                 
609                 std::pair<std::map<eServiceReferenceDVB, ePtr<eDVBService> >::iterator, bool> i = m_new_services.insert(std::pair<eServiceReferenceDVB, ePtr<eDVBService> >(ref, service));
610                 
611                 if (i.second)
612                 {
613                         m_last_service = i.first;
614                         m_event(evtNewService);
615                 }
616         }
617         return 0;
618 }
619
620 RESULT eDVBScan::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
621 {
622         connection = new eConnection(this, m_event.connect(event));
623         return 0;
624 }
625
626 void eDVBScan::getStats(int &transponders_done, int &transponders_total, int &services)
627 {
628         transponders_done = m_ch_scanned.size() + m_ch_unavailable.size();
629         transponders_total = m_ch_toScan.size() + transponders_done;
630         services = m_new_services.size();
631 }
632
633 void eDVBScan::getLastServiceName(std::string &last_service_name)
634 {
635         if (m_last_service == m_new_services.end())
636                 last_service_name = "";
637         else
638                 last_service_name = m_last_service->second->m_service_name;
639 }