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;
1329 eDebug("skipmode %d:%d (x%d)", m_skipmode_m, m_skipmode_n, m_skipmode_frames);
1330 max = align(m_skipmode_n, blocksize);
1333 eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m);
1334 int frame_skip_success = 0;
1338 int frames_to_skip = m_skipmode_frames + m_skipmode_frames_remainder;
1339 eDebug("we are at %llx, and we try to skip %d+%d frames from here", current_offset, m_skipmode_frames, m_skipmode_frames_remainder);
1341 off_t iframe_start = current_offset;
1342 int frames_skipped = frames_to_skip;
1343 if (!m_tstools.findNextPicture(iframe_start, iframe_len, frames_skipped))
1345 m_skipmode_frames_remainder = frames_to_skip - frames_skipped;
1346 eDebug("successfully skipped %d (out of %d, rem now %d) frames.", frames_skipped, frames_to_skip, m_skipmode_frames_remainder);
1347 current_offset = align_with_len(iframe_start, blocksize, iframe_len);
1348 max = align(iframe_len + 187, blocksize);
1349 frame_skip_success = 1;
1352 m_skipmode_frames_remainder = 0;
1353 eDebug("frame skipping failed, reverting to byte-skipping");
1357 if (!frame_skip_success)
1359 current_offset += align(m_skipmode_m, blocksize);
1363 eDebug("we are at %llx, and we try to find the iframe here:", current_offset);
1365 off_t iframe_start = current_offset;
1367 int direction = (m_skipmode_m < 0) ? -1 : +1;
1368 if (m_tstools.findFrame(iframe_start, iframe_len, direction))
1372 current_offset = align_with_len(iframe_start, blocksize, iframe_len);
1373 max = align(iframe_len, blocksize);
1378 m_cue->m_lock.RdLock();
1380 while (!m_cue->m_seek_requests.empty())
1382 std::pair<int, pts_t> seek = m_cue->m_seek_requests.front();
1383 m_cue->m_lock.Unlock();
1384 m_cue->m_lock.WrLock();
1385 m_cue->m_seek_requests.pop_front();
1386 m_cue->m_lock.Unlock();
1387 m_cue->m_lock.RdLock();
1388 int relative = seek.first;
1389 pts_t pts = seek.second;
1394 if (!m_cue->m_decoder)
1396 eDebug("no decoder - can't seek relative");
1399 if (m_cue->m_decoder->getPTS(0, now))
1401 eDebug("decoder getPTS failed, can't seek relative");
1404 if (!m_cue->m_decoding_demux)
1406 eDebug("getNextSourceSpan, no decoding demux. couldn't seek to %llx... ignore request!", pts);
1407 start = current_offset;
1411 if (getCurrentPosition(m_cue->m_decoding_demux, now, 1))
1413 eDebug("seekTo: getCurrentPosition failed!");
1416 } else if (pts < 0) /* seek relative to end */
1419 if (!getLength(len))
1421 eDebug("seeking relative to end. len=%lld, seek = %lld", len, pts);
1425 eWarning("getLength failed - can't seek relative to end!");
1430 if (relative == 1) /* pts relative */
1441 if (relative == 2) /* AP relative */
1443 eDebug("AP relative seeking: %lld, at %lld", pts, now);
1445 if (m_tstools.getNextAccessPoint(nextap, now, pts))
1447 pts = now - 90000; /* approx. 1s */
1448 eDebug("AP relative seeking failed!");
1452 eDebug("next ap is %llx\n", pts);
1457 if (m_tstools.getOffset(offset, pts, -1))
1459 eDebug("get offset for pts=%lld failed!", pts);
1463 eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
1464 current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
1467 m_cue->m_lock.Unlock();
1469 for (std::list<std::pair<off_t, off_t> >::const_iterator i(m_source_span.begin()); i != m_source_span.end(); ++i)
1471 long long aligned_start = align(i->first, blocksize);
1472 long long aligned_end = align(i->second, blocksize);
1474 if ((current_offset >= aligned_start) && (current_offset < aligned_end))
1476 start = current_offset;
1477 /* max can not exceed max(size_t). aligned_end - current_offset, however, can. */
1478 if ((aligned_end - current_offset) > max)
1481 size = aligned_end - current_offset;
1482 eDebug("HIT, %lld < %lld < %lld, size: %d", i->first, current_offset, i->second, size);
1485 if (current_offset < aligned_start)
1487 /* ok, our current offset is in an 'out' zone. */
1488 if ((m_skipmode_m >= 0) || (i == m_source_span.begin()))
1490 /* in normal playback, just start at the next zone. */
1493 /* size is not 64bit! */
1494 if ((i->second - i->first) > max)
1497 size = aligned_end - aligned_start;
1500 if (m_skipmode_m < 0)
1502 eDebug("reached SOF");
1505 m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1509 /* when skipping reverse, however, choose the zone before. */
1511 eDebug("skip to previous block, which is %llx..%llx", i->first, i->second);
1514 aligned_start = align(i->first, blocksize);
1515 aligned_end = align(i->second, blocksize);
1517 if ((aligned_end - aligned_start) > max)
1520 len = aligned_end - aligned_start;
1522 start = aligned_end - len;
1523 eDebug("skipping to %llx, %d", start, len);
1526 eDebug("result: %llx, %x (%llx %llx)", start, size, aligned_start, aligned_end);
1531 if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0))
1533 eDebug("reached SOF");
1535 m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1538 if (m_source_span.empty())
1540 start = current_offset;
1542 eDebug("NO CUESHEET. (%08llx, %d)", start, size);
1545 start = current_offset;
1551 void eDVBChannel::AddUse()
1553 if (++m_use_count > 1 && m_state == state_last_instance)
1556 m_stateChanged(this);
1560 void eDVBChannel::ReleaseUse()
1564 m_state = state_release;
1565 m_stateChanged(this);
1567 else if (m_use_count == 1)
1569 m_state = state_last_instance;
1570 m_stateChanged(this);
1574 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontendParameters> &feparm)
1577 m_mgr->removeChannel(this);
1584 eDebug("no frontend to tune!");
1588 m_channel_id = channelid;
1589 m_mgr->addChannel(channelid, this);
1590 m_state = state_tuning;
1591 /* if tuning fails, shutdown the channel immediately. */
1593 res = m_frontend->get().tune(*feparm);
1594 m_current_frontend_parameters = feparm;
1598 m_state = state_release;
1599 m_stateChanged(this);
1606 RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
1608 connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
1612 RESULT eDVBChannel::connectEvent(const Slot2<void,iDVBChannel*,int> &event, ePtr<eConnection> &connection)
1614 connection = new eConnection((iDVBChannel*)this, m_event.connect(event));
1618 RESULT eDVBChannel::getState(int &state)
1624 RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
1629 void eDVBChannel::SDTready(int result)
1631 ePyObject args = PyTuple_New(2), ret;
1635 for (std::vector<ServiceDescriptionSection*>::const_iterator i = m_SDT->getSections().begin(); i != m_SDT->getSections().end(); ++i)
1638 PyTuple_SET_ITEM(args, 0, PyInt_FromLong((*i)->getTransportStreamId()));
1639 PyTuple_SET_ITEM(args, 1, PyInt_FromLong((*i)->getOriginalNetworkId()));
1645 PyTuple_SET_ITEM(args, 0, Py_None);
1646 PyTuple_SET_ITEM(args, 1, Py_None);
1650 ret = PyObject_CallObject(m_tsid_onid_callback, args);
1654 Py_DECREF(m_tsid_onid_callback);
1655 m_tsid_onid_callback = ePyObject();
1656 m_tsid_onid_demux = 0;
1660 RESULT eDVBChannel::requestTsidOnid(ePyObject callback)
1662 if (PyCallable_Check(callback))
1664 if (!getDemux(m_tsid_onid_demux, 0))
1666 m_SDT = new eTable<ServiceDescriptionSection>;
1667 CONNECT(m_SDT->tableReady, eDVBChannel::SDTready);
1668 if (m_SDT->start(m_tsid_onid_demux, eDVBSDTSpec()))
1670 m_tsid_onid_demux = 0;
1675 Py_INCREF(callback);
1676 m_tsid_onid_callback = callback;
1684 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
1686 ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
1692 if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
1697 /* don't hold a reference to the decoding demux, we don't need it. */
1699 /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux',
1700 the refcount is lost. thus, decoding demuxes are never allocated.
1702 this poses a big problem for PiP. */
1704 if (cap & capHoldDecodeReference) // this is set in eDVBResourceManager::allocateDemux for Dm500HD/DM800 and DM8000
1706 else if (cap & capDecode)
1715 RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
1720 frontend = &m_frontend->get();
1726 RESULT eDVBChannel::getCurrentFrontendParameters(ePtr<iDVBFrontendParameters> ¶m)
1728 param = m_current_frontend_parameters;
1732 RESULT eDVBChannel::playFile(const char *file)
1734 ASSERT(!m_frontend);
1737 m_pvr_thread->stop();
1738 delete m_pvr_thread;
1742 m_tstools.openFile(file);
1744 /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
1745 THEN DO A REAL FIX HERE! */
1747 if (m_pvr_fd_dst < 0)
1749 /* (this codepath needs to be improved anyway.) */
1750 #if HAVE_DVB_API_VERSION < 3
1751 m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
1753 m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
1755 if (m_pvr_fd_dst < 0)
1757 eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
1762 m_pvr_thread = new eDVBChannelFilePush();
1763 m_pvr_thread->enablePVRCommit(1);
1764 m_pvr_thread->setStreamMode(1);
1765 m_pvr_thread->setScatterGather(this);
1767 if (m_pvr_thread->start(file, m_pvr_fd_dst))
1769 delete m_pvr_thread;
1771 ::close(m_pvr_fd_dst);
1773 eDebug("can't open PVR file %s (%m)", file);
1776 CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent);
1779 m_stateChanged(this);
1784 void eDVBChannel::stopFile()
1788 m_pvr_thread->stop();
1789 delete m_pvr_thread;
1792 if (m_pvr_fd_dst >= 0)
1793 ::close(m_pvr_fd_dst);
1796 void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
1798 m_conn_cueSheetEvent = 0;
1801 m_cue->connectEvent(slot(*this, &eDVBChannel::cueSheetEvent), m_conn_cueSheetEvent);
1804 RESULT eDVBChannel::getLength(pts_t &len)
1806 return m_tstools.calcLen(len);
1809 RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, int mode)
1811 if (!decoding_demux)
1818 if (mode == 0) /* demux */
1820 r = decoding_demux->getSTC(now, 0);
1823 eDebug("demux getSTC failed");
1827 now = pos; /* fixup supplied */
1829 off_t off = 0; /* TODO: fixme */
1830 r = m_tstools.fixupPTS(off, now);
1833 eDebug("fixup PTS failed");
1842 void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
1844 /* when seeking, we have to ensure that all buffers are flushed.
1845 there are basically 3 buffers:
1846 a.) the filepush's internal buffer
1847 b.) the PVR buffer (before demux)
1848 c.) the ratebuffer (after demux)
1850 it's important to clear them in the correct order, otherwise
1851 the ratebuffer (for example) would immediately refill from
1852 the not-yet-flushed PVR buffer.
1855 m_pvr_thread->pause();
1856 /* flush internal filepush buffer */
1857 m_pvr_thread->flush();
1858 /* HACK: flush PVR buffer */
1859 ::ioctl(m_pvr_fd_dst, 0);
1861 /* flush ratebuffers (video, audio) */
1863 decoding_demux->flush();
1865 /* demux will also flush all decoder.. */
1866 /* resume will re-query the SG */
1867 m_pvr_thread->resume();
1870 DEFINE_REF(eCueSheet);
1872 eCueSheet::eCueSheet()
1874 m_skipmode_ratio = 0;
1877 void eCueSheet::seekTo(int relative, const pts_t &pts)
1880 m_seek_requests.push_back(std::pair<int, pts_t>(relative, pts));
1885 void eCueSheet::clear()
1892 void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
1894 ASSERT(begin < end);
1896 m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
1900 void eCueSheet::commitSpans()
1902 m_event(evtSpanChanged);
1905 void eCueSheet::setSkipmode(const pts_t &ratio)
1908 m_skipmode_ratio = ratio;
1910 m_event(evtSkipmode);
1913 void eCueSheet::setDecodingDemux(iDVBDemux *demux, iTSMPEGDecoder *decoder)
1915 m_decoding_demux = demux;
1916 m_decoder = decoder;
1919 RESULT eCueSheet::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
1921 connection = new eConnection(this, m_event.connect(event));