class Recorder: def __init__(self): logger.debug('Recorder.__init__()') # Disable this plugin if not loaded by record_server. if string.find(sys.argv[0], 'recordserver') == -1: return logger.info('ACTIVATING GENERIC RECORD PLUGIN') self.fc = FreevoChannels() self.thread = Record_Thread() self.thread.setDaemon(1) self.thread.mode = 'idle' self.thread.start() def Record(self, rec_prog): logger.debug('Record(rec_prog=%r)', rec_prog) vg = self.fc.getVideoGroup(rec_prog.tunerid, False) frequency = self.fc.chanSet(str(rec_prog.tunerid), False, 'record plugin') rec_prog.filename = tv_util.getProgFilename(rec_prog) cl_options = { 'channel' : rec_prog.tunerid, 'frequency' : frequency, 'frequencyMHz' : float(frequency) / 1000, 'filename' : rec_prog.filename, 'base_filename' : os.path.basename(rec_prog.filename), 'title' : rec_prog.title, 'sub-title' : rec_prog.sub_title, 'seconds' : rec_prog.rec_duration, 'start' : rec_prog.start, 'pdc-start' : rec_prog.pdc_start, 'group-type' : vg.group_type } if isinstance(config.VCR_CMD, str) or isinstance(config.VCR_CMD, unicode): self.rec_command = config.VCR_CMD % cl_options elif isinstance(config.VCR_CMD, list) or isinstance(config.VCR_CMD, tuple): self.rec_command = [] for arg in config.VCR_CMD: self.rec_command.append(arg % cl_options) self.thread.mode = 'record' self.thread.prog = rec_prog self.thread.command = self.rec_command self.thread.autokill = float(rec_prog.rec_duration + 10) self.thread.mode_flag.set() logger.debug('Recorder::Record: %s', self.rec_command) def Stop(self): logger.debug('Stop()') self.thread.mode = 'stop' self.thread.mode_flag.set()
def run(self): while 1: if DEBUG: print('Record_Thread::run: mode=%s' % self.mode) if self.mode == 'idle': self.mode_flag.wait() self.mode_flag.clear() elif self.mode == 'record': rc.post_event(Event('RECORD_START', arg=self.prog)) if DEBUG: print 'Record_Thread::run: started recording' fc = FreevoChannels() if DEBUG: print 'CHAN: %s' % fc.getChannel() (v_norm, v_input, v_clist, v_dev) = config.TV_SETTINGS.split() v = tv.ivtv.IVTV(v_dev) v.init_settings() vg = fc.getVideoGroup(self.prog.tunerid, False) if DEBUG: print 'Setting Input to %s' % vg.input_num v.setinput(vg.input_num) if DEBUG: print 'Setting Channel to %s' % self.prog.tunerid fc.chanSet(str(self.prog.tunerid), False) if DEBUG: v.print_settings() now = time.time() stop = now + self.prog.rec_duration time.sleep(2) v_in = open(v_dev, 'r') v_out = open(self.prog.filename, 'w') while time.time() < stop: buf = v_in.read(CHUNKSIZE) v_out.write(buf) if self.mode == 'stop': break v_in.close() v_out.close() v.close() v = None self.mode = 'idle' rc.post_event(Event('RECORD_STOP', arg=self.prog)) if DEBUG: print('Record_Thread::run: finished recording') else: self.mode = 'idle' time.sleep(0.5)
class Recorder: def __init__(self): logger.debug("Recorder.__init__()") # Disable this plugin if not loaded by record_server. if string.find(sys.argv[0], "recordserver") == -1: return logger.info("ACTIVATING GENERIC RECORD PLUGIN") self.fc = FreevoChannels() self.thread = Record_Thread() self.thread.setDaemon(1) self.thread.mode = "idle" self.thread.start() def Record(self, rec_prog): logger.debug("Record(rec_prog=%r)", rec_prog) vg = self.fc.getVideoGroup(rec_prog.tunerid, False) frequency = self.fc.chanSet(str(rec_prog.tunerid), False, "record plugin") rec_prog.filename = tv_util.getProgFilename(rec_prog) cl_options = { "channel": rec_prog.tunerid, "frequency": frequency, "frequencyMHz": float(frequency) / 1000, "filename": rec_prog.filename, "base_filename": os.path.basename(rec_prog.filename), "title": rec_prog.title, "sub-title": rec_prog.sub_title, "seconds": rec_prog.rec_duration, "start": rec_prog.start, "pdc-start": rec_prog.pdc_start, "group-type": vg.group_type, } if isinstance(config.VCR_CMD, str) or isinstance(config.VCR_CMD, unicode): self.rec_command = config.VCR_CMD % cl_options elif isinstance(config.VCR_CMD, list) or isinstance(config.VCR_CMD, tuple): self.rec_command = [] for arg in config.VCR_CMD: self.rec_command.append(arg % cl_options) self.thread.mode = "record" self.thread.prog = rec_prog self.thread.command = self.rec_command self.thread.autokill = float(rec_prog.rec_duration + 10) self.thread.mode_flag.set() logger.debug("Recorder::Record: %s", self.rec_command) def Stop(self): logger.debug("Stop()") self.thread.mode = "stop" self.thread.mode_flag.set()
class Record_Thread(threading.Thread): """ Thread class that actually does the recording. """ def __init__(self, manager): """ Initialise the recording thread passing in the DVBStreamerControl object to use to control instances of dvbstreamer. """ threading.Thread.__init__(self) self.fc = FreevoChannels() self.manager = manager self.mode = 'idle' self.mode_flag = threading.Event() self.prog = None self.app = None def run(self): """ Thread loop. """ while 1: if DEBUG: print('Record_Thread::run: mode=%s' % self.mode) if self.mode == 'idle': self.mode_flag.wait() self.mode_flag.clear() elif self.mode == 'record': rc.post_event(Event('RECORD_START', arg=self.prog)) if DEBUG: print('Record_Thread::run: started recording') prog = self.prog filename = tv_util.getProgFilename(prog) vg = self.fc.getVideoGroup(prog.tunerid, False) adapter = vg.vdev seconds = prog.rec_duration # Select the channel and start streaming to the file. self.manager.select(adapter, prog.tunerid) self.manager.enable_file_output(adapter,filename) while (self.mode == 'record') and (seconds > 0): seconds -= 0.5 time.sleep(0.5) # Close the file self.manager.disable_output(adapter) rc.post_event(Event('RECORD_STOP', arg=self.prog)) if DEBUG: print('Record_Thread::run: finished recording') self.mode = 'idle' else: self.mode = 'idle' time.sleep(0.5)
class Record_Thread(threading.Thread): """ Thread class that actually does the recording. """ def __init__(self, manager): """ Initialise the recording thread passing in the DVBStreamerControl object to use to control instances of dvbstreamer. """ threading.Thread.__init__(self) self.fc = FreevoChannels() self.manager = manager self.mode = 'idle' self.mode_flag = threading.Event() self.prog = None self.app = None def run(self): """ Thread loop. """ while 1: if DEBUG: print('Record_Thread::run: mode=%s' % self.mode) if self.mode == 'idle': self.mode_flag.wait() self.mode_flag.clear() elif self.mode == 'record': rc.post_event(Event('RECORD_START', arg=self.prog)) if DEBUG: print('Record_Thread::run: started recording') prog = self.prog filename = tv_util.getProgFilename(prog) vg = self.fc.getVideoGroup(prog.channel_id, False) adapter = int(vg.vdev) seconds = prog.rec_duration # Select the channel and start streaming to the file. self.manager.select(adapter, prog.tunerid) self.manager.enable_file_output(adapter, filename) while (self.mode == 'record') and (seconds > 0): seconds -= 0.5 time.sleep(0.5) # Close the file self.manager.disable_output(adapter) rc.post_event(Event('RECORD_STOP', arg=self.prog)) if DEBUG: print('Record_Thread::run: finished recording') self.mode = 'idle' else: self.mode = 'idle' time.sleep(0.5)
def run(self): while 1: if DEBUG: print('Record_Thread::run: mode=%s' % self.mode) if self.mode == 'idle': self.mode_flag.wait() self.mode_flag.clear() elif self.mode == 'record': rc.post_event(Event('RECORD_START', arg=self.prog)) if DEBUG: print('Record_Thread::run: cmd=%s' % self.command) fc = FreevoChannels() if DEBUG: print 'CHAN: %s' % fc.getChannel() (v_norm, v_input, v_clist, v_dev) = config.TV_SETTINGS.split() v = tv.ivtv.IVTV(v_dev) v.init_settings() self.vg = fc.getVideoGroup(self.prog.tunerid, False) if DEBUG: print 'Using video device %s' % self.vg.vdev if DEBUG: print 'Setting Input to %s' % self.vg.input_num v.setinput(self.vg.input_num) if DEBUG: print 'Setting Channel to %s' % self.prog.tunerid fc.chanSet(str(self.prog.tunerid), False) if DEBUG: v.print_settings() self.app = RecordApp(self.command) while self.mode == 'record' and self.app.isAlive(): self.autokill -= 0.5 time.sleep(0.5) if self.autokill <= 0: if DEBUG: print 'autokill timeout, stopping recording' self.mode = 'stop' if self.app.isAlive(): # might not want to do this is PDC is valid, programme may be delayed if DEBUG: print('Record_Thread::run: past wait!!') rc.post_event( Event(OS_EVENT_KILL, (self.app.child.pid, 15))) self.app.kill() rc.post_event(Event('RECORD_STOP', arg=self.prog)) if DEBUG: print('Record_Thread::run: finished recording') self.mode = 'idle' else: self.mode = 'idle' time.sleep(0.5)
def run(self): _debug_('Record_Thread.run()', 2) while 1: _debug_('mode=%s' % self.mode) if self.mode == 'idle': self.mode_flag.wait() self.mode_flag.clear() elif self.mode == 'record': rc.post_event(Event('RECORD_START', arg=self.prog)) fc = FreevoChannels() _debug_('channel %s' % fc.getChannel()) self.vg = fc.getVideoGroup(self.prog.tunerid, False) v = tv.ivtv.IVTV(self.vg.vdev) v.init_settings() _debug_('Using video device=%r' % self.vg.vdev) _debug_('Setting Channel to %r' % self.prog.tunerid) fc.chanSet(str(self.prog.tunerid), False) _debug_('command %r' % self.command) self.app = RecordApp(self.command) _debug_('app child pid: %s' % self.app.child.pid) while self.mode == 'record' and self.app.isAlive(): self.autokill -= 0.5 time.sleep(0.5) if self.autokill <= 0: _debug_('autokill timeout, stopping recording') self.mode = 'stop' if self.app.isAlive(): # might not want to do this is PDC is valid, programme may be delayed _debug_('past wait!!') rc.post_event( Event(OS_EVENT_KILL, (self.app.child.pid, 15))) self.app.kill() rc.post_event(Event('RECORD_STOP', arg=self.prog)) _debug_('finished recording') self.mode = 'idle' else: self.mode = 'idle' time.sleep(0.5)
def run(self): logger.log( 9, 'Record_Thread.run()') while 1: logger.debug('mode=%s', self.mode) if self.mode == 'idle': self.mode_flag.wait() self.mode_flag.clear() elif self.mode == 'record': rc.post_event(Event('RECORD_START', arg=self.prog)) fc = FreevoChannels() logger.debug('channel %s', fc.getChannel()) self.vg = fc.getVideoGroup(self.prog.tunerid, False) v = tv.ivtv.IVTV(self.vg.vdev) v.init_settings() logger.debug('Using video device=%r', self.vg.vdev) logger.debug('Setting Channel to %r', self.prog.tunerid) fc.chanSet(str(self.prog.tunerid), False) logger.debug('command %r', self.command) self.app = RecordApp(self.command) logger.debug('command pid: %s', self.app.child.pid) while self.mode == 'record' and self.app.isAlive(): self.autokill -= 0.5 time.sleep(0.5) if self.autokill <= 0: logger.debug('autokill timeout, stopping recording') self.mode = 'stop' if self.app.isAlive(): # might not want to do this is PDC is valid, programme may be delayed logger.debug('past wait!!') self.app.kill() rc.post_event(Event('RECORD_STOP', arg=self.prog)) logger.debug('finished recording') self.mode = 'idle' else: self.mode = 'idle' time.sleep(0.5)
def play(self, arg=None, menuw=None): """ Start watching TV """ logger.log(9, 'play(arg=%r, menuw=%r)', arg, menuw) # watching TV should only be possible from the guide if not self.context == 'guide': rc.post_event(MENU_SELECT) return now = time.time() if menuw: menuw.delete_submenu() # Check if the selected program is >7 min in the future if self.prog.start > now + (7 * 60): if menuw: menuw.show() # this program is in the future if self.scheduled: msgtext = _( 'Do you want to remove the Program from the record schedule?' ) confirmbtn = _('Remove') else: msgtext = _( 'This Program is in the future. Do you want to record it?') confirmbtn = _('Record') dialog.show_confirmation(msgtext, lambda: self.toggle_rec(menuw=menuw), proceed_text=confirmbtn) return else: # check if the device is free fc = FreevoChannels() # for that we need the name of the lock file suffix = fc.getVideoGroup(self.prog.channel_id, True, CHANNEL_ID).vdev suffix = suffix.split('/')[-1] tvlockfile = config.FREEVO_CACHEDIR + '/record.' + suffix if os.path.exists(tvlockfile): if menuw: menuw.show() # XXX: In the future add the options to watch what we are # recording or cancel it and watch TV. msgtext = _('Sorry, you cannot watch TV while recording. ') msgtext += _('If this is not true then remove ') msgtext += tvlockfile + '.' dialog.show_alert(msgtext) else: # everything is ok, we can start watching! self.parent.hide() self.parent.player('tv', self.prog.channel_id)
def writeTvtimeXML(self): tvtimexml = os.path.join(os.environ['HOME'], '.tvtime', 'tvtime.xml') configcmd = os.path.join(os.path.dirname(config.TVTIME_CMD), "tvtime-configure") #cf_norm, cf_input, cf_clist, cf_device = config.TV_SETTINGS.split() fc = FreevoChannels() vg = fc.getVideoGroup(config.TV_CHANNELS[0][2], True) cf_norm = vg.tuner_norm cf_input = vg.input_num cf_device = vg.vdev s_norm = cf_norm.upper() daoptions = '' if os.path.isfile(tvtimexml): daoptions = ' -F ' + tvtimexml if self.xmltv_supported: daoptions += ' -d %s -n %s -t %s' % (cf_device, s_norm, config.XMLTV_FILE) else: daoptions += ' -d %s -n %s' % (cf_device, s_norm) if hasattr(config, "TV_TVTIME_SETUP_OPTS") and config.TV_TVTIME_SETUP_OPTS: daoptions += ' %s' % config.TV_TVTIME_SETUP_OPTS os.system(configcmd+daoptions)
def play(self, arg=None, menuw=None): """ Start watching TV """ logger.log( 9, 'play(arg=%r, menuw=%r)', arg, menuw) # watching TV should only be possible from the guide if not self.context == 'guide': rc.post_event(MENU_SELECT) return now = time.time() if menuw: menuw.delete_submenu() # Check if the selected program is >7 min in the future if self.prog.start > now + (7*60): if menuw: menuw.show() # this program is in the future if self.scheduled: msgtext= _('Do you want to remove the Program from the record schedule?') confirmbtn = _('Remove') else: msgtext = _('This Program is in the future. Do you want to record it?') confirmbtn = _('Record') dialog.show_confirmation(msgtext, lambda: self.toggle_rec(menuw=menuw), proceed_text=confirmbtn) return else: # check if the device is free fc = FreevoChannels() # for that we need the name of the lock file suffix = fc.getVideoGroup(self.prog.channel_id, True, CHANNEL_ID).vdev suffix = suffix.split('/')[-1] tvlockfile = config.FREEVO_CACHEDIR + '/record.'+suffix if os.path.exists(tvlockfile): if menuw: menuw.show() # XXX: In the future add the options to watch what we are # recording or cancel it and watch TV. msgtext = _('Sorry, you cannot watch TV while recording. ') msgtext += _('If this is not true then remove ') msgtext += tvlockfile + '.' dialog.show_alert(msgtext) else: # everything is ok, we can start watching! self.parent.hide() self.parent.player('tv', self.prog.channel_id)
kaa.main.run() elif function == 'findnextprogram': def handler(result): print 'findnextprogram=%r' % (result, ) print result.__dict__ raise SystemExit server.findNextProgram(handler) kaa.main.run() elif function == 'isplayerrunning': def handler(result): print 'isplayerrunning=%r' % (result, ) raise SystemExit server.isPlayerRunning(handler) kaa.main.run() else: fc = FreevoChannels() vg = fc.getVideoGroup('K10', False) print 'vg=%s' % vg print 'dir(%s)' % dir(vg) for it in dir(vg): print ' %s:%s' % (it, eval('vg.' + it)) vdev = vg.vdev print 'vdev=%s' % vdev
class PluginInterface(plugin.DaemonPlugin): """ plugin to monitor if a recording is about to start and shut down the player if the video device is in use To activate this plugin, just put the following line at the end of your local_conf.py file: | plugin.activate('tv.upsoon') """ __author__ = 'Duncan Webb' __author_email__ = '*****@*****.**' __maintainer__ = __author__ __maintainer_email__ = __author_email__ __version__ = '$Revision: 10559 $' def __init__(self, standalone=False): """ init the upsoon plugin """ _debug_('upsoon.PluginInterface.__init__()', 2) plugin.DaemonPlugin.__init__(self) plugin.register(self, 'upsoon') self.standalone = standalone self.lock = thread.allocate_lock() self.running = True self.timer = Timer(self.timer_handler).start(15) self.event = EventHandler(self.event_handler) #self.event.register(('VIDEO_START', 'VIDEO_END')) self.event.register() self.recordclient = RecordClient() self.fc = FreevoChannels() self.rdev = config.RADIO_DEVICE self.next_program = None self.seconds_before_announce = 120 self.seconds_before_start = 60 self.pending_lockfile = config.FREEVO_CACHEDIR + '/record.soon' self.tv_lockfile = None # lockfile of recordserver self.stopped = None # flag that tells upsoon what stopped def findNextProgramHandler(self, result): """ Handles the result from the findNextProgram call """ _debug_('findNextProgramHandler(result=%r)' % (result, ), 2) (status, self.next_program) = result if not status: return now = time.time() _debug_( 'now=%s next=%s ' % (time.strftime('%T', time.localtime(now)), self.next_program), 2) self.vdev = self.getVideoForChannel(self.next_program.channel_id) self.tv_lockfile = os.path.join(config.FREEVO_CACHEDIR, 'record.' + self.vdev.split('/')[-1]) self.seconds_to_next = self.next_program.start - config.TV_RECORD_PADDING_PRE - int( now + 0.5) _debug_('next recording in %s secs' % (self.seconds_to_next), 2) # announce 120 seconds before recording is due to start # stop the player 60 seconds before recording is due to start if (self.seconds_to_next > self.seconds_before_announce): return if (self.seconds_to_next <= self.seconds_before_start): if not os.path.exists(self.pending_lockfile): open(self.pending_lockfile, 'w').close() _debug_('%r lockfile created' % (self.pending_lockfile)) _debug_('stopping video or radio player') self.stopVideoInUse(self.vdev) self.stopRadioInUse(self.rdev) return def getVideoForChannel(self, channel_id): """ get the video device given a channel id """ _debug_('getVideoForChannel(channel_id=%r)' % (channel_id), 2) return self.fc.getVideoGroup(channel_id, False).vdev def stopVideoInUse(self, vdev): """ stop the video device if being used """ _debug_('stopVideoInUse(vdev=%r)' % (vdev), 2) if vdev: try: dev_fh = os.open(vdev, os.O_TRUNC) try: os.read(dev_fh, 1) except: if (self.seconds_to_next > self.seconds_before_start): # just announce rc.post_event( Event( OSD_MESSAGE, arg=_('A recording will start in a few minutes' ))) else: # stop the tv rc.post_event(STOP) self.stopped = _('TV') os.close(dev_fh) except Exception, e: print '%r: %s' % (vdev, e) _debug_('cannot check video device \"%s\"' % (vdev), DINFO)
class Xawtv: __muted = 0 __igainvol = 0 def __init__(self, app, remote): self.tuner_chidx = 0 # Current channel, index into config.TV_CHANNELS self.event_context = 'tv' self.fc = FreevoChannels() self.current_vg = None self.xawtv_prog = app self.remote_prog = remote def TunerSetChannel(self, tuner_channel): for pos in range(len(config.TV_CHANNELS)): channel = config.TV_CHANNELS[pos] if channel[2] == tuner_channel: self.tuner_chidx = pos return print 'ERROR: Cannot find tuner channel "%s" in the TV channel listing' % tuner_channel self.tuner_chidx = 0 def TunerGetChannelInfo(self): '''Get program info for the current channel''' tuner_id = config.TV_CHANNELS[self.tuner_chidx][2] chan_name = config.TV_CHANNELS[self.tuner_chidx][1] chan_id = config.TV_CHANNELS[self.tuner_chidx][0] channels = epg.get_guide().get_programs(time.time(), time.time(), chan_id) if channels and channels[0] and channels[0].programs: start_s = time.strftime(config.TV_TIME_FORMAT, time.localtime(channels[0].programs[0].start)) stop_s = time.strftime(config.TV_TIME_FORMAT, time.localtime(channels[0].programs[0].stop)) ts = '(%s-%s)' % (start_s, stop_s) prog_info = '%s %s' % (ts, channels[0].programs[0].title) else: prog_info = 'No info' return tuner_id, chan_name, prog_info def TunerGetChannel(self): return config.TV_CHANNELS[self.tuner_chidx][2] def TunerNextChannel(self): self.tuner_chidx = (self.tuner_chidx+1) % len(config.TV_CHANNELS) def TunerPrevChannel(self): self.tuner_chidx = (self.tuner_chidx-1) % len(config.TV_CHANNELS) def Play(self, mode, tuner_channel=None, channel_change=0): if tuner_channel != None: try: self.TunerSetChannel(tuner_channel) except ValueError: pass if not tuner_channel: tuner_channel = self.fc.getChannel() vg = self.current_vg = self.fc.getVideoGroup(tuner_channel, True) if not vg.group_type == 'normal': print 'Xawtv only supports normal. "%s" is not implemented' % vg.group_type return if mode == 'tv' or mode == 'vcr': w, h = config.TV_VIEW_SIZE cf_norm = vg.tuner_norm cf_input = vg.input_num cf_device = vg.vdev s_norm = cf_norm.upper() if mode == 'vcr': cf_input = '1' if hasattr(config, "TV_VCR_INPUT_NUM") and config.TV_VCR_INPUT_NUM: cf_input = config.TV_VCR_INPUT_NUM if hasattr(config, "TV_XAWTV_OPTS") and config.TV_XAWTV_OPTS: daoptions = config.TV_XAWTV_OPTS else: daoptions = '-xv -f' command = '%s %s -device %s ' % (self.xawtv_prog, daoptions, cf_device) else: print 'Mode "%s" is not implemented' % mode # BUG ui.message() return self.mode = mode mixer = plugin.getbyname('MIXER') # BUG Mixer manipulation code. # TV is on line in # VCR is mic in # btaudio (different dsp device) will be added later if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer_vol = mixer.getMainVolume() mixer.setMainVolume(0) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer_vol = mixer.getPcmVolume() mixer.setPcmVolume(0) # Start up the TV task self.app=XawtvApp(command, self.remote_prog) if tuner_channel: time.sleep(0.5) self.app.sendcmd('setstation %s' % tuner_channel) #XXX use remote to change the input we want rc.add_app(self) # Suppress annoying audio clicks time.sleep(0.4) # BUG Hm.. This is hardcoded and very unflexible. if mixer and mode == 'vcr': mixer.setMicVolume(config.MIXER_VOLUME_VCR_IN) elif mixer: mixer.setLineinVolume(config.MIXER_VOLUME_TV_IN) mixer.setIgainVolume(config.MIXER_VOLUME_TV_IN) if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer.setMainVolume(mixer_vol) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer.setPcmVolume(mixer_vol) logger.debug('%s: started %s app', time.time(), self.mode) def Stop(self, channel_change=0): mixer = plugin.getbyname('MIXER') if mixer and not channel_change: mixer.setLineinVolume(0) mixer.setMicVolume(0) mixer.setIgainVolume(0) # Input on emu10k cards. self.app.stop() rc.remove_app(self) def eventhandler(self, event, menuw=None): logger.debug('%s: %s app got %s event', time.time(), self.mode, event) if event == em.STOP or event == em.PLAY_END: self.app.sendcmd('quit') time.sleep(1) self.Stop() rc.post_event(em.PLAY_END) return True elif event == em.TV_CHANNEL_UP or event == em.TV_CHANNEL_DOWN: if self.mode == 'vcr': return if event == em.TV_CHANNEL_UP: self.TunerPrevChannel() self.app.sendcmd('setstation next') else: self.TunerNextChannel() self.app.sendcmd('setstation prev') return True elif event == em.TOGGLE_OSD: #try to send channel name self.app.sendcmd('msg \'%s\'' % self.TunerGetChannel()) return True elif event == em.OSD_MESSAGE: self.app.sendcmd('msg \'%s\'' % event.arg) return True elif event in em.INPUT_ALL_NUMBERS: self.app.sendcmd('keypad %s' % event.arg) elif event == em.BUTTON: if event.arg == 'PREV_CH': self.app.sendcmd('setstation back') return True return False
class Vlc: """ the main class to control vlc """ def __init__(self, manager, path, size): self.name = 'vlc' self.app_mode = 'tv' self.app = None self.adapter_in_use = -1 self.fc = FreevoChannels() self.path = path self.size = size self.buffer = ring_buffer.RingBuffer(size, path) self.last_channel = None self.subtitles = False self.paused = False self.stop_time = 0 self.state = STATE_IDLE self.manager = manager self.udp_receiver = UDPReceiver(self.buffer, WAIT_FOR_DATA_TIMEOUT) self.slave_server = SlaveServer(self.buffer, self) self.fill = 0 self.cur_audio = None self.cur_sub = None self.cur_audiodev = None self.lastchan = None self.timer = kaa.Timer(self.__update_data_from_vlc) self.timer_start = kaa.OneShotTimer( self.__update_data_from_vlc) self.timer_disablebuffering = kaa.OneShotTimer( self.disable_buffering) if hasattr(config, 'LIVE_PAUSE_BUFFER_TIMEOUT'): self. buffering_timeout=config.LIVE_PAUSE_BUFFER_TIMEOUT else: # Disable after 5min if not LIVE_PAUSE_BUFFERING_TIMEOUT self.buffering_timeout= 5 * 60 # Create the command used to start vlc. # NOTE: We add the slave server MRL twice so that we can toggle between # them, this allows use to effectively reset vlc rendering pipeline and # make it possible to seek quickly. self.command = [ '--prio=%s' % config.MPLAYER_NICE ] + \ config.VLC_COMMAND.split(' ') + \ [ '--intf', 'rc' ] + \ ['--no-interact','--rc-fake-tty'] + \ [ '--sub-filter' ,'marq' ] + \ [ '--marq-marquee' ,'Playing' ] + \ [ '--marq-position','4'] + \ [ '--fullscreen', self.slave_server.get_vlc_mrl(), self.slave_server.get_vlc_mrl()] if not rc.PYLIRC and '--no-lirc' in self.command: self.command.remove('--no-lirc') self.udp_receiver.start() self.slave_server.start() def Play(self, mode, tuner_channel=None): """ play with vlc """ if not tuner_channel: tuner_channel = self.fc.getChannel() rc.app(self) same_channel = self.last_channel == tuner_channel # If it's the same channel as last time and we have come back to it after # more than 2 minutes start at the end of the buffer, otherwise jump # straight back in where we left off. if same_channel: if (time.time() - self.stop_time) > 120.0: start_at_end = True else: start_at_end = False else: self.fc.chanSet(tuner_channel, True, app=self.app) self.change_channel(tuner_channel) start_at_end = True if start_at_end: self.start_slave_server_at_end = True if same_channel: self.__change_state(STATE_PLAYING) else: self.__change_state(STATE_PLAYING) return None def stop(self, channel_change=False): """ Stop vlc """ if self.app: self.app.stop('quit\n') self.app = None if not channel_change: self.stop_time = time.time() _debug_('Shutting Down Buffering in %d secs' % self.buffering_timeout ) self.timer_disablebuffering.start(self.buffering_timeout) self.__change_state(STATE_IDLE) def disable_buffering(self): """ Stop buffering the current channel. """ if self.adapter_in_use != -1: self.time = 0 self.last_channel = None try: self.manager.disable_output(self.adapter_in_use) except: _debug_('Failed to disable output! ' + traceback.format_exc()) self.adapter_in_use = -1 def pause(self): """ Pause Playback. """ if self.app and not self.paused: _debug_('Pausing') self.app.write('pause\n') self.__osd_write('PAUSED') self.paused = True def resume(self): """ Resume Playback. """ if self.app and self.paused: _debug_('Resuming') self.app.write('pause\n') self.paused = False def shutdown(self): """ Stop the UDP receiver and slave server. """ if self.udp_receiver: self.udp_receiver.stop() self.udp_receiver = None if self.slave_server: self.slave_server.stop() self.slave_server = None def change_channel(self, channel): """ Select the correct dvbstreamer instance, change the channel and set the primary mrl. """ if self.last_channel == channel: # Already tune to this channel so nothing to do! return adapter = eval(self.fc.getVideoGroup(channel, True).vdev) if (self.adapter_in_use != -1) and (self.adapter_in_use != adapter): try: self.manager.disable_output(self.adapter_in_use) except: _debug_('Failed to disable output! ' + traceback.format_exc()) # We are tuning to a different channel so stop receiving packets and # reset the ring buffer self.udp_receiver.pause = True self.buffer.rwlock.acquire_write() self.buffer.reset() self.manager.select(adapter, channel) if self.adapter_in_use != adapter: self.adapter_in_use = adapter try: self.manager.enable_udp_output(self.adapter_in_use) except: _debug_('Failed to enable output! ' + traceback.format_exc()) # Start receiving packets and remember the channel we've just tuned to. self.udp_receiver.reset() self.udp_receiver.send_events = True self.udp_receiver.pause = False self.buffer.rwlock.release() self.last_channel = channel self.__change_state(STATE_TUNING) def eventhandler(self, event, menuw=None): """ Eventhandler for vlc control. If an event is not bound in this function it will be passed over to the items eventhandler """ event_consumed = False if self.state == STATE_IDLE: event_consumed = self.__eventhandler_idle(event, menuw) elif self.state == STATE_TUNING: event_consumed = self.__eventhandler_tuning(event, menuw) elif self.state == STATE_BUFFERING: event_consumed = self.__eventhandler_buffering(event, menuw) elif self.state == STATE_PLAYING: event_consumed = self.__eventhandler_playing(event, menuw) if not event_consumed: _debug_('Unused event %s in state %s' % (event.name, self.state)) return event_consumed def __eventhandler_idle(self, event, menuw): """ Internal event handler when in Idle state. """ return False def __eventhandler_tuning(self, event, menuw): """ Internal event handler when in Tuning state. """ if event == EVENT_DATA_STARTED: self.__change_state(STATE_BUFFERING) return True if event == EVENT_DATA_TIMEDOUT: # Timeout while waiting for data! # We could display a searching for signal graphic or something here self.__change_state(STATE_IDLE) return True return False def __eventhandler_buffering(self, event, menuw): """ Internal event handler when in Buffering state. """ if event == EVENT_DATA_ACQUIRED: self.wait_for_data_count -= 1 self.__draw_state_screen() if self.wait_for_data_count <= 0: self.__change_state(STATE_PLAYING) return True if event == EVENT_DATA_TIMEDOUT: # Timeout while waiting for data! # We could display a searching for signal graphic or something here self.__change_state(STATE_IDLE) return True return False def __eventhandler_playing(self, event, menuw): """ Internal event handler when in Playing state. """ if event == PAUSE or event == PLAY: if self.paused: self.resume() else: self.pause() return True if event in ( PLAY_END, USER_END, STOP ): self.timer.stop() self.stop() return True if event in [ TV_CHANNEL_UP, TV_CHANNEL_DOWN, TV_CHANNEL_LAST ] or (str(event).startswith('INPUT_') and str(event)[6:].isdigit()): _debug_('Event: %s' % str(event)) if event == TV_CHANNEL_UP: self.lastchan = self.fc.getChannel() nextchan = self.fc.getNextChannel() elif event == TV_CHANNEL_DOWN: self.lastchan = self.fc.getChannel() nextchan = self.fc.getPrevChannel() elif event == TV_CHANNEL_LAST: if not self.lastchan: return True nextchan = self.lastchan self.lastchan = self.fc.getChannel() else: self.lastchan = self.fc.getChannel() nextchan = self.fc.getManChannel(int(event.arg)) self.fc.chanSet(nextchan, True, app=self.app) self.stop(True) self.change_channel(nextchan) # Moves to the Tuning state return True if event == OSD_MESSAGE: self.__osd_write( event.arg) return True if event == TOGGLE_OSD: # Write percent through ringbuffer to OSD channel = self.__get_display_channel() percent = self.slave_server.get_percent() now = time.strftime(config.TV_TIME_FORMAT) self.__osd_write('%s - %d%% - %s' % (channel, percent, now) ) return True if event == SEEK: steps = int(event.arg) self.buffer.rwlock.acquire_write() bytes_per_sec = self.udp_receiver.average_pps * 188 if steps > 0: can_seek = self.slave_server.reader.available_forward() >= ((steps + 1) * bytes_per_sec) else: can_seek = self.slave_server.reader.available_backward() > 0 if can_seek: self.slave_server.reader.seek(steps * bytes_per_sec) if self.mrl_index == 0: self.mrl_index = 1 self.app.write('next\n') else: self.mrl_index = 0 self.app.write('prev\n') self.buffer.rwlock.release() return True if event == TV_GOTO_LIVE_PLAY: self.buffer.rwlock.acquire_write() self.slave_server.reader.seek(self.slave_server.reader.available_forward()) if self.mrl_index == 0: self.mrl_index = 1 self.app.write('next\n') else: self.mrl_index = 0 self.app.write('prev\n') self.buffer.rwlock.release() return True if event == EVENT_READER_OVERTAKEN: if self.paused: self.resume() return True if event == VIDEO_NEXT_FILLMODE: self.__set_next_fill() return True if event == VIDEO_NEXT_AUDIOLANG: self.__set_next_audio_track() return True if event == VIDEO_NEXT_SUBTITLE: self.__set_next_sub_track() return True if event == VIDEO_NEXT_AUDIOMODE: self.__set_next_audio_mode() return True # Consume UDP Receiver events. if event in (EVENT_DATA_ACQUIRED, EVENT_DATA_TIMEDOUT): return True return False def __change_state(self, new_state): """ Internal function to move to a new state. If new_state is different to the current state, set self.state to new_state and perform any state initialisation for the new state. """ if self.state == new_state: # No change in state nothing todo! return _debug_('Changing state from %s to %s' % (self.state, new_state)) self.state = new_state # State Initialisation code if self.state == STATE_IDLE: rc.app(None) rc.post_event(PLAY_END) self.udp_receiver.send_events = False elif self.state == STATE_TUNING: self.start_slave_server_at_end = True elif self.state == STATE_BUFFERING: self.wait_for_data_count = WAIT_FOR_DATA_COUNT elif self.state == STATE_PLAYING: self.slave_server.reader_at_end = self.start_slave_server_at_end self.slave_server.end_offset = self.udp_receiver.average_pps * WAIT_FOR_DATA_COUNT * 188 self.mrl_index = 0 self.app = VlcApp(self.command, self) self.timer.start(60) # Call every 60 seconds and self.timer_start.start(3) # at startup _debug_('self.timer_disablebuffering.get_interval %s' % self.timer_disablebuffering.get_interval()) if self.timer_disablebuffering.get_interval() != None: self.timer_disablebuffering.stop() self.start_slave_server_at_end = False self.__draw_state_screen() def __osd_write(self, text): if self.app: self.app.write('marq-marquee %s\n' % text) def __draw_state_screen(self): osd_obj = osd.get_singleton() percent = 0.0 channel = self.__get_display_channel() if self.state == STATE_IDLE: state_string = None elif self.state == STATE_TUNING: state_string = _('Tuning to %s' % self.__get_display_channel()) elif self.state == STATE_BUFFERING: state_string = _('Buffering %s' % self.__get_display_channel()) percent = float(WAIT_FOR_DATA_COUNT - self.wait_for_data_count) / float(WAIT_FOR_DATA_COUNT) elif self.state == STATE_PLAYING: state_string = _('Playing %s' % self.__get_display_channel()) percent = 1.0 osd_obj.clearscreen(color=osd_obj.COL_BLACK) if state_string: y = (config.CONF.height / 2) - config.OSD_DEFAULT_FONTSIZE h = -1 font = osd_obj.getfont(config.OSD_DEFAULT_FONTNAME, config.OSD_DEFAULT_FONTSIZE) osd_obj.drawstringframed(state_string, 0, y, config.CONF.width, h, font, fgcolor=osd_obj.COL_ORANGE, align_h='center') x = config.CONF.width / 4 w = x * 2 y = (config.CONF.height / 2) + config.OSD_DEFAULT_FONTSIZE h = int(20.0 * ( float(config.CONF.height) / 600.0 )) osd_obj.drawbox(x - 2,y - 2,x + w + 2, y + h + 2 , color=osd_obj.COL_ORANGE) w = int(float(w) * percent) osd_obj.drawbox(x,y,x + w, y +h , color=osd_obj.COL_ORANGE, fill=1) osd_obj.update() def __update_data_from_vlc(self): if self.app != None: self.app.write('atrack\n') self.app.write('adev\n') self.app.write('strack\n') def __set_next_audio_track(self): list = self.app.audio_tracks if len(list) > 0: if self.cur_audio == None: if len(list) > 1: self.cur_audio = 1 else: self.cur_audio = 0 self.cur_audio = self.__get_next_list(self.cur_audio,list) self.app.write('atrack %s\n' % list[self.cur_audio]) if list[self.cur_audio] == '-1': self.__osd_write('Audio Track Disabled') else: self.__osd_write('Audio Track %d' % (self.cur_audio ) ) else: self.__osd_write('audio track none') return True def __set_next_audio_mode(self): list = self.app.audio_chans if len(list) > 0: if self.cur_audiodev == None: if len(list) > 1: # STEREO self.cur_audiodev = 2 else: # MONO self.cur_audiodev = 1 self.cur_audiodev = self.__get_next_list(self.cur_audiodev,list) self.app.write('adev %s\n' % list[self.cur_audiodev ]) if list[self.cur_audiodev ] == '1': self.__osd_write('Audio Out: Mono' ) if list[self.cur_audiodev ] == '2': self.__osd_write('Audio Out: Stereo' ) if list[self.cur_audiodev ] == '4': self.__osd_write('Audio Out: 2 Front 2 Rear' ) if list[self.cur_audiodev ] == '6': self.__osd_write('Audio Out: 5.1 channel' ) if list[self.cur_audiodev ] == '10': self.__osd_write('Audio Out: A/52 over S/PDIF' ) else: self.__osd_write('Audio Out: unknown') return True def __get_next_list(self,current,list): for i in range (len(list)): if i == current: if i+1 < len(list): return i+1 else: return 0 break def __set_next_sub_track(self): # should work but untested , Maybe vlc doesn't understand mpeg-ts subs list = self.app.sub_tracks if len(list) > 0 : if self.cur_sub == None: if len(list) >1: self.cur_sub = 1 elif self.cur_sub == None: self.cur_sub = 0 self.cur_sub = self.__get_next_list(self.cur_sub,list) self.app.write('strack %s\n' % list[self.cur_sub]) if list[self.cur_audio] == '-1': self.__osd_write('Subtitle Track Off' ) else: self.__osd_write('Subtitle Track %d' % self.cur_sub ) else: self.__osd_write('Subtitle Track none') return True def __set_next_fill(self): cmdlist = ['Default','vcrop 16:10','vcrop 16:9','vcrop 4:3','vratio 16:10','vratio 16:9','vratio 4:3'] textlist = ['Default','Crop 16:10','Crop 16:9','Crop 4:3','Ratio 16:10','Ratio 16:9','Ratio 4:3'] self.fill=self.__get_next_list(self.fill,cmdlist) self.app.write('vcrop Default\n') self.app.write('vratio Default\n') self.app.write('%s\n' % cmdlist[self.fill]) self.__osd_write('Video Fill %s' % textlist[self.fill] ) def __get_display_channel(self): channel = self.last_channel for tv_channel_id, tv_display_name, tv_tuner_id in config.TV_CHANNELS: if self.last_channel == tv_tuner_id: channel = tv_display_name return channel
class Recorder: """ """ def __init__(self): logger.log(9, 'Recorder.__init__()') # Disable this plugin if not loaded by record_server. if string.find(sys.argv[0], 'recordserver') == -1: return logger.info('ACTIVATING VBI2SRT RECORD PLUGIN') self.vg = None self.fc = FreevoChannels() self.tuner_chidx = 0 # Current channel, index into config.TV_CHANNELS self.thread = Record_Thread() self.thread.setDaemon(1) self.thread.mode = 'idle' self.thread.start() def Record(self, rec_prog): logger.log(9, 'Record(rec_prog=%r)', rec_prog) frequency = self.fc.chanSet(str(rec_prog.tunerid), False, 'record plugin') rec_prog.filename = tv_util.getProgFilename(rec_prog) rec_prog.filename = os.path.splitext( tv_util.getProgFilename(rec_prog))[0] + '.mpeg' logger.debug('filename=%r', rec_prog.filename) self.vg = self.fc.getVideoGroup(rec_prog.tunerid, False) cl_options = { 'channel': rec_prog.tunerid, 'frequency': frequency, 'frequencyMHz': float(frequency) / 1000, 'filename': rec_prog.filename, 'base_filename': os.path.basename(rec_prog.filename), 'title': rec_prog.title, 'sub-title': rec_prog.sub_title, 'seconds': rec_prog.rec_duration, 'start': rec_prog.start, 'pdc-start': rec_prog.pdc_start, } logger.debug('cl_options=%r', cl_options) logger.debug('chan_index=%r', self.fc.chan_index) logger.debug('vg.vdev=%r', self.vg.vdev) logger.debug('vg.vvbi=%r', self.vg.vvbi) pagenum = None try: pagenum = int(config.TV_CHANNELS[self.fc.chan_index][5]) except: pagenum = None logger.debug('pagenum=%r', pagenum) if pagenum: # there is a bug in vbi2srt that causes out of sync subtitles when VPS is used self.rec_command = \ 'vbi2srt --verbose --video-in=%s --video-out=%s --vbi-device=%s --seconds=%s --vps=%s --page=%s' % \ (self.vg.vdev, rec_prog.filename, self.vg.vvbi, rec_prog.rec_duration, rec_prog.pdc_start, pagenum) else: self.rec_command = \ 'vbi2srt --verbose --video-in=%s --video-out=%s --vbi-device=%s --seconds=%s --vps=%s' % \ (self.vg.vdev, rec_prog.filename, self.vg.vvbi, rec_prog.rec_duration, rec_prog.pdc_start) logger.debug('rec_command=%r', self.rec_command) self.thread.mode = 'record' self.thread.prog = rec_prog self.thread.command = self.rec_command self.thread.autokill = float(rec_prog.rec_duration + 10) self.thread.mode_flag.set() def Stop(self): logger.log(9, 'Stop()') self.thread.mode = 'stop' self.thread.mode_flag.set()
# test code, run with freevo execute /path/to/upsoon.py config.DEBUG = 2 function = None if len(sys.argv) > 1: function = sys.argv[1].lower() server = RecordClient() if function == 'run': #import rc as rctrl #rc = rctrl.get_singleton(False) pi = PluginInterface() kaa.main.run() elif function == 'findnextprogram': def handler(result): print 'findnextprogram=%r' % (result,) print result.__dict__ raise SystemExit server.findNextProgram(handler) kaa.main.run() else: fc = FreevoChannels() vg=fc.getVideoGroup('K10', False) print 'vg=%s' % vg print 'dir(%s)' % dir(vg) for it in dir(vg): print ' %s:%s' % (it, eval('vg.'+it)) vdev=vg.vdev print 'vdev=%s' % vdev
class TVGuide(Item): def __init__(self, start_time, player, menuw): Item.__init__(self) self.n_items, hours_per_page = skin.items_per_page(('tv', self)) stop_time = start_time + hours_per_page * 60 * 60 msgtext = _('Preparing the program guide') guide = epg_xmltv.get_guide(PopupBox(text=msgtext)) channels = guide.GetPrograms(start=start_time + 1, stop=stop_time - 1) if not channels: AlertBox(text=_('TV Guide is corrupt!')).show() return selected = None for chan in channels: if chan.programs: selected = chan.programs[0] break self.col_time = 30 # each col represents 30 minutes self.n_cols = (stop_time - start_time) / 60 / self.col_time self.player = player self.type = 'tv' self.menuw = menuw self.visible = True self.select_time = start_time self.last_update = 0 self.lastinput_value = None self.lastinput_time = None self.update_schedules(force=True) self.fc = FreevoChannels() self.rebuild(start_time, stop_time, guide.chan_list[0].id, selected) self.event_context = 'tvmenu' menuw.pushmenu(self) def update_schedules(self, force=False): if not force and self.last_update + 60 > time.time(): return # less than one second? Do not belive the force update if self.last_update + 1 > time.time(): return upsoon = '%s/upsoon' % (config.FREEVO_CACHEDIR) if os.path.isfile(upsoon): os.unlink(upsoon) _debug_('update schedule', 2) self.last_update = time.time() self.scheduled_programs = [] self.overlap_programs = [] (got_schedule, schedule) = ri.getScheduledRecordings() util.misc.comingup(None, (got_schedule, schedule)) if got_schedule: progs = schedule.getProgramList() for k in progs: prog = progs[k] self.scheduled_programs.append(prog.str2utf()) if prog.overlap: self.overlap_programs.append(prog.str2utf()) ### event handler def eventhandler(self, event, menuw=None): """ Handles events in the tv guide Events handled by this are: MENU_CHANGE_STYLE: ? MENU_UP: Move one channel up in the guide MENU_DOWN: Move one channel down in the guide MENU_LEFT: Move to the next program on this channel MENU_RIGHT: Move to previous programm on this channel MENU_PAGEUP: Moves to the first of the currently displayed channels MENU_PAGEDOWN: Move to the last of the currently displayed channels MENU_SUBMENU: Open a submenu for the selected program MENU_SELECT: Open a submenu for the selected program TV_START_RECORDING: Start to record this or put it on schedule PLAY: Start to watch the selected channel (if it is possible) PLAY_END: Show the guide again numerical INPUTs: Jump to a specific channel number """ _debug_('TVGUIDE EVENT is %s' % event, 0) ## MENU_CHANGE_STYLE if event == MENU_CHANGE_STYLE: if skin.toggle_display_style('tv'): start_time = self.start_time stop_time = self.stop_time start_channel = self.start_channel selected = self.selected self.n_items, hours_per_page = skin.items_per_page( ('tv', self)) before = -1 after = -1 for c in self.guide.chan_list: if before >= 0 and after == -1: before += 1 if after >= 0: after += 1 if c.id == start_channel: before = 0 if c.id == selected.channel_id: after = 0 if self.n_items <= before: start_channel = selected.channel_id if after < self.n_items: up = min(self.n_items - after, len( self.guide.chan_list)) - 1 for i in range(len(self.guide.chan_list) - up): if self.guide.chan_list[i + up].id == start_channel: start_channel = self.guide.chan_list[i].id break stop_time = start_time + hours_per_page * 60 * 60 self.n_cols = (stop_time - start_time) / 60 / self.col_time self.rebuild(start_time, stop_time, start_channel, selected) ## MENU_UP: Move one channel up in the guide if event == MENU_UP: self.event_change_channel(-1) ## MENU_DOWN: Move one channel down in the guide elif event == MENU_DOWN: self.event_change_channel(1) ## MENU_LEFT: Move to the next program on this channel elif event == MENU_LEFT: self.event_change_program(-1) ## MENU_RIGHT: Move to previous programm on this channel elif event == MENU_RIGHT: self.event_change_program(1) ## MENU_PAGEUP: Moves to the first of the currently displayed channels elif event == MENU_PAGEUP: self.event_change_channel(-self.n_items) ## MENU_PAGEDOWN: Move to the last of the currently displayed channels elif event == MENU_PAGEDOWN: self.event_change_channel(self.n_items) ## MENU_SUBMENU: Open a submenu for the selected program elif event == MENU_SUBMENU: self.event_submenu() ## MENU_SELECT: Open a submenu for the selected program elif event == MENU_SELECT: self.event_submenu() ## TV_START_RECORDING: Start to record this or put it on schedule elif event == TV_START_RECORDING: self.event_record() ## PLAY: Start to watch the selected channel (if it is possible) elif event == PLAY: suffix = self.fc.getVideoGroup(self.selected.channel_id, True).vdev suffix = suffix.split('/')[-1] tvlockfile = config.FREEVO_CACHEDIR + '/record.' + suffix # Check if the selected program is >7 min in the future # if so, bring up the record dialog now = time.time() + (7 * 60) if self.selected.start > now: self.event_submenu() elif os.path.exists(tvlockfile): # XXX: In the future add the options to watch what we are # recording or cancel it and watch TV. AlertBox(text=_('Sorry, you cannot watch TV while recording. ')+ \ _('If this is not true then remove ') + \ tvlockfile + '.', height=200).show() return TRUE else: self.hide() self.player('tv', self.selected.channel_id) ## PLAY_END: Show the guide again elif event == PLAY_END: self.show() ## numerical INPUT: Jump to a specific channel number if str(event).startswith("INPUT_"): # tune explicit channel eventInput = str(event)[6] isNumeric = TRUE try: newinput_value = int(eventInput) except: #Protected against INPUT_UP, INPUT_DOWN, etc isNumeric = FALSE if isNumeric: newinput_time = int(time.time()) if (self.lastinput_value != None): # allow 1 seconds delay for multiple digit channels if (newinput_time - self.lastinput_time < 1): # this enables multiple (max 3) digit channel selection if (self.lastinput_value >= 100): self.lastinput_value = (self.lastinput_value % 100) newinput_value += self.lastinput_value * 10 self.lastinput_value = newinput_value self.lastinput_time = newinput_time channel_no = int(newinput_value) - 1 if channel_no < len(self.guide.chan_list): self.start_channel = self.guide.chan_list[channel_no].id else: self.lastinput_value = None self.rebuild(self.start_time, self.stop_time, self.start_channel, self.selected) ## unknown event else: return FALSE return TRUE ### actions def show(self): """ show the guide""" if not self.visible: self.visible = 1 self.refresh() def hide(self): """ hide the guide""" if self.visible: self.visible = 0 skin.clear() def refresh(self): """refresh the guide This function is called automatically by freevo whenever this menu is opened or reopened. """ _debug_('Refresh', 2) if not self.menuw.children: rc.set_context(self.event_context) self.menuw.refresh() self.update(force=True) def update(self, force=False): """ update the tv guide This function updates the scheduled and overlap flags for all currently displayed programs. """ _debug_('Update', 2) self.update_schedules(force) if self.table: for t in self.table: try: for p in t.programs: if p in self.scheduled_programs: p.scheduled = TRUE # DO NOT change this to 'True' Twisted # does not support boolean objects and # it will break under Python 2.3 else: p.scheduled = FALSE # Same as above; leave as 'FALSE' until # Twisted includes Boolean if p in self.overlap_programs: p.overlap = TRUE else: p.overlap = FALSE except: pass self.menuw.refresh() def rebuild(self, start_time, stop_time, start_channel, selected): """ rebuild the guide This is neccessary we change the set of programs that have to be displayed, this is the case when the user moves around in the menu. """ _debug_('Reload', 2) self.guide = epg_xmltv.get_guide() channels = self.guide.GetPrograms(start=start_time + 1, stop=stop_time - 1) table = [] self.start_time = start_time self.stop_time = stop_time self.start_channel = start_channel self.selected = selected self.display_up_arrow = FALSE self.display_down_arrow = FALSE # table header table += [['Chan']] for i in range(int(self.n_cols)): table[0] += [start_time + self.col_time * i * 60] table += [self.selected] # the selected program found_1stchannel = 0 if stop_time == None: found_1stchannel = 1 flag_selected = 0 n = 0 for chan in channels: if n >= self.n_items: self.display_down_arrow = TRUE break if start_channel != None and chan.id == start_channel: found_1stchannel = 1 if not found_1stchannel: self.display_up_arrow = TRUE if found_1stchannel: if not chan.programs: prg = epg_types.TvProgram() prg.channel_id = chan.id prg.start = 0 prg.stop = 2147483647 # Year 2038 prg.title = CHAN_NO_DATA prg.desc = '' chan.programs = [prg] for i in range(len(chan.programs)): if selected: if chan.programs[i] == selected: flag_selected = 1 table += [chan] n += 1 if flag_selected == 0: for i in range(2, len(table)): if flag_selected == 1: break else: if table[i].programs: for j in range(len(table[i].programs)): if table[i].programs[j].stop > start_time: self.selected = table[i].programs[j] table[1] = table[i].programs[j] flag_selected = 1 break self.table = table # then we can refresh the display with this programs self.update() def event_record(self): """ Add to schedule or remove from schedule This function adds or removes the selected program to schedule, if the user presses REC inside of TVGuide. This is a kind of a short cut, which directly manipulates the schedule without opening the submenu for ProgramItems. """ if self.selected.scheduled: # remove this program from schedule it it is already scheduled pi = ProgramItem(self, prog=self.selected).remove_program() else: # otherwise add it to schedule without more questions pi = ProgramItem(self, prog=self.selected).schedule_program() self.refresh() def event_submenu(self): """ Opens the submenu for ProgramItems The user can choose from this submenu what to do with the selected program, e.g. schedule a program for recording or search for more of that program. """ # create a ProgramItem for the selected program pi = ProgramItem(self, prog=self.selected) # and show its submenu pi.display_program(menuw=self.menuw) def event_change_program(self, value, full_scan=False): start_time = self.start_time stop_time = self.stop_time start_channel = self.start_channel last_prg = self.selected channel = self.guide.chan_dict[last_prg.channel_id] if full_scan: all_programs = self.guide.GetPrograms(start_time - 24 * 60 * 60, stop_time + 24 * 60 * 60, [channel.id]) else: all_programs = self.guide.GetPrograms(start_time + 1, stop_time - 1, [channel.id]) # Current channel programs programs = all_programs[0].programs if programs: for i in range(len(programs)): if programs[i].title == last_prg.title and \ programs[i].start == last_prg.start and \ programs[i].stop == last_prg.stop and \ programs[i].channel_id == last_prg.channel_id: break prg = None if value > 0: if i + value < len(programs): prg = programs[i + value] elif full_scan: prg = programs[-1] else: return self.event_change_program(value, True) else: if i + value >= 0: prg = programs[i + value] elif full_scan: prg = programs[0] else: return self.event_change_program(value, True) if prg.sub_title: procdesc = '"' + prg.sub_title + '"\n' + prg.desc else: procdesc = prg.desc to_info = (prg.title, procdesc) self.select_time = prg.start # set new (better) start / stop times extra_space = 0 if prg.stop - prg.start > self.col_time * 60: extra_space = self.col_time * 60 while prg.start + extra_space >= stop_time: start_time += (self.col_time * 60) stop_time += (self.col_time * 60) while prg.start + extra_space <= start_time: start_time -= (self.col_time * 60) stop_time -= (self.col_time * 60) else: prg = epg_types.TvProgram() prg.channel_id = channel.id prg.start = 0 prg.stop = 2147483647 # Year 2038 prg.title = CHAN_NO_DATA prg.desc = '' to_info = CHAN_NO_DATA self.rebuild(start_time, stop_time, start_channel, prg) def event_change_channel(self, value): start_time = self.start_time stop_time = self.stop_time start_channel = self.start_channel last_prg = self.selected for i in range(len(self.guide.chan_list)): if self.guide.chan_list[i].id == start_channel: start_pos = i end_pos = i + self.n_items if self.guide.chan_list[i].id == last_prg.channel_id: break channel_pos = min(len(self.guide.chan_list) - 1, max(0, i + value)) # calc real changed value value = channel_pos - i if value < 0 and channel_pos and channel_pos <= start_pos: # move start channel up start_channel = self.guide.chan_list[start_pos + value].id if value > 0 and channel_pos < len(self.guide.chan_list)-1 and \ channel_pos + 1 >= end_pos: # move start channel down start_channel = self.guide.chan_list[start_pos + value].id channel = self.guide.chan_list[channel_pos] programs = self.guide.GetPrograms(start_time + 1, stop_time - 1, [channel.id]) programs = programs[0].programs prg = None if programs and len(programs) > 0: for i in range(len(programs)): if programs[i].stop > self.select_time and programs[ i].stop > start_time: break prg = programs[i] if prg.sub_title: procdesc = '"' + prg.sub_title + '"\n' + prg.desc else: procdesc = prg.desc to_info = (prg.title, procdesc) else: prg = epg_types.TvProgram() prg.channel_id = channel.id prg.start = 0 prg.stop = 2147483647 # Year 2038 prg.title = CHAN_NO_DATA prg.desc = '' to_info = CHAN_NO_DATA self.rebuild(start_time, stop_time, start_channel, prg)
class LocalBackend(Backend): """ Class used to control tuning and the ringbuffer locally. """ def __init__(self): super(LocalBackend, self).__init__() self.fc = FreevoChannels() self.livepause_app = None self.controller = None self.device_in_use = None self.last_channel = None self.mode = 'raw' self.events_enabled = False def get_server_address(self): return 'localhost' def set_mode(self, mode): if self.livepause_app: self.livepause_app.set_mode(mode) self.mode = mode def get_buffer_info(self): if self.livepause_app: return self.livepause_app.info() return (0.0,0,0,0) def set_events_enabled(self, enable): if self.livepause_app: self.livepause_app.events_enabled = enable self.events_enabled = enable def seekto(self, to_time): if self.livepause_app: self.livepause_app.seekto(to_time) def seek(self, time_delta, now=False): if self.livepause_app: self.livepause_app.seek(time_delta, now) def disable_buffering(self): if self.device_in_use: self.last_channel = None self.controller.stop_filling() self.controller = None self.device_in_use = None self.livepause_app.quit() self.livepause_app = None logger.debug('Buffering disabled.') def change_channel(self, channel): if self.last_channel == channel: # Already tune to this channel so nothing to do! return if self.device_in_use: self.controller.stop_filling() self.controller = None self.device_in_use = None if self.livepause_app is None: self.livepause_app = LivePauseApp(config.CONF.livepause, config.LIVE_PAUSE2_BUFFER_PATH, config.LIVE_PAUSE2_BUFFER_SIZE, config.LIVE_PAUSE2_PORT, self.send_event) self.livepause_app.set_mode(self.mode) self.livepause_app.events_enabled = self.events_enabled # Find the controller for this VideoGroup type vg = self.fc.getVideoGroup(channel, True) self.controller = controllers.get_controller(vg.group_type) if not self.controller: logger.debug('Failed to find controller for device') return self.fc.chanSet(channel, True) self.livepause_app.reset() self.livepause_app.events_enabled = True self.controller.start_filling(self.livepause_app, vg, channel, WAIT_FOR_DATA_TIMEOUT) self.device_in_use = vg self.last_channel = channel def save(self, filename, time_start, time_end): if self.livepause_app: self.livepause_app.save(filename, time_start, time_end) def cancelsave(self): if self.livepause_app: self.livepause_app.cancel_save() def send_event(self, to_send): to_send.post()
class TunerControl: """ Class that controls the tuner device """ def __init__(self, xine): """ TunerControl constructor """ self.xine = xine self.ivtv_init = False self.fc = FreevoChannels() self.curr_channel = None self.embed = None self.stack = [] self.timer = OneShotTimer(self.timer_handler) def _kill_(self): """ TunerControl destructor """ if self.embed: ivtv_dev.setvbiembed(self.embed) def program_timer(self): """ program the timer to start of next show on the current channel """ _debug_('TunerControl: Program timer') # set timer to mark the next program start_t, stop_t, prog_s = self.GetInfo() if stop_t > 0: stop_t = stop_t - time.time() self.timer.start(stop_t) _debug_( 'TunerControl: Timer set to mark next program in: %s seconds' % stop_t) else: _debug_('TunerControl: Timer not set, stop_t not available') self.ShowInfo() def timer_handler(self): """ handle timer event """ _debug_('TunerControl: Timer event, mark new show') self.xine.SetMark() self.program_timer() def Stop(self): """ stop """ self.ivtv_init = False def GetName(self): """ get channel name """ tuner_id, chan_name, prog_info = self.fc.getChannelInfo(showtime=True) return prog_info def GetInfo(self): """ get channel info """ tuner_id, chan_id, chan_name, start_t, stop_t, prog_s = self.fc.getChannelInfoRaw( ) return (start_t, stop_t, prog_s) def ShowInfo(self): """ show channel info """ if self.curr_channel != None: # show channel info #vg = self.fc.getVideoGroup(self.curr_channel, True) tuner_id, chan_name, prog_info = self.fc.getChannelInfo( showtime=False) self.xine.ShowMessage(msg='%s: %s' % (chan_name, prog_info)) def PushChannel(self): """ push the current channel on the channel stack """ if self.curr_channel != None: self.stack.append(self.curr_channel) _debug_('TunerControl: Pushed channel %s' % self.curr_channel) _debug_('TunerControl: Channel stack = %s' % self.stack) def UnpushChannel(self): """ remove the top channel fromthe channel stack """ if len(self.stack) == 0: _debug_('TunerControl: Channel stack is empty') else: channel = self.stack.pop() _debug_('TunerControl: Unpushed channel %s' % channel) _debug_('TunerControl: Channel stack = %s' % self.stack) def PopChannel(self): """ pop the top channel from the channel stack and switch channel """ if len(self.stack) == 0: _debug_('TunerControl: Channel stack is empty') else: channel = self.stack.pop() _debug_('TunerControl: Popped channel %s' % channel) self.SetVideoGroup(channel) _debug_('TunerControl: Channel stack = %s' % self.stack) def SetChannelByName(self, channel=None, clearstack=False): """ tune to a new channel by name """ last_channel = self.curr_channel next_channel = None channel_index = -1 if clearstack == True: self.stack = [] self.curr_channel = None if channel == None: # get a channel next_channel = self.fc.getChannel() try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS)): entry = config.TV_CHANNELS[pos] if str(channel) == str(entry[2]): channel_index = pos next_channel = channel break except ValueError: pass if (next_channel == None): _debug_( 'TunerControl: Cannot find tuner channel %r in the TV channel listing' % channel, DWARNING) else: self.SetChannelByIndex(channel_index + 1) def SetChannelByIndex(self, channel): """ tune to a channel by index from the TV_CHANNELS list """ # tune channel by index next_channel = self.fc.getManChannel(channel) _debug_('TunerControl: Explicit channel selection by index = %r' % next_channel) self.PushChannel() self.SetVideoGroup(next_channel) def SetChannelByNumber(self, channel): """ tune to a channel by actual channel number """ # tune channel by number _debug_('TunerControl: Explicit channel selection by number = %r' % channel) self.PushChannel() self.SetVideoGroup(channel) def NextChannel(self): """ jump to the next channel in the TV_CHANNELS list """ next_channel = self.fc.getNextChannel() _debug_('TunerControl: Next channel selection = %r' % next_channel) self.PushChannel() self.SetVideoGroup(next_channel) def PrevChannel(self): """ jump to the previous channel in the TV_CHANNELS list """ prev_channel = self.fc.getPrevChannel() _debug_('TunerControl: Previous channel selection = %r' % prev_channel) self.PushChannel() self.SetVideoGroup(prev_channel) def SetVideoGroup(self, channel): """ select a channel's video group and tune to that channel """ _debug_('TunerControl: Channel: %r' % channel) new_vg = self.fc.getVideoGroup(channel, True) _debug_('TunerControl: Group: type=%r, desc=%r' % (new_vg.group_type, new_vg.desc)) _debug_('TunerControl: Input: type=%r, num=%r' % (new_vg.input_type, new_vg.input_num)) if (new_vg.group_type != 'ivtv'): _debug_( 'TunerControl: Video group %r is not supported' % new_vg.group_type, DERROR) pop = AlertBox( text=_('This plugin only supports the ivtv video group!')) pop.show() return # check if videogroup switch is needed switch_vg = (self.ivtv_init == False) or \ (self.curr_channel == None) or \ (new_vg != self.fc.getVideoGroup(self.curr_channel, True)) if switch_vg == True: # switch to a different video group _debug_('TunerControl: Set video group: %s' % new_vg.vdev, DINFO) ivtv_dev = ivtv.IVTV(new_vg.vdev) ivtv_dev.init_settings() self.xine.SetInput(new_vg.input_num) # disable embedded vbi data self.embed = ivtv_dev.getvbiembed() ivtv_dev.setvbiembed(0) if self.ivtv_init == False: # set channel directly on v4l device self.ivtv_init = True self.fc.chanSet(channel, True) self.curr_channel = channel if config.XINE_TV_LIVE_RECORD: # video stream is marked implicitly # start timer to mark next show self.program_timer() else: self.ShowInfo() else: # set channel through xine process # get channel frequency freq = self.fc.chanSet(channel, True, 'ivtv_xine_tv', None) if freq != 0: # channel has frequency _debug_('TunerControl: Set frequency: %s' % freq) self.xine.SetFrequency(freq) else: # channel has no frequency _debug_('TunerControl: Channel has no frequency') self.curr_channel = channel if config.XINE_TV_LIVE_RECORD: # explicitly mark video stream self.xine.SetMark() # start timer to mark next show self.program_timer() else: self.xine.SeekEnd() self.ShowInfo()
class Recorder: def __init__(self): # Disable this plugin if not loaded by record_server. if string.find(sys.argv[0], 'recordserver') == -1: return if DEBUG: print 'ACTIVATING VBI2SRT RECORD PLUGIN' self.vg = None self.fc = FreevoChannels() self.tuner_chidx = 0 # Current channel, index into config.TV_CHANNELS self.thread = Record_Thread() self.thread.setDaemon(1) self.thread.mode = 'idle' self.thread.start() def Record(self, rec_prog): frequency = self.fc.chanSet(str(rec_prog.tunerid), False, 'record plugin') rec_prog.filename = tv_util.getProgFilename(rec_prog) rec_prog.filename = os.path.splitext( tv_util.getProgFilename(rec_prog))[0] + '.mpeg' if DEBUG: print('Recorder::Record:filename %s' % rec_prog.filename) cl_options = { 'channel': rec_prog.tunerid, 'frequency': frequency, 'filename': rec_prog.filename, 'base_filename': os.path.basename(rec_prog.filename), 'title': rec_prog.title, 'sub-title': rec_prog.sub_title, 'seconds': rec_prog.rec_duration, 'start': rec_prog.start, 'pdc-start': rec_prog.pdc_start, } self.vg = self.fc.getVideoGroup(rec_prog.tunerid, False) if DEBUG: print('Recorder::Record:cl_options %s' % cl_options) if DEBUG: print('Recorder::Record:chan_index %s' % self.fc.chan_index) if DEBUG: print('Recorder::Record:vg.vdev %s' % self.vg.vdev) if DEBUG: print('Recorder::Record:vg.vvbi %s' % self.vg.vvbi) pagenum = None try: pagenum = int(config.TV_CHANNELS[self.fc.chan_index][5]) except: pagenum = None if DEBUG: print('Recorder::Record:pagenum "%s"' % pagenum) self.rec_command = config.VCR_CMD % cl_options if pagenum == None: self.rec_command = 'vbi2srt --verbose --video-in=%s --video-out=%s --vbi-device=%s --seconds=%s --vps=%s' % \ (self.vg.vdev, rec_prog.filename, self.vg.vvbi, rec_prog.rec_duration, rec_prog.pdc_start) else: # there is a bug in vbi2srt that causes out of sync subtitles when VPS is used self.rec_command = 'vbi2srt --verbose --video-in=%s --video-out=%s --vbi-device=%s --seconds=%s --vps=%s --page=%s' % \ (self.vg.vdev, rec_prog.filename, self.vg.vvbi, rec_prog.rec_duration, rec_prog.pdc_start, pagenum) self.thread.mode = 'record' self.thread.prog = rec_prog self.thread.command = self.rec_command self.thread.autokill = float(rec_prog.rec_duration + 10) self.thread.mode_flag.set() if DEBUG: print('Recorder::Record: %s' % self.rec_command) def Stop(self): self.thread.mode = 'stop' self.thread.mode_flag.set()
class MPlayer: __muted = 0 __igainvol = 0 def __init__(self): self.tuner_chidx = 0 # Current channel, index into config.TV_CHANNELS self.app_mode = 'tv' self.fc = FreevoChannels() self.current_vg = None def Play(self, mode, tuner_channel=None): if not tuner_channel: tuner_channel = self.fc.getChannel() vg = self.current_vg = self.fc.getVideoGroup(tuner_channel, True) # Convert to MPlayer TV setting strings norm = 'norm=%s' % vg.tuner_norm input = 'input=%s' % vg.input_num device = 'device=%s' % vg.vdev w, h = config.TV_VIEW_SIZE outfmt = 'outfmt=%s' % config.TV_VIEW_OUTFMT # Build the MPlayer command args = (config.MPLAYER_NICE, config.MPLAYER_CMD, config.MPLAYER_VO_DEV, config.MPLAYER_VO_DEV_OPTS, config.MPLAYER_AO_DEV, config.MPLAYER_ARGS_DEF) if mode == 'tv': if vg.group_type == 'ivtv': ivtv_dev = ivtv.IVTV(vg.vdev) ivtv_dev.init_settings() ivtv_dev.setinput(vg.input_num) #ivtv_dev.print_settings() ivtv_dev.close() self.fc.chanSet(tuner_channel, True) tvcmd = vg.vdev if config.MPLAYER_ARGS.has_key('ivtv'): args += (config.MPLAYER_ARGS['ivtv'], ) elif vg.group_type == 'webcam': self.fc.chanSet(tuner_channel, True, app='mplayer') tvcmd = '' if config.MPLAYER_ARGS.has_key('webcam'): args += (config.MPLAYER_ARGS['webcam'], ) elif vg.group_type == 'dvb': self.fc.chanSet(tuner_channel, True, app='mplayer') tvcmd = '' args += ('"dvb://%s" %s' % (tuner_channel, config.MPLAYER_ARGS['dvb']), ) elif vg.group_type == 'tvalsa': freq_khz = self.fc.chanSet(tuner_channel, True, app='mplayer') tuner_freq = '%1.3f' % (freq_khz / 1000.0) tvcmd = ('tv:// -tv driver=%s:%s:freq=%s:%s:%s:' '%s:width=%s:height=%s:%s %s' % (config.TV_DRIVER, vg.adev, tuner_freq, device, input, norm, w, h, outfmt, config.TV_OPTS)) if config.MPLAYER_ARGS.has_key('tv'): args += (config.MPLAYER_ARGS['tv'], ) else: # group_type == 'normal' freq_khz = self.fc.chanSet(tuner_channel, True, app='mplayer') tuner_freq = '%1.3f' % (freq_khz / 1000.0) tvcmd = ('tv:// -tv driver=%s:freq=%s:%s:%s:' '%s:width=%s:height=%s:%s %s' % (config.TV_DRIVER, tuner_freq, device, input, norm, w, h, outfmt, config.TV_OPTS)) if config.MPLAYER_ARGS.has_key('tv'): args += (config.MPLAYER_ARGS['tv'], ) elif mode == 'vcr': tvcmd = ('tv:// -tv driver=%s:%s:%s:' '%s:width=%s:height=%s:%s %s' % (config.TV_DRIVER, device, input, norm, w, h, outfmt, config.TV_OPTS)) if config.MPLAYER_ARGS.has_key('tv'): args += (config.MPLAYER_ARGS['tv'], ) else: print 'Mode "%s" is not implemented' % mode # XXX ui.message() return args += (tvcmd, ) mpl = '--prio=%s %s -vo %s %s -ao %s %s -slave %s %s' % args command = mpl _debug_('command=\"%s\"', (command)) self.mode = mode # XXX Mixer manipulation code. # TV is on line in # VCR is mic in # btaudio (different dsp device) will be added later mixer = plugin.getbyname('MIXER') if mixer and config.MAJOR_AUDIO_CTRL == 'VOL': mixer_vol = mixer.getMainVolume() mixer.setMainVolume(0) elif mixer and config.MAJOR_AUDIO_CTRL == 'PCM': mixer_vol = mixer.getPcmVolume() mixer.setPcmVolume(0) # Start up the TV task self.app = childapp.ChildApp2(command) self.prev_app = rc.app() rc.app(self) if osd.focused_app(): osd.focused_app().hide() # Suppress annoying audio clicks time.sleep(0.4) # XXX Hm.. This is hardcoded and very unflexible. if mixer and mode == 'vcr': mixer.setMicVolume(config.VCR_IN_VOLUME) elif mixer: mixer.setLineinVolume(config.TV_IN_VOLUME) mixer.setIgainVolume(config.TV_IN_VOLUME) if mixer and config.MAJOR_AUDIO_CTRL == 'VOL': mixer.setMainVolume(mixer_vol) elif mixer and config.MAJOR_AUDIO_CTRL == 'PCM': mixer.setPcmVolume(mixer_vol) if DEBUG: print '%s: started %s app' % (time.time(), self.mode) def Stop(self, channel_change=0): mixer = plugin.getbyname('MIXER') if mixer and not channel_change: mixer.setLineinVolume(0) mixer.setMicVolume(0) mixer.setIgainVolume(0) # Input on emu10k cards. self.app.stop('quit\n') rc.app(self.prev_app) if osd.focused_app() and not channel_change: osd.focused_app().show() if os.path.exists('/tmp/freevo.wid'): os.unlink('/tmp/freevo.wid') def eventhandler(self, event, menuw=None): s_event = '%s' % event if event == em.STOP or event == em.PLAY_END: self.Stop() rc.post_event(em.PLAY_END) return TRUE elif event == em.PAUSE or event == em.PLAY: self.app.write('pause\n') if DEBUG: print '%s: sending pause to mplayer' % (time.time()) return TRUE elif event in [em.TV_CHANNEL_UP, em.TV_CHANNEL_DOWN ] or s_event.startswith('INPUT_'): chan = None if event == em.TV_CHANNEL_UP: nextchan = self.fc.getNextChannel() nextchannum = self.fc.getNextChannelNum() elif event == em.TV_CHANNEL_DOWN: nextchan = self.fc.getPrevChannel() nextchannum = self.fc.getPrevChannelNum() else: chan = int(s_event[6]) nextchan = self.fc.getManChannel(chan) nextchannum = self.fc.getManChannelNum(chan) nextvg = self.fc.getVideoGroup(nextchan, True) _debug_('chan=%s, nextchannum=%s, nextchan=%s nextvg=%s' % (chan, nextchannum, nextchan, nextvg)) if self.current_vg != nextvg: self.Stop(channel_change=1) self.Play('tv', nextchan) return TRUE if self.mode == 'vcr': return elif self.current_vg.group_type == 'dvb': card = 0 # May be this should come from video groups or TV_CHANNELS if em.TV_CHANNEL_UP: self.app.write('dvb_set_channel %s %s\n' % (nextchannum, card)) elif em.TV_CHANNEL_DOWN: self.app.write('dvb_set_channel %s %s\n' % (nextchannum, card)) return TRUE elif self.current_vg.group_type == 'ivtv': self.fc.chanSet(nextchan, True) self.app.write('seek 999999 0\n') else: freq_khz = self.fc.chanSet(nextchan, True, app=self.app) new_freq = '%1.3f' % (freq_khz / 1000.0) self.app.write('tv_set_freq %s\n' % new_freq) self.current_vg = self.fc.getVideoGroup(self.fc.getChannel(), True) # Display a channel changed message tuner_id, chan_name, prog_info = self.fc.getChannelInfo() now = time.strftime('%H:%M') msg = '%s %s (%s): %s' % (now, chan_name, tuner_id, prog_info) cmd = 'osd_show_text "%s"\n' % msg self.app.write(cmd) return TRUE elif event == em.TOGGLE_OSD: # Display the channel info message tuner_id, chan_name, prog_info = self.fc.getChannelInfo() now = time.strftime('%H:%M') msg = '%s %s (%s): %s' % (now, chan_name, tuner_id, prog_info) cmd = 'osd_show_text "%s"\n' % msg self.app.write(cmd) return FALSE return FALSE
class Xawtv: __muted = 0 __igainvol = 0 def __init__(self, app, remote): self.tuner_chidx = 0 # Current channel, index into config.TV_CHANNELS self.app_mode = 'tv' self.fc = FreevoChannels() self.current_vg = None self.xawtv_prog = app self.remote_prog = remote def TunerSetChannel(self, tuner_channel): for pos in range(len(config.TV_CHANNELS)): channel = config.TV_CHANNELS[pos] if channel[2] == tuner_channel: self.tuner_chidx = pos return print 'ERROR: Cannot find tuner channel "%s" in the TV channel listing' % tuner_channel self.tuner_chidx = 0 def TunerGetChannelInfo(self): '''Get program info for the current channel''' tuner_id = config.TV_CHANNELS[self.tuner_chidx][2] chan_name = config.TV_CHANNELS[self.tuner_chidx][1] chan_id = config.TV_CHANNELS[self.tuner_chidx][0] channels = epg.get_guide().get_programs(time.time(), time.time(), chan_id) if channels and channels[0] and channels[0].programs: start_s = time.strftime( config.TV_TIME_FORMAT, time.localtime(channels[0].programs[0].start)) stop_s = time.strftime( config.TV_TIME_FORMAT, time.localtime(channels[0].programs[0].stop)) ts = '(%s-%s)' % (start_s, stop_s) prog_info = '%s %s' % (ts, channels[0].programs[0].title) else: prog_info = 'No info' return tuner_id, chan_name, prog_info def TunerGetChannel(self): return config.TV_CHANNELS[self.tuner_chidx][2] def TunerNextChannel(self): self.tuner_chidx = (self.tuner_chidx + 1) % len(config.TV_CHANNELS) def TunerPrevChannel(self): self.tuner_chidx = (self.tuner_chidx - 1) % len(config.TV_CHANNELS) def Play(self, mode, tuner_channel=None, channel_change=0): if tuner_channel != None: try: self.TunerSetChannel(tuner_channel) except ValueError: pass if not tuner_channel: tuner_channel = self.fc.getChannel() vg = self.current_vg = self.fc.getVideoGroup(tuner_channel, True) if not vg.group_type == 'normal': print 'Xawtv only supports normal. "%s" is not implemented' % vg.group_type return if mode == 'tv' or mode == 'vcr': w, h = config.TV_VIEW_SIZE cf_norm = vg.tuner_norm cf_input = vg.input_num cf_device = vg.vdev s_norm = cf_norm.upper() if mode == 'vcr': cf_input = '1' if hasattr(config, "TV_VCR_INPUT_NUM") and config.TV_VCR_INPUT_NUM: cf_input = config.TV_VCR_INPUT_NUM if hasattr(config, "TV_XAWTV_OPTS") and config.TV_XAWTV_OPTS: daoptions = config.TV_XAWTV_OPTS else: daoptions = '-xv -f' command = '%s %s -device %s ' % (self.xawtv_prog, daoptions, cf_device) else: print 'Mode "%s" is not implemented' % mode # BUG ui.message() return self.mode = mode mixer = plugin.getbyname('MIXER') # BUG Mixer manipulation code. # TV is on line in # VCR is mic in # btaudio (different dsp device) will be added later if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer_vol = mixer.getMainVolume() mixer.setMainVolume(0) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer_vol = mixer.getPcmVolume() mixer.setPcmVolume(0) # Start up the TV task self.app = XawtvApp(command, self.remote_prog) if tuner_channel: time.sleep(0.5) self.app.sendcmd('setstation %s' % tuner_channel) #XXX use remote to change the input we want self.prev_app = rc.app() rc.app(self) # Suppress annoying audio clicks time.sleep(0.4) # BUG Hm.. This is hardcoded and very unflexible. if mixer and mode == 'vcr': mixer.setMicVolume(config.MIXER_VOLUME_VCR_IN) elif mixer: mixer.setLineinVolume(config.MIXER_VOLUME_TV_IN) mixer.setIgainVolume(config.MIXER_VOLUME_TV_IN) if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer.setMainVolume(mixer_vol) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer.setPcmVolume(mixer_vol) _debug_('%s: started %s app' % (time.time(), self.mode)) def Stop(self, channel_change=0): mixer = plugin.getbyname('MIXER') if mixer and not channel_change: mixer.setLineinVolume(0) mixer.setMicVolume(0) mixer.setIgainVolume(0) # Input on emu10k cards. self.app.stop() rc.app(self.prev_app) def eventhandler(self, event, menuw=None): _debug_('%s: %s app got %s event' % (time.time(), self.mode, event)) if event == em.STOP or event == em.PLAY_END: self.app.sendcmd('quit') time.sleep(1) self.Stop() rc.post_event(em.PLAY_END) return True elif event == em.TV_CHANNEL_UP or event == em.TV_CHANNEL_DOWN: if self.mode == 'vcr': return if event == em.TV_CHANNEL_UP: self.TunerPrevChannel() self.app.sendcmd('setstation next') else: self.TunerNextChannel() self.app.sendcmd('setstation prev') return True elif event == em.TOGGLE_OSD: #try to send channel name self.app.sendcmd('msg \'%s\'' % self.TunerGetChannel()) return True elif event == em.OSD_MESSAGE: self.app.sendcmd('msg \'%s\'' % event.arg) return True elif event in em.INPUT_ALL_NUMBERS: self.app.sendcmd('keypad %s' % event.arg) elif event == em.BUTTON: if event.arg == 'PREV_CH': self.app.sendcmd('setstation back') return True return False
class PluginInterface(plugin.DaemonPlugin): """ plugin to monitor if a recording is about to start and shut down the player if the video device is in use To activate this plugin, just put the following line at the end of your local_conf.py file: | plugin.activate('tv.upsoon') """ __author__ = 'Duncan Webb' __author_email__ = '*****@*****.**' __maintainer__ = __author__ __maintainer_email__ = __author_email__ __version__ = '$Revision$' def __init__(self, standalone=False): """ init the upsoon plugin """ logger.log( 9, 'upsoon.PluginInterface.__init__()') plugin.DaemonPlugin.__init__(self) plugin.register(self, 'upsoon') self.standalone = standalone self.lock = thread.allocate_lock() self.running = True self.timer = Timer(self.timer_handler).start(15) self.event = EventHandler(self.event_handler) #self.event.register(('VIDEO_START', 'VIDEO_END')) self.event.register() self.recordclient = RecordClient() self.fc = FreevoChannels() self.rdev = config.RADIO_DEVICE self.next_program = None self.announced = False self.seconds_before_announce = config.TV_UPSOON_ANNOUNCE self.seconds_before_start = config.TV_UPSOON_BEFORE_START self.pending_lockfile = config.FREEVO_CACHEDIR + '/record.soon' self.tv_lockfile = None # lockfile of recordserver self.stopped = None # flag that tells upsoon what stopped if os.path.exists(self.pending_lockfile): os.remove(self.pending_lockfile) logger.debug('%r lockfile removed', self.pending_lockfile) def config(self): return [ ('TV_UPSOON_BEFORE_START', 1*60, 'Number of seconds before start of recording to stop player'), ('TV_UPSOON_ANNOUNCE', 1*60+60, 'Number of seconds before start of recording to announce starting'), ] def getVideoForChannel(self, channel_id): """ get the video device given a channel id """ logger.log( 9, 'getVideoForChannel(channel_id=%r)', channel_id) return self.fc.getVideoGroup(channel_id, False, CHANNEL_ID).vdev def stopVideoInUse(self, vdev): """ stop the video device if being used """ logger.log( 9, 'stopVideoInUse(vdev=%r)', vdev) if vdev: try: dev_fh = os.open(vdev, os.O_TRUNC) try: os.read(dev_fh, 1) except: if (self.seconds_to_next > self.seconds_before_start): # just announce rc.post_event(Event(OSD_MESSAGE, arg=_('A recording will start in a few minutes'))) else: # stop the tv rc.post_event(STOP) self.stopped = _('TV') os.close(dev_fh) except Exception, why: logger.warning('cannot check video device %r: %s', vdev, why)
class MPlayer: __muted = 0 __igainvol = 0 def __init__(self): self.tuner_chidx = 0 # Current channel, index into config.TV_CHANNELS self.event_context = 'tv' self.fc = FreevoChannels() self.current_vg = None def Play(self, mode, tuner_channel=None): """ """ logger.log( 9, 'MPlayer.Play(mode=%r, tuner_channel=%r)', mode, tuner_channel) # Try to see if the channel is not tunable try: channel = int(tuner_channel) except ValueError: channel = 0 vg = self.current_vg = self.fc.getVideoGroup(tuner_channel, True) if not tuner_channel: tuner_channel = self.fc.getChannel() # Convert to MPlayer TV setting strings device = 'device=%s' % vg.vdev input = 'input=%s' % vg.input_num norm = 'norm=%s' % vg.tuner_norm w, h = config.TV_VIEW_SIZE outfmt = 'outfmt=%s' % config.TV_VIEW_OUTFMT # Build the MPlayer command line args = { 'nice': config.MPLAYER_NICE, 'cmd': config.MPLAYER_CMD, 'vo': '-vo %s' % config.MPLAYER_VO_DEV, 'vo_opts': config.MPLAYER_VO_DEV_OPTS, 'vc': '', 'ao': '-ao %s' % config.MPLAYER_AO_DEV, 'ao_opts': config.MPLAYER_AO_DEV_OPTS, 'default_args': config.MPLAYER_ARGS_DEF.split(), 'mode_args': config.MPLAYER_ARGS.has_key(mode) and config.MPLAYER_ARGS[mode].split() or [], 'geometry': (config.CONF.x or config.CONF.y) and '-geometry %d:%d' % (config.CONF.x, config.CONF.y) or '', 'verbose': '', 'dvd-device': '', 'cdrom-device': '', 'alang': '', 'aid': '', 'slang': '', 'sid': '', 'playlist': '', 'field-dominance': '', 'edl': '', 'mc': '', 'delay': '', 'sub': '', 'audiofile': '', 'af': [], 'vf': [], 'tv': '', 'url': '', 'disable_osd': False, } if dialog.overlay_display_supports_dialogs: # Disable the mplayer OSD if we have a better option. args['disable_osd'] = True if mode == 'tv': if vg.group_type == 'ivtv': ivtv_dev = ivtv.IVTV(vg.vdev) ivtv_dev.init_settings() ivtv_dev.setinputbyname(vg.input_type) cur_std = ivtv_dev.getstd() import tv.v4l2 try: new_std = tv.v4l2.NORMS.get(vg.tuner_norm) if cur_std != new_std: ivtv_dev.setstd(new_std) except: logger.error('Error! Videogroup norm value "%s" not from NORMS: %s', vg.tuner_norm, tv.v4l2.NORMS.keys()) ivtv_dev.close() # Do not set the channel if negative if channel >= 0: self.fc.chanSet(tuner_channel, True) args['url'] = vg.vdev if config.MPLAYER_ARGS.has_key('ivtv'): args['mode_args'] = config.MPLAYER_ARGS['ivtv'].split() elif vg.group_type == 'webcam': self.fc.chanSet(tuner_channel, True, app='mplayer') args['url'] = '' if config.MPLAYER_ARGS.has_key('webcam'): args['mode_args'] = config.MPLAYER_ARGS['webcam'].split() elif vg.group_type == 'dvb': self.fc.chanSet(tuner_channel, True, app='mplayer') args['url'] = ('dvb://%s' % (tuner_channel,)) args['mode_args'] = config.MPLAYER_ARGS['dvb'].split() elif vg.group_type == 'tvalsa': freq_khz = self.fc.chanSet(tuner_channel, True, app='mplayer') tuner_freq = '%1.3f' % (freq_khz / 1000.0) args['tv'] = '-tv driver=%s:%s:freq=%s:%s:%s:%s:width=%s:height=%s:%s %s' % \ (config.TV_DRIVER, vg.adev, tuner_freq, device, input, norm, w, h, outfmt, config.TV_OPTS) args['url'] = 'tv://' if config.MPLAYER_ARGS.has_key('tv'): args['mode_args'] = config.MPLAYER_ARGS['tv'].split() else: # group_type == 'normal' freq_khz = self.fc.chanSet(tuner_channel, True, app='mplayer') tuner_freq = '%1.3f' % (freq_khz / 1000.0) args['tv'] = '-tv driver=%s:freq=%s:%s:%s:%s:width=%s:height=%s:%s %s' % \ (config.TV_DRIVER, tuner_freq, device, input, norm, w, h, outfmt, config.TV_OPTS) args['url'] = 'tv://' if config.MPLAYER_ARGS.has_key('tv'): args['mode_args'] = config.MPLAYER_ARGS['tv'].split() elif mode == 'vcr': args['tv'] = '-tv driver=%s:%s:%s:%s:width=%s:height=%s:%s %s' % \ (config.TV_DRIVER, device, input, norm, w, h, outfmt, config.TV_OPTS) args['url'] = 'tv://' if config.MPLAYER_ARGS.has_key('tv'): args['mode_args'] = config.MPLAYER_ARGS['tv'].split() else: logger.error('Mode "%s" is not implemented', mode) return logger.debug('mplayer args = %r', args) vo = ['%(vo)s' % args, '%(vo_opts)s' % args] vo = filter(len, vo) vo = ':'.join(vo) ao = ['%(ao)s' % args, '%(ao_opts)s' % args] ao = filter(len, ao) ao = ':'.join(ao) command = ['--prio=%(nice)s' % args] command += ['%(cmd)s' % args] command += ['-slave'] command += str('%(verbose)s' % args).split() command += str('%(geometry)s' % args).split() command += vo.split() command += str('%(vc)s' % args).split() command += ao.split() command += args['default_args'] command += args['mode_args'] command += str('%(dvd-device)s' % args).split() command += str('%(cdrom-device)s' % args).split() command += str('%(alang)s' % args).split() command += str('%(aid)s' % args).split() command += str('%(audiofile)s' % args).split() command += str('%(slang)s' % args).split() command += str('%(sid)s' % args).split() command += str('%(sub)s' % args).split() command += str('%(field-dominance)s' % args).split() command += str('%(edl)s' % args).split() command += str('%(mc)s' % args).split() command += str('%(delay)s' % args).split() if args['af']: command += ['-af', '%s' % ','.join(args['af'])] if args['vf']: command += ['-vf', '%s' % ','.join(args['vf'])] command += str('%(tv)s' % args).split() command += args['disable_osd'] and ['-osdlevel', '0'] or [] if config.OSD_SINGLE_WINDOW: command += ['-wid', str(osd.video_window.id)] osd.video_window.show() # use software scaler? if '-nosws' in command: command.remove('-nosws') elif '-framedrop' not in command: command += config.MPLAYER_SOFTWARE_SCALER.split() #if options: # command += options command = filter(len, command) #command = self.sort_filter(command) url = '%(url)s' % args command += [url] logger.debug('%r', command) self.mode = mode # XXX Mixer manipulation code. # TV is on line in # VCR is mic in # btaudio (different dsp device) will be added later mixer = plugin.getbyname('MIXER') if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer_vol = mixer.getMainVolume() mixer.setMainVolume(0) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer_vol = mixer.getPcmVolume() mixer.setPcmVolume(0) # Start up the TV task self.app = childapp.ChildApp2(command) rc.add_app(self) # Suppress annoying audio clicks time.sleep(0.4) # XXX Hm.. This is hardcoded and very unflexible. if mixer and mode == 'vcr': mixer.setMicVolume(config.MIXER_VOLUME_VCR_IN) elif mixer: mixer.setLineinVolume(config.MIXER_VOLUME_TV_IN) mixer.setIgainVolume(config.MIXER_VOLUME_TV_IN) if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer.setMainVolume(mixer_vol) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer.setPcmVolume(mixer_vol) dialog.enable_overlay_display(AppTextDisplay(self.show_message)) logger.debug('%s: started %s app', time.time(), self.mode) def Stop(self, channel_change=0): mixer = plugin.getbyname('MIXER') if mixer and not channel_change: mixer.setLineinVolume(0) mixer.setMicVolume(0) mixer.setIgainVolume(0) # Input on emu10k cards. if config.OSD_SINGLE_WINDOW: osd.video_window.hide() self.app.stop('quit\n') rc.remove_app(self) if os.path.exists('/tmp/freevo.wid'): os.unlink('/tmp/freevo.wid') if config.MPLAYER_OLDTVCHANNELCHANGE: lastchanfile = os.path.join(config.FREEVO_CACHEDIR, 'lastchan') lcfp = open(lastchanfile, "w") lastchan = self.fc.getChannel() lastchannum = self.fc.getChannelNum() lcfp.write(str(lastchan)) lcfp.write('\n') lcfp.write(str(lastchannum)) lcfp.write('\n') lcfp.close() dialog.disable_overlay_display() def eventhandler(self, event, menuw=None): s_event = '%s' % event if event == em.STOP or event == em.PLAY_END: self.Stop() rc.post_event(em.PLAY_END) return True elif event == em.PAUSE or event == em.PLAY: self.app.write('pause\n') logger.debug('%s: sending pause to mplayer', time.time()) return True elif event in [ em.TV_CHANNEL_UP, em.TV_CHANNEL_DOWN, em.TV_CHANNEL_LAST ] or s_event.startswith('INPUT_'): chan = None if event == em.TV_CHANNEL_UP: nextchan = self.fc.getNextChannel() nextchannum = self.fc.getNextChannelNum() elif event == em.TV_CHANNEL_DOWN: nextchan = self.fc.getPrevChannel() nextchannum = self.fc.getPrevChannelNum() elif event == em.TV_CHANNEL_LAST: if config.MPLAYER_OLDTVCHANNELCHANGE: if os.path.isfile(os.path.join(config.FREEVO_CACHEDIR, 'lastchan')): lastchanfile = os.path.join(config.FREEVO_CACHEDIR, 'lastchan') lcfp = open(lastchanfile, "r") nextchan = lcfp.readline() nextchan = nextchan.strip() nextchannum = lcfp.readline() nextchannum = nextchannum.strip() nextchannum = int(nextchannum) lcfp.close() else: nextchan = self.fc.getChannel() nextchannum = self.fc.getChannelNum() else: return True else: chan = int(s_event[6]) nextchan = self.fc.getManChannel(chan) nextchannum = self.fc.getManChannelNum(chan) nextvg = self.fc.getVideoGroup(nextchan, True) logger.debug('chan=%s, nextchannum=%s, nextchan=%s nextvg=%s', chan, nextchannum, nextchan, nextvg) if self.current_vg != nextvg: self.Stop(channel_change=1) self.Play('tv', nextchan) return True if self.mode == 'vcr': return elif self.current_vg.group_type == 'dvb': if not config.MPLAYER_OLDTVCHANNELCHANGE: card = 0 # May be this should come from video groups or TV_CHANNELS if em.TV_CHANNEL_UP: self.app.write('dvb_set_channel %s %s\n' % (nextchannum, card)) self.fc.chanSet(nextchan, True) elif em.TV_CHANNEL_DOWN: self.app.write('dvb_set_channel %s %s\n' % (nextchannum, card)) self.fc.chanSet(nextchan, True) else: self.Stop(channel_change=1) self.Play('tv', nextchan) return True elif self.current_vg.group_type == 'ivtv': self.fc.chanSet(nextchan, True) self.app.write('seek 999999 0\n') else: freq_khz = self.fc.chanSet(nextchan, True, app=self.app) new_freq = '%1.3f' % (freq_khz / 1000.0) self.app.write('tv_set_freq %s\n' % new_freq) self.current_vg = self.fc.getVideoGroup(self.fc.getChannel(), True) # Display a channel changed message tuner_id, chan_name, prog_info = self.fc.getChannelInfo() now = time.strftime(config.TV_TIME_FORMAT) msg = '%s %s (%s): %s' % (now, chan_name, tuner_id, prog_info) cmd = 'osd_show_text "%s"\n' % msg self.app.write(cmd) return True elif event == em.TOGGLE_OSD: if dialog.is_dialog_supported(): dialog.show_play_state(dialog.PLAY_STATE_INFO, self.fc) else: # Display the channel info message tuner_id, chan_name, prog_info = self.fc.getChannelInfo() now = time.strftime(config.TV_TIME_FORMAT) msg = '%s %s (%s): %s' % (now, chan_name, tuner_id, prog_info) cmd = 'osd_show_text "%s"\n' % msg self.app.write(cmd) return False elif event == em.OSD_MESSAGE: self.app.write('osd_show_text "%s"\n' % event.arg); return True elif event == em.TV_SEND_MPLAYER_CMD: self.app.write('%s\n' % event.arg); return True return False def show_message(self, message): self.app.write('osd_show_text "%s"\n' % message)
class Recorder: """ """ def __init__(self): logger.log( 9, 'Recorder.__init__()') # Disable this plugin if not loaded by record_server. if string.find(sys.argv[0], 'recordserver') == -1: return logger.info('ACTIVATING VBI2SRT RECORD PLUGIN') self.vg = None self.fc = FreevoChannels() self.tuner_chidx = 0 # Current channel, index into config.TV_CHANNELS self.thread = Record_Thread() self.thread.setDaemon(1) self.thread.mode = 'idle' self.thread.start() def Record(self, rec_prog): logger.log( 9, 'Record(rec_prog=%r)', rec_prog) frequency = self.fc.chanSet(str(rec_prog.tunerid), False, 'record plugin') rec_prog.filename = tv_util.getProgFilename(rec_prog) rec_prog.filename = os.path.splitext(tv_util.getProgFilename(rec_prog))[0] + '.mpeg' logger.debug('filename=%r', rec_prog.filename) self.vg = self.fc.getVideoGroup(rec_prog.tunerid, False) cl_options = { 'channel' : rec_prog.tunerid, 'frequency' : frequency, 'frequencyMHz' : float(frequency) / 1000, 'filename' : rec_prog.filename, 'base_filename' : os.path.basename(rec_prog.filename), 'title' : rec_prog.title, 'sub-title' : rec_prog.sub_title, 'seconds' : rec_prog.rec_duration, 'start' : rec_prog.start, 'pdc-start' : rec_prog.pdc_start, } logger.debug('cl_options=%r', cl_options) logger.debug('chan_index=%r', self.fc.chan_index) logger.debug('vg.vdev=%r', self.vg.vdev) logger.debug('vg.vvbi=%r', self.vg.vvbi) pagenum = None; try: pagenum = int(config.TV_CHANNELS[self.fc.chan_index][5]) except: pagenum = None; logger.debug('pagenum=%r', pagenum) if pagenum: # there is a bug in vbi2srt that causes out of sync subtitles when VPS is used self.rec_command = \ 'vbi2srt --verbose --video-in=%s --video-out=%s --vbi-device=%s --seconds=%s --vps=%s --page=%s' % \ (self.vg.vdev, rec_prog.filename, self.vg.vvbi, rec_prog.rec_duration, rec_prog.pdc_start, pagenum) else: self.rec_command = \ 'vbi2srt --verbose --video-in=%s --video-out=%s --vbi-device=%s --seconds=%s --vps=%s' % \ (self.vg.vdev, rec_prog.filename, self.vg.vvbi, rec_prog.rec_duration, rec_prog.pdc_start) logger.debug('rec_command=%r', self.rec_command) self.thread.mode = 'record' self.thread.prog = rec_prog self.thread.command = self.rec_command self.thread.autokill = float(rec_prog.rec_duration + 10) self.thread.mode_flag.set() def Stop(self): logger.log( 9, 'Stop()') self.thread.mode = 'stop' self.thread.mode_flag.set()
def run(self): _debug_('Record_Thread.run()', 2) while 1: _debug_('Record_Thread::run: mode=%s' % self.mode) if self.mode == 'idle': self.mode_flag.wait() self.mode_flag.clear() elif self.mode == 'record': try: _debug_('Record_Thread::run: started recording', DINFO) fc = FreevoChannels() _debug_('Channel: %s' % (fc.getChannel())) vg = fc.getVideoGroup(self.prog.tunerid, False) _debug_('Opening device %r' % (vg.vdev)) v = tv.ivtv.IVTV(vg.vdev) v.init_settings() _debug_('Setting input to %r' % (vg.input_type)) v.setinputbyname(vg.input_type) cur_std = v.getstd() try: new_std = V4L2.NORMS.get(vg.tuner_norm) if cur_std != new_std: _debug_('Setting standard to %s' % (new_std)) v.setstd(new_std) except: _debug_("Videogroup norm value '%s' not from NORMS: %s" % \ (vg.tuner_norm, V4L2.NORMS.keys()), DERROR) _debug_('Setting channel to %r' % self.prog.tunerid) fc.chanSet(str(self.prog.tunerid), False) if vg.cmd != None: _debug_("Running command %r" % vg.cmd) retcode = os.system(vg.cmd) _debug_("exit code: %g" % retcode) now = time.time() stop = now + self.prog.rec_duration rc.post_event(Event('RECORD_START', arg=self.prog)) time.sleep(2) v_in = open(vg.vdev, 'r') v_out = open(self.prog.filename, 'w') _debug_('Recording from %r to %r in %s byte chunks' % (vg.vdev, self.prog.filename, CHUNKSIZE)) while time.time() < stop: buf = v_in.read(CHUNKSIZE) v_out.write(buf) if self.mode == 'stop': _debug_('Recording stopped', DINFO) break v_in.close() v_out.close() v.close() v = None self.mode = 'idle' rc.post_event(Event('RECORD_STOP', arg=self.prog)) _debug_('Record_Thread::run: finished recording', DINFO) except Exception, why: _debug_('%s' % (why), DCRITICAL) return else: self.mode = 'idle' time.sleep(0.5)
class TVTime: __muted = 0 __igainvol = 0 def __init__(self): self.event_context = 'tv' self.fc = FreevoChannels() self.current_vg = None self.optionD_supported = False self.xmltv_supported = False def TunerSetChannel(self, tuner_channel): for pos in range(len(config.TV_CHANNELS)): channel = config.TV_CHANNELS[pos] if channel[2] == tuner_channel: return pos print 'ERROR: Cannot find tuner channel "%s" in the TV channel listing' % tuner_channel return 0 def TunerGetChannelInfo(self): return self.fc.getChannelInfo() def TunerGetChannel(self): return self.fc.getChannel() def Play(self, mode, tuner_channel=None, channel_change=0): if not tuner_channel: tuner_channel = self.fc.getChannel() vg = self.current_vg = self.fc.getVideoGroup(tuner_channel, True) if not vg.group_type == 'normal': print 'Tvtime only supports normal. "%s" is not implemented' % vg.group_type return if mode == 'tv' or mode == 'vcr': w, h = config.TV_VIEW_SIZE cf_norm = vg.tuner_norm cf_input = vg.input_num cf_device = vg.vdev s_norm = cf_norm.upper() outputplugin = '' if plugin.getbyname(plugin.TV).optionD_supported: if config.CONF.display == 'x11': outputplugin = 'Xv' elif config.CONF.display == 'mga': outputplugin = 'mga' elif config.CONF.display in ( 'directfb', 'dfbmga' ): outputplugin = 'directfb' else: outputplugin = config.CONF.display outputplugin = '-D %s' % outputplugin if mode == 'vcr': cf_input = '1' if hasattr(config, "TV_VCR_INPUT_NUM") and config.TV_VCR_INPUT_NUM: cf_input = config.TV_VCR_INPUT_NUM self.fc.chan_index = self.TunerSetChannel(tuner_channel) if hasattr(config, 'TV_PAD_CHAN_NUMBERS') and config.TV_PAD_CHAN_NUMBERS: mychan = tuner_channel else: mychan = self.fc.chan_index logger.debug('starting channel is %s', mychan) command = '%s %s -k -I %s -n %s -d %s -f %s -c %s -i %s' % \ (config.TVTIME_CMD, outputplugin, w, s_norm, cf_device, 'freevo', mychan, cf_input) if osd.get_fullscreen() == 1: command += ' -m' else: command += ' -M' else: print 'Mode "%s" is not implemented' % mode # BUG ui.message() return self.mode = mode mixer = plugin.getbyname('MIXER') # BUG Mixer manipulation code. # TV is on line in # VCR is mic in # btaudio (different dsp device) will be added later if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer_vol = mixer.getMainVolume() mixer.setMainVolume(0) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer_vol = mixer.getPcmVolume() mixer.setPcmVolume(0) # Start up the TV task self.app=TVTimeApp(command) rc.add_app(self) # Suppress annoying audio clicks time.sleep(0.4) # BUG Hm.. This is hardcoded and very unflexible. if mixer and mode == 'vcr': mixer.setMicVolume(config.MIXER_VOLUME_VCR_IN) elif mixer: mixer.setLineinVolume(config.MIXER_VOLUME_TV_IN) mixer.setIgainVolume(config.MIXER_VOLUME_TV_IN) if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer.setMainVolume(mixer_vol) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer.setPcmVolume(mixer_vol) logger.debug('%s: started %s app', time.time(), self.mode) def Stop(self, channel_change=0): mixer = plugin.getbyname('MIXER') if mixer and not channel_change: mixer.setLineinVolume(0) mixer.setMicVolume(0) mixer.setIgainVolume(0) # Input on emu10k cards. self.app.stop('quit\n') rc.remove_app(self) def eventhandler(self, event, menuw=None): logger.debug('%s: %s app got %s event', time.time(), self.mode, event) if event == em.STOP or event == em.PLAY_END: self.Stop() rc.post_event(em.PLAY_END) return True elif event == em.TV_CHANNEL_UP or event == em.TV_CHANNEL_DOWN: if self.mode == 'vcr': return if event == em.TV_CHANNEL_UP: nextchan = self.fc.getNextChannel() elif event == em.TV_CHANNEL_DOWN: nextchan = self.fc.getPrevChannel() nextvg = self.fc.getVideoGroup(nextchan, True) logger.debug("nextchan is %s", nextchan) # XXX hazardous to your health. don't use tvtime with anything # other than one normal video_group. # we lose track of the channel index at some points and # chaos ensues #if self.current_vg != nextvg: # self.Stop(channel_change=1) # self.Play('tv', nextchan) # return TRUE self.fc.chanSet(nextchan, True, app=self.app) #self.current_vg = self.fc.getVideoGroup(self.fc.getChannel(), True) # Go to the prev/next channel in the list if event == em.TV_CHANNEL_UP: self.app.write('CHANNEL_UP\n') else: self.app.write('CHANNEL_DOWN\n') return True elif event == em.TOGGLE_OSD: self.app.write('DISPLAY_INFO\n') return True elif event == em.OSD_MESSAGE: # XXX this doesn't work #self.app.write('display_message %s\n' % event.arg) #this does os.system('tvtime-command display_message \'%s\'' % event.arg) return True elif event == em.TV_SEND_TVTIME_CMD: os.system('tvtime-command %s' % event.arg) return True elif event in em.INPUT_ALL_NUMBERS: self.app.write('CHANNEL_%s\n' % event.arg) elif event == em.BUTTON: if event.arg == 'PREV_CH': self.app.write('CHANNEL_PREV\n') return True return False
class TunerControl: """ Class that controls the tuner device """ def __init__(self, xine): """ TunerControl constructor """ self.xine = xine self.ivtv_init = False self.fc = FreevoChannels() self.curr_channel = None self.embed = None self.stack = [ ] def _kill_(self): """ TunerControl destructor """ if self.embed: ivtv_dev.setvbiembed(self.embed) def Stop(self): """ stop """ self.ivtv_init = False def GetChannelName(self): """ get channel info """ tuner_id, chan_name, prog_info = self.fc.getChannelInfo(showtime=False) return (chan_name) def GetProgramName(self): """ get channel name """ tuner_id, chan_name, prog_info = self.fc.getChannelInfo(showtime=True) return prog_info def GetInfo(self): """ get channel info """ tuner_id, chan_id, chan_name, start_t, stop_t, prog_s = self.fc.getChannelInfoRaw() return (start_t, stop_t, prog_s) def ShowInfo(self): """ show channel info """ if self.curr_channel is not None: tuner_id, chan_name, prog_info = self.fc.getChannelInfo(showtime=False) self.xine.ShowMessage(msg = '%s: %s' % (chan_name, prog_info)) def PushChannel(self): """ push the current channel on the channel stack """ if self.curr_channel is not None: self.stack.append(self.curr_channel) logger.debug('TunerControl: Pushed channel %s', self.curr_channel) logger.debug('TunerControl: Channel stack = %s', self.stack) def UnpushChannel(self): """ remove the top channel from the channel stack """ if len(self.stack) == 0: logger.debug('TunerControl: Channel stack is empty') else: channel = self.stack.pop() logger.debug('TunerControl: Unpushed channel %s', channel) logger.debug('TunerControl: Channel stack = %s', self.stack) def PopChannel(self): """ pop the top channel from the channel stack and switch channel """ if len(self.stack) == 0: logger.debug('TunerControl: Channel stack is empty') else: channel = self.stack.pop() logger.debug('TunerControl: Popped channel %s', channel) self.SetVideoGroup(channel) logger.debug('TunerControl: Channel stack = %s', self.stack) def SwapChannel(self): """swap the current display channel and the top of the stack channel """ if self.curr_channel is not None: toswap = self.curr_channel if len(self.stack) == 0: logger.debug('TunerControl: Channel stack is empty') else: channel = self.stack.pop() logger.debug('TunerControl: Popped channel %s', channel) self.SetVideoGroup(channel) self.stack.append(toswap) logger.debug('TunerControl: Pushed channel %s', toswap) logger.debug('TunerControl: Channel stack = %s', self.stack) def SetChannelByName(self, channel=None, clearstack=False): """ tune to a new channel by name """ last_channel = self.curr_channel next_channel = None channel_index = -1 if clearstack: self.stack = [ ] self.curr_channel = None if channel is None: # get a channel next_channel = self.fc.getChannel() try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS)): entry = config.TV_CHANNELS[pos] if str(channel) == str(entry[2]): channel_index = pos next_channel = channel break except ValueError: pass if next_channel is None: logger.warning('TunerControl: Cannot find tuner channel %r in the TV channel listing', channel) else: self.SetChannelByIndex(channel_index + 1) def SetChannelByIndex(self, channel, tvlike=0): """ tune to a channel by index from the TV_CHANNELS list """ next_channel = self.fc.getManChannel(channel, tvlike) logger.debug('TunerControl: Explicit channel selection by index = %r', next_channel) self.PushChannel() self.SetVideoGroup(next_channel) def SetChannelByNumber(self, channel): """ tune to a channel by actual channel number """ logger.debug('TunerControl: Explicit channel selection by number = %r', channel) self.PushChannel() self.SetVideoGroup(channel) def NextChannel(self): """ jump to the next channel in the TV_CHANNELS list """ next_channel = self.fc.getNextChannel() logger.debug('TunerControl: Next channel selection = %r', next_channel) self.PushChannel() self.SetVideoGroup(next_channel) def PrevChannel(self): """ jump to the previous channel in the TV_CHANNELS list """ prev_channel = self.fc.getPrevChannel() logger.debug('TunerControl: Previous channel selection = %r', prev_channel) self.PushChannel() self.SetVideoGroup(prev_channel) def SetVideoGroup(self, channel): """ select a channel's video group and tune to that channel """ try: channel_num = int(channel) except ValueError: channel_num = 0 logger.debug('TunerControl: Channel: %r', channel) new_vg = self.fc.getVideoGroup(channel, True) logger.debug('TunerControl: Group: type=%r, desc=%r', new_vg.group_type, new_vg.desc) logger.debug('TunerControl: Input: type=%r, num=%r', new_vg.input_type, new_vg.input_num) if new_vg.group_type != 'ivtv': logger.error('TunerControl: VideoGroup %s is not supported', new_vg) pop = AlertBox(text=_('This plugin only supports the ivtv video group!')) pop.show() return # check if videogroup switch is needed switch_vg = not self.ivtv_init or self.curr_channel is None or \ new_vg != self.fc.getVideoGroup(self.curr_channel, True) if switch_vg: # switch to a different video group logger.info('TunerControl: Set VideoGroup: %s', new_vg) ivtv_dev = ivtv.IVTV(new_vg.vdev) ivtv_dev.init_settings() self.xine.SetInput(new_vg.input_num) # disable embedded vbi data self.embed = ivtv_dev.getvbiembed() ivtv_dev.setvbiembed(0) if not self.ivtv_init: # set channel directly on v4l device, if channel is not negative if channel_num >= 0: self.fc.chanSet(channel, True) self.curr_channel = channel self.ivtv_init = True else: # set channel through xine process freq = self.fc.chanSet(channel, True, 'ivtv_xine_tv', None) if freq != 0: # channel has frequency logger.debug('TunerControl: Set frequency: %s', freq) self.xine.SetFrequency(freq) else: # channel has no frequency logger.debug('TunerControl: Channel has no frequency') self.curr_channel = channel self.xine.SeekEnd() self.ShowInfo() self.SetAudioByChannel(channel) def SetAudioByChannel(self, channel=-1): """ Set the PVR sound level This is a mix : The base volume is set by the avol option in each TV_VIDEO_GROUP. The value is hardware dependant. seems bo be between 0 and 65535. If this value is missing in the tv_video_group, that sub does nothing If the value is present, the actual audio value is this value time the 6th field in TV_CHANNELS (expressed in % ) """ try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS)): entry = config.TV_CHANNELS[pos] if str(channel) == str(entry[2]): channel_index = pos break except ValueError: pass logger.debug('SetAudioByChannel: Channel: %r TV_CHANNEL pos(%d)', channel, channel_index) vg = self.fc.getVideoGroup(channel, True) try: ivtv_avol = vg.avol except AttributeError: ivtv_avol = 0 if ivtv_avol <= 0: logger.debug('SetAudioByChannel: The tv_video group for %r doesn\'t set the volume', channel) else: # Is there a specific volume level in TV_CHANNELS_VOLUME ivtv_dev = ivtv.IVTV(vg.vdev) avol_percent = 100 try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS_VOLUME)): if config.TV_CHANNELS_VOLUME[pos][0] == config.TV_CHANNELS[channel_index][0]: avol_percent = config.TV_CHANNELS_VOLUME[pos][1] break except: pass try: avol_percent = int(avol_percent) except ValueError: avol_percent = 100 avol = int(ivtv_avol * avol_percent / 100) if avol > 65535: avol = 65535 if avol < 0: avol = 0 logger.debug('SetAudioByChannel: Current PVR Sound level is : %s', ivtv_dev.getctrl(0x00980905)) logger.debug('SetAudioByChannel: Set the PVR Sound Level to : %s (%s * %s)', avol, ivtv_avol, avol_percent) ivtv_dev.setctrl(0x00980905, avol) logger.debug('SetAudioByChannel: New PVR Sound level is : %s', ivtv_dev.getctrl(0x00980905))
class Xine: """ the main class to control xine """ def __init__(self, type, version, manager, path, size): self.name = 'xine' self.app_mode = 'tv' self.xine_type = type self.version = version self.app = None self.adapter_in_use = -1 self.fc = FreevoChannels() self.path = path self.size = size self.buffer = ring_buffer.RingBuffer(size, path) self.last_channel = None self.subtitles = False self.paused = False self.stop_time = 0 self.state = STATE_IDLE self.manager = manager self.udp_receiver = UDPReceiver(self.buffer, WAIT_FOR_DATA_TIMEOUT) self.slave_server = SlaveServer(self.buffer, self) # Create the command used to start xine. # NOTE: We add the slave server MRL twice so that we can toggle between # them, this allows use to effectively reset Xine rendering pipeline and # make it possible to seek quickly. self.command = [ '--prio=%s' % config.MPLAYER_NICE ] + \ config.XINE_COMMAND.split(' ') + \ [ '--stdctl', '-V', config.XINE_VO_DEV, '-A', config.XINE_AO_DEV ] + \ config.XINE_ARGS_DEF.split(' ') + \ [self.slave_server.get_xine_mrl(), self.slave_server.get_xine_mrl()] if not rc.PYLIRC and '--no-lirc' in self.command: self.command.remove('--no-lirc') self.udp_receiver.start() self.slave_server.start() def Play(self, mode, tuner_channel=None): """ play with xine """ if not tuner_channel: tuner_channel = self.fc.getChannel() if plugin.getbyname('MIXER'): plugin.getbyname('MIXER').reset() rc.app(self) same_channel = self.last_channel == tuner_channel # If it's the same channel as last time and we have come back to it after # more than 2 minutes start at the end of the buffer, otherwise jump # straight back in where we left off. if same_channel: if (time.time() - self.stop_time) > 120.0: start_at_end = True else: start_at_end = False else: self.change_channel(tuner_channel) start_at_end = True if start_at_end: self.start_slave_server_at_end = True if same_channel: self.__change_state(STATE_PLAYING) else: self.__change_state(STATE_PLAYING) return None def stop(self, channel_change=False): """ Stop xine """ if self.app: self.app.stop('quit\n') self.app = None if not channel_change: self.stop_time = time.time() self.__change_state(STATE_IDLE) def disable_buffering(self): """ Stop buffering the current channel. """ if self.adapter_in_use != -1: self.time = 0 self.last_channel = None try: self.manager.disable_output(self.adapter_in_use) except: _debug_('Failed to disable output! ' + traceback.format_exc()) self.adapter_in_use = -1 def pause(self): """ Pause Playback. """ if self.app and not self.paused: _debug_('Pausing') self.app.write('pause\n') self.paused = True def resume(self): """ Resume Playback. """ if self.app and self.paused: _debug_('Resuming') self.app.write('pause\n') self.paused = False def shutdown(self): """ Stop the UDP receiver and slave server. """ if self.udp_receiver: self.udp_receiver.stop() self.udp_receiver = None if self.slave_server: self.slave_server.stop() self.slave_server = None def change_channel(self, channel): """ Select the correct dvbstreamer instance, change the channel and set the primary mrl. """ if self.last_channel == channel: # Already tune to this channel so nothing to do! return adapter = eval(self.fc.getVideoGroup(channel, True).vdev) if (self.adapter_in_use != -1) and (self.adapter_in_use != adapter): try: self.manager.disable_output(self.adapter_in_use) except: _debug_('Failed to disable output! ' + traceback.format_exc()) # We are tuning to a different channel so stop receiving packets and # reset the ring buffer self.udp_receiver.pause = True self.buffer.rwlock.acquire_write() self.buffer.reset() self.manager.select(adapter, channel) if self.adapter_in_use != adapter: self.adapter_in_use = adapter try: if adapter.find(':') != -1: my_ip = self.manager.controllers[adapter].my_ip else: my_ip = 'localhost' self.manager.enable_udp_output(self.adapter_in_use, my_ip, 1234) except: _debug_('Failed to enable output! ' + traceback.format_exc()) # Start receiving packets and remember the channel we've just tuned to. self.udp_receiver.reset() self.udp_receiver.send_events = True self.udp_receiver.pause = False self.buffer.rwlock.release() self.last_channel = channel self.__change_state(STATE_TUNING) def eventhandler(self, event, menuw=None): """ Eventhandler for xine control. If an event is not bound in this function it will be passed over to the items eventhandler """ event_consumed = False if self.state == STATE_IDLE: event_consumed = self.__eventhandler_idle(event, menuw) elif self.state == STATE_TUNING: event_consumed = self.__eventhandler_tuning(event, menuw) elif self.state == STATE_BUFFERING: event_consumed = self.__eventhandler_buffering(event, menuw) elif self.state == STATE_PLAYING: event_consumed = self.__eventhandler_playing(event, menuw) if not event_consumed: _debug_('Unused event %s in state %s' % (event.name, self.state)) return event_consumed def __eventhandler_idle(self, event, menuw): """ Internal event handler when in Idle state. """ return False def __eventhandler_tuning(self, event, menuw): """ Internal event handler when in Tuning state. """ if event == EVENT_DATA_STARTED: self.__change_state(STATE_BUFFERING) return True if event == EVENT_DATA_TIMEDOUT: # Timeout while waiting for data! # We could display a searching for signal graphic or something here self.__change_state(STATE_IDLE) return True return False def __eventhandler_buffering(self, event, menuw): """ Internal event handler when in Buffering state. """ if event == EVENT_DATA_ACQUIRED: self.wait_for_data_count -= 1 self.__draw_state_screen() if self.wait_for_data_count <= 0: self.__change_state(STATE_PLAYING) return True if event == EVENT_DATA_TIMEDOUT: # Timeout while waiting for data! # We could display a searching for signal graphic or something here self.__change_state(STATE_IDLE) return True return False def __eventhandler_playing(self, event, menuw): """ Internal event handler when in Playing state. """ if event == PAUSE or event == PLAY: if self.paused: self.resume() else: self.pause() return True if event in (PLAY_END, USER_END, STOP): self.stop() return True if event in [ TV_CHANNEL_UP, TV_CHANNEL_DOWN ] or (str(event).startswith('INPUT_') and str(event)[6:].isdigit()): _debug_('Event: %s' % str(event)) if event == TV_CHANNEL_UP: nextchan = self.fc.getNextChannel() elif event == TV_CHANNEL_DOWN: nextchan = self.fc.getPrevChannel() else: nextchan = self.fc.getManChannel(int(event.arg)) self.stop(True) self.change_channel(nextchan) # Moves to the Tuning state return True if (event == BUTTON) and (event.arg == 'SUBTITLE'): # Enable/Disable subtitles if self.subtitles: cmd = 'SpuPrior\n' else: cmd = 'SpuNext\n' self.app.write(cmd) self.subtitles = not self.subtitles return True if event == OSD_MESSAGE: self.__osd_write(event.arg) return True if event == TOGGLE_OSD: # Write percent through ringbuffer to OSD channel = self.__get_display_channel() percent = self.slave_server.get_percent() self.__osd_write('%s - %d%%' % (channel, percent)) return True if event == VIDEO_TOGGLE_INTERLACE: self.app.write('ToggleInterleave\n') return True if event == SEEK: steps = int(event.arg) self.buffer.rwlock.acquire_write() bytes_per_sec = self.udp_receiver.average_pps * 188 if steps > 0: can_seek = self.slave_server.reader.available_forward() >= ( (steps + 1) * bytes_per_sec) else: can_seek = self.slave_server.reader.available_backward() > 0 if can_seek: self.slave_server.reader.seek(steps * bytes_per_sec) if self.mrl_index == 0: self.mrl_index = 1 self.app.write('NextMrl\n') else: self.mrl_index = 0 self.app.write('PriorMrl\n') self.buffer.rwlock.release() return True if event == EVENT_READER_OVERTAKEN: if self.paused: self.resume() return True # Consume UDP Receiver events. if event in (EVENT_DATA_ACQUIRED, EVENT_DATA_TIMEDOUT): return True return False def __change_state(self, new_state): """ Internal function to move to a new state. If new_state is different to the current state, set self.state to new_state and perform any state initialisation for the new state. """ if self.state == new_state: # No change in state nothing todo! return _debug_('Changing state from %s to %s' % (self.state, new_state)) self.state = new_state # State Initialisation code if self.state == STATE_IDLE: rc.app(None) rc.post_event(PLAY_END) self.udp_receiver.send_events = False elif self.state == STATE_TUNING: self.start_slave_server_at_end = True elif self.state == STATE_BUFFERING: self.wait_for_data_count = WAIT_FOR_DATA_COUNT elif self.state == STATE_PLAYING: self.slave_server.reader_at_end = self.start_slave_server_at_end self.slave_server.end_offset = self.udp_receiver.average_pps * WAIT_FOR_DATA_COUNT * 188 self.mrl_index = 0 self.app = childapp.ChildApp2(self.command) self.start_slave_server_at_end = False self.__draw_state_screen() def __osd_write(self, text): if self.app: self.app.write('OSDWriteText$ %s\n' % text) def __draw_state_screen(self): osd_obj = osd.get_singleton() percent = 0.0 channel = self.__get_display_channel() if self.state == STATE_IDLE: state_string = None elif self.state == STATE_TUNING: state_string = _('Tuning to %s' % self.last_channel) elif self.state == STATE_BUFFERING: state_string = _('Buffering %s' % self.last_channel) percent = float(WAIT_FOR_DATA_COUNT - self.wait_for_data_count ) / float(WAIT_FOR_DATA_COUNT) elif self.state == STATE_PLAYING: state_string = _('Playing %s' % self.last_channel) percent = 1.0 osd_obj.clearscreen(color=osd_obj.COL_BLACK) if state_string: y = (config.CONF.height / 2) - config.OSD_DEFAULT_FONTSIZE h = -1 font = osd_obj.getfont(config.OSD_DEFAULT_FONTNAME, config.OSD_DEFAULT_FONTSIZE) osd_obj.drawstringframed(state_string, 0, y, config.CONF.width, h, font, fgcolor=osd_obj.COL_ORANGE, align_h='center') x = config.CONF.width / 4 w = x * 2 y = (config.CONF.height / 2) + config.OSD_DEFAULT_FONTSIZE h = int(20.0 * (float(config.CONF.height) / 600.0)) osd_obj.drawbox(x - 2, y - 2, x + w + 2, y + h + 2, color=osd_obj.COL_ORANGE) w = int(float(w) * percent) osd_obj.drawbox(x, y, x + w, y + h, color=osd_obj.COL_ORANGE, fill=1) osd_obj.update() def __get_display_channel(self): channel = self.last_channel for tv_channel_id, tv_display_name, tv_tuner_id in config.TV_CHANNELS: if self.last_channel == tv_tuner_id: channel = tv_display_name return channel
class MPlayer: __muted = 0 __igainvol = 0 def __init__(self): self.tuner_chidx = 0 # Current channel, index into config.TV_CHANNELS self.event_context = 'tv' self.fc = FreevoChannels() self.current_vg = None def Play(self, mode, tuner_channel=None): """ """ logger.log(9, 'MPlayer.Play(mode=%r, tuner_channel=%r)', mode, tuner_channel) # Try to see if the channel is not tunable try: channel = int(tuner_channel) except ValueError: channel = 0 vg = self.current_vg = self.fc.getVideoGroup(tuner_channel, True) if not tuner_channel: tuner_channel = self.fc.getChannel() # Convert to MPlayer TV setting strings device = 'device=%s' % vg.vdev input = 'input=%s' % vg.input_num norm = 'norm=%s' % vg.tuner_norm w, h = config.TV_VIEW_SIZE outfmt = 'outfmt=%s' % config.TV_VIEW_OUTFMT # Build the MPlayer command line args = { 'nice': config.MPLAYER_NICE, 'cmd': config.MPLAYER_CMD, 'vo': '-vo %s' % config.MPLAYER_VO_DEV, 'vo_opts': config.MPLAYER_VO_DEV_OPTS, 'vc': '', 'ao': '-ao %s' % config.MPLAYER_AO_DEV, 'ao_opts': config.MPLAYER_AO_DEV_OPTS, 'default_args': config.MPLAYER_ARGS_DEF.split(), 'mode_args': config.MPLAYER_ARGS.has_key(mode) and config.MPLAYER_ARGS[mode].split() or [], 'geometry': (config.CONF.x or config.CONF.y) and '-geometry %d:%d' % (config.CONF.x, config.CONF.y) or '', 'verbose': '', 'dvd-device': '', 'cdrom-device': '', 'alang': '', 'aid': '', 'slang': '', 'sid': '', 'playlist': '', 'field-dominance': '', 'edl': '', 'mc': '', 'delay': '', 'sub': '', 'audiofile': '', 'af': [], 'vf': [], 'tv': '', 'url': '', 'disable_osd': False, } if dialog.overlay_display_supports_dialogs: # Disable the mplayer OSD if we have a better option. args['disable_osd'] = True if mode == 'tv': if vg.group_type == 'ivtv': ivtv_dev = ivtv.IVTV(vg.vdev) ivtv_dev.init_settings() ivtv_dev.setinputbyname(vg.input_type) cur_std = ivtv_dev.getstd() import tv.v4l2 try: new_std = tv.v4l2.NORMS.get(vg.tuner_norm) if cur_std != new_std: ivtv_dev.setstd(new_std) except: logger.error( 'Error! Videogroup norm value "%s" not from NORMS: %s', vg.tuner_norm, tv.v4l2.NORMS.keys()) ivtv_dev.close() # Do not set the channel if negative if channel >= 0: self.fc.chanSet(tuner_channel, True) args['url'] = vg.vdev if config.MPLAYER_ARGS.has_key('ivtv'): args['mode_args'] = config.MPLAYER_ARGS['ivtv'].split() elif vg.group_type == 'webcam': self.fc.chanSet(tuner_channel, True, app='mplayer') args['url'] = '' if config.MPLAYER_ARGS.has_key('webcam'): args['mode_args'] = config.MPLAYER_ARGS['webcam'].split() elif vg.group_type == 'dvb': self.fc.chanSet(tuner_channel, True, app='mplayer') args['url'] = ('dvb://%s' % (tuner_channel, )) args['mode_args'] = config.MPLAYER_ARGS['dvb'].split() elif vg.group_type == 'tvalsa': freq_khz = self.fc.chanSet(tuner_channel, True, app='mplayer') tuner_freq = '%1.3f' % (freq_khz / 1000.0) args['tv'] = '-tv driver=%s:%s:freq=%s:%s:%s:%s:width=%s:height=%s:%s %s' % \ (config.TV_DRIVER, vg.adev, tuner_freq, device, input, norm, w, h, outfmt, config.TV_OPTS) args['url'] = 'tv://' if config.MPLAYER_ARGS.has_key('tv'): args['mode_args'] = config.MPLAYER_ARGS['tv'].split() else: # group_type == 'normal' freq_khz = self.fc.chanSet(tuner_channel, True, app='mplayer') tuner_freq = '%1.3f' % (freq_khz / 1000.0) args['tv'] = '-tv driver=%s:freq=%s:%s:%s:%s:width=%s:height=%s:%s %s' % \ (config.TV_DRIVER, tuner_freq, device, input, norm, w, h, outfmt, config.TV_OPTS) args['url'] = 'tv://' if config.MPLAYER_ARGS.has_key('tv'): args['mode_args'] = config.MPLAYER_ARGS['tv'].split() elif mode == 'vcr': args['tv'] = '-tv driver=%s:%s:%s:%s:width=%s:height=%s:%s %s' % \ (config.TV_DRIVER, device, input, norm, w, h, outfmt, config.TV_OPTS) args['url'] = 'tv://' if config.MPLAYER_ARGS.has_key('tv'): args['mode_args'] = config.MPLAYER_ARGS['tv'].split() else: logger.error('Mode "%s" is not implemented', mode) return logger.debug('mplayer args = %r', args) vo = ['%(vo)s' % args, '%(vo_opts)s' % args] vo = filter(len, vo) vo = ':'.join(vo) ao = ['%(ao)s' % args, '%(ao_opts)s' % args] ao = filter(len, ao) ao = ':'.join(ao) command = ['--prio=%(nice)s' % args] command += ['%(cmd)s' % args] command += ['-slave'] command += str('%(verbose)s' % args).split() command += str('%(geometry)s' % args).split() command += vo.split() command += str('%(vc)s' % args).split() command += ao.split() command += args['default_args'] command += args['mode_args'] command += str('%(dvd-device)s' % args).split() command += str('%(cdrom-device)s' % args).split() command += str('%(alang)s' % args).split() command += str('%(aid)s' % args).split() command += str('%(audiofile)s' % args).split() command += str('%(slang)s' % args).split() command += str('%(sid)s' % args).split() command += str('%(sub)s' % args).split() command += str('%(field-dominance)s' % args).split() command += str('%(edl)s' % args).split() command += str('%(mc)s' % args).split() command += str('%(delay)s' % args).split() if args['af']: command += ['-af', '%s' % ','.join(args['af'])] if args['vf']: command += ['-vf', '%s' % ','.join(args['vf'])] command += str('%(tv)s' % args).split() command += args['disable_osd'] and ['-osdlevel', '0'] or [] if config.OSD_SINGLE_WINDOW: command += ['-wid', str(osd.video_window.id)] osd.video_window.show() # use software scaler? if '-nosws' in command: command.remove('-nosws') elif '-framedrop' not in command: command += config.MPLAYER_SOFTWARE_SCALER.split() #if options: # command += options command = filter(len, command) #command = self.sort_filter(command) url = '%(url)s' % args command += [url] logger.debug('%r', command) self.mode = mode # XXX Mixer manipulation code. # TV is on line in # VCR is mic in # btaudio (different dsp device) will be added later mixer = plugin.getbyname('MIXER') if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer_vol = mixer.getMainVolume() mixer.setMainVolume(0) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer_vol = mixer.getPcmVolume() mixer.setPcmVolume(0) # Start up the TV task self.app = childapp.ChildApp2(command) rc.add_app(self) # Suppress annoying audio clicks time.sleep(0.4) # XXX Hm.. This is hardcoded and very unflexible. if mixer and mode == 'vcr': mixer.setMicVolume(config.MIXER_VOLUME_VCR_IN) elif mixer: mixer.setLineinVolume(config.MIXER_VOLUME_TV_IN) mixer.setIgainVolume(config.MIXER_VOLUME_TV_IN) if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer.setMainVolume(mixer_vol) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer.setPcmVolume(mixer_vol) dialog.enable_overlay_display(AppTextDisplay(self.show_message)) logger.debug('%s: started %s app', time.time(), self.mode) def Stop(self, channel_change=0): mixer = plugin.getbyname('MIXER') if mixer and not channel_change: mixer.setLineinVolume(0) mixer.setMicVolume(0) mixer.setIgainVolume(0) # Input on emu10k cards. if config.OSD_SINGLE_WINDOW: osd.video_window.hide() self.app.stop('quit\n') rc.remove_app(self) if os.path.exists('/tmp/freevo.wid'): os.unlink('/tmp/freevo.wid') if config.MPLAYER_OLDTVCHANNELCHANGE: lastchanfile = os.path.join(config.FREEVO_CACHEDIR, 'lastchan') lcfp = open(lastchanfile, "w") lastchan = self.fc.getChannel() lastchannum = self.fc.getChannelNum() lcfp.write(str(lastchan)) lcfp.write('\n') lcfp.write(str(lastchannum)) lcfp.write('\n') lcfp.close() dialog.disable_overlay_display() def eventhandler(self, event, menuw=None): s_event = '%s' % event if event == em.STOP or event == em.PLAY_END: self.Stop() rc.post_event(em.PLAY_END) return True elif event == em.PAUSE or event == em.PLAY: self.app.write('pause\n') logger.debug('%s: sending pause to mplayer', time.time()) return True elif event in [ em.TV_CHANNEL_UP, em.TV_CHANNEL_DOWN, em.TV_CHANNEL_LAST ] or s_event.startswith('INPUT_'): chan = None if event == em.TV_CHANNEL_UP: nextchan = self.fc.getNextChannel() nextchannum = self.fc.getNextChannelNum() elif event == em.TV_CHANNEL_DOWN: nextchan = self.fc.getPrevChannel() nextchannum = self.fc.getPrevChannelNum() elif event == em.TV_CHANNEL_LAST: if config.MPLAYER_OLDTVCHANNELCHANGE: if os.path.isfile( os.path.join(config.FREEVO_CACHEDIR, 'lastchan')): lastchanfile = os.path.join(config.FREEVO_CACHEDIR, 'lastchan') lcfp = open(lastchanfile, "r") nextchan = lcfp.readline() nextchan = nextchan.strip() nextchannum = lcfp.readline() nextchannum = nextchannum.strip() nextchannum = int(nextchannum) lcfp.close() else: nextchan = self.fc.getChannel() nextchannum = self.fc.getChannelNum() else: return True else: chan = int(s_event[6]) nextchan = self.fc.getManChannel(chan) nextchannum = self.fc.getManChannelNum(chan) nextvg = self.fc.getVideoGroup(nextchan, True) logger.debug('chan=%s, nextchannum=%s, nextchan=%s nextvg=%s', chan, nextchannum, nextchan, nextvg) if self.current_vg != nextvg: self.Stop(channel_change=1) self.Play('tv', nextchan) return True if self.mode == 'vcr': return elif self.current_vg.group_type == 'dvb': if not config.MPLAYER_OLDTVCHANNELCHANGE: card = 0 # May be this should come from video groups or TV_CHANNELS if em.TV_CHANNEL_UP: self.app.write('dvb_set_channel %s %s\n' % (nextchannum, card)) self.fc.chanSet(nextchan, True) elif em.TV_CHANNEL_DOWN: self.app.write('dvb_set_channel %s %s\n' % (nextchannum, card)) self.fc.chanSet(nextchan, True) else: self.Stop(channel_change=1) self.Play('tv', nextchan) return True elif self.current_vg.group_type == 'ivtv': self.fc.chanSet(nextchan, True) self.app.write('seek 999999 0\n') else: freq_khz = self.fc.chanSet(nextchan, True, app=self.app) new_freq = '%1.3f' % (freq_khz / 1000.0) self.app.write('tv_set_freq %s\n' % new_freq) self.current_vg = self.fc.getVideoGroup(self.fc.getChannel(), True) # Display a channel changed message tuner_id, chan_name, prog_info = self.fc.getChannelInfo() now = time.strftime(config.TV_TIME_FORMAT) msg = '%s %s (%s): %s' % (now, chan_name, tuner_id, prog_info) cmd = 'osd_show_text "%s"\n' % msg self.app.write(cmd) return True elif event == em.TOGGLE_OSD: if dialog.is_dialog_supported(): dialog.show_play_state(dialog.PLAY_STATE_INFO, self.fc) else: # Display the channel info message tuner_id, chan_name, prog_info = self.fc.getChannelInfo() now = time.strftime(config.TV_TIME_FORMAT) msg = '%s %s (%s): %s' % (now, chan_name, tuner_id, prog_info) cmd = 'osd_show_text "%s"\n' % msg self.app.write(cmd) return False elif event == em.OSD_MESSAGE: self.app.write('osd_show_text "%s"\n' % event.arg) return True elif event == em.TV_SEND_MPLAYER_CMD: self.app.write('%s\n' % event.arg) return True return False def show_message(self, message): self.app.write('osd_show_text "%s"\n' % message)
class TunerControl: """ Class that controls the tuner device """ def __init__(self): self.ivtv_init = False self.fc = FreevoChannels() self.curr_channel = None self.embed = None self.stack = [] def _kill_(self): if self.embed: ivtv_dev.setvbiembed(self.embed) def PushChannel(self): if self.curr_channel != None: self.stack.append(self.curr_channel) _debug_("TunerControl: Pushed channel %s" % self.curr_channel) _debug_("TunerControl: Channel stack = %s" % self.stack) def UnpushChannel(self): if len(self.stack) == 0: _debug_("TunerControl: Channel stack is empty") else: channel = self.stack.pop() _debug_("TunerControl: Unpushed channel %s" % channel) _debug_("TunerControl: Channel stack = %s" % self.stack) def PopChannel(self): if len(self.stack) == 0: _debug_("TunerControl: Channel stack is empty") else: channel = self.stack.pop() _debug_("TunerControl: Popped channel %s" % channel) self.SetVideoGroup(channel) _debug_("TunerControl: Channel stack = %s" % self.stack) def SetChannel(self, channel=None, clearstack=False): # set channel by name last_channel = self.curr_channel next_channel = None channel_index = -1 if clearstack == True: self.stack = [] self.curr_channel = None if channel == None: # get a channel next_channel = self.fc.getChannel() try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS)): entry = config.TV_CHANNELS[pos] if channel == entry[2]: channel_index = pos next_channel = channel break except ValueError: pass if (next_channel == None): _debug_( "TunerControl: Cannot find tuner channel '%s' in the TV channel listing" % channel) else: self.TuneChannelByIndex(channel_index + 1) def TuneChannelByIndex(self, channel): # tune channel by index next_channel = self.fc.getManChannel(channel) _debug_("TunerControl: Explicit channel selection = '%s'" % next_channel) self.PushChannel() self.SetVideoGroup(next_channel) def TuneChannelByNumber(self, channel): # tune channel by number self.PushChannel() self.SetVideoGroup(str(channel)) def NextChannel(self): next_channel = self.fc.getNextChannel() _debug_("TunerControl: Next channel selection = '%s'" % next_channel) self.PushChannel() self.SetVideoGroup(next_channel) def PrevChannel(self): prev_channel = self.fc.getPrevChannel() _debug_("TunerControl: Previous channel selection = '%s'" % prev_channel) self.PushChannel() self.SetVideoGroup(prev_channel) def SetVideoGroup(self, channel): _debug_("TunerControl: Play channel = '%s'" % channel) vg = self.fc.getVideoGroup(channel, True) _debug_("TunerControl: Play group = '%s'" % vg.desc) if (vg.group_type != "ivtv"): _debug_("TunerControl: Video group '%s' is not supported" % vg.group_type) pop = AlertBox(text=_( "This plugin only supports the ivtv video group in tv mode!")) pop.show() return if self.ivtv_init == False: ivtv_dev = ivtv.IVTV(vg.vdev) ivtv_dev.init_settings() ivtv_dev.setinput(vg.input_num) ivtv_dev.print_settings() # disable embedded vbi data self.embed = ivtv_dev.getvbiembed() ivtv_dev.setvbiembed(0) self.ivtv_init = True self.fc.chanSet(channel, True) self.curr_channel = channel
class TVTime: __muted = 0 __igainvol = 0 def __init__(self): self.event_context = 'tv' self.fc = FreevoChannels() self.current_vg = None self.optionD_supported = False self.xmltv_supported = False def TunerSetChannel(self, tuner_channel): for pos in range(len(config.TV_CHANNELS)): channel = config.TV_CHANNELS[pos] if channel[2] == tuner_channel: return pos print 'ERROR: Cannot find tuner channel "%s" in the TV channel listing' % tuner_channel return 0 def TunerGetChannelInfo(self): return self.fc.getChannelInfo() def TunerGetChannel(self): return self.fc.getChannel() def Play(self, mode, tuner_channel=None, channel_change=0): if not tuner_channel: tuner_channel = self.fc.getChannel() vg = self.current_vg = self.fc.getVideoGroup(tuner_channel, True) if not vg.group_type == 'normal': print 'Tvtime only supports normal. "%s" is not implemented' % vg.group_type return if mode == 'tv' or mode == 'vcr': w, h = config.TV_VIEW_SIZE cf_norm = vg.tuner_norm cf_input = vg.input_num cf_device = vg.vdev s_norm = cf_norm.upper() outputplugin = '' if plugin.getbyname(plugin.TV).optionD_supported: if config.CONF.display == 'x11': outputplugin = 'Xv' elif config.CONF.display == 'mga': outputplugin = 'mga' elif config.CONF.display in ('directfb', 'dfbmga'): outputplugin = 'directfb' else: outputplugin = config.CONF.display outputplugin = '-D %s' % outputplugin if mode == 'vcr': cf_input = '1' if hasattr(config, "TV_VCR_INPUT_NUM") and config.TV_VCR_INPUT_NUM: cf_input = config.TV_VCR_INPUT_NUM self.fc.chan_index = self.TunerSetChannel(tuner_channel) if hasattr(config, 'TV_PAD_CHAN_NUMBERS') and config.TV_PAD_CHAN_NUMBERS: mychan = tuner_channel else: mychan = self.fc.chan_index logger.debug('starting channel is %s', mychan) command = '%s %s -k -I %s -n %s -d %s -f %s -c %s -i %s' % \ (config.TVTIME_CMD, outputplugin, w, s_norm, cf_device, 'freevo', mychan, cf_input) if osd.get_fullscreen() == 1: command += ' -m' else: command += ' -M' else: print 'Mode "%s" is not implemented' % mode # BUG ui.message() return self.mode = mode mixer = plugin.getbyname('MIXER') # BUG Mixer manipulation code. # TV is on line in # VCR is mic in # btaudio (different dsp device) will be added later if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer_vol = mixer.getMainVolume() mixer.setMainVolume(0) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer_vol = mixer.getPcmVolume() mixer.setPcmVolume(0) # Start up the TV task self.app = TVTimeApp(command) rc.add_app(self) # Suppress annoying audio clicks time.sleep(0.4) # BUG Hm.. This is hardcoded and very unflexible. if mixer and mode == 'vcr': mixer.setMicVolume(config.MIXER_VOLUME_VCR_IN) elif mixer: mixer.setLineinVolume(config.MIXER_VOLUME_TV_IN) mixer.setIgainVolume(config.MIXER_VOLUME_TV_IN) if mixer and config.MIXER_MAJOR_CTRL == 'VOL': mixer.setMainVolume(mixer_vol) elif mixer and config.MIXER_MAJOR_CTRL == 'PCM': mixer.setPcmVolume(mixer_vol) logger.debug('%s: started %s app', time.time(), self.mode) def Stop(self, channel_change=0): mixer = plugin.getbyname('MIXER') if mixer and not channel_change: mixer.setLineinVolume(0) mixer.setMicVolume(0) mixer.setIgainVolume(0) # Input on emu10k cards. self.app.stop('quit\n') rc.remove_app(self) def eventhandler(self, event, menuw=None): logger.debug('%s: %s app got %s event', time.time(), self.mode, event) if event == em.STOP or event == em.PLAY_END: self.Stop() rc.post_event(em.PLAY_END) return True elif event == em.TV_CHANNEL_UP or event == em.TV_CHANNEL_DOWN: if self.mode == 'vcr': return if event == em.TV_CHANNEL_UP: nextchan = self.fc.getNextChannel() elif event == em.TV_CHANNEL_DOWN: nextchan = self.fc.getPrevChannel() nextvg = self.fc.getVideoGroup(nextchan, True) logger.debug("nextchan is %s", nextchan) # XXX hazardous to your health. don't use tvtime with anything # other than one normal video_group. # we lose track of the channel index at some points and # chaos ensues #if self.current_vg != nextvg: # self.Stop(channel_change=1) # self.Play('tv', nextchan) # return TRUE self.fc.chanSet(nextchan, True, app=self.app) #self.current_vg = self.fc.getVideoGroup(self.fc.getChannel(), True) # Go to the prev/next channel in the list if event == em.TV_CHANNEL_UP: self.app.write('CHANNEL_UP\n') else: self.app.write('CHANNEL_DOWN\n') return True elif event == em.TOGGLE_OSD: self.app.write('DISPLAY_INFO\n') return True elif event == em.OSD_MESSAGE: # XXX this doesn't work #self.app.write('display_message %s\n' % event.arg) #this does os.system('tvtime-command display_message \'%s\'' % event.arg) return True elif event == em.TV_SEND_TVTIME_CMD: os.system('tvtime-command %s' % event.arg) return True elif event in em.INPUT_ALL_NUMBERS: self.app.write('CHANNEL_%s\n' % event.arg) elif event == em.BUTTON: if event.arg == 'PREV_CH': self.app.write('CHANNEL_PREV\n') return True return False
class TunerControl: """ Class that controls the tuner device """ def __init__(self, xine): """ TunerControl constructor """ self.xine = xine self.ivtv_init = False self.fc = FreevoChannels() self.curr_channel = None self.embed = None self.stack = [] def _kill_(self): """ TunerControl destructor """ if self.embed: ivtv_dev.setvbiembed(self.embed) def Stop(self): """ stop """ self.ivtv_init = False def GetChannelName(self): """ get channel info """ tuner_id, chan_name, prog_info = self.fc.getChannelInfo(showtime=False) return (chan_name) def GetProgramName(self): """ get channel name """ tuner_id, chan_name, prog_info = self.fc.getChannelInfo(showtime=True) return prog_info def GetInfo(self): """ get channel info """ tuner_id, chan_id, chan_name, start_t, stop_t, prog_s = self.fc.getChannelInfoRaw( ) return (start_t, stop_t, prog_s) def ShowInfo(self): """ show channel info """ if self.curr_channel is not None: tuner_id, chan_name, prog_info = self.fc.getChannelInfo( showtime=False) self.xine.ShowMessage(msg='%s: %s' % (chan_name, prog_info)) def PushChannel(self): """ push the current channel on the channel stack """ if self.curr_channel is not None: self.stack.append(self.curr_channel) logger.debug('TunerControl: Pushed channel %s', self.curr_channel) logger.debug('TunerControl: Channel stack = %s', self.stack) def UnpushChannel(self): """ remove the top channel from the channel stack """ if len(self.stack) == 0: logger.debug('TunerControl: Channel stack is empty') else: channel = self.stack.pop() logger.debug('TunerControl: Unpushed channel %s', channel) logger.debug('TunerControl: Channel stack = %s', self.stack) def PopChannel(self): """ pop the top channel from the channel stack and switch channel """ if len(self.stack) == 0: logger.debug('TunerControl: Channel stack is empty') else: channel = self.stack.pop() logger.debug('TunerControl: Popped channel %s', channel) self.SetVideoGroup(channel) logger.debug('TunerControl: Channel stack = %s', self.stack) def SwapChannel(self): """swap the current display channel and the top of the stack channel """ if self.curr_channel is not None: toswap = self.curr_channel if len(self.stack) == 0: logger.debug('TunerControl: Channel stack is empty') else: channel = self.stack.pop() logger.debug('TunerControl: Popped channel %s', channel) self.SetVideoGroup(channel) self.stack.append(toswap) logger.debug('TunerControl: Pushed channel %s', toswap) logger.debug('TunerControl: Channel stack = %s', self.stack) def SetChannelByName(self, channel=None, clearstack=False): """ tune to a new channel by name """ last_channel = self.curr_channel next_channel = None channel_index = -1 if clearstack: self.stack = [] self.curr_channel = None if channel is None: # get a channel next_channel = self.fc.getChannel() try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS)): entry = config.TV_CHANNELS[pos] if str(channel) == str(entry[2]): channel_index = pos next_channel = channel break except ValueError: pass if next_channel is None: logger.warning( 'TunerControl: Cannot find tuner channel %r in the TV channel listing', channel) else: self.SetChannelByIndex(channel_index + 1) def SetChannelByIndex(self, channel, tvlike=0): """ tune to a channel by index from the TV_CHANNELS list """ next_channel = self.fc.getManChannel(channel, tvlike) logger.debug('TunerControl: Explicit channel selection by index = %r', next_channel) self.PushChannel() self.SetVideoGroup(next_channel) def SetChannelByNumber(self, channel): """ tune to a channel by actual channel number """ logger.debug('TunerControl: Explicit channel selection by number = %r', channel) self.PushChannel() self.SetVideoGroup(channel) def NextChannel(self): """ jump to the next channel in the TV_CHANNELS list """ next_channel = self.fc.getNextChannel() logger.debug('TunerControl: Next channel selection = %r', next_channel) self.PushChannel() self.SetVideoGroup(next_channel) def PrevChannel(self): """ jump to the previous channel in the TV_CHANNELS list """ prev_channel = self.fc.getPrevChannel() logger.debug('TunerControl: Previous channel selection = %r', prev_channel) self.PushChannel() self.SetVideoGroup(prev_channel) def SetVideoGroup(self, channel): """ select a channel's video group and tune to that channel """ try: channel_num = int(channel) except ValueError: channel_num = 0 logger.debug('TunerControl: Channel: %r', channel) new_vg = self.fc.getVideoGroup(channel, True) logger.debug('TunerControl: Group: type=%r, desc=%r', new_vg.group_type, new_vg.desc) logger.debug('TunerControl: Input: type=%r, num=%r', new_vg.input_type, new_vg.input_num) if new_vg.group_type != 'ivtv': logger.error('TunerControl: VideoGroup %s is not supported', new_vg) pop = AlertBox( text=_('This plugin only supports the ivtv video group!')) pop.show() return # check if videogroup switch is needed switch_vg = not self.ivtv_init or self.curr_channel is None or \ new_vg != self.fc.getVideoGroup(self.curr_channel, True) if switch_vg: # switch to a different video group logger.info('TunerControl: Set VideoGroup: %s', new_vg) ivtv_dev = ivtv.IVTV(new_vg.vdev) ivtv_dev.init_settings() self.xine.SetInput(new_vg.input_num) # disable embedded vbi data self.embed = ivtv_dev.getvbiembed() ivtv_dev.setvbiembed(0) if not self.ivtv_init: # set channel directly on v4l device, if channel is not negative if channel_num >= 0: self.fc.chanSet(channel, True) self.curr_channel = channel self.ivtv_init = True else: # set channel through xine process freq = self.fc.chanSet(channel, True, 'ivtv_xine_tv', None) if freq != 0: # channel has frequency logger.debug('TunerControl: Set frequency: %s', freq) self.xine.SetFrequency(freq) else: # channel has no frequency logger.debug('TunerControl: Channel has no frequency') self.curr_channel = channel self.xine.SeekEnd() self.ShowInfo() self.SetAudioByChannel(channel) def SetAudioByChannel(self, channel=-1): """ Set the PVR sound level This is a mix : The base volume is set by the avol option in each TV_VIDEO_GROUP. The value is hardware dependant. seems bo be between 0 and 65535. If this value is missing in the tv_video_group, that sub does nothing If the value is present, the actual audio value is this value time the 6th field in TV_CHANNELS (expressed in % ) """ try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS)): entry = config.TV_CHANNELS[pos] if str(channel) == str(entry[2]): channel_index = pos break except ValueError: pass logger.debug('SetAudioByChannel: Channel: %r TV_CHANNEL pos(%d)', channel, channel_index) vg = self.fc.getVideoGroup(channel, True) try: ivtv_avol = vg.avol except AttributeError: ivtv_avol = 0 if ivtv_avol <= 0: logger.debug( 'SetAudioByChannel: The tv_video group for %r doesn\'t set the volume', channel) else: # Is there a specific volume level in TV_CHANNELS_VOLUME ivtv_dev = ivtv.IVTV(vg.vdev) avol_percent = 100 try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS_VOLUME)): if config.TV_CHANNELS_VOLUME[pos][0] == config.TV_CHANNELS[ channel_index][0]: avol_percent = config.TV_CHANNELS_VOLUME[pos][1] break except: pass try: avol_percent = int(avol_percent) except ValueError: avol_percent = 100 avol = int(ivtv_avol * avol_percent / 100) if avol > 65535: avol = 65535 if avol < 0: avol = 0 logger.debug('SetAudioByChannel: Current PVR Sound level is : %s', ivtv_dev.getctrl(0x00980905)) logger.debug( 'SetAudioByChannel: Set the PVR Sound Level to : %s (%s * %s)', avol, ivtv_avol, avol_percent) ivtv_dev.setctrl(0x00980905, avol) logger.debug('SetAudioByChannel: New PVR Sound level is : %s', ivtv_dev.getctrl(0x00980905))
def run(self): logger.log(9, 'Record_Thread.run()') while 1: logger.debug('Record_Thread::run: mode=%s', self.mode) if self.mode == 'idle': self.mode_flag.wait() self.mode_flag.clear() elif self.mode == 'record': try: logger.info('Record_Thread::run: started recording') fc = FreevoChannels() logger.debug('Channel: %s', fc.getChannel()) vg = fc.getVideoGroup(self.prog.tunerid, False) logger.debug('Opening device %r', vg.vdev) v = tv.ivtv.IVTV(vg.vdev) v.init_settings() logger.debug('Setting input to %r', vg.input_type) v.setinputbyname(vg.input_type) cur_std = v.getstd() try: new_std = V4L2.NORMS.get(vg.tuner_norm) if cur_std != new_std: logger.debug('Setting standard to %s', new_std) v.setstd(new_std) except: logger.error( "Videogroup norm value '%s' not from NORMS: %s", vg.tuner_norm, V4L2.NORMS.keys()) logger.debug('Setting channel to %r', self.prog.tunerid) fc.chanSet(str(self.prog.tunerid), False) # Setting the input sound level on the PVR card channel = self.prog.tunerid try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS)): entry = config.TV_CHANNELS[pos] if str(channel) == str(entry[2]): channel_index = pos break except ValueError: pass logger.debug( 'SetAudioByChannel: Channel: %r TV_CHANNEL pos(%d)', channel, channel_index) try: ivtv_avol = vg.avol except AttributeError: ivtv_avol = 0 if ivtv_avol <= 0: logger.debug( 'SetAudioByChannel: The tv_video group for %r doesn\'t set the volume', channel) else: # Is there a specific volume level in TV_CHANNELS_VOLUME avol_percent = 100 try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS_VOLUME)): if config.TV_CHANNELS_VOLUME[pos][ 0] == config.TV_CHANNELS[ channel_index][0]: avol_percent = config.TV_CHANNELS_VOLUME[ pos][1] break except: pass try: avol_percent = int(avol_percent) except ValueError: avol_percent = 100 avol = int(ivtv_avol * avol_percent / 100) if avol > 65535: avol = 65535 if avol < 0: avol = 0 logger.debug( 'SetAudioByChannel: Current PVR Sound level is : %s', v.getctrl(0x00980905)) logger.debug( 'SetAudioByChannel: Set the PVR Sound Level to : %s (%s * %s)', avol, ivtv_avol, avol_percent) v.setctrl(0x00980905, avol) logger.debug( 'SetAudioByChannel: New PVR Sound level is : %s', v.getctrl(0x00980905)) if vg.cmd != None: logger.debug("Running command %r", vg.cmd) retcode = os.system(vg.cmd) logger.debug("exit code: %g", retcode) now = time.time() stop = now + self.prog.rec_duration rc.post_event(Event('RECORD_START', arg=self.prog)) time.sleep(2) v_in = open(vg.vdev, 'r') v_out = open(self.prog.filename, 'w') logger.debug('Recording from %r to %r in %s byte chunks', vg.vdev, self.prog.filename, CHUNKSIZE) while time.time() < stop: buf = v_in.read(CHUNKSIZE) v_out.write(buf) if self.mode == 'stop': logger.info('Recording stopped') break v_in.close() v_out.close() v.close() v = None self.mode = 'idle' rc.post_event(Event('RECORD_STOP', arg=self.prog)) logger.info('Record_Thread::run: finished recording') except Exception, why: logger.critical('%s', why) return else: self.mode = 'idle' time.sleep(0.5)
def run(self): logger.log( 9, 'Record_Thread.run()') while 1: logger.debug('Record_Thread::run: mode=%s', self.mode) if self.mode == 'idle': self.mode_flag.wait() self.mode_flag.clear() elif self.mode == 'record': try: logger.info('Record_Thread::run: started recording') fc = FreevoChannels() logger.debug('Channel: %s', fc.getChannel()) vg = fc.getVideoGroup(self.prog.tunerid, False) logger.debug('Opening device %r', vg.vdev) v = tv.ivtv.IVTV(vg.vdev) v.init_settings() logger.debug('Setting input to %r', vg.input_type) v.setinputbyname(vg.input_type) cur_std = v.getstd() try: new_std = V4L2.NORMS.get(vg.tuner_norm) if cur_std != new_std: logger.debug('Setting standard to %s', new_std) v.setstd(new_std) except: logger.error("Videogroup norm value '%s' not from NORMS: %s", vg.tuner_norm, V4L2.NORMS.keys()) logger.debug('Setting channel to %r', self.prog.tunerid) fc.chanSet(str(self.prog.tunerid), False) # Setting the input sound level on the PVR card channel = self.prog.tunerid try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS)): entry = config.TV_CHANNELS[pos] if str(channel) == str(entry[2]): channel_index = pos break except ValueError: pass logger.debug('SetAudioByChannel: Channel: %r TV_CHANNEL pos(%d)', channel, channel_index) try: ivtv_avol = vg.avol except AttributeError: ivtv_avol = 0 if ivtv_avol <= 0: logger.debug('SetAudioByChannel: The tv_video group for %r doesn\'t set the volume', channel) else: # Is there a specific volume level in TV_CHANNELS_VOLUME avol_percent = 100 try: # lookup the channel name in TV_CHANNELS for pos in range(len(config.TV_CHANNELS_VOLUME)): if config.TV_CHANNELS_VOLUME[pos][0] == config.TV_CHANNELS[channel_index][0]: avol_percent = config.TV_CHANNELS_VOLUME[pos][1] break except: pass try: avol_percent = int(avol_percent) except ValueError: avol_percent = 100 avol = int(ivtv_avol * avol_percent / 100) if avol > 65535: avol = 65535 if avol < 0: avol = 0 logger.debug('SetAudioByChannel: Current PVR Sound level is : %s', v.getctrl(0x00980905)) logger.debug('SetAudioByChannel: Set the PVR Sound Level to : %s (%s * %s)', avol, ivtv_avol, avol_percent) v.setctrl(0x00980905, avol) logger.debug('SetAudioByChannel: New PVR Sound level is : %s', v.getctrl(0x00980905)) if vg.cmd != None: logger.debug("Running command %r", vg.cmd) retcode = os.system(vg.cmd) logger.debug("exit code: %g", retcode) now = time.time() stop = now + self.prog.rec_duration rc.post_event(Event('RECORD_START', arg=self.prog)) time.sleep(2) v_in = open(vg.vdev, 'r') v_out = open(self.prog.filename, 'w') logger.debug('Recording from %r to %r in %s byte chunks', vg.vdev, self.prog.filename, CHUNKSIZE) while time.time() < stop: buf = v_in.read(CHUNKSIZE) v_out.write(buf) if self.mode == 'stop': logger.info('Recording stopped') break v_in.close() v_out.close() v.close() v = None self.mode = 'idle' rc.post_event(Event('RECORD_STOP', arg=self.prog)) logger.info('Record_Thread::run: finished recording') except Exception, why: logger.critical('%s', why) return else: self.mode = 'idle' time.sleep(0.5)