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