use read/write lock for eCueSheet
[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                         m_cue->m_lock.WrLock();
895                         m_cue->m_seek_requests.push_back(std::pair<int, pts_t>(1, 0)); /* resync */
896                         m_cue->m_lock.Unlock();
897                         eRdLocker l(m_cue->m_lock);
898                         if (m_cue->m_skipmode_ratio)
899                         {
900                                 int bitrate = m_tstools.calcBitrate(); /* in bits/s */
901                                 eDebug("skipmode ratio is %lld:90000, bitrate is %d bit/s", m_cue->m_skipmode_ratio, bitrate);
902                                                 /* i agree that this might look a bit like black magic. */
903                                 m_skipmode_n = 512*1024; /* must be 1 iframe at least. */
904                                 m_skipmode_m = bitrate / 8 / 90000 * m_cue->m_skipmode_ratio / 8;
905                                 
906                                 if (m_cue->m_skipmode_ratio < 0)
907                                         m_skipmode_m -= m_skipmode_n;
908         
909                                 eDebug("resolved to: %d %d", m_skipmode_m, m_skipmode_n);
910                                 
911                                 if (abs(m_skipmode_m) < abs(m_skipmode_n))
912                                 {
913                                         eWarning("something is wrong with this calculation");
914                                         m_skipmode_n = m_skipmode_m = 0;
915                                 }
916                                 
917                         } else
918                         {
919                                 eDebug("skipmode ratio is 0, normal play");
920                                 m_skipmode_n = m_skipmode_m = 0;
921                         }
922                 }
923                 m_pvr_thread->setIFrameSearch(m_skipmode_n != 0);
924                 eDebug("flush pvr");
925                 flushPVR(m_cue->m_decoding_demux);
926                 eDebug("done");
927                 break;
928         }
929         case eCueSheet::evtSpanChanged:
930         {
931                 m_source_span.clear();
932                 for (std::list<std::pair<pts_t, pts_t> >::const_iterator i(m_cue->m_spans.begin()); i != m_cue->m_spans.end(); ++i)
933                 {
934                         off_t offset_in, offset_out;
935                         pts_t pts_in = i->first, pts_out = i->second;
936                         if (m_tstools.getOffset(offset_in, pts_in) || m_tstools.getOffset(offset_out, pts_out))
937                         {
938                                 eDebug("span translation failed.\n");
939                                 continue;
940                         }
941                         eDebug("source span: %llx .. %llx, translated to %llx..%llx", pts_in, pts_out, offset_in, offset_out);
942                         m_source_span.push_back(std::pair<off_t, off_t>(offset_in, offset_out));
943                 }
944                 break;
945         }
946         }
947 }
948
949         /* align toward zero */
950 static inline long long align(long long x, int align)
951 {
952         int sign = x < 0;
953
954         if (sign)
955                 x = -x;
956
957         x -= x % align;
958
959         if (sign)
960                 x = -x;
961
962         return x;
963 }
964
965         /* remember, this gets called from another thread. */
966 void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size)
967 {
968         const int blocksize = 188;
969         unsigned int max = align(10*1024*1024, blocksize);
970         current_offset = align(current_offset, blocksize);
971         
972         if (!m_cue)
973         {
974                 eDebug("no cue sheet. forcing normal play");
975                 start = current_offset;
976                 size = max;
977                 return;
978         }
979
980         m_cue->m_lock.RdLock();
981         if (!m_cue->m_decoding_demux)
982         {
983                 start = current_offset;
984                 size = max;
985                 eDebug("getNextSourceSpan, no decoding demux. forcing normal play");
986                 m_cue->m_lock.Unlock();
987                 return;
988         }
989
990         if (m_skipmode_n)
991         {
992                 eDebug("skipmode %d:%d", m_skipmode_m, m_skipmode_n);
993                 max = align(m_skipmode_n, blocksize);
994         }
995
996         eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m);
997
998         current_offset += align(m_skipmode_m, blocksize);
999
1000         while (!m_cue->m_seek_requests.empty())
1001         {
1002                 std::pair<int, pts_t> seek = m_cue->m_seek_requests.front();
1003                 m_cue->m_lock.Unlock();
1004                 m_cue->m_lock.WrLock();
1005                 m_cue->m_seek_requests.pop_front();
1006                 m_cue->m_lock.Unlock();
1007                 m_cue->m_lock.RdLock();
1008                 int relative = seek.first;
1009                 pts_t pts = seek.second;
1010
1011                 pts_t now = 0;
1012                 if (relative)
1013                 {
1014                         if (!m_cue->m_decoder)
1015                         {
1016                                 eDebug("no decoder - can't seek relative");
1017                                 continue;
1018                         }
1019                         if (m_cue->m_decoder->getPTS(0, now))
1020                         {
1021                                 eDebug("decoder getPTS failed, can't seek relative");
1022                                 continue;
1023                         }
1024                         if (getCurrentPosition(m_cue->m_decoding_demux, now, 1))
1025                         {
1026                                 eDebug("seekTo: getCurrentPosition failed!");
1027                                 continue;
1028                         }
1029                 } else if (pts < 0) /* seek relative to end */
1030                 {
1031                         pts_t len;
1032                         if (!getLength(len))
1033                         {
1034                                 eDebug("seeking relative to end. len=%lld, seek = %lld", len, pts);
1035                                 pts += len;
1036                         } else
1037                         {
1038                                 eWarning("getLength failed - can't seek relative to end!");
1039                                 continue;
1040                         }
1041                 }
1042                 
1043                 if (relative == 1) /* pts relative */
1044                 {
1045                         pts += now;
1046                         if (pts < 0)
1047                                 pts = 0;
1048                 }
1049
1050                 if (relative != 2)
1051                         if (pts < 0)
1052                                 pts = 0;
1053                 
1054                 if (relative == 2) /* AP relative */
1055                 {
1056                         eDebug("AP relative seeking: %lld, at %lld", pts, now);
1057                         pts_t nextap;
1058                         if (m_tstools.getNextAccessPoint(nextap, now, pts))
1059                         {
1060                                 pts = now;
1061                                 eDebug("AP relative seeking failed!");
1062                         } else
1063                         {
1064                                 eDebug("next ap is %llx\n", pts);
1065                                 pts = nextap;
1066                         }
1067                 }
1068                 
1069                 off_t offset = 0;
1070                 if (m_tstools.getOffset(offset, pts))
1071                 {
1072                         eDebug("get offset for pts=%lld failed!", pts);
1073                         continue;
1074                 }
1075
1076                 eDebug("ok, resolved skip (rel: %d, diff %lld), now at %08llx", relative, pts, offset);
1077                 current_offset = align(offset, blocksize); /* in case tstools return non-aligned offset */
1078         }
1079
1080         m_cue->m_lock.Unlock();
1081
1082         for (std::list<std::pair<off_t, off_t> >::const_iterator i(m_source_span.begin()); i != m_source_span.end(); ++i)
1083         {
1084                 long long aligned_start = align(i->first, blocksize);
1085                 long long aligned_end = align(i->second, blocksize);
1086         
1087                 if ((current_offset >= aligned_start) && (current_offset < aligned_end))
1088                 {
1089                         start = current_offset;
1090                                 /* max can not exceed max(size_t). aligned_end - current_offset, however, can. */
1091                         if ((aligned_end - current_offset) > max)
1092                                 size = max;
1093                         else
1094                                 size = aligned_end - current_offset;
1095                         eDebug("HIT, %lld < %lld < %lld, size: %d", i->first, current_offset, i->second, size);
1096                         return;
1097                 }
1098                 if (current_offset < aligned_start)
1099                 {
1100                                 /* ok, our current offset is in an 'out' zone. */
1101                         if ((m_skipmode_m >= 0) || (i == m_source_span.begin()))
1102                         {
1103                                         /* in normal playback, just start at the next zone. */
1104                                 start = i->first;
1105                                 
1106                                         /* size is not 64bit! */
1107                                 if ((i->second - i->first) > max)
1108                                         size = max;
1109                                 else
1110                                         size = aligned_end - aligned_start;
1111
1112                                 eDebug("skip");
1113                                 if (m_skipmode_m < 0)
1114                                 {
1115                                         eDebug("reached SOF");
1116                                                 /* reached SOF */
1117                                         m_skipmode_m = 0;
1118                                         m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1119                                 }
1120                         } else
1121                         {
1122                                         /* when skipping reverse, however, choose the zone before. */
1123                                 --i;
1124                                 eDebug("skip to previous block, which is %llx..%llx", i->first, i->second);
1125                                 size_t len;
1126                                 
1127                                 if ((i->second - i->first) > max)
1128                                         len = max;
1129                                 else
1130                                         len = aligned_end - aligned_start;
1131
1132                                 start = aligned_end - len;
1133                                 eDebug("skipping to %llx, %d", start, len);
1134                         }
1135                         
1136                         eDebug("result: %llx, %x (%llx %llx)", start, size, aligned_start, aligned_end);
1137                         return;
1138                 }
1139         }
1140         
1141         if ((current_offset < -m_skipmode_m) && (m_skipmode_m < 0))
1142         {
1143                 eDebug("reached SOF");
1144                 m_skipmode_m = 0;
1145                 m_pvr_thread->sendEvent(eFilePushThread::evtUser);
1146         }
1147         
1148         start = current_offset;
1149         size = max;
1150
1151         eDebug("END OF CUESHEET. (%08llx, %d)", start, size);
1152         return;
1153 }
1154
1155 void eDVBChannel::AddUse()
1156 {
1157         if (++m_use_count > 1 && m_state == state_last_instance)
1158         {
1159                 m_state = state_ok;
1160                 m_stateChanged(this);
1161         }
1162 }
1163
1164 void eDVBChannel::ReleaseUse()
1165 {
1166         if (!--m_use_count)
1167         {
1168                 m_state = state_release;
1169                 m_stateChanged(this);
1170         }
1171         else if (m_use_count == 1)
1172         {
1173                 m_state = state_last_instance;
1174                 m_stateChanged(this);
1175         }
1176 }
1177
1178 RESULT eDVBChannel::setChannel(const eDVBChannelID &channelid, ePtr<iDVBFrontendParameters> &feparm)
1179 {
1180         if (m_channel_id)
1181                 m_mgr->removeChannel(this);
1182                 
1183         if (!channelid)
1184                 return 0;
1185
1186         if (!m_frontend)
1187         {
1188                 eDebug("no frontend to tune!");
1189                 return -ENODEV;
1190         }
1191         
1192         m_channel_id = channelid;
1193         m_mgr->addChannel(channelid, this);
1194         m_state = state_tuning;
1195                         /* if tuning fails, shutdown the channel immediately. */
1196         int res;
1197         res = m_frontend->get().tune(*feparm);
1198         m_current_frontend_parameters = feparm;
1199         
1200         if (res)
1201         {
1202                 m_state = state_release;
1203                 m_stateChanged(this);
1204                 return res;
1205         }
1206         
1207         return 0;
1208 }
1209
1210 RESULT eDVBChannel::connectStateChange(const Slot1<void,iDVBChannel*> &stateChange, ePtr<eConnection> &connection)
1211 {
1212         connection = new eConnection((iDVBChannel*)this, m_stateChanged.connect(stateChange));
1213         return 0;
1214 }
1215
1216 RESULT eDVBChannel::connectEvent(const Slot2<void,iDVBChannel*,int> &event, ePtr<eConnection> &connection)
1217 {
1218         connection = new eConnection((iDVBChannel*)this, m_event.connect(event));
1219         return 0;
1220 }
1221
1222 RESULT eDVBChannel::getState(int &state)
1223 {
1224         state = m_state;
1225         return 0;
1226 }
1227
1228 RESULT eDVBChannel::setCIRouting(const eDVBCIRouting &routing)
1229 {
1230         return -1;
1231 }
1232
1233 RESULT eDVBChannel::getDemux(ePtr<iDVBDemux> &demux, int cap)
1234 {
1235         ePtr<eDVBAllocatedDemux> &our_demux = (cap & capDecode) ? m_decoder_demux : m_demux;
1236         
1237         if (!our_demux)
1238         {
1239                 demux = 0;
1240                 
1241                 if (m_mgr->allocateDemux(m_frontend ? (eDVBRegisteredFrontend*)*m_frontend : (eDVBRegisteredFrontend*)0, our_demux, cap))
1242                         return -1;
1243         }
1244         
1245         demux = *our_demux;
1246                 /* don't hold a reference to the decoding demux, we don't need it. */
1247                 
1248                 /* FIXME: by dropping the 'allocated demux' in favour of the 'iDVBDemux',
1249                    the refcount is lost. thus, decoding demuxes are never allocated. 
1250                    
1251                    this poses a big problem for PiP. */
1252         if (cap & capDecode)
1253                 our_demux = 0;
1254         return 0;
1255 }
1256
1257 RESULT eDVBChannel::getFrontend(ePtr<iDVBFrontend> &frontend)
1258 {
1259         frontend = 0;
1260         if (!m_frontend)
1261                 return -ENODEV;
1262         frontend = &m_frontend->get();
1263         if (frontend)
1264                 return 0;
1265         return -ENODEV;
1266 }
1267
1268 RESULT eDVBChannel::getCurrentFrontendParameters(ePtr<iDVBFrontendParameters> &param)
1269 {
1270         param = m_current_frontend_parameters;
1271         return 0;
1272 }
1273
1274 RESULT eDVBChannel::playFile(const char *file)
1275 {
1276         ASSERT(!m_frontend);
1277         if (m_pvr_thread)
1278         {
1279                 m_pvr_thread->stop();
1280                 delete m_pvr_thread;
1281                 m_pvr_thread = 0;
1282         }
1283         
1284         m_tstools.openFile(file);
1285         
1286                 /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST,
1287                    THEN DO A REAL FIX HERE! */
1288         
1289                 /* (this codepath needs to be improved anyway.) */
1290 #if HAVE_DVB_API_VERSION < 3
1291         m_pvr_fd_dst = open("/dev/pvr", O_WRONLY);
1292 #else
1293         m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY);
1294 #endif
1295         if (m_pvr_fd_dst < 0)
1296         {
1297                 eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved.
1298                 return -ENODEV;
1299         }
1300
1301         m_pvr_thread = new eDVBChannelFilePush();
1302         m_pvr_thread->enablePVRCommit(1);
1303         m_pvr_thread->setStreamMode(1);
1304         m_pvr_thread->setScatterGather(this);
1305
1306         if (m_pvr_thread->start(file, m_pvr_fd_dst))
1307         {
1308                 delete m_pvr_thread;
1309                 m_pvr_thread = 0;
1310                 eDebug("can't open PVR file %s (%m)", file);
1311                 return -ENOENT;
1312         }
1313         CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent);
1314
1315         m_state = state_ok;
1316         m_stateChanged(this);
1317
1318         return 0;
1319 }
1320
1321 void eDVBChannel::stopFile()
1322 {
1323         if (m_pvr_thread)
1324         {
1325                 m_pvr_thread->stop();
1326                 ::close(m_pvr_fd_dst);
1327                 delete m_pvr_thread;
1328                 m_pvr_thread = 0;
1329         }
1330 }
1331
1332 void eDVBChannel::setCueSheet(eCueSheet *cuesheet)
1333 {
1334         m_conn_cueSheetEvent = 0;
1335         m_cue = cuesheet;
1336         if (m_cue)
1337                 m_cue->connectEvent(slot(*this, &eDVBChannel::cueSheetEvent), m_conn_cueSheetEvent);
1338 }
1339
1340 RESULT eDVBChannel::getLength(pts_t &len)
1341 {
1342         return m_tstools.calcLen(len);
1343 }
1344
1345 RESULT eDVBChannel::getCurrentPosition(iDVBDemux *decoding_demux, pts_t &pos, int mode)
1346 {
1347         if (!decoding_demux)
1348                 return -1;
1349         
1350         pts_t now;
1351         
1352         int r;
1353         
1354         if (mode == 0) /* demux */
1355         {
1356                 r = decoding_demux->getSTC(now, 0);
1357                 if (r)
1358                 {
1359                         eDebug("demux getSTC failed");
1360                         return -1;
1361                 }
1362         } else
1363                 now = pos; /* fixup supplied */
1364         
1365         off_t off = 0; /* TODO: fixme */
1366         r = m_tstools.fixupPTS(off, now);
1367         if (r)
1368         {
1369                 eDebug("fixup PTS failed");
1370                 return -1;
1371         }
1372         
1373         pos = now;
1374         
1375         return 0;
1376 }
1377
1378 void eDVBChannel::flushPVR(iDVBDemux *decoding_demux)
1379 {
1380                         /* when seeking, we have to ensure that all buffers are flushed.
1381                            there are basically 3 buffers:
1382                            a.) the filepush's internal buffer
1383                            b.) the PVR buffer (before demux)
1384                            c.) the ratebuffer (after demux)
1385                            
1386                            it's important to clear them in the correct order, otherwise
1387                            the ratebuffer (for example) would immediately refill from
1388                            the not-yet-flushed PVR buffer.
1389                         */
1390
1391         m_pvr_thread->pause();
1392                 /* flush internal filepush buffer */
1393         m_pvr_thread->flush();
1394                 /* HACK: flush PVR buffer */
1395         ::ioctl(m_pvr_fd_dst, 0);
1396         
1397                 /* flush ratebuffers (video, audio) */
1398         if (decoding_demux)
1399                 decoding_demux->flush();
1400
1401                 /* demux will also flush all decoder.. */
1402                 /* resume will re-query the SG */
1403         m_pvr_thread->resume();
1404 }
1405
1406 DEFINE_REF(eCueSheet);
1407
1408 eCueSheet::eCueSheet()
1409 {
1410         m_skipmode_ratio = 0;
1411 }
1412
1413 void eCueSheet::seekTo(int relative, const pts_t &pts)
1414 {
1415         m_lock.WrLock();
1416         m_seek_requests.push_back(std::pair<int, pts_t>(relative, pts));
1417         m_lock.Unlock();
1418         m_event(evtSeek);
1419 }
1420         
1421 void eCueSheet::clear()
1422 {
1423         m_lock.WrLock();
1424         m_spans.clear();
1425         m_lock.Unlock();
1426 }
1427
1428 void eCueSheet::addSourceSpan(const pts_t &begin, const pts_t &end)
1429 {
1430         m_lock.WrLock();
1431         m_spans.push_back(std::pair<pts_t, pts_t>(begin, end));
1432         m_lock.Unlock();
1433 }
1434
1435 void eCueSheet::commitSpans()
1436 {
1437         m_event(evtSpanChanged);
1438 }
1439
1440 void eCueSheet::setSkipmode(const pts_t &ratio)
1441 {
1442         m_lock.WrLock();
1443         m_skipmode_ratio = ratio;
1444         m_lock.Unlock();
1445         m_event(evtSkipmode);
1446 }
1447
1448 void eCueSheet::setDecodingDemux(iDVBDemux *demux, iTSMPEGDecoder *decoder)
1449 {
1450         m_decoding_demux = demux;
1451         m_decoder = decoder;
1452 }
1453
1454 RESULT eCueSheet::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &connection)
1455 {
1456         connection = new eConnection(this, m_event.connect(event));
1457         return 0;
1458 }