fix ratio calculation for slow reverse skipping
[enigma2.git] / lib / dvb / dvb.cpp
index cac5a67925c11b125ed744af33017d034ba84dd9..f8f5a3785d567e97df50a3a6ddb7bcd860301b95 100644 (file)
@@ -43,6 +43,7 @@ DEFINE_REF(eDVBResourceManager);
 eDVBResourceManager *eDVBResourceManager::instance;
 
 eDVBResourceManager::eDVBResourceManager()
+       :m_releaseCachedChannelTimer(eApp)
 {
        avail = 1;
        busy = 0;
@@ -63,6 +64,8 @@ eDVBResourceManager::eDVBResourceManager()
        
        eDebug("found %d adapter, %d frontends and %d demux", 
                m_adapter.size(), m_frontend.size(), m_demux.size());
+
+       CONNECT(m_releaseCachedChannelTimer.timeout, eDVBResourceManager::releaseCachedChannel);
 }
 
 
@@ -307,7 +310,9 @@ RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUse
                        channel = m_cached_channel;
                        return 0;
                }
+               m_cached_channel_state_changed_conn.disconnect();
                m_cached_channel=0;
+               m_releaseCachedChannelTimer.stop();
        }
 
 //     eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
@@ -355,16 +360,52 @@ RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUse
                return errChidNotFound;
        }
        m_cached_channel = channel = ch;
+       m_cached_channel_state_changed_conn =
+               CONNECT(ch->m_stateChanged,eDVBResourceManager::DVBChannelStateChanged);
 
        return 0;
 }
 
+void eDVBResourceManager::DVBChannelStateChanged(iDVBChannel *chan)
+{
+       int state=0;
+       chan->getState(state);
+       switch (state)
+       {
+               case iDVBChannel::state_release:
+               case iDVBChannel::state_ok:
+               {
+                       eDebug("stop release channel timer");
+                       m_releaseCachedChannelTimer.stop();
+                       break;
+               }
+               case iDVBChannel::state_last_instance:
+               {
+                       eDebug("start release channel timer");
+                       m_releaseCachedChannelTimer.start(3000, true);
+                       break;
+               }
+               default: // ignore all other events
+                       break;
+       }
+}
+
+void eDVBResourceManager::releaseCachedChannel()
+{
+       eDebug("release cached channel (timer timeout)");
+       m_cached_channel=0;
+}
+
 RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel, int frontend_index)
 {
        ePtr<eDVBAllocatedFrontend> fe;
 
        if (m_cached_channel)
+       {
+               m_cached_channel_state_changed_conn.disconnect();
                m_cached_channel=0;
+               m_releaseCachedChannelTimer.stop();
+       }
 
        if (allocateFrontendByIndex(fe, frontend_index))
                return errNoFrontend;
@@ -381,9 +422,6 @@ RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
 {
        ePtr<eDVBAllocatedDemux> demux;
 
-       if (m_cached_channel)
-               m_cached_channel=0;
-
        eDVBChannel *ch;
        ch = new eDVBChannel(this, 0);
        
@@ -630,9 +668,6 @@ void eDVBChannel::pvrEvent(int event)
 
 void eDVBChannel::cueSheetEvent(int event)
 {
-               /* we need proper locking here! */
-       eDebug("CUE SHEET EVENT %d", event);
-       
        switch (event)
        {
        case eCueSheet::evtSeek:
@@ -641,27 +676,33 @@ void eDVBChannel::cueSheetEvent(int event)
                break;
        case eCueSheet::evtSkipmode:
        {
-               m_cue->m_seek_requests.push_back(std::pair<int, pts_t>(1, 0)); /* resync */
-               if (m_cue->m_skipmode_ratio)
                {
-                       int bitrate = m_tstools.calcBitrate(); /* in bits/s */
-                       eDebug("skipmode ratio is %lld:90000, bitrate is %d bit/s", m_cue->m_skipmode_ratio, bitrate);
-                                       /* i agree that this might look a bit like black magic. */
-                       m_skipmode_n = 512*1024; /* must be 1 iframe at least. */
-                       m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio;
-
-                       eDebug("resolved to: %d %d", m_skipmode_m, m_skipmode_n);
-                       
-                       if (abs(m_skipmode_m) < abs(m_skipmode_n))
+                       eSingleLocker l(m_cue->m_lock);
+                       m_cue->m_seek_requests.push_back(std::pair<int, pts_t>(1, 0)); /* resync */
+                       if (m_cue->m_skipmode_ratio)
                        {
-                               eFatal("damn, something is wrong with this calculation");
+                               int bitrate = m_tstools.calcBitrate(); /* in bits/s */
+                               eDebug("skipmode ratio is %lld:90000, bitrate is %d bit/s", m_cue->m_skipmode_ratio, bitrate);
+                                               /* i agree that this might look a bit like black magic. */
+                               m_skipmode_n = 512*1024; /* must be 1 iframe at least. */
+                               m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio / 8;
+                               
+                               if (m_cue->m_skipmode_ratio < 0)
+                                       m_skipmode_m -= m_skipmode_n;
+       
+                               eDebug("resolved to: %d %d", m_skipmode_m, m_skipmode_n);
+                               
+                               if (abs(m_skipmode_m) < abs(m_skipmode_n))
+                               {
+                                       eWarning("something is wrong with this calculation");
+                                       m_skipmode_n = m_skipmode_m = 0;
+                               }
+                               
+                       } else
+                       {
+                               eDebug("skipmode ratio is 0, normal play");
                                m_skipmode_n = m_skipmode_m = 0;
                        }
-                       
-               } else
-               {
-                       eDebug("skipmode ratio is 0, normal play");
-                       m_skipmode_n = m_skipmode_m = 0;
                }
                flushPVR(m_cue->m_decoding_demux);
                break;
@@ -685,6 +726,8 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                size = max;
                return;
        }
+
+       eSingleLocker l(m_cue->m_lock);
        
        if (!m_cue->m_decoding_demux)
        {
@@ -726,8 +769,16 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
                if (relative)
                {
                        pts_t now;
-                                       /* we're using the decoder's timestamp here. this 
-                                          won't work for radio (ouch). */
+                       if (!m_cue->m_decoder)
+                       {
+                               eDebug("no decoder - can't seek relative");
+                               continue;
+                       }
+                       if (m_cue->m_decoder->getPTS(0, now))
+                       {
+                               eDebug("decoder getPTS failed, can't seek relative");
+                               continue;
+                       }
                        if (getCurrentPosition(m_cue->m_decoding_demux, now, 1))
                        {
                                eDebug("seekTo: getCurrentPosition failed!");
@@ -778,7 +829,11 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off
 
 void eDVBChannel::AddUse()
 {
-       ++m_use_count;
+       if (++m_use_count > 1 && m_state == state_last_instance)
+       {
+               m_state = state_ok;
+               m_stateChanged(this);
+       }
 }
 
 void eDVBChannel::ReleaseUse()
@@ -788,6 +843,11 @@ void eDVBChannel::ReleaseUse()
                m_state = state_release;
                m_stateChanged(this);
        }
+       else if (m_use_count == 1)
+       {
+               m_state = state_last_instance;
+               m_stateChanged(this);
+       }
 }
 
 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontendParameters> &feparm)
@@ -947,39 +1007,30 @@ RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, in
        if (!decoding_demux)
                return -1;
        
-       off_t begin = 0;
-               /* getPTS for offset 0 is cached, so it doesn't harm. */
-       int r = m_tstools.getPTS(begin, pos);
-       if (r)
-       {
-               eDebug("tstools getpts(0) failed!");
-               return r;
-       }
-       
        pts_t now;
        
-                       /* TODO: this is a gross hack. */
-       r = decoding_demux->getSTC(now, mode ? 128 : 0);
-
-       if (r)
+       int r;
+       
+       if (mode == 0) /* demux */
        {
-               eDebug("demux getSTC failed");
-               return -1;
-       }
+               r = decoding_demux->getSTC(now, 0);
+               if (r)
+               {
+                       eDebug("demux getSTC failed");
+                       return -1;
+               }
+       } else
+               now = pos; /* fixup supplied */
        
-//     eDebug("STC: %08llx PTS: %08llx, diff %lld", now, pos, now - pos);
-               /* when we are less than 10 seconds before the start, return 0. */
-               /* (we're just waiting for the timespam to start) */
-       if ((now < pos) && ((pos - now) < 90000 * 10))
+       off_t off = 0; /* TODO: fixme */
+       r = m_tstools.fixupPTS(off, now);
+       if (r)
        {
-               pos = 0;
-               return 0;
+               eDebug("fixup PTS failed");
+               return -1;
        }
        
-       if (now < pos) /* wrap around */
-               pos = now + ((pts_t)1)<<33 - pos;
-       else
-               pos = now - pos;
+       pos = now;
        
        return 0;
 }
@@ -1021,30 +1072,41 @@ eCueSheet::eCueSheet()
 
 void eCueSheet::seekTo(int relative, const pts_t &pts)
 {
-       m_seek_requests.push_back(std::pair<int, pts_t>(relative, pts));
+       {
+               eSingleLock l(m_lock);
+               m_seek_requests.push_back(std::pair<int, pts_t>(relative, pts));
+       }
        m_event(evtSeek);
 }
        
 void eCueSheet::clear()
 {
+       eSingleLock l(m_lock);
        m_spans.clear();
 }
 
 void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
 {
-       m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
+       {
+               eSingleLock l(m_lock);
+               m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
+       }
        m_event(evtSpanChanged);
 }
 
 void eCueSheet::setSkipmode(const pts_t &ratio)
 {
-       m_skipmode_ratio = ratio;
+       {
+               eSingleLock l(m_lock);
+               m_skipmode_ratio = ratio;
+       }
        m_event(evtSkipmode);
 }
 
-void eCueSheet::setDecodingDemux(iDVBDemux *demux)
+void eCueSheet::setDecodingDemux(iDVBDemux *demux, iTSMPEGDecoder *decoder)
 {
        m_decoding_demux = demux;
+       m_decoder = decoder;
 }
 
 RESULT eCueSheet::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)