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))
100 eDebug("boxtype detection via /proc/stb/info not possible... use fallback via demux count!\n");
101 if (m_demux.size() == 3)
103 else if (m_demux.size() < 5)
109 eDebug("found %d adapter, %d frontends(%d sim) and %d demux, boxtype %d",
110 m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size(), m_boxtype);
112 eDVBCAService::registerChannelCallback(this);
114 CONNECT(m_releaseCachedChannelTimer->timeout, eDVBResourceManager::releaseCachedChannel);
117 void eDVBResourceManager::feStateChanged()
120 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
122 mask |= ( 1 << i->m_frontend->getSlotID() );
123 /* emit */ frontendUseMaskChanged(mask);
126 DEFINE_REF(eDVBAdapterLinux);
127 eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
132 eDebug("scanning for frontends..");
137 #if HAVE_DVB_API_VERSION < 3
138 sprintf(filename, "/dev/dvb/card%d/frontend%d", m_nr, num_fe);
140 sprintf(filename, "/dev/dvb/adapter%d/frontend%d", m_nr, num_fe);
142 if (stat(filename, &s))
144 ePtr<eDVBFrontend> fe;
148 fe = new eDVBFrontend(m_nr, num_fe, ok);
150 m_frontend.push_back(fe);
154 fe = new eDVBFrontend(m_nr, num_fe, ok, true);
156 m_simulate_frontend.push_back(fe);
167 #if HAVE_DVB_API_VERSION < 3
168 sprintf(filename, "/dev/dvb/card%d/demux%d", m_nr, num_demux);
170 sprintf(filename, "/dev/dvb/adapter%d/demux%d", m_nr, num_demux);
172 if (stat(filename, &s))
174 ePtr<eDVBDemux> demux;
176 demux = new eDVBDemux(m_nr, num_demux);
177 m_demux.push_back(demux);
183 int eDVBAdapterLinux::getNumDemux()
185 return m_demux.size();
188 RESULT eDVBAdapterLinux::getDemux(ePtr<eDVBDemux> &demux, int nr)
190 eSmartPtrList<eDVBDemux>::iterator i(m_demux.begin());
191 while (nr && (i != m_demux.end()))
197 if (i != m_demux.end())
205 int eDVBAdapterLinux::getNumFrontends()
207 return m_frontend.size();
210 RESULT eDVBAdapterLinux::getFrontend(ePtr<eDVBFrontend> &fe, int nr, bool simulate)
212 eSmartPtrList<eDVBFrontend>::iterator i(simulate ? m_simulate_frontend.begin() : m_frontend.begin());
213 while (nr && (i != m_frontend.end()))
219 if (i != m_frontend.end())
227 int eDVBAdapterLinux::exist(int nr)
231 #if HAVE_DVB_API_VERSION < 3
232 sprintf(filename, "/dev/dvb/card%d", nr);
234 sprintf(filename, "/dev/dvb/adapter%d", nr);
236 if (!stat(filename, &s))
241 eDVBResourceManager::~eDVBResourceManager()
243 if (instance == this)
247 void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
249 int num_fe = adapter->getNumFrontends();
250 int num_demux = adapter->getNumDemux();
252 m_adapter.push_back(adapter);
255 for (i=0; i<num_demux; ++i)
257 ePtr<eDVBDemux> demux;
258 if (!adapter->getDemux(demux, i))
259 m_demux.push_back(new eDVBRegisteredDemux(demux, adapter));
262 ePtr<eDVBRegisteredFrontend> prev_dvbt_frontend;
263 for (i=0; i<num_fe; ++i)
265 ePtr<eDVBFrontend> frontend;
266 if (!adapter->getFrontend(frontend, i))
269 frontend->getFrontendType(frontendType);
270 eDVBRegisteredFrontend *new_fe = new eDVBRegisteredFrontend(frontend, adapter);
271 CONNECT(new_fe->stateChanged, eDVBResourceManager::feStateChanged);
272 m_frontend.push_back(new_fe);
273 frontend->setSEC(m_sec);
274 // we must link all dvb-t frontends ( for active antenna voltage )
275 if (frontendType == iDVBFrontend::feTerrestrial)
277 if (prev_dvbt_frontend)
279 prev_dvbt_frontend->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, (long)new_fe);
280 frontend->setData(eDVBFrontend::LINKED_PREV_PTR, (long)&(*prev_dvbt_frontend));
282 prev_dvbt_frontend = new_fe;
287 prev_dvbt_frontend = 0;
288 for (i=0; i<num_fe; ++i)
290 ePtr<eDVBFrontend> frontend;
291 if (!adapter->getFrontend(frontend, i, true))
294 frontend->getFrontendType(frontendType);
295 eDVBRegisteredFrontend *new_fe = new eDVBRegisteredFrontend(frontend, adapter);
296 // CONNECT(new_fe->stateChanged, eDVBResourceManager::feStateChanged);
297 m_simulate_frontend.push_back(new_fe);
298 frontend->setSEC(m_sec);
299 // we must link all dvb-t frontends ( for active antenna voltage )
300 if (frontendType == iDVBFrontend::feTerrestrial)
302 if (prev_dvbt_frontend)
304 prev_dvbt_frontend->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, (long)new_fe);
305 frontend->setData(eDVBFrontend::LINKED_PREV_PTR, (long)&(*prev_dvbt_frontend));
307 prev_dvbt_frontend = new_fe;
314 PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list)
316 if (!PyList_Check(list))
318 PyErr_SetString(PyExc_StandardError, "eDVBResourceManager::setFrontendSlotInformations argument should be a python list");
321 if ((unsigned int)PyList_Size(list) != m_frontend.size())
324 sprintf(blasel, "eDVBResourceManager::setFrontendSlotInformations list size incorrect %d frontends avail, but %d entries in slotlist",
325 m_frontend.size(), PyList_Size(list));
326 PyErr_SetString(PyExc_StandardError, blasel);
330 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
332 ePyObject obj = PyList_GET_ITEM(list, pos++);
333 if (!i->m_frontend->setSlotInfo(obj))
337 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_simulate_frontend.begin()); i != m_simulate_frontend.end(); ++i)
339 ePyObject obj = PyList_GET_ITEM(list, pos++);
340 if (!i->m_frontend->setSlotInfo(obj))
346 RESULT eDVBResourceManager::allocateFrontend(ePtr<eDVBAllocatedFrontend> &fe, ePtr<iDVBFrontendParameters> &feparm, bool simulate)
348 eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
349 ePtr<eDVBRegisteredFrontend> best;
353 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(frontends.begin()); i != frontends.end(); ++i)
355 int c = i->m_frontend->isCompatibleWith(feparm);
357 if (c) /* if we have at least one frontend which is compatible with the source, flag this. */
362 // eDebug("Slot %d, score %d", i->m_frontend->getSlotID(), c);
370 // eDebug("Slot %d, score %d... but BUSY!!!!!!!!!!!", i->m_frontend->getSlotID(), c);
375 fe = new eDVBAllocatedFrontend(best);
382 return errAllSourcesBusy;
384 return errNoSourceFound;
387 RESULT eDVBResourceManager::allocateFrontendByIndex(ePtr<eDVBAllocatedFrontend> &fe, int slot_index)
389 int err = errNoSourceFound;
390 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
391 if (!i->m_inuse && i->m_frontend->getSlotID() == slot_index)
393 // check if another slot linked to this is in use
395 i->m_frontend->getData(eDVBFrontend::SATPOS_DEPENDS_PTR, tmp);
398 eDVBRegisteredFrontend *satpos_depends_to_fe = (eDVBRegisteredFrontend *)tmp;
399 if (satpos_depends_to_fe->m_inuse)
401 eDebug("another satpos depending frontend is in use.. so allocateFrontendByIndex not possible!");
402 err = errAllSourcesBusy;
403 goto alloc_fe_by_id_not_possible;
406 else // check linked tuners
408 i->m_frontend->getData(eDVBFrontend::LINKED_NEXT_PTR, tmp);
411 eDVBRegisteredFrontend *next = (eDVBRegisteredFrontend *) tmp;
414 eDebug("another linked frontend is in use.. so allocateFrontendByIndex not possible!");
415 err = errAllSourcesBusy;
416 goto alloc_fe_by_id_not_possible;
418 next->m_frontend->getData(eDVBFrontend::LINKED_NEXT_PTR, tmp);
420 i->m_frontend->getData(eDVBFrontend::LINKED_PREV_PTR, tmp);
423 eDVBRegisteredFrontend *prev = (eDVBRegisteredFrontend *) tmp;
426 eDebug("another linked frontend is in use.. so allocateFrontendByIndex not possible!");
427 err = errAllSourcesBusy;
428 goto alloc_fe_by_id_not_possible;
430 prev->m_frontend->getData(eDVBFrontend::LINKED_PREV_PTR, tmp);
433 fe = new eDVBAllocatedFrontend(i);
436 alloc_fe_by_id_not_possible:
441 #define capHoldDecodeReference 64
443 RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int &cap)
445 /* find first unused demux which is on same adapter as frontend (or any, if PVR)
446 never use the first one unless we need a decoding demux. */
448 eDebug("allocate demux");
449 eSmartPtrList<eDVBRegisteredDemux>::iterator i(m_demux.begin());
453 if (i == m_demux.end())
456 ePtr<eDVBRegisteredDemux> unused;
458 if (m_boxtype == DM800 || m_boxtype == DM500HD) // dm800 / 500hd
460 cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
461 for (; i != m_demux.end(); ++i, ++n)
472 if (i->m_adapter == fe->m_adapter &&
473 i->m_demux->getSource() == fe->m_frontend->getDVBID())
475 demux = new eDVBAllocatedDemux(i);
479 else if (i->m_demux->getSource() == -1) // PVR
481 demux = new eDVBAllocatedDemux(i);
487 else if (m_boxtype == DM7025) // ATI
489 /* FIXME: hardware demux policy */
490 if (!(cap & iDVBChannel::capDecode))
492 if (m_demux.size() > 2) /* assumed to be true, otherwise we have lost anyway */
499 for (; i != m_demux.end(); ++i, ++n)
501 int is_decode = n < 2;
503 int in_use = is_decode ? (i->m_demux->getRefCount() != 2) : i->m_inuse;
505 if ((!in_use) && ((!fe) || (i->m_adapter == fe->m_adapter)))
507 if ((cap & iDVBChannel::capDecode) && !is_decode)
514 else if (m_boxtype == DM8000)
516 cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux
517 for (; i != m_demux.end(); ++i, ++n)
526 else if (i->m_adapter == fe->m_adapter &&
527 i->m_demux->getSource() == fe->m_frontend->getDVBID())
529 demux = new eDVBAllocatedDemux(i);
533 else if (n == 4) // always use demux4 for PVR (demux 4 can not descramble...)
536 demux = new eDVBAllocatedDemux(i);
546 demux = new eDVBAllocatedDemux(unused);
548 demux->get().setSourceFrontend(fe->m_frontend->getDVBID());
550 demux->get().setSourcePVR(0);
554 eDebug("demux not found");
558 RESULT eDVBResourceManager::setChannelList(iDVBChannelList *list)
564 RESULT eDVBResourceManager::getChannelList(ePtr<iDVBChannelList> &list)
573 #define eDebugNoSimulate(x...) \
580 // eDebugNoNewLine("SIMULATE:"); \
585 RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel, bool simulate)
587 /* first, check if a channel is already existing. */
588 std::list<active_channel> &active_channels = simulate ? m_active_simulate_channels : m_active_channels;
590 if (!simulate && m_cached_channel)
592 eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
593 if(channelid==cache_chan->getChannelID())
595 eDebug("use cached_channel");
596 channel = m_cached_channel;
599 m_cached_channel_state_changed_conn.disconnect();
601 m_releaseCachedChannelTimer->stop();
604 eDebugNoSimulate("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
605 for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
607 eDebugNoSimulate("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
608 if (i->m_channel_id == channelid)
610 eDebugNoSimulate("found shared channel..");
611 channel = i->m_channel;
616 /* no currently available channel is tuned to this channelid. create a new one, if possible. */
620 eDebugNoSimulate("no channel list set!");
621 return errNoChannelList;
624 ePtr<iDVBFrontendParameters> feparm;
625 if (m_list->getChannelFrontendData(channelid, feparm))
627 eDebugNoSimulate("channel not found!");
628 return errChannelNotInList;
631 /* allocate a frontend. */
633 ePtr<eDVBAllocatedFrontend> fe;
635 int err = allocateFrontend(fe, feparm, simulate);
640 ePtr<eDVBChannel> ch = new eDVBChannel(this, fe);
642 res = ch->setChannel(channelid, feparm);
646 return errChidNotFound;
653 m_cached_channel = channel = ch;
654 m_cached_channel_state_changed_conn =
655 CONNECT(ch->m_stateChanged,eDVBResourceManager::DVBChannelStateChanged);
661 void eDVBResourceManager::DVBChannelStateChanged(iDVBChannel *chan)
664 chan->getState(state);
667 case iDVBChannel::state_release:
668 case iDVBChannel::state_ok:
670 eDebug("stop release channel timer");
671 m_releaseCachedChannelTimer->stop();
674 case iDVBChannel::state_last_instance:
676 eDebug("start release channel timer");
677 m_releaseCachedChannelTimer->start(3000, true);
680 default: // ignore all other events
685 void eDVBResourceManager::releaseCachedChannel()
687 eDebug("release cached channel (timer timeout)");
691 RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel, int slot_index)
693 ePtr<eDVBAllocatedFrontend> fe;
695 if (m_cached_channel)
697 m_cached_channel_state_changed_conn.disconnect();
699 m_releaseCachedChannelTimer->stop();
702 int err = allocateFrontendByIndex(fe, slot_index);
706 channel = new eDVBChannel(this, fe);
711 RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
713 ePtr<eDVBAllocatedDemux> demux;
715 if (m_cached_channel && m_releaseCachedChannelTimer->isActive())
717 m_cached_channel_state_changed_conn.disconnect();
719 m_releaseCachedChannelTimer->stop();
722 channel = new eDVBChannel(this, 0);
726 RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch)
728 ePtr<iDVBFrontend> fe;
729 if (!ch->getFrontend(fe))
731 eDVBFrontend *frontend = (eDVBFrontend*)&(*fe);
732 if (frontend->is_simulate())
733 m_active_simulate_channels.push_back(active_channel(chid, ch));
736 m_active_channels.push_back(active_channel(chid, ch));
737 /* emit */ m_channelAdded(ch);
743 RESULT eDVBResourceManager::removeChannel(eDVBChannel *ch)
745 ePtr<iDVBFrontend> fe;
746 if (!ch->getFrontend(fe))
748 eDVBFrontend *frontend = (eDVBFrontend*)&(*fe);
749 std::list<active_channel> &active_channels = frontend->is_simulate() ? m_active_simulate_channels : m_active_channels;
751 for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end();)
753 if (i->m_channel == ch)
755 i = active_channels.erase(i);
767 RESULT eDVBResourceManager::connectChannelAdded(const Slot1<void,eDVBChannel*> &channelAdded, ePtr<eConnection> &connection)
769 connection = new eConnection((eDVBResourceManager*)this, m_channelAdded.connect(channelAdded));
773 int eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm, bool simulate)
775 eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
776 ePtr<eDVBRegisteredFrontend> best;
779 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(frontends.begin()); i != frontends.end(); ++i)
782 int c = i->m_frontend->isCompatibleWith(feparm);
789 int tuner_type_channel_default(ePtr<iDVBChannelList> &channellist, const eDVBChannelID &chid)
793 ePtr<iDVBFrontendParameters> feparm;
794 if (!channellist->getChannelFrontendData(chid, feparm))
797 if (!feparm->getSystem(system))
801 case iDVBFrontend::feSatellite:
803 case iDVBFrontend::feCable:
805 case iDVBFrontend::feTerrestrial:
816 int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore, bool simulate)
818 std::list<active_channel> &active_channels = simulate ? m_active_simulate_channels : m_active_channels;
820 if (!simulate && m_cached_channel)
822 eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
823 if(channelid==cache_chan->getChannelID())
824 return tuner_type_channel_default(m_list, channelid);
827 /* first, check if a channel is already existing. */
828 // eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
829 for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
831 // eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
832 if (i->m_channel_id == channelid)
834 // eDebug("found shared channel..");
835 return tuner_type_channel_default(m_list, channelid);
839 int *decremented_cached_channel_fe_usecount=NULL,
840 *decremented_fe_usecount=NULL;
842 for (std::list<active_channel>::iterator i(active_channels.begin()); i != active_channels.end(); ++i)
844 eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
845 // eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
846 if (i->m_channel_id == ignore)
848 eDVBChannel *channel = (eDVBChannel*) &(*i->m_channel);
849 // one eUsePtr<iDVBChannel> is used in eDVBServicePMTHandler
850 // another on eUsePtr<iDVBChannel> is used in the eDVBScan instance used in eDVBServicePMTHandler (for SDT scan)
851 // so we must check here if usecount is 3 (when the channel is equal to the cached channel)
852 // or 2 when the cached channel is not equal to the compared channel
853 if (channel == &(*m_cached_channel) ? channel->getUseCount() == 3 : channel->getUseCount() == 2) // channel only used once..
855 ePtr<iDVBFrontend> fe;
856 if (!i->m_channel->getFrontend(fe))
858 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(frontends.begin()); ii != frontends.end(); ++ii)
860 if ( &(*fe) == &(*ii->m_frontend) )
863 decremented_fe_usecount = &ii->m_inuse;
864 if (channel == &(*m_cached_channel))
865 decremented_cached_channel_fe_usecount = decremented_fe_usecount;
875 if (!decremented_cached_channel_fe_usecount)
877 if (m_cached_channel)
879 eDVBChannel *channel = (eDVBChannel*) &(*m_cached_channel);
880 if (channel->getUseCount() == 1)
882 ePtr<iDVBFrontend> fe;
883 if (!channel->getFrontend(fe))
885 eSmartPtrList<eDVBRegisteredFrontend> &frontends = simulate ? m_simulate_frontend : m_frontend;
886 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(frontends.begin()); ii != frontends.end(); ++ii)
888 if ( &(*fe) == &(*ii->m_frontend) )
891 decremented_cached_channel_fe_usecount = &ii->m_inuse;
900 decremented_cached_channel_fe_usecount=NULL;
902 ePtr<iDVBFrontendParameters> feparm;
906 eDebug("no channel list set!");
910 if (m_list->getChannelFrontendData(channelid, feparm))
912 eDebug("channel not found!");
916 ret = canAllocateFrontend(feparm, simulate);
919 if (decremented_fe_usecount)
920 ++(*decremented_fe_usecount);
921 if (decremented_cached_channel_fe_usecount)
922 ++(*decremented_cached_channel_fe_usecount);
927 bool eDVBResourceManager::canMeasureFrontendInputPower()
929 for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
931 return i->m_frontend->readInputpower() >= 0;
936 class eDVBChannelFilePush: public eFilePushThread
939 eDVBChannelFilePush() { setIFrameSearch(0); setTimebaseChange(0); }
940 void setIFrameSearch(int enabled) { m_iframe_search = enabled; m_iframe_state = 0; }
942 /* "timebase change" is for doing trickmode playback at an exact speed, even when pictures are skipped. */
943 /* you need to set it to 1/16 if you want 16x playback, for example. you need video master sync. */
944 void setTimebaseChange(int ratio) { m_timebase_change = ratio; } /* 16bit fixpoint, 0 for disable */
946 int m_iframe_search, m_iframe_state, m_pid;
947 int m_timebase_change;
948 int filterRecordData(const unsigned char *data, int len, size_t ¤t_span_remaining);
951 int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, size_t ¤t_span_remaining)
954 if (m_timebase_change)
956 eDebug("timebase change: %d", m_timebase_change);
958 for (offset = 0; offset < len; offset += 188)
960 unsigned char *pkt = (unsigned char*)_data + offset;
961 if (pkt[1] & 0x40) /* pusi */
963 if (pkt[3] & 0x20) // adaption field present?
964 pkt += pkt[4] + 4 + 1; /* skip adaption field and header */
966 pkt += 4; /* skip header */
967 if (pkt[0] || pkt[1] || (pkt[2] != 1))
969 eWarning("broken startcode");
975 if (pkt[7] & 0x80) // PTS present?
977 pts = ((unsigned long long)(pkt[ 9]&0xE)) << 29;
978 pts |= ((unsigned long long)(pkt[10]&0xFF)) << 22;
979 pts |= ((unsigned long long)(pkt[11]&0xFE)) << 14;
980 pts |= ((unsigned long long)(pkt[12]&0xFF)) << 7;
981 pts |= ((unsigned long long)(pkt[13]&0xFE)) >> 1;
985 RESULT r = m_tstools.fixupPTS(off, pts);
987 eWarning("fixup PTS while trickmode playback failed.\n");
990 int sec = pts / 90000;
991 int frm = pts % 90000;
999 // eDebug("original, fixed pts: %016llx %d:%02d:%02d:%02d:%05d", pts, d, hr, min, sec, frm);
1001 pts += 0x80000000LL;
1002 pts *= m_timebase_change;
1014 // eDebug("new pts (after timebase change): %016llx %d:%02d:%02d:%02d:%05d", pts, d, hr, min, sec, frm);
1022 pkt[9] |= (pts >> 29) & 0xE;
1023 pkt[10] |= (pts >> 22) & 0xFF;
1024 pkt[11] |= (pts >> 14) & 0xFE;
1025 pkt[12] |= (pts >> 7) & 0xFF;
1026 pkt[13] |= (pts << 1) & 0xFE;
1034 if (!m_iframe_search)
1037 unsigned char *data = (unsigned char*)_data; /* remove that const. we know what we are doing. */
1039 // eDebug("filterRecordData, size=%d (mod 188=%d), first byte is %02x", len, len %188, data[0]);
1041 unsigned char *d = data;
1042 while ((d + 3 < data + len) && (d = (unsigned char*)memmem(d, data + len - d, "\x00\x00\x01", 3)))
1044 int offset = d - data;
1045 int ts_offset = offset - offset % 188; /* offset to the start of TS packet */
1046 unsigned char *ts = data + ts_offset;
1047 int pid = ((ts[1] << 8) | ts[2]) & 0x1FFF;
1049 if ((d[3] == 0 || d[3] == 0x09 && d[-1] == 0 && (ts[1] & 0x40)) && (m_pid == pid)) /* picture start */
1051 int picture_type = (d[3]==0 ? (d[5] >> 3) & 7 : (d[4] >> 5) + 1);
1054 // eDebug("%d-frame at %d, offset in TS packet: %d, pid=%04x", picture_type, offset, offset % 188, pid);
1056 if (m_iframe_state == 1)
1058 /* we are allowing data, and stop allowing data on the next frame.
1059 we now found a frame. so stop here. */
1060 memset(data + offset, 0, 188 - (offset%188)); /* zero out rest of TS packet */
1061 current_span_remaining = 0;
1063 unsigned char *fts = ts + 188;
1064 while (fts < (data + len))
1067 fts[2] |= 0xff; /* drop packet */
1071 return len; // ts_offset + 188; /* deliver this packet, but not more. */
1074 if (picture_type != 1) /* we are only interested in I frames */
1077 unsigned char *fts = data;
1081 fts[2] |= 0xff; /* drop packet */
1088 } else if ((d[3] & 0xF0) == 0xE0) /* video stream */
1090 /* verify that this is actually a PES header, not just some ES data */
1091 if (ts[1] & 0x40) /* PUSI set */
1093 int payload_start = 4;
1094 if (ts[3] & 0x20) /* adaptation field present */
1095 payload_start += ts[4] + 1; /* skip AF */
1096 if (payload_start == (offset%188)) /* the 00 00 01 should be directly at the payload start, otherwise it's not a PES header */
1100 eDebug("now locked to pid %04x (%02x %02x %02x %02x)", pid, ts[0], ts[1], ts[2], ts[3]);
1108 d += 4; /* ignore */
1111 if (m_iframe_state == 1)
1114 return 0; /* we need find an iframe first */
1120 DEFINE_REF(eDVBChannel);
1122 eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *frontend): m_state(state_idle), m_mgr(mgr)
1124 m_frontend = frontend;
1129 m_skipmode_n = m_skipmode_m = m_skipmode_frames = 0;
1132 m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
1135 eDVBChannel::~eDVBChannel()
1138 m_mgr->removeChannel(this);
1143 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
1145 int state, ourstate = 0;
1147 /* if we are already in shutdown, don't change state. */
1148 if (m_state == state_release)
1151 if (fe->getState(state))
1154 if (state == iDVBFrontend::stateLock)
1156 eDebug("OURSTATE: ok");
1157 ourstate = state_ok;
1158 } else if (state == iDVBFrontend::stateTuning)
1160 eDebug("OURSTATE: tuning");
1161 ourstate = state_tuning;
1162 } else if (state == iDVBFrontend::stateLostLock)
1164 /* on managed channels, we try to retune in order to re-acquire lock. */
1165 if (m_current_frontend_parameters)
1167 eDebug("OURSTATE: lost lock, trying to retune");
1168 ourstate = state_tuning;
1169 m_frontend->get().tune(*m_current_frontend_parameters);
1171 /* on unmanaged channels, we don't do this. the client will do this. */
1173 eDebug("OURSTATE: lost lock, unavailable now.");
1174 ourstate = state_unavailable;
1176 } else if (state == iDVBFrontend::stateFailed)
1178 eDebug("OURSTATE: failed");
1179 ourstate = state_failed;
1181 eFatal("state unknown");
1183 if (ourstate != m_state)
1186 m_stateChanged(this);
1190 void eDVBChannel::pvrEvent(int event)
1194 case eFilePushThread::evtEOF:
1195 eDebug("eDVBChannel: End of file!");
1196 m_event(this, evtEOF);
1198 case eFilePushThread::evtUser: /* start */
1200 m_event(this, evtSOF);
1205 void eDVBChannel::cueSheetEvent(int event)
1207 /* we might end up here if playing failed or stopped, but the client hasn't (yet) noted. */
1212 case eCueSheet::evtSeek:
1214 flushPVR(m_cue->m_decoding_demux);
1216 case eCueSheet::evtSkipmode:
1219 m_cue->m_lock.WrLock();
1220 m_cue->m_seek_requests.push_back(std::pair<int, pts_t>(1, 0)); /* resync */
1221 m_cue->m_lock.Unlock();
1222 eRdLocker l(m_cue->m_lock);
1223 if (m_cue->m_skipmode_ratio)
1225 int bitrate = m_tstools.calcBitrate(); /* in bits/s */
1226 eDebug("skipmode ratio is %lld:90000, bitrate is %d bit/s", m_cue->m_skipmode_ratio, bitrate);
1227 /* i agree that this might look a bit like black magic. */
1228 m_skipmode_n = 512*1024; /* must be 1 iframe at least. */
1229 m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio / 8;
1230 m_skipmode_frames = m_cue->m_skipmode_ratio / 90000;
1231 m_skipmode_frames_remainder = 0;
1233 if (m_cue->m_skipmode_ratio < 0)
1234 m_skipmode_m -= m_skipmode_n;
1236 eDebug("resolved to: %d %d", m_skipmode_m, m_skipmode_n);
1238 if (abs(m_skipmode_m) < abs(m_skipmode_n))
1240 eWarning("something is wrong with this calculation");
1241 m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0;
1245 eDebug("skipmode ratio is 0, normal play");
1246 m_skipmode_frames = m_skipmode_n = m_skipmode_m = 0;
1249 m_pvr_thread->setIFrameSearch(m_skipmode_n != 0);
1250 if (m_cue->m_skipmode_ratio != 0)
1251 m_pvr_thread->setTimebaseChange(0x10000 * 9000 / (m_cue->m_skipmode_ratio / 10)); /* negative values are also ok */
1253 m_pvr_thread->setTimebaseChange(0); /* normal playback */
1254 eDebug("flush pvr");
1255 flushPVR(m_cue->m_decoding_demux);
1259 case eCueSheet::evtSpanChanged:
1261 m_source_span.clear();
1262 for (std::list<std::pair<pts_t, pts_t> >::const_iterator i(m_cue->m_spans.begin()); i != m_cue->m_spans.end(); ++i)
1264 off_t offset_in, offset_out;
1265 pts_t pts_in = i->first, pts_out = i->second;
1266 if (m_tstools.getOffset(offset_in, pts_in, -1) || m_tstools.getOffset(offset_out, pts_out, 1))
1268 eDebug("span translation failed.\n");
1271 eDebug("source span: %llx .. %llx, translated to %llx..%llx", pts_in, pts_out, offset_in, offset_out);
1272 m_source_span.push_back(std::pair<off_t, off_t>(offset_in, offset_out));
1279 /* align toward zero */
1280 static inline long long align(long long x, int align)
1295 /* align toward zero */
1296 static inline long long align_with_len(long long x, int align, size_t &len)
1312 /* remember, this gets called from another thread. */
1313 void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size)
1315 const int blocksize = 188;
1316 unsigned int max = align(10*1024*1024, blocksize);
1317 current_offset = align(current_offset, blocksize);
1321 eDebug("no cue sheet. forcing normal play");
1322 start = current_offset;
1327 m_cue->m_lock.RdLock();
1328 if (!m_cue->m_decoding_demux)
1330 start = current_offset;
1332 eDebug("getNextSourceSpan, no decoding demux. forcing normal play");
1333 m_cue->m_lock.Unlock();
1339 eDebug("skipmode %d:%d (x%d)", m_skipmode_m, m_skipmode_n, m_skipmode_frames);
1340 max = align(m_skipmode_n, blocksize);
1343 eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m);
1345 int frame_skip_success = 0;
1349 int frames_to_skip = m_skipmode_frames + m_skipmode_frames_remainder;
1350 eDebug("we are at %llx, and we try to skip %d+%d frames from here", current_offset, m_skipmode_frames, m_skipmode_frames_remainder);
1352 off_t iframe_start = current_offset;
1353 int frames_skipped = frames_to_skip;
1354 if (!m_tstools.findNextPicture(iframe_start, iframe_len, frames_skipped))
1356 m_skipmode_frames_remainder = frames_to_skip - frames_skipped;
1357 eDebug("successfully skipped %d (out of %d, rem now %d) frames.", frames_skipped, frames_to_skip, m_skipmode_frames_remainder);
1358 current_offset = align_with_len(iframe_start, blocksize, iframe_len);
1359 max = align(iframe_len + 187, blocksize);
1360 frame_skip_success = 1;
1363 m_skipmode_frames_remainder = 0;
1364 eDebug("frame skipping failed, reverting to byte-skipping");
1368 if (!frame_skip_success)
1370 current_offset += align(m_skipmode_m, blocksize);
1374 eDebug("we are at %llx, and we try to find the iframe here:", current_offset);
1376 off_t iframe_start = current_offset;
1378 int direction = (m_skipmode_m < 0) ? -1 : +1;
1379 if (m_tstools.findFrame(iframe_start, iframe_len, direction))
1383 current_offset = align_with_len(iframe_start, blocksize, iframe_len);
1384 max = align(iframe_len, blocksize);
1389 while (!m_cue->m_seek_requests.empty())
1391 std::pair<int, pts_t> seek = m_cue->m_seek_requests.front();
1392 m_cue->m_lock.Unlock();
1393 m_cue->m_lock.WrLock();
1394 m_cue->m_seek_requests.pop_front();
1395 m_cue->m_lock.Unlock();
1396 m_cue->m_lock.RdLock();
1397 int relative = seek.first;
1398 pts_t pts = seek.second;
1403 if (!m_cue->m_decoder)
1405 eDebug("no decoder - can't seek relative");
1408 if (m_cue->m_decoder->getPTS(0, now))
1410 eDebug("decoder getPTS failed, can't seek relative");
1413 if (getCurrentPosition(m_cue->m_decoding_demux, now, 1))
1415 eDebug("seekTo: getCurrentPosition failed!");
1418 } else if (pts < 0) /* seek relative to end */
1421 if (!getLength(len))
1423 eDebug("seeking relative to end. len=%lld, seek = %lld", len, pts);
1427 eWarning("getLength failed - can't seek relative to end!");
1432 if (relative == 1) /* pts relative */
1443 if (relative == 2) /* AP relative */
1445 eDebug("AP relative seeking: %lld, at %lld", pts, now);
1447 if (m_tstools.getNextAccessPoint(nextap, now, pts))
1449 pts = now - 90000; /* approx. 1s */
1450 eDebug("AP relative seeking failed!");
1454 eDebug("next ap is %llx\n", pts);
1459 if (m_tstools.getOffset(offset, pts, -1))
1461 eDebug("get offset for pts=%lld failed!", pts);
1465 eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
1466 current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
1469 m_cue->m_lock.Unlock();
1471 for (std::list<std::pair<off_t, off_t> >::const_iterator i(m_source_span.begin()); i != m_source_span.end(); ++i)
1473 long long aligned_start = align(i->first, blocksize);
1474 long long aligned_end = align(i->second, blocksize);
1476 if ((current_offset >= aligned_start) && (current_offset < aligned_end))
1478 start = current_offset;
1479 /* max can not exceed max(size_t). aligned_end - current_offset, however, can. */
1480 if ((aligned_end - current_offset) > max)
1483 size = aligned_end - current_offset;
1484 eDebug("HIT, %lld < %lld < %lld, size: %d", i->first, current_offset, i->second, size);
1487 if (current_offset < aligned_start)
1489 /* ok, our current offset is in an 'out' zone. */
1490 if ((m_skipmode_m >= 0) || (i == m_source_span.begin()))
1492 /* in normal playback, just start at the next zone. */
1495 /* size is not 64bit! */
1496 if ((i->second - i->first) > max)
1499 size = aligned_end - aligned_start;
1502 if (m_skipmode_m < 0)
1504 eDebug("reached SOF");
1507 m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1511 /* when skipping reverse, however, choose the zone before. */
1513 eDebug("skip to previous block, which is %llx..%llx", i->first, i->second);
1516 aligned_start = align(i->first, blocksize);
1517 aligned_end = align(i->second, blocksize);
1519 if ((aligned_end - aligned_start) > max)
1522 len = aligned_end - aligned_start;
1524 start = aligned_end - len;
1525 eDebug("skipping to %llx, %d", start, len);
1528 eDebug("result: %llx, %x (%llx %llx)", start, size, aligned_start, aligned_end);
1533 if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0))
1535 eDebug("reached SOF");
1537 m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1540 if (m_source_span.empty())
1542 start = current_offset;
1544 eDebug("NO CUESHEET. (%08llx, %d)", start, size);
1547 start = current_offset;
1553 void eDVBChannel::AddUse()
1555 if (++m_use_count > 1 && m_state == state_last_instance)
1558 m_stateChanged(this);
1562 void eDVBChannel::ReleaseUse()
1566 m_state = state_release;
1567 m_stateChanged(this);
1569 else if (m_use_count == 1)
1571 m_state = state_last_instance;
1572 m_stateChanged(this);
1576 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontendParameters> &feparm)
1579 m_mgr->removeChannel(this);
1586 eDebug("no frontend to tune!");
1590 m_channel_id = channelid;
1591 m_mgr->addChannel(channelid, this);
1592 m_state = state_tuning;
1593 /* if tuning fails, shutdown the channel immediately. */
1595 res = m_frontend->get().tune(*feparm);
1596 m_current_frontend_parameters = feparm;
1600 m_state = state_release;
1601 m_stateChanged(this);
1608 RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
1610 connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
1614 RESULT eDVBChannel::connectEvent(const Slot2<void,iDVBChannel*,int> &event, ePtr<eConnection> &connection)
1616 connection = new eConnection((iDVBChannel*)this, m_event.connect(event));
1620 RESULT eDVBChannel::getState(int &state)
1626 RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
1631 void eDVBChannel::SDTready(int result)
1633 ePyObject args = PyTuple_New(2), ret;
1637 for (std::vector<ServiceDescriptionSection*>::const_iterator i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i)
1640 PyTuple_SET_ITEM(args, 0, PyInt_FromLong((*i)->getTransportStreamId()));
1641 PyTuple_SET_ITEM(args, 1, PyInt_FromLong((*i)->getOriginalNetworkId()));
1647 PyTuple_SET_ITEM(args, 0, Py_None);
1648 PyTuple_SET_ITEM(args, 1, Py_None);
1652 ret = PyObject_CallObject(m_tsid_onid_callback, args);
1656 Py_DECREF(m_tsid_onid_callback);
1657 m_tsid_onid_callback = ePyObject();
1658 m_tsid_onid_demux = 0;
1662 RESULT eDVBChannel::requestTsidOnid(ePyObject callback)
1664 if (PyCallable_Check(callback))
1666 if (!getDemux(m_tsid_onid_demux, 0))
1668 m_SDT = new eTable<ServiceDescriptionSection>;
1669 CONNECT(m_SDT->tableReady, eDVBChannel::SDTready);
1670 if (m_SDT->start(m_tsid_onid_demux, eDVBSDTSpec()))
1672 m_tsid_onid_demux = 0;
1677 Py_INCREF(callback);
1678 m_tsid_onid_callback = callback;
1686 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
1688 ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
1694 if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
1699 /* don't hold a reference to the decoding demux, we don't need it. */
1701 /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux',
1702 the refcount is lost. thus, decoding demuxes are never allocated.
1704 this poses a big problem for PiP. */
1706 if (cap & capHoldDecodeReference) // this is set in eDVBResourceManager::allocateDemux for Dm500HD/DM800 and DM8000
1708 else if (cap & capDecode)
1717 RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
1722 frontend = &m_frontend->get();
1728 RESULT eDVBChannel::getCurrentFrontendParameters(ePtr<iDVBFrontendParameters> ¶m)
1730 param = m_current_frontend_parameters;
1734 RESULT eDVBChannel::playFile(const char *file)
1736 ASSERT(!m_frontend);
1739 m_pvr_thread->stop();
1740 delete m_pvr_thread;
1744 m_tstools.openFile(file);
1746 /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
1747 THEN DO A REAL FIX HERE! */
1749 if (m_pvr_fd_dst < 0)
1751 /* (this codepath needs to be improved anyway.) */
1752 #if HAVE_DVB_API_VERSION < 3
1753 m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
1755 m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
1757 if (m_pvr_fd_dst < 0)
1759 eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
1764 m_pvr_thread = new eDVBChannelFilePush();
1765 m_pvr_thread->enablePVRCommit(1);
1766 m_pvr_thread->setStreamMode(1);
1767 m_pvr_thread->setScatterGather(this);
1769 if (m_pvr_thread->start(file, m_pvr_fd_dst))
1771 delete m_pvr_thread;
1773 ::close(m_pvr_fd_dst);
1775 eDebug("can't open PVR file %s (%m)", file);
1778 CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent);
1781 m_stateChanged(this);
1786 void eDVBChannel::stopFile()
1790 m_pvr_thread->stop();
1791 delete m_pvr_thread;
1794 if (m_pvr_fd_dst >= 0)
1795 ::close(m_pvr_fd_dst);
1798 void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
1800 m_conn_cueSheetEvent = 0;
1803 m_cue->connectEvent(slot(*this, &eDVBChannel::cueSheetEvent), m_conn_cueSheetEvent);
1806 RESULT eDVBChannel::getLength(pts_t &len)
1808 return m_tstools.calcLen(len);
1811 RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, int mode)
1813 if (!decoding_demux)
1820 if (mode == 0) /* demux */
1822 r = decoding_demux->getSTC(now, 0);
1825 eDebug("demux getSTC failed");
1829 now = pos; /* fixup supplied */
1831 off_t off = 0; /* TODO: fixme */
1832 r = m_tstools.fixupPTS(off, now);
1835 eDebug("fixup PTS failed");
1844 void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
1846 /* when seeking, we have to ensure that all buffers are flushed.
1847 there are basically 3 buffers:
1848 a.) the filepush's internal buffer
1849 b.) the PVR buffer (before demux)
1850 c.) the ratebuffer (after demux)
1852 it's important to clear them in the correct order, otherwise
1853 the ratebuffer (for example) would immediately refill from
1854 the not-yet-flushed PVR buffer.
1857 m_pvr_thread->pause();
1858 /* flush internal filepush buffer */
1859 m_pvr_thread->flush();
1860 /* HACK: flush PVR buffer */
1861 ::ioctl(m_pvr_fd_dst, 0);
1863 /* flush ratebuffers (video, audio) */
1865 decoding_demux->flush();
1867 /* demux will also flush all decoder.. */
1868 /* resume will re-query the SG */
1869 m_pvr_thread->resume();
1872 DEFINE_REF(eCueSheet);
1874 eCueSheet::eCueSheet()
1876 m_skipmode_ratio = 0;
1879 void eCueSheet::seekTo(int relative, const pts_t &pts)
1882 m_seek_requests.push_back(std::pair<int, pts_t>(relative, pts));
1887 void eCueSheet::clear()
1894 void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
1896 ASSERT(begin < end);
1898 m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
1902 void eCueSheet::commitSpans()
1904 m_event(evtSpanChanged);
1907 void eCueSheet::setSkipmode(const pts_t &ratio)
1910 m_skipmode_ratio = ratio;
1912 m_event(evtSkipmode);
1915 void eCueSheet::setDecodingDemux(iDVBDemux *demux, iTSMPEGDecoder *decoder)
1917 m_decoding_demux = demux;
1918 m_decoder = decoder;
1921 RESULT eCueSheet::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
1923 connection = new eConnection(this, m_event.connect(event));