fix scan
[enigma2.git] / lib / dvb / scan.cpp
index 1c98dce0a613471744eebf3523da9af30601afcc..89c4735ad1cd914de8158aa417551767ce71cdd5 100644 (file)
@@ -5,6 +5,8 @@
 #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/base/estring.h>
 #include <errno.h>
 
-#define SCAN_eDebug(x...) eDebug(x)
-#define SCAN_eDebugNoNewLine(x...) eDebugNoNewLine(x)
+static bool scan_debug;
+#define SCAN_eDebug(x...) do { if (scan_debug) eDebug(x); } while(0)
+#define SCAN_eDebugNoNewLine(x...) do { if (scan_debug) eDebugNoNewLine(x); } while(0)
 
 DEFINE_REF(eDVBScan);
 
-eDVBScan::eDVBScan(iDVBChannel *channel): m_channel(channel)
+eDVBScan::eDVBScan(iDVBChannel *channel, bool debug)
+       :m_channel(channel), m_channel_state(iDVBChannel::state_idle)
+       ,m_ready(0), m_ready_all(readySDT), m_flags(0)
 {
+       scan_debug=debug;
        if (m_channel->getDemux(m_demux))
                SCAN_eDebug("scan: failed to allocate demux!");
        m_channel->connectStateChange(slot(*this, &eDVBScan::stateChange), m_stateChanged_connection);
@@ -30,29 +36,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);
 }
@@ -96,16 +101,15 @@ RESULT eDVBScan::nextChannel()
        
        if (m_ch_toScan.empty())
        {
-               eDebug("no channels left to scan.");
-               eDebug("%d channels scanned, %d were unavailable.", 
+               SCAN_eDebug("no channels left to scan.");
+               SCAN_eDebug("%d channels scanned, %d were unavailable.", 
                                m_ch_scanned.size(), m_ch_unavailable.size());
-               eDebug("%d channels in database.", m_new_channels.size());
+               SCAN_eDebug("%d channels in database.", m_new_channels.size());
                m_event(evtFinish);
                return -ENOENT;
        }
        
        m_ch_current = m_ch_toScan.front();
-       m_chid_current = eDVBChannelID();
        
        m_ch_toScan.pop_front();
        
@@ -114,7 +118,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))
        {
@@ -222,7 +237,7 @@ void eDVBScan::addChannelToScan(const eDVBChannelID &chid, iDVBFrontendParameter
                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
@@ -240,6 +255,11 @@ void eDVBScan::channelDone()
        if (m_ready & validSDT)
        {
                unsigned long hash = 0;
+
+               // m_ch_current is not set, when eDVBScan is just used for a SDT update
+               if (!m_ch_current)
+                       m_channel->getCurrentFrontendParameters(m_ch_current);
+
                m_ch_current->getHash(hash);
                
                eDVBNamespace dvbnamespace = buildNamespace(
@@ -276,20 +296,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;
                                                
@@ -302,8 +345,9 @@ void eDVBScan::channelDone()
                                                
                                                eDVBNamespace ns = buildNamespace(onid, tsid, hash);
                                                
-                                               if (m_chid_current && ((ns.get() ^ m_chid_current.dvbnamespace.get()) & 0xFFFF0000))
-                                                       eDebug("dropping this transponder, it's on another satellite.");
+                                               if ( m_chid_current.dvbnamespace.get() != -1 &&
+                                                       ((ns.get() ^ m_chid_current.dvbnamespace.get()) & 0xFFFF0000))
+                                                       SCAN_eDebug("dropping this transponder, it's on another satellite.");
                                                else
                                                {
                                                        addChannelToScan(
@@ -347,7 +391,7 @@ void eDVBScan::channelDone()
                   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
@@ -365,12 +409,109 @@ void eDVBScan::start(const eSmartPtrList<iDVBFrontendParameters> &known_transpon
        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)
+void eDVBScan::insertInto(iDVBChannelList *db, bool dontRemoveOldFlags)
 {
+       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);
@@ -379,9 +520,25 @@ void eDVBScan::insertInto(iDVBChannelList *db)
        {
                ePtr<eDVBService> dvb_service;
                if (!db->getService(service->first, dvb_service))
-                       *dvb_service = *service->second;
+               {
+                       if (dvb_service->m_flags & eDVBService::dxNoSDT)
+                               continue;
+                       if (!(dvb_service->m_flags & eDVBService::dxHoldName))
+                       {
+                               dvb_service->m_service_name = service->second->m_service_name;
+                               dvb_service->m_service_name_sort = service->second->m_service_name_sort;
+                       }
+                       dvb_service->m_provider_name = service->second->m_provider_name;
+                       if (service->second->m_ca.size())
+                               dvb_service->m_ca = service->second->m_ca;
+                       if (!dontRemoveOldFlags) // do not remove new found flags when not wished
+                               dvb_service->m_flags &= ~eDVBService::dxNewFound;
+               }
                else
+               {
                        db->addService(service->first, service->second);
+                       service->second->m_flags |= eDVBService::dxNewFound;
+               }
        }
 }
 
@@ -433,7 +590,7 @@ RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescription
                                for (CaSystemIdList::const_iterator i(caids.begin()); i != caids.end(); ++i)
                                {
                                        SCAN_eDebugNoNewLine("%04x ", *i);
-                                       service->m_ca.insert(*i);
+                                       service->m_ca.push_front(*i);
                                }
                                SCAN_eDebug("");
                                break;
@@ -444,7 +601,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;
 }
@@ -461,3 +624,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;
+}