fix "Clearall before scan" handling
[enigma2.git] / lib / dvb / scan.cpp
index 4b31aadaed0ecdf2b8c308a5b3edb3f1d2597ae9..605128e7c8670caa6674aa81425840183d8a3bbb 100644 (file)
@@ -1,17 +1,21 @@
 #include <lib/dvb/idvb.h>
-#include <lib/dvb_si/sdt.h>
-#include <lib/dvb_si/nit.h>
-#include <lib/dvb_si/bat.h>
-#include <lib/dvb_si/descriptor_tag.h>
-#include <lib/dvb_si/service_descriptor.h>
-#include <lib/dvb_si/satellite_delivery_system_descriptor.h>
-#include <lib/dvb_si/ca_identifier_descriptor.h>
+#include <dvbsi++/service_description_section.h>
+#include <dvbsi++/network_information_section.h>
+#include <dvbsi++/bouquet_association_section.h>
+#include <dvbsi++/descriptor_tag.h>
+#include <dvbsi++/service_descriptor.h>
+#include <dvbsi++/satellite_delivery_system_descriptor.h>
+#include <dvbsi++/terrestrial_delivery_system_descriptor.h>
+#include <dvbsi++/cable_delivery_system_descriptor.h>
+#include <dvbsi++/ca_identifier_descriptor.h>
 #include <lib/dvb/specs.h>
 #include <lib/dvb/esection.h>
 #include <lib/dvb/scan.h>
 #include <lib/dvb/frontend.h>
 #include <lib/base/eerror.h>
+#include <lib/base/estring.h>
 #include <errno.h>
+#include <set>
 
 #define SCAN_eDebug(x...) eDebug(x)
 #define SCAN_eDebugNoNewLine(x...) eDebugNoNewLine(x)
@@ -29,29 +33,28 @@ eDVBScan::~eDVBScan()
 {
 }
 
-int eDVBScan::isValidONIDTSID(eOriginalNetworkID onid, eTransportStreamID tsid)
+int eDVBScan::isValidONIDTSID(int orbital_position, eOriginalNetworkID onid, eTransportStreamID tsid)
 {
        switch (onid.get())
        {
        case 0:
-       case 0xFFFF:
        case 0x1111:
                return 0;
        case 1:
-               return tsid>1;
+               return orbital_position == 192;
        case 0x00B1:
                return tsid != 0x00B0;
        case 0x0002:
-               return tsid != 0x07E8;
+               return abs(orbital_position-282) < 6;
        default:
-               return 1;
+               return onid.get() < 0xFF00;
        }
 }
 
 eDVBNamespace eDVBScan::buildNamespace(eOriginalNetworkID onid, eTransportStreamID tsid, unsigned long hash)
 {
                // on valid ONIDs, ignore frequency ("sub network") part
-       if (isValidONIDTSID(onid, tsid))
+       if (isValidONIDTSID((hash >> 16) & 0xFFFF, onid, tsid))
                hash &= ~0xFFFF;
        return eDVBNamespace(hash);
 }
@@ -68,11 +71,12 @@ void eDVBScan::stateChange(iDVBChannel *ch)
        {
                startFilter();
                m_channel_state = state;
-       } else if (state == iDVBChannel::state_unavailable)
+       } else if (state == iDVBChannel::state_failed)
        {
                m_ch_unavailable.push_back(m_ch_current);
                nextChannel();
        }
+                       /* unavailable will timeout, anyway. */
 }
 
 RESULT eDVBScan::nextChannel()
@@ -81,7 +85,17 @@ RESULT eDVBScan::nextChannel()
 
        m_SDT = 0; m_BAT = 0; m_NIT = 0;
 
-       m_ready = readyBAT;
+       m_ready = 0;
+       
+               /* check what we need */
+       m_ready_all = readySDT;
+       
+       if (m_flags & scanNetworkSearch)
+               m_ready_all |= readyNIT;
+       
+       if (m_flags & scanSearchBAT)
+               m_ready_all |= readyBAT;
+       
        if (m_ch_toScan.empty())
        {
                eDebug("no channels left to scan.");
@@ -93,6 +107,7 @@ RESULT eDVBScan::nextChannel()
        }
        
        m_ch_current = m_ch_toScan.front();
+       
        m_ch_toScan.pop_front();
        
        if (m_channel->getFrontend(fe))
@@ -100,7 +115,18 @@ RESULT eDVBScan::nextChannel()
                m_event(evtFail);
                return -ENOTSUP;
        }
-       
+
+       int fetype;
+       fe->getFrontendType(fetype);
+       if ( fetype == iDVBFrontend::feSatellite)
+       {
+               eDVBFrontendParametersSatellite p;
+               m_ch_current->getDVBS(p);
+               m_chid_current = eDVBChannelID(p.orbital_position << 16, -1, -1);
+       }
+       else
+               m_chid_current = eDVBChannelID();
+
        m_channel_state = iDVBChannel::state_idle;
        if (fe->tune(*m_ch_current))
        {
@@ -117,21 +143,35 @@ RESULT eDVBScan::startFilter()
 {
        assert(m_demux);
        
-       m_SDT = new eTable<ServiceDescriptionTable>();
-       if (m_SDT->start(m_demux, eDVBSDTSpec()))
-               return -1;
-       CONNECT(m_SDT->tableReady, eDVBScan::SDTready);
+                       /* only start required filters filter */
+       
+       m_SDT = 0;
+
+       if (m_ready_all & readySDT)
+       {
+               m_SDT = new eTable<ServiceDescriptionSection>();
+               if (m_SDT->start(m_demux, eDVBSDTSpec()))
+                       return -1;
+               CONNECT(m_SDT->tableReady, eDVBScan::SDTready);
+       }
 
        m_NIT = 0;
-       m_NIT = new eTable<NetworkInformationTable>();
-       if (m_NIT->start(m_demux, eDVBNITSpec()))
-               return -1;
-       CONNECT(m_NIT->tableReady, eDVBScan::NITready);
-       
-       m_BAT = new eTable<BouquetAssociationTable>();
-       if (m_BAT->start(m_demux, eDVBBATSpec()))
-               return -1;
-       CONNECT(m_BAT->tableReady, eDVBScan::BATready);
+       if (m_ready_all & readyNIT)
+       {
+               m_NIT = new eTable<NetworkInformationSection>();
+               if (m_NIT->start(m_demux, eDVBNITSpec()))
+                       return -1;
+               CONNECT(m_NIT->tableReady, eDVBScan::NITready);
+       }
+
+       m_BAT = 0;
+       if (m_ready_all & readyBAT)
+       {
+               m_BAT = new eTable<BouquetAssociationSection>();
+               if (m_BAT->start(m_demux, eDVBBATSpec()))
+                       return -1;
+               CONNECT(m_BAT->tableReady, eDVBScan::BATready);
+       }
        
        return 0;
 }
@@ -163,12 +203,15 @@ void eDVBScan::BATready(int err)
        channelDone();
 }
 
-void eDVBScan::addChannel(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
+void eDVBScan::addKnownGoodChannel(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
 {
                /* add it to the list of known channels. */
        if (chid)
                m_new_channels.insert(std::pair<eDVBChannelID,ePtr<iDVBFrontendParameters> >(chid, feparm));
-       
+}
+
+void eDVBScan::addChannelToScan(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
+{
                /* check if we don't already have that channel ... */
                
                /* ... in the list of channels to scan */
@@ -191,7 +234,7 @@ void eDVBScan::addChannel(const eDVBChannelID &chid, iDVBFrontendParameters *fep
                return;
 
                /* otherwise, add it to the todo list. */
-       m_ch_toScan.push_back(feparm);
+       m_ch_toScan.push_front(feparm); // better.. then the rotor not turning wild from east to west :)
 }
 
 int eDVBScan::sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *ch2) const
@@ -217,7 +260,7 @@ void eDVBScan::channelDone()
                        hash);
                
                SCAN_eDebug("SDT: ");
-               ServiceDescriptionTableConstIterator i;
+               std::vector<ServiceDescriptionSection*>::const_iterator i;
                for (i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i)
                        processSDT(dvbnamespace, **i);
                m_ready &= ~validSDT;
@@ -226,10 +269,10 @@ void eDVBScan::channelDone()
        if (m_ready & validNIT)
        {
                SCAN_eDebug("dumping NIT");
-               NetworkInformationTableConstIterator i;
+               std::vector<NetworkInformationSection*>::const_iterator i;
                for (i = m_NIT->getSections().begin(); i != m_NIT->getSections().end(); ++i)
                {
-                       const TransportStreamInfoVector &tsinfovec = *(*i)->getTsInfo();
+                       const TransportStreamInfoList &tsinfovec = *(*i)->getTsInfo();
                        
                        for (TransportStreamInfoConstIterator tsinfo(tsinfovec.begin()); 
                                tsinfo != tsinfovec.end(); ++tsinfo)
@@ -245,20 +288,43 @@ void eDVBScan::channelDone()
                                {
                                        switch ((*desc)->getTag())
                                        {
-//                                     case SERVICE_LIST_DESCRIPTOR:
+                                       case CABLE_DELIVERY_SYSTEM_DESCRIPTOR:
+                                       {
+                                               CableDeliverySystemDescriptor &d = (CableDeliverySystemDescriptor&)**desc;
+                                               ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
+                                               eDVBFrontendParametersCable cable;
+                                               cable.set(d);
+                                               feparm->setDVBC(cable);
+
+                                               unsigned long hash=0;
+                                               feparm->getHash(hash);
+                                               eDVBNamespace ns = buildNamespace(onid, tsid, hash);
+
+                                               addChannelToScan(
+                                                       eDVBChannelID(ns, tsid, onid),
+                                                       feparm);
+                                               break;
+                                       }
+                                       case TERRESTRIAL_DELIVERY_SYSTEM_DESCRIPTOR:
+                                       {
+                                               TerrestrialDeliverySystemDescriptor &d = (TerrestrialDeliverySystemDescriptor&)**desc;
+                                               ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
+                                               eDVBFrontendParametersTerrestrial terr;
+                                               terr.set(d);
+                                               feparm->setDVBT(terr);
+
+                                               unsigned long hash=0;
+                                               feparm->getHash(hash);
+                                               eDVBNamespace ns = buildNamespace(onid, tsid, hash);
+
+                                               addChannelToScan(
+                                                       eDVBChannelID(ns, tsid, onid),
+                                                       feparm);
+                                               break;
+                                       }
                                        case SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR:
                                        {
                                                SatelliteDeliverySystemDescriptor &d = (SatelliteDeliverySystemDescriptor&)**desc;
-                                               SCAN_eDebug("%d kHz, %d%d%d.%d%c %s MOD:%d %d symb/s, fec %d", 
-                                                               d.getFrequency(), 
-                                                               (d.getOrbitalPosition()>>12)&0xF,
-                                                               (d.getOrbitalPosition()>>8)&0xF,
-                                                               (d.getOrbitalPosition()>>4)&0xF,
-                                                               d.getOrbitalPosition()&0xF, d.getWestEastFlag()?'E':'W',
-                                                               d.getPolarization() ? "hor" : "vert",
-                                                               d.getModulation(), d.getSymbolRate(), d.getFecInner());
-                                               
-                                                       /* some sanity checking: below 100MHz is invalid */
                                                if (d.getFrequency() < 10000)
                                                        break;
                                                
@@ -270,10 +336,16 @@ void eDVBScan::channelDone()
                                                feparm->getHash(hash);
                                                
                                                eDVBNamespace ns = buildNamespace(onid, tsid, hash);
-
-                                               addChannel(
-                                                               eDVBChannelID(ns, tsid, onid),
-                                                               feparm);
+                                               
+                                               if ( m_chid_current.dvbnamespace.get() != -1 &&
+                                                       ((ns.get() ^ m_chid_current.dvbnamespace.get()) & 0xFFFF0000))
+                                                       eDebug("dropping this transponder, it's on another satellite.");
+                                               else
+                                               {
+                                                       addChannelToScan(
+                                                                       eDVBChannelID(ns, tsid, onid),
+                                                                       feparm);
+                                               }
                                                break;
                                        }
                                        default:
@@ -287,26 +359,151 @@ void eDVBScan::channelDone()
                m_ready &= ~validNIT;
        }
        
-       if ((m_ready  & readyAll) != readyAll)
+       if ((m_ready  & m_ready_all) != m_ready_all)
                return;
        SCAN_eDebug("channel done!");
+       
+               /* if we had services on this channel, we declare
+                  this channels as "known good". add it.
+                  
+                  (TODO: not yet implemented)
+                  a NIT entry could have possible overridden
+                  our frontend data with more exact data.
+                  
+                  (TODO: not yet implemented)
+                  the tuning process could have lead to more
+                  exact data than the user entered.
+                  
+                  The channel id was probably corrected
+                  by the data written in the SDT. this is
+                  important, as "initial transponder lists"
+                  usually don't have valid CHIDs (and that's
+                  good).
+                  
+                  These are the reasons for adding the transponder
+                  here, and not before.
+               */
+       
+       if (!m_chid_current)
+               eWarning("SCAN: the current channel's ID was not corrected - not adding channel.");
+       else
+               addKnownGoodChannel(m_chid_current, m_ch_current);
+       
        m_ch_scanned.push_back(m_ch_current);
        nextChannel();
 }
 
-void eDVBScan::start(const std::list<ePtr<iDVBFrontendParameters> > &known_transponders)
+void eDVBScan::start(const eSmartPtrList<iDVBFrontendParameters> &known_transponders, int flags)
 {
+       m_flags = flags;
        m_ch_toScan.clear();
        m_ch_scanned.clear();
        m_ch_unavailable.clear();
        m_new_channels.clear();
        m_new_services.clear();
-       m_ch_toScan.insert(m_ch_toScan.end(), known_transponders.begin(), known_transponders.end());
+       m_last_service = m_new_services.end();
+
+       for (eSmartPtrList<iDVBFrontendParameters>::const_iterator i(known_transponders.begin()); i != known_transponders.end(); ++i)
+       {
+               bool exist=false;
+               for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator ii(m_ch_toScan.begin()); ii != m_ch_toScan.end(); ++ii)
+               {
+                       if (sameChannel(*i, *ii))
+                       {
+                               exist=true;
+                               break;
+                       }
+               }
+               if (!exist)
+                       m_ch_toScan.push_back(*i);
+       }
+
        nextChannel();
 }
 
 void eDVBScan::insertInto(iDVBChannelList *db)
 {
+       if (m_flags & scanRemoveServices)
+       {
+               bool clearTerrestrial=false;
+               bool clearCable=false;
+               std::set<unsigned int> scanned_sat_positions;
+               
+               std::list<ePtr<iDVBFrontendParameters> >::iterator it(m_ch_scanned.begin());
+               for (;it != m_ch_scanned.end(); ++it)
+               {
+                       int system;
+                       (*it)->getSystem(system);
+                       switch(system)
+                       {
+                               case iDVBFrontend::feSatellite:
+                               {
+                                       eDVBFrontendParametersSatellite sat_parm;
+                                       (*it)->getDVBS(sat_parm);
+                                       scanned_sat_positions.insert(sat_parm.orbital_position);
+                                       break;
+                               }
+                               case iDVBFrontend::feTerrestrial:
+                               {
+                                       clearTerrestrial=true;
+                                       break;
+                               }
+                               case iDVBFrontend::feCable:
+                               {
+                                       clearCable=true;
+                                       break;
+                               }
+                       }
+               }
+
+               for (it=m_ch_unavailable.begin();it != m_ch_unavailable.end(); ++it)
+               {
+                       int system;
+                       (*it)->getSystem(system);
+                       switch(system)
+                       {
+                               case iDVBFrontend::feSatellite:
+                               {
+                                       eDVBFrontendParametersSatellite sat_parm;
+                                       (*it)->getDVBS(sat_parm);
+                                       scanned_sat_positions.insert(sat_parm.orbital_position);
+                                       break;
+                               }
+                               case iDVBFrontend::feTerrestrial:
+                               {
+                                       clearTerrestrial=true;
+                                       break;
+                               }
+                               case iDVBFrontend::feCable:
+                               {
+                                       clearCable=true;
+                                       break;
+                               }
+                       }
+               }
+
+               if (clearTerrestrial)
+               {
+                       eDVBChannelID chid;
+                       chid.dvbnamespace=0xEEEE0000;
+                       db->removeServices(chid);
+               }
+               if (clearCable)
+               {
+                       eDVBChannelID chid;
+                       chid.dvbnamespace=0xFFFF0000;
+                       db->removeServices(chid);
+               }
+               for (std::set<unsigned int>::iterator x(scanned_sat_positions.begin()); x != scanned_sat_positions.end(); ++x)
+               {
+                       eDVBChannelID chid;
+                       if (m_flags & scanDontRemoveFeeds)
+                               chid.dvbnamespace = eDVBNamespace((*x)<<16);
+                       eDebug("remove %d %08x", *x, chid.dvbnamespace.get());
+                       db->removeServices(chid, *x);
+               }
+       }
+
        for (std::map<eDVBChannelID, ePtr<iDVBFrontendParameters> >::const_iterator 
                        ch(m_new_channels.begin()); ch != m_new_channels.end(); ++ch)
                db->addChannelToList(ch->first, ch->second);
@@ -321,12 +518,16 @@ void eDVBScan::insertInto(iDVBChannelList *db)
        }
 }
 
-RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescriptionTable &sdt)
+RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescriptionSection &sdt)
 {
-       const ServiceDescriptionVector &services = *sdt.getDescriptions();
+       const ServiceDescriptionList &services = *sdt.getDescriptions();
        SCAN_eDebug("ONID: %04x", sdt.getOriginalNetworkId());
        eDVBChannelID chid(dvbnamespace, sdt.getTransportStreamId(), sdt.getOriginalNetworkId());
        
+               /* save correct CHID for this channel if this is an ACTUAL_SDT */
+       if (sdt.getTableId() == TID_SDT_ACTUAL)
+               m_chid_current = chid;
+       
        for (ServiceDescriptionConstIterator s(services.begin()); s != services.end(); ++s)
        {
                SCAN_eDebugNoNewLine("SID %04x: ", (*s)->getServiceId());
@@ -350,17 +551,19 @@ RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescription
                        case SERVICE_DESCRIPTOR:
                        {
                                ServiceDescriptor &d = (ServiceDescriptor&)**desc;
-                               SCAN_eDebug("name '%s', provider_name '%s'", d.getServiceName().c_str(), d.getServiceProviderName().c_str());
-                               service->m_service_name = d.getServiceName();
-                               service->m_provider_name = d.getServiceProviderName();
+                               service->m_service_name = convertDVBUTF8(d.getServiceName());
+                               service->genSortName();
+
+                               service->m_provider_name = convertDVBUTF8(d.getServiceProviderName());
+                               SCAN_eDebug("name '%s', provider_name '%s'", service->m_service_name.c_str(), service->m_provider_name.c_str());
                                break;
                        }
                        case CA_IDENTIFIER_DESCRIPTOR:
                        {
                                CaIdentifierDescriptor &d = (CaIdentifierDescriptor&)**desc;
-                               const CaSystemIdVector &caids = *d.getCaSystemIds();
+                               const CaSystemIdList &caids = *d.getCaSystemIds();
                                SCAN_eDebugNoNewLine("CA ");
-                               for (CaSystemIdVector::const_iterator i(caids.begin()); i != caids.end(); ++i)
+                               for (CaSystemIdList::const_iterator i(caids.begin()); i != caids.end(); ++i)
                                {
                                        SCAN_eDebugNoNewLine("%04x ", *i);
                                        service->m_ca.insert(*i);
@@ -374,7 +577,13 @@ RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescription
                        }
                }
                
-               m_new_services.insert(std::pair<eServiceReferenceDVB, ePtr<eDVBService> >(ref, service));               
+               std::pair<std::map<eServiceReferenceDVB, ePtr<eDVBService> >::iterator, bool> i = m_new_services.insert(std::pair<eServiceReferenceDVB, ePtr<eDVBService> >(ref, service));
+               
+               if (i.second)
+               {
+                       m_last_service = i.first;
+                       m_event(evtNewService);
+               }
        }
        return 0;
 }
@@ -391,3 +600,11 @@ void eDVBScan::getStats(int &transponders_done, int &transponders_total, int &se
        transponders_total = m_ch_toScan.size() + transponders_done;
        services = m_new_services.size();
 }
+
+void eDVBScan::getLastServiceName(std::string &last_service_name)
+{
+       if (m_last_service == m_new_services.end())
+               last_service_name = "";
+       else
+               last_service_name = m_last_service->second->m_service_name;
+}