-#include <lib/dvb/idvb.h>
#include <lib/base/eerror.h>
+#include <lib/base/filepush.h>
+#include <lib/dvb/idvb.h>
#include <lib/dvb/dvb.h>
#include <lib/dvb/sec.h>
+
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+DEFINE_REF(eDVBRegisteredFrontend);
+DEFINE_REF(eDVBRegisteredDemux);
+
+DEFINE_REF(eDVBAllocatedFrontend);
+
+eDVBAllocatedFrontend::eDVBAllocatedFrontend(eDVBRegisteredFrontend *fe): m_fe(fe)
+{
+ m_fe->inc_use();
+}
+
+eDVBAllocatedFrontend::~eDVBAllocatedFrontend()
+{
+ m_fe->dec_use();
+}
+
+DEFINE_REF(eDVBAllocatedDemux);
+
+eDVBAllocatedDemux::eDVBAllocatedDemux(eDVBRegisteredDemux *demux): m_demux(demux)
+{
+ m_demux->m_inuse++;
+}
+
+eDVBAllocatedDemux::~eDVBAllocatedDemux()
+{
+ --m_demux->m_inuse;
+}
DEFINE_REF(eDVBResourceManager);
eDVBResourceManager *eDVBResourceManager::instance;
-eDVBResourceManager::eDVBResourceManager(): ref(0)
+eDVBResourceManager::eDVBResourceManager()
+ :m_releaseCachedChannelTimer(eApp)
{
avail = 1;
busy = 0;
- m_sec = new eDVBSatelliteEquipmentControl;
+ m_sec = new eDVBSatelliteEquipmentControl(m_frontend);
if (!instance)
instance = this;
+
+ /* search available adapters... */
+
+ // add linux devices
+
+ int num_adapter = 0;
+ while (eDVBAdapterLinux::exist(num_adapter))
+ {
+ addAdapter(new eDVBAdapterLinux(num_adapter));
+ num_adapter++;
+ }
+
+ eDebug("found %d adapter, %d frontends and %d demux",
+ m_adapter.size(), m_frontend.size(), m_demux.size());
+
+ CONNECT(m_releaseCachedChannelTimer.timeout, eDVBResourceManager::releaseCachedChannel);
+}
+
+
+DEFINE_REF(eDVBAdapterLinux);
+eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
+{
+ // scan frontends
+ int num_fe = 0;
+
+ eDebug("scanning for frontends..");
+ while (1)
+ {
+ struct stat s;
+ char filename[128];
+#if HAVE_DVB_API_VERSION < 3
+ sprintf(filename, "/dev/dvb/card%d/frontend%d", m_nr, num_fe);
+#else
+ sprintf(filename, "/dev/dvb/adapter%d/frontend%d", m_nr, num_fe);
+#endif
+ if (stat(filename, &s))
+ break;
+ ePtr<eDVBFrontend> fe;
+
+ int ok = 0;
+ fe = new eDVBFrontend(m_nr, num_fe, ok);
+ if (ok)
+ m_frontend.push_back(fe);
+ ++num_fe;
+ }
+
+ // scan demux
+ int num_demux = 0;
+ while (1)
+ {
+ struct stat s;
+ char filename[128];
+#if HAVE_DVB_API_VERSION < 3
+ sprintf(filename, "/dev/dvb/card%d/demux%d", m_nr, num_demux);
+#else
+ sprintf(filename, "/dev/dvb/adapter%d/demux%d", m_nr, num_demux);
+#endif
+ if (stat(filename, &s))
+ break;
+ ePtr<eDVBDemux> demux;
+
+ demux = new eDVBDemux(m_nr, num_demux);
+ m_demux.push_back(demux);
+
+ ++num_demux;
+ }
+}
+
+int eDVBAdapterLinux::getNumDemux()
+{
+ return m_demux.size();
+}
+
+RESULT eDVBAdapterLinux::getDemux(ePtr<eDVBDemux> &demux, int nr)
+{
+ eSmartPtrList<eDVBDemux>::iterator i(m_demux.begin());
+ while (nr && (i != m_demux.end()))
+ {
+ --nr;
+ ++i;
+ }
+
+ if (i != m_demux.end())
+ demux = *i;
+ else
+ return -1;
+
+ return 0;
+}
+
+int eDVBAdapterLinux::getNumFrontends()
+{
+ return m_frontend.size();
+}
+
+RESULT eDVBAdapterLinux::getFrontend(ePtr<eDVBFrontend> &fe, int nr)
+{
+ eSmartPtrList<eDVBFrontend>::iterator i(m_frontend.begin());
+ while (nr && (i != m_frontend.end()))
+ {
+ --nr;
+ ++i;
+ }
+
+ if (i != m_frontend.end())
+ fe = *i;
+ else
+ return -1;
+
+ return 0;
+}
+
+int eDVBAdapterLinux::exist(int nr)
+{
+ struct stat s;
+ char filename[128];
+#if HAVE_DVB_API_VERSION < 3
+ sprintf(filename, "/dev/dvb/card%d", nr);
+#else
+ sprintf(filename, "/dev/dvb/adapter%d", nr);
+#endif
+ if (!stat(filename, &s))
+ return 1;
+ return 0;
}
eDVBResourceManager::~eDVBResourceManager()
instance = 0;
}
+void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
+{
+ int num_fe = adapter->getNumFrontends();
+ int num_demux = adapter->getNumDemux();
+
+ m_adapter.push_back(adapter);
+
+ int i;
+ for (i=0; i<num_demux; ++i)
+ {
+ ePtr<eDVBDemux> demux;
+ if (!adapter->getDemux(demux, i))
+ m_demux.push_back(new eDVBRegisteredDemux(demux, adapter));
+ }
+
+ for (i=0; i<num_fe; ++i)
+ {
+ ePtr<eDVBFrontend> frontend;
+
+ if (!adapter->getFrontend(frontend, i))
+ {
+ frontend->setSEC(m_sec);
+ m_frontend.push_back(new eDVBRegisteredFrontend(frontend, adapter));
+ }
+ }
+}
+
+RESULT eDVBResourceManager::allocateFrontend(ePtr<eDVBAllocatedFrontend> &fe, ePtr<iDVBFrontendParameters> &feparm)
+{
+ ePtr<eDVBRegisteredFrontend> best;
+ int bestval = 0;
+
+ for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
+ if (!i->m_inuse)
+ {
+ int c = i->m_frontend->isCompatibleWith(feparm);
+ if (c > bestval)
+ {
+ bestval = c;
+ best = i;
+ }
+ }
+
+ if (best)
+ {
+ fe = new eDVBAllocatedFrontend(best);
+ return 0;
+ }
+
+ fe = 0;
+
+ return -1;
+}
+
+RESULT eDVBResourceManager::allocateFrontendByIndex(ePtr<eDVBAllocatedFrontend> &fe, int nr)
+{
+ for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i, --nr)
+ if ((!nr) && !i->m_inuse)
+ {
+ fe = new eDVBAllocatedFrontend(i);
+ return 0;
+ }
+
+ fe = 0;
+ return -1;
+}
+
+RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int cap)
+{
+ /* find first unused demux which is on same adapter as frontend (or any, if PVR)
+ never use the first one unless we need a decoding demux. */
+
+ eDebug("allocate demux");
+ eSmartPtrList<eDVBRegisteredDemux>::iterator i(m_demux.begin());
+
+ if (i == m_demux.end())
+ return -1;
+
+ int n=0;
+ /* FIXME: hardware demux policy */
+ if (!(cap & iDVBChannel::capDecode))
+ ++i, ++n;
+
+ for (; i != m_demux.end(); ++i, ++n)
+ if ((!i->m_inuse) && ((!fe) || (i->m_adapter == fe->m_adapter)))
+ {
+ if ((cap & iDVBChannel::capDecode) && n)
+ continue;
+
+ demux = new eDVBAllocatedDemux(i);
+ if (fe)
+ demux->get().setSourceFrontend(fe->m_frontend->getID());
+ else
+ demux->get().setSourcePVR(0);
+ return 0;
+ }
+ eDebug("demux not found");
+ return -1;
+}
+
RESULT eDVBResourceManager::setChannelList(iDVBChannelList *list)
{
m_list = list;
return -ENOENT;
}
-
-RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, ePtr<iDVBChannel> &channel)
+RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel)
{
- RESULT res;
- eDVBChannel *ch;
- channel = ch = new eDVBChannel(this, 0, 0, 0);
+ /* 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)
+ {
+// 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..");
+ channel = i->m_channel;
+ return 0;
+ }
+ }
+
+ /* no currently available channel is tuned to this channelid. create a new one, if possible. */
+
+ if (!m_list)
+ {
+ eDebug("no channel list set!");
+ return -ENOENT;
+ }
- ePtr<iDVBFrontend> fe;
- if (!channel->getFrontend(fe))
- fe->setSEC(m_sec);
+ ePtr<iDVBFrontendParameters> feparm;
+ if (m_list->getChannelFrontendData(channelid, feparm))
+ {
+ eDebug("channel not found!");
+ return -ENOENT;
+ }
- res = ch->setChannel(channelid);
+ /* allocate a frontend. */
+
+ ePtr<eDVBAllocatedFrontend> fe;
+
+ if (allocateFrontend(fe, feparm))
+ return errNoFrontend;
+
+ RESULT res;
+ ePtr<eDVBChannel> ch;
+ ch = new eDVBChannel(this, fe);
+
+ res = ch->setChannel(channelid, feparm);
if (res)
{
channel = 0;
- return res;
+ return errChidNotFound;
}
+ m_cached_channel = channel = ch;
+ m_cached_channel_state_changed_conn =
+ CONNECT(ch->m_stateChanged,eDVBResourceManager::DVBChannelStateChanged);
+
return 0;
}
-RESULT eDVBResourceManager::allocateRawChannel(ePtr<iDVBChannel> &channel)
+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)
{
- channel = new eDVBChannel(this, 0, 0, 0);
- ePtr<iDVBFrontend> fe;
- if (!channel->getFrontend(fe))
- fe->setSEC(m_sec);
+ 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;
+ eDVBChannel *ch;
+ ch = new eDVBChannel(this, fe);
+
+ channel = ch;
return 0;
}
-RESULT eDVBResourceManager::allocatePVRChannel(int caps)
+
+RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
{
- return -1; // will nicht, mag nicht, und das interface ist auch kaputt
+ ePtr<eDVBAllocatedDemux> demux;
+
+ eDVBChannel *ch;
+ ch = new eDVBChannel(this, 0);
+
+ channel = ch;
+ return 0;
}
RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch)
{
- eDebug("add channel %p", ch);
- m_active_channels.insert(std::pair<eDVBChannelID,eDVBChannel*>(chid, ch));
+ m_active_channels.push_back(active_channel(chid, ch));
+ /* emit */ m_channelAdded(ch);
return 0;
}
-RESULT eDVBResourceManager::removeChannel(const eDVBChannelID &chid, eDVBChannel *)
+RESULT eDVBResourceManager::removeChannel(eDVBChannel *ch)
{
- int cnt = m_active_channels.erase(chid);
- eDebug("remove channel: removed %d channels", cnt);
- ASSERT(cnt <= 1);
+ int cnt = 0;
+ for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end();)
+ {
+ if (i->m_channel == ch)
+ {
+ i = m_active_channels.erase(i);
+ ++cnt;
+ } else
+ ++i;
+ }
+ ASSERT(cnt == 1);
if (cnt == 1)
return 0;
return -ENOENT;
}
-eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, int adapter, int frontend, int demux): eDVBDemux(adapter, demux), m_state(state_idle), m_mgr(mgr)
+RESULT eDVBResourceManager::connectChannelAdded(const Slot1<void,eDVBChannel*> &channelAdded, ePtr<eConnection> &connection)
+{
+ connection = new eConnection((eDVBResourceManager*)this, m_channelAdded.connect(channelAdded));
+ return 0;
+}
+
+bool eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm)
+{
+ ePtr<eDVBRegisteredFrontend> best;
+ int bestval = 0;
+
+ for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
+ if (!i->m_inuse)
+ {
+ int c = i->m_frontend->isCompatibleWith(feparm);
+ if (c > bestval)
+ bestval = c;
+ }
+
+ return bestval>0;
+}
+
+bool eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore)
{
- if (frontend >= 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());
+ for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
{
- int ok;
- m_frontend = new eDVBFrontend(adapter, frontend, ok);
- if (!ok)
+// 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("warning, frontend failed");
- m_frontend = 0;
- return;
+// eDebug("found shared channel..");
+ return ret;
}
- m_frontend->connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
}
+
+ 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)
+ {
+// 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)
+ {
+ eDVBChannel *channel = (eDVBChannel*) &(*i->m_channel);
+ if (channel == &(*m_cached_channel) ? channel->getUseCount() == 2 : channel->getUseCount() == 1) // channel only used once..
+ {
+ 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) )
+ {
+ --ii->m_inuse;
+ decremented_fe_usecount = &ii->m_inuse;
+ if (channel == &(*m_cached_channel))
+ decremented_cached_channel_fe_usecount = decremented_fe_usecount;
+ break;
+ }
+ }
+ }
+ }
+ 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!");
+ ret = false;
+ goto error;
+ }
+
+ if (m_list->getChannelFrontendData(channelid, feparm))
+ {
+ eDebug("channel not found!");
+ ret = false;
+ goto error;
+ }
+
+ 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);
+
+eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *frontend): m_state(state_idle), m_mgr(mgr)
+{
+ m_frontend = frontend;
+
+ 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);
}
eDVBChannel::~eDVBChannel()
{
if (m_channel_id)
- m_mgr->removeChannel(m_channel_id, this);
+ m_mgr->removeChannel(this);
+
+ stopFile();
}
void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
{
- eDebug("fe state changed!");
int state, ourstate = 0;
+
+ /* if we are already in shutdown, don't change state. */
+ if (m_state == state_release)
+ return;
+
if (fe->getState(state))
return;
{
eDebug("OURSTATE: tuning");
ourstate = state_tuning;
+ } else if (state == iDVBFrontend::stateLostLock)
+ {
+ /* 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/unavailable");
- ourstate = state_unavailable;
+ eDebug("OURSTATE: failed");
+ ourstate = state_failed;
} else
eFatal("state unknown");
}
}
-RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid)
+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)
{
- ePtr<iDVBChannelList> list;
+ 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;
- if (m_mgr->getChannelList(list))
+ 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:
{
- eDebug("no channel list set!");
- return -ENOENT;
+ 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("tuning to chid: ns: %08x tsid %04x onid %04x",
- channelid.dvbnamespace.get(), channelid.transport_stream_id.get(), channelid.original_network_id.get());
+ 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;
- ePtr<iDVBFrontendParameters> feparm;
- if (list->getChannelFrontendData(channelid, feparm))
+ 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)
{
- eDebug("channel not found!");
- return -ENOENT;
+ 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)
+ {
+ /* 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("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);
}
- eDebug("allocateChannel: channel found..");
+ start = current_offset;
+ size = max;
+ eDebug("END OF CUESHEET. (%08llx, %d)", start, size);
+ return;
+}
+
+void eDVBChannel::AddUse()
+{
+ if (++m_use_count > 1 && m_state == state_last_instance)
+ {
+ m_state = state_ok;
+ m_stateChanged(this);
+ }
+}
+
+void eDVBChannel::ReleaseUse()
+{
+ if (!--m_use_count)
+ {
+ 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 (m_channel_id)
+ m_mgr->removeChannel(this);
+
+ if (!channelid)
+ return 0;
+
if (!m_frontend)
{
eDebug("no frontend to tune!");
return -ENODEV;
}
- if (m_channel_id)
- m_mgr->removeChannel(m_channel_id, this);
m_channel_id = channelid;
- m_mgr->addChannel(m_channel_id, this);
+ m_mgr->addChannel(channelid, this);
m_state = state_tuning;
- eDebug("%p", &*feparm);
- return m_frontend->tune(*feparm);
+ /* if tuning fails, shutdown the channel immediately. */
+ int res;
+ res = m_frontend->get().tune(*feparm);
+ m_feparm = feparm;
+
+ if (res)
+ {
+ m_state = state_release;
+ m_stateChanged(this);
+ return res;
+ }
+
+ return 0;
}
RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
{
- connection = new eConnection(m_stateChanged.connect(stateChange));
+ connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
+ 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;
}
return -1;
}
-RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux)
+RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
{
- demux = this;
+ ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
+
+ if (!our_demux)
+ {
+ demux = 0;
+
+ 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;
}
RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
{
- frontend = m_frontend;
+ frontend = &m_frontend->get();
if (frontend)
return 0;
else
return -ENODEV;
}
+
+RESULT eDVBChannel::playFile(const char *file)
+{
+ ASSERT(!m_frontend);
+ if (m_pvr_thread)
+ {
+ m_pvr_thread->stop();
+ delete m_pvr_thread;
+ m_pvr_thread = 0;
+ }
+
+ m_tstools.openFile(file);
+
+ /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
+ 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);
+
+ 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, 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;
+}