Merge commit 'origin/bug_449_fix_wlan_usbstick_recognition'
[enigma2.git] / lib / dvb / dvb.cpp
index f04dac0b908dc90f341aa3e5cb314b87e55820e6..6eaadb04db8de3f737c906c7c12bd2930c5aac6d 100644 (file)
@@ -4,6 +4,7 @@
 #include <lib/dvb/dvb.h>
 #include <lib/dvb/pmt.h>
 #include <lib/dvb/sec.h>
 #include <lib/dvb/dvb.h>
 #include <lib/dvb/pmt.h>
 #include <lib/dvb/sec.h>
+#include <lib/dvb/specs.h>
 
 #include <errno.h>
 #include <sys/types.h>
 
 #include <errno.h>
 #include <sys/types.h>
@@ -61,31 +62,56 @@ ePtr<eDVBResourceManager> NewResourceManagerPtr(void)
 }
 
 eDVBResourceManager::eDVBResourceManager()
 }
 
 eDVBResourceManager::eDVBResourceManager()
-       :m_releaseCachedChannelTimer(eApp)
+       :m_releaseCachedChannelTimer(eTimer::create(eApp))
 {
        avail = 1;
        busy = 0;
 {
        avail = 1;
        busy = 0;
-       m_sec = new eDVBSatelliteEquipmentControl(m_frontend);
+       m_sec = new eDVBSatelliteEquipmentControl(m_frontend, m_simulate_frontend);
+
        if (!instance)
                instance = this;
        if (!instance)
                instance = this;
-               
+
                /* search available adapters... */
 
                // add linux devices
                /* search available adapters... */
 
                // add linux devices
-       
+
        int num_adapter = 0;
        while (eDVBAdapterLinux::exist(num_adapter))
        {
                addAdapter(new eDVBAdapterLinux(num_adapter));
                num_adapter++;
        }
        int num_adapter = 0;
        while (eDVBAdapterLinux::exist(num_adapter))
        {
                addAdapter(new eDVBAdapterLinux(num_adapter));
                num_adapter++;
        }
-       
-       eDebug("found %d adapter, %d frontends and %d demux", 
-               m_adapter.size(), m_frontend.size(), m_demux.size());
+
+       int fd = open("/proc/stb/info/model", O_RDONLY);
+       char tmp[255];
+       int rd = fd >= 0 ? read(fd, tmp, 255) : 0;
+       if (fd >= 0)
+               close(fd);
+
+       if (!strncmp(tmp, "dm7025\n", rd))
+               m_boxtype = DM7025;
+       else if (!strncmp(tmp, "dm8000\n", rd))
+               m_boxtype = DM8000;
+       else if (!strncmp(tmp, "dm800\n", rd))
+               m_boxtype = DM800;
+       else if (!strncmp(tmp, "dm500hd\n", rd))
+               m_boxtype = DM500HD;
+       else {
+               eDebug("boxtype detection via /proc/stb/info not possible... use fallback via demux count!\n");
+               if (m_demux.size() == 3)
+                       m_boxtype = DM800;
+               else if (m_demux.size() < 5)
+                       m_boxtype = DM7025;
+               else
+                       m_boxtype = DM8000;
+       }
+
+       eDebug("found %d adapter, %d frontends(%d sim) and %d demux, boxtype %d",
+               m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size(), m_boxtype);
 
        eDVBCAService::registerChannelCallback(this);
 
 
        eDVBCAService::registerChannelCallback(this);
 
-       CONNECT(m_releaseCachedChannelTimer.timeout, eDVBResourceManager::releaseCachedChannel);
+       CONNECT(m_releaseCachedChannelTimer->timeout, eDVBResourceManager::releaseCachedChannel);
 }
 
 void eDVBResourceManager::feStateChanged()
 }
 
 void eDVBResourceManager::feStateChanged()
@@ -102,7 +128,7 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
 {
                // scan frontends
        int num_fe = 0;
 {
                // scan frontends
        int num_fe = 0;
-       
+
        eDebug("scanning for frontends..");
        while (1)
        {
        eDebug("scanning for frontends..");
        while (1)
        {
@@ -117,13 +143,21 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
                        break;
                ePtr<eDVBFrontend> fe;
 
                        break;
                ePtr<eDVBFrontend> fe;
 
-               int ok = 0;
-               fe = new eDVBFrontend(m_nr, num_fe, ok);
-               if (ok)
-                       m_frontend.push_back(fe);
+               {
+                       int ok = 0;
+                       fe = new eDVBFrontend(m_nr, num_fe, ok);
+                       if (ok)
+                               m_frontend.push_back(fe);
+               }
+               {
+                       int ok = 0;
+                       fe = new eDVBFrontend(m_nr, num_fe, ok, true);
+                       if (ok)
+                               m_simulate_frontend.push_back(fe);
+               }
                ++num_fe;
        }
                ++num_fe;
        }
-       
+
                // scan demux
        int num_demux = 0;
        while (1)
                // scan demux
        int num_demux = 0;
        while (1)
@@ -138,10 +172,10 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
                if (stat(filename, &s))
                        break;
                ePtr<eDVBDemux> demux;
                if (stat(filename, &s))
                        break;
                ePtr<eDVBDemux> demux;
-               
+
                demux = new eDVBDemux(m_nr, num_demux);
                m_demux.push_back(demux);
                demux = new eDVBDemux(m_nr, num_demux);
                m_demux.push_back(demux);
-                       
+
                ++num_demux;
        }
 }
                ++num_demux;
        }
 }
@@ -159,12 +193,12 @@ RESULT eDVBAdapterLinux::getDemux(ePtr<eDVBDemux> &demux, int nr)
                --nr;
                ++i;
        }
                --nr;
                ++i;
        }
-       
+
        if (i != m_demux.end())
                demux = *i;
        else
                return -1;
        if (i != m_demux.end())
                demux = *i;
        else
                return -1;
-               
+
        return 0;
 }
 
        return 0;
 }
 
@@ -173,20 +207,20 @@ int eDVBAdapterLinux::getNumFrontends()
        return m_frontend.size();
 }
 
        return m_frontend.size();
 }
 
-RESULT eDVBAdapterLinux::getFrontend(ePtr<eDVBFrontend> &fe, int nr)
+RESULT eDVBAdapterLinux::getFrontend(ePtr<eDVBFrontend> &fe, int nr, bool simulate)
 {
 {
-       eSmartPtrList<eDVBFrontend>::iterator i(m_frontend.begin());
+       eSmartPtrList<eDVBFrontend>::iterator i(simulate ? m_simulate_frontend.begin() : m_frontend.begin());
        while (nr && (i != m_frontend.end()))
        {
                --nr;
                ++i;
        }
        while (nr && (i != m_frontend.end()))
        {
                --nr;
                ++i;
        }
-       
+
        if (i != m_frontend.end())
                fe = *i;
        else
                return -1;
        if (i != m_frontend.end())
                fe = *i;
        else
                return -1;
-               
+
        return 0;
 }
 
        return 0;
 }
 
@@ -214,9 +248,9 @@ void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
 {
        int num_fe = adapter->getNumFrontends();
        int num_demux = adapter->getNumDemux();
 {
        int num_fe = adapter->getNumFrontends();
        int num_demux = adapter->getNumDemux();
-       
+
        m_adapter.push_back(adapter);
        m_adapter.push_back(adapter);
-       
+
        int i;
        for (i=0; i<num_demux; ++i)
        {
        int i;
        for (i=0; i<num_demux; ++i)
        {
@@ -249,6 +283,32 @@ void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
                        }
                }
        }
                        }
                }
        }
+
+       prev_dvbt_frontend = 0;
+       for (i=0; i<num_fe; ++i)
+       {
+               ePtr<eDVBFrontend> frontend;
+               if (!adapter->getFrontend(frontend, i, true))
+               {
+                       int frontendType=0;
+                       frontend->getFrontendType(frontendType);
+                       eDVBRegisteredFrontend *new_fe = new eDVBRegisteredFrontend(frontend, adapter);
+//                     CONNECT(new_fe->stateChanged, eDVBResourceManager::feStateChanged);
+                       m_simulate_frontend.push_back(new_fe);
+                       frontend->setSEC(m_sec);
+                       // we must link all dvb-t frontends ( for active antenna voltage )
+                       if (frontendType == iDVBFrontend::feTerrestrial)
+                       {
+                               if (prev_dvbt_frontend)
+                               {
+                                       prev_dvbt_frontend->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, (long)new_fe);
+                                       frontend->setData(eDVBFrontend::LINKED_PREV_PTR, (long)&(*prev_dvbt_frontend));
+                               }
+                               prev_dvbt_frontend = new_fe;
+                       }
+               }
+       }
+
 }
 
 PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list)
 }
 
 PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list)
@@ -273,16 +333,24 @@ PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list)
                if (!i->m_frontend->setSlotInfo(obj))
                        return NULL;
        }
                if (!i->m_frontend->setSlotInfo(obj))
                        return NULL;
        }
+       pos=0;
+       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_simulate_frontend.begin()); i != m_simulate_frontend.end(); ++i)
+       {
+               ePyObject obj = PyList_GET_ITEM(list, pos++);
+               if (!i->m_frontend->setSlotInfo(obj))
+                       return NULL;
+       }
        Py_RETURN_NONE;
 }
 
        Py_RETURN_NONE;
 }
 
-RESULT eDVBResourceManager::allocateFrontend(ePtr<eDVBAllocatedFrontend> &fe, ePtr<iDVBFrontendParameters> &feparm)
+RESULT eDVBResourceManager::allocateFrontend(ePtr<eDVBAllocatedFrontend> &fe, ePtr<iDVBFrontendParameters> &feparm, bool simulate)
 {
 {
+       eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
        ePtr<eDVBRegisteredFrontend> best;
        int bestval = 0;
        int foundone = 0;
 
        ePtr<eDVBRegisteredFrontend> best;
        int bestval = 0;
        int foundone = 0;
 
-       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
+       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(frontends.begin()); i != frontends.end(); ++i)
        {
                int c = i->m_frontend->isCompatibleWith(feparm);
 
        {
                int c = i->m_frontend->isCompatibleWith(feparm);
 
@@ -291,12 +359,15 @@ RESULT eDVBResourceManager::allocateFrontend(ePtr<eDVBAllocatedFrontend> &fe, eP
 
                if (!i->m_inuse)
                {
 
                if (!i->m_inuse)
                {
+//                     eDebug("Slot %d, score %d", i->m_frontend->getSlotID(), c);
                        if (c > bestval)
                        {
                                bestval = c;
                                best = i;
                        }
                }
                        if (c > bestval)
                        {
                                bestval = c;
                                best = i;
                        }
                }
+//             else
+//                     eDebug("Slot %d, score %d... but BUSY!!!!!!!!!!!", i->m_frontend->getSlotID(), c);
        }
 
        if (best)
        }
 
        if (best)
@@ -367,7 +438,9 @@ alloc_fe_by_id_not_possible:
        return err;
 }
 
        return err;
 }
 
-RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int cap)
+#define capHoldDecodeReference 64
+
+RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int &cap)
 {
                /* find first unused demux which is on same adapter as frontend (or any, if PVR)
                   never use the first one unless we need a decoding demux. */
 {
                /* find first unused demux which is on same adapter as frontend (or any, if PVR)
                   never use the first one unless we need a decoding demux. */
@@ -379,10 +452,39 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
 
        if (i == m_demux.end())
                return -1;
 
        if (i == m_demux.end())
                return -1;
-       
+
        ePtr<eDVBRegisteredDemux> unused;
        ePtr<eDVBRegisteredDemux> unused;
-       
-       if (m_demux.size() < 5)
+
+       if (m_boxtype == DM800 || m_boxtype == DM500HD) // dm800 / 500hd
+       {
+               cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
+               for (; i != m_demux.end(); ++i, ++n)
+               {
+                       if (!i->m_inuse)
+                       {
+                               if (!unused)
+                                       unused = i;
+                       }
+                       else
+                       {
+                               if (fe)
+                               {
+                                       if (i->m_adapter == fe->m_adapter && 
+                                           i->m_demux->getSource() == fe->m_frontend->getDVBID())
+                                       {
+                                               demux = new eDVBAllocatedDemux(i);
+                                               return 0;
+                                       }
+                               }
+                               else if (i->m_demux->getSource() == -1) // PVR
+                               {
+                                       demux = new eDVBAllocatedDemux(i);
+                                       return 0;
+                               }
+                       }
+               }
+       }
+       else if (m_boxtype == DM7025) // ATI
        {
                /* FIXME: hardware demux policy */
                if (!(cap & iDVBChannel::capDecode))
        {
                /* FIXME: hardware demux policy */
                if (!(cap & iDVBChannel::capDecode))
@@ -397,20 +499,21 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
                for (; i != m_demux.end(); ++i, ++n)
                {
                        int is_decode = n < 2;
                for (; i != m_demux.end(); ++i, ++n)
                {
                        int is_decode = n < 2;
-               
+
                        int in_use = is_decode ? (i->m_demux->getRefCount() != 2) : i->m_inuse;
                        int in_use = is_decode ? (i->m_demux->getRefCount() != 2) : i->m_inuse;
-               
+
                        if ((!in_use) && ((!fe) || (i->m_adapter == fe->m_adapter)))
                        {
                                if ((cap & iDVBChannel::capDecode) && !is_decode)
                                        continue;
                        if ((!in_use) && ((!fe) || (i->m_adapter == fe->m_adapter)))
                        {
                                if ((cap & iDVBChannel::capDecode) && !is_decode)
                                        continue;
-                               unused = i;     
+                               unused = i;
                                break;
                        }
                }
        }
                                break;
                        }
                }
        }
-       else // we asume dm8000
+       else if (m_boxtype == DM8000)
        {
        {
+               cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
                for (; i != m_demux.end(); ++i, ++n)
                {
                        if (fe)
                for (; i != m_demux.end(); ++i, ++n)
                {
                        if (fe)
@@ -420,7 +523,7 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
                                        if (!unused)
                                                unused = i;
                                }
                                        if (!unused)
                                                unused = i;
                                }
-                               else if (i->m_adapter == fe->m_adapter && 
+                               else if (i->m_adapter == fe->m_adapter &&
                                    i->m_demux->getSource() == fe->m_frontend->getDVBID())
                                {
                                        demux = new eDVBAllocatedDemux(i);
                                    i->m_demux->getSource() == fe->m_frontend->getDVBID())
                                {
                                        demux = new eDVBAllocatedDemux(i);
@@ -467,11 +570,24 @@ RESULT eDVBResourceManager::getChannelList(ePtr<iDVBChannelList> &list)
                return -ENOENT;
 }
 
                return -ENOENT;
 }
 
-RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel)
+#define eDebugNoSimulate(x...) \
+       do { \
+               if (!simulate) \
+                       eDebug(x); \
+       } while(0)
+//             else \
+//             { \
+//                     eDebugNoNewLine("SIMULATE:"); \
+//                     eDebug(x); \
+//             } \
+
+
+RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel, bool simulate)
 {
                /* first, check if a channel is already existing. */
 {
                /* first, check if a channel is already existing. */
+       std::list<active_channel> &active_channels = simulate ? m_active_simulate_channels : m_active_channels;
 
 
-       if (m_cached_channel)
+       if (!simulate && m_cached_channel)
        {
                eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
                if(channelid==cache_chan->getChannelID())
        {
                eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
                if(channelid==cache_chan->getChannelID())
@@ -482,47 +598,46 @@ RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUse
                }
                m_cached_channel_state_changed_conn.disconnect();
                m_cached_channel=0;
                }
                m_cached_channel_state_changed_conn.disconnect();
                m_cached_channel=0;
-               m_releaseCachedChannelTimer.stop();
+               m_releaseCachedChannelTimer->stop();
        }
 
        }
 
-//     eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
-       for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
+       eDebugNoSimulate("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
+       for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
        {
        {
-//             eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
+               eDebugNoSimulate("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
                if (i->m_channel_id == channelid)
                {
                if (i->m_channel_id == channelid)
                {
-//                     eDebug("found shared channel..");
+                       eDebugNoSimulate("found shared channel..");
                        channel = i->m_channel;
                        return 0;
                }
        }
                        channel = i->m_channel;
                        return 0;
                }
        }
-       
+
        /* no currently available channel is tuned to this channelid. create a new one, if possible. */
 
        if (!m_list)
        {
        /* no currently available channel is tuned to this channelid. create a new one, if possible. */
 
        if (!m_list)
        {
-               eDebug("no channel list set!");
+               eDebugNoSimulate("no channel list set!");
                return errNoChannelList;
        }
 
        ePtr<iDVBFrontendParameters> feparm;
        if (m_list->getChannelFrontendData(channelid, feparm))
        {
                return errNoChannelList;
        }
 
        ePtr<iDVBFrontendParameters> feparm;
        if (m_list->getChannelFrontendData(channelid, feparm))
        {
-               eDebug("channel not found!");
+               eDebugNoSimulate("channel not found!");
                return errChannelNotInList;
        }
 
        /* allocate a frontend. */
                return errChannelNotInList;
        }
 
        /* allocate a frontend. */
-       
+
        ePtr<eDVBAllocatedFrontend> fe;
 
        ePtr<eDVBAllocatedFrontend> fe;
 
-       int err = allocateFrontend(fe, feparm);
+       int err = allocateFrontend(fe, feparm, simulate);
        if (err)
                return err;
 
        RESULT res;
        if (err)
                return err;
 
        RESULT res;
-       ePtr<eDVBChannel> ch;
-       ch = new eDVBChannel(this, fe);
+       ePtr<eDVBChannel> ch = new eDVBChannel(this, fe);
 
        res = ch->setChannel(channelid, feparm);
        if (res)
 
        res = ch->setChannel(channelid, feparm);
        if (res)
@@ -530,9 +645,15 @@ RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUse
                channel = 0;
                return errChidNotFound;
        }
                channel = 0;
                return errChidNotFound;
        }
-       m_cached_channel = channel = ch;
-       m_cached_channel_state_changed_conn =
-               CONNECT(ch->m_stateChanged,eDVBResourceManager::DVBChannelStateChanged);
+
+       if (simulate)
+               channel = ch;
+       else
+       {
+               m_cached_channel = channel = ch;
+               m_cached_channel_state_changed_conn =
+                       CONNECT(ch->m_stateChanged,eDVBResourceManager::DVBChannelStateChanged);
+       }
 
        return 0;
 }
 
        return 0;
 }
@@ -547,13 +668,13 @@ void eDVBResourceManager::DVBChannelStateChanged(iDVBChannel *chan)
                case iDVBChannel::state_ok:
                {
                        eDebug("stop release channel timer");
                case iDVBChannel::state_ok:
                {
                        eDebug("stop release channel timer");
-                       m_releaseCachedChannelTimer.stop();
+                       m_releaseCachedChannelTimer->stop();
                        break;
                }
                case iDVBChannel::state_last_instance:
                {
                        eDebug("start release channel timer");
                        break;
                }
                case iDVBChannel::state_last_instance:
                {
                        eDebug("start release channel timer");
-                       m_releaseCachedChannelTimer.start(3000, true);
+                       m_releaseCachedChannelTimer->start(3000, true);
                        break;
                }
                default: // ignore all other events
                        break;
                }
                default: // ignore all other events
@@ -575,17 +696,14 @@ RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel, in
        {
                m_cached_channel_state_changed_conn.disconnect();
                m_cached_channel=0;
        {
                m_cached_channel_state_changed_conn.disconnect();
                m_cached_channel=0;
-               m_releaseCachedChannelTimer.stop();
+               m_releaseCachedChannelTimer->stop();
        }
 
        int err = allocateFrontendByIndex(fe, slot_index);
        if (err)
                return err;
 
        }
 
        int err = allocateFrontendByIndex(fe, slot_index);
        if (err)
                return err;
 
-       eDVBChannel *ch;
-       ch = new eDVBChannel(this, fe);
-
-       channel = ch;
+       channel = new eDVBChannel(this, fe);
        return 0;
 }
 
        return 0;
 }
 
@@ -594,42 +712,55 @@ RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
 {
        ePtr<eDVBAllocatedDemux> demux;
 
 {
        ePtr<eDVBAllocatedDemux> demux;
 
-       if (m_cached_channel && m_releaseCachedChannelTimer.isActive())
+       if (m_cached_channel && m_releaseCachedChannelTimer->isActive())
        {
                m_cached_channel_state_changed_conn.disconnect();
                m_cached_channel=0;
        {
                m_cached_channel_state_changed_conn.disconnect();
                m_cached_channel=0;
-               m_releaseCachedChannelTimer.stop();
+               m_releaseCachedChannelTimer->stop();
        }
 
        }
 
-       eDVBChannel *ch;
-       ch = new eDVBChannel(this, 0);
-
-       channel = ch;
+       channel = new eDVBChannel(this, 0);
        return 0;
 }
 
 RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch)
 {
        return 0;
 }
 
 RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch)
 {
-       m_active_channels.push_back(active_channel(chid, ch));
-       /* emit */ m_channelAdded(ch);
+       ePtr<iDVBFrontend> fe;
+       if (!ch->getFrontend(fe))
+       {
+               eDVBFrontend *frontend = (eDVBFrontend*)&(*fe);
+               if (frontend->is_simulate())
+                       m_active_simulate_channels.push_back(active_channel(chid, ch));
+               else
+               {
+                       m_active_channels.push_back(active_channel(chid, ch));
+                       /* emit */ m_channelAdded(ch);
+               }
+       }
        return 0;
 }
 
 RESULT eDVBResourceManager::removeChannel(eDVBChannel *ch)
 {
        return 0;
 }
 
 RESULT eDVBResourceManager::removeChannel(eDVBChannel *ch)
 {
-       int cnt = 0;
-       for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end();)
+       ePtr<iDVBFrontend> fe;
+       if (!ch->getFrontend(fe))
        {
        {
-               if (i->m_channel == ch)
+               eDVBFrontend *frontend = (eDVBFrontend*)&(*fe);
+               std::list<active_channel> &active_channels = frontend->is_simulate() ? m_active_simulate_channels : m_active_channels;
+               int cnt = 0;
+               for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end();)
                {
                {
-                       i = m_active_channels.erase(i);
-                       ++cnt;
-               } else
-                       ++i;
+                       if (i->m_channel == ch)
+                       {
+                               i = active_channels.erase(i);
+                               ++cnt;
+                       } else
+                               ++i;
+               }
+               ASSERT(cnt == 1);
+               if (cnt == 1)
+                       return 0;
        }
        }
-       ASSERT(cnt == 1);
-       if (cnt == 1)
-               return 0;
        return -ENOENT;
 }
 
        return -ENOENT;
 }
 
@@ -639,12 +770,13 @@ RESULT eDVBResourceManager::connectChannelAdded(const Slot1<void,eDVBChannel*> &
        return 0;
 }
 
        return 0;
 }
 
-int eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm)
+int eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm, bool simulate)
 {
 {
+       eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
        ePtr<eDVBRegisteredFrontend> best;
        int bestval = 0;
 
        ePtr<eDVBRegisteredFrontend> best;
        int bestval = 0;
 
-       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
+       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(frontends.begin()); i != frontends.end(); ++i)
                if (!i->m_inuse)
                {
                        int c = i->m_frontend->isCompatibleWith(feparm);
                if (!i->m_inuse)
                {
                        int c = i->m_frontend->isCompatibleWith(feparm);
@@ -681,10 +813,11 @@ int tuner_type_channel_default(ePtr<iDVBChannelList> &channellist, const eDVBCha
        return 0;
 }
 
        return 0;
 }
 
-int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore)
+int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore, bool simulate)
 {
 {
+       std::list<active_channel> &active_channels = simulate ? m_active_simulate_channels : m_active_channels;
        int ret=0;
        int ret=0;
-       if (m_cached_channel)
+       if (!simulate && m_cached_channel)
        {
                eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
                if(channelid==cache_chan->getChannelID())
        {
                eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
                if(channelid==cache_chan->getChannelID())
@@ -693,7 +826,7 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
 
                /* first, check if a channel is already existing. */
 //     eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
 
                /* first, check if a channel is already existing. */
 //     eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
-       for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
+       for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
        {
 //             eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
                if (i->m_channel_id == channelid)
        {
 //             eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
                if (i->m_channel_id == channelid)
@@ -706,8 +839,9 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
        int *decremented_cached_channel_fe_usecount=NULL,
                *decremented_fe_usecount=NULL;
 
        int *decremented_cached_channel_fe_usecount=NULL,
                *decremented_fe_usecount=NULL;
 
-       for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
+       for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
        {
        {
+               eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
 //             eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
                if (i->m_channel_id == ignore)
                {
 //             eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
                if (i->m_channel_id == ignore)
                {
@@ -721,7 +855,7 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                                ePtr<iDVBFrontend> fe;
                                if (!i->m_channel->getFrontend(fe))
                                {
                                ePtr<iDVBFrontend> fe;
                                if (!i->m_channel->getFrontend(fe))
                                {
-                                       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(m_frontend.begin()); ii != m_frontend.end(); ++ii)
+                                       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(frontends.begin()); ii != frontends.end(); ++ii)
                                        {
                                                if ( &(*fe) == &(*ii->m_frontend) )
                                                {
                                        {
                                                if ( &(*fe) == &(*ii->m_frontend) )
                                                {
@@ -748,7 +882,8 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                                ePtr<iDVBFrontend> fe;
                                if (!channel->getFrontend(fe))
                                {
                                ePtr<iDVBFrontend> fe;
                                if (!channel->getFrontend(fe))
                                {
-                                       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(m_frontend.begin()); ii != m_frontend.end(); ++ii)
+                                       eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
+                                       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(frontends.begin()); ii != frontends.end(); ++ii)
                                        {
                                                if ( &(*fe) == &(*ii->m_frontend) )
                                                {
                                        {
                                                if ( &(*fe) == &(*ii->m_frontend) )
                                                {
@@ -778,7 +913,7 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                goto error;
        }
 
                goto error;
        }
 
-       ret = canAllocateFrontend(feparm);
+       ret = canAllocateFrontend(feparm, simulate);
 
 error:
        if (decremented_fe_usecount)
 
 error:
        if (decremented_fe_usecount)
@@ -895,7 +1030,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
        }
 #endif
 
        }
 #endif
 
-#if 1 /* not yet */
+#if 0
        if (!m_iframe_search)
                return len;
 
        if (!m_iframe_search)
                return len;
 
@@ -904,23 +1039,23 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
 //     eDebug("filterRecordData, size=%d (mod 188=%d), first byte is %02x", len, len %188, data[0]);
 
        unsigned char *d = data;
 //     eDebug("filterRecordData, size=%d (mod 188=%d), first byte is %02x", len, len %188, data[0]);
 
        unsigned char *d = data;
-       while ((d = (unsigned char*)memmem(d, data + len - d, "\x00\x00\x01", 3)))
+       while ((d + 3 < data + len) && (d = (unsigned char*)memmem(d, data + len - d, "\x00\x00\x01", 3)))
        {
                int offset = d - data;
                int ts_offset = offset - offset % 188; /* offset to the start of TS packet */
                unsigned char *ts = data + ts_offset;
                int pid = ((ts[1] << 8) | ts[2]) & 0x1FFF;
 
        {
                int offset = d - data;
                int ts_offset = offset - offset % 188; /* offset to the start of TS packet */
                unsigned char *ts = data + ts_offset;
                int pid = ((ts[1] << 8) | ts[2]) & 0x1FFF;
 
-               if ((d[3] == 0) && (m_pid == pid))  /* picture start */
+               if ((d[3] == 0 || d[3] == 0x09 && d[-1] == 0 && (ts[1] & 0x40)) && (m_pid == pid))  /* picture start */
                {
                {
-                       int picture_type = (d[5] >> 3) & 7;
+                       int picture_type = (d[3]==0 ? (d[5] >> 3) & 7 : (d[4] >> 5) + 1);
                        d += 4;
 
 //                     eDebug("%d-frame at %d, offset in TS packet: %d, pid=%04x", picture_type, offset, offset % 188, pid);
 
                        if (m_iframe_state == 1)
                        {
                        d += 4;
 
 //                     eDebug("%d-frame at %d, offset in TS packet: %d, pid=%04x", picture_type, offset, offset % 188, pid);
 
                        if (m_iframe_state == 1)
                        {
-                                       /* we are allowing data, and stop allowing data on the next frame. 
+                                       /* we are allowing data, and stop allowing data on the next frame.
                                           we now found a frame. so stop here. */
                                memset(data + offset, 0, 188 - (offset%188)); /* zero out rest of TS packet */
                                current_span_remaining = 0;
                                           we now found a frame. so stop here. */
                                memset(data + offset, 0, 188 - (offset%188)); /* zero out rest of TS packet */
                                current_span_remaining = 0;
@@ -947,26 +1082,30 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
 
                                        fts += 188;
                                }
 
                                        fts += 188;
                                }
-                                               /* force payload only */
-                               ts[3] &= ~0x30;
-                               ts[3] |=  0x10;
-
-//                             memset(ts + 4, 0xFF, (offset % 188) - 4);
 
                                m_iframe_state = 1;
                        }
                } else if ((d[3] & 0xF0) == 0xE0) /* video stream */
                {
 
                                m_iframe_state = 1;
                        }
                } else if ((d[3] & 0xF0) == 0xE0) /* video stream */
                {
-                       if (m_pid != pid)
+                               /* verify that this is actually a PES header, not just some ES data */
+                       if (ts[1] & 0x40) /* PUSI set */
                        {
                        {
-                               eDebug("now locked to pid %04x", pid);
-                               m_pid = pid;
+                               int payload_start = 4;
+                               if (ts[3] & 0x20) /* adaptation field present */
+                                       payload_start += ts[4] + 1; /* skip AF */
+                               if (payload_start == (offset%188)) /* the 00 00 01 should be directly at the payload start, otherwise it's not a PES header */
+                               {
+                                       if (m_pid != pid)
+                                       {
+                                               eDebug("now locked to pid %04x (%02x %02x %02x %02x)", pid, ts[0], ts[1], ts[2], ts[3]);
+                                               m_pid = pid;
+                                       }
+                               }
                        }
 //                     m_pid = 0x6e;
                        d += 4;
                } else
                        d += 4; /* ignore */
                        }
 //                     m_pid = 0x6e;
                        d += 4;
                } else
                        d += 4; /* ignore */
-
        }
 
        if (m_iframe_state == 1)
        }
 
        if (m_iframe_state == 1)
@@ -985,9 +1124,10 @@ eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *fronte
        m_frontend = frontend;
 
        m_pvr_thread = 0;
        m_frontend = frontend;
 
        m_pvr_thread = 0;
-       
-       m_skipmode_n = m_skipmode_m = 0;
-       
+       m_pvr_fd_dst = -1;
+
+       m_skipmode_n = m_skipmode_m = m_skipmode_frames = 0;
+
        if (m_frontend)
                m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
 }
        if (m_frontend)
                m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
 }
@@ -1003,14 +1143,14 @@ eDVBChannel::~eDVBChannel()
 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
 {
        int state, ourstate = 0;
 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
 {
        int state, ourstate = 0;
-       
+
                /* if we are already in shutdown, don't change state. */
        if (m_state == state_release)
                return;
                /* if we are already in shutdown, don't change state. */
        if (m_state == state_release)
                return;
-       
+
        if (fe->getState(state))
                return;
        if (fe->getState(state))
                return;
-       
+
        if (state == iDVBFrontend::stateLock)
        {
                eDebug("OURSTATE: ok");
        if (state == iDVBFrontend::stateLock)
        {
                eDebug("OURSTATE: ok");
@@ -1039,7 +1179,7 @@ void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
                ourstate = state_failed;
        } else
                eFatal("state unknown");
                ourstate = state_failed;
        } else
                eFatal("state unknown");
-       
+
        if (ourstate != m_state)
        {
                m_state = ourstate;
        if (ourstate != m_state)
        {
                m_state = ourstate;
@@ -1087,6 +1227,8 @@ void eDVBChannel::cueSheetEvent(int event)
                                                /* i agree that this might look a bit like black magic. */
                                m_skipmode_n = 512*1024; /* must be 1 iframe at least. */
                                m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio / 8;
                                                /* i agree that this might look a bit like black magic. */
                                m_skipmode_n = 512*1024; /* must be 1 iframe at least. */
                                m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio / 8;
+                               m_skipmode_frames = m_cue->m_skipmode_ratio / 90000;
+                               m_skipmode_frames_remainder = 0;
 
                                if (m_cue->m_skipmode_ratio < 0)
                                        m_skipmode_m -= m_skipmode_n;
 
                                if (m_cue->m_skipmode_ratio < 0)
                                        m_skipmode_m -= m_skipmode_n;
@@ -1096,12 +1238,12 @@ void eDVBChannel::cueSheetEvent(int event)
                                if (abs(m_skipmode_m) < abs(m_skipmode_n))
                                {
                                        eWarning("something is wrong with this calculation");
                                if (abs(m_skipmode_m) < abs(m_skipmode_n))
                                {
                                        eWarning("something is wrong with this calculation");
-                                       m_skipmode_n = m_skipmode_m = 0;
+                                       m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0;
                                }
                        } else
                        {
                                eDebug("skipmode ratio is 0, normal play");
                                }
                        } else
                        {
                                eDebug("skipmode ratio is 0, normal play");
-                               m_skipmode_n = m_skipmode_m = 0;
+                               m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0;
                        }
                }
                m_pvr_thread->setIFrameSearch(m_skipmode_n != 0);
                        }
                }
                m_pvr_thread->setIFrameSearch(m_skipmode_n != 0);
@@ -1121,7 +1263,7 @@ void eDVBChannel::cueSheetEvent(int event)
                {
                        off_t offset_in, offset_out;
                        pts_t pts_in = i->first, pts_out = i->second;
                {
                        off_t offset_in, offset_out;
                        pts_t pts_in = i->first, pts_out = i->second;
-                       if (m_tstools.getOffset(offset_in, pts_in) || m_tstools.getOffset(offset_out, pts_out))
+                       if (m_tstools.getOffset(offset_in, pts_in, -1) || m_tstools.getOffset(offset_out, pts_out, 1))
                        {
                                eDebug("span translation failed.\n");
                                continue;
                        {
                                eDebug("span translation failed.\n");
                                continue;
@@ -1144,6 +1286,23 @@ static inline long long align(long long x, int align)
 
        x -= x % align;
 
 
        x -= x % align;
 
+       if (sign)
+               x = -x;
+
+       return x;
+}
+
+       /* align toward zero */
+static inline long long align_with_len(long long x, int align, size_t &len)
+{
+       int sign = x < 0;
+
+       if (sign)
+               x = -x;
+
+       x -= x % align;
+       len += x % align;
+
        if (sign)
                x = -x;
 
        if (sign)
                x = -x;
 
@@ -1156,7 +1315,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
        const int blocksize = 188;
        unsigned int max = align(10*1024*1024, blocksize);
        current_offset = align(current_offset, blocksize);
        const int blocksize = 188;
        unsigned int max = align(10*1024*1024, blocksize);
        current_offset = align(current_offset, blocksize);
-       
+
        if (!m_cue)
        {
                eDebug("no cue sheet. forcing normal play");
        if (!m_cue)
        {
                eDebug("no cue sheet. forcing normal play");
@@ -1177,13 +1336,55 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
 
        if (m_skipmode_n)
        {
 
        if (m_skipmode_n)
        {
-               eDebug("skipmode %d:%d", m_skipmode_m, m_skipmode_n);
+               eDebug("skipmode %d:%d (x%d)", m_skipmode_m, m_skipmode_n, m_skipmode_frames);
                max = align(m_skipmode_n, blocksize);
        }
 
        eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m);
                max = align(m_skipmode_n, blocksize);
        }
 
        eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m);
+       
+       int frame_skip_success = 0;
 
 
-       current_offset += align(m_skipmode_m, blocksize);
+       if (m_skipmode_m)
+       {
+               int frames_to_skip = m_skipmode_frames + m_skipmode_frames_remainder;
+               eDebug("we are at %llx, and we try to skip %d+%d frames from here", current_offset, m_skipmode_frames, m_skipmode_frames_remainder);
+               size_t iframe_len;
+               off_t iframe_start = current_offset;
+               int frames_skipped = frames_to_skip;
+               if (!m_tstools.findNextPicture(iframe_start, iframe_len, frames_skipped))
+               {
+                       m_skipmode_frames_remainder = frames_to_skip - frames_skipped;
+                       eDebug("successfully skipped %d (out of %d, rem now %d) frames.", frames_skipped, frames_to_skip, m_skipmode_frames_remainder);
+                       current_offset = align_with_len(iframe_start, blocksize, iframe_len);
+                       max = align(iframe_len + 187, blocksize);
+                       frame_skip_success = 1;
+               } else
+               {
+                       m_skipmode_frames_remainder = 0;
+                       eDebug("frame skipping failed, reverting to byte-skipping");
+               }
+       }
+       
+       if (!frame_skip_success)
+       {
+               current_offset += align(m_skipmode_m, blocksize);
+               
+               if (m_skipmode_m)
+               {
+                       eDebug("we are at %llx, and we try to find the iframe here:", current_offset);
+                       size_t iframe_len;
+                       off_t iframe_start = current_offset;
+                       
+                       int direction = (m_skipmode_m < 0) ? -1 : +1;
+                       if (m_tstools.findFrame(iframe_start, iframe_len, direction))
+                               eDebug("failed");
+                       else
+                       {
+                               current_offset = align_with_len(iframe_start, blocksize, iframe_len);
+                               max = align(iframe_len, blocksize);
+                       }
+               }
+       }
 
        while (!m_cue->m_seek_requests.empty())
        {
 
        while (!m_cue->m_seek_requests.empty())
        {
@@ -1227,7 +1428,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                                continue;
                        }
                }
                                continue;
                        }
                }
-               
+
                if (relative == 1) /* pts relative */
                {
                        pts += now;
                if (relative == 1) /* pts relative */
                {
                        pts += now;
@@ -1238,7 +1439,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                if (relative != 2)
                        if (pts < 0)
                                pts = 0;
                if (relative != 2)
                        if (pts < 0)
                                pts = 0;
-               
+
                if (relative == 2) /* AP relative */
                {
                        eDebug("AP relative seeking: %lld, at %lld", pts, now);
                if (relative == 2) /* AP relative */
                {
                        eDebug("AP relative seeking: %lld, at %lld", pts, now);
@@ -1249,18 +1450,18 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                                eDebug("AP relative seeking failed!");
                        } else
                        {
                                eDebug("AP relative seeking failed!");
                        } else
                        {
-                               eDebug("next ap is %llx\n", pts);
                                pts = nextap;
                                pts = nextap;
+                               eDebug("next ap is %llx\n", pts);
                        }
                }
                        }
                }
-               
+
                off_t offset = 0;
                off_t offset = 0;
-               if (m_tstools.getOffset(offset, pts))
+               if (m_tstools.getOffset(offset, pts, -1))
                {
                        eDebug("get offset for pts=%lld failed!", pts);
                        continue;
                }
                {
                        eDebug("get offset for pts=%lld failed!", pts);
                        continue;
                }
-
+               
                eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
                current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
        }
                eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
                current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
        }
@@ -1271,7 +1472,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
        {
                long long aligned_start = align(i->first, blocksize);
                long long aligned_end = align(i->second, blocksize);
        {
                long long aligned_start = align(i->first, blocksize);
                long long aligned_end = align(i->second, blocksize);
-       
+
                if ((current_offset >= aligned_start) && (current_offset < aligned_end))
                {
                        start = current_offset;
                if ((current_offset >= aligned_start) && (current_offset < aligned_end))
                {
                        start = current_offset;
@@ -1336,10 +1537,16 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                m_pvr_thread->sendEvent(eFilePushThread::evtUser);
        }
 
                m_pvr_thread->sendEvent(eFilePushThread::evtUser);
        }
 
-       start = current_offset;
-       size = max;
-
-       eDebug("END OF CUESHEET. (%08llx, %d)", start, size);
+       if (m_source_span.empty())
+       {
+               start = current_offset;
+               size = max;
+               eDebug("NO CUESHEET. (%08llx, %d)", start, size);
+       } else
+       {
+               start = current_offset;
+               size = 0;
+       }
        return;
 }
 
        return;
 }
 
@@ -1370,7 +1577,7 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
 {
        if (m_channel_id)
                m_mgr->removeChannel(this);
 {
        if (m_channel_id)
                m_mgr->removeChannel(this);
-               
+
        if (!channelid)
                return 0;
 
        if (!channelid)
                return 0;
 
@@ -1379,7 +1586,7 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
                eDebug("no frontend to tune!");
                return -ENODEV;
        }
                eDebug("no frontend to tune!");
                return -ENODEV;
        }
-       
+
        m_channel_id = channelid;
        m_mgr->addChannel(channelid, this);
        m_state = state_tuning;
        m_channel_id = channelid;
        m_mgr->addChannel(channelid, this);
        m_state = state_tuning;
@@ -1387,14 +1594,14 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
        int res;
        res = m_frontend->get().tune(*feparm);
        m_current_frontend_parameters = feparm;
        int res;
        res = m_frontend->get().tune(*feparm);
        m_current_frontend_parameters = feparm;
-       
+
        if (res)
        {
                m_state = state_release;
                m_stateChanged(this);
                return res;
        }
        if (res)
        {
                m_state = state_release;
                m_stateChanged(this);
                return res;
        }
-       
+
        return 0;
 }
 
        return 0;
 }
 
@@ -1421,27 +1628,89 @@ RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
        return -1;
 }
 
        return -1;
 }
 
+void eDVBChannel::SDTready(int result)
+{
+       ePyObject args = PyTuple_New(2), ret;
+       bool ok=false;
+       if (!result)
+       {
+               for (std::vector<ServiceDescriptionSection*>::const_iterator i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i)
+               {
+                       ok = true;
+                       PyTuple_SET_ITEM(args, 0, PyInt_FromLong((*i)->getTransportStreamId()));
+                       PyTuple_SET_ITEM(args, 1, PyInt_FromLong((*i)->getOriginalNetworkId()));
+                       break;
+               }
+       }
+       if (!ok)
+       {
+               PyTuple_SET_ITEM(args, 0, Py_None);
+               PyTuple_SET_ITEM(args, 1, Py_None);
+               Py_INCREF(Py_None);
+               Py_INCREF(Py_None);
+       }
+       ret = PyObject_CallObject(m_tsid_onid_callback, args);
+       if (ret)
+               Py_DECREF(ret);
+       Py_DECREF(args);
+       Py_DECREF(m_tsid_onid_callback);
+       m_tsid_onid_callback = ePyObject();
+       m_tsid_onid_demux = 0;
+       m_SDT = 0;
+}
+
+RESULT eDVBChannel::requestTsidOnid(ePyObject callback)
+{
+       if (PyCallable_Check(callback))
+       {
+               if (!getDemux(m_tsid_onid_demux, 0))
+               {
+                       m_SDT = new eTable<ServiceDescriptionSection>;
+                       CONNECT(m_SDT->tableReady, eDVBChannel::SDTready);
+                       if (m_SDT->start(m_tsid_onid_demux, eDVBSDTSpec()))
+                       {
+                               m_tsid_onid_demux = 0;
+                               m_SDT = 0;
+                       }
+                       else
+                       {
+                               Py_INCREF(callback);
+                               m_tsid_onid_callback = callback;
+                               return 0;
+                       }
+               }
+       }
+       return -1;
+}
+
 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
 {
        ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
 {
        ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
-       
+
        if (!our_demux)
        {
                demux = 0;
        if (!our_demux)
        {
                demux = 0;
-               
+
                if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
                        return -1;
                if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
                        return -1;
-       }
-       
-       demux = *our_demux;
+
+               demux = *our_demux;
+
                /* don't hold a reference to the decoding demux, we don't need it. */
                /* don't hold a reference to the decoding demux, we don't need it. */
-               
+
                /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux',
                /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux',
-                  the refcount is lost. thus, decoding demuxes are never allocated. 
-                  
+                  the refcount is lost. thus, decoding demuxes are never allocated.
+
                   this poses a big problem for PiP. */
                   this poses a big problem for PiP. */
-       if (cap & capDecode)
-               our_demux = 0;
+
+               if (cap & capHoldDecodeReference) // this is set in eDVBResourceManager::allocateDemux for Dm500HD/DM800 and DM8000
+                       ;
+               else if (cap & capDecode)
+                       our_demux = 0;
+       }
+       else
+               demux = *our_demux;
+
        return 0;
 }
 
        return 0;
 }
 
@@ -1471,22 +1740,25 @@ RESULT eDVBChannel::playFile(const char *file)
                delete m_pvr_thread;
                m_pvr_thread = 0;
        }
                delete m_pvr_thread;
                m_pvr_thread = 0;
        }
-       
+
        m_tstools.openFile(file);
        m_tstools.openFile(file);
-       
+
                /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
                   THEN DO A REAL FIX HERE! */
                /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
                   THEN DO A REAL FIX HERE! */
-       
+
+       if (m_pvr_fd_dst < 0)
+       {
                /* (this codepath needs to be improved anyway.) */
 #if HAVE_DVB_API_VERSION < 3
                /* (this codepath needs to be improved anyway.) */
 #if HAVE_DVB_API_VERSION < 3
-       m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
+               m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
 #else
 #else
-       m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
+               m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
 #endif
 #endif
-       if (m_pvr_fd_dst < 0)
-       {
-               eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
-               return -ENODEV;
+               if (m_pvr_fd_dst < 0)
+               {
+                       eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
+                       return -ENODEV;
+               }
        }
 
        m_pvr_thread = new eDVBChannelFilePush();
        }
 
        m_pvr_thread = new eDVBChannelFilePush();
@@ -1494,10 +1766,14 @@ RESULT eDVBChannel::playFile(const char *file)
        m_pvr_thread->setStreamMode(1);
        m_pvr_thread->setScatterGather(this);
 
        m_pvr_thread->setStreamMode(1);
        m_pvr_thread->setScatterGather(this);
 
+       m_event(this, evtPreStart);
+
        if (m_pvr_thread->start(file, m_pvr_fd_dst))
        {
                delete m_pvr_thread;
                m_pvr_thread = 0;
        if (m_pvr_thread->start(file, m_pvr_fd_dst))
        {
                delete m_pvr_thread;
                m_pvr_thread = 0;
+               ::close(m_pvr_fd_dst);
+               m_pvr_fd_dst = -1;
                eDebug("can't open PVR file %s (%m)", file);
                return -ENOENT;
        }
                eDebug("can't open PVR file %s (%m)", file);
                return -ENOENT;
        }
@@ -1514,10 +1790,11 @@ void eDVBChannel::stopFile()
        if (m_pvr_thread)
        {
                m_pvr_thread->stop();
        if (m_pvr_thread)
        {
                m_pvr_thread->stop();
-               ::close(m_pvr_fd_dst);
                delete m_pvr_thread;
                m_pvr_thread = 0;
        }
                delete m_pvr_thread;
                m_pvr_thread = 0;
        }
+       if (m_pvr_fd_dst >= 0)
+               ::close(m_pvr_fd_dst);
 }
 
 void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
 }
 
 void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
@@ -1537,11 +1814,11 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
 {
        if (!decoding_demux)
                return -1;
 {
        if (!decoding_demux)
                return -1;
-       
+
        pts_t now;
        pts_t now;
-       
+
        int r;
        int r;
-       
+
        if (mode == 0) /* demux */
        {
                r = decoding_demux->getSTC(now, 0);
        if (mode == 0) /* demux */
        {
                r = decoding_demux->getSTC(now, 0);
@@ -1552,7 +1829,7 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
                }
        } else
                now = pos; /* fixup supplied */
                }
        } else
                now = pos; /* fixup supplied */
-       
+
        off_t off = 0; /* TODO: fixme */
        r = m_tstools.fixupPTS(off, now);
        if (r)
        off_t off = 0; /* TODO: fixme */
        r = m_tstools.fixupPTS(off, now);
        if (r)
@@ -1560,9 +1837,9 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
                eDebug("fixup PTS failed");
                return -1;
        }
                eDebug("fixup PTS failed");
                return -1;
        }
-       
+
        pos = now;
        pos = now;
-       
+
        return 0;
 }
 
        return 0;
 }
 
@@ -1573,7 +1850,7 @@ void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
                           a.) the filepush's internal buffer
                           b.) the PVR buffer (before demux)
                           c.) the ratebuffer (after demux)
                           a.) the filepush's internal buffer
                           b.) the PVR buffer (before demux)
                           c.) the ratebuffer (after demux)
-                          
+
                           it's important to clear them in the correct order, otherwise
                           the ratebuffer (for example) would immediately refill from
                           the not-yet-flushed PVR buffer.
                           it's important to clear them in the correct order, otherwise
                           the ratebuffer (for example) would immediately refill from
                           the not-yet-flushed PVR buffer.
@@ -1584,7 +1861,7 @@ void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
        m_pvr_thread->flush();
                /* HACK: flush PVR buffer */
        ::ioctl(m_pvr_fd_dst, 0);
        m_pvr_thread->flush();
                /* HACK: flush PVR buffer */
        ::ioctl(m_pvr_fd_dst, 0);
-       
+
                /* flush ratebuffers (video, audio) */
        if (decoding_demux)
                decoding_demux->flush();
                /* flush ratebuffers (video, audio) */
        if (decoding_demux)
                decoding_demux->flush();
@@ -1608,7 +1885,7 @@ void eCueSheet::seekTo(int relative, const pts_t &pts)
        m_lock.Unlock();
        m_event(evtSeek);
 }
        m_lock.Unlock();
        m_event(evtSeek);
 }
-       
+
 void eCueSheet::clear()
 {
        m_lock.WrLock();
 void eCueSheet::clear()
 {
        m_lock.WrLock();
@@ -1618,7 +1895,7 @@ void eCueSheet::clear()
 
 void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
 {
 
 void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
 {
-       assert(begin < end);
+       ASSERT(begin < end);
        m_lock.WrLock();
        m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
        m_lock.Unlock();
        m_lock.WrLock();
        m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
        m_lock.Unlock();