use eEnv::resolve() / eEnv.resolve()
[vuplus_dvbapp] / lib / dvb / scan.cpp
index 17c6b5c..d559614 100644 (file)
 #include <lib/dvb/idvb.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 <dvbsi++/registration_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/eenv.h>
 #include <lib/base/eerror.h>
 #include <lib/base/estring.h>
+#include <lib/python/python.h>
 #include <errno.h>
-#include <set>
 
-#define SCAN_eDebug(x...) eDebug(x)
-#define SCAN_eDebugNoNewLine(x...) eDebugNoNewLine(x)
+#define SCAN_eDebug(x...) do { if (m_scan_debug) eDebug(x); } while(0)
+#define SCAN_eDebugNoNewLine(x...) do { if (m_scan_debug) eDebugNoNewLine(x); } while(0)
 
 DEFINE_REF(eDVBScan);
 
-eDVBScan::eDVBScan(iDVBChannel *channel): m_channel(channel)
+eDVBScan::eDVBScan(iDVBChannel *channel, bool usePAT, bool debug)
+       :m_channel(channel), m_channel_state(iDVBChannel::state_idle)
+       ,m_ready(0), m_ready_all(usePAT ? (readySDT|readyPAT) : readySDT)
+       ,m_pmt_running(false), m_abort_current_pmt(false), m_flags(0)
+       ,m_usePAT(usePAT), m_scan_debug(debug), m_show_add_tsid_onid_check_failed_msg(true)
 {
-       m_ready = m_flags = 0;
-       m_ready_all = readySDT;
-       m_channel_state = iDVBChannel::state_idle;
-
        if (m_channel->getDemux(m_demux))
                SCAN_eDebug("scan: failed to allocate demux!");
        m_channel->connectStateChange(slot(*this, &eDVBScan::stateChange), m_stateChanged_connection);
+       std::string filename = eEnv::resolve("${sysconfdir}/scan_tp_valid_check.py");
+       FILE *f = fopen(filename.c_str(), "r");
+       if (f)
+       {
+               char code[16384];
+               size_t rd = fread(code, 1, 16383, f);
+               if (rd)
+               {
+                       code[rd]=0;
+                       m_additional_tsid_onid_check_func = Py_CompileString(code, filename.c_str(), Py_file_input);
+               }
+               fclose(f);
+       }
 }
 
 eDVBScan::~eDVBScan()
 {
+       if (m_additional_tsid_onid_check_func)
+               Py_DECREF(m_additional_tsid_onid_check_func);
 }
 
 int eDVBScan::isValidONIDTSID(int orbital_position, eOriginalNetworkID onid, eTransportStreamID tsid)
 {
+       int ret;
        switch (onid.get())
        {
        case 0:
        case 0x1111:
-               return 0;
+               ret=0;
+               break;
+       case 0x13E:  // workaround for 11258H and 11470V on hotbird with same ONID/TSID (0x13E/0x578)
+               ret = orbital_position != 130 || tsid != 0x578;
+               break;
        case 1:
-               return orbital_position == 192;
+               ret = orbital_position == 192;
+               break;
        case 0x00B1:
-               return tsid != 0x00B0;
+               ret = tsid != 0x00B0;
+               break;
+       case 0x00eb:
+               ret = tsid != 0x4321;
+               break;
        case 0x0002:
-               return abs(orbital_position-282) < 6;
+               ret = abs(orbital_position-282) < 6 && tsid != 2019;
+               // 12070H and 10936V have same tsid/onid.. but even the same services are provided
+               break;
+       case 0x2000:
+               ret = tsid != 0x1000;
+               break;
+       case 0x5E: // Sirius 4.8E 12322V and 12226H
+               ret = abs(orbital_position-48) < 3 && tsid != 1;
+               break;
+       case 10100: // Eutelsat W7 36.0E 11644V and 11652V
+               ret = orbital_position != 360 || tsid != 10187;
+               break;
+       case 42: // Tuerksat 42.0E
+               ret = orbital_position != 420 || (
+                   tsid != 8 && // 11830V 12729V
+                   tsid != 5 && // 12679V 12685H
+                   tsid != 2 && // 11096V 12015H
+                   tsid != 55); // 11996V 11716V
+               break;
+       case 100: // Intelsat 10 68.5E 3808V 3796V 4012V, Amos 4.0W 10723V 11571H
+               ret = (orbital_position != 685 && orbital_position != 3560) || tsid != 1;
+               break;
+       case 70: // Thor 0.8W 11862H 12341V
+               ret = abs(orbital_position-3592) < 3 && tsid != 46;
+               break;
+       case 32: // NSS 806 (40.5W) 4059R, 3774L
+               ret = orbital_position != 3195 || tsid != 21;
+               break;
        default:
-               return onid.get() < 0xFF00;
+               ret = onid.get() < 0xFF00;
+               break;
        }
+       if (ret && m_additional_tsid_onid_check_func)
+       {
+               bool failed = true;
+               ePyObject dict = PyDict_New();
+               extern void PutToDict(ePyObject &, const char *, long);
+               PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins());
+               PutToDict(dict, "orbpos", orbital_position);
+               PutToDict(dict, "tsid", tsid.get());
+               PutToDict(dict, "onid", onid.get());
+               ePyObject r = PyEval_EvalCode((PyCodeObject*)(PyObject*)m_additional_tsid_onid_check_func, dict, dict);
+               if (r)
+               {
+                       ePyObject o = PyDict_GetItemString(dict, "ret");
+                       if (o)
+                       {
+                               if (PyInt_Check(o))
+                               {
+                                       ret = PyInt_AsLong(o);
+                                       failed = false;
+                               }
+                       }
+                       Py_DECREF(r);
+               }
+               if (failed && m_show_add_tsid_onid_check_failed_msg)
+               {
+                       eDebug("execing /etc/enigma2/scan_tp_valid_check failed!\n"
+                               "usable global variables in scan_tp_valid_check.py are 'orbpos', 'tsid', 'onid'\n"
+                               "the return value must be stored in a global var named 'ret'");
+                       m_show_add_tsid_onid_check_failed_msg=false;
+               }
+               Py_DECREF(dict);
+       }
+       return ret;
 }
 
 eDVBNamespace eDVBScan::buildNamespace(eOriginalNetworkID onid, eTransportStreamID tsid, unsigned long hash)
@@ -87,10 +172,12 @@ RESULT eDVBScan::nextChannel()
 {
        ePtr<iDVBFrontend> fe;
 
-       m_SDT = 0; m_BAT = 0; m_NIT = 0;
+       m_SDT = 0; m_PAT = 0; m_BAT = 0; m_NIT = 0, m_PMT = 0;
 
        m_ready = 0;
-       
+
+       m_pat_tsid = eTransportStreamID();
+
                /* check what we need */
        m_ready_all = readySDT;
        
@@ -99,7 +186,10 @@ RESULT eDVBScan::nextChannel()
        
        if (m_flags & scanSearchBAT)
                m_ready_all |= readyBAT;
-       
+
+       if (m_usePAT)
+               m_ready_all |= readyPAT;
+
        if (m_ch_toScan.empty())
        {
                SCAN_eDebug("no channels left to scan.");
@@ -120,69 +210,119 @@ RESULT eDVBScan::nextChannel()
                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_chid_current = eDVBChannelID();
 
        m_channel_state = iDVBChannel::state_idle;
+
        if (fe->tune(*m_ch_current))
-       {
                return nextChannel();
-               m_event(evtFail);
-               return -EINVAL;
-       }
-               
+
        m_event(evtUpdate);
        return 0;
 }
 
 RESULT eDVBScan::startFilter()
 {
-       assert(m_demux);
-       
+       bool startSDT=true;
+       ASSERT(m_demux);
+
                        /* only start required filters filter */
-       
-       m_SDT = 0;
 
-       if (m_ready_all & readySDT)
+       if (m_ready_all & readyPAT)
+               startSDT = m_ready & readyPAT;
+
+       // m_ch_current is not set, when eDVBScan is just used for a SDT update
+       if (!m_ch_current)
        {
-               m_SDT = new eTable<ServiceDescriptionSection>();
-               if (m_SDT->start(m_demux, eDVBSDTSpec()))
-                       return -1;
-               CONNECT(m_SDT->tableReady, eDVBScan::SDTready);
+               unsigned int channelFlags;
+               m_channel->getCurrentFrontendParameters(m_ch_current);
+               m_ch_current->getFlags(channelFlags);
+               if (channelFlags & iDVBFrontendParameters::flagOnlyFree)
+                       m_flags |= scanOnlyFree;
        }
 
-       m_NIT = 0;
-       if (m_ready_all & readyNIT)
+       m_SDT = 0;
+       if (startSDT && (m_ready_all & readySDT))
        {
-               m_NIT = new eTable<NetworkInformationSection>();
-               if (m_NIT->start(m_demux, eDVBNITSpec()))
+               m_SDT = new eTable<ServiceDescriptionSection>(m_scan_debug);
+               int tsid=-1;
+               if (m_ready & readyPAT && m_ready & validPAT)
+               {
+                       std::vector<ProgramAssociationSection*>::const_iterator i =
+                               m_PAT->getSections().begin();
+                       ASSERT(i != m_PAT->getSections().end());
+                       tsid = (*i)->getTableIdExtension(); // in PAT this is the transport stream id
+                       m_pat_tsid = eTransportStreamID(tsid);
+                       for (; i != m_PAT->getSections().end(); ++i)
+                       {
+                               const ProgramAssociationSection &pat = **i;
+                               ProgramAssociationConstIterator program = pat.getPrograms()->begin();
+                               for (; program != pat.getPrograms()->end(); ++program)
+                                       m_pmts_to_read.insert(std::pair<unsigned short, service>((*program)->getProgramNumber(), service((*program)->getProgramMapPid())));
+                       }
+                       m_PMT = new eTable<ProgramMapSection>(m_scan_debug);
+                       CONNECT(m_PMT->tableReady, eDVBScan::PMTready);
+                       PMTready(-2);
+                       // KabelBW HACK ... on 618Mhz and 626Mhz the transport stream id in PAT and SDT is different
+
+                       {
+                               int type;
+                               m_ch_current->getSystem(type);
+                               if (type == iDVBFrontend::feCable)
+                               {
+                                       eDVBFrontendParametersCable parm;
+                                       m_ch_current->getDVBC(parm);
+                                       if ((tsid == 0x00d7 && abs(parm.frequency-618000) < 2000) ||
+                                               (tsid == 0x00d8 && abs(parm.frequency-626000) < 2000))
+                                               tsid = -1;
+                               }
+                       }
+               }
+               if (tsid == -1)
+               {
+                       if (m_SDT->start(m_demux, eDVBSDTSpec()))
+                               return -1;
+               }
+               else if (m_SDT->start(m_demux, eDVBSDTSpec(tsid, true)))
                        return -1;
-               CONNECT(m_NIT->tableReady, eDVBScan::NITready);
+               CONNECT(m_SDT->tableReady, eDVBScan::SDTready);
        }
 
-       m_BAT = 0;
-       if (m_ready_all & readyBAT)
+       if (!(m_ready & readyPAT))
        {
-               m_BAT = new eTable<BouquetAssociationSection>();
-               if (m_BAT->start(m_demux, eDVBBATSpec()))
-                       return -1;
-               CONNECT(m_BAT->tableReady, eDVBScan::BATready);
+               m_PAT = 0;
+               if (m_ready_all & readyPAT)
+               {
+                       m_PAT = new eTable<ProgramAssociationSection>(m_scan_debug);
+                       if (m_PAT->start(m_demux, eDVBPATSpec(4000)))
+                               return -1;
+                       CONNECT(m_PAT->tableReady, eDVBScan::PATready);
+               }
+
+               m_NIT = 0;
+               if (m_ready_all & readyNIT)
+               {
+                       m_NIT = new eTable<NetworkInformationSection>(m_scan_debug);
+                       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>(m_scan_debug);
+                       if (m_BAT->start(m_demux, eDVBBATSpec()))
+                               return -1;
+                       CONNECT(m_BAT->tableReady, eDVBScan::BATready);
+               }
        }
-       
        return 0;
 }
 
 void eDVBScan::SDTready(int err)
 {
-       SCAN_eDebug("got sdt");
+       SCAN_eDebug("got sdt %d", err);
        m_ready |= readySDT;
        if (!err)
                m_ready |= validSDT;
@@ -207,6 +347,153 @@ void eDVBScan::BATready(int err)
        channelDone();
 }
 
+void eDVBScan::PATready(int err)
+{
+       SCAN_eDebug("got pat");
+       m_ready |= readyPAT;
+       if (!err)
+               m_ready |= validPAT;
+       startFilter(); // for starting the SDT filter
+}
+
+void eDVBScan::PMTready(int err)
+{
+       SCAN_eDebug("got pmt %d", err);
+       if (!err)
+       {
+               bool scrambled = false;
+               bool have_audio = false;
+               bool have_video = false;
+               unsigned short pcrpid = 0xFFFF;
+               std::vector<ProgramMapSection*>::const_iterator i;
+
+               for (i = m_PMT->getSections().begin(); i != m_PMT->getSections().end(); ++i)
+               {
+                       const ProgramMapSection &pmt = **i;
+                       if (pcrpid == 0xFFFF)
+                               pcrpid = pmt.getPcrPid();
+                       else
+                               SCAN_eDebug("already have a pcrpid %04x %04x", pcrpid, pmt.getPcrPid());
+                       ElementaryStreamInfoConstIterator es;
+                       for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es)
+                       {
+                               int isaudio = 0, isvideo = 0, is_scrambled = 0, forced_audio = 0, forced_video = 0;
+                               switch ((*es)->getType())
+                               {
+                               case 0x1b: // AVC Video Stream (MPEG4 H264)
+                               case 0x10: // MPEG 4 Part 2
+                               case 0x01: // MPEG 1 video
+                               case 0x02: // MPEG 2 video
+                                       isvideo = 1;
+                                       forced_video = 1;
+                                       //break; fall through !!!
+                               case 0x03: // MPEG 1 audio
+                               case 0x04: // MPEG 2 audio
+                               case 0x0f: // MPEG 2 AAC
+                               case 0x11: // MPEG 4 AAC
+                                       if (!isvideo) 
+                                       {
+                                               forced_audio = 1;
+                                               isaudio = 1;
+                                       }
+                               case 0x06: // PES Private
+                               case 0x81: // user private
+                               case 0xEA: // TS_PSI_ST_SMPTE_VC1
+                                       for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin();
+                                                       desc != (*es)->getDescriptors()->end(); ++desc)
+                                       {
+                                               uint8_t tag = (*desc)->getTag();
+                                               /* PES private can contain AC-3, DTS or lots of other stuff.
+                                                  check descriptors to get the exakt type. */
+                                               if (!forced_video && !forced_audio)
+                                               {
+                                                       switch (tag)
+                                                       {
+                                                       case 0x1C: // TS_PSI_DT_MPEG4_Audio
+                                                       case 0x2B: // TS_PSI_DT_MPEG2_AAC
+                                                       case AAC_DESCRIPTOR:
+                                                       case AC3_DESCRIPTOR:
+                                                       case DTS_DESCRIPTOR:
+                                                       case AUDIO_STREAM_DESCRIPTOR:
+                                                               isaudio = 1;
+                                                               break;
+                                                       case 0x28: // TS_PSI_DT_AVC
+                                                       case 0x1B: // TS_PSI_DT_MPEG4_Video
+                                                       case VIDEO_STREAM_DESCRIPTOR:
+                                                               isvideo = 1;
+                                                               break;
+                                                       case REGISTRATION_DESCRIPTOR: /* some services don't have a separate AC3 descriptor */
+                                                       {
+                                                               RegistrationDescriptor *d = (RegistrationDescriptor*)(*desc);
+                                                               switch (d->getFormatIdentifier())
+                                                               {
+                                                               case 0x44545331 ... 0x44545333: // DTS1/DTS2/DTS3
+                                                               case 0x41432d33: // == 'AC-3'
+                                                               case 0x42535344: // == 'BSSD' (LPCM)
+                                                                       isaudio = 1;
+                                                                       break;
+                                                               case 0x56432d31: // == 'VC-1'
+                                                                       isvideo = 1;
+                                                                       break;
+                                                               default:
+                                                                       break;
+                                                               }
+                                                       }
+                                                       default:
+                                                               break;
+                                                       }
+                                               }
+                                               if (tag == CA_DESCRIPTOR)
+                                                       is_scrambled = 1;
+                                       }
+                               default:
+                                       break;
+                               }
+                               if (isvideo)
+                                       have_video = true;
+                               else if (isaudio)
+                                       have_audio = true;
+                               else
+                                       continue;
+                               if (is_scrambled)
+                                       scrambled = true;
+                       }
+                       for (DescriptorConstIterator desc = pmt.getDescriptors()->begin();
+                               desc != pmt.getDescriptors()->end(); ++desc)
+                       {
+                               if ((*desc)->getTag() == CA_DESCRIPTOR)
+                                       scrambled = true;
+                       }
+               }
+               m_pmt_in_progress->second.scrambled = scrambled;
+               if ( have_video )
+                       m_pmt_in_progress->second.serviceType = 1;
+               else if ( have_audio )
+                       m_pmt_in_progress->second.serviceType = 2;
+               else
+                       m_pmt_in_progress->second.serviceType = 100;
+       }
+       if (err == -1) // timeout or removed by sdt
+               m_pmts_to_read.erase(m_pmt_in_progress++);
+       else if (m_pmt_running)
+               ++m_pmt_in_progress;
+       else
+       {
+               m_pmt_in_progress = m_pmts_to_read.begin();
+               m_pmt_running = true;
+       }
+
+       if (m_pmt_in_progress != m_pmts_to_read.end())
+               m_PMT->start(m_demux, eDVBPMTSpec(m_pmt_in_progress->second.pmtPid, m_pmt_in_progress->first, 4000));
+       else
+       {
+               m_PMT = 0;
+               m_pmt_running = false;
+               channelDone();
+       }
+}
+
+
 void eDVBScan::addKnownGoodChannel(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
 {
                /* add it to the list of known channels. */
@@ -217,34 +504,99 @@ void eDVBScan::addKnownGoodChannel(const eDVBChannelID &chid, iDVBFrontendParame
 void eDVBScan::addChannelToScan(const eDVBChannelID &chid, iDVBFrontendParameters *feparm)
 {
                /* check if we don't already have that channel ... */
-               
+
+       int type;
+       feparm->getSystem(type);
+
+       switch(type)
+       {
+       case iDVBFrontend::feSatellite:
+       {
+               eDVBFrontendParametersSatellite parm;
+               feparm->getDVBS(parm);
+               SCAN_eDebug("try to add %d %d %d %d %d %d",
+                       parm.orbital_position, parm.frequency, parm.symbol_rate, parm.polarisation, parm.fec, parm.modulation);
+               break;
+       }
+       case iDVBFrontend::feCable:
+       {
+               eDVBFrontendParametersCable parm;
+               feparm->getDVBC(parm);
+               SCAN_eDebug("try to add %d %d %d %d",
+                       parm.frequency, parm.symbol_rate, parm.modulation, parm.fec_inner);
+               break;
+       }
+       case iDVBFrontend::feTerrestrial:
+       {
+               eDVBFrontendParametersTerrestrial parm;
+               feparm->getDVBT(parm);
+               SCAN_eDebug("try to add %d %d %d %d %d %d %d %d",
+                       parm.frequency, parm.modulation, parm.transmission_mode, parm.hierarchy,
+                       parm.guard_interval, parm.code_rate_LP, parm.code_rate_HP, parm.bandwidth);
+               break;
+       }
+       }
+
+       int found_count=0;
                /* ... in the list of channels to scan */
-       for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_toScan.begin()); i != m_ch_toScan.end(); ++i)
+       for (std::list<ePtr<iDVBFrontendParameters> >::iterator i(m_ch_toScan.begin()); i != m_ch_toScan.end();)
+       {
                if (sameChannel(*i, feparm))
-                       return;
+               {
+                       if (!found_count)
+                       {
+                               *i = feparm;  // update
+                               SCAN_eDebug("update");
+                       }
+                       else
+                       {
+                               SCAN_eDebug("remove dupe");
+                               m_ch_toScan.erase(i++);
+                               continue;
+                       }
+                       ++found_count;
+               }
+               ++i;
+       }
+
+       if (found_count > 0)
+       {
+               SCAN_eDebug("already in todo list");
+               return;
+       }
 
                /* ... in the list of successfully scanned channels */
        for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_scanned.begin()); i != m_ch_scanned.end(); ++i)
                if (sameChannel(*i, feparm))
+               {
+                       SCAN_eDebug("successfully scanned");
                        return;
-               
+               }
+
                /* ... in the list of unavailable channels */
        for (std::list<ePtr<iDVBFrontendParameters> >::const_iterator i(m_ch_unavailable.begin()); i != m_ch_unavailable.end(); ++i)
-               if (sameChannel(*i, feparm))
+               if (sameChannel(*i, feparm, true))
+               {
+                       SCAN_eDebug("scanned but not available");
                        return;
+               }
 
                /* ... on the current channel */
        if (sameChannel(m_ch_current, feparm))
+       {
+               SCAN_eDebug("is current");
                return;
+       }
 
+       SCAN_eDebug("really add");
                /* otherwise, add it to the todo list. */
        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
+int eDVBScan::sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *ch2, bool exact) const
 {
        int diff;
-       if (ch1->calculateDifference(ch2, diff))
+       if (ch1->calculateDifference(ch2, diff, exact))
                return 0;
        if (diff < 4000) // more than 4mhz difference?
                return 1;
@@ -253,17 +605,12 @@ int eDVBScan::sameChannel(iDVBFrontendParameters *ch1, iDVBFrontendParameters *c
 
 void eDVBScan::channelDone()
 {
-       if (m_ready & validSDT)
+       if (m_ready & validSDT && (!(m_flags & scanOnlyFree) || !m_pmt_running))
        {
                unsigned long hash = 0;
 
-               ePtr<iDVBFrontendParameters> p = m_ch_current;
-
-               if (!p)  // used in sdt scan
-                       m_channel->getCurrentFrontendParameters(p);
-
-               p->getHash(hash);
-
+               m_ch_current->getHash(hash);
+               
                eDVBNamespace dvbnamespace = buildNamespace(
                        (**m_SDT->getSections().begin()).getOriginalNetworkId(),
                        (**m_SDT->getSections().begin()).getTransportStreamId(),
@@ -278,7 +625,15 @@ void eDVBScan::channelDone()
        
        if (m_ready & validNIT)
        {
+               int system;
+               std::list<ePtr<iDVBFrontendParameters> > m_ch_toScan_backup;
+               m_ch_current->getSystem(system);
                SCAN_eDebug("dumping NIT");
+               if (m_flags & clearToScanOnFirstNIT)
+               {
+                       m_ch_toScan_backup = m_ch_toScan;
+                       m_ch_toScan.clear();
+               }
                std::vector<NetworkInformationSection*>::const_iterator i;
                for (i = m_NIT->getSections().begin(); i != m_NIT->getSections().end(); ++i)
                {
@@ -300,6 +655,8 @@ void eDVBScan::channelDone()
                                        {
                                        case CABLE_DELIVERY_SYSTEM_DESCRIPTOR:
                                        {
+                                               if (system != iDVBFrontend::feCable)
+                                                       break; // when current locked transponder is no cable transponder ignore this descriptor
                                                CableDeliverySystemDescriptor &d = (CableDeliverySystemDescriptor&)**desc;
                                                ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
                                                eDVBFrontendParametersCable cable;
@@ -317,6 +674,8 @@ void eDVBScan::channelDone()
                                        }
                                        case TERRESTRIAL_DELIVERY_SYSTEM_DESCRIPTOR:
                                        {
+                                               if (system != iDVBFrontend::feTerrestrial)
+                                                       break; // when current locked transponder is no terrestrial transponder ignore this descriptor
                                                TerrestrialDeliverySystemDescriptor &d = (TerrestrialDeliverySystemDescriptor&)**desc;
                                                ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
                                                eDVBFrontendParametersTerrestrial terr;
@@ -334,6 +693,9 @@ void eDVBScan::channelDone()
                                        }
                                        case SATELLITE_DELIVERY_SYSTEM_DESCRIPTOR:
                                        {
+                                               if (system != iDVBFrontend::feSatellite)
+                                                       break; // when current locked transponder is no satellite transponder ignore this descriptor
+
                                                SatelliteDeliverySystemDescriptor &d = (SatelliteDeliverySystemDescriptor&)**desc;
                                                if (d.getFrequency() < 10000)
                                                        break;
@@ -341,19 +703,29 @@ void eDVBScan::channelDone()
                                                ePtr<eDVBFrontendParameters> feparm = new eDVBFrontendParameters;
                                                eDVBFrontendParametersSatellite sat;
                                                sat.set(d);
+
+                                               eDVBFrontendParametersSatellite p;
+                                               m_ch_current->getDVBS(p);
+
+                                               if ( abs(p.orbital_position - sat.orbital_position) < 5 )
+                                                       sat.orbital_position = p.orbital_position;
+
+                                               if ( abs(abs(3600 - p.orbital_position) - sat.orbital_position) < 5 )
+                                               {
+                                                       SCAN_eDebug("found transponder with incorrect west/east flag ... correct this");
+                                                       sat.orbital_position = p.orbital_position;
+                                               }
+
                                                feparm->setDVBS(sat);
-                                               unsigned long hash=0;
-                                               feparm->getHash(hash);
-                                               
-                                               eDVBNamespace ns = buildNamespace(onid, tsid, hash);
-                                               
-                                               if ( m_chid_current.dvbnamespace.get() != -1 &&
-                                                       ((ns.get() ^ m_chid_current.dvbnamespace.get()) & 0xFFFF0000))
+
+                                               if ( p.orbital_position != sat.orbital_position)
                                                        SCAN_eDebug("dropping this transponder, it's on another satellite.");
                                                else
                                                {
+                                                       unsigned long hash=0;
+                                                       feparm->getHash(hash);
                                                        addChannelToScan(
-                                                                       eDVBChannelID(ns, tsid, onid),
+                                                                       eDVBChannelID(buildNamespace(onid, tsid, hash), tsid, onid),
                                                                        feparm);
                                                }
                                                break;
@@ -363,14 +735,38 @@ void eDVBScan::channelDone()
                                                break;
                                        }
                                }
-                               
                        }
+                       
                }
+
+                       /* a pitfall is to have the clearToScanOnFirstNIT-flag set, and having channels which have
+                          no or invalid NIT. this code will not erase the toScan list unless at least one valid entry
+                          has been found.
+
+                          This is not a perfect solution, as the channel could contain a partial NIT. Life's bad.
+                       */
+               if (m_flags & clearToScanOnFirstNIT)
+               {
+                       if (m_ch_toScan.empty())
+                       {
+                               eWarning("clearToScanOnFirstNIT was set, but NIT is invalid. Refusing to stop scan.");
+                               m_ch_toScan = m_ch_toScan_backup;
+                       } else
+                               m_flags &= ~clearToScanOnFirstNIT;
+               }
                m_ready &= ~validNIT;
        }
-       
-       if ((m_ready  & m_ready_all) != m_ready_all)
+
+       if (m_pmt_running || (m_ready & m_ready_all) != m_ready_all)
+       {
+               if (m_abort_current_pmt)
+               {
+                       m_abort_current_pmt = false;
+                       PMTready(-1);
+               }
                return;
+       }
+
        SCAN_eDebug("channel done!");
        
                /* if we had services on this channel, we declare
@@ -394,16 +790,147 @@ void eDVBScan::channelDone()
                   here, and not before.
                */
 
-       ePtr<iDVBFrontendParameters> p = m_ch_current;
-       if (!p)
-               m_channel->getCurrentFrontendParameters(p);
+       int type;
+       if (m_ch_current->getSystem(type))
+               type = -1;
+
+       for (m_pmt_in_progress = m_pmts_to_read.begin(); m_pmt_in_progress != m_pmts_to_read.end();)
+       {
+               eServiceReferenceDVB ref;
+               ePtr<eDVBService> service = new eDVBService;
+
+               if (!m_chid_current)
+               {
+                       unsigned long hash = 0;
+
+                       m_ch_current->getHash(hash);
+
+                       m_chid_current = eDVBChannelID(
+                               buildNamespace(eOriginalNetworkID(0), m_pat_tsid, hash),
+                               m_pat_tsid, eOriginalNetworkID(0));
+               }
+
+               if (m_pmt_in_progress->second.serviceType == 1)
+                       SCAN_eDebug("SID %04x is VIDEO", m_pmt_in_progress->first);
+               else if (m_pmt_in_progress->second.serviceType == 2)
+                       SCAN_eDebug("SID %04x is AUDIO", m_pmt_in_progress->first);
+               else
+                       SCAN_eDebug("SID %04x is DATA", m_pmt_in_progress->first);
+
+               ref.set(m_chid_current);
+               ref.setServiceID(m_pmt_in_progress->first);
+               ref.setServiceType(m_pmt_in_progress->second.serviceType);
+
+               if (type != -1)
+               {
+                       char sname[255];
+                       char pname[255];
+                       memset(pname, 0, sizeof(pname));
+                       memset(sname, 0, sizeof(sname));
+                       switch(type)
+                       {
+                               case iDVBFrontend::feSatellite:
+                               {
+                                       eDVBFrontendParametersSatellite parm;
+                                       m_ch_current->getDVBS(parm);
+                                       snprintf(sname, 255, "%d%c SID 0x%02x",
+                                                       parm.frequency/1000,
+                                                       parm.polarisation ? 'V' : 'H',
+                                                       m_pmt_in_progress->first);
+                                       snprintf(pname, 255, "%s %s %d%c %d.%d°%c",
+                                               parm.system ? "DVB-S2" : "DVB-S",
+                                               parm.modulation == 1 ? "QPSK" : "8PSK",
+                                               parm.frequency/1000,
+                                               parm.polarisation ? 'V' : 'H',
+                                               parm.orbital_position/10,
+                                               parm.orbital_position%10,
+                                               parm.orbital_position > 0 ? 'E' : 'W');
+                                       break;
+                               }
+                               case iDVBFrontend::feTerrestrial:
+                               {
+                                       eDVBFrontendParametersTerrestrial parm;
+                                       m_ch_current->getDVBT(parm);
+                                       snprintf(sname, 255, "%d SID 0x%02x",
+                                               parm.frequency/1000,
+                                               m_pmt_in_progress->first);
+                                       break;
+                               }
+                               case iDVBFrontend::feCable:
+                               {
+                                       eDVBFrontendParametersCable parm;
+                                       m_ch_current->getDVBC(parm);
+                                       snprintf(sname, 255, "%d SID 0x%02x",
+                                               parm.frequency/1000,
+                                               m_pmt_in_progress->first);
+                                       break;
+                               }
+                       }
+                       SCAN_eDebug("name '%s', provider_name '%s'", sname, pname);
+                       service->m_service_name = convertDVBUTF8(sname);
+                       service->genSortName();
+                       service->m_provider_name = convertDVBUTF8(pname);
+               }
+
+               if (!(m_flags & scanOnlyFree) || !m_pmt_in_progress->second.scrambled) {
+                       SCAN_eDebug("add not scrambled!");
+                       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);
+                       }
+               }
+               else
+                       SCAN_eDebug("dont add... is scrambled!");
+               m_pmts_to_read.erase(m_pmt_in_progress++);
+       }
 
        if (!m_chid_current)
                eWarning("SCAN: the current channel's ID was not corrected - not adding channel.");
        else
-               addKnownGoodChannel(m_chid_current, p);
+       {
+               addKnownGoodChannel(m_chid_current, m_ch_current);
+               if (m_chid_current)
+               {
+                       switch(type)
+                       {
+                               case iDVBFrontend::feSatellite:
+                               case iDVBFrontend::feTerrestrial:
+                               case iDVBFrontend::feCable:
+                               {
+                                       ePtr<iDVBFrontend> fe;
+                                       if (!m_channel->getFrontend(fe))
+                                       {
+                                               ePyObject tp_dict = PyDict_New();
+                                               fe->getTransponderData(tp_dict, false);
+//                                             eDebug("add tuner data for tsid %04x, onid %04x, ns %08x",
+//                                                     m_chid_current.transport_stream_id.get(), m_chid_current.original_network_id.get(),
+//                                                     m_chid_current.dvbnamespace.get());
+                                               m_tuner_data.insert(std::pair<eDVBChannelID, ePyObjectWrapper>(m_chid_current, tp_dict));
+                                               Py_DECREF(tp_dict);
+                                       }
+                               }
+                               default:
+                                       break;
+                       }
+               }
+       }
 
-       m_ch_scanned.push_back(p);
+       m_ch_scanned.push_back(m_ch_current);
+
+       for (std::list<ePtr<iDVBFrontendParameters> >::iterator i(m_ch_toScan.begin()); i != m_ch_toScan.end();)
+       {
+               if (sameChannel(*i, m_ch_current))
+               {
+                       SCAN_eDebug("remove dupe 2");
+                       m_ch_toScan.erase(i++);
+                       continue;
+               }
+               ++i;
+       }
+       
        nextChannel();
 }
 
@@ -414,6 +941,7 @@ void eDVBScan::start(const eSmartPtrList<iDVBFrontendParameters> &known_transpon
        m_ch_scanned.clear();
        m_ch_unavailable.clear();
        m_new_channels.clear();
+       m_tuner_data.clear();
        m_new_services.clear();
        m_last_service = m_new_services.end();
 
@@ -422,7 +950,7 @@ void eDVBScan::start(const eSmartPtrList<iDVBFrontendParameters> &known_transpon
                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))
+                       if (sameChannel(*i, *ii, true))
                        {
                                exist=true;
                                break;
@@ -446,52 +974,62 @@ void eDVBScan::insertInto(iDVBChannelList *db, bool dontRemoveOldFlags)
                std::list<ePtr<iDVBFrontendParameters> >::iterator it(m_ch_scanned.begin());
                for (;it != m_ch_scanned.end(); ++it)
                {
-                       int system;
-                       (*it)->getSystem(system);
-                       switch(system)
+                       if (m_flags & scanDontRemoveUnscanned)
+                               db->removeServices(&(*(*it)));
+                       else
                        {
-                               case iDVBFrontend::feSatellite:
+                               int system;
+                               (*it)->getSystem(system);
+                               switch(system)
                                {
-                                       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;
+                                       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)
+                       if (m_flags & scanDontRemoveUnscanned)
+                               db->removeServices(&(*(*it)));
+                       else
                        {
-                               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:
+                               int system;
+                               (*it)->getSystem(system);
+                               switch(system)
                                {
-                                       clearCable=true;
-                                       break;
+                                       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;
+                                       }
                                }
                        }
                }
@@ -520,7 +1058,40 @@ void eDVBScan::insertInto(iDVBChannelList *db, bool dontRemoveOldFlags)
 
        for (std::map<eDVBChannelID, ePtr<iDVBFrontendParameters> >::const_iterator 
                        ch(m_new_channels.begin()); ch != m_new_channels.end(); ++ch)
+       {
+               int system;
+               ch->second->getSystem(system);
+               std::map<eDVBChannelID, ePyObjectWrapper>::iterator it = m_tuner_data.find(ch->first);
+
+               switch(system)
+               {
+                       case iDVBFrontend::feTerrestrial:
+                       {
+                               eDVBFrontendParameters *p = (eDVBFrontendParameters*)&(*ch->second);
+                               eDVBFrontendParametersTerrestrial parm;
+                               int freq = PyInt_AsLong(PyDict_GetItemString(it->second, "frequency"));
+                               p->getDVBT(parm);
+//                             eDebug("corrected freq for tsid %04x, onid %04x, ns %08x is %d, old was %d",
+//                                     ch->first.transport_stream_id.get(), ch->first.original_network_id.get(),
+//                                     ch->first.dvbnamespace.get(), freq, parm.frequency);
+                               parm.frequency = freq;
+                               p->setDVBT(parm);
+                               break;
+                       }
+                       case iDVBFrontend::feSatellite: // no update of any transponder parameter yet
+                       case iDVBFrontend::feCable:
+                               break;
+               }
+
+               if (m_flags & scanOnlyFree)
+               {
+                       eDVBFrontendParameters *ptr = (eDVBFrontendParameters*)&(*ch->second);
+                       ptr->setFlags(iDVBFrontendParameters::flagOnlyFree);
+               }
+
                db->addChannelToList(ch->first, ch->second);
+       }
+
        for (std::map<eServiceReferenceDVB, ePtr<eDVBService> >::const_iterator
                service(m_new_services.begin()); service != m_new_services.end(); ++service)
        {
@@ -535,14 +1106,16 @@ void eDVBScan::insertInto(iDVBChannelList *db, bool dontRemoveOldFlags)
                                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;
+                       if (!(m_flags & scanRemoveServices))
+                               service->second->m_flags |= eDVBService::dxNewFound;
                }
        }
 }
@@ -553,67 +1126,93 @@ RESULT eDVBScan::processSDT(eDVBNamespace dvbnamespace, const ServiceDescription
        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;
-       
+       /* save correct CHID for this channel */
+       m_chid_current = chid;
+
        for (ServiceDescriptionConstIterator s(services.begin()); s != services.end(); ++s)
        {
-               SCAN_eDebugNoNewLine("SID %04x: ", (*s)->getServiceId());
-
-               eServiceReferenceDVB ref;
-               ePtr<eDVBService> service = new eDVBService;
-               
-               ref.set(chid);
-               ref.setServiceID((*s)->getServiceId());
+               unsigned short service_id = (*s)->getServiceId();
+               SCAN_eDebugNoNewLine("SID %04x: ", service_id);
+               bool add = true;
 
-               for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
-                               desc != (*s)->getDescriptors()->end(); ++desc)
-                       if ((*desc)->getTag() == SERVICE_DESCRIPTOR)
-                               ref.setServiceType(((ServiceDescriptor&)**desc).getServiceType());
-               
-               for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
-                               desc != (*s)->getDescriptors()->end(); ++desc)
+               if (m_flags & scanOnlyFree)
                {
-                       switch ((*desc)->getTag())
-                       {
-                       case SERVICE_DESCRIPTOR:
+                       std::map<unsigned short, service>::iterator it =
+                               m_pmts_to_read.find(service_id);
+                       if (it != m_pmts_to_read.end())
                        {
-                               ServiceDescriptor &d = (ServiceDescriptor&)**desc;
-                               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 CaSystemIdList &caids = *d.getCaSystemIds();
-                               SCAN_eDebugNoNewLine("CA ");
-                               for (CaSystemIdList::const_iterator i(caids.begin()); i != caids.end(); ++i)
+                               if (it->second.scrambled)
                                {
-                                       SCAN_eDebugNoNewLine("%04x ", *i);
-                                       service->m_ca.insert(*i);
+                                       SCAN_eDebug("is scrambled!");
+                                       add = false;
                                }
-                               SCAN_eDebug("");
-                               break;
+                               else
+                                       SCAN_eDebug("is free");
                        }
-                       default:
-                               SCAN_eDebug("descr<%x>", (*desc)->getTag());
-                               break;
+                       else {
+                               SCAN_eDebug("not found in PAT.. so we assume it is scrambled!!");
+                               add = false;
                        }
                }
-               
-               std::pair<std::map<eServiceReferenceDVB, ePtr<eDVBService> >::iterator, bool> i = m_new_services.insert(std::pair<eServiceReferenceDVB, ePtr<eDVBService> >(ref, service));
-               
-               if (i.second)
+
+               if (add)
                {
-                       m_last_service = i.first;
-                       m_event(evtNewService);
+                       eServiceReferenceDVB ref;
+                       ePtr<eDVBService> service = new eDVBService;
+
+                       ref.set(chid);
+                       ref.setServiceID(service_id);
+
+                       for (DescriptorConstIterator desc = (*s)->getDescriptors()->begin();
+                                       desc != (*s)->getDescriptors()->end(); ++desc)
+                       {
+                               switch ((*desc)->getTag())
+                               {
+                               case SERVICE_DESCRIPTOR:
+                               {
+                                       ServiceDescriptor &d = (ServiceDescriptor&)**desc;
+                                       ref.setServiceType(d.getServiceType());
+                                       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 CaSystemIdList &caids = *d.getCaSystemIds();
+                                       SCAN_eDebugNoNewLine("CA ");
+                                       for (CaSystemIdList::const_iterator i(caids.begin()); i != caids.end(); ++i)
+                                       {
+                                               SCAN_eDebugNoNewLine("%04x ", *i);
+                                               service->m_ca.push_front(*i);
+                                       }
+                                       SCAN_eDebug("");
+                                       break;
+                               }
+                               default:
+                                       SCAN_eDebug("descr<%x>", (*desc)->getTag());
+                                       break;
+                               }
+                       }
+
+                       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);
+                       }
                }
+               if (m_pmt_running && m_pmt_in_progress->first == service_id)
+                       m_abort_current_pmt = true;
+               else
+                       m_pmts_to_read.erase(service_id);
        }
+
        return 0;
 }
 
@@ -637,3 +1236,22 @@ void eDVBScan::getLastServiceName(std::string &last_service_name)
        else
                last_service_name = m_last_service->second->m_service_name;
 }
+
+RESULT eDVBScan::getFrontend(ePtr<iDVBFrontend> &fe)
+{
+       if (m_channel)
+               return m_channel->getFrontend(fe);
+       fe = 0;
+       return -1;
+}
+
+RESULT eDVBScan::getCurrentTransponder(ePtr<iDVBFrontendParameters> &tp)
+{
+       if (m_ch_current)
+       {
+               tp = m_ch_current;
+               return 0;
+       }
+       tp = 0;
+       return -1;
+}