record timing information from a single PID
[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 #if HAVE_DVB_API_VERSION < 3
9 #include <ost/dmx.h>
10
11 #ifndef DMX_SET_NEGFILTER_MASK
12         #define DMX_SET_NEGFILTER_MASK   _IOW('o',48,uint8_t *)
13 #endif
14
15 #ifndef DMX_GET_STC
16         struct dmx_stc
17         {
18                 unsigned int num;       /* input : which STC? O..N */
19                 unsigned int base;      /* output: divisor for stc to get 90 kHz clock */
20                 unsigned long long stc; /* output: src in 'base'*90 kHz units */
21         };
22         #define DMX_GET_STC             _IOR('o', 50, struct dmx_stc)
23 #endif
24
25 #else
26 #include <linux/dvb/dmx.h>
27 #endif
28
29 #include "crc32.h"
30
31 #include <lib/base/eerror.h>
32 #include <lib/base/filepush.h>
33 #include <lib/dvb/idvb.h>
34 #include <lib/dvb/demux.h>
35 #include <lib/dvb/esection.h>
36 #include <lib/dvb/decoder.h>
37 #include <lib/dvb/pvrparse.h>
38
39 eDVBDemux::eDVBDemux(int adapter, int demux): adapter(adapter), demux(demux)
40 {
41         m_dvr_busy = 0;
42 }
43
44 eDVBDemux::~eDVBDemux()
45 {
46 }
47
48 int eDVBDemux::openDemux(void)
49 {
50         char filename[128];
51 #if HAVE_DVB_API_VERSION < 3
52         snprintf(filename, 128, "/dev/dvb/card%d/demux%d", adapter, demux);
53 #else
54         snprintf(filename, 128, "/dev/dvb/adapter%d/demux%d", adapter, demux);
55 #endif
56         return ::open(filename, O_RDWR);
57 }
58
59 DEFINE_REF(eDVBDemux)
60
61 RESULT eDVBDemux::setSourceFrontend(int fenum)
62 {
63 #if HAVE_DVB_API_VERSION >= 3
64         int fd = openDemux();
65         
66         int n = DMX_SOURCE_FRONT0 + fenum;
67         int res = ::ioctl(fd, DMX_SET_SOURCE, &n);
68         if (res)
69                 eDebug("DMX_SET_SOURCE failed! - %m");
70         ::close(fd);
71         return res;
72 #endif
73         return 0;
74 }
75
76 RESULT eDVBDemux::setSourcePVR(int pvrnum)
77 {
78 #if HAVE_DVB_API_VERSION >= 3
79         int fd = openDemux();
80         int n = DMX_SOURCE_DVR0 + pvrnum;
81         int res = ::ioctl(fd, DMX_SET_SOURCE, &n);
82         ::close(fd);
83         return res;
84 #endif
85         return 0;
86 }
87
88 RESULT eDVBDemux::createSectionReader(eMainloop *context, ePtr<iDVBSectionReader> &reader)
89 {
90         RESULT res;
91         reader = new eDVBSectionReader(this, context, res);
92         if (res)
93                 reader = 0;
94         return res;
95 }
96
97 RESULT eDVBDemux::createTSRecorder(ePtr<iDVBTSRecorder> &recorder)
98 {
99         if (m_dvr_busy)
100                 return -EBUSY;
101         recorder = new eDVBTSRecorder(this);
102         return 0;
103 }
104
105 RESULT eDVBDemux::getMPEGDecoder(ePtr<iTSMPEGDecoder> &decoder)
106 {
107         decoder = new eTSMPEGDecoder(this, 0);
108         return 0;
109 }
110
111 RESULT eDVBDemux::getSTC(pts_t &pts, int num)
112 {
113         int fd = openDemux();
114         
115         if (fd < 0)
116                 return -ENODEV;
117
118         struct dmx_stc stc;
119         stc.num = num;
120         stc.base = 1;
121         
122         if (ioctl(fd, DMX_GET_STC, &stc) < 0)
123         {
124                 ::close(fd);
125                 return -1;
126         }
127         
128         pts = stc.stc;
129         
130         ::close(fd);
131         return 0;
132 }
133
134 RESULT eDVBDemux::flush()
135 {
136         // FIXME: implement flushing the PVR queue here.
137         
138         m_event(evtFlush);
139         return 0;
140 }
141
142 RESULT eDVBDemux::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &conn)
143 {
144         conn = new eConnection(this, m_event.connect(event));
145         return 0;
146 }
147
148 void eDVBSectionReader::data(int)
149 {
150         __u8 data[4096]; // max. section size
151         int r;
152         r = ::read(fd, data, 4096);
153         if(r < 0)
154         {
155                 eWarning("ERROR reading section - %m\n");
156                 return;
157         }
158         if (checkcrc)
159         {
160                         // this check should never happen unless the driver is crappy!
161                 unsigned int c;
162                 if ((c = crc32((unsigned)-1, data, r)))
163                 {
164                         eDebug("crc32 failed! is %x\n", c);
165                         return;
166                 }
167         }
168         if (active)
169                 read(data);
170         else
171                 eDebug("data.. but not active");
172 }
173
174 eDVBSectionReader::eDVBSectionReader(eDVBDemux *demux, eMainloop *context, RESULT &res): demux(demux)
175 {
176         char filename[128];
177         fd = demux->openDemux();
178         
179         if (fd >= 0)
180         {
181                 notifier=new eSocketNotifier(context, fd, eSocketNotifier::Read, false);
182                 CONNECT(notifier->activated, eDVBSectionReader::data);
183                 res = 0;
184         } else
185         {
186                 perror(filename);
187                 res = errno;
188         }
189 }
190
191 DEFINE_REF(eDVBSectionReader)
192
193 eDVBSectionReader::~eDVBSectionReader()
194 {
195         if (notifier)
196                 delete notifier;
197         if (fd >= 0)
198                 ::close(fd);
199 }
200
201 RESULT eDVBSectionReader::start(const eDVBSectionFilterMask &mask)
202 {
203         RESULT res;
204         if (fd < 0)
205                 return -ENODEV;
206
207         notifier->start();
208 #if HAVE_DVB_API_VERSION < 3
209         dmxSctFilterParams sct;
210 #else
211         dmx_sct_filter_params sct;
212 #endif
213         sct.pid     = mask.pid;
214         sct.timeout = 0;
215 #if HAVE_DVB_API_VERSION < 3
216         sct.flags   = 0;
217 #else
218         sct.flags   = DMX_IMMEDIATE_START;
219 #endif
220         if (mask.flags & eDVBSectionFilterMask::rfCRC)
221         {
222                 sct.flags |= DMX_CHECK_CRC;
223                 checkcrc = 1;
224         } else
225                 checkcrc = 0;
226         
227         memcpy(sct.filter.filter, mask.data, DMX_FILTER_SIZE);
228         memcpy(sct.filter.mask, mask.mask, DMX_FILTER_SIZE);
229 #if HAVE_DVB_API_VERSION >= 3
230         memcpy(sct.filter.mode, mask.mode, DMX_FILTER_SIZE);
231         if (::ioctl(fd, DMX_SET_BUFFER_SIZE, 8192*8) < 0)
232                 eDebug("DMX_SET_BUFFER_SIZE failed(%m)");
233 #endif
234         
235         res = ::ioctl(fd, DMX_SET_FILTER, &sct);
236         if (!res)
237         {
238 #if HAVE_DVB_API_VERSION < 3
239                 res = ::ioctl(fd, DMX_SET_NEGFILTER_MASK, mask.mode);
240                 if (!res)
241                 {
242                         res = ::ioctl(fd, DMX_START, 0);
243                         if (!res)
244                                 active = 1;
245                 }
246 #else
247                 active = 1;
248 #endif
249         }
250         return res;
251 }
252
253 RESULT eDVBSectionReader::stop()
254 {
255         if (!active)
256                 return -1;
257
258         active=0;
259         ::ioctl(fd, DMX_STOP);
260         notifier->stop();
261
262         return 0;
263 }
264
265 RESULT eDVBSectionReader::connectRead(const Slot1<void,const __u8*> &r, ePtr<eConnection> &conn)
266 {
267         conn = new eConnection(this, read.connect(r));
268         return 0;
269 }
270
271 class eDVBRecordFileThread: public eFilePushThread
272 {
273 public:
274         eDVBRecordFileThread();
275         void setTimingPID(int pid);
276 protected:
277         void filterRecordData(const char *data, int len);
278 private:
279         eMPEGStreamParserTS m_ts_parser;
280         eMPEGStreamInformation m_stream_info;
281         off_t m_current_offset;
282         int m_pid;
283 };
284
285 eDVBRecordFileThread::eDVBRecordFileThread()
286         : m_ts_parser(m_stream_info)
287 {
288         m_current_offset = 0;
289 }
290
291 void eDVBRecordFileThread::setTimingPID(int pid)
292 {
293         m_ts_parser.setPid(pid);
294 }
295
296 void eDVBRecordFileThread::filterRecordData(const char *data, int len)
297 {
298         m_ts_parser.parseData(m_current_offset, data, len);
299         
300         m_current_offset += len;
301 }
302
303 DEFINE_REF(eDVBTSRecorder);
304
305 eDVBTSRecorder::eDVBTSRecorder(eDVBDemux *demux): m_demux(demux)
306 {
307         m_running = 0;
308         m_target_fd = -1;
309         m_thread = new eDVBRecordFileThread();
310         m_demux->m_dvr_busy = 1;
311 }
312
313 eDVBTSRecorder::~eDVBTSRecorder()
314 {
315         stop();
316         delete m_thread;
317         m_demux->m_dvr_busy = 0;
318 }
319
320 RESULT eDVBTSRecorder::start()
321 {
322         if (m_running)
323                 return -1;
324         
325         if (m_target_fd == -1)
326                 return -2;
327                 
328         char filename[128];
329 #if HAVE_DVB_API_VERSION < 3
330         snprintf(filename, 128, "/dev/dvb/card%d/dvr%d", m_demux->adapter, m_demux->demux);
331 #else
332         snprintf(filename, 128, "/dev/dvb/adapter%d/dvr%d", m_demux->adapter, m_demux->demux);
333 #endif
334         m_source_fd = ::open(filename, O_RDONLY);
335         
336         if (m_source_fd < 0)
337         {
338                 eDebug("FAILED to open dvr (%s) in ts recoder (%m)", filename);
339                 return -3;
340         }
341         
342         m_thread->start(m_source_fd, m_target_fd);
343         m_running = 1;
344         
345         for (std::map<int,int>::iterator i(m_pids.begin()); i != m_pids.end(); ++i)
346                 startPID(i->first);
347         
348         return 0;
349 }
350
351 RESULT eDVBTSRecorder::addPID(int pid)
352 {
353         if (m_pids.find(pid) != m_pids.end())
354                 return -1;
355         
356         m_pids.insert(std::pair<int,int>(pid, -1));
357         if (m_running)
358                 startPID(pid);
359         return 0;
360 }
361
362 RESULT eDVBTSRecorder::removePID(int pid)
363 {
364         if (m_pids.find(pid) == m_pids.end())
365                 return -1;
366                 
367         if (m_running)
368                 stopPID(pid);
369         
370         m_pids.erase(pid);
371         return 0;
372 }
373
374 RESULT eDVBTSRecorder::setTimingPID(int pid)
375 {
376         if (m_running)
377                 return -1;
378         m_thread->setTimingPID(pid);
379         return 0;
380 }
381
382 RESULT eDVBTSRecorder::setTargetFD(int fd)
383 {
384         m_target_fd = fd;
385         return 0;
386 }
387
388 RESULT eDVBTSRecorder::setBoundary(off_t max)
389 {
390         return -1; // not yet implemented
391 }
392
393 RESULT eDVBTSRecorder::stop()
394 {
395         for (std::map<int,int>::iterator i(m_pids.begin()); i != m_pids.end(); ++i)
396                 stopPID(i->first);
397
398         if (!m_running)
399                 return -1;
400         m_thread->stop();
401         
402         close(m_source_fd);
403         
404         return 0;
405 }
406
407 RESULT eDVBTSRecorder::connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &conn)
408 {
409         conn = new eConnection(this, m_event.connect(event));
410         return 0;
411 }
412
413 RESULT eDVBTSRecorder::startPID(int pid)
414 {
415         int fd = m_demux->openDemux();
416         if (fd < 0)
417         {
418                 eDebug("FAILED to open demux in ts recoder (%m)");
419                 return -1;
420         }
421
422 #if HAVE_DVB_API_VERSION < 3
423         dmxPesFilterParams flt;
424         
425         flt.pesType = DMX_PES_OTHER;
426 #else
427         dmx_pes_filter_params flt;
428         
429         flt.pes_type = DMX_PES_OTHER;
430 #endif
431
432         flt.pid     = pid;
433         flt.input   = DMX_IN_FRONTEND;
434         flt.output  = DMX_OUT_TS_TAP;
435         
436         flt.flags   = DMX_IMMEDIATE_START;
437
438         int res = ::ioctl(fd, DMX_SET_PES_FILTER, &flt);
439         if (res < 0)
440         {
441                 eDebug("set pes filter failed!");
442                 ::close(fd);
443                 return -1;
444         }
445         m_pids[pid] = fd;
446
447         return 0;
448 }
449
450 void eDVBTSRecorder::stopPID(int pid)
451 {
452         if (m_pids[pid] != -1)
453                 ::close(m_pids[pid]);
454         m_pids[pid] = -1;
455 }