Add QuadPiP plugin.
[vuplus_dvbapp] / lib / service / servicedvb.cpp
old mode 100644 (file)
new mode 100755 (executable)
index 3e58091..f740b47
@@ -17,6 +17,9 @@
 #include <lib/dvb/tstools.h>
 #include <lib/python/python.h>
 #include <lib/base/nconfig.h> // access to python config
+#include <lib/base/httpstream.h>
+
+#include <lib/service/servicedvbfcc.h>
 
                /* for subtitles */
 #include <lib/gui/esubtitle.h>
@@ -27,6 +30,8 @@
 #include <byteswap.h>
 #include <netinet/in.h>
 
+#include <lib/dvb/fcc.h>
+
 #ifndef BYTE_ORDER
 #error no byte order defined!
 #endif
@@ -89,9 +94,10 @@ int eStaticServiceDVBInformation::isPlayable(const eServiceReference &ref, const
        else
        {
                eDVBChannelID chid, chid_ignore;
+               int system;
                ((const eServiceReferenceDVB&)ref).getChannelID(chid);
                ((const eServiceReferenceDVB&)ignore).getChannelID(chid_ignore);
-               return res_mgr->canAllocateChannel(chid, chid_ignore);
+               return res_mgr->canAllocateChannel(chid, chid_ignore, system);
        }
        return false;
 }
@@ -239,23 +245,24 @@ int eStaticServiceDVBBouquetInformation::isPlayable(const eServiceReference &ref
                                { 1, 2, 3 }, // -T -C -S
                                { 2, 1, 3 }  // -T -S -C
                        };
+                       int system;
                        ((const eServiceReferenceDVB&)*it).getChannelID(chid);
-                       int tmp=res->canAllocateChannel(chid, chid_ignore, simulate);
-                       switch(tmp)
+                       int tmp = res->canAllocateChannel(chid, chid_ignore, system, simulate);
+                       if (tmp > 0)
                        {
-                               case 0:
-                                       break;
-                               case 30000: // cached DVB-T channel
-                               case 1: // DVB-T frontend
-                                       tmp = prio_map[prio_order][2];
-                                       break;
-                               case 40000: // cached DVB-C channel
-                               case 2:
-                                       tmp = prio_map[prio_order][1];
-                                       break;
-                               default: // DVB-S
-                                       tmp = prio_map[prio_order][0];
-                                       break;
+                               switch (system)
+                               {
+                                       case iDVBFrontend::feTerrestrial:
+                                               tmp = prio_map[prio_order][2];
+                                               break;
+                                       case iDVBFrontend::feCable:
+                                               tmp = prio_map[prio_order][1];
+                                               break;
+                                       default:
+                                       case iDVBFrontend::feSatellite:
+                                               tmp = prio_map[prio_order][0];
+                                               break;
+                               }
                        }
                        if (tmp > cur)
                        {
@@ -292,7 +299,7 @@ public:
        RESULT getName(const eServiceReference &ref, std::string &name);
        int getLength(const eServiceReference &ref);
        RESULT getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &SWIG_OUTPUT, time_t start_time);
-       int isPlayable(const eServiceReference &ref, const eServiceReference &ignore) { return 1; }
+       int isPlayable(const eServiceReference &ref, const eServiceReference &ignore, bool simulate) { return 1; }
        int getInfo(const eServiceReference &ref, int w);
        std::string getInfoString(const eServiceReference &ref,int w);
        PyObject *getInfoObject(const eServiceReference &r, int what);
@@ -369,6 +376,8 @@ int eStaticServiceDVBPVRInformation::getInfo(const eServiceReference &ref, int w
                        return m_parser.m_time_create;
                else
                        return iServiceInformation::resNA;
+       case iServiceInformation::sIsScrambled:
+               return m_parser.m_scrambled;
        default:
                return iServiceInformation::resNA;
        }
@@ -404,14 +413,25 @@ RESULT eStaticServiceDVBPVRInformation::getEvent(const eServiceReference &ref, e
 {
        if (!ref.path.empty())
        {
-               ePtr<eServiceEvent> event = new eServiceEvent;
-               std::string filename = ref.path;
-               filename.erase(filename.length()-2, 2);
-               filename+="eit";
-               if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get()))
+               if (ref.path.substr(0, 7) == "http://")
                {
-                       evt = event;
-                       return 0;
+                       eServiceReference equivalentref(ref);
+                       /* this might be a scrambled stream (id + 0x100), force equivalent dvb type */
+                       equivalentref.type = eServiceFactoryDVB::id;
+                       equivalentref.path.clear();
+                       return eEPGCache::getInstance()->lookupEventTime(equivalentref, start_time, evt);
+               }
+               else
+               {
+                       ePtr<eServiceEvent> event = new eServiceEvent;
+                       std::string filename = ref.path;
+                       filename.erase(filename.length()-2, 2);
+                       filename+="eit";
+                       if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get()))
+                       {
+                               evt = event;
+                               return 0;
+                       }
                }
        }
        evt = 0;
@@ -800,25 +820,23 @@ RESULT eDVBServiceList::setListName(const std::string &name)
 RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
 {
        ePtr<eDVBService> service;
+
        int r = lookupService(service, ref);
        if (r)
                service = 0;
                // check resources...
-       ptr = new eDVBServicePlay(ref, service);
+       if (eFCCServiceManager::checkAvailable(ref))
+               ptr = new eDVBServiceFCCPlay(ref, service);
+       else
+               ptr = new eDVBServicePlay(ref, service);
        return 0;
 }
 
 RESULT eServiceFactoryDVB::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr)
 {
-       if (ref.path.empty())
-       {
-               ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref);
-               return 0;
-       } else
-       {
-               ptr = 0;
-               return -1;
-       }
+       bool isstream = ref.path.substr(0, 7) == "http://";
+       ptr = new eDVBServiceRecord((eServiceReferenceDVB&)ref, isstream);
+       return 0;
 }
 
 RESULT eServiceFactoryDVB::list(const eServiceReference &ref, ePtr<iListableService> &ptr)
@@ -913,16 +931,20 @@ RESULT eServiceFactoryDVB::lookupService(ePtr<eDVBService> &service, const eServ
        return 0;
 }
 
-eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service):
+eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service, bool connect_event):
        m_reference(ref), m_dvb_service(service), m_have_video_pid(0), m_is_paused(0)
 {
        m_is_primary = 1;
-       m_is_pvr = !m_reference.path.empty();
-       
+       m_decoder_index = 0;
+       m_is_stream = m_reference.path.substr(0, 7) == "http://";
+       m_is_pvr = (!m_reference.path.empty() && !m_is_stream);
+
        m_timeshift_enabled = m_timeshift_active = 0, m_timeshift_changed = 0;
        m_skipmode = m_fastforward = m_slowmotion = 0;
-       
-       CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent);
+
+       if (connect_event)
+               CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent);
+
        CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift);
        CONNECT(m_event_handler.m_eit_changed, eDVBServicePlay::gotNewEvent);
 
@@ -935,6 +957,12 @@ eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *serv
 
        m_subtitle_sync_timer = eTimer::create(eApp);
 
+       m_current_video_pid_type = 0;
+
+       m_qpip_mode = false;
+
+       m_play_audio = true;
+
        CONNECT(m_subtitle_sync_timer->timeout, eDVBServicePlay::checkSubtitleTiming);
 }
 
@@ -1042,6 +1070,9 @@ void eDVBServicePlay::serviceEvent(int event)
        case eDVBServicePMTHandler::eventSOF:
                m_event((iPlayableService*)this, evSOF);
                break;
+       case eDVBServicePMTHandler::eventHBBTVInfo:
+               m_event((iPlayableService*)this, evHBBTVInfo);
+               break;
        }
 }
 
@@ -1097,7 +1128,7 @@ void eDVBServicePlay::serviceEventTimeshift(int event)
                        if (m_skipmode < 0)
                                m_cue->seekTo(0, -1000);
                        ePtr<iTsSource> source = createTsSource(r);
-                       m_service_handler_timeshift.tuneExt(r, 1, source, r.path.c_str(), m_cue, 0, m_dvb_service); /* use the decoder demux for everything */
+                       m_service_handler_timeshift.tuneExt(r, 1, source, r.path.c_str(), m_cue, 0, m_dvb_service, false); /* use the decoder demux for everything */
 
                        m_event((iPlayableService*)this, evUser+1);
                }
@@ -1127,38 +1158,62 @@ void eDVBServicePlay::serviceEventTimeshift(int event)
                                resetTimeshift(1);
 
                                ePtr<iTsSource> source = createTsSource(r);
-                               m_service_handler_timeshift.tuneExt(r, 1, source, m_timeshift_file_next.c_str(), m_cue, 0, m_dvb_service); /* use the decoder demux for everything */
+                               int use_decode_demux = 1;
+                               eDVBServicePMTHandler::serviceType type = eDVBServicePMTHandler::timeshift_playback;
+                               m_service_handler_timeshift.tuneExt(r, use_decode_demux, source, m_timeshift_file_next.c_str(), m_cue, 0, m_dvb_service, type, false); /* use the decoder demux for everything */
 
                                m_event((iPlayableService*)this, evUser+1);
                        }
                }
                break;
+       case eDVBServicePMTHandler::eventNoDiskSpace:
+               eDebug("No space!");
+               m_event((iPlayableService*)this, evUser+3);
+               break;
        }
 }
 
 RESULT eDVBServicePlay::start()
 {
        eServiceReferenceDVB service = (eServiceReferenceDVB&)m_reference;
+       bool scrambled = true;
+       eDVBServicePMTHandler::serviceType type = eDVBServicePMTHandler::livetv;
+
+       int use_decode_demux = 0;
 
                /* in pvr mode, we only want to use one demux. in tv mode, we're using 
                   two (one for decoding, one for data source), as we must be prepared
                   to start recording from the data demux. */
        if (m_is_pvr)
        {
+               use_decode_demux = 1;
                eDVBMetaParser meta;
                if (!meta.parseFile(m_reference.path))
                {
                        service = meta.m_ref;
                        service.path = m_reference.path;
+                       scrambled = meta.m_scrambled;
+               }
+               else
+               {
+                       /* when there is no meta file we need to handle ts/m2ts as descrambled */
+                       scrambled = false;
                }
                m_cue = new eCueSheet();
+               type = eDVBServicePMTHandler::playback;
        }
        else
                m_event(this, evStart);
 
+       if (m_is_stream)
+       {
+               scrambled = true;
+               type = eDVBServicePMTHandler::streamclient;
+       }
+
        m_first_program_info = 1;
        ePtr<iTsSource> source = createTsSource(service);
-       m_service_handler.tuneExt(service, m_is_pvr, source, service.path.c_str(), m_cue, false, m_dvb_service);
+       m_service_handler.tuneExt(service, use_decode_demux, source, service.path.c_str(), m_cue, false, m_dvb_service, type, scrambled);
 
        if (m_is_pvr)
        {
@@ -1228,6 +1283,7 @@ RESULT eDVBServicePlay::stop()
 RESULT eDVBServicePlay::setTarget(int target)
 {
        m_is_primary = !target;
+       m_decoder_index = target;
        return 0;
 }
 
@@ -1300,7 +1356,18 @@ RESULT eDVBServicePlay::setFastForward_internal(int ratio, bool final_seek)
        {
                eDebug("setting cue skipmode to %d", skipmode);
                if (m_cue)
-                       m_cue->setSkipmode(skipmode * 90000); /* convert to 90000 per second */
+               {
+                       long long _skipmode = skipmode;
+                       if (m_current_video_pid_type == eDVBServicePMTHandler::videoStream::vtH265_HEVC)
+                       {
+                               if (ratio < 0)
+                                       _skipmode = skipmode * 3;
+                               else
+                                       _skipmode = skipmode * 4;
+                       }
+
+                       m_cue->setSkipmode(_skipmode * 90000); /* convert to 90000 per second */
+               }
        }
 
        m_skipmode = skipmode;
@@ -1502,7 +1569,7 @@ RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
 {
        ptr = 0;
        if (m_have_video_pid &&  // HACK !!! FIXMEE !! temporary no timeshift on radio services !!
-               (m_timeshift_enabled || !m_is_pvr))
+               (m_timeshift_enabled || (!m_is_pvr&&!m_is_stream)))
        {
                if (!m_timeshift_enabled)
                {
@@ -1569,6 +1636,18 @@ RESULT eDVBServicePlay::getName(std::string &name)
                ePtr<iStaticServiceInformation> i = new eStaticServiceDVBPVRInformation(m_reference);
                return i->getName(m_reference, name);
        }
+       else if (m_is_stream)
+       {
+               name = m_reference.name;
+               if (name.empty())
+               {
+                       name = m_reference.path;
+               }
+               if (name.empty())
+               {
+                       name = "(...)";
+               }
+       }
        else if (m_dvb_service)
        {
                m_dvb_service->getName(m_reference, name);
@@ -1680,12 +1759,21 @@ int eDVBServicePlay::getInfo(int w)
        case sAudioPID:
                if (m_dvb_service)
                {
-                       int apid = m_dvb_service->getCacheEntry(eDVBService::cAPID);
+                       int apid = m_dvb_service->getCacheEntry(eDVBService::cMPEGAPID);
                        if (apid != -1)
                                return apid;
                        apid = m_dvb_service->getCacheEntry(eDVBService::cAC3PID);
                        if (apid != -1)
                                return apid;
+                       apid = m_dvb_service->getCacheEntry(eDVBService::cDDPPID);
+                       if (apid != -1)
+                               return apid;
+                       apid = m_dvb_service->getCacheEntry(eDVBService::cAACHEAPID);
+                       if (apid != -1)
+                               return apid;
+                       apid = m_dvb_service->getCacheEntry(eDVBService::cAACAPID);
+                       if (apid != -1)
+                               return apid;
                }
                if (no_program_info) return -1; if (program.audioStreams.empty()) return -1; return program.audioStreams[0].pid;
        case sPCRPID:
@@ -1720,6 +1808,23 @@ std::string eDVBServicePlay::getInfoString(int w)
                return m_dvb_service->m_provider_name;
        case sServiceref:
                return m_reference.toString();
+       case sHBBTVUrl:
+       {
+               std::string url;
+               eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+               h.getHBBTVUrl(url);
+               return url;
+       }
+       case sLiveStreamDemuxId:
+       {
+               int id;
+               eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+               h.getDemuxID(id);
+
+               std::string demux;
+               demux += id + '0';
+               return demux;
+       }
        default:
                break;
        }
@@ -1736,6 +1841,11 @@ PyObject *eDVBServicePlay::getInfoObject(int w)
                return m_service_handler.getCaIds(true);
        case sTransponderData:
                return eStaticServiceDVBInformation().getInfoObject(m_reference, w);
+       case sHBBTVUrl:
+       {
+               eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
+               return h.getHbbTVApplications();
+       }
        default:
                break;
        }
@@ -1794,13 +1904,17 @@ RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int
        if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atMPEG)
                info.m_description = "MPEG";
        else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAC3)
-               info.m_description = "AC3";
+               info.m_description = "Dolby Digital";
+        else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDDP)
+                info.m_description = "Dolby Digital+";
        else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAAC)
                info.m_description = "AAC";
        else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atAACHE)
                info.m_description = "AAC-HE";
        else  if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS)
                info.m_description = "DTS";
+       else  if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTSHD)
+               info.m_description = "DTS-HD";
        else
                info.m_description = "???";
 
@@ -1853,7 +1967,7 @@ int eDVBServicePlay::selectAudioStream(int i)
 
        m_current_audio_pid = apid;
 
-       if (m_is_primary && m_decoder->setAudioPID(apid, apidtype))
+       if ((m_is_primary || (m_qpip_mode && m_play_audio)) && m_decoder->setAudioPID(apid, apidtype))
        {
                eDebug("set audio pid failed");
                return -4;
@@ -1891,23 +2005,17 @@ int eDVBServicePlay::selectAudioStream(int i)
                                    case the real default is not yet available.)
                        */
        if (m_dvb_service && ((i != -1)
-               || ((m_dvb_service->getCacheEntry(eDVBService::cAPID) == -1) && (m_dvb_service->getCacheEntry(eDVBService::cAC3PID)==-1))))
+               || ((m_dvb_service->getCacheEntry(eDVBService::cMPEGAPID) == -1)
+               && (m_dvb_service->getCacheEntry(eDVBService::cAC3PID)==-1)
+               && (m_dvb_service->getCacheEntry(eDVBService::cDDPPID)==-1)
+               && (m_dvb_service->getCacheEntry(eDVBService::cAACHEAPID)==-1)
+               && (m_dvb_service->getCacheEntry(eDVBService::cAACAPID)==-1))))
        {
-               if (apidtype == eDVBAudio::aMPEG)
-               {
-                       m_dvb_service->setCacheEntry(eDVBService::cAPID, apid);
-                       m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
-               }
-               else if (apidtype == eDVBAudio::aAC3)
-               {
-                       m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
-                       m_dvb_service->setCacheEntry(eDVBService::cAC3PID, apid);
-               }
-               else
-               {
-                       m_dvb_service->setCacheEntry(eDVBService::cAPID, -1);
-                       m_dvb_service->setCacheEntry(eDVBService::cAC3PID, -1);
-               }
+               m_dvb_service->setCacheEntry(eDVBService::cMPEGAPID, apidtype == eDVBAudio::aMPEG ? apid : -1);
+               m_dvb_service->setCacheEntry(eDVBService::cAC3PID, apidtype == eDVBAudio::aAC3 ? apid : -1);
+               m_dvb_service->setCacheEntry(eDVBService::cDDPPID, apidtype == eDVBAudio::aDDP ? apid : -1);
+               m_dvb_service->setCacheEntry(eDVBService::cAACHEAPID, apidtype == eDVBAudio::aAACHE ? apid : -1);
+               m_dvb_service->setCacheEntry(eDVBService::cAACAPID, apidtype == eDVBAudio::aAAC ? apid : -1);
        }
 
        h.resetCachedProgram();
@@ -2153,6 +2261,9 @@ RESULT eDVBServicePlay::startTimeshift()
        }
                
        m_record->setTargetFD(m_timeshift_fd);
+       m_record->setTargetFilename(m_timeshift_file.c_str());
+       m_record->enableAccessPoints(false);
+       m_record->setTimeshift(true);
 
        m_timeshift_enabled = 1;
        
@@ -2178,6 +2289,11 @@ RESULT eDVBServicePlay::stopTimeshift(bool swToLive)
        close(m_timeshift_fd);
        eDebug("remove timeshift file");
        eBackgroundFileEraser::getInstance()->erase(m_timeshift_file.c_str());
+
+       {
+               std::string timeshift_file_sc = m_timeshift_file + ".sc";
+               eBackgroundFileEraser::getInstance()->erase(timeshift_file_sc.c_str());
+       }
        
        return 0;
 }
@@ -2274,6 +2390,8 @@ void eDVBServicePlay::updateTimeshiftPids()
                return;
        else
        {
+               int timing_pid = -1;
+               int timing_pid_type = -1;
                std::set<int> pids_to_record;
                pids_to_record.insert(0); // PAT
                if (program.pmtPid != -1)
@@ -2285,12 +2403,28 @@ void eDVBServicePlay::updateTimeshiftPids()
                for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
                        i(program.videoStreams.begin()); 
                        i != program.videoStreams.end(); ++i)
+               {
                        pids_to_record.insert(i->pid);
 
+                       if (timing_pid == -1)
+                       {
+                               timing_pid = i->pid;
+                               timing_pid_type = i->type;
+                       }
+               }
+
                for (std::vector<eDVBServicePMTHandler::audioStream>::const_iterator
                        i(program.audioStreams.begin()); 
                        i != program.audioStreams.end(); ++i)
-                               pids_to_record.insert(i->pid);
+               {
+                       pids_to_record.insert(i->pid);
+
+                       if (timing_pid == -1)
+                       {
+                               timing_pid = i->pid;
+                               timing_pid_type = -1;
+                       }
+               }
 
                for (std::vector<eDVBServicePMTHandler::subtitleStream>::const_iterator
                        i(program.subtitleStreams.begin());
@@ -2314,6 +2448,9 @@ void eDVBServicePlay::updateTimeshiftPids()
 
                for (std::set<int>::iterator i(obsolete_pids.begin()); i != obsolete_pids.end(); ++i)
                        m_record->removePID(*i);
+
+               if (timing_pid != -1)
+                       m_record->setTimingPID(timing_pid, timing_pid_type);
        }
 }
 
@@ -2366,9 +2503,18 @@ void eDVBServicePlay::resetTimeshift(int start)
 
 ePtr<iTsSource> eDVBServicePlay::createTsSource(eServiceReferenceDVB &ref)
 {
-       eRawFile *f = new eRawFile();
-       f->open(ref.path.c_str());
-       return ePtr<iTsSource>(f);
+       if (m_is_stream)
+       {
+               eHttpStream *f = new eHttpStream();
+               f->open(ref.path.c_str());
+               return ePtr<iTsSource>(f);
+       }
+       else
+       {
+               eRawFile *f = new eRawFile();
+               f->open(ref.path.c_str());
+               return ePtr<iTsSource>(f);
+       }
 }
 
 void eDVBServicePlay::switchToTimeshift()
@@ -2384,7 +2530,9 @@ void eDVBServicePlay::switchToTimeshift()
        m_cue->seekTo(0, -1000);
 
        ePtr<iTsSource> source = createTsSource(r);
-       m_service_handler_timeshift.tuneExt(r, 1, source, m_timeshift_file.c_str(), m_cue, 0, m_dvb_service); /* use the decoder demux for everything */
+       int use_decode_demux = 1;
+       eDVBServicePMTHandler::serviceType type = eDVBServicePMTHandler::timeshift_playback;
+       m_service_handler_timeshift.tuneExt(r, use_decode_demux, source, m_timeshift_file.c_str(), m_cue, 0, m_dvb_service, type, false); /* use the decoder demux for everything */
 
        eDebug("eDVBServicePlay::switchToTimeshift, in pause mode now.");
        pause();
@@ -2403,7 +2551,7 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged)
                eDebug("getting program info failed.");
        else
        {
-               eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size());
+               eDebugNoNewLine("have %zd video stream(s)", program.videoStreams.size());
                if (!program.videoStreams.empty())
                {
                        eDebugNoNewLine(" (");
@@ -2422,7 +2570,7 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged)
                        }
                        eDebugNoNewLine(")");
                }
-               eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size());
+               eDebugNoNewLine(", and %zd audio stream(s)", program.audioStreams.size());
                if (!program.audioStreams.empty())
                {
                        eDebugNoNewLine(" (");
@@ -2447,7 +2595,7 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged)
                h.getDecodeDemux(m_decode_demux);
                if (m_decode_demux)
                {
-                       m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary);
+                       m_decode_demux->getMPEGDecoder(m_decoder, m_decoder_index);
                        if (m_decoder)
                                m_decoder->connectVideoEvent(slot(*this, &eDVBServicePlay::video_event), m_video_event_connection);
                        if (m_is_primary)
@@ -2520,9 +2668,13 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged)
                setPCMDelay(pcm_delay == -1 ? 0 : pcm_delay);
 
                m_decoder->setVideoPID(vpid, vpidtype);
-               selectAudioStream();
+               m_current_video_pid_type = vpidtype;
 
-               if (!(m_is_pvr || m_timeshift_active || !m_is_primary))
+               if (!m_qpip_mode || m_play_audio) // 1) no qpip mode, 2) qpip mode & play audio
+                       selectAudioStream();
+
+               //if (!(m_is_pvr || m_is_stream || m_timeshift_active || !m_is_primary))
+               if (!(m_is_pvr || m_is_stream || m_timeshift_active))
                        m_decoder->setSyncPCR(pcrpid);
                else
                        m_decoder->setSyncPCR(-1);
@@ -2599,7 +2751,7 @@ void eDVBServicePlay::loadCuesheet()
                        m_cue_entries.insert(cueEntry(where, what));
                }
                fclose(f);
-               eDebug("%d entries", m_cue_entries.size());
+               eDebug("%zd entries", m_cue_entries.size());
        } else
                eDebug("cutfile not found!");
        
@@ -2934,11 +3086,30 @@ void eDVBServicePlay::newSubtitlePage(const eDVBTeletextSubtitlePage &page)
 {
        if (m_subtitle_widget)
        {
+               int subtitledelay = 0;
                pts_t pos = 0;
                if (m_decoder)
                        m_decoder->getPTS(0, pos);
+               if (m_is_pvr || m_timeshift_enabled)
+               {
+                       eDebug("Subtitle in recording/timeshift");
+                       subtitledelay = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_noPTSrecordingdelay", 315000);
+               }
+               else
+               {
+                       /* check the setting for subtitle delay in live playback, either with pos, or without pos */
+                       subtitledelay = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bad_timing_delay", 0);
+               }
+
                eDebug("got new subtitle page %lld %lld %d", pos, page.m_pts, page.m_have_pts);
-               m_subtitle_pages.push_back(page);
+               eDVBTeletextSubtitlePage tmppage = page;
+               tmppage.m_have_pts = true;
+
+               if (abs(tmppage.m_pts - pos) > 20*90000)
+                       tmppage.m_pts = pos; // fix abnormal pos diffs
+
+               tmppage.m_pts += subtitledelay;
+               m_subtitle_pages.push_back(tmppage);
                checkSubtitleTiming();
        }
 }
@@ -3022,7 +3193,29 @@ void eDVBServicePlay::newDVBSubtitlePage(const eDVBSubtitlePage &p)
                if (m_decoder)
                        m_decoder->getPTS(0, pos);
                eDebug("got new subtitle page %lld %lld", pos, p.m_show_time);
-               m_dvb_subtitle_pages.push_back(p);
+               if ( abs(pos-p.m_show_time)>1800000 && (m_is_pvr || m_timeshift_enabled))
+               {
+                       eDebug("[eDVBServicePlay] Subtitle without PTS and recording");
+                       int subtitledelay = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_noPTSrecordingdelay", 315000);
+
+                       eDVBSubtitlePage tmppage;
+                       tmppage = p;
+                       tmppage.m_show_time = pos + subtitledelay;
+                       m_dvb_subtitle_pages.push_back(tmppage);
+               }
+               else
+               {
+                       int subtitledelay = ePythonConfigQuery::getConfigIntValue("config.subtitles.subtitle_bad_timing_delay", 0);
+                       if (subtitledelay != 0)
+                       {
+                               eDVBSubtitlePage tmppage;
+                               tmppage = p;
+                               tmppage.m_show_time += subtitledelay;
+                               m_dvb_subtitle_pages.push_back(tmppage);
+                       }
+                       else
+                               m_dvb_subtitle_pages.push_back(p);
+               }
                checkSubtitleTiming();
        }
 }
@@ -3118,6 +3311,24 @@ PyObject *eDVBServicePlay::getStreamingData()
        return r;
 }
 
+void eDVBServicePlay::setQpipMode(bool value, bool audio)
+{
+       m_qpip_mode = value;
+       m_play_audio = audio;
+
+       if(m_decoder)
+       {
+               if (m_play_audio)
+               {
+                       selectAudioStream();
+               }
+               else
+               {
+                       m_decoder->setAudioPID(-1, -1);
+               }
+               m_decoder->set();
+       }
+}
 
 DEFINE_REF(eDVBServicePlay)