1 #include <lib/base/eerror.h>
2 #include <lib/base/filepush.h>
3 #include <lib/dvb/idvb.h>
4 #include <lib/dvb/dvb.h>
5 #include <lib/dvb/pmt.h>
6 #include <lib/dvb/sec.h>
7 #include <lib/dvb/specs.h>
10 #include <sys/types.h>
14 #include <sys/ioctl.h>
16 DEFINE_REF(eDVBRegisteredFrontend);
17 DEFINE_REF(eDVBRegisteredDemux);
19 DEFINE_REF(eDVBAllocatedFrontend);
21 eDVBAllocatedFrontend::eDVBAllocatedFrontend(eDVBRegisteredFrontend *fe): m_fe(fe)
26 eDVBAllocatedFrontend::~eDVBAllocatedFrontend()
31 DEFINE_REF(eDVBAllocatedDemux);
33 eDVBAllocatedDemux::eDVBAllocatedDemux(eDVBRegisteredDemux *demux): m_demux(demux)
38 eDVBAllocatedDemux::~eDVBAllocatedDemux()
43 DEFINE_REF(eDVBResourceManager);
45 eDVBResourceManager *eDVBResourceManager::instance;
47 RESULT eDVBResourceManager::getInstance(ePtr<eDVBResourceManager> &ptr)
57 ePtr<eDVBResourceManager> NewResourceManagerPtr(void)
59 ePtr<eDVBResourceManager> ptr;
60 eDVBResourceManager::getInstance(ptr);
64 eDVBResourceManager::eDVBResourceManager()
65 :m_releaseCachedChannelTimer(eTimer::create(eApp))
69 m_sec = new eDVBSatelliteEquipmentControl(m_frontend, m_simulate_frontend);
74 /* search available adapters... */
79 while (eDVBAdapterLinux::exist(num_adapter))
81 addAdapter(new eDVBAdapterLinux(num_adapter));
85 int fd = open("/proc/stb/info/model", O_RDONLY);
87 int rd = fd >= 0 ? read(fd, tmp, 255) : 0;
91 if (!strncmp(tmp, "dm7025\n", rd))
93 else if (!strncmp(tmp, "dm8000\n", rd))
95 else if (!strncmp(tmp, "dm800\n", rd))
97 else if (!strncmp(tmp, "dm500hd\n", rd))
99 else if (!strncmp(tmp, "dm800se\n", rd))
102 eDebug("boxtype detection via /proc/stb/info not possible... use fallback via demux count!\n");
103 if (m_demux.size() == 3)
105 else if (m_demux.size() < 5)
111 eDebug("found %d adapter, %d frontends(%d sim) and %d demux, boxtype %d",
112 m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size(), m_boxtype);
114 eDVBCAService::registerChannelCallback(this);
116 CONNECT(m_releaseCachedChannelTimer->timeout, eDVBResourceManager::releaseCachedChannel);
119 void eDVBResourceManager::feStateChanged()
122 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
124 mask |= ( 1 << i->m_frontend->getSlotID() );
125 /* emit */ frontendUseMaskChanged(mask);
128 DEFINE_REF(eDVBAdapterLinux);
129 eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
134 eDebug("scanning for frontends..");
139 #if HAVE_DVB_API_VERSION < 3
140 sprintf(filename, "/dev/dvb/card%d/frontend%d", m_nr, num_fe);
142 sprintf(filename, "/dev/dvb/adapter%d/frontend%d", m_nr, num_fe);
144 if (stat(filename, &s))
146 ePtr<eDVBFrontend> fe;
150 fe = new eDVBFrontend(m_nr, num_fe, ok);
152 m_frontend.push_back(fe);
156 fe = new eDVBFrontend(m_nr, num_fe, ok, true);
158 m_simulate_frontend.push_back(fe);
169 #if HAVE_DVB_API_VERSION < 3
170 sprintf(filename, "/dev/dvb/card%d/demux%d", m_nr, num_demux);
172 sprintf(filename, "/dev/dvb/adapter%d/demux%d", m_nr, num_demux);
174 if (stat(filename, &s))
176 ePtr<eDVBDemux> demux;
178 demux = new eDVBDemux(m_nr, num_demux);
179 m_demux.push_back(demux);
185 int eDVBAdapterLinux::getNumDemux()
187 return m_demux.size();
190 RESULT eDVBAdapterLinux::getDemux(ePtr<eDVBDemux> &demux, int nr)
192 eSmartPtrList<eDVBDemux>::iterator i(m_demux.begin());
193 while (nr && (i != m_demux.end()))
199 if (i != m_demux.end())
207 int eDVBAdapterLinux::getNumFrontends()
209 return m_frontend.size();
212 RESULT eDVBAdapterLinux::getFrontend(ePtr<eDVBFrontend> &fe, int nr, bool simulate)
214 eSmartPtrList<eDVBFrontend>::iterator i(simulate ? m_simulate_frontend.begin() : m_frontend.begin());
215 while (nr && (i != m_frontend.end()))
221 if (i != m_frontend.end())
229 int eDVBAdapterLinux::exist(int nr)
233 #if HAVE_DVB_API_VERSION < 3
234 sprintf(filename, "/dev/dvb/card%d", nr);
236 sprintf(filename, "/dev/dvb/adapter%d", nr);
238 if (!stat(filename, &s))
243 eDVBResourceManager::~eDVBResourceManager()
245 if (instance == this)
249 void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
251 int num_fe = adapter->getNumFrontends();
252 int num_demux = adapter->getNumDemux();
254 m_adapter.push_back(adapter);
257 for (i=0; i<num_demux; ++i)
259 ePtr<eDVBDemux> demux;
260 if (!adapter->getDemux(demux, i))
261 m_demux.push_back(new eDVBRegisteredDemux(demux, adapter));
264 ePtr<eDVBRegisteredFrontend> prev_dvbt_frontend;
265 for (i=0; i<num_fe; ++i)
267 ePtr<eDVBFrontend> frontend;
268 if (!adapter->getFrontend(frontend, i))
271 frontend->getFrontendType(frontendType);
272 eDVBRegisteredFrontend *new_fe = new eDVBRegisteredFrontend(frontend, adapter);
273 CONNECT(new_fe->stateChanged, eDVBResourceManager::feStateChanged);
274 m_frontend.push_back(new_fe);
275 frontend->setSEC(m_sec);
276 // we must link all dvb-t frontends ( for active antenna voltage )
277 if (frontendType == iDVBFrontend::feTerrestrial)
279 if (prev_dvbt_frontend)
281 prev_dvbt_frontend->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, (long)new_fe);
282 frontend->setData(eDVBFrontend::LINKED_PREV_PTR, (long)&(*prev_dvbt_frontend));
284 prev_dvbt_frontend = new_fe;
289 prev_dvbt_frontend = 0;
290 for (i=0; i<num_fe; ++i)
292 ePtr<eDVBFrontend> frontend;
293 if (!adapter->getFrontend(frontend, i, true))
296 frontend->getFrontendType(frontendType);
297 eDVBRegisteredFrontend *new_fe = new eDVBRegisteredFrontend(frontend, adapter);
298 // CONNECT(new_fe->stateChanged, eDVBResourceManager::feStateChanged);
299 m_simulate_frontend.push_back(new_fe);
300 frontend->setSEC(m_sec);
301 // we must link all dvb-t frontends ( for active antenna voltage )
302 if (frontendType == iDVBFrontend::feTerrestrial)
304 if (prev_dvbt_frontend)
306 prev_dvbt_frontend->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, (long)new_fe);
307 frontend->setData(eDVBFrontend::LINKED_PREV_PTR, (long)&(*prev_dvbt_frontend));
309 prev_dvbt_frontend = new_fe;
316 PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list)
318 if (!PyList_Check(list))
320 PyErr_SetString(PyExc_StandardError, "eDVBResourceManager::setFrontendSlotInformations argument should be a python list");
323 if ((unsigned int)PyList_Size(list) != m_frontend.size())
326 sprintf(blasel, "eDVBResourceManager::setFrontendSlotInformations list size incorrect %d frontends avail, but %d entries in slotlist",
327 m_frontend.size(), PyList_Size(list));
328 PyErr_SetString(PyExc_StandardError, blasel);
332 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
334 ePyObject obj = PyList_GET_ITEM(list, pos++);
335 if (!i->m_frontend->setSlotInfo(obj))
339 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_simulate_frontend.begin()); i != m_simulate_frontend.end(); ++i)
341 ePyObject obj = PyList_GET_ITEM(list, pos++);
342 if (!i->m_frontend->setSlotInfo(obj))
348 RESULT eDVBResourceManager::allocateFrontend(ePtr<eDVBAllocatedFrontend> &fe, ePtr<iDVBFrontendParameters> &feparm, bool simulate)
350 eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
351 ePtr<eDVBRegisteredFrontend> best;
355 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(frontends.begin()); i != frontends.end(); ++i)
357 int c = i->m_frontend->isCompatibleWith(feparm);
359 if (c) /* if we have at least one frontend which is compatible with the source, flag this. */
364 // eDebug("Slot %d, score %d", i->m_frontend->getSlotID(), c);
372 // eDebug("Slot %d, score %d... but BUSY!!!!!!!!!!!", i->m_frontend->getSlotID(), c);
377 fe = new eDVBAllocatedFrontend(best);
384 return errAllSourcesBusy;
386 return errNoSourceFound;
389 RESULT eDVBResourceManager::allocateFrontendByIndex(ePtr<eDVBAllocatedFrontend> &fe, int slot_index)
391 int err = errNoSourceFound;
392 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
393 if (!i->m_inuse && i->m_frontend->getSlotID() == slot_index)
395 // check if another slot linked to this is in use
397 i->m_frontend->getData(eDVBFrontend::SATPOS_DEPENDS_PTR, tmp);
400 eDVBRegisteredFrontend *satpos_depends_to_fe = (eDVBRegisteredFrontend *)tmp;
401 if (satpos_depends_to_fe->m_inuse)
403 eDebug("another satpos depending frontend is in use.. so allocateFrontendByIndex not possible!");
404 err = errAllSourcesBusy;
405 goto alloc_fe_by_id_not_possible;
408 else // check linked tuners
410 i->m_frontend->getData(eDVBFrontend::LINKED_NEXT_PTR, tmp);
413 eDVBRegisteredFrontend *next = (eDVBRegisteredFrontend *) tmp;
416 eDebug("another linked frontend is in use.. so allocateFrontendByIndex not possible!");
417 err = errAllSourcesBusy;
418 goto alloc_fe_by_id_not_possible;
420 next->m_frontend->getData(eDVBFrontend::LINKED_NEXT_PTR, tmp);
422 i->m_frontend->getData(eDVBFrontend::LINKED_PREV_PTR, tmp);
425 eDVBRegisteredFrontend *prev = (eDVBRegisteredFrontend *) tmp;
428 eDebug("another linked frontend is in use.. so allocateFrontendByIndex not possible!");
429 err = errAllSourcesBusy;
430 goto alloc_fe_by_id_not_possible;
432 prev->m_frontend->getData(eDVBFrontend::LINKED_PREV_PTR, tmp);
435 fe = new eDVBAllocatedFrontend(i);
438 alloc_fe_by_id_not_possible:
443 #define capHoldDecodeReference 64
445 RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int &cap)
447 /* find first unused demux which is on same adapter as frontend (or any, if PVR)
448 never use the first one unless we need a decoding demux. */
450 eDebug("allocate demux");
451 eSmartPtrList<eDVBRegisteredDemux>::iterator i(m_demux.begin());
455 if (i == m_demux.end())
458 ePtr<eDVBRegisteredDemux> unused;
460 if (m_boxtype == DM800 || m_boxtype == DM500HD || m_boxtype == DM800SE) // dm800 / 500hd
462 cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
463 for (; i != m_demux.end(); ++i, ++n)
474 if (i->m_adapter == fe->m_adapter &&
475 i->m_demux->getSource() == fe->m_frontend->getDVBID())
477 demux = new eDVBAllocatedDemux(i);
481 else if (i->m_demux->getSource() == -1) // PVR
483 demux = new eDVBAllocatedDemux(i);
489 else if (m_boxtype == DM7025) // ATI
491 /* FIXME: hardware demux policy */
492 if (!(cap & iDVBChannel::capDecode))
494 if (m_demux.size() > 2) /* assumed to be true, otherwise we have lost anyway */
501 for (; i != m_demux.end(); ++i, ++n)
503 int is_decode = n < 2;
505 int in_use = is_decode ? (i->m_demux->getRefCount() != 2) : i->m_inuse;
507 if ((!in_use) && ((!fe) || (i->m_adapter == fe->m_adapter)))
509 if ((cap & iDVBChannel::capDecode) && !is_decode)
516 else if (m_boxtype == DM8000)
518 cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
519 for (; i != m_demux.end(); ++i, ++n)
528 else if (i->m_adapter == fe->m_adapter &&
529 i->m_demux->getSource() == fe->m_frontend->getDVBID())
531 demux = new eDVBAllocatedDemux(i);
535 else if (n == 4) // always use demux4 for PVR (demux 4 can not descramble...)
538 demux = new eDVBAllocatedDemux(i);
548 demux = new eDVBAllocatedDemux(unused);
550 demux->get().setSourceFrontend(fe->m_frontend->getDVBID());
552 demux->get().setSourcePVR(0);
556 eDebug("demux not found");
560 RESULT eDVBResourceManager::setChannelList(iDVBChannelList *list)
566 RESULT eDVBResourceManager::getChannelList(ePtr<iDVBChannelList> &list)
575 #define eDebugNoSimulate(x...) \
582 // eDebugNoNewLine("SIMULATE:"); \
587 RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel, bool simulate)
589 /* first, check if a channel is already existing. */
590 std::list<active_channel> &active_channels = simulate ? m_active_simulate_channels : m_active_channels;
592 if (!simulate && m_cached_channel)
594 eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
595 if(channelid==cache_chan->getChannelID())
597 eDebug("use cached_channel");
598 channel = m_cached_channel;
601 m_cached_channel_state_changed_conn.disconnect();
603 m_releaseCachedChannelTimer->stop();
606 eDebugNoSimulate("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
607 for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
609 eDebugNoSimulate("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
610 if (i->m_channel_id == channelid)
612 eDebugNoSimulate("found shared channel..");
613 channel = i->m_channel;
618 /* no currently available channel is tuned to this channelid. create a new one, if possible. */
622 eDebugNoSimulate("no channel list set!");
623 return errNoChannelList;
626 ePtr<iDVBFrontendParameters> feparm;
627 if (m_list->getChannelFrontendData(channelid, feparm))
629 eDebugNoSimulate("channel not found!");
630 return errChannelNotInList;
633 /* allocate a frontend. */
635 ePtr<eDVBAllocatedFrontend> fe;
637 int err = allocateFrontend(fe, feparm, simulate);
642 ePtr<eDVBChannel> ch = new eDVBChannel(this, fe);
644 res = ch->setChannel(channelid, feparm);
648 return errChidNotFound;
655 m_cached_channel = channel = ch;
656 m_cached_channel_state_changed_conn =
657 CONNECT(ch->m_stateChanged,eDVBResourceManager::DVBChannelStateChanged);
663 void eDVBResourceManager::DVBChannelStateChanged(iDVBChannel *chan)
666 chan->getState(state);
669 case iDVBChannel::state_release:
670 case iDVBChannel::state_ok:
672 eDebug("stop release channel timer");
673 m_releaseCachedChannelTimer->stop();
676 case iDVBChannel::state_last_instance:
678 eDebug("start release channel timer");
679 m_releaseCachedChannelTimer->start(3000, true);
682 default: // ignore all other events
687 void eDVBResourceManager::releaseCachedChannel()
689 eDebug("release cached channel (timer timeout)");
693 RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel, int slot_index)
695 ePtr<eDVBAllocatedFrontend> fe;
697 if (m_cached_channel)
699 m_cached_channel_state_changed_conn.disconnect();
701 m_releaseCachedChannelTimer->stop();
704 int err = allocateFrontendByIndex(fe, slot_index);
708 channel = new eDVBChannel(this, fe);
713 RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
715 ePtr<eDVBAllocatedDemux> demux;
717 if (m_cached_channel && m_releaseCachedChannelTimer->isActive())
719 m_cached_channel_state_changed_conn.disconnect();
721 m_releaseCachedChannelTimer->stop();
724 channel = new eDVBChannel(this, 0);
728 RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch)
730 ePtr<iDVBFrontend> fe;
731 if (!ch->getFrontend(fe))
733 eDVBFrontend *frontend = (eDVBFrontend*)&(*fe);
734 if (frontend->is_simulate())
735 m_active_simulate_channels.push_back(active_channel(chid, ch));
738 m_active_channels.push_back(active_channel(chid, ch));
739 /* emit */ m_channelAdded(ch);
745 RESULT eDVBResourceManager::removeChannel(eDVBChannel *ch)
747 ePtr<iDVBFrontend> fe;
748 if (!ch->getFrontend(fe))
750 eDVBFrontend *frontend = (eDVBFrontend*)&(*fe);
751 std::list<active_channel> &active_channels = frontend->is_simulate() ? m_active_simulate_channels : m_active_channels;
753 for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end();)
755 if (i->m_channel == ch)
757 i = active_channels.erase(i);
769 RESULT eDVBResourceManager::connectChannelAdded(const Slot1<void,eDVBChannel*> &channelAdded, ePtr<eConnection> &connection)
771 connection = new eConnection((eDVBResourceManager*)this, m_channelAdded.connect(channelAdded));
775 int eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm, bool simulate)
777 eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
778 ePtr<eDVBRegisteredFrontend> best;
781 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(frontends.begin()); i != frontends.end(); ++i)
784 int c = i->m_frontend->isCompatibleWith(feparm);
791 int tuner_type_channel_default(ePtr<iDVBChannelList> &channellist, const eDVBChannelID &chid)
795 ePtr<iDVBFrontendParameters> feparm;
796 if (!channellist->getChannelFrontendData(chid, feparm))
799 if (!feparm->getSystem(system))
803 case iDVBFrontend::feSatellite:
805 case iDVBFrontend::feCable:
807 case iDVBFrontend::feTerrestrial:
818 int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore, bool simulate)
820 std::list<active_channel> &active_channels = simulate ? m_active_simulate_channels : m_active_channels;
822 if (!simulate && m_cached_channel)
824 eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
825 if(channelid==cache_chan->getChannelID())
826 return tuner_type_channel_default(m_list, channelid);
829 /* first, check if a channel is already existing. */
830 // eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
831 for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
833 // eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
834 if (i->m_channel_id == channelid)
836 // eDebug("found shared channel..");
837 return tuner_type_channel_default(m_list, channelid);
841 int *decremented_cached_channel_fe_usecount=NULL,
842 *decremented_fe_usecount=NULL;
844 for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
846 eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
847 // eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
848 if (i->m_channel_id == ignore)
850 eDVBChannel *channel = (eDVBChannel*) &(*i->m_channel);
851 // one eUsePtr<iDVBChannel> is used in eDVBServicePMTHandler
852 // another on eUsePtr<iDVBChannel> is used in the eDVBScan instance used in eDVBServicePMTHandler (for SDT scan)
853 // so we must check here if usecount is 3 (when the channel is equal to the cached channel)
854 // or 2 when the cached channel is not equal to the compared channel
855 if (channel == &(*m_cached_channel) ? channel->getUseCount() == 3 : channel->getUseCount() == 2) // channel only used once..
857 ePtr<iDVBFrontend> fe;
858 if (!i->m_channel->getFrontend(fe))
860 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(frontends.begin()); ii != frontends.end(); ++ii)
862 if ( &(*fe) == &(*ii->m_frontend) )
865 decremented_fe_usecount = &ii->m_inuse;
866 if (channel == &(*m_cached_channel))
867 decremented_cached_channel_fe_usecount = decremented_fe_usecount;
877 if (!decremented_cached_channel_fe_usecount)
879 if (m_cached_channel)
881 eDVBChannel *channel = (eDVBChannel*) &(*m_cached_channel);
882 if (channel->getUseCount() == 1)
884 ePtr<iDVBFrontend> fe;
885 if (!channel->getFrontend(fe))
887 eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
888 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(frontends.begin()); ii != frontends.end(); ++ii)
890 if ( &(*fe) == &(*ii->m_frontend) )
893 decremented_cached_channel_fe_usecount = &ii->m_inuse;
902 decremented_cached_channel_fe_usecount=NULL;
904 ePtr<iDVBFrontendParameters> feparm;
908 eDebug("no channel list set!");
912 if (m_list->getChannelFrontendData(channelid, feparm))
914 eDebug("channel not found!");
918 ret = canAllocateFrontend(feparm, simulate);
921 if (decremented_fe_usecount)
922 ++(*decremented_fe_usecount);
923 if (decremented_cached_channel_fe_usecount)
924 ++(*decremented_cached_channel_fe_usecount);
929 bool eDVBResourceManager::canMeasureFrontendInputPower()
931 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
933 return i->m_frontend->readInputpower() >= 0;
938 class eDVBChannelFilePush: public eFilePushThread
941 eDVBChannelFilePush() { setIFrameSearch(0); setTimebaseChange(0); }
942 void setIFrameSearch(int enabled) { m_iframe_search = enabled; m_iframe_state = 0; }
944 /* "timebase change" is for doing trickmode playback at an exact speed, even when pictures are skipped. */
945 /* you need to set it to 1/16 if you want 16x playback, for example. you need video master sync. */
946 void setTimebaseChange(int ratio) { m_timebase_change = ratio; } /* 16bit fixpoint, 0 for disable */
948 int m_iframe_search, m_iframe_state, m_pid;
949 int m_timebase_change;
950 int filterRecordData(const unsigned char *data, int len, size_t ¤t_span_remaining);
953 int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, size_t ¤t_span_remaining)
956 if (m_timebase_change)
958 eDebug("timebase change: %d", m_timebase_change);
960 for (offset = 0; offset < len; offset += 188)
962 unsigned char *pkt = (unsigned char*)_data + offset;
963 if (pkt[1] & 0x40) /* pusi */
965 if (pkt[3] & 0x20) // adaption field present?
966 pkt += pkt[4] + 4 + 1; /* skip adaption field and header */
968 pkt += 4; /* skip header */
969 if (pkt[0] || pkt[1] || (pkt[2] != 1))
971 eWarning("broken startcode");
977 if (pkt[7] & 0x80) // PTS present?
979 pts = ((unsigned long long)(pkt[ 9]&0xE)) << 29;
980 pts |= ((unsigned long long)(pkt[10]&0xFF)) << 22;
981 pts |= ((unsigned long long)(pkt[11]&0xFE)) << 14;
982 pts |= ((unsigned long long)(pkt[12]&0xFF)) << 7;
983 pts |= ((unsigned long long)(pkt[13]&0xFE)) >> 1;
987 RESULT r = m_tstools.fixupPTS(off, pts);
989 eWarning("fixup PTS while trickmode playback failed.\n");
992 int sec = pts / 90000;
993 int frm = pts % 90000;
1001 // eDebug("original, fixed pts: %016llx %d:%02d:%02d:%02d:%05d", pts, d, hr, min, sec, frm);
1003 pts += 0x80000000LL;
1004 pts *= m_timebase_change;
1016 // eDebug("new pts (after timebase change): %016llx %d:%02d:%02d:%02d:%05d", pts, d, hr, min, sec, frm);
1024 pkt[9] |= (pts >> 29) & 0xE;
1025 pkt[10] |= (pts >> 22) & 0xFF;
1026 pkt[11] |= (pts >> 14) & 0xFE;
1027 pkt[12] |= (pts >> 7) & 0xFF;
1028 pkt[13] |= (pts << 1) & 0xFE;
1036 if (!m_iframe_search)
1039 unsigned char *data = (unsigned char*)_data; /* remove that const. we know what we are doing. */
1041 // eDebug("filterRecordData, size=%d (mod 188=%d), first byte is %02x", len, len %188, data[0]);
1043 unsigned char *d = data;
1044 while ((d + 3 < data + len) && (d = (unsigned char*)memmem(d, data + len - d, "\x00\x00\x01", 3)))
1046 int offset = d - data;
1047 int ts_offset = offset - offset % 188; /* offset to the start of TS packet */
1048 unsigned char *ts = data + ts_offset;
1049 int pid = ((ts[1] << 8) | ts[2]) & 0x1FFF;
1051 if ((d[3] == 0 || d[3] == 0x09 && d[-1] == 0 && (ts[1] & 0x40)) && (m_pid == pid)) /* picture start */
1053 int picture_type = (d[3]==0 ? (d[5] >> 3) & 7 : (d[4] >> 5) + 1);
1056 // eDebug("%d-frame at %d, offset in TS packet: %d, pid=%04x", picture_type, offset, offset % 188, pid);
1058 if (m_iframe_state == 1)
1060 /* we are allowing data, and stop allowing data on the next frame.
1061 we now found a frame. so stop here. */
1062 memset(data + offset, 0, 188 - (offset%188)); /* zero out rest of TS packet */
1063 current_span_remaining = 0;
1065 unsigned char *fts = ts + 188;
1066 while (fts < (data + len))
1069 fts[2] |= 0xff; /* drop packet */
1073 return len; // ts_offset + 188; /* deliver this packet, but not more. */
1076 if (picture_type != 1) /* we are only interested in I frames */
1079 unsigned char *fts = data;
1083 fts[2] |= 0xff; /* drop packet */
1090 } else if ((d[3] & 0xF0) == 0xE0) /* video stream */
1092 /* verify that this is actually a PES header, not just some ES data */
1093 if (ts[1] & 0x40) /* PUSI set */
1095 int payload_start = 4;
1096 if (ts[3] & 0x20) /* adaptation field present */
1097 payload_start += ts[4] + 1; /* skip AF */
1098 if (payload_start == (offset%188)) /* the 00 00 01 should be directly at the payload start, otherwise it's not a PES header */
1102 eDebug("now locked to pid %04x (%02x %02x %02x %02x)", pid, ts[0], ts[1], ts[2], ts[3]);
1110 d += 4; /* ignore */
1113 if (m_iframe_state == 1)
1116 return 0; /* we need find an iframe first */
1122 DEFINE_REF(eDVBChannel);
1124 eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *frontend): m_state(state_idle), m_mgr(mgr)
1126 m_frontend = frontend;
1131 m_skipmode_n = m_skipmode_m = m_skipmode_frames = 0;
1134 m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
1137 eDVBChannel::~eDVBChannel()
1140 m_mgr->removeChannel(this);
1145 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
1147 int state, ourstate = 0;
1149 /* if we are already in shutdown, don't change state. */
1150 if (m_state == state_release)
1153 if (fe->getState(state))
1156 if (state == iDVBFrontend::stateLock)
1158 eDebug("OURSTATE: ok");
1159 ourstate = state_ok;
1160 } else if (state == iDVBFrontend::stateTuning)
1162 eDebug("OURSTATE: tuning");
1163 ourstate = state_tuning;
1164 } else if (state == iDVBFrontend::stateLostLock)
1166 /* on managed channels, we try to retune in order to re-acquire lock. */
1167 if (m_current_frontend_parameters)
1169 eDebug("OURSTATE: lost lock, trying to retune");
1170 ourstate = state_tuning;
1171 m_frontend->get().tune(*m_current_frontend_parameters);
1173 /* on unmanaged channels, we don't do this. the client will do this. */
1175 eDebug("OURSTATE: lost lock, unavailable now.");
1176 ourstate = state_unavailable;
1178 } else if (state == iDVBFrontend::stateFailed)
1180 #ifdef BUILD_VUPLUS /* ikseong */
1181 if (m_current_frontend_parameters)
1183 eDebug("OURSTATE: lost lock, trying to retune");
1184 ourstate = state_tuning;
1185 m_frontend->get().tune(*m_current_frontend_parameters);
1189 eDebug("OURSTATE: failed");
1190 ourstate = state_failed;
1193 eDebug("OURSTATE: failed");
1194 ourstate = state_failed;
1197 eFatal("state unknown");
1199 if (ourstate != m_state)
1202 m_stateChanged(this);
1206 void eDVBChannel::pvrEvent(int event)
1210 case eFilePushThread::evtEOF:
1211 eDebug("eDVBChannel: End of file!");
1212 m_event(this, evtEOF);
1214 case eFilePushThread::evtUser: /* start */
1216 m_event(this, evtSOF);
1221 void eDVBChannel::cueSheetEvent(int event)
1223 /* we might end up here if playing failed or stopped, but the client hasn't (yet) noted. */
1228 case eCueSheet::evtSeek:
1230 flushPVR(m_cue->m_decoding_demux);
1232 case eCueSheet::evtSkipmode:
1235 m_cue->m_lock.WrLock();
1236 m_cue->m_seek_requests.push_back(std::pair<int, pts_t>(1, 0)); /* resync */
1237 m_cue->m_lock.Unlock();
1238 eRdLocker l(m_cue->m_lock);
1239 if (m_cue->m_skipmode_ratio)
1241 int bitrate = m_tstools.calcBitrate(); /* in bits/s */
1242 eDebug("skipmode ratio is %lld:90000, bitrate is %d bit/s", m_cue->m_skipmode_ratio, bitrate);
1243 /* i agree that this might look a bit like black magic. */
1244 m_skipmode_n = 512*1024; /* must be 1 iframe at least. */
1245 m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio / 8;
1246 m_skipmode_frames = m_cue->m_skipmode_ratio / 90000;
1247 m_skipmode_frames_remainder = 0;
1249 if (m_cue->m_skipmode_ratio < 0)
1250 m_skipmode_m -= m_skipmode_n;
1252 eDebug("resolved to: %d %d", m_skipmode_m, m_skipmode_n);
1254 if (abs(m_skipmode_m) < abs(m_skipmode_n))
1256 eWarning("something is wrong with this calculation");
1257 m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0;
1261 eDebug("skipmode ratio is 0, normal play");
1262 m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0;
1265 m_pvr_thread->setIFrameSearch(m_skipmode_n != 0);
1266 if (m_cue->m_skipmode_ratio != 0)
1267 m_pvr_thread->setTimebaseChange(0x10000 * 9000 / (m_cue->m_skipmode_ratio / 10)); /* negative values are also ok */
1269 m_pvr_thread->setTimebaseChange(0); /* normal playback */
1270 eDebug("flush pvr");
1271 flushPVR(m_cue->m_decoding_demux);
1275 case eCueSheet::evtSpanChanged:
1277 m_source_span.clear();
1278 for (std::list<std::pair<pts_t, pts_t> >::const_iterator i(m_cue->m_spans.begin()); i != m_cue->m_spans.end(); ++i)
1280 off_t offset_in, offset_out;
1281 pts_t pts_in = i->first, pts_out = i->second;
1282 if (m_tstools.getOffset(offset_in, pts_in, -1) || m_tstools.getOffset(offset_out, pts_out, 1))
1284 eDebug("span translation failed.\n");
1287 eDebug("source span: %llx .. %llx, translated to %llx..%llx", pts_in, pts_out, offset_in, offset_out);
1288 m_source_span.push_back(std::pair<off_t, off_t>(offset_in, offset_out));
1295 /* align toward zero */
1296 static inline long long align(long long x, int align)
1311 /* align toward zero */
1312 static inline long long align_with_len(long long x, int align, size_t &len)
1328 /* remember, this gets called from another thread. */
1329 void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size)
1331 const int blocksize = 188;
1332 unsigned int max = align(10*1024*1024, blocksize);
1333 current_offset = align(current_offset, blocksize);
1337 eDebug("no cue sheet. forcing normal play");
1338 start = current_offset;
1345 eDebug("skipmode %d:%d (x%d)", m_skipmode_m, m_skipmode_n, m_skipmode_frames);
1346 max = align(m_skipmode_n, blocksize);
1349 eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m);
1350 int frame_skip_success = 0;
1354 int frames_to_skip = m_skipmode_frames + m_skipmode_frames_remainder;
1355 eDebug("we are at %llx, and we try to skip %d+%d frames from here", current_offset, m_skipmode_frames, m_skipmode_frames_remainder);
1357 off_t iframe_start = current_offset;
1358 int frames_skipped = frames_to_skip;
1359 if (!m_tstools.findNextPicture(iframe_start, iframe_len, frames_skipped))
1361 m_skipmode_frames_remainder = frames_to_skip - frames_skipped;
1362 eDebug("successfully skipped %d (out of %d, rem now %d) frames.", frames_skipped, frames_to_skip, m_skipmode_frames_remainder);
1363 current_offset = align_with_len(iframe_start, blocksize, iframe_len);
1364 max = align(iframe_len + 187, blocksize);
1365 frame_skip_success = 1;
1368 m_skipmode_frames_remainder = 0;
1369 eDebug("frame skipping failed, reverting to byte-skipping");
1373 if (!frame_skip_success)
1375 current_offset += align(m_skipmode_m, blocksize);
1379 eDebug("we are at %llx, and we try to find the iframe here:", current_offset);
1381 off_t iframe_start = current_offset;
1383 int direction = (m_skipmode_m < 0) ? -1 : +1;
1384 if (m_tstools.findFrame(iframe_start, iframe_len, direction))
1388 current_offset = align_with_len(iframe_start, blocksize, iframe_len);
1389 max = align(iframe_len, blocksize);
1394 m_cue->m_lock.RdLock();
1396 while (!m_cue->m_seek_requests.empty())
1398 std::pair<int, pts_t> seek = m_cue->m_seek_requests.front();
1399 m_cue->m_lock.Unlock();
1400 m_cue->m_lock.WrLock();
1401 m_cue->m_seek_requests.pop_front();
1402 m_cue->m_lock.Unlock();
1403 m_cue->m_lock.RdLock();
1404 int relative = seek.first;
1405 pts_t pts = seek.second;
1410 if (!m_cue->m_decoder)
1412 eDebug("no decoder - can't seek relative");
1415 if (m_cue->m_decoder->getPTS(0, now))
1417 eDebug("decoder getPTS failed, can't seek relative");
1420 if (!m_cue->m_decoding_demux)
1422 eDebug("getNextSourceSpan, no decoding demux. couldn't seek to %llx... ignore request!", pts);
1423 start = current_offset;
1427 if (getCurrentPosition(m_cue->m_decoding_demux, now, 1))
1429 eDebug("seekTo: getCurrentPosition failed!");
1432 } else if (pts < 0) /* seek relative to end */
1435 if (!getLength(len))
1437 eDebug("seeking relative to end. len=%lld, seek = %lld", len, pts);
1441 eWarning("getLength failed - can't seek relative to end!");
1446 if (relative == 1) /* pts relative */
1457 if (relative == 2) /* AP relative */
1459 eDebug("AP relative seeking: %lld, at %lld", pts, now);
1461 if (m_tstools.getNextAccessPoint(nextap, now, pts))
1463 pts = now - 90000; /* approx. 1s */
1464 eDebug("AP relative seeking failed!");
1468 eDebug("next ap is %llx\n", pts);
1473 if (m_tstools.getOffset(offset, pts, -1))
1475 eDebug("get offset for pts=%lld failed!", pts);
1479 eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
1480 current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
1483 m_cue->m_lock.Unlock();
1485 for (std::list<std::pair<off_t, off_t> >::const_iterator i(m_source_span.begin()); i != m_source_span.end(); ++i)
1487 long long aligned_start = align(i->first, blocksize);
1488 long long aligned_end = align(i->second, blocksize);
1490 if ((current_offset >= aligned_start) && (current_offset < aligned_end))
1492 start = current_offset;
1493 /* max can not exceed max(size_t). aligned_end - current_offset, however, can. */
1494 if ((aligned_end - current_offset) > max)
1497 size = aligned_end - current_offset;
1498 eDebug("HIT, %lld < %lld < %lld, size: %d", i->first, current_offset, i->second, size);
1501 if (current_offset < aligned_start)
1503 /* ok, our current offset is in an 'out' zone. */
1504 if ((m_skipmode_m >= 0) || (i == m_source_span.begin()))
1506 /* in normal playback, just start at the next zone. */
1509 /* size is not 64bit! */
1510 if ((i->second - i->first) > max)
1513 size = aligned_end - aligned_start;
1516 if (m_skipmode_m < 0)
1518 eDebug("reached SOF");
1521 m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1525 /* when skipping reverse, however, choose the zone before. */
1527 eDebug("skip to previous block, which is %llx..%llx", i->first, i->second);
1530 aligned_start = align(i->first, blocksize);
1531 aligned_end = align(i->second, blocksize);
1533 if ((aligned_end - aligned_start) > max)
1536 len = aligned_end - aligned_start;
1538 start = aligned_end - len;
1539 eDebug("skipping to %llx, %d", start, len);
1542 eDebug("result: %llx, %x (%llx %llx)", start, size, aligned_start, aligned_end);
1547 if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0))
1549 eDebug("reached SOF");
1551 m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1554 if (m_source_span.empty())
1556 start = current_offset;
1558 eDebug("NO CUESHEET. (%08llx, %d)", start, size);
1561 start = current_offset;
1567 void eDVBChannel::AddUse()
1569 if (++m_use_count > 1 && m_state == state_last_instance)
1572 m_stateChanged(this);
1576 void eDVBChannel::ReleaseUse()
1580 m_state = state_release;
1581 m_stateChanged(this);
1583 else if (m_use_count == 1)
1585 m_state = state_last_instance;
1586 m_stateChanged(this);
1590 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontendParameters> &feparm)
1593 m_mgr->removeChannel(this);
1600 eDebug("no frontend to tune!");
1604 m_channel_id = channelid;
1605 m_mgr->addChannel(channelid, this);
1606 m_state = state_tuning;
1607 /* if tuning fails, shutdown the channel immediately. */
1609 res = m_frontend->get().tune(*feparm);
1610 m_current_frontend_parameters = feparm;
1614 m_state = state_release;
1615 m_stateChanged(this);
1622 RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
1624 connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
1628 RESULT eDVBChannel::connectEvent(const Slot2<void,iDVBChannel*,int> &event, ePtr<eConnection> &connection)
1630 connection = new eConnection((iDVBChannel*)this, m_event.connect(event));
1634 RESULT eDVBChannel::getState(int &state)
1640 RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
1645 void eDVBChannel::SDTready(int result)
1647 ePyObject args = PyTuple_New(2), ret;
1651 for (std::vector<ServiceDescriptionSection*>::const_iterator i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i)
1654 PyTuple_SET_ITEM(args, 0, PyInt_FromLong((*i)->getTransportStreamId()));
1655 PyTuple_SET_ITEM(args, 1, PyInt_FromLong((*i)->getOriginalNetworkId()));
1661 PyTuple_SET_ITEM(args, 0, Py_None);
1662 PyTuple_SET_ITEM(args, 1, Py_None);
1666 ret = PyObject_CallObject(m_tsid_onid_callback, args);
1670 Py_DECREF(m_tsid_onid_callback);
1671 m_tsid_onid_callback = ePyObject();
1672 m_tsid_onid_demux = 0;
1676 int eDVBChannel::reserveDemux()
1678 ePtr<iDVBDemux> dmx;
1679 if (!getDemux(dmx, 0))
1682 if (!dmx->getCADemuxID(id))
1688 RESULT eDVBChannel::requestTsidOnid(ePyObject callback)
1690 if (PyCallable_Check(callback))
1692 if (!getDemux(m_tsid_onid_demux, 0))
1694 m_SDT = new eTable<ServiceDescriptionSection>;
1695 CONNECT(m_SDT->tableReady, eDVBChannel::SDTready);
1696 if (m_SDT->start(m_tsid_onid_demux, eDVBSDTSpec()))
1698 m_tsid_onid_demux = 0;
1703 Py_INCREF(callback);
1704 m_tsid_onid_callback = callback;
1712 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
1714 ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
1720 if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
1725 /* don't hold a reference to the decoding demux, we don't need it. */
1727 /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux',
1728 the refcount is lost. thus, decoding demuxes are never allocated.
1730 this poses a big problem for PiP. */
1732 if (cap & capHoldDecodeReference) // this is set in eDVBResourceManager::allocateDemux for Dm500HD/DM800 and DM8000
1734 else if (cap & capDecode)
1743 RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
1748 frontend = &m_frontend->get();
1754 RESULT eDVBChannel::getCurrentFrontendParameters(ePtr<iDVBFrontendParameters> ¶m)
1756 param = m_current_frontend_parameters;
1760 RESULT eDVBChannel::playFile(const char *file)
1762 ASSERT(!m_frontend);
1765 m_pvr_thread->stop();
1766 delete m_pvr_thread;
1770 m_tstools.openFile(file);
1772 /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
1773 THEN DO A REAL FIX HERE! */
1775 if (m_pvr_fd_dst < 0)
1777 /* (this codepath needs to be improved anyway.) */
1778 #if HAVE_DVB_API_VERSION < 3
1779 m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
1781 m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
1783 if (m_pvr_fd_dst < 0)
1785 eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
1790 m_pvr_thread = new eDVBChannelFilePush();
1791 m_pvr_thread->enablePVRCommit(1);
1792 m_pvr_thread->setStreamMode(1);
1793 m_pvr_thread->setScatterGather(this);
1795 m_event(this, evtPreStart);
1797 if (m_pvr_thread->start(file, m_pvr_fd_dst))
1799 delete m_pvr_thread;
1801 ::close(m_pvr_fd_dst);
1803 eDebug("can't open PVR file %s (%m)", file);
1806 CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent);
1809 m_stateChanged(this);
1814 void eDVBChannel::stopFile()
1818 m_pvr_thread->stop();
1819 delete m_pvr_thread;
1822 if (m_pvr_fd_dst >= 0)
1823 ::close(m_pvr_fd_dst);
1826 void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
1828 m_conn_cueSheetEvent = 0;
1831 m_cue->connectEvent(slot(*this, &eDVBChannel::cueSheetEvent), m_conn_cueSheetEvent);
1834 RESULT eDVBChannel::getLength(pts_t &len)
1836 return m_tstools.calcLen(len);
1839 RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, int mode)
1841 if (!decoding_demux)
1848 if (mode == 0) /* demux */
1850 r = decoding_demux->getSTC(now, 0);
1853 eDebug("demux getSTC failed");
1857 now = pos; /* fixup supplied */
1859 off_t off = 0; /* TODO: fixme */
1860 r = m_tstools.fixupPTS(off, now);
1863 eDebug("fixup PTS failed");
1872 void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
1874 /* when seeking, we have to ensure that all buffers are flushed.
1875 there are basically 3 buffers:
1876 a.) the filepush's internal buffer
1877 b.) the PVR buffer (before demux)
1878 c.) the ratebuffer (after demux)
1880 it's important to clear them in the correct order, otherwise
1881 the ratebuffer (for example) would immediately refill from
1882 the not-yet-flushed PVR buffer.
1885 m_pvr_thread->pause();
1886 /* flush internal filepush buffer */
1887 m_pvr_thread->flush();
1888 /* HACK: flush PVR buffer */
1889 ::ioctl(m_pvr_fd_dst, 0);
1891 /* flush ratebuffers (video, audio) */
1893 decoding_demux->flush();
1895 /* demux will also flush all decoder.. */
1896 /* resume will re-query the SG */
1897 m_pvr_thread->resume();
1900 DEFINE_REF(eCueSheet);
1902 eCueSheet::eCueSheet()
1904 m_skipmode_ratio = 0;
1907 void eCueSheet::seekTo(int relative, const pts_t &pts)
1910 m_seek_requests.push_back(std::pair<int, pts_t>(relative, pts));
1915 void eCueSheet::clear()
1922 void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
1924 ASSERT(begin < end);
1926 m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
1930 void eCueSheet::commitSpans()
1932 m_event(evtSpanChanged);
1935 void eCueSheet::setSkipmode(const pts_t &ratio)
1938 m_skipmode_ratio = ratio;
1940 m_event(evtSkipmode);
1943 void eCueSheet::setDecodingDemux(iDVBDemux *demux, iTSMPEGDecoder *decoder)
1945 m_decoding_demux = demux;
1946 m_decoder = decoder;
1949 RESULT eCueSheet::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
1951 connection = new eConnection(this, m_event.connect(event));