Reported by Fedora team for gcc-13 and Andrew created patch here
[goodguy/cinelerra.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 #ifdef HAVE_DVB
28 #include "chantables.h"
29 #include "devicedvbinput.h"
30 #include "devicempeginput.h"
31 #include "recordconfig.h"
32 #include "signalstatus.h"
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdint.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <linux/dvb/dmx.h>
42 #include <linux/dvb/frontend.h>
43 #include <sys/ioctl.h>
44
45
46 DVBInputStatus::DVBInputStatus(DeviceDVBInput *dvb_input)
47  : Thread(1, 0, 0)
48 {
49         this->dvb_input = dvb_input;
50         signal_status = 0;
51         done = 1;
52 }
53
54 DVBInputStatus::~DVBInputStatus()
55 {
56         stop();
57         if( signal_status ) signal_status->disconnect();
58 }
59
60 void DVBInputStatus::stop()
61 {
62         if( done ) return;
63         done = 1;
64         Thread::cancel();
65         Thread::join();
66 }
67
68 void DVBInputStatus::start()
69 {
70         if( !done ) return;
71         done = 0;
72         Thread::start();
73 }
74
75 void DVBInputStatus::lock_dvb()
76 {
77         Thread::disable_cancel();
78         dvb_input->dvb_lock->lock("DVBInputStatus::lock_dvb");
79 }
80
81 void DVBInputStatus::unlock_dvb()
82 {
83         dvb_input->dvb_lock->unlock();
84         Thread::enable_cancel();
85 }
86
87 void DVBInputStatus::run()
88 {
89         lock_dvb();
90
91         while( !done ) {
92                 dvb_input->status_dev();
93                 update();
94                 unlock_dvb();
95                 usleep(250000);
96                 lock_dvb();
97         }
98
99         unlock_dvb();
100 }
101
102 void DVBInputStatus::update()
103 {
104         if( !signal_status ) return;
105         signal_status->lock_window("DVBInputStatus::update");
106         signal_status->update();
107         signal_status->unlock_window();
108 }
109
110
111 DeviceDVBInput::DeviceDVBInput(const char *name, int no)
112  : DeviceMPEGInput(name, no)
113 {
114         frontend_fd = -1;
115         demux_fd = -1;
116         dvb_fd = -1;
117         reset_signal();
118         dvb_locked = 0;
119         dvb_lock = new Mutex("DeviceDVBInput::dvb_lock", 0);
120         sprintf(frontend_path, "%s/frontend%d", name, no);
121         sprintf(demux_path, "%s/demux%d", name, no);
122         sprintf(dvb_path, "%s/dvr%d", name, no);
123         dvb_input_status = new DVBInputStatus(this);
124 }
125
126 DeviceMPEGInput *DeviceDVBInput::
127 NewDVBInput(const char *name, int no)
128 {
129         return (DeviceMPEGInput *) new DeviceDVBInput(name, no);
130 }
131
132 DeviceDVBInput::~DeviceDVBInput()
133 {
134         close_dev();
135         delete dvb_input_status;
136         delete dvb_lock;
137         dvb_close(1);
138 }
139
140
141 DeviceMPEGInput* DeviceDVBInput::get_mpeg_input(VideoDevice *device)
142 {
143         DeviceMPEGInput* mpeg_device = get_mpeg_video(device, NewDVBInput,
144                 device->in_config->dvb_in_adapter, device->in_config->dvb_in_device);
145         if( !mpeg_device ) return 0;
146         device->channel->has_subchan = 1;
147         device->channel->has_scanning = 1;
148         device->channel->use_frequency = 1;
149         mpeg_device->set_device_number(0);
150         Channel *input_src = device->new_input_source((char*)"input0");
151         input_src->device_index = 0;
152         input_src->tuner = 0;
153         return mpeg_device;
154 }
155
156 DeviceMPEGInput* DeviceDVBInput::get_mpeg_input(AudioDevice *device)
157 {
158         return get_mpeg_audio(device, NewDVBInput,
159                 device->in_config->dvb_in_adapter, device->in_config->dvb_in_device);
160 }
161
162
163 int DeviceDVBInput::wait_signal(int ms, int trys)
164 {
165         Timer timer;
166         for( int retry=trys; --retry>=0; ) {
167                 timer.update();
168                 if( !dvb_status() && has_signal() ) break;
169                 int dt = ms - timer.get_difference();
170                 if( dt > 0 ) Timer::delay(dt);
171         }
172         return has_signal() ? 0 : 1;
173 }
174
175
176 int DeviceDVBBuffer::read(int retries, int usec, int bsz)
177 {
178         while( --retries >= 0 ) {
179                 struct timeval tv;  tv.tv_sec = 0;  tv.tv_usec = usec;
180                 fd_set rd_fd;  FD_ZERO(&rd_fd);  FD_SET(fd, &rd_fd);
181                 fd_set er_fd;  FD_ZERO(&er_fd);  FD_SET(fd, &er_fd);
182                 int ret = select(fd+1, &rd_fd, 0, &er_fd, &tv);
183                 if( ret < 0 ) break;
184                 if( !ret ) continue;
185                 if( !FD_ISSET(fd, &er_fd) ) {
186                         select(0, 0, 0, 0, &tv);
187                         continue;
188                 }
189                 if( !FD_ISSET(fd, &rd_fd) ) continue;
190                 if( (ret=::read(fd, bfr, bsz)) > 0 )
191                         return sz = ret;
192         }
193         return -1;
194 }
195
196 int DeviceDVBInput::dvb_sync()
197 {
198         const int SYNC = 0x47, TS_BLKSZ = 188;
199         const int BSZ = 100*TS_BLKSZ, XSZ = BSZ*5;
200         DeviceDVBBuffer data(dvb_fd, BSZ);
201
202         int nsync = 0, xfr = 0;
203         while( xfr < XSZ ) {
204                 if( data.read(5, 200000, BSZ) < 0 ) break;
205                 int ofs = 0;
206                 while( ofs < data.size() ) {
207                         nsync = 0;
208                         int i = ofs;
209                         while( i<data.size() && data[i]!=SYNC ) ++i;
210                         if( i >= data.size() ) break;
211                         ofs = i;
212                         while( (ofs+=TS_BLKSZ)<data.size() && data[ofs]==SYNC ) {
213                                 ++nsync;  i = ofs;
214                         }
215                 }
216                 if( nsync >= 32 && (ofs-=data.size()) >= 0 ) {
217                         while( ofs > 0 ) {
218                                 if( data.read(5, 200000, ofs) < 0 ) return 1;
219                                 ofs -= data.size();
220                         }
221                         return 0;
222                 }
223                 xfr += data.size();
224         }
225         return 1;
226 }
227
228 // Return values: 0 = OK, 1 = unreported error, 2 = error reported to stderr
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 = 2;
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 = 2;
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 && table >= CHANLIST_SIZE ) ret = 1;
255
256         struct dvb_frontend_parameters frontend_param;
257 #if 1
258         if( !ret ) {
259                 uint32_t frequency = chanlists[table].list[index].freq * 1000;
260                 class dtv_props : public ArrayList<dtv_property> {
261                 public:
262                         void add(int c, int d) {
263                                 dtv_property &p = append();
264                                 memset(&p, 0, sizeof(p));
265                                 p.cmd = c;  p.u.data = d;
266                         }
267                 } props;
268
269                 switch( table ) {
270                 case NTSC_DVB:
271                 case NTSC_BCAST:
272                 case NTSC_HRC:
273                 case NTSC_BCAST_JP:
274                         props.add(DTV_DELIVERY_SYSTEM, SYS_ATSC);
275                         props.add(DTV_FREQUENCY, frequency);
276                         props.add(DTV_INVERSION, INVERSION_AUTO);
277                         props.add(DTV_MODULATION, VSB_8);
278 //                      frontend_param.u.vsb.modulation = VSB_8;
279                         break;
280                 case PAL_EUROPE:
281                 case PAL_E_EUROPE:
282                 case PAL_ITALY:
283                 case PAL_NEWZEALAND:
284                 case PAL_AUSTRALIA:
285                 case PAL_IRELAND:
286                 case CATV_DVB:
287                 case NTSC_CABLE:
288                 case NTSC_CABLE_JP:
289 //                      props.add(DTV_DELIVERY_SYSTEM, SYS_DVBT);
290 //                      if( t->delsys == SYS_DVBT2 ) { props.add(DTV_STREAM_ID, 0 /* id */); }
291                         props.add(DTV_FREQUENCY, frequency /* 174.00MHz ... 862.00MHz */);
292 //                      props.add(DTV_INVERSION, INVERSION_AUTO);
293                         props.add(DTV_BANDWIDTH_HZ, 6000000 /* 6/7/8 Mhz */);
294                         props.add(DTV_CODE_RATE_HP, FEC_AUTO);
295                         props.add(DTV_CODE_RATE_LP, FEC_AUTO);
296                         props.add(DTV_MODULATION, QAM_AUTO);
297                         props.add(DTV_TRANSMISSION_MODE, TRANSMISSION_MODE_AUTO);
298                         props.add(DTV_GUARD_INTERVAL, GUARD_INTERVAL_AUTO);
299                         props.add(DTV_HIERARCHY, HIERARCHY_AUTO);
300 //                      frontend_param.u.qam.modulation = QAM_AUTO;
301                         break;
302                 default:
303                         fprintf(stderr,
304                                 "DeviceDVBInput::dvb_open bad table index=%d\n", table);
305                         ret = 2;
306                         break;
307                 }
308                 struct dtv_properties dtv_props;
309                 memset(&dtv_props, 0, sizeof(dtv_props));
310                 props.add(DTV_TUNE, 0);
311                 dtv_props.num = props.size();
312                 dtv_props.props = &props[0];
313                 if( ioctl(frontend_fd, FE_SET_PROPERTY, &dtv_props) < 0 ) {
314                         fprintf(stderr,
315                                 "DeviceDVBInput::dvb_open FE_SET_PROPERY frequency=%d: %s\n",
316                                 frequency, strerror(errno));
317                         ret = 2;
318                 }
319         }
320
321 #else
322 // older version
323         if( !ret && ioctl(frontend_fd, FE_SET_FRONTEND, &frontend_param) < 0 ) {
324                 fprintf(stderr,
325                         "DeviceDVBInput::dvb_open FE_SET_FRONTEND frequency=%d: %s\n",
326                         frontend_param.frequency, strerror(errno));
327                 ret = 2;
328         }
329
330         if( !ret ) {
331                 uint32_t frequency = chanlists[table].list[index].freq * 1000;
332                 if( frequency < fe_info.frequency_min ||
333                         frequency > fe_info.frequency_max )  {
334                         fprintf(stderr,
335                                 "DeviceDVBInput::dvb_open channel %s frequency %d out of range %d-%d\n",
336                                 chanlists[table].list[index].name, frequency, fe_info.frequency_min, fe_info.frequency_max);
337                          ret = 2;
338                 }
339                 memset(&frontend_param, 0, sizeof(frontend_param));
340                 frontend_param.frequency = frequency;
341         }
342         if( !ret ) {
343                 switch( table ) {
344                 case NTSC_DVB:
345                 case NTSC_BCAST:
346                 case NTSC_HRC:
347                 case NTSC_BCAST_JP:
348                         frontend_param.u.vsb.modulation = VSB_8;
349                         break;
350                 case PAL_EUROPE:
351                 case PAL_E_EUROPE:
352                 case PAL_ITALY:
353                 case PAL_NEWZEALAND:
354                 case PAL_AUSTRALIA:
355                 case PAL_IRELAND:
356                 case CATV_DVB:
357                 case NTSC_CABLE:
358                 case NTSC_CABLE_JP:
359                         frontend_param.u.vsb.modulation = QAM_AUTO;
360                         break;
361                 default:
362                         fprintf(stderr,
363                                 "DeviceDVBInput::dvb_open bad table index=%d\n", table);
364                         ret = 2;
365                         break;
366                 }
367         }
368
369         if( !ret && ioctl(frontend_fd, FE_SET_FRONTEND, &frontend_param) < 0 ) {
370                 fprintf(stderr,
371                         "DeviceDVBInput::dvb_open FE_SET_FRONTEND frequency=%d: %s\n",
372                         frontend_param.frequency, strerror(errno));
373                 ret = 2;
374         }
375 #endif
376
377         if( !ret && wait_signal(333,3) ) {
378                 fprintf(stderr,
379                          "DeviceDVBInput::dvb_open: no signal, index=%d, channel %s, frequency=%d\n",
380                         index, chanlists[table].list[index].name, frontend_param.frequency);
381                 ret = 2;
382         }
383
384         if( !ret && ioctl(frontend_fd, FE_GET_FRONTEND, &frontend_param) ) {
385                 fprintf(stderr,
386                         "DeviceDVBInput::dvb_open FE_GET_FRONTEND: %s\n",
387                         strerror(errno));
388                 ret = 2;
389         }
390         if( !ret ) { // goofy quirks
391                 if( !strcmp("Samsung S5H1409 QAM/8VSB Frontend", fe_info.name) ) {
392                         switch(frontend_param.u.vsb.modulation) {
393                         case QAM_64:  snr_min = 200;  snr_max = 300;  break;
394                         case QAM_256: snr_min = 260;  snr_max = 400;  break;
395                         case VSB_8:   snr_min = 111;  snr_max = 300;  break;
396                         default: break;
397                         }
398                         pwr_min = snr_min;  pwr_max = snr_max;
399                 }
400                 else if( !strcmp("Auvitek AU8522 QAM/8VSB Frontend", fe_info.name) ) {
401                         switch(frontend_param.u.vsb.modulation) {
402                         case QAM_64:  snr_min = 190;  snr_max = 290;  break;
403                         case QAM_256: snr_min = 280;  snr_max = 400;  break;
404                         case VSB_8:   snr_min = 115;  snr_max = 270;  break;
405                         default: break;
406                         }
407                         pwr_min = snr_min;  pwr_max = snr_max;
408                 }
409         }
410
411         if( !ret && (demux_fd = ::open(demux_path, O_RDWR)) < 0 ) {
412                 fprintf(stderr,
413                         "DeviceDVBInput::dvb_open %s for video: %s\n",
414                         demux_path,
415                         strerror(errno));
416                 ret = 2;
417         }
418
419 // Setting exactly one PES filter to 0x2000 dumps the entire
420 // transport stream.
421         if( !ret ) {
422                 struct dmx_pes_filter_params pesfilter;
423                 memset(&pesfilter,0,sizeof(pesfilter));
424                 pesfilter.pid = 0x2000;
425                 pesfilter.input = DMX_IN_FRONTEND;
426                 pesfilter.output = DMX_OUT_TS_TAP;
427                 pesfilter.pes_type = DMX_PES_OTHER;
428                 pesfilter.flags = DMX_IMMEDIATE_START;
429                 if( ioctl(demux_fd, DMX_SET_PES_FILTER, &pesfilter) < 0 ) {
430                         fprintf(stderr,
431                 "DeviceDVBInput::dvb_open DMX_SET_PES_FILTER for raw: %s\n",
432                                 strerror(errno));
433                         ret = 1;
434                 }
435         }
436 // Open transport stream for reading
437         if( !ret && (dvb_fd = ::open(dvb_path, O_RDONLY+O_NONBLOCK)) < 0 ) {
438                 fprintf(stderr, "DeviceDVBInput::dvb_open %s: %s\n",
439                         dvb_path, strerror(errno));
440                 ret = 1;
441         }
442 #else
443         dvb_fd = -1;
444         ret = 1;
445 #endif
446 #else
447         dvb_fd = open(TEST_DATA,O_RDONLY);
448         if( dvb_fd < 0 ) ret = 1;
449 #endif
450         reset_signal();
451         dvb_locked = 0;
452         if( !ret ) ret = dvb_sync();
453         if( !ret ) ret = dvb_status();
454         if( ret ) dvb_close();
455         return ret;
456 }
457
458
459 void DeviceDVBInput::dvb_close(int fe)
460 {
461         if( dvb_fd >= 0 ) { ::close(dvb_fd); dvb_fd = -1; }
462         if( demux_fd >= 0 ) { ::close(demux_fd); demux_fd = -1; }
463         if( fe && frontend_fd >= 0 ) { ::close(frontend_fd); frontend_fd = -1; }
464 }
465
466
467 int DeviceDVBInput::dvb_status()
468 {
469 //note: this function can take > 500ms to execute  (slowww)
470 #ifndef TEST_DATA
471         int locked = 0;
472         reset_signal();
473 #ifdef HAVE_DVB
474         int &fe = frontend_fd;
475         if( fe < 0 ) return -1;
476         fe_status_t st;  memset(&st, 0, sizeof(st));
477         if( ioctl(fe, FE_READ_STATUS, &st) ) return 1;
478         if( (st & FE_TIMEDOUT) != 0 ) return 1;
479         signal_lck = (st & FE_HAS_LOCK) != 0 ? 1 : 0;
480         signal_crr = (st & FE_HAS_CARRIER) != 0 ? 1 : 0;
481         uint16_t power = 0;
482         signal_pwr = ioctl(fe,FE_READ_SIGNAL_STRENGTH,&power) ? -1 :
483                 drange(power, pwr_min, pwr_max);
484         uint16_t ratio = 0;
485         signal_snr = ioctl(fe, FE_READ_SNR, &ratio) ? -1 :
486                 drange(ratio, snr_min, snr_max);
487         uint32_t rate = 0;
488         signal_ber = ioctl(fe, FE_READ_BER, &rate) ? -1 : rate;
489         uint32_t errs = 0;
490         signal_unc = ioctl(fe, FE_READ_UNCORRECTED_BLOCKS, &errs) ? -1 : errs;
491         if( signal_lck && signal_ber < 255 ) locked = 1;
492         if( dvb_locked != locked ) {
493                 printf(_("** %scarrier, dvb_locked %s\n"),
494                          signal_crr ? "" : _("no "), locked ? _("lock") : _("lost") );
495         }
496 #endif
497         dvb_locked = locked;
498 #endif
499         return 0;
500 }
501
502 int DeviceDVBInput::drange(int v, int min, int max)
503 {
504         if( (v-=min) < 0 || (max-=min) <= 0 ) return 0;
505         return v>max ? 65535U : (65535U * v) / max;
506 }
507
508 void DeviceDVBInput::reset_signal()
509 {
510         signal_pwr = signal_crr = signal_lck =
511         signal_snr = signal_ber = signal_unc = -1;
512 }
513
514 int DeviceDVBInput::open_dev(int color_model)
515 {
516         int ret = 0;
517         dvb_lock->lock("DeviceDVBInput::open_dev");
518         if( dvb_fd < 0 ) {
519                 ret = dvb_open();
520                 if( ret == 1 ) {
521                         printf("DeviceDVBInput::open_dev: adaptor %s open failed\n",
522                                 dev_name);
523                 }
524         }
525         if( !ret )
526                 dvb_input_status->start();
527         else
528                 dvb_close();
529         dvb_lock->unlock();
530         return ret;
531 }
532
533 void DeviceDVBInput::close_dev()
534 {
535         dvb_lock->lock("DeviceDVBInput::close_dev");
536         dvb_input_status->stop();
537         dvb_close();
538         dvb_lock->unlock();
539 }
540
541 int DeviceDVBInput::status_dev()
542 {
543         int result = 0;
544 #ifndef TEST_DATA
545         result = frontend_fd >= 0 ? dvb_status() : 1;
546 #endif
547         return result;
548 }
549
550 void DeviceDVBInput::set_signal_status(SignalStatus *stat)
551 {
552         dvb_lock->lock("DeviceDVBInput::set_signal_status");
553         if( dvb_input_status->signal_status )
554                 dvb_input_status->signal_status->disconnect();
555         dvb_input_status->signal_status = stat;
556         if( stat ) stat->dvb_input = this;
557         dvb_lock->unlock();
558 }
559
560 #endif