"Search NIT" -> "Network scan"
[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         
108         m_ch_toScan.pop_front();
109         
110         if (m_channel->getFrontend(fe))
111         {
112                 m_event(evtFail);
113                 return -ENOTSUP;
114         }
115
116         int fetype;
117         fe->getFrontendType(fetype);
118         if ( fetype == iDVBFrontend::feSatellite)
119         {
120                 eDVBFrontendParametersSatellite p;
121                 m_ch_current->getDVBS(p);
122                 m_chid_current = eDVBChannelID(p.orbital_position << 16, -1, -1);
123         }
124         else
125                 m_chid_current = eDVBChannelID();
126
127         m_channel_state = iDVBChannel::state_idle;
128         if (fe->tune(*m_ch_current))
129         {
130                 return nextChannel();
131                 m_event(evtFail);
132                 return -EINVAL;
133         }
134                 
135         m_event(evtUpdate);
136         return 0;
137 }
138
139 RESULT eDVBScan::startFilter()
140 {
141         assert(m_demux);
142         
143                         /* only start required filters filter */
144         
145         m_SDT = 0;
146
147         if (m_ready_all & readySDT)
148         {
149                 m_SDT = new eTable<ServiceDescriptionSection>();
150                 if (m_SDT->start(m_demux, eDVBSDTSpec()))
151                         return -1;
152                 CONNECT(m_SDT->tableReady, eDVBScan::SDTready);
153         }
154
155         m_NIT = 0;
156         if (m_ready_all & readyNIT)
157         {
158                 m_NIT = new eTable<NetworkInformationSection>();
159                 if (m_NIT->start(m_demux, eDVBNITSpec()))
160                         return -1;
161                 CONNECT(m_NIT->tableReady, eDVBScan::NITready);
162         }
163
164         m_BAT = 0;
165         if (m_ready_all & readyBAT)
166         {
167                 m_BAT = new eTable<BouquetAssociationSection>();
168                 if (m_BAT->start(m_demux, eDVBBATSpec()))
169                         return -1;
170                 CONNECT(m_BAT->tableReady, eDVBScan::BATready);
171         }
172         
173         return 0;
174 }
175
176 void eDVBScan::SDTready(int err)
177 {
178         SCAN_eDebug("got sdt");
179         m_ready |= readySDT;
180         if (!err)
181                 m_ready |= validSDT;
182         channelDone();
183 }
184
185 void eDVBScan::NITready(int err)
186 {
187         SCAN_eDebug("got nit, err %d", err);
188         m_ready |= readyNIT;
189         if (!err)
190                 m_ready |= validNIT;
191         channelDone();
192 }
193
194 void eDVBScan::BATready(int err)
195 {
196         SCAN_eDebug("got bat");
197         m_ready |= readyBAT;
198         if (!err)
199                 m_ready |= validBAT;
200         channelDone();
201 }
202
203 void eDVBScan::addKnownGoodChannel(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
204 {
205                 /* add it to the list of known channels. */
206         if (chid)
207                 m_new_channels.insert(std::pair<eDVBChannelID,ePtr<iDVBFrontendParameters> >(chid, feparm));
208 }
209
210 void eDVBScan::addChannelToScan(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
211 {
212                 /* check if we don't already have that channel ... */
213                 
214                 /* ... in the list of channels to scan */
215         for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_toScan.begin()); i != m_ch_toScan.end(); ++i)
216                 if (sameChannel(*i, feparm))
217                         return;
218
219                 /* ... in the list of successfully scanned channels */
220         for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_scanned.begin()); i != m_ch_scanned.end(); ++i)
221                 if (sameChannel(*i, feparm))
222                         return;
223                 
224                 /* ... in the list of unavailable channels */
225         for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_unavailable.begin()); i != m_ch_unavailable.end(); ++i)
226                 if (sameChannel(*i, feparm))
227                         return;
228
229                 /* ... on the current channel */
230         if (sameChannel(m_ch_current, feparm))
231                 return;
232
233                 /* otherwise, add it to the todo list. */
234         m_ch_toScan.push_front(feparm); // better.. then the rotor not turning wild from east to west :)
235 }
236
237 int eDVBScan::sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *ch2) const
238 {
239         int diff;
240         if (ch1->calculateDifference(ch2, diff))
241                 return 0;
242         if (diff < 4000) // more than 4mhz difference?
243                 return 1;
244         return 0;
245 }
246
247 void eDVBScan::channelDone()
248 {
249         if (m_ready & validSDT)
250         {
251                 unsigned long hash = 0;
252                 m_ch_current->getHash(hash);
253                 
254                 eDVBNamespace dvbnamespace = buildNamespace(
255                         (**m_SDT->getSections().begin()).getOriginalNetworkId(),
256                         (**m_SDT->getSections().begin()).getTransportStreamId(),
257                         hash);
258                 
259                 SCAN_eDebug("SDT: ");
260                 std::vector<ServiceDescriptionSection*>::const_iterator i;
261                 for (i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i)
262                         processSDT(dvbnamespace, **i);
263                 m_ready &= ~validSDT;
264         }
265         
266         if (m_ready & validNIT)
267         {
268                 SCAN_eDebug("dumping NIT");
269                 std::vector<NetworkInformationSection*>::const_iterator i;
270                 for (i = m_NIT->getSections().begin(); i != m_NIT->getSections().end(); ++i)
271                 {
272                         const TransportStreamInfoList &tsinfovec = *(*i)->getTsInfo();
273                         
274                         for (TransportStreamInfoConstIterator tsinfo(tsinfovec.begin()); 
275                                 tsinfo != tsinfovec.end(); ++tsinfo)
276                         {
277                                 SCAN_eDebug("TSID: %04x ONID: %04x", (*tsinfo)->getTransportStreamId(),
278                                         (*tsinfo)->getOriginalNetworkId());
279                                 
280                                 eOriginalNetworkID onid = (*tsinfo)->getOriginalNetworkId();
281                                 eTransportStreamID tsid = (*tsinfo)->getTransportStreamId();
282                                 
283                                 for (DescriptorConstIterator desc = (*tsinfo)->getDescriptors()->begin();
284                                                 desc != (*tsinfo)->getDescriptors()->end(); ++desc)
285                                 {
286                                         switch ((*desc)->getTag())
287                                         {
288 //                                      case SERVICE_LIST_DESCRIPTOR:
289                                         case SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR:
290                                         {
291                                                 SatelliteDeliverySystemDescriptor &d = (SatelliteDeliverySystemDescriptor&)**desc;
292                                                 SCAN_eDebug("%d kHz, %d%d%d.%d%c %s MOD:%d %d symb/s, fec %d", 
293                                                                 d.getFrequency(), 
294                                                                 (d.getOrbitalPosition()>>12)&0xF,
295                                                                 (d.getOrbitalPosition()>>8)&0xF,
296                                                                 (d.getOrbitalPosition()>>4)&0xF,
297                                                                 d.getOrbitalPosition()&0xF, d.getWestEastFlag()?'E':'W',
298                                                                 d.getPolarization() ? "hor" : "vert",
299                                                                 d.getModulation(), d.getSymbolRate(), d.getFecInner());
300                                                 
301                                                         /* some sanity checking: below 100MHz is invalid */
302                                                 if (d.getFrequency() < 10000)
303                                                         break;
304                                                 
305                                                 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
306                                                 eDVBFrontendParametersSatellite sat;
307                                                 sat.set(d);
308                                                 feparm->setDVBS(sat);
309                                                 unsigned long hash=0;
310                                                 feparm->getHash(hash);
311                                                 
312                                                 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
313                                                 
314                                                 if ( m_chid_current.dvbnamespace.get() != -1 &&
315                                                         ((ns.get() ^ m_chid_current.dvbnamespace.get()) & 0xFFFF0000))
316                                                         eDebug("dropping this transponder, it's on another satellite.");
317                                                 else
318                                                 {
319                                                         addChannelToScan(
320                                                                         eDVBChannelID(ns, tsid, onid),
321                                                                         feparm);
322                                                 }
323                                                 break;
324                                         }
325                                         default:
326                                                 SCAN_eDebug("descr<%x>", (*desc)->getTag());
327                                                 break;
328                                         }
329                                 }
330                                 
331                         }
332                 }
333                 m_ready &= ~validNIT;
334         }
335         
336         if ((m_ready  & m_ready_all) != m_ready_all)
337                 return;
338         SCAN_eDebug("channel done!");
339         
340                 /* if we had services on this channel, we declare
341                    this channels as "known good". add it.
342                    
343                    (TODO: not yet implemented)
344                    a NIT entry could have possible overridden
345                    our frontend data with more exact data.
346                    
347                    (TODO: not yet implemented)
348                    the tuning process could have lead to more
349                    exact data than the user entered.
350                    
351                    The channel id was probably corrected
352                    by the data written in the SDT. this is
353                    important, as "initial transponder lists"
354                    usually don't have valid CHIDs (and that's
355                    good).
356                    
357                    These are the reasons for adding the transponder
358                    here, and not before.
359                 */
360         
361         if (!m_chid_current)
362                 eWarning("SCAN: the current channel's ID was not corrected - not adding channel.");
363         else
364                 addKnownGoodChannel(m_chid_current, m_ch_current);
365         
366         m_ch_scanned.push_back(m_ch_current);
367         nextChannel();
368 }
369
370 void eDVBScan::start(const eSmartPtrList<iDVBFrontendParameters> &known_transponders, int flags)
371 {
372         m_flags = flags;
373         m_ch_toScan.clear();
374         m_ch_scanned.clear();
375         m_ch_unavailable.clear();
376         m_new_channels.clear();
377         m_new_services.clear();
378         m_last_service = m_new_services.end();
379
380         for (eSmartPtrList<iDVBFrontendParameters>::const_iterator i(known_transponders.begin()); i != known_transponders.end(); ++i)
381         {
382                 bool exist=false;
383                 for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator ii(m_ch_toScan.begin()); ii != m_ch_toScan.end(); ++ii)
384                 {
385                         if (sameChannel(*i, *ii))
386                         {
387                                 exist=true;
388                                 break;
389                         }
390                 }
391                 if (!exist)
392                         m_ch_toScan.push_back(*i);
393         }
394
395         nextChannel();
396 }
397
398 void eDVBScan::insertInto(iDVBChannelList *db)
399 {
400         for (std::map<eDVBChannelID, ePtr<iDVBFrontendParameters> >::const_iterator 
401                         ch(m_new_channels.begin()); ch != m_new_channels.end(); ++ch)
402                 db->addChannelToList(ch->first, ch->second);
403         for (std::map<eServiceReferenceDVB, ePtr<eDVBService> >::const_iterator
404                 service(m_new_services.begin()); service != m_new_services.end(); ++service)
405         {
406                 ePtr<eDVBService> dvb_service;
407                 if (!db->getService(service->first, dvb_service))
408                         *dvb_service = *service->second;
409                 else
410                         db->addService(service->first, service->second);
411         }
412 }
413
414 RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescriptionSection &sdt)
415 {
416         const ServiceDescriptionList &services = *sdt.getDescriptions();
417         SCAN_eDebug("ONID: %04x", sdt.getOriginalNetworkId());
418         eDVBChannelID chid(dvbnamespace, sdt.getTransportStreamId(), sdt.getOriginalNetworkId());
419         
420                 /* save correct CHID for this channel if this is an ACTUAL_SDT */
421         if (sdt.getTableId() == TID_SDT_ACTUAL)
422                 m_chid_current = chid;
423         
424         for (ServiceDescriptionConstIterator s(services.begin()); s != services.end(); ++s)
425         {
426                 SCAN_eDebugNoNewLine("SID %04x: ", (*s)->getServiceId());
427
428                 eServiceReferenceDVB ref;
429                 ePtr<eDVBService> service = new eDVBService;
430                 
431                 ref.set(chid);
432                 ref.setServiceID((*s)->getServiceId());
433
434                 for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
435                                 desc != (*s)->getDescriptors()->end(); ++desc)
436                         if ((*desc)->getTag() == SERVICE_DESCRIPTOR)
437                                 ref.setServiceType(((ServiceDescriptor&)**desc).getServiceType());
438                 
439                 for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
440                                 desc != (*s)->getDescriptors()->end(); ++desc)
441                 {
442                         switch ((*desc)->getTag())
443                         {
444                         case SERVICE_DESCRIPTOR:
445                         {
446                                 ServiceDescriptor &d = (ServiceDescriptor&)**desc;
447                                 service->m_service_name = convertDVBUTF8(d.getServiceName());
448                                 service->genSortName();
449
450                                 service->m_provider_name = convertDVBUTF8(d.getServiceProviderName());
451                                 SCAN_eDebug("name '%s', provider_name '%s'", service->m_service_name.c_str(), service->m_provider_name.c_str());
452                                 break;
453                         }
454                         case CA_IDENTIFIER_DESCRIPTOR:
455                         {
456                                 CaIdentifierDescriptor &d = (CaIdentifierDescriptor&)**desc;
457                                 const CaSystemIdList &caids = *d.getCaSystemIds();
458                                 SCAN_eDebugNoNewLine("CA ");
459                                 for (CaSystemIdList::const_iterator i(caids.begin()); i != caids.end(); ++i)
460                                 {
461                                         SCAN_eDebugNoNewLine("%04x ", *i);
462                                         service->m_ca.insert(*i);
463                                 }
464                                 SCAN_eDebug("");
465                                 break;
466                         }
467                         default:
468                                 SCAN_eDebug("descr<%x>", (*desc)->getTag());
469                                 break;
470                         }
471                 }
472                 
473                 std::pair<std::map<eServiceReferenceDVB, ePtr<eDVBService> >::iterator, bool> i = m_new_services.insert(std::pair<eServiceReferenceDVB, ePtr<eDVBService> >(ref, service));
474                 
475                 if (i.second)
476                 {
477                         m_last_service = i.first;
478                         m_event(evtNewService);
479                 }
480         }
481         return 0;
482 }
483
484 RESULT eDVBScan::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
485 {
486         connection = new eConnection(this, m_event.connect(event));
487         return 0;
488 }
489
490 void eDVBScan::getStats(int &transponders_done, int &transponders_total, int &services)
491 {
492         transponders_done = m_ch_scanned.size() + m_ch_unavailable.size();
493         transponders_total = m_ch_toScan.size() + transponders_done;
494         services = m_new_services.size();
495 }
496
497 void eDVBScan::getLastServiceName(std::string &last_service_name)
498 {
499         if (m_last_service == m_new_services.end())
500                 last_service_name = "";
501         else
502                 last_service_name = m_last_service->second->m_service_name;
503 }