X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/a6014454bb8627882386ced03f75628304078637..8ef240801bf66e543cdea9df3b32fc09cebcc7e5:/lib/dvb/dvb.cpp diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index cac5a679..f8f95276 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -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 &channel, int frontend_index) { ePtr 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 &channel) { ePtr 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,36 +676,55 @@ void eDVBChannel::cueSheetEvent(int event) break; case eCueSheet::evtSkipmode: { - m_cue->m_seek_requests.push_back(std::pair(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(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; } case eCueSheet::evtSpanChanged: - eDebug("source span translation not yet supported"); - // recheckCuesheetSpans(); + { + m_source_span.clear(); + for (std::list >::const_iterator i(m_cue->m_spans.begin()); i != m_cue->m_spans.end(); ++i) + { + off_t offset_in, offset_out; + pts_t pts_in = i->first, pts_out = i->second; + if (m_tstools.getOffset(offset_in, pts_in) || m_tstools.getOffset(offset_out, pts_out)) + { + eDebug("span translation failed.\n"); + continue; + } + eDebug("source span: %llx .. %llx, translated to %llx..%llx", pts_in, pts_out, offset_in, offset_out); + m_source_span.push_back(std::pair(offset_in, offset_out)); + } break; } + } } /* remember, this gets called from another thread. */ @@ -685,6 +739,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) { @@ -702,13 +758,6 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off eDebug("getNextSourceSpan, current offset is %08llx!", current_offset); - if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0)) - { - eDebug("reached SOF"); - m_skipmode_m = 0; - m_pvr_thread->sendEvent(eFilePushThread::evtUser); - } - current_offset += m_skipmode_m; while (!m_cue->m_seek_requests.empty()) @@ -718,29 +767,52 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off int relative = seek.first; pts_t pts = seek.second; - int bitrate = m_tstools.calcBitrate(); /* in bits/s */ - - if (bitrate == -1) - continue; - + pts_t now = 0; 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!"); continue; } + } + + if (relative == 1) /* pts relative */ pts += now; + + if (relative != 2) + if (pts < 0) + pts = 0; + + if (relative == 2) /* AP relative */ + { + eDebug("AP relative seeking: %lld, at %lld", pts, now); + pts_t nextap; + if (m_tstools.getNextAccessPoint(nextap, now, pts)) + { + pts = now; + eDebug("AP relative seeking failed!"); + } else + { + eDebug("next ap is %llx\n", pts); + pts = nextap; + } } - - if (pts < 0) - pts = 0; - - off_t offset = (pts * (pts_t)bitrate) / 8ULL / 90000ULL; + off_t offset = 0; + if (m_tstools.getOffset(offset, pts)) + continue; + eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset); current_offset = offset; } @@ -758,27 +830,58 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off } if (current_offset < i->first) { - start = i->first; - size = i->second - i->first; - if (size > max) + /* ok, our current offset is in an 'out' zone. */ + if ((m_skipmode_m >= 0) || (i == m_source_span.begin())) + { + /* in normal playback, just start at the next zone. */ + start = i->first; + size = i->second - i->first; + if (size > max) + size = max; + eDebug("skip"); + if (m_skipmode_m < 0) + { + eDebug("reached SOF"); + /* reached SOF */ + m_skipmode_m = 0; + m_pvr_thread->sendEvent(eFilePushThread::evtUser); + } + } else + { + /* when skipping reverse, however, choose the zone before. */ + --i; + eDebug("skip to previous block, which is %llx..%llx", i->first, i->second); + size_t len = i->second - i->first; + if (max > len) + max = len; + start = i->second - max; size = max; - eDebug("skip"); + eDebug("skipping to %llx, %d", start, size); + } return; } } + if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0)) + { + eDebug("reached SOF"); + m_skipmode_m = 0; + m_pvr_thread->sendEvent(eFilePushThread::evtUser); + } + start = current_offset; size = max; eDebug("END OF CUESHEET. (%08llx, %d)", start, size); - - if (size < 4096) - eFatal("blub"); return; } 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 +891,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 &feparm) @@ -889,21 +997,17 @@ RESULT eDVBChannel::playFile(const char *file) THEN DO A REAL FIX HERE! */ /* (this codepath needs to be improved anyway.) */ +#if HAVE_DVB_API_VERSION < 3 + m_pvr_fd_dst = open("/dev/pvr", O_WRONLY); +#else m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY); +#endif if (m_pvr_fd_dst < 0) { eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved. return -ENODEV; } - m_pvr_fd_src = open(file, O_RDONLY|O_LARGEFILE); - if (m_pvr_fd_src < 0) - { - eDebug("can't open PVR m_pvr_fd_src file %s (%m)", file); - close(m_pvr_fd_dst); - return -ENOENT; - } - m_state = state_ok; m_stateChanged(this); @@ -911,7 +1015,13 @@ RESULT eDVBChannel::playFile(const char *file) m_pvr_thread->enablePVRCommit(1); m_pvr_thread->setScatterGather(this); - m_pvr_thread->start(m_pvr_fd_src, m_pvr_fd_dst); + if (m_pvr_thread->start(file, m_pvr_fd_dst)) + { + delete m_pvr_thread; + m_pvr_thread = 0; + eDebug("can't open PVR file %s (%m)", file); + return -ENOENT; + } CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent); return 0; @@ -922,7 +1032,6 @@ void eDVBChannel::stopFile() if (m_pvr_thread) { m_pvr_thread->stop(); - ::close(m_pvr_fd_src); ::close(m_pvr_fd_dst); delete m_pvr_thread; m_pvr_thread = 0; @@ -947,39 +1056,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 +1121,45 @@ eCueSheet::eCueSheet() void eCueSheet::seekTo(int relative, const pts_t &pts) { - m_seek_requests.push_back(std::pair(relative, pts)); + { + eSingleLock l(m_lock); + m_seek_requests.push_back(std::pair(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(begin, end)); + { + eSingleLock l(m_lock); + m_spans.push_back(std::pair(begin, end)); + } +} + +void eCueSheet::commitSpans() +{ 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 &event, ePtr &connection)