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;
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);
}
if (!adapter->getFrontend(frontend, i))
{
- frontend->setTone(iDVBFrontend::toneOff);
- frontend->setVoltage(iDVBFrontend::voltageOff);
frontend->setSEC(m_sec);
m_frontend.push_back(new eDVBRegisteredFrontend(frontend, adapter));
}
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;
-
+
eDVBChannel *ch;
ch = new eDVBChannel(this, 0);
return 0;
}
-bool eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm, int used_tuner_mask)
+bool eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm)
{
- eDebug("canAllocateFrontend mask %08x", used_tuner_mask);
ePtr<eDVBRegisteredFrontend> best;
int bestval = 0;
for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
- if ( !(used_tuner_mask & (1<<i->m_frontend->getID())) )
+ if (!i->m_inuse)
{
int c = i->m_frontend->isCompatibleWith(feparm);
if (c > bestval)
return bestval>0;
}
-bool eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid)
+bool eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore)
{
- return true;
-#if 0
- int used_tuner_mask=0;
+ bool ret=true;
+ 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());
- int cnt=0;
- for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i, ++cnt)
+ for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
{
// eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
if (i->m_channel_id == channelid)
{
// eDebug("found shared channel..");
- return true;
+ return ret;
}
+ }
+
+ int *decremented_cached_channel_fe_usecount=NULL,
+ *decremented_fe_usecount=NULL;
- eDebug("activeChannel %d, NonDecoderDemux %p, DecoderDemux %p",
- cnt, i->m_channel->getNonDecoderDemux(), i->m_channel->getDecoderDemux() );
- if ( i->m_channel->getNonDecoderDemux() )
+ for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
+ {
+// eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
+ if (i->m_channel_id == ignore)
{
- ePtr<iDVBFrontend> fe;
- if (!i->m_channel->getFrontend(fe))
+ eDVBChannel *channel = (eDVBChannel*) &(*i->m_channel);
+ if (channel == &(*m_cached_channel) ? channel->getUseCount() == 2 : channel->getUseCount() == 1) // channel only used once..
{
- if (fe)
+ ePtr<iDVBFrontend> fe;
+ if (!i->m_channel->getFrontend(fe))
{
for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(m_frontend.begin()); ii != m_frontend.end(); ++ii)
{
if ( &(*fe) == &(*ii->m_frontend) )
{
- used_tuner_mask |= (1 << ii->m_frontend->getID());
+ --ii->m_inuse;
+ decremented_fe_usecount = &ii->m_inuse;
+ if (channel == &(*m_cached_channel))
+ decremented_cached_channel_fe_usecount = decremented_fe_usecount;
break;
}
}
}
- else
- eDebug("fe kaputt");
}
+ 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 = false;
+ goto error;
}
- ePtr<iDVBFrontendParameters> feparm;
if (m_list->getChannelFrontendData(channelid, feparm))
{
eDebug("channel not found!");
- return false;
+ ret = false;
+ goto error;
}
- return canAllocateFrontend(feparm, used_tuner_mask);
-#endif
+ ret = canAllocateFrontend(feparm);
+
+error:
+ if (decremented_fe_usecount)
+ ++(*decremented_fe_usecount);
+ if (decremented_cached_channel_fe_usecount)
+ ++(*decremented_cached_channel_fe_usecount);
+
+ return ret;
}
DEFINE_REF(eDVBChannel);
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_feparm)
+ {
+ eDebug("OURSTATE: lost lock, trying to retune");
+ ourstate = state_tuning;
+ m_frontend->get().tune(*m_feparm);
+ } 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;
+
+ eDebug("resolved to: %d %d", m_skipmode_m, m_skipmode_n);
+
+ if (abs(m_skipmode_m) < abs(m_skipmode_n))
+ {
+ eFatal("damn, 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:
+ eDebug("source span translation not yet supported");
+ // recheckCuesheetSpans();
+ 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);
+
+ 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())
+ {
+ 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;
+
+ int bitrate = m_tstools.calcBitrate(); /* in bits/s */
+
+ if (bitrate == -1)
+ continue;
+
+ if (relative)
+ {
+ pts_t now;
+ 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;
+ }
+ pts += now;
+ }
+
+ if (pts < 0)
+ pts = 0;
+
+ off_t offset = (pts * (pts_t)bitrate) / 8ULL / 90000ULL;
+
+ 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;
+ size = i->second - current_offset;
+ if (size > max)
+ size = max;
+ eDebug("HIT, %lld < %lld < %lld", i->first, current_offset, i->second);
+ return;
+ }
+ if (current_offset < i->first)
+ {
+ start = i->first;
+ size = i->second - i->first;
+ if (size > max)
+ size = max;
+ eDebug("skip");
+ return;
+ }
+ }
+
+ 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()
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_feparm = 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. */
if (cap & capDecode)
- {
our_demux = 0;
- }
return 0;
}
return -ENODEV;
}
- m_pvr_fd_src = open(file, O_RDONLY);
+ 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);
m_stateChanged(this);
m_pvr_thread = new eFilePushThread();
+ m_pvr_thread->enablePVRCommit(1);
+ m_pvr_thread->setScatterGather(this);
+
m_pvr_thread->start(m_pvr_fd_src, m_pvr_fd_dst);
+ CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent);
return 0;
}
+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;
+ }
+}
+
+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;
- }
+ int r;
-// 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))
+ if (mode == 0) /* demux */
{
- 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 */
-
- if (bitrate == -1)
- return -1;
-
- if (relative)
- {
- 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)
+{
+ {
+ 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));
+ }
+ 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;
}