fix comment
[enigma2.git] / lib / dvb / dvb.cpp
index 119025073a10e7c4fcce60300cff830a8a53b4c3..4dd5526a2d16baf0033e6d5f8d4f5618d9e9d08e 100644 (file)
@@ -4,6 +4,7 @@
 #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>
@@ -61,31 +62,32 @@ ePtr<eDVBResourceManager> NewResourceManagerPtr(void)
 }
 
 eDVBResourceManager::eDVBResourceManager()
-       :m_releaseCachedChannelTimer(eApp)
+       :m_releaseCachedChannelTimer(eTimer::create(eApp))
 {
        avail = 1;
        busy = 0;
-       m_sec = new eDVBSatelliteEquipmentControl(m_frontend);
+       m_sec = new eDVBSatelliteEquipmentControl(m_frontend, m_simulate_frontend);
+
        if (!instance)
                instance = this;
-               
+
                /* search available adapters... */
 
                // add linux devices
-       
+
        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());
+
+       eDebug("found %d adapter, %d frontends(%d sim) and %d demux",
+               m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size());
 
        eDVBCAService::registerChannelCallback(this);
 
-       CONNECT(m_releaseCachedChannelTimer.timeout, eDVBResourceManager::releaseCachedChannel);
+       CONNECT(m_releaseCachedChannelTimer->timeout, eDVBResourceManager::releaseCachedChannel);
 }
 
 void eDVBResourceManager::feStateChanged()
@@ -102,7 +104,7 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
 {
                // scan frontends
        int num_fe = 0;
-       
+
        eDebug("scanning for frontends..");
        while (1)
        {
@@ -117,13 +119,21 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
                        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;
        }
-       
+
                // scan demux
        int num_demux = 0;
        while (1)
@@ -138,10 +148,10 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
                if (stat(filename, &s))
                        break;
                ePtr<eDVBDemux> demux;
-               
+
                demux = new eDVBDemux(m_nr, num_demux);
                m_demux.push_back(demux);
-                       
+
                ++num_demux;
        }
 }
@@ -159,12 +169,12 @@ RESULT eDVBAdapterLinux::getDemux(ePtr<eDVBDemux> &demux, int nr)
                --nr;
                ++i;
        }
-       
+
        if (i != m_demux.end())
                demux = *i;
        else
                return -1;
-               
+
        return 0;
 }
 
@@ -173,20 +183,20 @@ int eDVBAdapterLinux::getNumFrontends()
        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;
        }
-       
+
        if (i != m_frontend.end())
                fe = *i;
        else
                return -1;
-               
+
        return 0;
 }
 
@@ -214,9 +224,9 @@ void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
 {
        int num_fe = adapter->getNumFrontends();
        int num_demux = adapter->getNumDemux();
-       
+
        m_adapter.push_back(adapter);
-       
+
        int i;
        for (i=0; i<num_demux; ++i)
        {
@@ -249,6 +259,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)
@@ -273,16 +309,24 @@ PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list)
                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;
 }
 
-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;
 
-       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);
 
@@ -291,12 +335,15 @@ RESULT eDVBResourceManager::allocateFrontend(ePtr<eDVBAllocatedFrontend> &fe, eP
 
                if (!i->m_inuse)
                {
+//                     eDebug("Slot %d, score %d", i->m_frontend->getSlotID(), c);
                        if (c > bestval)
                        {
                                bestval = c;
                                best = i;
                        }
                }
+//             else
+//                     eDebug("Slot %d, score %d... but BUSY!!!!!!!!!!!", i->m_frontend->getSlotID(), c);
        }
 
        if (best)
@@ -379,10 +426,10 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
 
        if (i == m_demux.end())
                return -1;
-       
+
        ePtr<eDVBRegisteredDemux> unused;
-       
-       if (m_demux.length() < 5)
+
+       if (m_demux.size() < 5)
        {
                /* FIXME: hardware demux policy */
                if (!(cap & iDVBChannel::capDecode))
@@ -397,14 +444,14 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
                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;
-               
+
                        if ((!in_use) && ((!fe) || (i->m_adapter == fe->m_adapter)))
                        {
                                if ((cap & iDVBChannel::capDecode) && !is_decode)
                                        continue;
-                               unused = i;     
+                               unused = i;
                                break;
                        }
                }
@@ -420,7 +467,7 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
                                        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);
@@ -467,11 +514,24 @@ RESULT eDVBResourceManager::getChannelList(ePtr<iDVBChannelList> &list)
                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. */
+       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())
@@ -482,47 +542,46 @@ RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUse
                }
                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)
                {
-//                     eDebug("found shared channel..");
+                       eDebugNoSimulate("found shared channel..");
                        channel = i->m_channel;
                        return 0;
                }
        }
-       
+
        /* 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))
        {
-               eDebug("channel not found!");
+               eDebugNoSimulate("channel not found!");
                return errChannelNotInList;
        }
 
        /* allocate a frontend. */
-       
+
        ePtr<eDVBAllocatedFrontend> fe;
 
-       int err = allocateFrontend(fe, feparm);
+       int err = allocateFrontend(fe, feparm, simulate);
        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)
@@ -530,9 +589,15 @@ RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUse
                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;
 }
@@ -547,13 +612,13 @@ void eDVBResourceManager::DVBChannelStateChanged(iDVBChannel *chan)
                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");
-                       m_releaseCachedChannelTimer.start(3000, true);
+                       m_releaseCachedChannelTimer->start(3000, true);
                        break;
                }
                default: // ignore all other events
@@ -575,17 +640,14 @@ RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel, in
        {
                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;
 
-       eDVBChannel *ch;
-       ch = new eDVBChannel(this, fe);
-
-       channel = ch;
+       channel = new eDVBChannel(this, fe);
        return 0;
 }
 
@@ -594,42 +656,55 @@ RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
 {
        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_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)
 {
-       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)
 {
-       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;
 }
 
@@ -639,12 +714,13 @@ RESULT eDVBResourceManager::connectChannelAdded(const Slot1<void,eDVBChannel*> &
        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;
 
-       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);
@@ -681,10 +757,11 @@ int tuner_type_channel_default(ePtr<iDVBChannelList> &channellist, const eDVBCha
        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;
-       if (m_cached_channel)
+       if (!simulate && m_cached_channel)
        {
                eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
                if(channelid==cache_chan->getChannelID())
@@ -693,7 +770,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());
-       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)
@@ -706,8 +783,9 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
        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)
                {
@@ -721,7 +799,7 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                                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) )
                                                {
@@ -748,7 +826,8 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                                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) )
                                                {
@@ -778,7 +857,7 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                goto error;
        }
 
-       ret = canAllocateFrontend(feparm);
+       ret = canAllocateFrontend(feparm, simulate);
 
 error:
        if (decremented_fe_usecount)
@@ -895,7 +974,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
        }
 #endif
 
-#if 1 /* not yet */
+#if 1 /* This codepath is required on Broadcom-based Dreamboxes (DM800, DM8000) and strips away non-I-frames. */
        if (!m_iframe_search)
                return len;
 
@@ -904,7 +983,7 @@ 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;
-       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 */
@@ -920,7 +999,7 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
 
                        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;
@@ -966,7 +1045,6 @@ int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, s
                        d += 4;
                } else
                        d += 4; /* ignore */
-
        }
 
        if (m_iframe_state == 1)
@@ -985,9 +1063,9 @@ eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *fronte
        m_frontend = frontend;
 
        m_pvr_thread = 0;
-       
+
        m_skipmode_n = m_skipmode_m = 0;
-       
+
        if (m_frontend)
                m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
 }
@@ -1003,14 +1081,14 @@ eDVBChannel::~eDVBChannel()
 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 (fe->getState(state))
                return;
-       
+
        if (state == iDVBFrontend::stateLock)
        {
                eDebug("OURSTATE: ok");
@@ -1039,7 +1117,7 @@ void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
                ourstate = state_failed;
        } else
                eFatal("state unknown");
-       
+
        if (ourstate != m_state)
        {
                m_state = ourstate;
@@ -1156,7 +1234,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);
-       
+
        if (!m_cue)
        {
                eDebug("no cue sheet. forcing normal play");
@@ -1227,7 +1305,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                                continue;
                        }
                }
-               
+
                if (relative == 1) /* pts relative */
                {
                        pts += now;
@@ -1238,7 +1316,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                if (relative != 2)
                        if (pts < 0)
                                pts = 0;
-               
+
                if (relative == 2) /* AP relative */
                {
                        eDebug("AP relative seeking: %lld, at %lld", pts, now);
@@ -1253,7 +1331,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                                pts = nextap;
                        }
                }
-               
+
                off_t offset = 0;
                if (m_tstools.getOffset(offset, pts))
                {
@@ -1271,7 +1349,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);
-       
+
                if ((current_offset >= aligned_start) && (current_offset < aligned_end))
                {
                        start = current_offset;
@@ -1370,7 +1448,7 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
 {
        if (m_channel_id)
                m_mgr->removeChannel(this);
-               
+
        if (!channelid)
                return 0;
 
@@ -1379,7 +1457,7 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
                eDebug("no frontend to tune!");
                return -ENODEV;
        }
-       
+
        m_channel_id = channelid;
        m_mgr->addChannel(channelid, this);
        m_state = state_tuning;
@@ -1387,14 +1465,14 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
        int res;
        res = m_frontend->get().tune(*feparm);
        m_current_frontend_parameters = feparm;
-       
+
        if (res)
        {
                m_state = state_release;
                m_stateChanged(this);
                return res;
        }
-       
+
        return 0;
 }
 
@@ -1421,24 +1499,79 @@ RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
        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;
-       
+
        if (!our_demux)
        {
                demux = 0;
-               
+
                if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
                        return -1;
        }
-       
+
        demux = *our_demux;
                /* 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',
-                  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. */
        if (cap & capDecode)
                our_demux = 0;
@@ -1471,12 +1604,12 @@ RESULT eDVBChannel::playFile(const char *file)
                delete m_pvr_thread;
                m_pvr_thread = 0;
        }
-       
+
        m_tstools.openFile(file);
-       
+
                /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
                   THEN DO A REAL FIX HERE! */
-       
+
                /* (this codepath needs to be improved anyway.) */
 #if HAVE_DVB_API_VERSION < 3
        m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
@@ -1537,11 +1670,11 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
 {
        if (!decoding_demux)
                return -1;
-       
+
        pts_t now;
-       
+
        int r;
-       
+
        if (mode == 0) /* demux */
        {
                r = decoding_demux->getSTC(now, 0);
@@ -1552,7 +1685,7 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
                }
        } else
                now = pos; /* fixup supplied */
-       
+
        off_t off = 0; /* TODO: fixme */
        r = m_tstools.fixupPTS(off, now);
        if (r)
@@ -1560,9 +1693,9 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
                eDebug("fixup PTS failed");
                return -1;
        }
-       
+
        pos = now;
-       
+
        return 0;
 }
 
@@ -1573,7 +1706,7 @@ void eDVBChannel::flushPVR(iDVBDemux *decoding_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.
@@ -1584,7 +1717,7 @@ void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
        m_pvr_thread->flush();
                /* HACK: flush PVR buffer */
        ::ioctl(m_pvr_fd_dst, 0);
-       
+
                /* flush ratebuffers (video, audio) */
        if (decoding_demux)
                decoding_demux->flush();
@@ -1608,7 +1741,7 @@ void eCueSheet::seekTo(int relative, const pts_t &pts)
        m_lock.Unlock();
        m_event(evtSeek);
 }
-       
+
 void eCueSheet::clear()
 {
        m_lock.WrLock();