+ }
+
+ m_pvr_thread = new eFilePushThread();
+ m_pvr_thread->enablePVRCommit(1);
+ m_pvr_thread->setScatterGather(this);
+
+ 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);
+
+ m_state = state_ok;
+ m_stateChanged(this);
+
+ return 0;
+}
+
+void eDVBChannel::stopFile()
+{
+ if (m_pvr_thread)
+ {
+ m_pvr_thread->stop();
+ ::close(m_pvr_fd_dst);
+ delete m_pvr_thread;
+ m_pvr_thread = 0;
+ }
+}
+
+void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
+{
+ m_conn_cueSheetEvent = 0;
+ m_cue = cuesheet;
+ if (m_cue)
+ m_cue->connectEvent(slot(*this, &eDVBChannel::cueSheetEvent), m_conn_cueSheetEvent);
+}
+
+RESULT eDVBChannel::getLength(pts_t &len)
+{
+ return m_tstools.calcLen(len);
+}
+
+RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, int mode)
+{
+ if (!decoding_demux)
+ return -1;
+
+ pts_t now;
+
+ int r;
+
+ if (mode == 0) /* demux */
+ {
+ r = decoding_demux->getSTC(now, 0);
+ if (r)
+ {
+ eDebug("demux getSTC failed");
+ return -1;
+ }
+ } else
+ now = pos; /* fixup supplied */
+
+ off_t off = 0; /* TODO: fixme */
+ r = m_tstools.fixupPTS(off, now);
+ if (r)
+ {
+ eDebug("fixup PTS failed");
+ return -1;
+ }
+
+ pos = now;
+
+ return 0;
+}
+
+void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
+{
+ /* when seeking, we have to ensure that all buffers are flushed.
+ there are basically 3 buffers:
+ a.) the filepush's internal buffer
+ b.) the PVR buffer (before demux)
+ c.) the ratebuffer (after demux)
+
+ it's important to clear them in the correct order, otherwise
+ the ratebuffer (for example) would immediately refill from
+ the not-yet-flushed PVR buffer.
+ */
+
+ m_pvr_thread->pause();
+ /* flush internal filepush buffer */
+ m_pvr_thread->flush();
+ /* HACK: flush PVR buffer */
+ ::ioctl(m_pvr_fd_dst, 0);
+
+ /* flush ratebuffers (video, audio) */
+ if (decoding_demux)
+ decoding_demux->flush();
+
+ /* demux will also flush all decoder.. */
+ /* resume will re-query the SG */
+ m_pvr_thread->resume();
+}
+
+DEFINE_REF(eCueSheet);
+
+eCueSheet::eCueSheet()
+{
+ m_skipmode_ratio = 0;
+}
+
+void eCueSheet::seekTo(int relative, const pts_t &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)
+{
+ {
+ eSingleLock l(m_lock);
+ m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
+ }
+}
+
+void eCueSheet::commitSpans()
+{
+ m_event(evtSpanChanged);
+}
+
+void eCueSheet::setSkipmode(const pts_t &ratio)
+{
+ {
+ eSingleLock l(m_lock);
+ m_skipmode_ratio = ratio;
+ }
+ m_event(evtSkipmode);
+}
+
+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)
+{
+ connection = new eConnection(this, m_event.connect(event));
+ return 0;