lib/gdi/font.h/cpp: another try to fix top/bottom reordering for arabic language
[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::start(const eDVBSectionFilterMask &mask)
251 {
252         RESULT res;
253         if (fd < 0)
254                 return -ENODEV;
255
256         notifier->start();
257 #if HAVE_DVB_API_VERSION < 3
258         dmxSctFilterParams sct;
259 #else
260         dmx_sct_filter_params sct;
261 #endif
262         sct.pid     = mask.pid;
263         sct.timeout = 0;
264 #if HAVE_DVB_API_VERSION < 3
265         sct.flags   = 0;
266 #else
267         sct.flags   = DMX_IMMEDIATE_START;
268 #endif
269 #if !FUZZING
270         if (mask.flags & eDVBSectionFilterMask::rfCRC)
271         {
272                 sct.flags |= DMX_CHECK_CRC;
273                 checkcrc = 1;
274         } else
275 #endif
276                 checkcrc = 0;
277         
278         memcpy(sct.filter.filter, mask.data, DMX_FILTER_SIZE);
279         memcpy(sct.filter.mask, mask.mask, DMX_FILTER_SIZE);
280 #if HAVE_DVB_API_VERSION >= 3
281         memcpy(sct.filter.mode, mask.mode, DMX_FILTER_SIZE);
282         if (::ioctl(fd, DMX_SET_BUFFER_SIZE, 8192*8) < 0)
283                 eDebug("DMX_SET_BUFFER_SIZE failed(%m)");
284 #endif
285         
286         res = ::ioctl(fd, DMX_SET_FILTER, &sct);
287         if (!res)
288         {
289 #if HAVE_DVB_API_VERSION < 3
290                 res = ::ioctl(fd, DMX_SET_NEGFILTER_MASK, mask.mode);
291                 if (!res)
292                 {
293                         res = ::ioctl(fd, DMX_START, 0);
294                         if (!res)
295                                 active = 1;
296                 }
297 #else
298                 active = 1;
299 #endif
300         }
301         return res;
302 }
303
304 RESULT eDVBSectionReader::stop()
305 {
306         if (!active)
307                 return -1;
308
309         active=0;
310         ::ioctl(fd, DMX_STOP);
311         notifier->stop();
312
313         return 0;
314 }
315
316 RESULT eDVBSectionReader::connectRead(const Slot1<void,const __u8*> &r, ePtr<eConnection> &conn)
317 {
318         conn = new eConnection(this, read.connect(r));
319         return 0;
320 }
321
322 void eDVBPESReader::data(int)
323 {
324         while (1)
325         {
326                 __u8 buffer[16384];
327                 int r;
328                 r = ::read(m_fd, buffer, 16384);
329                 if (!r)
330                         return;
331                 if(r < 0)
332                 {
333                         if (errno == EAGAIN || errno == EINTR) /* ok */
334                                 return;
335                         eWarning("ERROR reading PES (fd=%d) - %m", m_fd);
336                         return;
337                 }
338
339                 if (m_active)
340                         m_read(buffer, r);
341                 else
342                         eWarning("PES reader not active");
343                 if (r != 16384)
344                         break;
345         }
346 }
347
348 eDVBPESReader::eDVBPESReader(eDVBDemux *demux, eMainloop *context, RESULT &res): m_demux(demux)
349 {
350         char filename[128];
351         m_fd = m_demux->openDemux();
352         
353         if (m_fd >= 0)
354         {
355                 ::ioctl(m_fd, DMX_SET_BUFFER_SIZE, 64*1024);
356                 ::fcntl(m_fd, F_SETFL, O_NONBLOCK);
357                 m_notifier = eSocketNotifier::create(context, m_fd, eSocketNotifier::Read, false);
358                 CONNECT(m_notifier->activated, eDVBPESReader::data);
359                 res = 0;
360         } else
361         {
362                 perror(filename);
363                 res = errno;
364         }
365 }
366
367 DEFINE_REF(eDVBPESReader)
368
369 eDVBPESReader::~eDVBPESReader()
370 {
371         if (m_fd >= 0)
372                 ::close(m_fd);
373 }
374
375 RESULT eDVBPESReader::start(int pid)
376 {
377         RESULT res;
378         if (m_fd < 0)
379                 return -ENODEV;
380
381         m_notifier->start();
382
383 #if HAVE_DVB_API_VERSION < 3
384         dmxPesFilterParams flt;
385         
386         flt.pesType = DMX_PES_OTHER;
387 #else
388         dmx_pes_filter_params flt;
389         
390         flt.pes_type = DMX_PES_OTHER;
391 #endif
392
393         flt.pid     = pid;
394         flt.input   = DMX_IN_FRONTEND;
395         flt.output  = DMX_OUT_TAP;
396         
397         flt.flags   = DMX_IMMEDIATE_START;
398
399         res = ::ioctl(m_fd, DMX_SET_PES_FILTER, &flt);
400         
401         if (res)
402                 eWarning("PES filter: DMX_SET_PES_FILTER - %m");
403         if (!res)
404                 m_active = 1;
405         return res;
406 }
407
408 RESULT eDVBPESReader::stop()
409 {
410         if (!m_active)
411                 return -1;
412
413         m_active=0;
414         ::ioctl(m_fd, DMX_STOP);
415         m_notifier->stop();
416
417         return 0;
418 }
419
420 RESULT eDVBPESReader::connectRead(const Slot2<void,const __u8*,int> &r, ePtr<eConnection> &conn)
421 {
422         conn = new eConnection(this, m_read.connect(r));
423         return 0;
424 }
425
426 class eDVBRecordFileThread: public eFilePushThread
427 {
428 public:
429         eDVBRecordFileThread();
430         void setTimingPID(int pid, int type);
431         
432         void startSaveMetaInformation(const std::string &filename);
433         void stopSaveMetaInformation();
434         int getLastPTS(pts_t &pts);
435 protected:
436         int filterRecordData(const unsigned char *data, int len, size_t &current_span_remaining);
437 private:
438         eMPEGStreamParserTS m_ts_parser;
439         eMPEGStreamInformation m_stream_info;
440         off_t m_current_offset;
441         pts_t m_last_pcr; /* very approximate.. */
442         int m_pid;
443 };
444
445 eDVBRecordFileThread::eDVBRecordFileThread()
446         :eFilePushThread(IOPRIO_CLASS_RT, 7), m_ts_parser(m_stream_info)
447 {
448         m_current_offset = 0;
449 }
450
451 void eDVBRecordFileThread::setTimingPID(int pid, int type)
452 {
453         m_ts_parser.setPid(pid, type);
454 }
455
456 void eDVBRecordFileThread::startSaveMetaInformation(const std::string &filename)
457 {
458         m_stream_info.startSave(filename.c_str());
459 }
460
461 void eDVBRecordFileThread::stopSaveMetaInformation()
462 {
463         m_stream_info.stopSave();
464 }
465
466 int eDVBRecordFileThread::getLastPTS(pts_t &pts)
467 {
468         return m_ts_parser.getLastPTS(pts);
469 }
470
471 int eDVBRecordFileThread::filterRecordData(const unsigned char *data, int len, size_t &current_span_remaining)
472 {
473         m_ts_parser.parseData(m_current_offset, data, len);
474         
475         m_current_offset += len;
476         
477         return len;
478 }
479
480 DEFINE_REF(eDVBTSRecorder);
481
482 eDVBTSRecorder::eDVBTSRecorder(eDVBDemux *demux): m_demux(demux)
483 {
484         m_running = 0;
485         m_target_fd = -1;
486         m_thread = new eDVBRecordFileThread();
487   CONNECT(m_thread->m_event, eDVBTSRecorder::filepushEvent);
488 #ifndef HAVE_ADD_PID
489         m_demux->m_dvr_busy = 1;
490 #endif
491 }
492
493 eDVBTSRecorder::~eDVBTSRecorder()
494 {
495         stop();
496         delete m_thread;
497 #ifndef HAVE_ADD_PID
498         m_demux->m_dvr_busy = 0;
499 #endif
500 }
501
502 RESULT eDVBTSRecorder::start()
503 {
504         std::map<int,int>::iterator i(m_pids.begin());
505
506         if (m_running)
507                 return -1;
508         
509         if (m_target_fd == -1)
510                 return -2;
511
512         if (i == m_pids.end())
513                 return -3;
514
515         char filename[128];
516 #ifndef HAVE_ADD_PID
517 #if HAVE_DVB_API_VERSION < 3
518         snprintf(filename, 128, "/dev/dvb/card%d/dvr%d", m_demux->adapter, m_demux->demux);
519 #else
520         snprintf(filename, 128, "/dev/dvb/adapter%d/dvr%d", m_demux->adapter, m_demux->demux);
521 #endif
522         m_source_fd = ::open(filename, O_RDONLY);
523         
524         if (m_source_fd < 0)
525         {
526                 eDebug("FAILED to open dvr (%s) in ts recoder (%m)", filename);
527                 return -3;
528         }
529 #else
530         snprintf(filename, 128, "/dev/dvb/adapter%d/demux%d", m_demux->adapter, m_demux->demux);
531
532         m_source_fd = ::open(filename, O_RDONLY);
533         
534         if (m_source_fd < 0)
535         {
536                 eDebug("FAILED to open demux (%s) in ts recoder (%m)", filename);
537                 return -3;
538         }
539         
540         ::ioctl(m_source_fd, DMX_SET_BUFFER_SIZE, 1024*1024);
541
542         dmx_pes_filter_params flt;
543 #if HAVE_DVB_API_VERSION > 3
544         flt.pes_type = DMX_PES_OTHER;
545         flt.output  = DMX_OUT_TSDEMUX_TAP;
546 #else
547         flt.pes_type = (dmx_pes_type_t)DMX_TAP_TS;
548         flt.output  = DMX_OUT_TAP;
549 #endif
550         flt.pid     = i->first;
551         ++i;
552         flt.input   = DMX_IN_FRONTEND;
553         flt.flags   = 0;
554         int res = ::ioctl(m_source_fd, DMX_SET_PES_FILTER, &flt);
555         if (res)
556         {
557                 eDebug("DMX_SET_PES_FILTER: %m");
558                 ::close(m_source_fd);
559                 return -3;
560         }
561         
562         ::ioctl(m_source_fd, DMX_START);
563         
564 #endif
565
566         if (m_target_filename != "")
567                 m_thread->startSaveMetaInformation(m_target_filename);
568         
569         m_thread->start(m_source_fd, m_target_fd);
570         m_running = 1;
571
572         while (i != m_pids.end()) {
573                 startPID(i->first);
574                 ++i;
575         }
576
577         return 0;
578 }
579
580 RESULT eDVBTSRecorder::addPID(int pid)
581 {
582         if (m_pids.find(pid) != m_pids.end())
583                 return -1;
584         
585         m_pids.insert(std::pair<int,int>(pid, -1));
586         if (m_running)
587                 startPID(pid);
588         return 0;
589 }
590
591 RESULT eDVBTSRecorder::removePID(int pid)
592 {
593         if (m_pids.find(pid) == m_pids.end())
594                 return -1;
595                 
596         if (m_running)
597                 stopPID(pid);
598         
599         m_pids.erase(pid);
600         return 0;
601 }
602
603 RESULT eDVBTSRecorder::setTimingPID(int pid, int type)
604 {
605         m_thread->setTimingPID(pid, type);
606         return 0;
607 }
608
609 RESULT eDVBTSRecorder::setTargetFD(int fd)
610 {
611         m_target_fd = fd;
612         return 0;
613 }
614
615 RESULT eDVBTSRecorder::setTargetFilename(const char *filename)
616 {
617         m_target_filename = filename;
618         return 0;
619 }
620
621 RESULT eDVBTSRecorder::setBoundary(off_t max)
622 {
623         return -1; // not yet implemented
624 }
625
626 RESULT eDVBTSRecorder::stop()
627 {
628         for (std::map<int,int>::iterator i(m_pids.begin()); i != m_pids.end(); ++i)
629                 stopPID(i->first);
630
631         if (!m_running)
632                 return -1;
633         m_thread->stop();
634         
635         close(m_source_fd);
636         m_source_fd = -1;
637         
638         m_thread->stopSaveMetaInformation();
639         
640         return 0;
641 }
642
643 RESULT eDVBTSRecorder::getCurrentPCR(pts_t &pcr)
644 {
645         if (!m_running)
646                 return 0;
647         if (!m_thread)
648                 return 0;
649                 /* XXX: we need a lock here */
650
651                         /* we don't filter PCR data, so just use the last received PTS, which is not accurate, but better than nothing */
652         return m_thread->getLastPTS(pcr);
653 }
654
655 RESULT eDVBTSRecorder::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &conn)
656 {
657         conn = new eConnection(this, m_event.connect(event));
658         return 0;
659 }
660
661 RESULT eDVBTSRecorder::startPID(int pid)
662 {
663 #ifndef HAVE_ADD_PID
664         int fd = m_demux->openDemux();
665         if (fd < 0)
666         {
667                 eDebug("FAILED to open demux in ts recoder (%m)");
668                 return -1;
669         }
670
671 #if HAVE_DVB_API_VERSION < 3
672         dmxPesFilterParams flt;
673         
674         flt.pesType = DMX_PES_OTHER;
675 #else
676         dmx_pes_filter_params flt;
677         
678         flt.pes_type = DMX_PES_OTHER;
679 #endif
680
681         flt.pid     = pid;
682         flt.input   = DMX_IN_FRONTEND;
683         flt.output  = DMX_OUT_TS_TAP;
684         
685         flt.flags   = DMX_IMMEDIATE_START;
686
687         int res = ::ioctl(fd, DMX_SET_PES_FILTER, &flt);
688         if (res < 0)
689         {
690                 eDebug("set pes filter failed!");
691                 ::close(fd);
692                 return -1;
693         }
694         m_pids[pid] = fd;
695 #else
696         while(true) {
697 #if HAVE_DVB_API_VERSION > 3
698                 __u16 p = pid;
699                 if (::ioctl(m_source_fd, DMX_ADD_PID, &p) < 0) {
700 #else
701                 if (::ioctl(m_source_fd, DMX_ADD_PID, pid) < 0) {
702 #endif
703                         perror("DMX_ADD_PID");
704                         if (errno == EAGAIN || errno == EINTR) {
705                                 eDebug("retry!");
706                                 continue;
707                         }
708                 } else
709                         m_pids[pid] = 1;
710                 break;
711         }
712 #endif
713         return 0;
714 }
715
716 void eDVBTSRecorder::stopPID(int pid)
717 {
718 #ifndef HAVE_ADD_PID
719         if (m_pids[pid] != -1)
720                 ::close(m_pids[pid]);
721 #else
722         if (m_pids[pid] != -1)
723         {
724                 while(true) {
725 #if HAVE_DVB_API_VERSION > 3
726                         __u16 p = pid;
727                         if (::ioctl(m_source_fd, DMX_REMOVE_PID, &p) < 0) {
728 #else
729                         if (::ioctl(m_source_fd, DMX_REMOVE_PID, pid) < 0) {
730 #endif
731                                 perror("DMX_REMOVE_PID");
732                                 if (errno == EAGAIN || errno == EINTR) {
733                                         eDebug("retry!");
734                                         continue;
735                                 }
736                         }
737                         break;
738                 }
739         }
740 #endif
741         m_pids[pid] = -1;
742 }
743
744 void eDVBTSRecorder::filepushEvent(int event)
745 {
746         switch (event)
747         {
748         case eFilePushThread::evtWriteError:
749                 m_event(eventWriteError);
750                 break;
751         }
752 }