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 ... */
255 feparm->getSystem(type);
259 case iDVBFrontend::feSatellite:
261 eDVBFrontendParametersSatellite parm;
262 feparm->getDVBS(parm);
263 eDebug("try to add %d %d %d %d %d %d",
264 parm.orbital_position, parm.frequency, parm.symbol_rate, parm.polarisation, parm.fec, parm.modulation);
267 case iDVBFrontend::feCable:
269 eDVBFrontendParametersCable parm;
270 feparm->getDVBC(parm);
271 eDebug("try to add %d %d %d %d %d",
272 parm.frequency, parm.symbol_rate, parm.modulation, parm.fec_inner);
275 case iDVBFrontend::feTerrestrial:
277 eDVBFrontendParametersTerrestrial parm;
278 feparm->getDVBT(parm);
279 eDebug("try to add %d %d %d %d %d %d %d %d",
280 parm.frequency, parm.modulation, parm.transmission_mode, parm.hierarchy,
281 parm.guard_interval, parm.code_rate_LP, parm.code_rate_HP, parm.bandwidth);
286 /* ... in the list of channels to scan */
287 for (std::list<ePtr<iDVBFrontendParameters> >::iterator i(m_ch_toScan.begin()); i != m_ch_toScan.end(); ++i)
288 if (sameChannel(*i, feparm))
290 *i = feparm; // update
295 /* ... in the list of successfully scanned channels */
296 for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_scanned.begin()); i != m_ch_scanned.end(); ++i)
297 if (sameChannel(*i, feparm))
299 eDebug("successfully scanned");
303 /* ... in the list of unavailable channels */
304 for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_unavailable.begin()); i != m_ch_unavailable.end(); ++i)
305 if (sameChannel(*i, feparm, true))
307 eDebug("scanned but not available");
311 /* ... on the current channel */
312 if (sameChannel(m_ch_current, feparm))
314 eDebug("is current");
318 eDebug("really add");
319 /* otherwise, add it to the todo list. */
320 m_ch_toScan.push_front(feparm); // better.. then the rotor not turning wild from east to west :)
323 int eDVBScan::sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *ch2, bool exact) const
326 if (ch1->calculateDifference(ch2, diff, exact))
328 if (diff < 4000) // more than 4mhz difference?
333 void eDVBScan::channelDone()
335 if (m_ready & validSDT)
337 unsigned long hash = 0;
339 // m_ch_current is not set, when eDVBScan is just used for a SDT update
341 m_channel->getCurrentFrontendParameters(m_ch_current);
343 m_ch_current->getHash(hash);
345 eDVBNamespace dvbnamespace = buildNamespace(
346 (**m_SDT->getSections().begin()).getOriginalNetworkId(),
347 (**m_SDT->getSections().begin()).getTransportStreamId(),
350 SCAN_eDebug("SDT: ");
351 std::vector<ServiceDescriptionSection*>::const_iterator i;
352 for (i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i)
353 processSDT(dvbnamespace, **i);
354 m_ready &= ~validSDT;
357 if (m_ready & validNIT)
359 SCAN_eDebug("dumping NIT");
360 std::vector<NetworkInformationSection*>::const_iterator i;
361 for (i = m_NIT->getSections().begin(); i != m_NIT->getSections().end(); ++i)
363 const TransportStreamInfoList &tsinfovec = *(*i)->getTsInfo();
365 for (TransportStreamInfoConstIterator tsinfo(tsinfovec.begin());
366 tsinfo != tsinfovec.end(); ++tsinfo)
368 SCAN_eDebug("TSID: %04x ONID: %04x", (*tsinfo)->getTransportStreamId(),
369 (*tsinfo)->getOriginalNetworkId());
371 eOriginalNetworkID onid = (*tsinfo)->getOriginalNetworkId();
372 eTransportStreamID tsid = (*tsinfo)->getTransportStreamId();
374 for (DescriptorConstIterator desc = (*tsinfo)->getDescriptors()->begin();
375 desc != (*tsinfo)->getDescriptors()->end(); ++desc)
377 switch ((*desc)->getTag())
379 case CABLE_DELIVERY_SYSTEM_DESCRIPTOR:
381 CableDeliverySystemDescriptor &d = (CableDeliverySystemDescriptor&)**desc;
382 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
383 eDVBFrontendParametersCable cable;
385 feparm->setDVBC(cable);
387 unsigned long hash=0;
388 feparm->getHash(hash);
389 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
392 eDVBChannelID(ns, tsid, onid),
396 case TERRESTRIAL_DELIVERY_SYSTEM_DESCRIPTOR:
398 TerrestrialDeliverySystemDescriptor &d = (TerrestrialDeliverySystemDescriptor&)**desc;
399 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
400 eDVBFrontendParametersTerrestrial terr;
402 feparm->setDVBT(terr);
404 unsigned long hash=0;
405 feparm->getHash(hash);
406 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
409 eDVBChannelID(ns, tsid, onid),
413 case SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR:
415 SatelliteDeliverySystemDescriptor &d = (SatelliteDeliverySystemDescriptor&)**desc;
416 if (d.getFrequency() < 10000)
419 ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
420 eDVBFrontendParametersSatellite sat;
422 feparm->setDVBS(sat);
423 unsigned long hash=0;
424 feparm->getHash(hash);
426 eDVBNamespace ns = buildNamespace(onid, tsid, hash);
428 if ( m_chid_current.dvbnamespace.get() != -1 &&
429 ((ns.get() ^ m_chid_current.dvbnamespace.get()) & 0xFFFF0000))
430 SCAN_eDebug("dropping this transponder, it's on another satellite.");
434 eDVBChannelID(ns, tsid, onid),
440 SCAN_eDebug("descr<%x>", (*desc)->getTag());
447 m_ready &= ~validNIT;
450 if ((m_ready & m_ready_all) != m_ready_all)
452 SCAN_eDebug("channel done!");
454 /* if we had services on this channel, we declare
455 this channels as "known good". add it.
457 (TODO: not yet implemented)
458 a NIT entry could have possible overridden
459 our frontend data with more exact data.
461 (TODO: not yet implemented)
462 the tuning process could have lead to more
463 exact data than the user entered.
465 The channel id was probably corrected
466 by the data written in the SDT. this is
467 important, as "initial transponder lists"
468 usually don't have valid CHIDs (and that's
471 These are the reasons for adding the transponder
472 here, and not before.
476 eWarning("SCAN: the current channel's ID was not corrected - not adding channel.");
478 addKnownGoodChannel(m_chid_current, m_ch_current);
480 m_ch_scanned.push_back(m_ch_current);
484 void eDVBScan::start(const eSmartPtrList<iDVBFrontendParameters> &known_transponders, int flags)
488 m_ch_scanned.clear();
489 m_ch_unavailable.clear();
490 m_new_channels.clear();
491 m_new_services.clear();
492 m_last_service = m_new_services.end();
494 for (eSmartPtrList<iDVBFrontendParameters>::const_iterator i(known_transponders.begin()); i != known_transponders.end(); ++i)
497 for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator ii(m_ch_toScan.begin()); ii != m_ch_toScan.end(); ++ii)
499 if (sameChannel(*i, *ii))
506 m_ch_toScan.push_back(*i);
512 void eDVBScan::insertInto(iDVBChannelList *db, bool dontRemoveOldFlags)
514 if (m_flags & scanRemoveServices)
516 bool clearTerrestrial=false;
517 bool clearCable=false;
518 std::set<unsigned int> scanned_sat_positions;
520 std::list<ePtr<iDVBFrontendParameters> >::iterator it(m_ch_scanned.begin());
521 for (;it != m_ch_scanned.end(); ++it)
524 (*it)->getSystem(system);
527 case iDVBFrontend::feSatellite:
529 eDVBFrontendParametersSatellite sat_parm;
530 (*it)->getDVBS(sat_parm);
531 scanned_sat_positions.insert(sat_parm.orbital_position);
534 case iDVBFrontend::feTerrestrial:
536 clearTerrestrial=true;
539 case iDVBFrontend::feCable:
547 for (it=m_ch_unavailable.begin();it != m_ch_unavailable.end(); ++it)
550 (*it)->getSystem(system);
553 case iDVBFrontend::feSatellite:
555 eDVBFrontendParametersSatellite sat_parm;
556 (*it)->getDVBS(sat_parm);
557 scanned_sat_positions.insert(sat_parm.orbital_position);
560 case iDVBFrontend::feTerrestrial:
562 clearTerrestrial=true;
565 case iDVBFrontend::feCable:
573 if (clearTerrestrial)
576 chid.dvbnamespace=0xEEEE0000;
577 db->removeServices(chid);
582 chid.dvbnamespace=0xFFFF0000;
583 db->removeServices(chid);
585 for (std::set<unsigned int>::iterator x(scanned_sat_positions.begin()); x != scanned_sat_positions.end(); ++x)
588 if (m_flags & scanDontRemoveFeeds)
589 chid.dvbnamespace = eDVBNamespace((*x)<<16);
590 // eDebug("remove %d %08x", *x, chid.dvbnamespace.get());
591 db->removeServices(chid, *x);
595 for (std::map<eDVBChannelID, ePtr<iDVBFrontendParameters> >::const_iterator
596 ch(m_new_channels.begin()); ch != m_new_channels.end(); ++ch)
597 db->addChannelToList(ch->first, ch->second);
598 for (std::map<eServiceReferenceDVB, ePtr<eDVBService> >::const_iterator
599 service(m_new_services.begin()); service != m_new_services.end(); ++service)
601 ePtr<eDVBService> dvb_service;
602 if (!db->getService(service->first, dvb_service))
604 if (dvb_service->m_flags & eDVBService::dxNoSDT)
606 if (!(dvb_service->m_flags & eDVBService::dxHoldName))
608 dvb_service->m_service_name = service->second->m_service_name;
609 dvb_service->m_service_name_sort = service->second->m_service_name_sort;
611 dvb_service->m_provider_name = service->second->m_provider_name;
612 if (service->second->m_ca.size())
613 dvb_service->m_ca = service->second->m_ca;
614 if (!dontRemoveOldFlags) // do not remove new found flags when not wished
615 dvb_service->m_flags &= ~eDVBService::dxNewFound;
619 db->addService(service->first, service->second);
620 service->second->m_flags |= eDVBService::dxNewFound;
625 RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescriptionSection &sdt)
627 const ServiceDescriptionList &services = *sdt.getDescriptions();
628 SCAN_eDebug("ONID: %04x", sdt.getOriginalNetworkId());
629 eDVBChannelID chid(dvbnamespace, sdt.getTransportStreamId(), sdt.getOriginalNetworkId());
631 /* save correct CHID for this channel */
632 m_chid_current = chid;
634 for (ServiceDescriptionConstIterator s(services.begin()); s != services.end(); ++s)
636 SCAN_eDebugNoNewLine("SID %04x: ", (*s)->getServiceId());
638 eServiceReferenceDVB ref;
639 ePtr<eDVBService> service = new eDVBService;
642 ref.setServiceID((*s)->getServiceId());
644 for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
645 desc != (*s)->getDescriptors()->end(); ++desc)
646 if ((*desc)->getTag() == SERVICE_DESCRIPTOR)
647 ref.setServiceType(((ServiceDescriptor&)**desc).getServiceType());
649 for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
650 desc != (*s)->getDescriptors()->end(); ++desc)
652 switch ((*desc)->getTag())
654 case SERVICE_DESCRIPTOR:
656 ServiceDescriptor &d = (ServiceDescriptor&)**desc;
657 service->m_service_name = convertDVBUTF8(d.getServiceName());
658 service->genSortName();
660 service->m_provider_name = convertDVBUTF8(d.getServiceProviderName());
661 SCAN_eDebug("name '%s', provider_name '%s'", service->m_service_name.c_str(), service->m_provider_name.c_str());
664 case CA_IDENTIFIER_DESCRIPTOR:
666 CaIdentifierDescriptor &d = (CaIdentifierDescriptor&)**desc;
667 const CaSystemIdList &caids = *d.getCaSystemIds();
668 SCAN_eDebugNoNewLine("CA ");
669 for (CaSystemIdList::const_iterator i(caids.begin()); i != caids.end(); ++i)
671 SCAN_eDebugNoNewLine("%04x ", *i);
672 service->m_ca.push_front(*i);
678 SCAN_eDebug("descr<%x>", (*desc)->getTag());
683 std::pair<std::map<eServiceReferenceDVB, ePtr<eDVBService> >::iterator, bool> i = m_new_services.insert(std::pair<eServiceReferenceDVB, ePtr<eDVBService> >(ref, service));
687 m_last_service = i.first;
688 m_event(evtNewService);
694 RESULT eDVBScan::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
696 connection = new eConnection(this, m_event.connect(event));
700 void eDVBScan::getStats(int &transponders_done, int &transponders_total, int &services)
702 transponders_done = m_ch_scanned.size() + m_ch_unavailable.size();
703 transponders_total = m_ch_toScan.size() + transponders_done;
704 services = m_new_services.size();
707 void eDVBScan::getLastServiceName(std::string &last_service_name)
709 if (m_last_service == m_new_services.end())
710 last_service_name = "";
712 last_service_name = m_last_service->second->m_service_name;