hopefully fix "secondary cable from motorized tuner" setting
[vuplus_dvbapp] / lib / dvb / dvb.cpp
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/sec.h>
6
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <sys/ioctl.h>
13
14 DEFINE_REF(eDVBRegisteredFrontend);
15 DEFINE_REF(eDVBRegisteredDemux);
16
17 DEFINE_REF(eDVBAllocatedFrontend);
18
19 eDVBAllocatedFrontend::eDVBAllocatedFrontend(eDVBRegisteredFrontend *fe): m_fe(fe)
20 {
21         m_fe->inc_use();
22 }
23
24 eDVBAllocatedFrontend::~eDVBAllocatedFrontend()
25 {
26         m_fe->dec_use();
27 }
28
29 DEFINE_REF(eDVBAllocatedDemux);
30
31 eDVBAllocatedDemux::eDVBAllocatedDemux(eDVBRegisteredDemux *demux): m_demux(demux)
32 {
33         m_demux->m_inuse++;
34 }
35
36 eDVBAllocatedDemux::~eDVBAllocatedDemux()
37 {
38         --m_demux->m_inuse;
39 }
40
41 DEFINE_REF(eDVBResourceManager);
42
43 eDVBResourceManager *eDVBResourceManager::instance;
44
45 RESULT eDVBResourceManager::getInstance(ePtr<eDVBResourceManager> &ptr)
46 {
47         if (instance)
48         {
49                 ptr = instance;
50                 return 0;
51         }
52         return -1;
53 }
54
55 ePtr<eDVBResourceManager> NewResourceManagerPtr(void)
56 {
57         ePtr<eDVBResourceManager> ptr;
58         eDVBResourceManager::getInstance(ptr);
59         return ptr;
60 }
61
62 eDVBResourceManager::eDVBResourceManager()
63         :m_releaseCachedChannelTimer(eApp)
64 {
65         avail = 1;
66         busy = 0;
67         m_sec = new eDVBSatelliteEquipmentControl(m_frontend);
68         if (!instance)
69                 instance = this;
70                 
71                 /* search available adapters... */
72
73                 // add linux devices
74         
75         int num_adapter = 0;
76         while (eDVBAdapterLinux::exist(num_adapter))
77         {
78                 addAdapter(new eDVBAdapterLinux(num_adapter));
79                 num_adapter++;
80         }
81         
82         eDebug("found %d adapter, %d frontends and %d demux", 
83                 m_adapter.size(), m_frontend.size(), m_demux.size());
84
85         CONNECT(m_releaseCachedChannelTimer.timeout, eDVBResourceManager::releaseCachedChannel);
86 }
87
88 void eDVBResourceManager::feStateChanged()
89 {
90         int mask=0;
91         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
92                 if (i->m_inuse)
93                         mask |= ( 1 << i->m_frontend->getSlotID() );
94         /* emit */ frontendUseMaskChanged(mask);
95 }
96
97 DEFINE_REF(eDVBAdapterLinux);
98 eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr)
99 {
100                 // scan frontends
101         int num_fe = 0;
102         
103         eDebug("scanning for frontends..");
104         while (1)
105         {
106                 struct stat s;
107                 char filename[128];
108 #if HAVE_DVB_API_VERSION < 3
109                 sprintf(filename, "/dev/dvb/card%d/frontend%d", m_nr, num_fe);
110 #else
111                 sprintf(filename, "/dev/dvb/adapter%d/frontend%d", m_nr, num_fe);
112 #endif
113                 if (stat(filename, &s))
114                         break;
115                 ePtr<eDVBFrontend> fe;
116
117                 int ok = 0;
118                 fe = new eDVBFrontend(m_nr, num_fe, ok);
119                 if (ok)
120                         m_frontend.push_back(fe);
121                 ++num_fe;
122         }
123         
124                 // scan demux
125         int num_demux = 0;
126         while (1)
127         {
128                 struct stat s;
129                 char filename[128];
130 #if HAVE_DVB_API_VERSION < 3
131                 sprintf(filename, "/dev/dvb/card%d/demux%d", m_nr, num_demux);
132 #else
133                 sprintf(filename, "/dev/dvb/adapter%d/demux%d", m_nr, num_demux);
134 #endif
135                 if (stat(filename, &s))
136                         break;
137                 ePtr<eDVBDemux> demux;
138                 
139                 demux = new eDVBDemux(m_nr, num_demux);
140                 m_demux.push_back(demux);
141                         
142                 ++num_demux;
143         }
144 }
145
146 int eDVBAdapterLinux::getNumDemux()
147 {
148         return m_demux.size();
149 }
150
151 RESULT eDVBAdapterLinux::getDemux(ePtr<eDVBDemux> &demux, int nr)
152 {
153         eSmartPtrList<eDVBDemux>::iterator i(m_demux.begin());
154         while (nr && (i != m_demux.end()))
155         {
156                 --nr;
157                 ++i;
158         }
159         
160         if (i != m_demux.end())
161                 demux = *i;
162         else
163                 return -1;
164                 
165         return 0;
166 }
167
168 int eDVBAdapterLinux::getNumFrontends()
169 {
170         return m_frontend.size();
171 }
172
173 RESULT eDVBAdapterLinux::getFrontend(ePtr<eDVBFrontend> &fe, int nr)
174 {
175         eSmartPtrList<eDVBFrontend>::iterator i(m_frontend.begin());
176         while (nr && (i != m_frontend.end()))
177         {
178                 --nr;
179                 ++i;
180         }
181         
182         if (i != m_frontend.end())
183                 fe = *i;
184         else
185                 return -1;
186                 
187         return 0;
188 }
189
190 int eDVBAdapterLinux::exist(int nr)
191 {
192         struct stat s;
193         char filename[128];
194 #if HAVE_DVB_API_VERSION < 3
195         sprintf(filename, "/dev/dvb/card%d", nr);
196 #else
197         sprintf(filename, "/dev/dvb/adapter%d", nr);
198 #endif
199         if (!stat(filename, &s))
200                 return 1;
201         return 0;
202 }
203
204 eDVBResourceManager::~eDVBResourceManager()
205 {
206         if (instance == this)
207                 instance = 0;
208 }
209
210 void eDVBResourceManager::addAdapter(iDVBAdapter *adapter)
211 {
212         int num_fe = adapter->getNumFrontends();
213         int num_demux = adapter->getNumDemux();
214         
215         m_adapter.push_back(adapter);
216         
217         int i;
218         for (i=0; i<num_demux; ++i)
219         {
220                 ePtr<eDVBDemux> demux;
221                 if (!adapter->getDemux(demux, i))
222                         m_demux.push_back(new eDVBRegisteredDemux(demux, adapter));
223         }
224
225         ePtr<eDVBRegisteredFrontend> prev_dvbt_frontend;
226         for (i=0; i<num_fe; ++i)
227         {
228                 ePtr<eDVBFrontend> frontend;
229                 if (!adapter->getFrontend(frontend, i))
230                 {
231                         int frontendType=0;
232                         frontend->getFrontendType(frontendType);
233                         eDVBRegisteredFrontend *new_fe = new eDVBRegisteredFrontend(frontend, adapter);
234                         CONNECT(new_fe->stateChanged, eDVBResourceManager::feStateChanged);
235                         m_frontend.push_back(new_fe);
236                         frontend->setSEC(m_sec);
237                         // we must link all dvb-t frontends ( for active antenna voltage )
238                         if (frontendType == iDVBFrontend::feTerrestrial)
239                         {
240                                 if (prev_dvbt_frontend)
241                                 {
242                                         prev_dvbt_frontend->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, (int)new_fe);
243                                         frontend->setData(eDVBFrontend::LINKED_PREV_PTR, (int)&(*prev_dvbt_frontend));
244                                 }
245                                 prev_dvbt_frontend = new_fe;
246                         }
247                 }
248         }
249 }
250
251 void eDVBResourceManager::setFrontendSlotInformations(ePyObject list)
252 {
253         if (!PyList_Check(list))
254         {
255                 PyErr_SetString(PyExc_StandardError, "eDVBResourceManager::setFrontendSlotInformations argument should be a python list");
256                 return;
257         }
258         if ((unsigned int)PyList_Size(list) != m_frontend.size())
259         {
260                 char blasel[256];
261                 sprintf(blasel, "eDVBResourceManager::setFrontendSlotInformations list size incorrect %d frontends avail, but %d entries in slotlist",
262                         m_frontend.size(), PyList_Size(list));
263                 PyErr_SetString(PyExc_StandardError, blasel);
264                 return;
265         }
266         int pos=0;
267         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
268         {
269                 ePyObject obj = PyList_GET_ITEM(list, pos++);
270                 i->m_frontend->setSlotInfo(obj);
271         }
272 }
273
274 RESULT eDVBResourceManager::allocateFrontend(ePtr<eDVBAllocatedFrontend> &fe, ePtr<iDVBFrontendParameters> &feparm)
275 {
276         ePtr<eDVBRegisteredFrontend> best;
277         int bestval = 0;
278
279         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
280                 if (!i->m_inuse)
281                 {
282                         int c = i->m_frontend->isCompatibleWith(feparm);
283                         if (c > bestval)
284                         {
285                                 bestval = c;
286                                 best = i;
287                         }
288                 }
289
290         if (best)
291         {
292                 fe = new eDVBAllocatedFrontend(best);
293                 return 0;
294         }
295         
296         fe = 0;
297         
298         return -1;
299 }
300
301 RESULT eDVBResourceManager::allocateFrontendByIndex(ePtr<eDVBAllocatedFrontend> &fe, int slot_index)
302 {
303         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
304                 if (!i->m_inuse && i->m_frontend->getSlotID() == slot_index)
305                 {
306                         // check if another slot linked to this is in use
307                         eDVBRegisteredFrontend *satpos_depends_to_fe =
308                                 (eDVBRegisteredFrontend*) i->m_frontend->m_data[eDVBFrontend::SATPOS_DEPENDS_PTR];
309                         if ( (int)satpos_depends_to_fe != -1 )
310                         {
311                                 if (satpos_depends_to_fe->m_inuse)
312                                 {
313                                         eDebug("another satpos depending frontend is in use.. so allocateFrontendByIndex not possible!");
314                                         goto alloc_fe_by_id_not_possible;
315                                 }
316                         }
317                         else // check linked tuners
318                         {
319                                 eDVBRegisteredFrontend *next =
320                                         (eDVBRegisteredFrontend *) i->m_frontend->m_data[eDVBFrontend::LINKED_NEXT_PTR];
321                                 while ( (int)next != -1 )
322                                 {
323                                         if (next->m_inuse)
324                                         {
325                                                 eDebug("another linked frontend is in use.. so allocateFrontendByIndex not possible!");
326                                                 goto alloc_fe_by_id_not_possible;
327                                         }
328                                         next = (eDVBRegisteredFrontend *)next->m_frontend->m_data[eDVBFrontend::LINKED_NEXT_PTR];
329                                 }
330                                 eDVBRegisteredFrontend *prev = (eDVBRegisteredFrontend *)
331                                         i->m_frontend->m_data[eDVBFrontend::LINKED_PREV_PTR];
332                                 while ( (int)prev != -1 )
333                                 {
334                                         if (prev->m_inuse)
335                                         {
336                                                 eDebug("another linked frontend is in use.. so allocateFrontendByIndex not possible!");
337                                                 goto alloc_fe_by_id_not_possible;
338                                         }
339                                         prev = (eDVBRegisteredFrontend *)prev->m_frontend->m_data[eDVBFrontend::LINKED_PREV_PTR];
340                                 }
341                         }
342                         fe = new eDVBAllocatedFrontend(i);
343                         return 0;
344                 }
345 alloc_fe_by_id_not_possible:
346         fe = 0;
347         return -1;
348 }
349
350 RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBAllocatedDemux> &demux, int cap)
351 {
352                 /* find first unused demux which is on same adapter as frontend (or any, if PVR)
353                    never use the first one unless we need a decoding demux. */
354
355         eDebug("allocate demux");
356         eSmartPtrList<eDVBRegisteredDemux>::iterator i(m_demux.begin());
357         
358         if (i == m_demux.end())
359                 return -1;
360                 
361         int n=0;
362                 /* FIXME: hardware demux policy */
363         if (!(cap & iDVBChannel::capDecode))
364         {
365                 if (m_demux.size() > 2)  /* assumed to be true, otherwise we have lost anyway */
366                 {
367                         ++i, ++n;
368                         ++i, ++n;
369                 }
370         }
371         
372         for (; i != m_demux.end(); ++i, ++n)
373         {
374                 int is_decode = n < 2;
375                 
376                 int in_use = is_decode ? (i->m_demux->getRefCount() != 2) : i->m_inuse;
377                 
378                 if ((!in_use) && ((!fe) || (i->m_adapter == fe->m_adapter)))
379                 {
380                         if ((cap & iDVBChannel::capDecode) && !is_decode)
381                                 continue;
382                         
383                         demux = new eDVBAllocatedDemux(i);
384                         if (fe)
385                                 demux->get().setSourceFrontend(fe->m_frontend->getDVBID());
386                         else
387                                 demux->get().setSourcePVR(0);
388                         return 0;
389                 }
390         }
391         eDebug("demux not found");
392         return -1;
393 }
394
395 RESULT eDVBResourceManager::setChannelList(iDVBChannelList *list)
396 {
397         m_list = list;
398         return 0;
399 }
400
401 RESULT eDVBResourceManager::getChannelList(ePtr<iDVBChannelList> &list)
402 {
403         list = m_list;
404         if (list)
405                 return 0;
406         else
407                 return -ENOENT;
408 }
409
410 RESULT eDVBResourceManager::allocateChannel(const eDVBChannelID &channelid, eUsePtr<iDVBChannel> &channel)
411 {
412                 /* first, check if a channel is already existing. */
413
414         if (m_cached_channel)
415         {
416                 eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
417                 if(channelid==cache_chan->getChannelID())
418                 {
419                         eDebug("use cached_channel");
420                         channel = m_cached_channel;
421                         return 0;
422                 }
423                 m_cached_channel_state_changed_conn.disconnect();
424                 m_cached_channel=0;
425                 m_releaseCachedChannelTimer.stop();
426         }
427
428 //      eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
429         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
430         {
431 //              eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
432                 if (i->m_channel_id == channelid)
433                 {
434 //                      eDebug("found shared channel..");
435                         channel = i->m_channel;
436                         return 0;
437                 }
438         }
439         
440         /* no currently available channel is tuned to this channelid. create a new one, if possible. */
441
442         if (!m_list)
443         {
444                 eDebug("no channel list set!");
445                 return -ENOENT;
446         }
447
448         ePtr<iDVBFrontendParameters> feparm;
449         if (m_list->getChannelFrontendData(channelid, feparm))
450         {
451                 eDebug("channel not found!");
452                 return -ENOENT;
453         }
454
455         /* allocate a frontend. */
456         
457         ePtr<eDVBAllocatedFrontend> fe;
458         
459         if (allocateFrontend(fe, feparm))
460                 return errNoFrontend;
461
462         RESULT res;
463         ePtr<eDVBChannel> ch;
464         ch = new eDVBChannel(this, fe);
465
466         res = ch->setChannel(channelid, feparm);
467         if (res)
468         {
469                 channel = 0;
470                 return errChidNotFound;
471         }
472         m_cached_channel = channel = ch;
473         m_cached_channel_state_changed_conn =
474                 CONNECT(ch->m_stateChanged,eDVBResourceManager::DVBChannelStateChanged);
475
476         return 0;
477 }
478
479 void eDVBResourceManager::DVBChannelStateChanged(iDVBChannel *chan)
480 {
481         int state=0;
482         chan->getState(state);
483         switch (state)
484         {
485                 case iDVBChannel::state_release:
486                 case iDVBChannel::state_ok:
487                 {
488                         eDebug("stop release channel timer");
489                         m_releaseCachedChannelTimer.stop();
490                         break;
491                 }
492                 case iDVBChannel::state_last_instance:
493                 {
494                         eDebug("start release channel timer");
495                         m_releaseCachedChannelTimer.start(3000, true);
496                         break;
497                 }
498                 default: // ignore all other events
499                         break;
500         }
501 }
502
503 void eDVBResourceManager::releaseCachedChannel()
504 {
505         eDebug("release cached channel (timer timeout)");
506         m_cached_channel=0;
507 }
508
509 RESULT eDVBResourceManager::allocateRawChannel(eUsePtr<iDVBChannel> &channel, int slot_index)
510 {
511         ePtr<eDVBAllocatedFrontend> fe;
512
513         if (m_cached_channel)
514         {
515                 m_cached_channel_state_changed_conn.disconnect();
516                 m_cached_channel=0;
517                 m_releaseCachedChannelTimer.stop();
518         }
519
520         if (allocateFrontendByIndex(fe, slot_index))
521                 return errNoFrontend;
522         
523         eDVBChannel *ch;
524         ch = new eDVBChannel(this, fe);
525
526         channel = ch;
527         return 0;
528 }
529
530
531 RESULT eDVBResourceManager::allocatePVRChannel(eUsePtr<iDVBPVRChannel> &channel)
532 {
533         ePtr<eDVBAllocatedDemux> demux;
534
535         if (m_cached_channel && m_releaseCachedChannelTimer.isActive())
536         {
537                 m_cached_channel_state_changed_conn.disconnect();
538                 m_cached_channel=0;
539                 m_releaseCachedChannelTimer.stop();
540         }
541
542         eDVBChannel *ch;
543         ch = new eDVBChannel(this, 0);
544
545         channel = ch;
546         return 0;
547 }
548
549 RESULT eDVBResourceManager::addChannel(const eDVBChannelID &chid, eDVBChannel *ch)
550 {
551         m_active_channels.push_back(active_channel(chid, ch));
552         /* emit */ m_channelAdded(ch);
553         return 0;
554 }
555
556 RESULT eDVBResourceManager::removeChannel(eDVBChannel *ch)
557 {
558         int cnt = 0;
559         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end();)
560         {
561                 if (i->m_channel == ch)
562                 {
563                         i = m_active_channels.erase(i);
564                         ++cnt;
565                 } else
566                         ++i;
567         }
568         ASSERT(cnt == 1);
569         if (cnt == 1)
570                 return 0;
571         return -ENOENT;
572 }
573
574 RESULT eDVBResourceManager::connectChannelAdded(const Slot1<void,eDVBChannel*> &channelAdded, ePtr<eConnection> &connection)
575 {
576         connection = new eConnection((eDVBResourceManager*)this, m_channelAdded.connect(channelAdded));
577         return 0;
578 }
579
580 int eDVBResourceManager::canAllocateFrontend(ePtr<iDVBFrontendParameters> &feparm)
581 {
582         ePtr<eDVBRegisteredFrontend> best;
583         int bestval = 0;
584
585         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i)
586                 if (!i->m_inuse)
587                 {
588                         int c = i->m_frontend->isCompatibleWith(feparm);
589                         if (c > bestval)
590                                 bestval = c;
591                 }
592         return bestval;
593 }
594
595 int eDVBResourceManager::canAllocateChannel(const eDVBChannelID &channelid, const eDVBChannelID& ignore)
596 {
597         int ret=30000;
598         if (m_cached_channel)
599         {
600                 eDVBChannel *cache_chan = (eDVBChannel*)&(*m_cached_channel);
601                 if(channelid==cache_chan->getChannelID())
602                         return ret;
603         }
604
605                 /* first, check if a channel is already existing. */
606 //      eDebug("allocate channel.. %04x:%04x", channelid.transport_stream_id.get(), channelid.original_network_id.get());
607         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
608         {
609 //              eDebug("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)
611                 {
612 //                      eDebug("found shared channel..");
613                         return ret;
614                 }
615         }
616
617         int *decremented_cached_channel_fe_usecount=NULL,
618                 *decremented_fe_usecount=NULL;
619
620         for (std::list<active_channel>::iterator i(m_active_channels.begin()); i != m_active_channels.end(); ++i)
621         {
622 //              eDebug("available channel.. %04x:%04x", i->m_channel_id.transport_stream_id.get(), i->m_channel_id.original_network_id.get());
623                 if (i->m_channel_id == ignore)
624                 {
625                         eDVBChannel *channel = (eDVBChannel*) &(*i->m_channel);
626                         // one eUsePtr<iDVBChannel> is used in eDVBServicePMTHandler
627                         // another on eUsePtr<iDVBChannel> is used in the eDVBScan instance used in eDVBServicePMTHandler (for SDT scan)
628                         // so we must check here if usecount is 3 (when the channel is equal to the cached channel)
629                         // or 2 when the cached channel is not equal to the compared channel
630                         if (channel == &(*m_cached_channel) ? channel->getUseCount() == 3 : channel->getUseCount() == 2)  // channel only used once..
631                         {
632                                 ePtr<iDVBFrontend> fe;
633                                 if (!i->m_channel->getFrontend(fe))
634                                 {
635                                         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(m_frontend.begin()); ii != m_frontend.end(); ++ii)
636                                         {
637                                                 if ( &(*fe) == &(*ii->m_frontend) )
638                                                 {
639                                                         --ii->m_inuse;
640                                                         decremented_fe_usecount = &ii->m_inuse;
641                                                         if (channel == &(*m_cached_channel))
642                                                                 decremented_cached_channel_fe_usecount = decremented_fe_usecount;
643                                                         break;
644                                                 }
645                                         }
646                                 }
647                         }
648                         break;
649                 }
650         }
651
652         if (!decremented_cached_channel_fe_usecount)
653         {
654                 if (m_cached_channel)
655                 {
656                         eDVBChannel *channel = (eDVBChannel*) &(*m_cached_channel);
657                         if (channel->getUseCount() == 1)
658                         {
659                                 ePtr<iDVBFrontend> fe;
660                                 if (!channel->getFrontend(fe))
661                                 {
662                                         for (eSmartPtrList<eDVBRegisteredFrontend>::iterator ii(m_frontend.begin()); ii != m_frontend.end(); ++ii)
663                                         {
664                                                 if ( &(*fe) == &(*ii->m_frontend) )
665                                                 {
666                                                         --ii->m_inuse;
667                                                         decremented_cached_channel_fe_usecount = &ii->m_inuse;
668                                                         break;
669                                                 }
670                                         }
671                                 }
672                         }
673                 }
674         }
675         else
676                 decremented_cached_channel_fe_usecount=NULL;
677
678         ePtr<iDVBFrontendParameters> feparm;
679
680         if (!m_list)
681         {
682                 eDebug("no channel list set!");
683                 ret = 0;
684                 goto error;
685         }
686
687         if (m_list->getChannelFrontendData(channelid, feparm))
688         {
689                 eDebug("channel not found!");
690                 ret = 0;
691                 goto error;
692         }
693
694         ret = canAllocateFrontend(feparm);
695
696 error:
697         if (decremented_fe_usecount)
698                 ++(*decremented_fe_usecount);
699         if (decremented_cached_channel_fe_usecount)
700                 ++(*decremented_cached_channel_fe_usecount);
701
702         return ret;
703 }
704
705 class eDVBChannelFilePush: public eFilePushThread
706 {
707 public:
708         void setIFrameSearch(int enabled) { m_iframe_search = enabled; m_iframe_state = 0; }
709 protected:
710         int m_iframe_search, m_iframe_state, m_pid;
711         int filterRecordData(const unsigned char *data, int len, size_t &current_span_remaining);
712 };
713
714 int eDVBChannelFilePush::filterRecordData(const unsigned char *_data, int len, size_t &current_span_remaining)
715 {
716 #if 0 /* not yet */
717         if (!m_iframe_search)
718                 return len;
719
720         unsigned char *data = (unsigned char*)_data; /* remove that const. we know what we are doing. */
721
722         eDebug("filterRecordData, size=%d (mod 188=%d), first byte is %02x", len, len %188, data[0]);
723
724         unsigned char *d = data;
725         while ((d = (unsigned char*)memmem(d, data + len - d, "\x00\x00\x01", 3)))
726         {
727                 int offset = d - data;
728                 int ts_offset = offset - offset % 188; /* offset to the start of TS packet */
729                 unsigned char *ts = data + ts_offset;
730                 int pid = ((ts[1] << 8) | ts[2]) & 0x1FFF;
731
732                 if ((d[3] == 0) && (m_pid == pid))  /* picture start */
733                 {
734                         int picture_type = (d[5] >> 3) & 7;
735                         d += 4;
736                         
737                         eDebug("%d-frame at %d, offset in TS packet: %d, pid=%04x", picture_type, offset, offset % 188, pid);
738
739                         if (m_iframe_state == 1)
740                         {
741                                         /* we are allowing data, and stop allowing data on the next frame. 
742                                            we now found a frame. so stop here. */
743                                 memset(data + offset, 0, 188 - (offset%188)); /* zero out rest of TS packet */
744                                 current_span_remaining = 0;
745                                 m_iframe_state = 0;
746                                 unsigned char *fts = ts + 188;
747                                 while (fts < (data + len))
748                                 {
749                                         fts[1] |= 0x1f;
750                                         fts[2] |= 0xff; /* drop packet */
751                                         fts += 188;
752                                 }
753
754                                 return len; // ts_offset + 188; /* deliver this packet, but not more. */
755                         } else
756                         {
757                                 if (picture_type != 1) /* we are only interested in I frames */
758                                         continue;
759                         
760                                 unsigned char *fts = data;
761                                 while (fts < ts)
762                                 {
763                                         fts[1] |= 0x1f;
764                                         fts[2] |= 0xff; /* drop packet */
765                                 
766                                         fts += 188;
767                                 }
768                                                 /* force payload only */
769                                 ts[3] &= ~0x30;
770                                 ts[3] |=  0x10;
771                                 
772 //                              memset(ts + 4, 0xFF, (offset % 188) - 4);
773
774                                 m_iframe_state = 1;
775                         }
776                 } else if ((d[3] & 0xF0) == 0xE0) /* video stream */
777                 {
778                         if (m_pid != pid)
779                         {
780                                 eDebug("now locked to pid %04x", pid);
781                                 m_pid = pid;
782                         }
783 //                      m_pid = 0x6e;
784                         d += 4;
785                 } else
786                         d += 4; /* ignore */
787
788         }
789
790         if (m_iframe_state == 1)
791                 return len;
792         else
793                 return 0; /* we need find an iframe first */
794 #else
795         return len;
796 #endif
797 }
798
799 DEFINE_REF(eDVBChannel);
800
801 eDVBChannel::eDVBChannel(eDVBResourceManager *mgr, eDVBAllocatedFrontend *frontend): m_state(state_idle), m_mgr(mgr)
802 {
803         m_frontend = frontend;
804
805         m_pvr_thread = 0;
806         
807         m_skipmode_n = m_skipmode_m = 0;
808         
809         if (m_frontend)
810                 m_frontend->get().connectStateChange(slot(*this, &eDVBChannel::frontendStateChanged), m_conn_frontendStateChanged);
811 }
812
813 eDVBChannel::~eDVBChannel()
814 {
815         if (m_channel_id)
816                 m_mgr->removeChannel(this);
817
818         stopFile();
819 }
820
821 void eDVBChannel::frontendStateChanged(iDVBFrontend*fe)
822 {
823         int state, ourstate = 0;
824         
825                 /* if we are already in shutdown, don't change state. */
826         if (m_state == state_release)
827                 return;
828         
829         if (fe->getState(state))
830                 return;
831         
832         if (state == iDVBFrontend::stateLock)
833         {
834                 eDebug("OURSTATE: ok");
835                 ourstate = state_ok;
836         } else if (state == iDVBFrontend::stateTuning)
837         {
838                 eDebug("OURSTATE: tuning");
839                 ourstate = state_tuning;
840         } else if (state == iDVBFrontend::stateLostLock)
841         {
842                         /* on managed channels, we try to retune in order to re-acquire lock. */
843                 if (m_current_frontend_parameters)
844                 {
845                         eDebug("OURSTATE: lost lock, trying to retune");
846                         ourstate = state_tuning;
847                         m_frontend->get().tune(*m_current_frontend_parameters);
848                 } else
849                         /* on unmanaged channels, we don't do this. the client will do this. */
850                 {
851                         eDebug("OURSTATE: lost lock, unavailable now.");
852                         ourstate = state_unavailable;
853                 }
854         } else if (state == iDVBFrontend::stateFailed)
855         {
856                 eDebug("OURSTATE: failed");
857                 ourstate = state_failed;
858         } else
859                 eFatal("state unknown");
860         
861         if (ourstate != m_state)
862         {
863                 m_state = ourstate;
864                 m_stateChanged(this);
865         }
866 }
867
868 void eDVBChannel::pvrEvent(int event)
869 {
870         switch (event)
871         {
872         case eFilePushThread::evtEOF:
873                 eDebug("eDVBChannel: End of file!");
874                 m_event(this, evtEOF);
875                 break;
876         case eFilePushThread::evtUser: /* start */
877                 eDebug("SOF");
878                 m_event(this, evtSOF);
879                 break;
880         }
881 }
882
883 void eDVBChannel::cueSheetEvent(int event)
884 {
885         switch (event)
886         {
887         case eCueSheet::evtSeek:
888                 eDebug("seek.");
889                 flushPVR(m_cue->m_decoding_demux);
890                 break;
891         case eCueSheet::evtSkipmode:
892         {
893                 {
894                         eSingleLocker l(m_cue->m_lock);
895                         m_cue->m_seek_requests.push_back(std::pair<int, pts_t>(1, 0)); /* resync */
896                         if (m_cue->m_skipmode_ratio)
897                         {
898                                 int bitrate = m_tstools.calcBitrate(); /* in bits/s */
899                                 eDebug("skipmode ratio is %lld:90000, bitrate is %d bit/s", m_cue->m_skipmode_ratio, bitrate);
900                                                 /* i agree that this might look a bit like black magic. */
901                                 m_skipmode_n = 512*1024; /* must be 1 iframe at least. */
902                                 m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio / 8;
903                                 
904                                 if (m_cue->m_skipmode_ratio < 0)
905                                         m_skipmode_m -= m_skipmode_n;
906         
907                                 eDebug("resolved to: %d %d", m_skipmode_m, m_skipmode_n);
908                                 
909                                 if (abs(m_skipmode_m) < abs(m_skipmode_n))
910                                 {
911                                         eWarning("something is wrong with this calculation");
912                                         m_skipmode_n = m_skipmode_m = 0;
913                                 }
914                                 
915                         } else
916                         {
917                                 eDebug("skipmode ratio is 0, normal play");
918                                 m_skipmode_n = m_skipmode_m = 0;
919                         }
920                 }
921                 m_pvr_thread->setIFrameSearch(m_skipmode_n != 0);
922                 eDebug("flush pvr");
923                 flushPVR(m_cue->m_decoding_demux);
924                 eDebug("done");
925                 break;
926         }
927         case eCueSheet::evtSpanChanged:
928         {
929                 m_source_span.clear();
930                 for (std::list<std::pair<pts_t, pts_t> >::const_iterator i(m_cue->m_spans.begin()); i != m_cue->m_spans.end(); ++i)
931                 {
932                         off_t offset_in, offset_out;
933                         pts_t pts_in = i->first, pts_out = i->second;
934                         if (m_tstools.getOffset(offset_in, pts_in) || m_tstools.getOffset(offset_out, pts_out))
935                         {
936                                 eDebug("span translation failed.\n");
937                                 continue;
938                         }
939                         eDebug("source span: %llx .. %llx, translated to %llx..%llx", pts_in, pts_out, offset_in, offset_out);
940                         m_source_span.push_back(std::pair<off_t, off_t>(offset_in, offset_out));
941                 }
942                 break;
943         }
944         }
945 }
946
947         /* align toward zero */
948 static inline long long align(long long x, int align)
949 {
950         int sign = x < 0;
951
952         if (sign)
953                 x = -x;
954
955         x -= x % align;
956
957         if (sign)
958                 x = -x;
959
960         return x;
961 }
962
963         /* remember, this gets called from another thread. */
964 void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size)
965 {
966         const int blocksize = 188;
967         unsigned int max = align(10*1024*1024, blocksize);
968         current_offset = align(current_offset, blocksize);
969         
970         if (!m_cue)
971         {
972                 eDebug("no cue sheet. forcing normal play");
973                 start = current_offset;
974                 size = max;
975                 return;
976         }
977
978         eSingleLocker l(m_cue->m_lock);
979         
980         if (!m_cue->m_decoding_demux)
981         {
982                 start = current_offset;
983                 size = max;
984                 eDebug("getNextSourceSpan, no decoding demux. forcing normal play");
985                 return;
986         }
987
988         if (m_skipmode_n)
989         {
990                 eDebug("skipmode %d:%d", m_skipmode_m, m_skipmode_n);
991                 max = align(m_skipmode_n, blocksize);
992         }
993
994         eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m);
995
996         current_offset += align(m_skipmode_m, blocksize);
997
998         while (!m_cue->m_seek_requests.empty())
999         {
1000                 std::pair<int, pts_t> seek = m_cue->m_seek_requests.front();
1001                 m_cue->m_seek_requests.pop_front();
1002                 int relative = seek.first;
1003                 pts_t pts = seek.second;
1004
1005                 pts_t now = 0;
1006                 if (relative)
1007                 {
1008                         if (!m_cue->m_decoder)
1009                         {
1010                                 eDebug("no decoder - can't seek relative");
1011                                 continue;
1012                         }
1013                         if (m_cue->m_decoder->getPTS(0, now))
1014                         {
1015                                 eDebug("decoder getPTS failed, can't seek relative");
1016                                 continue;
1017                         }
1018                         if (getCurrentPosition(m_cue->m_decoding_demux, now, 1))
1019                         {
1020                                 eDebug("seekTo: getCurrentPosition failed!");
1021                                 continue;
1022                         }
1023                 } else if (pts < 0) /* seek relative to end */
1024                 {
1025                         pts_t len;
1026                         if (!getLength(len))
1027                         {
1028                                 eDebug("seeking relative to end. len=%lld, seek = %lld", len, pts);
1029                                 pts += len;
1030                         } else
1031                         {
1032                                 eWarning("getLength failed - can't seek relative to end!");
1033                                 continue;
1034                         }
1035                 }
1036                 
1037                 if (relative == 1) /* pts relative */
1038                 {
1039                         pts += now;
1040                         if (pts < 0)
1041                                 pts = 0;
1042                 }
1043
1044                 if (relative != 2)
1045                         if (pts < 0)
1046                                 pts = 0;
1047                 
1048                 if (relative == 2) /* AP relative */
1049                 {
1050                         eDebug("AP relative seeking: %lld, at %lld", pts, now);
1051                         pts_t nextap;
1052                         if (m_tstools.getNextAccessPoint(nextap, now, pts))
1053                         {
1054                                 pts = now;
1055                                 eDebug("AP relative seeking failed!");
1056                         } else
1057                         {
1058                                 eDebug("next ap is %llx\n", pts);
1059                                 pts = nextap;
1060                         }
1061                 }
1062                 
1063                 off_t offset = 0;
1064                 if (m_tstools.getOffset(offset, pts))
1065                 {
1066                         eDebug("get offset for pts=%lld failed!", pts);
1067                         continue;
1068                 }
1069
1070                 eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
1071                 current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
1072         }
1073
1074         for (std::list<std::pair<off_t, off_t> >::const_iterator i(m_source_span.begin()); i != m_source_span.end(); ++i)
1075         {
1076                 long long aligned_start = align(i->first, blocksize);
1077                 long long aligned_end = align(i->second, blocksize);
1078         
1079                 if ((current_offset >= aligned_start) && (current_offset < aligned_end))
1080                 {
1081                         start = current_offset;
1082                                 /* max can not exceed max(size_t). aligned_end - current_offset, however, can. */
1083                         if ((aligned_end - current_offset) > max)
1084                                 size = max;
1085                         else
1086                                 size = aligned_end - current_offset;
1087                         eDebug("HIT, %lld < %lld < %lld, size: %d", i->first, current_offset, i->second, size);
1088                         return;
1089                 }
1090                 if (current_offset < aligned_start)
1091                 {
1092                                 /* ok, our current offset is in an 'out' zone. */
1093                         if ((m_skipmode_m >= 0) || (i == m_source_span.begin()))
1094                         {
1095                                         /* in normal playback, just start at the next zone. */
1096                                 start = i->first;
1097                                 
1098                                         /* size is not 64bit! */
1099                                 if ((i->second - i->first) > max)
1100                                         size = max;
1101                                 else
1102                                         size = aligned_end - aligned_start;
1103
1104                                 eDebug("skip");
1105                                 if (m_skipmode_m < 0)
1106                                 {
1107                                         eDebug("reached SOF");
1108                                                 /* reached SOF */
1109                                         m_skipmode_m = 0;
1110                                         m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1111                                 }
1112                         } else
1113                         {
1114                                         /* when skipping reverse, however, choose the zone before. */
1115                                 --i;
1116                                 eDebug("skip to previous block, which is %llx..%llx", i->first, i->second);
1117                                 size_t len;
1118                                 
1119                                 if ((i->second - i->first) > max)
1120                                         len = max;
1121                                 else
1122                                         len = aligned_end - aligned_start;
1123
1124                                 start = aligned_end - len;
1125                                 eDebug("skipping to %llx, %d", start, len);
1126                         }
1127                         
1128                         eDebug("result: %llx, %x (%llx %llx)", start, size, aligned_start, aligned_end);
1129                         return;
1130                 }
1131         }
1132         
1133         if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0))
1134         {
1135                 eDebug("reached SOF");
1136                 m_skipmode_m = 0;
1137                 m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1138         }
1139         
1140         start = current_offset;
1141         size = max;
1142
1143         eDebug("END OF CUESHEET. (%08llx, %d)", start, size);
1144         return;
1145 }
1146
1147 void eDVBChannel::AddUse()
1148 {
1149         if (++m_use_count > 1 && m_state == state_last_instance)
1150         {
1151                 m_state = state_ok;
1152                 m_stateChanged(this);
1153         }
1154 }
1155
1156 void eDVBChannel::ReleaseUse()
1157 {
1158         if (!--m_use_count)
1159         {
1160                 m_state = state_release;
1161                 m_stateChanged(this);
1162         }
1163         else if (m_use_count == 1)
1164         {
1165                 m_state = state_last_instance;
1166                 m_stateChanged(this);
1167         }
1168 }
1169
1170 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontendParameters> &feparm)
1171 {
1172         if (m_channel_id)
1173                 m_mgr->removeChannel(this);
1174                 
1175         if (!channelid)
1176                 return 0;
1177
1178         if (!m_frontend)
1179         {
1180                 eDebug("no frontend to tune!");
1181                 return -ENODEV;
1182         }
1183         
1184         m_channel_id = channelid;
1185         m_mgr->addChannel(channelid, this);
1186         m_state = state_tuning;
1187                         /* if tuning fails, shutdown the channel immediately. */
1188         int res;
1189         res = m_frontend->get().tune(*feparm);
1190         m_current_frontend_parameters = feparm;
1191         
1192         if (res)
1193         {
1194                 m_state = state_release;
1195                 m_stateChanged(this);
1196                 return res;
1197         }
1198         
1199         return 0;
1200 }
1201
1202 RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
1203 {
1204         connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
1205         return 0;
1206 }
1207
1208 RESULT eDVBChannel::connectEvent(const Slot2<void,iDVBChannel*,int> &event, ePtr<eConnection> &connection)
1209 {
1210         connection = new eConnection((iDVBChannel*)this, m_event.connect(event));
1211         return 0;
1212 }
1213
1214 RESULT eDVBChannel::getState(int &state)
1215 {
1216         state = m_state;
1217         return 0;
1218 }
1219
1220 RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
1221 {
1222         return -1;
1223 }
1224
1225 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
1226 {
1227         ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
1228         
1229         if (!our_demux)
1230         {
1231                 demux = 0;
1232                 
1233                 if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
1234                         return -1;
1235         }
1236         
1237         demux = *our_demux;
1238                 /* don't hold a reference to the decoding demux, we don't need it. */
1239                 
1240                 /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux',
1241                    the refcount is lost. thus, decoding demuxes are never allocated. 
1242                    
1243                    this poses a big problem for PiP. */
1244         if (cap & capDecode)
1245                 our_demux = 0;
1246         return 0;
1247 }
1248
1249 RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
1250 {
1251         frontend = 0;
1252         if (!m_frontend)
1253                 return -ENODEV;
1254         frontend = &m_frontend->get();
1255         if (frontend)
1256                 return 0;
1257         return -ENODEV;
1258 }
1259
1260 RESULT eDVBChannel::getCurrentFrontendParameters(ePtr<iDVBFrontendParameters> &param)
1261 {
1262         param = m_current_frontend_parameters;
1263         return 0;
1264 }
1265
1266 RESULT eDVBChannel::playFile(const char *file)
1267 {
1268         ASSERT(!m_frontend);
1269         if (m_pvr_thread)
1270         {
1271                 m_pvr_thread->stop();
1272                 delete m_pvr_thread;
1273                 m_pvr_thread = 0;
1274         }
1275         
1276         m_tstools.openFile(file);
1277         
1278                 /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
1279                    THEN DO A REAL FIX HERE! */
1280         
1281                 /* (this codepath needs to be improved anyway.) */
1282 #if HAVE_DVB_API_VERSION < 3
1283         m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
1284 #else
1285         m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
1286 #endif
1287         if (m_pvr_fd_dst < 0)
1288         {
1289                 eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
1290                 return -ENODEV;
1291         }
1292
1293         m_pvr_thread = new eDVBChannelFilePush();
1294         m_pvr_thread->enablePVRCommit(1);
1295         m_pvr_thread->setStreamMode(1);
1296         m_pvr_thread->setScatterGather(this);
1297
1298         if (m_pvr_thread->start(file, m_pvr_fd_dst))
1299         {
1300                 delete m_pvr_thread;
1301                 m_pvr_thread = 0;
1302                 eDebug("can't open PVR file %s (%m)", file);
1303                 return -ENOENT;
1304         }
1305         CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent);
1306
1307         m_state = state_ok;
1308         m_stateChanged(this);
1309
1310         return 0;
1311 }
1312
1313 void eDVBChannel::stopFile()
1314 {
1315         if (m_pvr_thread)
1316         {
1317                 m_pvr_thread->stop();
1318                 ::close(m_pvr_fd_dst);
1319                 delete m_pvr_thread;
1320                 m_pvr_thread = 0;
1321         }
1322 }
1323
1324 void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
1325 {
1326         m_conn_cueSheetEvent = 0;
1327         m_cue = cuesheet;
1328         if (m_cue)
1329                 m_cue->connectEvent(slot(*this, &eDVBChannel::cueSheetEvent), m_conn_cueSheetEvent);
1330 }
1331
1332 RESULT eDVBChannel::getLength(pts_t &len)
1333 {
1334         return m_tstools.calcLen(len);
1335 }
1336
1337 RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, int mode)
1338 {
1339         if (!decoding_demux)
1340                 return -1;
1341         
1342         pts_t now;
1343         
1344         int r;
1345         
1346         if (mode == 0) /* demux */
1347         {
1348                 r = decoding_demux->getSTC(now, 0);
1349                 if (r)
1350                 {
1351                         eDebug("demux getSTC failed");
1352                         return -1;
1353                 }
1354         } else
1355                 now = pos; /* fixup supplied */
1356         
1357         off_t off = 0; /* TODO: fixme */
1358         r = m_tstools.fixupPTS(off, now);
1359         if (r)
1360         {
1361                 eDebug("fixup PTS failed");
1362                 return -1;
1363         }
1364         
1365         pos = now;
1366         
1367         return 0;
1368 }
1369
1370 void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
1371 {
1372                         /* when seeking, we have to ensure that all buffers are flushed.
1373                            there are basically 3 buffers:
1374                            a.) the filepush's internal buffer
1375                            b.) the PVR buffer (before demux)
1376                            c.) the ratebuffer (after demux)
1377                            
1378                            it's important to clear them in the correct order, otherwise
1379                            the ratebuffer (for example) would immediately refill from
1380                            the not-yet-flushed PVR buffer.
1381                         */
1382
1383         m_pvr_thread->pause();
1384                 /* flush internal filepush buffer */
1385         m_pvr_thread->flush();
1386                 /* HACK: flush PVR buffer */
1387         ::ioctl(m_pvr_fd_dst, 0);
1388         
1389                 /* flush ratebuffers (video, audio) */
1390         if (decoding_demux)
1391                 decoding_demux->flush();
1392
1393                 /* demux will also flush all decoder.. */
1394                 /* resume will re-query the SG */
1395         m_pvr_thread->resume();
1396 }
1397
1398 DEFINE_REF(eCueSheet);
1399
1400 eCueSheet::eCueSheet()
1401 {
1402         m_skipmode_ratio = 0;
1403 }
1404
1405 void eCueSheet::seekTo(int relative, const pts_t &pts)
1406 {
1407         {
1408                 eSingleLocker l(m_lock);
1409                 m_seek_requests.push_back(std::pair<int, pts_t>(relative, pts));
1410         }
1411         m_event(evtSeek);
1412 }
1413         
1414 void eCueSheet::clear()
1415 {
1416         eSingleLocker l(m_lock);
1417         m_spans.clear();
1418 }
1419
1420 void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
1421 {
1422         {
1423                 eSingleLocker l(m_lock);
1424                 m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
1425         }
1426 }
1427
1428 void eCueSheet::commitSpans()
1429 {
1430         m_event(evtSpanChanged);
1431 }
1432
1433 void eCueSheet::setSkipmode(const pts_t &ratio)
1434 {
1435         {
1436                 eSingleLocker l(m_lock);
1437                 m_skipmode_ratio = ratio;
1438         }
1439         m_event(evtSkipmode);
1440 }
1441
1442 void eCueSheet::setDecodingDemux(iDVBDemux *demux, iTSMPEGDecoder *decoder)
1443 {
1444         m_decoding_demux = demux;
1445         m_decoder = decoder;
1446 }
1447
1448 RESULT eCueSheet::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
1449 {
1450         connection = new eConnection(this, m_event.connect(event));
1451         return 0;
1452 }