eDVBAllocatedFrontend::eDVBAllocatedFrontend(eDVBRegisteredFrontend *fe): m_fe(fe)
{
- m_fe->m_inuse++;
+ m_fe->inc_use();
}
eDVBAllocatedFrontend::~eDVBAllocatedFrontend()
{
- --m_fe->m_inuse;
+ m_fe->dec_use();
}
DEFINE_REF(eDVBAllocatedDemux);
eDVBResourceManager *eDVBResourceManager::instance;
+RESULT eDVBResourceManager::getInstance(ePtr<eDVBResourceManager> &ptr)
+{
+ if (instance)
+ {
+ ptr = instance;
+ return 0;
+ }
+ return -1;
+}
+
+ePtr<eDVBResourceManager> NewResourceManagerPtr(void)
+{
+ ePtr<eDVBResourceManager> ptr;
+ eDVBResourceManager::getInstance(ptr);
+ return ptr;
+}
+
eDVBResourceManager::eDVBResourceManager()
+ :m_releaseCachedChannelTimer(eApp)
{
avail = 1;
busy = 0;
eDebug("found %d adapter, %d frontends and %d demux",
m_adapter.size(), m_frontend.size(), m_demux.size());
+
+ CONNECT(m_releaseCachedChannelTimer.timeout, eDVBResourceManager::releaseCachedChannel);
}
+void eDVBResourceManager::feStateChanged()
+{
+ int mask=0;
+ for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
+ if (i->m_inuse)
+ mask |= ( 1 << i->m_frontend->getID() );
+ /* emit */ frontendUseMaskChanged(mask);
+}
DEFINE_REF(eDVBAdapterLinux);
eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
m_demux.push_back(new eDVBRegisteredDemux(demux, adapter));
}
+ ePtr<eDVBRegisteredFrontend> prev_dvbt_frontend;
for (i=0; i<num_fe; ++i)
{
ePtr<eDVBFrontend> frontend;
-
if (!adapter->getFrontend(frontend, i))
{
- frontend->setTone(iDVBFrontend::toneOff);
- frontend->setVoltage(iDVBFrontend::voltageOff);
+ int frontendType=0;
+ frontend->getFrontendType(frontendType);
+ eDVBRegisteredFrontend *new_fe = new eDVBRegisteredFrontend(frontend, adapter);
+ CONNECT(new_fe->stateChanged, eDVBResourceManager::feStateChanged);
+ m_frontend.push_back(new_fe);
frontend->setSEC(m_sec);
- m_frontend.push_back(new eDVBRegisteredFrontend(frontend, adapter));
+ // we must link all dvb-t frontends ( for active antenna voltage )
+ if (frontendType == iDVBFrontend::feTerrestrial)
+ {
+ if (prev_dvbt_frontend)
+ {
+ prev_dvbt_frontend->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, (int)new_fe);
+ frontend->setData(eDVBFrontend::LINKED_PREV_PTR, (int)&(*prev_dvbt_frontend));
+ }
+ prev_dvbt_frontend = new_fe;
+ }
}
}
}
int n=0;
/* FIXME: hardware demux policy */
if (!(cap & iDVBChannel::capDecode))
- ++i, ++n;
+ {
+ if (m_demux.size() > 2) /* assumed to be true, otherwise we have lost anyway */
+ {
+ ++i, ++n;
+ ++i, ++n;
+ }
+ }
for (; i != m_demux.end(); ++i, ++n)
- if ((!i->m_inuse) && ((!fe) || (i->m_adapter == fe->m_adapter)))
+ {
+ int is_decode = n < 2;
+
+ int in_use = is_decode ? (i->m_demux->getRefCount() != 2) : i->m_inuse;
+
+ if ((!in_use) && ((!fe) || (i->m_adapter == fe->m_adapter)))
{
- if ((cap & iDVBChannel::capDecode) && n)
+ if ((cap & iDVBChannel::capDecode) && !is_decode)
continue;
demux = new eDVBAllocatedDemux(i);
demux->get().setSourcePVR(0);
return 0;
}
+ }
eDebug("demux not found");
return -1;
}
RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel)
{
/* first, check if a channel is already existing. */
-
+
+ if (m_cached_channel)
+ {
+ eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
+ if(channelid==cache_chan->getChannelID())
+ {
+ eDebug("use cached_channel");
+ 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());
for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
{
channel = 0;
return errChidNotFound;
}
- channel = ch;
+ 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;
RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
{
ePtr<eDVBAllocatedDemux> demux;
-
+
+ if (m_cached_channel && m_releaseCachedChannelTimer.isActive())
+ {
+ m_cached_channel_state_changed_conn.disconnect();
+ m_cached_channel=0;
+ m_releaseCachedChannelTimer.stop();
+ }
+
eDVBChannel *ch;
ch = new eDVBChannel(this, 0);
-
+
channel = ch;
return 0;
}
return 0;
}
-bool eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm)
+int eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm)
{
ePtr<eDVBRegisteredFrontend> best;
int bestval = 0;
bestval = c;
}
- return bestval>0;
+ return bestval;
}
-bool eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore)
+int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore)
{
+ int ret=30000;
+ if (m_cached_channel)
+ {
+ eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
+ if(channelid==cache_chan->getChannelID())
+ return ret;
+ }
+
/* first, check if a channel is already existing. */
// eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
if (i->m_channel_id == channelid)
{
// eDebug("found shared channel..");
- return true;
+ return ret;
}
}
- int *decremented_fe_usecount=NULL;
+ int *decremented_cached_channel_fe_usecount=NULL,
+ *decremented_fe_usecount=NULL;
for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
{
if (i->m_channel_id == ignore)
{
eDVBChannel *channel = (eDVBChannel*) &(*i->m_channel);
- if (channel->getUseCount() == 1) // channel only used once..
+ // one eUsePtr<iDVBChannel> is used in eDVBServicePMTHandler
+ // another on eUsePtr<iDVBChannel> is used in the eDVBScan instance used in eDVBServicePMTHandler (for SDT scan)
+ // so we must check here if usecount is 3 (when the channel is equal to the cached channel)
+ // or 2 when the cached channel is not equal to the compared channel
+ if (channel == &(*m_cached_channel) ? channel->getUseCount() == 3 : channel->getUseCount() == 2) // channel only used once..
{
ePtr<iDVBFrontend> fe;
if (!i->m_channel->getFrontend(fe))
{
--ii->m_inuse;
decremented_fe_usecount = &ii->m_inuse;
+ if (channel == &(*m_cached_channel))
+ decremented_cached_channel_fe_usecount = decremented_fe_usecount;
break;
}
}
}
}
+ if (!decremented_cached_channel_fe_usecount)
+ {
+ if (m_cached_channel)
+ {
+ eDVBChannel *channel = (eDVBChannel*) &(*m_cached_channel);
+ if (channel->getUseCount() == 1)
+ {
+ ePtr<iDVBFrontend> fe;
+ if (!channel->getFrontend(fe))
+ {
+ for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(m_frontend.begin()); ii != m_frontend.end(); ++ii)
+ {
+ if ( &(*fe) == &(*ii->m_frontend) )
+ {
+ --ii->m_inuse;
+ decremented_cached_channel_fe_usecount = &ii->m_inuse;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ decremented_cached_channel_fe_usecount=NULL;
+
+ ePtr<iDVBFrontendParameters> feparm;
+
if (!m_list)
{
eDebug("no channel list set!");
- return false;
+ ret = 0;
+ goto error;
}
- ePtr<iDVBFrontendParameters> feparm;
if (m_list->getChannelFrontendData(channelid, feparm))
{
eDebug("channel not found!");
- return false;
+ ret = 0;
+ goto error;
}
- bool ret = canAllocateFrontend(feparm);
+ ret = canAllocateFrontend(feparm);
+error:
if (decremented_fe_usecount)
++(*decremented_fe_usecount);
+ if (decremented_cached_channel_fe_usecount)
+ ++(*decremented_cached_channel_fe_usecount);
return ret;
}
m_pvr_thread = 0;
+ m_skipmode_n = m_skipmode_m = 0;
+
if (m_frontend)
m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
}
{
if (m_channel_id)
m_mgr->removeChannel(this);
-
- if (m_pvr_thread)
- {
- m_pvr_thread->stop();
- ::close(m_pvr_fd_src);
- ::close(m_pvr_fd_dst);
- delete m_pvr_thread;
- }
+
+ stopFile();
}
void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
ourstate = state_tuning;
} else if (state == iDVBFrontend::stateLostLock)
{
- eDebug("OURSTATE: lost lock");
- ourstate = state_unavailable;
+ /* on managed channels, we try to retune in order to re-acquire lock. */
+ if (m_current_frontend_parameters)
+ {
+ eDebug("OURSTATE: lost lock, trying to retune");
+ ourstate = state_tuning;
+ m_frontend->get().tune(*m_current_frontend_parameters);
+ } else
+ /* on unmanaged channels, we don't do this. the client will do this. */
+ {
+ eDebug("OURSTATE: lost lock, unavailable now.");
+ ourstate = state_unavailable;
+ }
} else if (state == iDVBFrontend::stateFailed)
{
eDebug("OURSTATE: failed");
}
}
+void eDVBChannel::pvrEvent(int event)
+{
+ switch (event)
+ {
+ case eFilePushThread::evtEOF:
+ eDebug("eDVBChannel: End of file!");
+ m_event(this, evtEOF);
+ break;
+ case eFilePushThread::evtUser: /* start */
+ eDebug("SOF");
+ m_event(this, evtSOF);
+ break;
+ }
+}
+
+void eDVBChannel::cueSheetEvent(int event)
+{
+ switch (event)
+ {
+ case eCueSheet::evtSeek:
+ eDebug("seek.");
+ flushPVR(m_cue->m_decoding_demux);
+ break;
+ case eCueSheet::evtSkipmode:
+ {
+ {
+ 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)
+ {
+ 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;
+ }
+ }
+ flushPVR(m_cue->m_decoding_demux);
+ break;
+ }
+ case eCueSheet::evtSpanChanged:
+ {
+ m_source_span.clear();
+ for (std::list<std::pair<pts_t, pts_t> >::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<off_t, off_t>(offset_in, offset_out));
+ }
+ break;
+ }
+ }
+}
+
+ /* remember, this gets called from another thread. */
+void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size)
+{
+ unsigned int max = 10*1024*1024;
+
+ if (!m_cue)
+ {
+ eDebug("no cue sheet. forcing normal play");
+ start = current_offset;
+ size = max;
+ return;
+ }
+
+ eSingleLocker l(m_cue->m_lock);
+
+ if (!m_cue->m_decoding_demux)
+ {
+ start = current_offset;
+ size = max;
+ eDebug("getNextSourceSpan, no decoding demux. forcing normal play");
+ return;
+ }
+
+ if (m_skipmode_n)
+ {
+ eDebug("skipmode %d:%d", m_skipmode_m, m_skipmode_n);
+ max = m_skipmode_n;
+ }
+
+ eDebug("getNextSourceSpan, current offset is %08llx!", current_offset);
+
+ current_offset += m_skipmode_m;
+
+ while (!m_cue->m_seek_requests.empty())
+ {
+ std::pair<int, pts_t> seek = m_cue->m_seek_requests.front();
+ m_cue->m_seek_requests.pop_front();
+ int relative = seek.first;
+ pts_t pts = seek.second;
+
+ pts_t now = 0;
+ if (relative)
+ {
+ 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 (pts < 0)
+ pts = 0;
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ }
+
+ for (std::list<std::pair<off_t, off_t> >::const_iterator i(m_source_span.begin()); i != m_source_span.end(); ++i)
+ {
+ if ((current_offset >= i->first) && (current_offset < i->second))
+ {
+ start = current_offset;
+ /* max can not exceed max(size_t). i->second - current_offset, however, can. */
+ if ((i->second - current_offset) > max)
+ size = max;
+ else
+ size = i->second - current_offset;
+ eDebug("HIT, %lld < %lld < %lld, size: %d", i->first, current_offset, i->second, size);
+ return;
+ }
+ if (current_offset < i->first)
+ {
+ /* 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 is not 64bit! */
+ if ((i->second - i->first) > max)
+ size = max;
+ else
+ size = i->second - i->first;
+
+ 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;
+
+ if ((i->second - i->first) > max)
+ len = max;
+ else
+ len = i->second - i->first;
+
+ start = i->second - len;
+ eDebug("skipping to %llx, %d", start, len);
+ }
+ 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);
+ 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()
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)
/* if tuning fails, shutdown the channel immediately. */
int res;
res = m_frontend->get().tune(*feparm);
+ m_current_frontend_parameters = feparm;
if (res)
{
return 0;
}
+RESULT eDVBChannel::connectEvent(const Slot2<void,iDVBChannel*,int> &event, ePtr<eConnection> &connection)
+{
+ connection = new eConnection((iDVBChannel*)this, m_event.connect(event));
+ return 0;
+}
+
RESULT eDVBChannel::getState(int &state)
{
state = m_state;
RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
{
- eDebug("get %d demux", cap);
ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
if (!our_demux)
if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
return -1;
-
}
demux = *our_demux;
+ /* don't hold a reference to the decoding demux, we don't need it. */
+
+ /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux',
+ the refcount is lost. thus, decoding demuxes are never allocated.
+
+ this poses a big problem for PiP. */
if (cap & capDecode)
- {
our_demux = 0;
- }
return 0;
}
RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
{
+ frontend = 0;
+ if (!m_frontend)
+ return -ENODEV;
frontend = &m_frontend->get();
if (frontend)
return 0;
- else
- return -ENODEV;
+ return -ENODEV;
+}
+
+RESULT eDVBChannel::getCurrentFrontendParameters(ePtr<iDVBFrontendParameters> ¶m)
+{
+ param = m_current_frontend_parameters;
+ return 0;
}
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);
- if (m_pvr_fd_src < 0)
+
+ 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))
{
- eDebug("can't open PVR m_pvr_fd_src file %s (%m)", file);
- close(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);
-
- m_pvr_thread = new eFilePushThread();
- m_pvr_thread->start(m_pvr_fd_src, m_pvr_fd_dst);
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)
+RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, int mode)
{
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;
- r = decoding_demux->getSTC(now);
-
- if (r)
- {
- eDebug("demux getSTC failed");
- return -1;
- }
-
-// 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))
- {
- pos = 0;
- return 0;
- }
-
- if (now < pos) /* wrap around */
- pos = now + ((pts_t)1)<<33 - pos;
- else
- pos = now - pos;
-
- return 0;
-}
-
-RESULT eDVBChannel::seekTo(iDVBDemux *decoding_demux, int relative, pts_t &pts)
-{
- int bitrate = m_tstools.calcBitrate(); /* in bits/s */
+ int r;
- if (bitrate == -1)
- return -1;
-
- if (relative)
+ if (mode == 0) /* demux */
{
- pts_t now;
- if (getCurrentPosition(decoding_demux, now))
+ r = decoding_demux->getSTC(now, 0);
+ if (r)
{
- eDebug("seekTo: getCurrentPosition failed!");
+ eDebug("demux getSTC failed");
return -1;
}
- pts += now;
- }
+ } else
+ now = pos; /* fixup supplied */
- if (pts < 0)
- pts = 0;
+ off_t off = 0; /* TODO: fixme */
+ r = m_tstools.fixupPTS(off, now);
+ if (r)
+ {
+ eDebug("fixup PTS failed");
+ return -1;
+ }
- off_t offset = (pts * (pts_t)bitrate) / 8ULL / 90000ULL;
+ pos = now;
- seekToPosition(decoding_demux, offset);
return 0;
}
-RESULT eDVBChannel::seekToPosition(iDVBDemux *decoding_demux, const off_t &r)
+void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
{
/* when seeking, we have to ensure that all buffers are flushed.
there are basically 3 buffers:
the ratebuffer (for example) would immediately refill from
the not-yet-flushed PVR buffer.
*/
- eDebug("eDVBChannel: seekToPosition .. %llx", r);
- m_pvr_thread->pause();
+ m_pvr_thread->pause();
/* flush internal filepush buffer */
m_pvr_thread->flush();
-
/* HACK: flush PVR buffer */
::ioctl(m_pvr_fd_dst, 0);
decoding_demux->flush();
/* demux will also flush all decoder.. */
- m_pvr_thread->seek(SEEK_SET, r);
+ /* 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)
+{
+ {
+ eSingleLocker l(m_lock);
+ m_seek_requests.push_back(std::pair<int, pts_t>(relative, pts));
+ }
+ m_event(evtSeek);
+}
+
+void eCueSheet::clear()
+{
+ eSingleLocker l(m_lock);
+ m_spans.clear();
+}
+
+void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
+{
+ {
+ eSingleLocker 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)
+{
+ {
+ eSingleLocker 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;
}