Remove check legacy dvbapi version.
[vuplus_dvbapp] / lib / dvb / demux.cpp
1 #include <stdio.h>
2 #include <fcntl.h>
3 #include <sys/ioctl.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <signal.h>
7
8 // #define FUZZING 1
9
10 #if FUZZING
11                 /* change every 1:FUZZING_PROPABILITY byte */
12 #define FUZZING_PROPABILITY 100
13 #endif
14
15 #include <linux/dvb/dmx.h>
16
17 #ifndef DMX_ADD_PID
18 #define DMX_ADD_PID             _IOW('o', 51, __u16)
19 #define DMX_REMOVE_PID          _IOW('o', 52, __u16)
20 #endif
21
22 #include "crc32.h"
23
24 #include <lib/base/eerror.h>
25 #include <lib/dvb/idvb.h>
26 #include <lib/dvb/demux.h>
27 #include <lib/dvb/esection.h>
28 #include <lib/dvb/decoder.h>
29
30 eDVBDemux::eDVBDemux(int adapter, int demux): adapter(adapter), demux(demux)
31 {
32         m_dvr_busy = 0;
33 }
34
35 eDVBDemux::~eDVBDemux()
36 {
37 }
38
39 int eDVBDemux::openDemux(void)
40 {
41         char filename[128];
42         snprintf(filename, 128, "/dev/dvb/adapter%d/demux%d", adapter, demux);
43         return ::open(filename, O_RDWR);
44 }
45
46 int eDVBDemux::openDVR(int flags)
47 {
48         char filename[128];
49         snprintf(filename, 128, "/dev/dvb/adapter%d/dvr%d", adapter, demux);
50         return ::open(filename, flags);
51 }
52
53 DEFINE_REF(eDVBDemux)
54
55 RESULT eDVBDemux::setSourceFrontend(int fenum)
56 {
57         int fd = openDemux();
58         if (fd < 0) return -1;
59         int n = DMX_SOURCE_FRONT0 + fenum;
60         int res = ::ioctl(fd, DMX_SET_SOURCE, &n);
61         if (res)
62                 eDebug("DMX_SET_SOURCE failed! - %m");
63         else
64                 source = fenum;
65         ::close(fd);
66         return res;
67 }
68
69 RESULT eDVBDemux::setSourcePVR(int pvrnum)
70 {
71         int fd = openDemux();
72         if (fd < 0) return -1;
73         int n = DMX_SOURCE_DVR0 + pvrnum;
74         int res = ::ioctl(fd, DMX_SET_SOURCE, &n);
75         source = -1;
76         ::close(fd);
77         return res;
78 }
79
80 RESULT eDVBDemux::createSectionReader(eMainloop *context, ePtr<iDVBSectionReader> &reader)
81 {
82         RESULT res;
83         reader = new eDVBSectionReader(this, context, res);
84         if (res)
85                 reader = 0;
86         return res;
87 }
88
89 RESULT eDVBDemux::createPESReader(eMainloop *context, ePtr<iDVBPESReader> &reader)
90 {
91         RESULT res;
92         reader = new eDVBPESReader(this, context, res);
93         if (res)
94                 reader = 0;
95         return res;
96 }
97
98 RESULT eDVBDemux::createTSRecorder(ePtr<iDVBTSRecorder> &recorder)
99 {
100         if (m_dvr_busy)
101                 return -EBUSY;
102         recorder = new eDVBTSRecorder(this);
103         return 0;
104 }
105
106 RESULT eDVBDemux::getMPEGDecoder(ePtr<iTSMPEGDecoder> &decoder, int index)
107 {
108         decoder = new eTSMPEGDecoder(this, index);
109         return 0;
110 }
111
112 RESULT eDVBDemux::getSTC(pts_t &pts, int num)
113 {
114         int fd = openDemux();
115         
116         if (fd < 0)
117                 return -ENODEV;
118
119         struct dmx_stc stc;
120         stc.num = num;
121         stc.base = 1;
122         
123         if (ioctl(fd, DMX_GET_STC, &stc) < 0)
124         {
125                 eDebug("DMX_GET_STC failed!");
126                 ::close(fd);
127                 return -1;
128         }
129         
130         pts = stc.stc;
131         
132         eDebug("DMX_GET_STC - %lld", pts);
133         
134         ::close(fd);
135         return 0;
136 }
137
138 RESULT eDVBDemux::flush()
139 {
140         // FIXME: implement flushing the PVR queue here.
141         
142         m_event(evtFlush);
143         return 0;
144 }
145
146 RESULT eDVBDemux::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &conn)
147 {
148         conn = new eConnection(this, m_event.connect(event));
149         return 0;
150 }
151
152 void eDVBSectionReader::data(int)
153 {
154         __u8 data[4096]; // max. section size
155         int r;
156         r = ::read(fd, data, 4096);
157 #if FUZZING
158         int j;
159         for (j = 0; j < r; ++j)
160         {
161                 if (!(rand()%FUZZING_PROPABILITY))
162                         data[j] ^= rand();
163         }
164 #endif  
165         if(r < 0)
166         {
167                 eWarning("ERROR reading section - %m\n");
168                 return;
169         }
170         if (checkcrc)
171         {
172                         // this check should never happen unless the driver is crappy!
173                 unsigned int c;
174                 if ((c = crc32((unsigned)-1, data, r)))
175                 {
176                         eDebug("crc32 failed! is %x\n", c);
177                         return;
178                 }
179         }
180         if (active)
181                 read(data);
182         else
183                 eDebug("data.. but not active");
184 }
185
186 eDVBSectionReader::eDVBSectionReader(eDVBDemux *demux, eMainloop *context, RESULT &res): demux(demux)
187 {
188         char filename[128];
189         fd = demux->openDemux();
190         
191         if (fd >= 0)
192         {
193                 notifier=eSocketNotifier::create(context, fd, eSocketNotifier::Read, false);
194                 CONNECT(notifier->activated, eDVBSectionReader::data);
195                 res = 0;
196         } else
197         {
198                 perror(filename);
199                 res = errno;
200         }
201 }
202
203 DEFINE_REF(eDVBSectionReader)
204
205 eDVBSectionReader::~eDVBSectionReader()
206 {
207         if (fd >= 0)
208                 ::close(fd);
209 }
210
211 RESULT eDVBSectionReader::setBufferSize(int size)
212 {
213         int res=::ioctl(fd, DMX_SET_BUFFER_SIZE, size);
214         if (res < 0)
215                 eDebug("eDVBSectionReader DMX_SET_BUFFER_SIZE failed(%m)");
216         return res;
217 }
218
219 RESULT eDVBSectionReader::start(const eDVBSectionFilterMask &mask)
220 {
221         RESULT res;
222         if (fd < 0)
223                 return -ENODEV;
224
225         notifier->start();
226         dmx_sct_filter_params sct;
227         sct.pid     = mask.pid;
228         sct.timeout = 0;
229         sct.flags   = DMX_IMMEDIATE_START;
230 #if !FUZZING
231         if (mask.flags & eDVBSectionFilterMask::rfCRC)
232         {
233                 sct.flags |= DMX_CHECK_CRC;
234                 checkcrc = 1;
235         } else
236 #endif
237                 checkcrc = 0;
238         
239         memcpy(sct.filter.filter, mask.data, DMX_FILTER_SIZE);
240         memcpy(sct.filter.mask, mask.mask, DMX_FILTER_SIZE);
241         memcpy(sct.filter.mode, mask.mode, DMX_FILTER_SIZE);
242         setBufferSize(8192*8);
243         
244         res = ::ioctl(fd, DMX_SET_FILTER, &sct);
245         if (!res)
246         {
247                 active = 1;
248         }
249         return res;
250 }
251
252 RESULT eDVBSectionReader::stop()
253 {
254         if (!active)
255                 return -1;
256
257         active=0;
258         ::ioctl(fd, DMX_STOP);
259         notifier->stop();
260
261         return 0;
262 }
263
264 RESULT eDVBSectionReader::connectRead(const Slot1<void,const __u8*> &r, ePtr<eConnection> &conn)
265 {
266         conn = new eConnection(this, read.connect(r));
267         return 0;
268 }
269
270 void eDVBPESReader::data(int)
271 {
272         while (1)
273         {
274                 __u8 buffer[16384];
275                 int r;
276                 r = ::read(m_fd, buffer, 16384);
277                 if (!r)
278                         return;
279                 if(r < 0)
280                 {
281                         if (errno == EAGAIN || errno == EINTR) /* ok */
282                                 return;
283                         eWarning("ERROR reading PES (fd=%d) - %m", m_fd);
284                         return;
285                 }
286
287                 if (m_active)
288                         m_read(buffer, r);
289                 else
290                         eWarning("PES reader not active");
291                 if (r != 16384)
292                         break;
293         }
294 }
295
296 eDVBPESReader::eDVBPESReader(eDVBDemux *demux, eMainloop *context, RESULT &res): m_demux(demux)
297 {
298         char filename[128];
299         m_fd = m_demux->openDemux();
300         
301         if (m_fd >= 0)
302         {
303                 setBufferSize(64*1024);
304                 ::fcntl(m_fd, F_SETFL, O_NONBLOCK);
305                 m_notifier = eSocketNotifier::create(context, m_fd, eSocketNotifier::Read, false);
306                 CONNECT(m_notifier->activated, eDVBPESReader::data);
307                 res = 0;
308         } else
309         {
310                 perror(filename);
311                 res = errno;
312         }
313 }
314
315 RESULT eDVBPESReader::setBufferSize(int size)
316 {
317         int res = ::ioctl(m_fd, DMX_SET_BUFFER_SIZE, size);
318         if (res < 0)
319                 eDebug("eDVBPESReader DMX_SET_BUFFER_SIZE failed(%m)");
320         return res;
321 }
322
323 DEFINE_REF(eDVBPESReader)
324
325 eDVBPESReader::~eDVBPESReader()
326 {
327         if (m_fd >= 0)
328                 ::close(m_fd);
329 }
330
331 RESULT eDVBPESReader::start(int pid)
332 {
333         RESULT res;
334         if (m_fd < 0)
335                 return -ENODEV;
336
337         m_notifier->start();
338
339         dmx_pes_filter_params flt;
340         
341         flt.pes_type = DMX_PES_OTHER;
342         flt.pid     = pid;
343         flt.input   = DMX_IN_FRONTEND;
344         flt.output  = DMX_OUT_TAP;
345         
346         flt.flags   = DMX_IMMEDIATE_START;
347
348         res = ::ioctl(m_fd, DMX_SET_PES_FILTER, &flt);
349         
350         if (res)
351                 eWarning("PES filter: DMX_SET_PES_FILTER - %m");
352         if (!res)
353                 m_active = 1;
354         return res;
355 }
356
357 RESULT eDVBPESReader::stop()
358 {
359         if (!m_active)
360                 return -1;
361
362         m_active=0;
363         ::ioctl(m_fd, DMX_STOP);
364         m_notifier->stop();
365
366         return 0;
367 }
368
369 RESULT eDVBPESReader::connectRead(const Slot2<void,const __u8*,int> &r, ePtr<eConnection> &conn)
370 {
371         conn = new eConnection(this, m_read.connect(r));
372         return 0;
373 }
374
375 eDVBRecordFileThread::eDVBRecordFileThread()
376         :eFilePushThread(IOPRIO_CLASS_RT, 7), m_ts_parser(m_stream_info)
377 {
378         m_current_offset = 0;
379 }
380
381 void eDVBRecordFileThread::setTimingPID(int pid, int type)
382 {
383         m_ts_parser.setPid(pid, type);
384 }
385
386 void eDVBRecordFileThread::startSaveMetaInformation(const std::string &filename)
387 {
388         m_stream_info.startSave(filename.c_str());
389 }
390
391 void eDVBRecordFileThread::stopSaveMetaInformation()
392 {
393         m_stream_info.stopSave();
394 }
395
396 void eDVBRecordFileThread::enableAccessPoints(bool enable)
397 {
398         m_ts_parser.enableAccessPoints(enable);
399 }
400
401 int eDVBRecordFileThread::getLastPTS(pts_t &pts)
402 {
403         return m_ts_parser.getLastPTS(pts);
404 }
405
406 int eDVBRecordFileThread::filterRecordData(const unsigned char *data, int len, size_t &current_span_remaining)
407 {
408         m_ts_parser.parseData(m_current_offset, data, len);
409         
410         m_current_offset += len;
411         
412         return len;
413 }
414
415 DEFINE_REF(eDVBTSRecorder);
416
417 eDVBTSRecorder::eDVBTSRecorder(eDVBDemux *demux): m_demux(demux)
418 {
419         m_running = 0;
420         m_target_fd = -1;
421         m_thread = new eDVBRecordFileThread();
422         CONNECT(m_thread->m_event, eDVBTSRecorder::filepushEvent);
423 }
424
425 eDVBTSRecorder::~eDVBTSRecorder()
426 {
427         stop();
428         delete m_thread;
429 }
430
431 RESULT eDVBTSRecorder::start()
432 {
433         std::map<int,int>::iterator i(m_pids.begin());
434
435         if (m_running)
436                 return -1;
437         
438         if (m_target_fd == -1)
439                 return -2;
440
441         if (i == m_pids.end())
442                 return -3;
443
444         char filename[128];
445         snprintf(filename, 128, "/dev/dvb/adapter%d/demux%d", m_demux->adapter, m_demux->demux);
446
447         m_source_fd = ::open(filename, O_RDONLY);
448         
449         if (m_source_fd < 0)
450         {
451                 eDebug("FAILED to open demux (%s) in ts recoder (%m)", filename);
452                 return -3;
453         }
454
455         setBufferSize(1024*1024);
456
457         dmx_pes_filter_params flt;
458         flt.pes_type = DMX_PES_OTHER;
459         flt.output  = DMX_OUT_TSDEMUX_TAP;
460         flt.pid     = i->first;
461         ++i;
462         flt.input   = DMX_IN_FRONTEND;
463         flt.flags   = 0;
464         int res = ::ioctl(m_source_fd, DMX_SET_PES_FILTER, &flt);
465         if (res)
466         {
467                 eDebug("DMX_SET_PES_FILTER: %m");
468                 ::close(m_source_fd);
469                 m_source_fd = -1;
470                 return -3;
471         }
472         
473         ::ioctl(m_source_fd, DMX_START);
474
475         if (m_target_filename != "")
476                 m_thread->startSaveMetaInformation(m_target_filename);
477         
478         m_thread->start(m_source_fd, m_target_fd);
479         m_running = 1;
480
481         while (i != m_pids.end()) {
482                 startPID(i->first);
483                 ++i;
484         }
485
486         return 0;
487 }
488
489 RESULT eDVBTSRecorder::setBufferSize(int size)
490 {
491         int res = ::ioctl(m_source_fd, DMX_SET_BUFFER_SIZE, size);
492         if (res < 0)
493                 eDebug("eDVBTSRecorder DMX_SET_BUFFER_SIZE failed(%m)");
494         return res;
495 }
496
497 RESULT eDVBTSRecorder::addPID(int pid)
498 {
499         if (m_pids.find(pid) != m_pids.end())
500                 return -1;
501         
502         m_pids.insert(std::pair<int,int>(pid, -1));
503         if (m_running)
504                 startPID(pid);
505         return 0;
506 }
507
508 RESULT eDVBTSRecorder::removePID(int pid)
509 {
510         if (m_pids.find(pid) == m_pids.end())
511                 return -1;
512                 
513         if (m_running)
514                 stopPID(pid);
515         
516         m_pids.erase(pid);
517         return 0;
518 }
519
520 RESULT eDVBTSRecorder::setTimingPID(int pid, int type)
521 {
522         m_thread->setTimingPID(pid, type);
523         return 0;
524 }
525
526 RESULT eDVBTSRecorder::setTargetFD(int fd)
527 {
528         m_target_fd = fd;
529         return 0;
530 }
531
532 RESULT eDVBTSRecorder::setTargetFilename(const char *filename)
533 {
534         m_target_filename = filename;
535
536         std::string target_path = m_target_filename;
537         std::string::size_type filePos = target_path.rfind('/');
538         m_thread->setTSPath(target_path.erase(filePos));
539
540         return 0;
541 }
542
543 RESULT eDVBTSRecorder::enableAccessPoints(bool enable)
544 {
545         m_thread->enableAccessPoints(enable);
546         return 0;
547 }
548
549 RESULT eDVBTSRecorder::setBoundary(off_t max)
550 {
551         return -1; // not yet implemented
552 }
553
554 RESULT eDVBTSRecorder::setTimeshift(bool enable)
555 {
556         m_thread->setTimeshift(enable);
557 }
558
559 RESULT eDVBTSRecorder::stop()
560 {
561         int state=3;
562
563         for (std::map<int,int>::iterator i(m_pids.begin()); i != m_pids.end(); ++i)
564                 stopPID(i->first);
565
566         if (!m_running)
567                 return -1;
568
569         /* workaround for record thread stop */
570         if (m_source_fd >= 0)
571         {
572                 if (::ioctl(m_source_fd, DMX_STOP) < 0)
573                         perror("DMX_STOP");
574                 else
575                         state &= ~1;
576
577                 if (::close(m_source_fd) < 0)
578                         perror("close");
579                 else
580                         state &= ~2;
581                 m_source_fd = -1;
582         }
583
584         m_thread->stop();
585
586         if (state & 3)
587         {
588                 if (m_source_fd >= 0)
589                 {
590                         ::close(m_source_fd);
591                         m_source_fd = -1;
592                 }
593         }
594
595         m_running = 0;
596         m_thread->stopSaveMetaInformation();
597         return 0;
598 }
599
600 RESULT eDVBTSRecorder::getCurrentPCR(pts_t &pcr)
601 {
602         if (!m_running)
603                 return 0;
604         if (!m_thread)
605                 return 0;
606                 /* XXX: we need a lock here */
607
608                         /* we don't filter PCR data, so just use the last received PTS, which is not accurate, but better than nothing */
609         return m_thread->getLastPTS(pcr);
610 }
611
612 RESULT eDVBTSRecorder::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &conn)
613 {
614         conn = new eConnection(this, m_event.connect(event));
615         return 0;
616 }
617
618 RESULT eDVBTSRecorder::startPID(int pid)
619 {
620         while(true) {
621                 __u16 p = pid;
622                 if (::ioctl(m_source_fd, DMX_ADD_PID, &p) < 0) {
623                         perror("DMX_ADD_PID");
624                         if (errno == EAGAIN || errno == EINTR) {
625                                 eDebug("retry!");
626                                 continue;
627                         }
628                 } else
629                         m_pids[pid] = 1;
630                 break;
631         }
632         return 0;
633 }
634
635 void eDVBTSRecorder::stopPID(int pid)
636 {
637         if (m_pids[pid] != -1)
638         {
639                 while(true) {
640                         __u16 p = pid;
641                         if (::ioctl(m_source_fd, DMX_REMOVE_PID, &p) < 0) {
642                                 perror("DMX_REMOVE_PID");
643                                 if (errno == EAGAIN || errno == EINTR) {
644                                         eDebug("retry!");
645                                         continue;
646                                 }
647                         }
648                         break;
649                 }
650         }
651         m_pids[pid] = -1;
652 }
653
654 void eDVBTSRecorder::filepushEvent(int event)
655 {
656         switch (event)
657         {
658         case eFilePushThread::evtWriteError:
659                 m_event(eventWriteError);
660                 break;
661         }
662 }