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"
9 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
28 #include "chantables.h"
29 #include "devicedvbinput.h"
30 #include "devicempeginput.h"
31 #include "recordconfig.h"
32 #include "signalstatus.h"
41 #include <linux/dvb/dmx.h>
42 #include <linux/dvb/frontend.h>
43 #include <sys/ioctl.h>
46 DVBInputStatus::DVBInputStatus(DeviceDVBInput *dvb_input)
49 this->dvb_input = dvb_input;
54 DVBInputStatus::~DVBInputStatus()
57 if( signal_status ) signal_status->disconnect();
60 void DVBInputStatus::stop()
68 void DVBInputStatus::start()
75 void DVBInputStatus::lock_dvb()
77 Thread::disable_cancel();
78 dvb_input->dvb_lock->lock("DVBInputStatus::lock_dvb");
81 void DVBInputStatus::unlock_dvb()
83 dvb_input->dvb_lock->unlock();
84 Thread::enable_cancel();
87 void DVBInputStatus::run()
92 dvb_input->status_dev();
102 void DVBInputStatus::update()
104 if( !signal_status ) return;
105 signal_status->lock_window("DVBInputStatus::update");
106 signal_status->update();
107 signal_status->unlock_window();
111 DeviceDVBInput::DeviceDVBInput(const char *name, int no)
112 : DeviceMPEGInput(name, no)
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);
126 DeviceMPEGInput *DeviceDVBInput::
127 NewDVBInput(const char *name, int no)
129 return (DeviceMPEGInput *) new DeviceDVBInput(name, no);
132 DeviceDVBInput::~DeviceDVBInput()
135 delete dvb_input_status;
141 DeviceMPEGInput* DeviceDVBInput::get_mpeg_input(VideoDevice *device)
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;
156 DeviceMPEGInput* DeviceDVBInput::get_mpeg_input(AudioDevice *device)
158 return get_mpeg_audio(device, NewDVBInput,
159 device->in_config->dvb_in_adapter, device->in_config->dvb_in_device);
163 int DeviceDVBInput::wait_signal(int ms, int trys)
166 for( int retry=trys; --retry>=0; ) {
168 if( !dvb_status() && has_signal() ) break;
169 int dt = ms - timer.get_difference();
170 if( dt > 0 ) Timer::delay(dt);
172 return has_signal() ? 0 : 1;
176 int DeviceDVBBuffer::read(int retries, int usec, int bsz)
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);
185 if( !FD_ISSET(fd, &er_fd) ) {
186 select(0, 0, 0, 0, &tv);
189 if( !FD_ISSET(fd, &rd_fd) ) continue;
190 if( (ret=::read(fd, bfr, bsz)) > 0 )
196 int DeviceDVBInput::dvb_sync()
198 const int SYNC = 0x47, TS_BLKSZ = 188;
199 const int BSZ = 100*TS_BLKSZ, XSZ = BSZ*5;
200 DeviceDVBBuffer data(dvb_fd, BSZ);
202 int nsync = 0, xfr = 0;
204 if( data.read(5, 200000, BSZ) < 0 ) break;
206 while( ofs < data.size() ) {
209 while( i<data.size() && data[i]!=SYNC ) ++i;
210 if( i >= data.size() ) break;
212 while( (ofs+=TS_BLKSZ)<data.size() && data[ofs]==SYNC ) {
216 if( nsync >= 32 && (ofs-=data.size()) >= 0 ) {
218 if( data.read(5, 200000, ofs) < 0 ) return 1;
228 // Return values: 0 = OK, 1 = unreported error, 2 = error reported to stderr
229 int DeviceDVBInput::dvb_open()
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));
240 if( !ret && ioctl(frontend_fd, FE_GET_INFO, &fe_info) < 0 ) {
242 "DeviceDVBInput::dvb_open FE_GET_INFO: %s\n",
247 pwr_min = snr_min = 0;
248 pwr_max = snr_max = 65535;
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;
256 struct dvb_frontend_parameters frontend_param;
259 uint32_t frequency = chanlists[table].list[index].freq * 1000;
260 class dtv_props : public ArrayList<dtv_property> {
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;
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;
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;
304 "DeviceDVBInput::dvb_open bad table index=%d\n", table);
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 ) {
315 "DeviceDVBInput::dvb_open FE_SET_PROPERY frequency=%d: %s\n",
316 frequency, strerror(errno));
323 if( !ret && ioctl(frontend_fd, FE_SET_FRONTEND, &frontend_param) < 0 ) {
325 "DeviceDVBInput::dvb_open FE_SET_FRONTEND frequency=%d: %s\n",
326 frontend_param.frequency, strerror(errno));
331 uint32_t frequency = chanlists[table].list[index].freq * 1000;
332 if( frequency < fe_info.frequency_min ||
333 frequency > fe_info.frequency_max ) {
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);
339 memset(&frontend_param, 0, sizeof(frontend_param));
340 frontend_param.frequency = frequency;
348 frontend_param.u.vsb.modulation = VSB_8;
359 frontend_param.u.vsb.modulation = QAM_AUTO;
363 "DeviceDVBInput::dvb_open bad table index=%d\n", table);
369 if( !ret && ioctl(frontend_fd, FE_SET_FRONTEND, &frontend_param) < 0 ) {
371 "DeviceDVBInput::dvb_open FE_SET_FRONTEND frequency=%d: %s\n",
372 frontend_param.frequency, strerror(errno));
377 if( !ret && wait_signal(333,3) ) {
379 "DeviceDVBInput::dvb_open: no signal, index=%d, channel %s, frequency=%d\n",
380 index, chanlists[table].list[index].name, frontend_param.frequency);
384 if( !ret && ioctl(frontend_fd, FE_GET_FRONTEND, &frontend_param) ) {
386 "DeviceDVBInput::dvb_open FE_GET_FRONTEND: %s\n",
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;
398 pwr_min = snr_min; pwr_max = snr_max;
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;
407 pwr_min = snr_min; pwr_max = snr_max;
411 if( !ret && (demux_fd = ::open(demux_path, O_RDWR)) < 0 ) {
413 "DeviceDVBInput::dvb_open %s for video: %s\n",
419 // Setting exactly one PES filter to 0x2000 dumps the entire
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 ) {
431 "DeviceDVBInput::dvb_open DMX_SET_PES_FILTER for raw: %s\n",
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));
447 dvb_fd = open(TEST_DATA,O_RDONLY);
448 if( dvb_fd < 0 ) ret = 1;
452 if( !ret ) ret = dvb_sync();
453 if( !ret ) ret = dvb_status();
454 if( ret ) dvb_close();
459 void DeviceDVBInput::dvb_close(int fe)
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; }
467 int DeviceDVBInput::dvb_status()
469 //note: this function can take > 500ms to execute (slowww)
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;
482 signal_pwr = ioctl(fe,FE_READ_SIGNAL_STRENGTH,&power) ? -1 :
483 drange(power, pwr_min, pwr_max);
485 signal_snr = ioctl(fe, FE_READ_SNR, &ratio) ? -1 :
486 drange(ratio, snr_min, snr_max);
488 signal_ber = ioctl(fe, FE_READ_BER, &rate) ? -1 : rate;
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") );
502 int DeviceDVBInput::drange(int v, int min, int max)
504 if( (v-=min) < 0 || (max-=min) <= 0 ) return 0;
505 return v>max ? 65535U : (65535U * v) / max;
508 void DeviceDVBInput::reset_signal()
510 signal_pwr = signal_crr = signal_lck =
511 signal_snr = signal_ber = signal_unc = -1;
514 int DeviceDVBInput::open_dev(int color_model)
517 dvb_lock->lock("DeviceDVBInput::open_dev");
521 printf("DeviceDVBInput::open_dev: adaptor %s open failed\n",
526 dvb_input_status->start();
533 void DeviceDVBInput::close_dev()
535 dvb_lock->lock("DeviceDVBInput::close_dev");
536 dvb_input_status->stop();
541 int DeviceDVBInput::status_dev()
545 result = frontend_fd >= 0 ? dvb_status() : 1;
550 void DeviceDVBInput::set_signal_status(SignalStatus *stat)
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;