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>
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)
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)
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);
37 int eDVBScan::isValidONIDTSID(int orbital_position, eOriginalNetworkID onid, eTransportStreamID tsid)
45 return orbital_position == 192;
47 return tsid != 0x00B0;
49 return abs(orbital_position-282) < 6;
51 return onid.get() < 0xFF00;
55 eDVBNamespace eDVBScan::buildNamespace(eOriginalNetworkID onid, eTransportStreamID tsid, unsigned long hash)
57 // on valid ONIDs, ignore frequency ("sub network") part
58 if (isValidONIDTSID((hash >> 16) & 0xFFFF, onid, tsid))
60 return eDVBNamespace(hash);
63 void eDVBScan::stateChange(iDVBChannel *ch)
66 if (ch->getState(state))
68 if (m_channel_state == state)
71 if (state == iDVBChannel::state_ok)
74 m_channel_state = state;
75 } else if (state == iDVBChannel::state_failed)
77 m_ch_unavailable.push_back(m_ch_current);
80 /* unavailable will timeout, anyway. */
83 RESULT eDVBScan::nextChannel()
85 ePtr<iDVBFrontend> fe;
87 m_SDT = 0; m_BAT = 0; m_NIT = 0;
91 /* check what we need */
92 m_ready_all = readySDT;
94 if (m_flags & scanNetworkSearch)
95 m_ready_all |= readyNIT;
97 if (m_flags & scanSearchBAT)
98 m_ready_all |= readyBAT;
101 m_ready_all |= readyPAT;
103 if (m_ch_toScan.empty())
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());
113 m_ch_current = m_ch_toScan.front();
115 m_ch_toScan.pop_front();
117 if (m_channel->getFrontend(fe))
124 fe->getFrontendType(fetype);
125 if ( fetype == iDVBFrontend::feSatellite)
127 eDVBFrontendParametersSatellite p;
128 m_ch_current->getDVBS(p);
129 m_chid_current = eDVBChannelID(p.orbital_position << 16, -1, -1);
132 m_chid_current = eDVBChannelID();
134 m_channel_state = iDVBChannel::state_idle;
135 if (fe->tune(*m_ch_current))
137 return nextChannel();
146 RESULT eDVBScan::startFilter()
151 /* only start required filters filter */
153 if (m_ready_all & readyPAT)
154 startSDT = m_ready & readyPAT;
157 if (startSDT && (m_ready_all & readySDT))
159 m_SDT = new eTable<ServiceDescriptionSection>();
160 if (m_ready & readyPAT && m_ready & validPAT)
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)))
169 else if (m_SDT->start(m_demux, eDVBSDTSpec()))
171 CONNECT(m_SDT->tableReady, eDVBScan::SDTready);
174 if (!(m_ready & readyPAT))
177 if (m_ready_all & readyPAT)
179 m_PAT = new eTable<ProgramAssociationSection>();
180 if (m_PAT->start(m_demux, eDVBPATSpec()))
182 CONNECT(m_PAT->tableReady, eDVBScan::PATready);
186 if (m_ready_all & readyNIT)
188 m_NIT = new eTable<NetworkInformationSection>();
189 if (m_NIT->start(m_demux, eDVBNITSpec()))
191 CONNECT(m_NIT->tableReady, eDVBScan::NITready);
195 if (m_ready_all & readyBAT)
197 m_BAT = new eTable<BouquetAssociationSection>();
198 if (m_BAT->start(m_demux, eDVBBATSpec()))
200 CONNECT(m_BAT->tableReady, eDVBScan::BATready);
207 void eDVBScan::SDTready(int err)
209 SCAN_eDebug("got sdt");
216 void eDVBScan::NITready(int err)
218 SCAN_eDebug("got nit, err %d", err);
225 void eDVBScan::BATready(int err)
227 SCAN_eDebug("got bat");
234 void eDVBScan::PATready(int err)
236 SCAN_eDebug("got pat");
240 startFilter(); // for starting the SDT filter
243 void eDVBScan::addKnownGoodChannel(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
245 /* add it to the list of known channels. */
247 m_new_channels.insert(std::pair<eDVBChannelID,ePtr<iDVBFrontendParameters> >(chid, feparm));
250 void eDVBScan::addChannelToScan(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
252 /* check if we don't already have that channel ... */
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))
258 *i = feparm; // update
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))
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))
272 /* ... on the current channel */
273 if (sameChannel(m_ch_current, feparm))
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 :)
280 int eDVBScan::sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *ch2, bool exact) const
283 if (ch1->calculateDifference(ch2, diff, exact))
285 if (diff < 4000) // more than 4mhz difference?
290 void eDVBScan::channelDone()
292 if (m_ready & validSDT)
294 unsigned long hash = 0;
296 // m_ch_current is not set, when eDVBScan is just used for a SDT update
298 m_channel->getCurrentFrontendParameters(m_ch_current);
300 m_ch_current->getHash(hash);
302 eDVBNamespace dvbnamespace = buildNamespace(
303 (**m_SDT->getSections().begin()).getOriginalNetworkId(),
304 (**m_SDT->getSections().begin()).getTransportStreamId(),
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;
314 if (m_ready & validNIT)
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)
320 const TransportStreamInfoList &tsinfovec = *(*i)->getTsInfo();
322 for (TransportStreamInfoConstIterator tsinfo(tsinfovec.begin());
323 tsinfo != tsinfovec.end(); ++tsinfo)
325 SCAN_eDebug("TSID: %04x ONID: %04x", (*tsinfo)->getTransportStreamId(),
326 (*tsinfo)->getOriginalNetworkId());
328 eOriginalNetworkID onid = (*tsinfo)->getOriginalNetworkId();
329 eTransportStreamID tsid = (*tsinfo)->getTransportStreamId();
331 for (DescriptorConstIterator desc = (*tsinfo)->getDescriptors()->begin();
332 desc != (*tsinfo)->getDescriptors()->end(); ++desc)
334 switch ((*desc)->getTag())
336 case CABLE_DELIVERY_SYSTEM_DESCRIPTOR:
338 CableDeliverySystemDescriptor &d = (CableDeliverySystemDescriptor&)**desc;
339 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
340 eDVBFrontendParametersCable cable;
342 feparm->setDVBC(cable);
344 unsigned long hash=0;
345 feparm->getHash(hash);
346 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
349 eDVBChannelID(ns, tsid, onid),
353 case TERRESTRIAL_DELIVERY_SYSTEM_DESCRIPTOR:
355 TerrestrialDeliverySystemDescriptor &d = (TerrestrialDeliverySystemDescriptor&)**desc;
356 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
357 eDVBFrontendParametersTerrestrial terr;
359 feparm->setDVBT(terr);
361 unsigned long hash=0;
362 feparm->getHash(hash);
363 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
366 eDVBChannelID(ns, tsid, onid),
370 case SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR:
372 SatelliteDeliverySystemDescriptor &d = (SatelliteDeliverySystemDescriptor&)**desc;
373 if (d.getFrequency() < 10000)
376 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
377 eDVBFrontendParametersSatellite sat;
379 feparm->setDVBS(sat);
380 unsigned long hash=0;
381 feparm->getHash(hash);
383 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
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.");
391 eDVBChannelID(ns, tsid, onid),
397 SCAN_eDebug("descr<%x>", (*desc)->getTag());
404 m_ready &= ~validNIT;
407 if ((m_ready & m_ready_all) != m_ready_all)
409 SCAN_eDebug("channel done!");
411 /* if we had services on this channel, we declare
412 this channels as "known good". add it.
414 (TODO: not yet implemented)
415 a NIT entry could have possible overridden
416 our frontend data with more exact data.
418 (TODO: not yet implemented)
419 the tuning process could have lead to more
420 exact data than the user entered.
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
428 These are the reasons for adding the transponder
429 here, and not before.
433 eWarning("SCAN: the current channel's ID was not corrected - not adding channel.");
435 addKnownGoodChannel(m_chid_current, m_ch_current);
437 m_ch_scanned.push_back(m_ch_current);
441 void eDVBScan::start(const eSmartPtrList<iDVBFrontendParameters> &known_transponders, int flags)
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();
451 for (eSmartPtrList<iDVBFrontendParameters>::const_iterator i(known_transponders.begin()); i != known_transponders.end(); ++i)
454 for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator ii(m_ch_toScan.begin()); ii != m_ch_toScan.end(); ++ii)
456 if (sameChannel(*i, *ii))
463 m_ch_toScan.push_back(*i);
469 void eDVBScan::insertInto(iDVBChannelList *db, bool dontRemoveOldFlags)
471 if (m_flags & scanRemoveServices)
473 bool clearTerrestrial=false;
474 bool clearCable=false;
475 std::set<unsigned int> scanned_sat_positions;
477 std::list<ePtr<iDVBFrontendParameters> >::iterator it(m_ch_scanned.begin());
478 for (;it != m_ch_scanned.end(); ++it)
481 (*it)->getSystem(system);
484 case iDVBFrontend::feSatellite:
486 eDVBFrontendParametersSatellite sat_parm;
487 (*it)->getDVBS(sat_parm);
488 scanned_sat_positions.insert(sat_parm.orbital_position);
491 case iDVBFrontend::feTerrestrial:
493 clearTerrestrial=true;
496 case iDVBFrontend::feCable:
504 for (it=m_ch_unavailable.begin();it != m_ch_unavailable.end(); ++it)
507 (*it)->getSystem(system);
510 case iDVBFrontend::feSatellite:
512 eDVBFrontendParametersSatellite sat_parm;
513 (*it)->getDVBS(sat_parm);
514 scanned_sat_positions.insert(sat_parm.orbital_position);
517 case iDVBFrontend::feTerrestrial:
519 clearTerrestrial=true;
522 case iDVBFrontend::feCable:
530 if (clearTerrestrial)
533 chid.dvbnamespace=0xEEEE0000;
534 db->removeServices(chid);
539 chid.dvbnamespace=0xFFFF0000;
540 db->removeServices(chid);
542 for (std::set<unsigned int>::iterator x(scanned_sat_positions.begin()); x != scanned_sat_positions.end(); ++x)
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);
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)
558 ePtr<eDVBService> dvb_service;
559 if (!db->getService(service->first, dvb_service))
561 if (dvb_service->m_flags & eDVBService::dxNoSDT)
563 if (!(dvb_service->m_flags & eDVBService::dxHoldName))
565 dvb_service->m_service_name = service->second->m_service_name;
566 dvb_service->m_service_name_sort = service->second->m_service_name_sort;
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;
576 db->addService(service->first, service->second);
577 service->second->m_flags |= eDVBService::dxNewFound;
582 RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescriptionSection &sdt)
584 const ServiceDescriptionList &services = *sdt.getDescriptions();
585 SCAN_eDebug("ONID: %04x", sdt.getOriginalNetworkId());
586 eDVBChannelID chid(dvbnamespace, sdt.getTransportStreamId(), sdt.getOriginalNetworkId());
588 /* save correct CHID for this channel */
589 m_chid_current = chid;
591 for (ServiceDescriptionConstIterator s(services.begin()); s != services.end(); ++s)
593 SCAN_eDebugNoNewLine("SID %04x: ", (*s)->getServiceId());
595 eServiceReferenceDVB ref;
596 ePtr<eDVBService> service = new eDVBService;
599 ref.setServiceID((*s)->getServiceId());
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());
606 for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
607 desc != (*s)->getDescriptors()->end(); ++desc)
609 switch ((*desc)->getTag())
611 case SERVICE_DESCRIPTOR:
613 ServiceDescriptor &d = (ServiceDescriptor&)**desc;
614 service->m_service_name = convertDVBUTF8(d.getServiceName());
615 service->genSortName();
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());
621 case CA_IDENTIFIER_DESCRIPTOR:
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)
628 SCAN_eDebugNoNewLine("%04x ", *i);
629 service->m_ca.push_front(*i);
635 SCAN_eDebug("descr<%x>", (*desc)->getTag());
640 std::pair<std::map<eServiceReferenceDVB, ePtr<eDVBService> >::iterator, bool> i = m_new_services.insert(std::pair<eServiceReferenceDVB, ePtr<eDVBService> >(ref, service));
644 m_last_service = i.first;
645 m_event(evtNewService);
651 RESULT eDVBScan::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
653 connection = new eConnection(this, m_event.connect(event));
657 void eDVBScan::getStats(int &transponders_done, int &transponders_total, int &services)
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();
664 void eDVBScan::getLastServiceName(std::string &last_service_name)
666 if (m_last_service == m_new_services.end())
667 last_service_name = "";
669 last_service_name = m_last_service->second->m_service_name;