[FCC] Fix to perform normal decoding for usb tuner on FCC mode.
[vuplus_dvbapp] / lib / dvb / dvb.cpp
old mode 100755 (executable)
new mode 100644 (file)
index af46823..b7b831b
@@ -6,12 +6,16 @@
 #include <lib/dvb/sec.h>
 #include <lib/dvb/specs.h>
 
+#include <lib/dvb/fbc.h>
+#include <lib/dvb/fcc.h>
+
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
+#include <fstream>
 
 DEFINE_REF(eDVBRegisteredFrontend);
 DEFINE_REF(eDVBRegisteredDemux);
@@ -26,6 +30,14 @@ eDVBAllocatedFrontend::eDVBAllocatedFrontend(eDVBRegisteredFrontend *fe): m_fe(f
 eDVBAllocatedFrontend::~eDVBAllocatedFrontend()
 {
        m_fe->dec_use();
+       if (m_fe->m_frontend->is_FBCTuner())
+       {
+               eFBCTunerManager* fbcmng = eFBCTunerManager::getInstance();
+               if (fbcmng)
+               {
+                       fbcmng->unLink(m_fe);
+               }
+       }
 }
 
 DEFINE_REF(eDVBAllocatedDemux);
@@ -82,6 +94,8 @@ eDVBResourceManager::eDVBResourceManager()
                num_adapter++;
        }
 
+       setUsbTuner();
+
        int fd = open("/proc/stb/info/model", O_RDONLY);
        char tmp[255];
        int rd = fd >= 0 ? read(fd, tmp, 255) : 0;
@@ -115,6 +129,8 @@ eDVBResourceManager::eDVBResourceManager()
 
        eDVBCAService::registerChannelCallback(this);
 
+       m_fbc_mng = new eFBCTunerManager(this);
+
        CONNECT(m_releaseCachedChannelTimer->timeout, eDVBResourceManager::releaseCachedChannel);
 }
 
@@ -270,14 +286,12 @@ void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
                ePtr<eDVBFrontend> frontend;
                if (!adapter->getFrontend(frontend, i))
                {
-                       int frontendType=0;
-                       frontend->getFrontendType(frontendType);
                        eDVBRegisteredFrontend *new_fe = new eDVBRegisteredFrontend(frontend, adapter);
                        CONNECT(new_fe->stateChanged, eDVBResourceManager::feStateChanged);
                        m_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 (frontend->supportsDeliverySystem(SYS_DVBT, false) || frontend->supportsDeliverySystem(SYS_DVBT2, false))
                        {
                                if (prev_dvbt_frontend)
                                {
@@ -295,14 +309,12 @@ void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
                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 (frontend->supportsDeliverySystem(SYS_DVBT, false) || frontend->supportsDeliverySystem(SYS_DVBT2, false))
                        {
                                if (prev_dvbt_frontend)
                                {
@@ -316,6 +328,59 @@ void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
 
 }
 
+void eDVBResourceManager::setUsbTuner()
+{
+       std::ifstream in("/proc/bus/nim_sockets");
+       std::string line;
+
+       int res = -1;
+       int fe_idx = -1;
+       int usbtuner_idx[8] = {0};
+       int usbtuner_count = 0;
+
+       if (in.is_open())
+       {
+               while(!in.eof())
+               {
+                       getline(in, line);
+                       if ((res = sscanf(line.c_str(), "NIM Socket %d:", &fe_idx)) == 1)
+                               continue;
+
+                       if ((fe_idx != -1) && (line.find("\tName: ") == 0) && (line.find("VTUNER") != -1))
+                               usbtuner_idx[usbtuner_count++] = fe_idx;
+               }
+               in.close();
+       }
+
+       if (usbtuner_count)
+       {
+               for (eSmartPtrList<eDVBRegisteredFrontend>::iterator it(m_frontend.begin()); it != m_frontend.end(); ++it)
+               {
+                       int slotid = it->m_frontend->getSlotID();
+                       for (int i=0; i < usbtuner_count ; i++)
+                       {
+                               if (slotid == usbtuner_idx[i])
+                               {
+                                       it->m_frontend->setUSBTuner(true);
+                                       break;
+                               }
+                       }
+               }
+               for (eSmartPtrList<eDVBRegisteredFrontend>::iterator it(m_simulate_frontend.begin()); it != m_simulate_frontend.end(); ++it)
+               {
+                       int slotid = it->m_frontend->getSlotID();
+                       for (int i=0; i < usbtuner_count ; i++)
+                       {
+                               if (slotid == usbtuner_idx[i])
+                               {
+                                       it->m_frontend->setUSBTuner(true);
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
 PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list)
 {
        if (!PyList_Check(list))
@@ -355,35 +420,139 @@ PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list)
        Py_RETURN_NONE;
 }
 
+bool eDVBResourceManager::frontendIsCompatible(int index, const char *type)
+{
+       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
+       {
+               if (i->m_frontend->getSlotID() == index)
+               {
+                       if (!strcmp(type, "DVB-S2"))
+                       {
+                               return i->m_frontend->supportsDeliverySystem(SYS_DVBS2, false);
+                       }
+                       else if (!strcmp(type, "DVB-S"))
+                       {
+                               return i->m_frontend->supportsDeliverySystem(SYS_DVBS, false);
+                       }
+                       else if (!strcmp(type, "DVB-T2"))
+                       {
+                               return i->m_frontend->supportsDeliverySystem(SYS_DVBT2, false);
+                       }
+                       else if (!strcmp(type, "DVB-T"))
+                       {
+                               return i->m_frontend->supportsDeliverySystem(SYS_DVBT, false);
+                       }
+                       else if (!strcmp(type, "DVB-C"))
+                       {
+#if DVB_API_VERSION > 5 || DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= 6
+                               return i->m_frontend->supportsDeliverySystem(SYS_DVBC_ANNEX_A, false);
+#else
+                               return i->m_frontend->supportsDeliverySystem(SYS_DVBC_ANNEX_AC, false);
+#endif
+                       }
+               }
+       }
+       return false;
+}
+
+void eDVBResourceManager::setFrontendType(int index, const char *type)
+{
+       eDebug("[eDVBResourceManager::setFrontendType] index : %d, type : %s", index, type);
+
+       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
+       {
+               if (i->m_frontend->getSlotID() == index)
+               {
+                       std::vector<fe_delivery_system_t> whitelist;
+                       if (!strcmp(type, "DVB-S2") || !strcmp(type, "DVB-S"))
+                       {
+                               whitelist.push_back(SYS_DVBS);
+                               whitelist.push_back(SYS_DVBS2);
+                       }
+                       else if (!strcmp(type, "DVB-T2") || !strcmp(type, "DVB-T"))
+                       {
+                               whitelist.push_back(SYS_DVBT);
+                               whitelist.push_back(SYS_DVBT2);
+                       }
+                       else if (!strcmp(type, "DVB-C"))
+                       {
+#if DVB_API_VERSION > 5 || DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= 6
+                               whitelist.push_back(SYS_DVBC_ANNEX_A);
+#else
+                               whitelist.push_back(SYS_DVBC_ANNEX_AC);
+#endif
+                       }
+                       i->m_frontend->setDeliverySystemWhitelist(whitelist);
+                       break;
+               }
+       }
+}
+
 RESULT eDVBResourceManager::allocateFrontend(ePtr<eDVBAllocatedFrontend> &fe, ePtr<iDVBFrontendParameters> &feparm, bool simulate)
 {
        eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
-       ePtr<eDVBRegisteredFrontend> best;
+       eDVBRegisteredFrontend *best = NULL;
        int bestval = 0;
        int foundone = 0;
 
+       int check_fbc_leaf_linkable = 0;
+       int current_fbc_setid = -1;
+       eDVBRegisteredFrontend *fbc_fe = NULL;
+       eDVBRegisteredFrontend *best_fbc_fe = NULL;
+
        for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(frontends.begin()); i != frontends.end(); ++i)
        {
-               int c = i->m_frontend->isCompatibleWith(feparm);
+               int c = 0;
+               fbc_fe = NULL;
+               if (i->m_frontend->is_FBCTuner() && m_fbc_mng->canLink(*i))
+               {
+                       int fbc_setid = m_fbc_mng->getFBCSetID(i->m_frontend->getSlotID());
+                       if (fbc_setid != current_fbc_setid)
+                       {
+                               current_fbc_setid = fbc_setid;
+                               check_fbc_leaf_linkable = 0;
+                       }
+
+                       if (!check_fbc_leaf_linkable)
+                       {
+                               c = m_fbc_mng->isCompatibleWith(feparm, *i, fbc_fe, simulate);
+                               check_fbc_leaf_linkable = 1;
+                               eDebug("[eDVBResourceManager::allocateFrontend] m_fbc_mng->isCompatibleWith slotid : %p (%d), fbc_fe : %p (%d), score : %d", (eDVBRegisteredFrontend *)*i,  i->m_frontend->getSlotID(), fbc_fe, fbc_fe?fbc_fe->m_frontend->getSlotID():-1, c);
+                       }
+               }
+               else
+               {
+                       c = i->m_frontend->isCompatibleWith(feparm);
+               }
 
                if (c)  /* if we have at least one frontend which is compatible with the source, flag this. */
                        foundone = 1;
 
                if (!i->m_inuse)
                {
-//                     eDebug("Slot %d, score %d", i->m_frontend->getSlotID(), c);
+                       eDebug("Slot %d, score %d", i->m_frontend->getSlotID(), c);
                        if (c > bestval)
                        {
                                bestval = c;
-                               best = i;
+                               best = *i;
+                               best_fbc_fe = fbc_fe;
                        }
                }
-//             else
-//                     eDebug("Slot %d, score %d... but BUSY!!!!!!!!!!!", i->m_frontend->getSlotID(), c);
+               else
+               {
+                       eDebug("Slot %d, score %d... but BUSY!!!!!!!!!!!", i->m_frontend->getSlotID(), c);
+               }
+
+               eDVBRegisteredFrontend *tmp = *i;
        }
 
        if (best)
        {
+               if (best_fbc_fe)
+               {
+                       m_fbc_mng->addLink(best, best_fbc_fe, simulate);
+               }
+
                fe = new eDVBAllocatedFrontend(best);
                return 0;
        }
@@ -525,30 +694,46 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA
        }
        else if (m_boxtype == DM8000 || m_boxtype == DM500HD || m_boxtype == DM800SE || m_boxtype == DM7020HD)
        {
+               iDVBAdapter *adapter = fe ? fe->m_adapter : m_adapter.begin(); /* look for a demux on the same adapter as the frontend, or the first adapter for dvr playback */
+               int source = fe ? fe->m_frontend->getDVBID() : -1;
                cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
-               for (; i != m_demux.end(); ++i, ++n)
+               if (!fe)
                {
-                       if (fe)
+                       /*
+                        * For pvr playback, start with the last demux.
+                        * On some hardware, we have less ca devices than demuxes,
+                        * so we should try to leave the first demuxes for live tv,
+                        * and start with the last for pvr playback
+                        */
+                       i = m_demux.end();
+                       --i;
+               }
+               while (i != m_demux.end())
+               {
+                       if (i->m_adapter == adapter)
                        {
                                if (!i->m_inuse)
                                {
-                                       if (!unused)
-                                               unused = i;
+                                       /* mark the first unused demux, we'll use that when we do not find a better match */
+                                       if (!unused) unused = i;
                                }
-                               else if (i->m_adapter == fe->m_adapter &&
-                                   i->m_demux->getSource() == fe->m_frontend->getDVBID())
+                               else
                                {
-                                       demux = new eDVBAllocatedDemux(i);
-                                       return 0;
+                                       /* demux is in use, see if we can share it */
+                                       if (source >= 0 && i->m_demux->getSource() == source)
+                                       {
+                                               demux = new eDVBAllocatedDemux(i);
+                                               return 0;
+                                       }
                                }
                        }
-                       else if (n == 4) // always use demux4 for PVR (demux 4 can not descramble...)
+                       if (fe)
                        {
-                               if (i->m_inuse) {
-                                       demux = new eDVBAllocatedDemux(i);
-                                       return 0;
-                               }
-                               unused = i;
+                               ++i;
+                       }
+                       else
+                       {
+                               --i;
                        }
                }
        }
@@ -587,12 +772,6 @@ RESULT eDVBResourceManager::getChannelList(ePtr<iDVBChannelList> &list)
                if (!simulate) \
                        eDebug(x); \
        } while(0)
-//             else \
-//             { \
-//                     eDebugNoNewLine("SIMULATE:"); \
-//                     eDebug(x); \
-//             } \
-
 
 RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel, bool simulate)
 {
@@ -719,8 +898,7 @@ RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel, in
        return 0;
 }
 
-
-RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
+RESULT eDVBResourceManager::allocatePVRChannel(const eDVBChannelID &channelid, eUsePtr<iDVBPVRChannel> &channel)
 {
        ePtr<eDVBAllocatedDemux> demux;
 
@@ -731,7 +909,18 @@ RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
                m_releaseCachedChannelTimer->stop();
        }
 
-       channel = new eDVBChannel(this, 0);
+       ePtr<eDVBChannel> ch = new eDVBChannel(this, 0);
+       if (channelid)
+       {
+               /*
+                * user provided a channelid, with the clear intention for
+                * this channel to be registered at the resource manager.
+                * (allowing e.g. epgcache to be started)
+                */
+               ePtr<iDVBFrontendParameters> feparm;
+               ch->setChannel(channelid, feparm);
+       }
+       channel = ch;
        return 0;
 }
 
@@ -787,28 +976,52 @@ int eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &fepar
        eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
        ePtr<eDVBRegisteredFrontend> best;
        int bestval = 0;
+       int check_fbc_leaf_linkable = 0;
+       int current_fbc_setid = -1;
 
        for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(frontends.begin()); i != frontends.end(); ++i)
+       {
                if (!i->m_inuse)
                {
-                       int c = i->m_frontend->isCompatibleWith(feparm);
+                       int c = 0;
+                       if (i->m_frontend->is_FBCTuner() && m_fbc_mng->canLink(*i))
+                       {
+                               int fbc_setid = m_fbc_mng->getFBCSetID(i->m_frontend->getSlotID());
+                               if (fbc_setid != current_fbc_setid)
+                               {
+                                       current_fbc_setid = fbc_setid;
+                                       check_fbc_leaf_linkable = 0;
+                               }
+
+                               if (!check_fbc_leaf_linkable)
+                               {
+                                       eDVBRegisteredFrontend *dummy;
+                                       c = m_fbc_mng->isCompatibleWith(feparm, *i, dummy, simulate);
+                                       check_fbc_leaf_linkable = 1;
+                               }
+                       }
+                       else
+                       {
+                               c = i->m_frontend->isCompatibleWith(feparm);
+                       }
                        if (c > bestval)
                                bestval = c;
                }
+       }
        return bestval;
 }
 
-int tuner_type_channel_default(ePtr<iDVBChannelList> &channellist, const eDVBChannelID &chid)
+int tuner_type_channel_default(ePtr<iDVBChannelList> &channellist, const eDVBChannelID &chid, int &system)
 {
+       system = iDVBFrontend::feSatellite;
        if (channellist)
        {
                ePtr<iDVBFrontendParameters> feparm;
                if (!channellist->getChannelFrontendData(chid, feparm))
                {
-                       int system;
                        if (!feparm->getSystem(system))
                        {
-                               switch(system)
+                               switch (system)
                                {
                                        case iDVBFrontend::feSatellite:
                                                return 50000;
@@ -825,15 +1038,16 @@ int tuner_type_channel_default(ePtr<iDVBChannelList> &channellist, const eDVBCha
        return 0;
 }
 
-int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore, bool simulate)
+int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore, int &system, bool simulate)
 {
        std::list<active_channel> &active_channels = simulate ? m_active_simulate_channels : m_active_channels;
-       int ret=0;
+       int ret = 0;
+       system = iDVBFrontend::feSatellite;
        if (!simulate && m_cached_channel)
        {
                eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
                if(channelid==cache_chan->getChannelID())
-                       return tuner_type_channel_default(m_list, channelid);
+                       return tuner_type_channel_default(m_list, channelid, system);
        }
 
                /* first, check if a channel is already existing. */
@@ -844,13 +1058,63 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                if (i->m_channel_id == channelid)
                {
 //                     eDebug("found shared channel..");
-                       return tuner_type_channel_default(m_list, channelid);
+                       return tuner_type_channel_default(m_list, channelid, system);
                }
        }
 
        int *decremented_cached_channel_fe_usecount=NULL,
                *decremented_fe_usecount=NULL;
 
+       /* check FCC channels */
+       std::vector<int*> fcc_decremented_fe_usecounts;
+       std::map<eDVBChannelID, int> fcc_chids;
+       int apply_to_ignore = 0;
+       if (!eFCCServiceManager::getFCCChannelID(fcc_chids))
+       {
+               for (std::map<eDVBChannelID, int>::iterator i(fcc_chids.begin()); i != fcc_chids.end(); ++i)
+               {
+                       //eDebug("[eDVBResourceManager::canAllocateChannel] FCC NS : %08x, TSID : %04x, ONID : %04x", i->first.dvbnamespace.get(), i->first.transport_stream_id.get(), i->first.original_network_id.get());
+                       if (ignore == i->first)
+                       {
+                               apply_to_ignore = i->second;
+                               continue;
+                       }
+                       for (std::list<active_channel>::iterator ii(active_channels.begin()); ii != active_channels.end(); ++ii)
+                       {
+                               eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
+                               if (ii->m_channel_id == i->first)
+                               {
+                                       eDVBChannel *channel = (eDVBChannel*) &(*ii->m_channel);
+
+                                       int check_usecount = channel == &(*m_cached_channel) ? 1 : 0;
+                                       check_usecount += i->second * 2; // one is used in eDVBServicePMTHandler and another is used in eDVBScan.
+                                       //eDebug("[eDVBResourceManager::canAllocateChannel] channel->getUseCount() : %d , check_usecount : %d (cached : %d)", channel->getUseCount(), check_usecount, channel == &(*m_cached_channel));
+                                       if (channel->getUseCount() == check_usecount)
+                                       {
+                                               ePtr<iDVBFrontend> fe;
+                                               if (!ii->m_channel->getFrontend(fe))
+                                               {
+                                                       for (eSmartPtrList<eDVBRegisteredFrontend>::iterator iii(frontends.begin()); iii != frontends.end(); ++iii)
+                                                       {
+                                                               if ( &(*fe) == &(*iii->m_frontend) )
+                                                               {
+                                                                       //eDebug("[eDVBResourceManager::canAllocateChannel] fcc : decrease fcc fe use_count! feid : %d (%d -> %d)", iii->m_frontend->getSlotID(), iii->m_inuse, iii->m_inuse-1);
+                                                                       --iii->m_inuse;
+                                                                       int *tmp_decremented_fe_usecount = &iii->m_inuse;
+                                                                       fcc_decremented_fe_usecounts.push_back(tmp_decremented_fe_usecount);
+                                                                       if (channel == &(*m_cached_channel))
+                                                                               decremented_cached_channel_fe_usecount = tmp_decremented_fe_usecount;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       break;
+                               }
+                       }
+               }
+       }
+
        for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
        {
                eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
@@ -862,7 +1126,10 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                        // another on eUsePtr<iDVBChannel> is used in the eDVBScan instance used in eDVBServicePMTHandler (for SDT scan)
                        // so we must check here if usecount is 3 (when the channel is equal to the cached channel)
                        // or 2 when the cached channel is not equal to the compared channel
-                       if (channel == &(*m_cached_channel) ? channel->getUseCount() == 3 : channel->getUseCount() == 2)  // channel only used once..
+                       int check_usecount = channel == &(*m_cached_channel) ? 1 : 0;
+                       check_usecount += (apply_to_ignore+1) * 2; // one is used in eDVBServicePMTHandler and another is used in eDVBScan.
+                       //eDebug("[eDVBResourceManager::canAllocateChannel] channel->getUseCount() : %d , check_usecount : %d (cached : %d)", channel->getUseCount(), check_usecount, channel == &(*m_cached_channel));
+                       if (channel->getUseCount() == check_usecount)  // channel only used once..(except fcc)
                        {
                                ePtr<iDVBFrontend> fe;
                                if (!i->m_channel->getFrontend(fe))
@@ -871,6 +1138,7 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                                        {
                                                if ( &(*fe) == &(*ii->m_frontend) )
                                                {
+                                                       //eDebug("[eDVBResourceManager::canAllocateChannel] ignore : decrease fcc fe use_count! feid : %d (%d -> %d)", ii->m_frontend->getSlotID(), ii->m_inuse, ii->m_inuse-1);
                                                        --ii->m_inuse;
                                                        decremented_fe_usecount = &ii->m_inuse;
                                                        if (channel == &(*m_cached_channel))
@@ -924,6 +1192,7 @@ int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, cons
                eDebug("channel not found!");
                goto error;
        }
+       feparm->getSystem(system);
 
        ret = canAllocateFrontend(feparm, simulate);
 
@@ -932,6 +1201,14 @@ error:
                ++(*decremented_fe_usecount);
        if (decremented_cached_channel_fe_usecount)
                ++(*decremented_cached_channel_fe_usecount);
+       if (fcc_decremented_fe_usecounts.size())
+       {
+               for (std::vector<int*>::iterator i(fcc_decremented_fe_usecounts.begin()); i != fcc_decremented_fe_usecounts.end(); ++i)
+               {
+                       //eDebug("[eDVBResourceManager::canAllocateChannel] fcc : increase fcc fe use_count!");
+                       ++(**i);
+               }
+       }
 
        return ret;
 }
@@ -1187,7 +1464,7 @@ void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
                }
        } else if (state == iDVBFrontend::stateFailed)
        {
-#ifdef BUILD_VUPLUS /* ikseong  */
+#ifdef BUILD_VUPLUS
                if (m_current_frontend_parameters)
                {
                        eDebug("OURSTATE: lost lock, trying to retune");
@@ -1225,6 +1502,10 @@ void eDVBChannel::pvrEvent(int event)
                eDebug("SOF");
                m_event(this, evtSOF);
                break;
+       case eFilePushThread::evtUser+3: /* limit space */
+               eDebug("Too large file");
+               m_event(this, evtFailed+3);
+               break;
        }
 }
 
@@ -1326,8 +1607,9 @@ static inline long long align_with_len(long long x, int align, size_t &len)
        if (sign)
                x = -x;
 
-       x -= x % align;
-       len += x % align;
+       int r = x % align;
+       x -= r;
+       len += r;
 
        if (sign)
                x = -x;
@@ -1356,13 +1638,13 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                max = align(m_skipmode_n, blocksize);
        }
 
-       eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m);
+       eDebug("getNextSourceSpan, current offset is %08lld, m_skipmode_m = %d!", current_offset, m_skipmode_m);
        int frame_skip_success = 0;
 
        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);
+               eDebug("we are at %lld, 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;
@@ -1383,20 +1665,24 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
        if (!frame_skip_success)
        {
                current_offset += align(m_skipmode_m, blocksize);
-               
-               if (m_skipmode_m)
+               if(current_offset < 0)
+                       current_offset = 0;
+               else
                {
-                       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
+                       if (m_skipmode_m)
                        {
-                               current_offset = align_with_len(iframe_start, blocksize, iframe_len);
-                               max = align(iframe_len, blocksize);
+                               eDebug("we are at %lld, and we try to find the iframe here:", current_offset);
+                               size_t iframe_len;
+                               off_t start_offset = current_offset;
+                               off_t new_offset = start_offset;
+                               int direction = (m_skipmode_m < 0) ? -1 : +1;
+                               if (m_tstools.findFrame(start_offset, new_offset, iframe_len, direction))
+                                       eDebug("failed");
+                               else
+                               {
+                                       current_offset = align_with_len(new_offset, blocksize, iframe_len);
+                                       max = align(iframe_len, blocksize);
+                               }
                        }
                }
        }
@@ -1554,18 +1840,19 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                }
        }
 
+       if(current_offset <0)
+               current_offset =0;
        if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0))
        {
                eDebug("reached SOF");
                m_skipmode_m = 0;
                m_pvr_thread->sendEvent(eFilePushThread::evtUser);
        }
-
        if (m_source_span.empty())
        {
                start = current_offset;
                size = max;
-               eDebug("NO CUESHEET. (%08llx, %zd)", start, size);
+               eDebug("NO CUESHEET. (%08lld, %zd)", start, size);
        } else
        {
                start = current_offset;
@@ -1605,14 +1892,15 @@ RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontend
        if (!channelid)
                return 0;
 
+       m_channel_id = channelid;
+       m_mgr->addChannel(channelid, this);
+
        if (!m_frontend)
        {
-               eDebug("no frontend to tune!");
-               return -ENODEV;
+               /* no frontend, no need to tune (must be a streamed service) */
+               return 0;
        }
 
-       m_channel_id = channelid;
-       m_mgr->addChannel(channelid, this);
        m_state = state_tuning;
                        /* if tuning fails, shutdown the channel immediately. */
        int res;
@@ -1723,6 +2011,12 @@ RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
 {
        ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
 
+       if (m_frontend == NULL)
+       {
+               /* in dvr mode, we have to stick to a single demux (the one connected to our dvr device) */
+               our_demux = m_decoder_demux ? m_decoder_demux : m_demux;
+       }
+
        if (!our_demux)
        {
                demux = 0;
@@ -1791,13 +2085,14 @@ RESULT eDVBChannel::playSource(ePtr<iTsSource> &source, const char *streaminfo_f
                m_pvr_thread = 0;
        }
 
-       if (!source->valid())
+       if (!source->valid() && !source->isStream())
        {
                eDebug("PVR source is not valid!");
                return -ENOENT;
        }
 
-       m_tstools.setSource(source, streaminfo_file);
+       m_source = source;
+       m_tstools.setSource(m_source, streaminfo_file);
 
                /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
                   THEN DO A REAL FIX HERE! */
@@ -1833,12 +2128,13 @@ RESULT eDVBChannel::playSource(ePtr<iTsSource> &source, const char *streaminfo_f
 
        m_pvr_thread = new eDVBChannelFilePush();
        m_pvr_thread->enablePVRCommit(1);
-       m_pvr_thread->setStreamMode(1);
+       /* If the source specifies a length, it's a file. If not, it's a stream */
+       m_pvr_thread->setStreamMode(m_source->isStream());
        m_pvr_thread->setScatterGather(this);
 
        m_event(this, evtPreStart);
 
-       m_pvr_thread->start(source, m_pvr_fd_dst);
+       m_pvr_thread->start(m_source, m_pvr_fd_dst);
        CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent);
 
        m_state = state_ok;
@@ -1856,9 +2152,13 @@ void eDVBChannel::stopSource()
                m_pvr_thread = 0;
        }
        if (m_pvr_fd_dst >= 0)
+       {
                ::close(m_pvr_fd_dst);
-       ePtr<iTsSource> d;
-       m_tstools.setSource(d);
+               m_pvr_fd_dst = -1;
+       }
+
+       m_source = NULL;
+       m_tstools.setSource(m_source);
 }
 
 void eDVBChannel::stopFile()
@@ -1899,8 +2199,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);
+       r = m_tstools.fixupPTS(m_source ? m_source->offset() : 0, now);
        if (r)
        {
                eDebug("fixup PTS failed");