Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / devicedvbinput.C
1 // jam job dvb file testing
2 //  1) define TEST_DATA as mpeg ts file "name.ts"
3 //  2) mod record.C Record::resync(), comment out: record_channel->drain_audio()
4 //  3) enable audio delay in audioidevice.C  AudioDevice::run_input, #if 0 <-> #if 1
5 //#define TEST_DATA "/tmp/xx.ts"
6
7 /*
8  * CINELERRA
9  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
10  * 
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  * 
25  */
26
27 #include "chantables.h"
28 #include "devicedvbinput.h"
29 #include "devicempeginput.h"
30 #include "recordconfig.h"
31 #include "signalstatus.h"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdint.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #ifdef HAVE_DVB
41 #include <linux/dvb/dmx.h>
42 #include <linux/dvb/frontend.h>
43 #endif
44 #include <sys/ioctl.h>
45
46
47 DVBInputStatus::DVBInputStatus(DeviceDVBInput *dvb_input)
48  : Thread(1, 0, 0)
49 {
50         this->dvb_input = dvb_input;
51         signal_status = 0;
52         done = 1;
53 }
54
55 DVBInputStatus::~DVBInputStatus()
56 {
57         stop();
58         if( signal_status ) signal_status->disconnect();
59 }
60
61 void DVBInputStatus::stop()
62 {
63         if( done ) return;
64         done = 1;
65         Thread::cancel();
66         Thread::join();
67 }
68
69 void DVBInputStatus::start()
70 {
71         if( !done ) return;
72         done = 0;
73         Thread::start();
74 }
75
76 void DVBInputStatus::lock_dvb()
77 {
78         Thread::disable_cancel();
79         dvb_input->dvb_lock->lock("DVBInputStatus::lock_dvb");
80 }
81
82 void DVBInputStatus::unlock_dvb()
83 {
84         dvb_input->dvb_lock->unlock();
85         Thread::enable_cancel();
86 }
87
88 void DVBInputStatus::run()
89 {
90         lock_dvb();
91
92         while( !done ) {
93                 dvb_input->status_dev();
94                 update();
95                 unlock_dvb();
96                 usleep(250000);
97                 lock_dvb();
98         }
99
100         unlock_dvb();
101 }
102
103 void DVBInputStatus::update()
104 {
105         if( !signal_status ) return;
106         signal_status->lock_window("DVBInputStatus::update");
107         signal_status->update();
108         signal_status->unlock_window();
109 }
110
111
112 DeviceDVBInput::DeviceDVBInput(const char *name, int no)
113  : DeviceMPEGInput(name, no)
114 {
115         frontend_fd = -1;
116         demux_fd = -1;
117         dvb_fd = -1;
118         reset_signal();
119         dvb_locked = 0;
120         dvb_lock = new Mutex("DeviceDVBInput::dvb_lock", 0);
121         sprintf(frontend_path, "%s/frontend%d", name, no);
122         sprintf(demux_path, "%s/demux%d", name, no);
123         sprintf(dvb_path, "%s/dvr%d", name, no);
124         dvb_input_status = new DVBInputStatus(this);
125 }
126
127 DeviceMPEGInput *DeviceDVBInput::
128 NewDVBInput(const char *name, int no)
129 {
130         return (DeviceMPEGInput *) new DeviceDVBInput(name, no);
131 }
132
133 DeviceDVBInput::~DeviceDVBInput()
134 {
135         close_dev();
136         delete dvb_input_status;
137         delete dvb_lock;
138         dvb_close(1);
139 }
140
141
142 DeviceMPEGInput* DeviceDVBInput::get_mpeg_input(VideoDevice *device)
143 {
144         DeviceMPEGInput* mpeg_device = get_mpeg_video(device, NewDVBInput,
145                 device->in_config->dvb_in_adapter, device->in_config->dvb_in_device);
146         if( !mpeg_device ) return 0;
147         device->channel->has_subchan = 1;
148         device->channel->has_scanning = 1;
149         device->channel->use_frequency = 1;
150         mpeg_device->set_device_number(0);
151         Channel *input_src = device->new_input_source((char*)"input0");
152         input_src->device_index = 0;
153         input_src->tuner = 0;
154         return mpeg_device;
155 }
156
157 DeviceMPEGInput* DeviceDVBInput::get_mpeg_input(AudioDevice *device)
158 {
159         return get_mpeg_audio(device, NewDVBInput,
160                 device->in_config->dvb_in_adapter, device->in_config->dvb_in_device);
161 }
162
163
164 int DeviceDVBInput::wait_signal(int ms, int trys)
165 {
166         Timer timer;
167         for( int retry=trys; --retry>=0; ) {
168                 timer.update();
169                 if( !dvb_status() && has_signal() ) break;
170                 int dt = ms - timer.get_difference();
171                 if( dt > 0 ) Timer::delay(dt);
172         }
173         return has_signal() ? 0 : 1;
174 }
175
176
177 int DeviceDVBBuffer::read(int retries, int usec, int bsz)
178 {
179         while( --retries >= 0 ) {
180                 struct timeval tv;  tv.tv_sec = 0;  tv.tv_usec = usec;
181                 fd_set rd_fd;  FD_ZERO(&rd_fd);  FD_SET(fd, &rd_fd);
182                 fd_set er_fd;  FD_ZERO(&er_fd);  FD_SET(fd, &er_fd);
183                 int ret = select(fd+1, &rd_fd, 0, &er_fd, &tv);
184                 if( ret < 0 ) break;
185                 if( !ret ) continue;
186                 if( !FD_ISSET(fd, &er_fd) ) {
187                         select(0, 0, 0, 0, &tv);
188                         continue;
189                 }
190                 if( !FD_ISSET(fd, &rd_fd) ) continue;
191                 if( (ret=::read(fd, bfr, bsz)) > 0 )
192                         return sz = ret;
193         }
194         return -1;
195 }
196
197 int DeviceDVBInput::dvb_sync()
198 {
199         const int SYNC = 0x47, TS_BLKSZ = 188;
200         const int BSZ = 100*TS_BLKSZ, XSZ = BSZ*5;
201         DeviceDVBBuffer data(dvb_fd, BSZ);
202
203         int nsync = 0, xfr = 0;
204         while( xfr < XSZ ) {
205                 if( data.read(5, 200000, BSZ) < 0 ) break;
206                 int ofs = 0;
207                 while( ofs < data.size() ) {
208                         nsync = 0;
209                         int i = ofs;
210                         while( i<data.size() && data[i]!=SYNC ) ++i;
211                         if( i >= data.size() ) break;
212                         ofs = i;
213                         while( (ofs+=TS_BLKSZ)<data.size() && data[ofs]==SYNC ) {
214                                 ++nsync;  i = ofs;
215                         }
216                 }
217                 if( nsync >= 32 && (ofs-=data.size()) >= 0 ) {
218                         while( ofs > 0 ) {
219                                 if( data.read(5, 200000, ofs) < 0 ) return 1;
220                                 ofs -= data.size();
221                         }
222                         return 0;
223                 }
224                 xfr += data.size();
225         }
226         return 1;
227 }
228
229 int DeviceDVBInput::dvb_open()
230 {
231         int ret = 0;
232 #ifndef TEST_DATA
233 #ifdef HAVE_DVB
234         if( frontend_fd < 0 &&
235            (frontend_fd = ::open(frontend_path, O_RDWR)) < 0 ) {
236                 fprintf(stderr, "DeviceDVBInput::dvb_open %s: %s\n",
237                         frontend_path, strerror(errno));
238                 ret = 1;
239         }
240         if( !ret && ioctl(frontend_fd, FE_GET_INFO, &fe_info) < 0 ) {
241                 fprintf(stderr, 
242                         "DeviceDVBInput::dvb_open FE_GET_INFO: %s\n",
243                         strerror(errno));
244                 ret = 1;
245         }
246
247         pwr_min = snr_min = 0;
248         pwr_max = snr_max = 65535;
249
250 // Set frequency
251         int index = -1, table = -1;
252         if( !ret && (index = get_channel()) < 0 ) ret = 1;
253         if( !ret && (table = get_channel_table()) < 0 ) ret = 1;
254         if( !ret ) {
255                 uint32_t frequency = 0;
256                 struct dvb_frontend_parameters frontend_param;
257                 bzero(&frontend_param, sizeof(frontend_param));
258                 ret = 1;
259                 switch(table) {
260                 case NETTUNE_AIR:
261                         if( index >= chanlists[NTSC_DVB].count ) break;
262                         frequency = chanlists[NTSC_DVB].list[index].freq * 1000000;
263                         if( frequency < fe_info.frequency_min ||
264                                 frequency > fe_info.frequency_max ) break;
265                         frontend_param.frequency = frequency;
266                         frontend_param.u.vsb.modulation = VSB_8;
267                         ret = 0;
268                         break;
269                 case NETTUNE_CABLE:
270                         if( index >= chanlists[CATV_DVB].count ) break;
271                         frequency = chanlists[CATV_DVB].list[index].freq * 1000000;
272                         if( frequency < fe_info.frequency_min ||
273                                 frequency > fe_info.frequency_max ) break;
274                         frontend_param.frequency = frequency;
275                         frontend_param.u.vsb.modulation = QAM_AUTO;
276                         ret = 0;
277                         break;
278                 }
279
280                 if( !ret && ioctl(frontend_fd, FE_SET_FRONTEND, &frontend_param) < 0 ) {
281                         fprintf(stderr, 
282                                 "DeviceDVBInput::dvb_open FE_SET_FRONTEND frequency=%d: %s\n",
283                                 frontend_param.frequency, strerror(errno));
284                         ret = 1;
285                 }
286
287                 if( !ret && wait_signal(333,3) ) {
288                         fprintf(stderr,
289                                  "DeviceDVBInput::dvb_open: no signal, index=%d frequency=%d\n",
290                                 index, frontend_param.frequency);
291                         ret = 1;
292                 }
293
294                 if( !ret && ioctl(frontend_fd, FE_GET_FRONTEND, &frontend_param) ) {
295                         fprintf(stderr, 
296                                 "DeviceDVBInput::dvb_open FE_GET_FRONTEND: %s\n",
297                                 strerror(errno));
298                         ret = 1;
299                 }
300                 if( !ret ) { // goofy quirks
301                         if( !strcmp("Samsung S5H1409 QAM/8VSB Frontend", fe_info.name) ) {
302                                 switch(frontend_param.u.vsb.modulation) {
303                                 case QAM_64:  snr_min = 200;  snr_max = 300;  break;
304                                 case QAM_256: snr_min = 260;  snr_max = 400;  break;
305                                 case VSB_8:   snr_min = 111;  snr_max = 300;  break;
306                                 default: break;
307                                 }
308                                 pwr_min = snr_min;  pwr_max = snr_max;
309                         }
310                         else if( !strcmp("Auvitek AU8522 QAM/8VSB Frontend", fe_info.name) ) {
311                                 switch(frontend_param.u.vsb.modulation) {
312                                 case QAM_64:  snr_min = 190;  snr_max = 290;  break;
313                                 case QAM_256: snr_min = 280;  snr_max = 400;  break;
314                                 case VSB_8:   snr_min = 115;  snr_max = 270;  break;
315                                 default: break;
316                                 }
317                                 pwr_min = snr_min;  pwr_max = snr_max;
318                         }
319                 }
320         }
321
322         if( !ret && (demux_fd = ::open(demux_path, O_RDWR)) < 0 ) {
323                 fprintf(stderr,
324                         "DeviceDVBInput::dvb_open %s for video: %s\n",
325                         demux_path,
326                         strerror(errno));
327                 ret = 1;
328         }
329
330 // Setting exactly one PES filter to 0x2000 dumps the entire
331 // transport stream.
332         if( !ret ) {
333                 struct dmx_pes_filter_params pesfilter;
334                 memset(&pesfilter,0,sizeof(pesfilter));
335                 pesfilter.pid = 0x2000;
336                 pesfilter.input = DMX_IN_FRONTEND;
337                 pesfilter.output = DMX_OUT_TS_TAP;
338                 pesfilter.pes_type = DMX_PES_OTHER;
339                 pesfilter.flags = DMX_IMMEDIATE_START;
340                 if( ioctl(demux_fd, DMX_SET_PES_FILTER, &pesfilter) < 0 ) {
341                         fprintf(stderr, 
342                 "DeviceDVBInput::dvb_open DMX_SET_PES_FILTER for raw: %s\n",
343                                 strerror(errno));
344                         ret = 1;
345                 }
346         }
347 // Open transport stream for reading
348         if( !ret && (dvb_fd = ::open(dvb_path, O_RDONLY+O_NONBLOCK)) < 0 ) {
349                 fprintf(stderr, "DeviceDVBInput::dvb_open %s: %s\n",
350                         dvb_path, strerror(errno));
351                 ret = 1;
352         }
353 #else
354         dvb_fd = -1;
355         ret = 1;
356 #endif
357 #else
358         dvb_fd = open(TEST_DATA,O_RDONLY);
359         if( dvb_fd < 0 ) ret = 1;
360 #endif
361         reset_signal();
362         dvb_locked = 0;
363         if( !ret ) ret = dvb_sync();
364         if( !ret ) ret = dvb_status();
365         if( ret ) dvb_close();
366         return ret;
367 }
368
369
370 void DeviceDVBInput::dvb_close(int fe)
371 {
372         if( dvb_fd >= 0 ) { ::close(dvb_fd); dvb_fd = -1; }
373         if( demux_fd >= 0 ) { ::close(demux_fd); demux_fd = -1; }
374         if( fe && frontend_fd >= 0 ) { ::close(frontend_fd); frontend_fd = -1; }
375 }
376
377
378 int DeviceDVBInput::dvb_status()
379 {
380 //note: this function can take > 500ms to execute  (slowww)
381 #ifndef TEST_DATA
382         int locked = 0;
383         reset_signal();
384 #ifdef HAVE_DVB
385         int &fe = frontend_fd;
386         if( fe < 0 ) return -1;
387         fe_status_t st;  memset(&st, 0, sizeof(st));
388         if( ioctl(fe, FE_READ_STATUS, &st) ) return 1;
389         if( (st & FE_TIMEDOUT) != 0 ) return 1;
390         signal_lck = (st & FE_HAS_LOCK) != 0 ? 1 : 0;
391         signal_crr = (st & FE_HAS_CARRIER) != 0 ? 1 : 0;
392         uint16_t power = 0;
393         signal_pwr = ioctl(fe,FE_READ_SIGNAL_STRENGTH,&power) ? -1 :
394                 drange(power, pwr_min, pwr_max);
395         uint16_t ratio = 0;
396         signal_snr = ioctl(fe, FE_READ_SNR, &ratio) ? -1 :
397                 drange(ratio, snr_min, snr_max);
398         uint32_t rate = 0;
399         signal_ber = ioctl(fe, FE_READ_BER, &rate) ? -1 : rate;
400         uint32_t errs = 0;
401         signal_unc = ioctl(fe, FE_READ_UNCORRECTED_BLOCKS, &errs) ? -1 : errs;
402         if( signal_lck && signal_ber >= 0 && signal_ber < 255 ) locked = 1;
403         if( dvb_locked != locked ) {
404                 printf(_("** %scarrier, dvb_locked %s\n"),
405                          signal_crr ? "" : _("no "), locked ? _("lock") : _("lost") );
406         }
407 #endif
408         dvb_locked = locked;
409 #endif
410         return 0;
411 }
412
413 int DeviceDVBInput::drange(int v, int min, int max)
414 {
415         if( (v-=min) < 0 || (max-=min) <= 0 ) return 0;
416         return v>max ? 65535U : (65535U * v) / max;
417 }
418
419 void DeviceDVBInput::reset_signal()
420 {
421         signal_pwr = signal_crr = signal_lck =
422         signal_snr = signal_ber = signal_unc = -1;
423 }
424
425 int DeviceDVBInput::open_dev(int color_model)
426 {
427         int ret = 0;
428         dvb_lock->lock("DeviceDVBInput::open_dev");
429         if( dvb_fd < 0 )
430         {
431                 ret = dvb_open();
432                 if( ret ) {
433                         printf("DeviceDVBInput::open_dev: adaptor %s open failed\n", 
434                                 dev_name);
435                 }
436         }
437         if( !ret )
438                 dvb_input_status->start();
439         else
440                 dvb_close();
441         dvb_lock->unlock();
442         return ret;
443 }
444
445 void DeviceDVBInput::close_dev()
446 {
447         dvb_lock->lock("DeviceDVBInput::close_dev");
448         dvb_input_status->stop();
449         dvb_close();
450         dvb_lock->unlock();
451 }
452
453 int DeviceDVBInput::status_dev()
454 {
455         int result = 0;
456 #ifndef TEST_DATA
457         result = frontend_fd >= 0 ? dvb_status() : 1;
458 #endif
459         return result;
460 }
461
462 void DeviceDVBInput::set_signal_status(SignalStatus *stat)
463 {
464         dvb_lock->lock("DeviceDVBInput::set_signal_status");
465         if( dvb_input_status->signal_status )
466                 dvb_input_status->signal_status->disconnect();
467         dvb_input_status->signal_status = stat;
468         if( stat ) stat->dvb_input = this;
469         dvb_lock->unlock();
470 }
471