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