add isCurrentlSeekable to iPlayableService
[enigma2.git] / lib / service / servicedvb.cpp
index d8567fd83fd6930d736760c3f373b3dfea1cec8d..650b609e16713d6fd4784cbe3748e0a588857d39 100644 (file)
 #include <lib/service/servicedvbrecord.h>
 #include <lib/dvb/metaparser.h>
 #include <lib/dvb/tstools.h>
+#include <lib/python/python.h>
+
+#include <sys/vfs.h>
+
+#define TSPATH "/media/hdd"
 
 class eStaticServiceDVBInformation: public iStaticServiceInformation
 {
@@ -305,7 +310,32 @@ RESULT eDVBServiceList::startQuery()
        return 0;
 }
 
-RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list)
+RESULT eDVBServiceList::getContent(PyObject *list, bool sorted)
+{
+       eServiceReferenceDVB ref;
+
+       if (!m_query || !list || !PyList_Check(list))
+               return -1;
+
+       std::list<eServiceReferenceDVB> tmplist;
+
+       while (!m_query->getNextResult(ref))
+               tmplist.push_back(ref);
+
+       if (sorted)
+               tmplist.sort(iListableServiceCompare(this));
+
+       for (std::list<eServiceReferenceDVB>::iterator it(tmplist.begin());
+               it != tmplist.end(); ++it)
+       {
+               PyObject *refobj = New_eServiceReference(*it);
+               PyList_Append(list, refobj);
+               Py_DECREF(refobj);
+       }
+       return 0;
+}
+
+RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list, bool sorted)
 {
        eServiceReferenceDVB ref;
        
@@ -314,6 +344,10 @@ RESULT eDVBServiceList::getContent(std::list<eServiceReference> &list)
        
        while (!m_query->getNextResult(ref))
                list.push_back(ref);
+
+       if (sorted)
+               list.sort(iListableServiceCompare(this));
+
        return 0;
 }
 
@@ -379,6 +413,13 @@ RESULT eDVBServiceList::flushChanges()
        return m_bouquet->flushChanges();
 }
 
+RESULT eDVBServiceList::setListName(const std::string &name)
+{
+       if (!m_bouquet)
+               return -1;
+       return m_bouquet->setListName(name);
+}
+
 RESULT eServiceFactoryDVB::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr)
 {
        ePtr<eDVBService> service;
@@ -489,6 +530,7 @@ eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *serv
 {
        m_is_pvr = !ref.path.empty();
        m_timeshift_enabled = m_timeshift_active = 0;
+       m_skipmode = 0;
        
        CONNECT(m_service_handler.serviceEvent, eDVBServicePlay::serviceEvent);
        CONNECT(m_service_handler_timeshift.serviceEvent, eDVBServicePlay::serviceEventTimeshift);
@@ -553,7 +595,11 @@ void eDVBServicePlay::serviceEvent(int event)
                break;
        }
        case eDVBServicePMTHandler::eventEOF:
-               m_event((iPlayableService*)this, evEnd);
+               m_event((iPlayableService*)this, evEOF);
+               break;
+       case eDVBServicePMTHandler::eventSOF:
+               m_event((iPlayableService*)this, evSOF);
+               break;
        }
 }
 
@@ -565,6 +611,9 @@ void eDVBServicePlay::serviceEventTimeshift(int event)
                if (m_timeshift_active)
                        updateDecoder();
                break;
+       case eDVBServicePMTHandler::eventEOF:
+               switchToLive();
+               break;
        }
 }
 
@@ -574,13 +623,24 @@ RESULT eDVBServicePlay::start()
                /* 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. */
-       r = m_service_handler.tune((eServiceReferenceDVB&)m_reference, m_is_pvr);
+       m_cue = new eCueSheet();
+       r = m_service_handler.tune((eServiceReferenceDVB&)m_reference, m_is_pvr, m_cue);
        m_event(this, evStart);
+       m_event((iPlayableService*)this, evSeekableStatusChanged);
        return 0;
 }
 
 RESULT eDVBServicePlay::stop()
 {
+       eDebug("stop timeshift");
+       stopTimeshift(); /* in case timeshift was enabled, remove buffer etc. */
+       
+       eDebug("free ts handler");
+       m_service_handler_timeshift.free();
+       eDebug("stop service handler");
+       m_service_handler.free();
+       eDebug("ok");
+       
        return 0;
 }
 
@@ -592,6 +652,9 @@ RESULT eDVBServicePlay::connectEvent(const Slot2<void,iPlayableService*,int> &ev
 
 RESULT eDVBServicePlay::pause(ePtr<iPauseableService> &ptr)
 {
+               /* note: we check for timeshift to be enabled,
+                  not neccessary active. if you pause when timeshift
+                  is not active, you should activate it when unpausing */
        if ((!m_is_pvr) && (!m_timeshift_enabled))
        {
                ptr = 0;
@@ -612,10 +675,39 @@ RESULT eDVBServicePlay::setSlowMotion(int ratio)
 
 RESULT eDVBServicePlay::setFastForward(int ratio)
 {
-       if (m_decoder)
-               return m_decoder->setFastForward(ratio);
-       else
+       int skipmode, ffratio;
+       
+       if (ratio > 8)
+       {
+               skipmode = ratio;
+               ffratio = 1;
+       } else if (ratio > 0)
+       {
+               skipmode = 0;
+               ffratio = ratio;
+       } else if (!ratio)
+       {
+               skipmode = 0;
+               ffratio = 0;
+       } else // if (ratio < 0)
+       {
+               skipmode = ratio;
+               ffratio = 1;
+       }
+
+       if (m_skipmode != skipmode)
+       {
+               eDebug("setting cue skipmode to %d", skipmode);
+               if (m_cue)
+                       m_cue->setSkipmode(skipmode * 90000); /* convert to 90000 per second */
+       }
+       
+       m_skipmode = skipmode;
+       
+       if (!m_decoder)
                return -1;
+
+       return m_decoder->setFastForward(ffratio);
 }
     
 RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
@@ -630,26 +722,19 @@ RESULT eDVBServicePlay::seek(ePtr<iSeekableService> &ptr)
        return -1;
 }
 
+       /* TODO: when timeshift is enabled but not active, this doesn't work. */
 RESULT eDVBServicePlay::getLength(pts_t &len)
 {
        ePtr<iDVBPVRChannel> pvr_channel;
        
-       if (m_service_handler.getPVRChannel(pvr_channel))
-       {
-               eDebug("getPVRChannel failed!");
+       if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
                return -1;
-       }
        
        return pvr_channel->getLength(len);
 }
 
 RESULT eDVBServicePlay::pause()
 {
-       if (m_timeshift_enabled && !m_timeshift_active)
-       {
-               switchToTimeshift();
-               return 0;
-       }
        if (!m_is_paused && m_decoder)
        {
                m_is_paused = 1;
@@ -677,10 +762,14 @@ RESULT eDVBServicePlay::seekTo(pts_t to)
 
        ePtr<iDVBPVRChannel> pvr_channel;
        
-       if (m_service_handler.getPVRChannel(pvr_channel))
+       if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
                return -1;
        
-       return pvr_channel->seekTo(m_decode_demux, 0, to);
+       if (!m_cue)
+               return -1;
+       
+       m_cue->seekTo(0, to);
+       return 0;
 }
 
 RESULT eDVBServicePlay::seekRelative(int direction, pts_t to)
@@ -692,12 +781,16 @@ RESULT eDVBServicePlay::seekRelative(int direction, pts_t to)
 
        ePtr<iDVBPVRChannel> pvr_channel;
        
-       if (m_service_handler.getPVRChannel(pvr_channel))
+       if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
                return -1;
        
        to *= direction;
        
-       return pvr_channel->seekTo(m_decode_demux, 1, to);
+       if (!m_cue)
+               return 0;
+       
+       m_cue->seekTo(1, to);
+       return 0;
 }
 
 RESULT eDVBServicePlay::getPlayPosition(pts_t &pos)
@@ -707,7 +800,7 @@ RESULT eDVBServicePlay::getPlayPosition(pts_t &pos)
        if (!m_decode_demux)
                return -1;
        
-       if (m_service_handler.getPVRChannel(pvr_channel))
+       if ((m_timeshift_enabled ? m_service_handler_timeshift : m_service_handler).getPVRChannel(pvr_channel))
                return -1;
        
        return pvr_channel->getCurrentPosition(m_decode_demux, pos, 1);
@@ -720,6 +813,11 @@ RESULT eDVBServicePlay::setTrickmode(int trick)
        return 0;
 }
 
+RESULT eDVBServicePlay::isCurrentlySeekable()
+{
+       return m_is_pvr || m_timeshift_active;
+}
+
 RESULT eDVBServicePlay::frontendStatusInfo(ePtr<iFrontendStatusInformation> &ptr)
 {
        ptr = this;
@@ -746,17 +844,38 @@ RESULT eDVBServicePlay::subServices(ePtr<iSubserviceList> &ptr)
 
 RESULT eDVBServicePlay::timeshift(ePtr<iTimeshiftService> &ptr)
 {
+       ptr = 0;
        if (m_timeshift_enabled || !m_is_pvr)
        {
+               if (!m_timeshift_enabled)
+               {
+                               /* we need enough diskspace */
+                       struct statfs fs;
+                       if (statfs(TSPATH "/.", &fs) < 0)
+                       {
+                               eDebug("statfs failed!");
+                               return -2;
+                       }
+               
+                       if (((off_t)fs.f_bavail) * ((off_t)fs.f_bsize) < 1024*1024*1024LL)
+                       {
+                               eDebug("not enough diskspace for timeshift! (less than 1GB)");
+                               return -3;
+                       }
+               }
                ptr = this;
                return 0;
        }
-       ptr = 0;
        return -1;
 }
 
 RESULT eDVBServicePlay::getName(std::string &name)
 {
+       if (m_is_pvr)
+       {
+               ePtr<iStaticServiceInformation> i = new eStaticServiceDVBPVRInformation(m_reference);
+               return i->getName(m_reference, name);
+       }
        if (m_dvb_service)
        {
                m_dvb_service->getName(m_reference, name);
@@ -828,7 +947,7 @@ int eDVBServicePlay::getInfo(int w)
        case sAudioPID: if (program.audioStreams.empty()) return -1; return program.audioStreams[m_current_audio_stream].pid;
        case sPCRPID: return program.pcrPid;
        case sPMTPID: return program.pmtPid;
-       case sTXTPID: return -1;
+       case sTXTPID: return program.textPid;
        case sSID: return ((const eServiceReferenceDVB&)m_reference).getServiceID().get();
        case sONID: return ((const eServiceReferenceDVB&)m_reference).getOriginalNetworkID().get();
        case sTSID: return ((const eServiceReferenceDVB&)m_reference).getTransportStreamID().get();
@@ -989,7 +1108,7 @@ RESULT eDVBServicePlay::startTimeshift()
        if (!m_record)
                return -3;
 
-       char templ[]="/media/hdd/timeshift.XXXXXX";
+       char templ[]=TSPATH "/timeshift.XXXXXX";
        m_timeshift_fd = mkstemp(templ);
        m_timeshift_file = templ;
        
@@ -997,7 +1116,7 @@ RESULT eDVBServicePlay::startTimeshift()
        
        if (m_timeshift_fd < 0)
        {
-               delete m_record;
+               m_record = 0;
                return -4;
        }
                
@@ -1021,15 +1140,34 @@ RESULT eDVBServicePlay::stopTimeshift()
        m_timeshift_enabled = 0;
        
        m_record->stop();
-       delete m_record;
+       m_record = 0;
        
        close(m_timeshift_fd);
+       eDebug("remove timeshift file");
        remove(m_timeshift_file.c_str());
        
-       eDebug("timeshift disabled");
        return 0;
 }
 
+int eDVBServicePlay::isTimeshiftActive()
+{
+       return m_timeshift_enabled && m_timeshift_active;
+}
+
+RESULT eDVBServicePlay::activateTimeshift()
+{
+       if (!m_timeshift_enabled)
+               return -1;
+       
+       if (!m_timeshift_active)
+       {
+               switchToTimeshift();
+               return 0;
+       }
+       
+       return -2;
+}
+
 void eDVBServicePlay::updateTimeshiftPids()
 {
        if (!m_record)
@@ -1044,7 +1182,10 @@ void eDVBServicePlay::updateTimeshiftPids()
                pids_to_record.insert(0); // PAT
                if (program.pmtPid != -1)
                        pids_to_record.insert(program.pmtPid); // PMT
-               
+
+               if (program.textPid != -1)
+                       pids_to_record.insert(program.textPid); // Videotext
+
                for (std::vector<eDVBServicePMTHandler::videoStream>::const_iterator
                        i(program.videoStreams.begin()); 
                        i != program.videoStreams.end(); ++i)
@@ -1066,7 +1207,7 @@ void eDVBServicePlay::updateTimeshiftPids()
                                pids_to_record.begin(), pids_to_record.end(), 
                                std::inserter(new_pids, new_pids.begin())
                                );
-               
+
                for (std::set<int>::iterator i(new_pids.begin()); i != new_pids.end(); ++i)
                        m_record->addPID(*i);
 
@@ -1077,7 +1218,6 @@ void eDVBServicePlay::updateTimeshiftPids()
 
 void eDVBServicePlay::switchToLive()
 {
-       eDebug("SwitchToLive");
        if (!m_timeshift_active)
                return;
        
@@ -1087,12 +1227,13 @@ void eDVBServicePlay::switchToLive()
        m_service_handler_timeshift.free();
        m_timeshift_active = 0;
        
+       m_event((iPlayableService*)this, evSeekableStatusChanged);
+       
        updateDecoder();
 }
 
 void eDVBServicePlay::switchToTimeshift()
 {
-       eDebug("SwitchToTimeshift");
        if (m_timeshift_active)
                return;
        
@@ -1100,17 +1241,18 @@ void eDVBServicePlay::switchToTimeshift()
        m_decoder = 0;
        
        m_timeshift_active = 1;
+
+       m_event((iPlayableService*)this, evSeekableStatusChanged);
        
        eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference;
        r.path = m_timeshift_file;
        
-       eDebug("ok, re-tuning to %s", r.toString().c_str());
-       m_service_handler_timeshift.tune(r, 1); /* use the decoder demux for everything */
+       m_service_handler_timeshift.tune(r, 1, m_cue); /* use the decoder demux for everything */
 }
 
 void eDVBServicePlay::updateDecoder()
 {
-       int vpid = -1, apid = -1, apidtype = -1, pcrpid = -1;
+       int vpid = -1, apid = -1, apidtype = -1, pcrpid = -1, tpid = -1;
        eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler;
 
        eDVBServicePMTHandler::program program;
@@ -1153,9 +1295,10 @@ void eDVBServicePlay::updateDecoder()
                        }
                        eDebugNoNewLine(")");
                }
-               eDebug(", and the pcr pid is %04x", program.pcrPid);
-               if (program.pcrPid != 0x1fff)
-                       pcrpid = program.pcrPid;
+               eDebugNoNewLine(", and the pcr pid is %04x", program.pcrPid);
+               pcrpid = program.pcrPid;
+               eDebug(", and the text pid is %04x", program.textPid);
+               tpid = program.textPid;
        }
 
        if (!m_decoder)
@@ -1163,6 +1306,8 @@ void eDVBServicePlay::updateDecoder()
                h.getDecodeDemux(m_decode_demux);
                if (m_decode_demux)
                        m_decode_demux->getMPEGDecoder(m_decoder);
+               if (m_cue)
+                       m_cue->setDecodingDemux(m_decode_demux);
        }
 
        if (m_decoder)
@@ -1174,27 +1319,29 @@ void eDVBServicePlay::updateDecoder()
                        m_decoder->setSyncPCR(pcrpid);
                else
                        m_decoder->setSyncPCR(-1);
+               m_decoder->setTextPID(tpid);
                m_decoder->start();
 // how we can do this better?
 // update cache pid when the user changed the audio track or video track
 // TODO handling of difference audio types.. default audio types..
                                
-                               /* don't worry about non-existing services, nor pvr services */
-                       if (m_dvb_service && !m_is_pvr)
+               /* don't worry about non-existing services, nor pvr services */
+               if (m_dvb_service && !m_is_pvr)
+               {
+                       if (apidtype == eDVBAudio::aMPEG)
                        {
-                               if (apidtype == eDVBAudio::aMPEG)
-                               {
-                                       m_dvb_service->setCachePID(eDVBService::cAPID, apid);
-                                       m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
-                               }
-                               else
-                               {
-                                       m_dvb_service->setCachePID(eDVBService::cAPID, -1);
-                                       m_dvb_service->setCachePID(eDVBService::cAC3PID, apid);
-                               }
-                               m_dvb_service->setCachePID(eDVBService::cVPID, vpid);
-                               m_dvb_service->setCachePID(eDVBService::cPCRPID, pcrpid);
+                               m_dvb_service->setCachePID(eDVBService::cAPID, apid);
+                               m_dvb_service->setCachePID(eDVBService::cAC3PID, -1);
+                       }
+                       else
+                       {
+                               m_dvb_service->setCachePID(eDVBService::cAPID, -1);
+                               m_dvb_service->setCachePID(eDVBService::cAC3PID, apid);
                        }
+                       m_dvb_service->setCachePID(eDVBService::cVPID, vpid);
+                       m_dvb_service->setCachePID(eDVBService::cPCRPID, pcrpid);
+                       m_dvb_service->setCachePID(eDVBService::cTPID, tpid);
+               }
        }
 }