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