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