- added lowlevel/eit.h
[enigma2.git] / lib / dvb / demux.cpp
1 #include <config.h>
2 #include <stdio.h>
3 #include <fcntl.h>
4 #include <sys/ioctl.h>
5 #include <errno.h>
6 #include <unistd.h>
7 #include <signal.h>
8
9 #include <lib/base/thread.h>
10
11 #if HAVE_DVB_API_VERSION < 3
12 #include <ost/dmx.h>
13 #ifndef DMX_SET_NEGFILTER_MASK
14         #define DMX_SET_NEGFILTER_MASK   _IOW('o',48,uint8_t *)
15 #endif
16 #else
17 #include <linux/dvb/dmx.h>
18 #endif
19
20 #include "crc32.h"
21
22 #include <lib/base/eerror.h>
23 #include <lib/dvb/idvb.h>
24 #include <lib/dvb/demux.h>
25 #include <lib/dvb/esection.h>
26 #include <lib/dvb/decoder.h>
27
28 eDVBDemux::eDVBDemux(int adapter, int demux): adapter(adapter), demux(demux)
29 {
30         m_dvr_busy = 0;
31 }
32
33 eDVBDemux::~eDVBDemux()
34 {
35 }
36
37 DEFINE_REF(eDVBDemux)
38
39 RESULT eDVBDemux::createSectionReader(eMainloop *context, ePtr<iDVBSectionReader> &reader)
40 {
41         RESULT res;
42         reader = new eDVBSectionReader(this, context, res);
43         if (res)
44                 reader = 0;
45         return res;
46 }
47
48 RESULT eDVBDemux::createTSRecorder(ePtr<iDVBTSRecorder> &recorder)
49 {
50         if (m_dvr_busy)
51                 return -EBUSY;
52         recorder = new eDVBTSRecorder(this);
53         return 0;
54 }
55
56 RESULT eDVBDemux::getMPEGDecoder(ePtr<iTSMPEGDecoder> &decoder)
57 {
58         decoder = new eTSMPEGDecoder(this, 0);
59         return 0;
60 }
61
62 void eDVBSectionReader::data(int)
63 {
64         __u8 data[4096]; // max. section size
65         int r;
66         r = ::read(fd, data, 4096);
67         if(r < 0)
68         {
69                 eWarning("ERROR reading section - %m\n");
70                 return;
71         }
72         if (checkcrc)
73         {
74                         // this check should never happen unless the driver is crappy!
75                 unsigned int c;
76                 if ((c = crc32((unsigned)-1, data, r)))
77                         eFatal("crc32 failed! is %x\n", c);
78         }
79         if (active)
80                 read(data);
81         else
82                 eDebug("data.. but not active");
83 }
84
85 eDVBSectionReader::eDVBSectionReader(eDVBDemux *demux, eMainloop *context, RESULT &res): demux(demux)
86 {
87         char filename[128];
88 #if HAVE_DVB_API_VERSION < 3
89         sprintf(filename, "/dev/dvb/card%d/demux%d", demux->adapter, demux->demux);
90 #else
91         sprintf(filename, "/dev/dvb/adapter%d/demux%d", demux->adapter, demux->demux);
92 #endif
93         fd = ::open(filename, O_RDWR);
94         
95         eDebug("eDVBSectionReader has fd %d", fd);
96         
97         if (fd >= 0)
98         {
99                 notifier=new eSocketNotifier(context, fd, eSocketNotifier::Read);
100                 CONNECT(notifier->activated, eDVBSectionReader::data);
101                 res = 0;
102         } else
103         {
104                 perror(filename);
105                 res = errno;
106         }
107 }
108
109 DEFINE_REF(eDVBSectionReader)
110
111 eDVBSectionReader::~eDVBSectionReader()
112 {
113         if (notifier)
114                 delete notifier;
115         if (fd >= 0)
116                 ::close(fd);
117 }
118
119 RESULT eDVBSectionReader::start(const eDVBSectionFilterMask &mask)
120 {
121         RESULT res;
122         if (fd < 0)
123                 return -ENODEV;
124
125 #if HAVE_DVB_API_VERSION < 3
126         dmxSctFilterParams sct;
127 #else
128         dmx_sct_filter_params sct;
129 #endif
130         sct.pid     = mask.pid;
131         sct.timeout = 0;
132 #if HAVE_DVB_API_VERSION < 3
133         sct.flags   = 0;
134 #else
135         sct.flags   = DMX_IMMEDIATE_START;
136 #endif
137         if (mask.flags & eDVBSectionFilterMask::rfCRC)
138         {
139                 sct.flags |= DMX_CHECK_CRC;
140                 checkcrc = 1;
141         } else
142                 checkcrc = 0;
143         
144         memcpy(sct.filter.filter, mask.data, DMX_FILTER_SIZE);
145         memcpy(sct.filter.mask, mask.mask, DMX_FILTER_SIZE);
146 #if HAVE_DVB_API_VERSION >= 3
147         memcpy(sct.filter.mode, mask.mode, DMX_FILTER_SIZE);
148 #endif
149         
150         res = ::ioctl(fd, DMX_SET_FILTER, &sct);
151         if (!res)
152         {
153 #if HAVE_DVB_API_VERSION < 3
154                 res = ::ioctl(fd, DMX_SET_NEGFILTER_MASK, mask.mode);
155                 if (!res)
156                 {
157                         res = ::ioctl(fd, DMX_START, 0);
158                         if (!res)
159                                 active = 1;
160                 }
161 #else
162                 active = 1;
163 #endif
164         }
165         return res;
166 }
167
168 RESULT eDVBSectionReader::stop()
169 {
170         if (!active)
171                 return -1;
172
173         active=0;
174         ::ioctl(fd, DMX_STOP);
175         
176         return 0;
177 }
178
179 RESULT eDVBSectionReader::connectRead(const Slot1<void,const __u8*> &r, ePtr<eConnection> &conn)
180 {
181         conn = new eConnection(this, read.connect(r));
182         return 0;
183 }
184
185 DEFINE_REF(eDVBTSRecorder);
186
187 class eDVBTSRecorderThread: public eThread
188 {
189 public:
190         eDVBTSRecorderThread();
191         void thread();
192         void stop();
193         void start(int sourcefd, int destfd);
194 private:
195         int m_stop;
196         unsigned char m_buffer[65536];
197         int m_buf_start, m_buf_end;
198         int m_fd_source, m_fd_dest;
199 };
200
201 eDVBTSRecorderThread::eDVBTSRecorderThread()
202 {
203         m_stop = 0;
204         m_buf_start = m_buf_end = 0;
205 }
206
207 static void signal_handler(int x)
208 {
209 }
210
211 void eDVBTSRecorderThread::thread()
212 {
213         eDebug("RECORDING THREAD START");
214                 // this is race. FIXME.
215         
216                 /* we set the signal to not restart syscalls, so we can detect our signal. */
217         struct sigaction act;
218         act.sa_handler = signal_handler; // no, SIG_IGN doesn't do it :/
219         act.sa_flags = 0;
220         sigaction(SIGUSR1, &act, 0);
221         
222                 /* m_stop must be evaluated after each syscall. */
223         while (!m_stop)
224         {
225                         /* first try flushing the bufptr */
226                 if (m_buf_start != m_buf_end)
227                 {
228                                 // TODO: take care of boundaries.
229                         int w = write(m_fd_dest, m_buffer + m_buf_start, m_buf_end - m_buf_start);
230                         if (w <= 0)
231                         {
232                                 if (errno == -EINTR)
233                                         continue;
234                                 eDebug("eDVBTSRecorder *write error* - not yet handled");
235                                 // ... we would stop the thread
236                         }
237                         printf("TSRECORD: wrote %d bytes\n", w);
238                         m_buf_start += w;
239                         continue;
240                 }
241                         
242                         /* now fill our buffer. */
243                 m_buf_start = 0;
244                 m_buf_end = read(m_fd_source, m_buffer, sizeof(m_buffer));
245                 if (m_buf_end < 0)
246                 {
247                         m_buf_end = 0;
248                         if (errno == EINTR)
249                                 continue;
250                         eDebug("eDVBTSRecorder *read error* - not yet handled");
251                 }
252                 printf("TSRECORD: read %d bytes\n", m_buf_end);
253         }
254         
255         eDebug("RECORDING THREAD STOP");
256 }
257
258 void eDVBTSRecorderThread::start(int fd_source, int fd_dest)
259 {
260         m_fd_source = fd_source;
261         m_fd_dest = fd_dest;
262         m_stop = 0;
263         run();
264 }
265
266 void eDVBTSRecorderThread::stop()
267 {
268         m_stop = 1;
269         sendSignal(SIGUSR1);
270         kill();
271 }
272
273 eDVBTSRecorder::eDVBTSRecorder(eDVBDemux *demux): m_demux(demux)
274 {
275         m_running = 0;
276         m_format = 0;
277         m_target_fd = -1;
278         m_thread = new eDVBTSRecorderThread();
279         m_demux->m_dvr_busy = 1;
280 }
281
282 eDVBTSRecorder::~eDVBTSRecorder()
283 {
284         stop();
285         delete m_thread;
286         m_demux->m_dvr_busy = 0;
287 }
288
289 RESULT eDVBTSRecorder::start()
290 {
291         if (m_running)
292                 return -1;
293         
294         if (m_target_fd == -1)
295                 return -2;
296                 
297         char filename[128];
298 #if HAVE_DVB_API_VERSION < 3
299         snprintf(filename, 128, "/dev/dvb/card%d/dvr%d", m_demux->adapter, m_demux->demux);
300 #else
301         snprintf(filename, 128, "/dev/dvb/adapter%d/dvr%d", m_demux->adapter, m_demux->demux);
302 #endif
303         m_source_fd = ::open(filename, O_RDONLY);
304         
305         if (m_source_fd < 0)
306         {
307                 eDebug("FAILED to open dvr (%s) in ts recoder (%m)", filename);
308                 return -3;
309         }
310         
311         m_thread->start(m_source_fd, m_target_fd);
312         m_running = 1;
313         
314         for (std::map<int,int>::iterator i(m_pids.begin()); i != m_pids.end(); ++i)
315                 startPID(i->first);
316         
317         return 0;
318 }
319
320 RESULT eDVBTSRecorder::addPID(int pid)
321 {
322         if (m_pids.find(pid) != m_pids.end())
323                 return -1;
324         
325         m_pids.insert(std::pair<int,int>(pid, -1));
326         if (m_running)
327                 startPID(pid);
328         return 0;
329 }
330
331 RESULT eDVBTSRecorder::removePID(int pid)
332 {
333         if (m_pids.find(pid) == m_pids.end())
334                 return -1;
335                 
336         if (m_running)
337                 stopPID(pid);
338         
339         m_pids.erase(pid);
340         return 0;
341 }
342
343 RESULT eDVBTSRecorder::setFormat(int format)
344 {
345         if (m_running)
346                 return -1;
347         m_format = format;
348         return 0;
349 }
350
351 RESULT eDVBTSRecorder::setTargetFD(int fd)
352 {
353         m_target_fd = fd;
354         return 0;
355 }
356
357 RESULT eDVBTSRecorder::setBoundary(off_t max)
358 {
359         return -1; // not yet implemented
360 }
361
362 RESULT eDVBTSRecorder::stop()
363 {
364         for (std::map<int,int>::iterator i(m_pids.begin()); i != m_pids.end(); ++i)
365                 stopPID(i->first);
366
367         if (!m_running)
368                 return -1;
369         m_thread->stop();
370         
371         close(m_source_fd);
372         
373         return 0;
374 }
375
376 RESULT eDVBTSRecorder::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &conn)
377 {
378         conn = new eConnection(this, m_event.connect(event));
379         return 0;
380 }
381
382 RESULT eDVBTSRecorder::startPID(int pid)
383 {
384         char filename[128];
385 #if HAVE_DVB_API_VERSION < 3
386         snprintf(filename, 128, "/dev/dvb/card%d/demux%d", m_demux->adapter, m_demux->demux);
387 #else
388         snprintf(filename, 128, "/dev/dvb/adapter%d/demux%d", m_demux->adapter, m_demux->demux);
389 #endif
390         int fd = ::open(filename, O_RDWR);
391         if (fd < 0)
392         {
393                 eDebug("FAILED to open demux (%s) in ts recoder (%m)", filename);
394                 return -1;
395         }
396
397 #if HAVE_DVB_API_VERSION < 3
398         dmxPesFilterParams flt;
399         
400         flt.pesType = DMX_PES_OTHER;
401 #else
402         dmx_pes_filter_params flt;
403         
404         flt.pes_type = DMX_PES_OTHER;
405 #endif
406
407         flt.pid     = pid;
408         flt.input   = DMX_IN_FRONTEND;
409         flt.output  = DMX_OUT_TS_TAP;
410         
411         flt.flags   = DMX_IMMEDIATE_START;
412
413         int res = ::ioctl(fd, DMX_SET_PES_FILTER, &flt);
414         if (res < 0)
415         {
416                 eDebug("set pes filter failed!");
417                 ::close(fd);
418                 return -1;
419         }
420         m_pids[pid] = fd;
421
422         return 0;
423 }
424
425 void eDVBTSRecorder::stopPID(int pid)
426 {
427         ::close(m_pids[pid]);
428         m_pids[pid] = -1;
429 }