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)
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)
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 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 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 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 Xine: """ the main class to control xine """ def __init__(self, type): self.name = 'xine' self.event_context = 'tv' self.xine_type = type self.app = None self.fc = FreevoChannels() 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(' ') def ShowMessage(self, msg): """ Show a message on the OSD """ logger.debug("XINE: Show OSD Message: '%s'", msg) self.app.write("OSDWriteText$ %s\n" % msg) 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() command = copy.copy(self.command) if config.XINE_HAS_NO_LIRC: command.append('--no-lirc') if config.OSD_SINGLE_WINDOW: command += ['-W', str(osd.video_window.id), '--no-mouse'] osd.video_window.show() command.append('dvb://' + tuner_channel) logger.debug('Starting cmd=%s', command) rc.add_app(self) self.app = childapp.ChildApp2(command) dialog.enable_overlay_display(AppTextDisplay(self.ShowMessage)) return None def stop(self, channel_change=0): """ Stop xine """ if self.app: if config.OSD_SINGLE_WINDOW: osd.video_window.hide() self.app.stop('quit\n') rc.remove_app(self) dialog.disable_overlay_display() if not channel_change: pass 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 """ if event in ( PLAY_END, USER_END, STOP ): self.stop() rc.post_event(PLAY_END) return True if event == PAUSE or event == PLAY: self.app.write('pause\n') return True elif event in [ TV_CHANNEL_UP, TV_CHANNEL_DOWN] or str(event).startswith('INPUT_'): 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(channel_change=1) self.fc.chanSet(nextchan, True) self.Play('tv', nextchan) return True if event == TOGGLE_OSD: self.app.write('PartMenu\n') return True if event == VIDEO_TOGGLE_INTERLACE: self.app.write('ToggleInterleave\n') return True if event == OSD_MESSAGE: self.ShowMessage(event.arg) return True # nothing found return False
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 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 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 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
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 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 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 LivePauseController: """ The main class to control play back. """ def __init__(self, player): self.name = 'livepause' self.event_context = 'tv' self.fc = FreevoChannels() self.backend = backend.get_backend() self.backend.set_mode(player.mode) self.last_channel = None self.stop_time = 0 self.data_started_timer = kaa.OneShotTimer(self.__buffering_data_timedout) self.disable_buffering_timer = kaa.OneShotTimer(self.__disable_buffering_timeout) self.state = State.IDLE self.player = player self.changing_channel = False self.subtitles = None self.subtitle_index = -1 self.audio_langs = None self.audio_lang_index = -1 self.recording = False self.state_dialog = None # Setup Event Maps self.event_maps = {} self.event_maps[State.IDLE] = { 'READER_OVERTAKEN' : self.__idle_reader_overtaken, } self.event_maps[State.TUNING] = { 'DATA_STARTED' : self.__tuning_data_started, 'DATA_TIMEDOUT': self.__tuning_data_timedout, 'PLAY_END' : self.__handle_stop, 'USER_END' : self.__handle_stop, 'STOP' : self.__handle_stop } self.event_maps[State.BUFFERING] = { 'DATA_ACQUIRED': self.__buffering_data_acquired, 'DATA_TIMEDOUT': self.__buffering_data_timedout, 'PLAY_END' : self.__handle_stop, 'USER_END' : self.__handle_stop, 'STOP' : self.__handle_stop } self.event_maps[State.PLAYING] = { 'PLAY' : self.__playing_play_pause, 'PAUSE' : self.__playing_play_pause, 'PLAY_END' : self.__handle_stop, 'USER_END' : self.__handle_stop, 'STOP' : self.__handle_stop, 'TV_CHANNEL_UP' : self.__playing_tv_channel_up, 'TV_CHANNEL_DOWN' : self.__playing_tv_channel_down, 'TV_CHANNEL_NUMBER' : self.__playing_tv_channel_number, 'TV_START_RECORDING' : self.__playing_tv_record, 'SAVE_STARTED' : self.__playing_tv_record_start, 'SAVE_FINISHED' : self.__playing_tv_record_stop, 'BUTTON' : self.__playing_button_pressed, 'TOGGLE_OSD' : self.__playing_display_info, 'SEEK' : self.__playing_seek, 'SECONDS_LEFT' : self.__playing_seconds_left, 'READER_OVERTAKEN' : self.__playing_reader_overtaken, 'DATA_ACQUIRED' : None, 'DATA_TIMEDOUT' : None, 'VIDEO_NEXT_FILLMODE' : None, 'VIDEO_NEXT_AUDIOMODE': None, 'VIDEO_NEXT_AUDIOLANG': None, #self.__playing_toggle_audo_lang, 'VIDEO_NEXT_SUBTITLE' : self.__playing_toggle_subtitles, } self.current_event_map = self.event_maps[self.state] def Play(self, mode, tuner_channel=None): """ Start play back. """ if not tuner_channel: tuner_channel = self.fc.getChannel() if plugin.getbyname('MIXER'): plugin.getbyname('MIXER').reset() self.disable_buffering_timer.stop() rc.add_app(self) # 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 self.last_channel == tuner_channel: now = time.time() seconds_since_played = now - self.stop_time logger.debug('Same channel, seconds since last playing this channel %d', seconds_since_played) self.backend.set_events_enabled(True) if seconds_since_played > 120.0: # Start at the end of the buffer buffer_info = self.backend.get_buffer_info() self.backend.seekto(buffer_info[2] - 3) self.__change_state(State.PLAYING) else: logger.debug('New channel, tuning to %s', tuner_channel) self.backend.set_events_enabled(True) self.change_channel(tuner_channel) return None def stop(self): """ Stop playback and go into idle. """ logger.debug('Stopping play back.') display.get_osd().hide() dialog.disable_overlay_display() self.player.stop() self.stop_time = time.time() self.backend.set_events_enabled(False) self.__change_state(State.IDLE) self.disable_buffering_timer.start(config.LIVE_PAUSE2_BUFFER_TIMEOUT) return True def disable_buffering(self): """ Stop buffering the current channel. """ self.stop_time = 0 self.last_channel = None self.disable_buffering_timer.stop() self.backend.disable_buffering() logger.debug('Buffering disabled.') def shutdown(self): """ Stop buffering and the slave server. """ self.disable_buffering() 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 self.fc.chanSet(channel, True) self.last_channel = channel self.backend.change_channel(channel) self.__change_state(State.TUNING) ########################################################################### # Event Handlers ########################################################################### def eventhandler(self, event, menuw=None): """ Eventhandler for livepause control. If an event is not bound in this function it will be passed over to the items eventhandler """ logger.log( 9, 'Event %s', event) event_consumed = False if self.state == State.PLAYING: event_consumed = tv.dialogs.handle_channel_number_input(event) if not event_consumed and event.name in self.current_event_map: handler = self.current_event_map[event.name] if handler: event_consumed = handler(event, menuw) else: # Event was in map but no handler so just consume the event. event_consumed = True if not event_consumed: logger.debug('Unused event %s in state %s', event.name, self.state) return event_consumed def __handle_stop(self, event, menuw): if self.changing_channel: self.changing_channel = False else: self.stop() return True def __idle_reader_overtaken(self, event, menuw): # Seek ten seconds forward from the end of the buffer. self.backend.seek(10, now=True) return True def __tuning_data_started(self, event, menuw): self.__change_state(State.BUFFERING) return True def __tuning_data_timedout(self, event, menuw): # Timeout while waiting for data! # We could display a searching for signal graphic or something here self.__change_state(State.IDLE) return True def __buffering_data_acquired(self, event, menuw): self.wait_for_data_count -= 1 self.__draw_state_screen() if self.wait_for_data_count <= 0: self.__change_state(State.PLAYING) return True def __buffering_data_timedout(self, event, menuw): # Timeout while waiting for data! # We could display a searching for signal graphic or something here self.__change_state(State.IDLE) return True def __playing_play_pause(self, event, menuw): if self.player.paused: self.player.resume() else: self.player.pause() self.osd.display_buffer_pos(self.__get_display_info) return True def __playing_tv_channel_up(self, event, menuw): next_channel = self.fc.getNextChannel() self.changing_channel = True self.player.stop() self.change_channel(next_channel) return True def __playing_tv_channel_down(self, event, menuw): next_channel = self.fc.getPrevChannel() self.changing_channel = True self.player.stop() self.change_channel(next_channel) return True def __playing_tv_channel_number(self, event, menuw): next_channel = self.fc.getManChannel(event.arg) if self.last_channel != next_channel: self.changing_channel = True self.player.stop() self.change_channel(next_channel) return True def __playing_tv_record(self, event, menuw): if self.recording: self.backend.cancelsave() self.recording = False else: self.recording = True record.start_recording(self.backend, self.last_channel) def __playing_tv_record_start(self, event, menuw): dialog.show_message(_('Recording started')) def __playing_tv_record_stop(self, event, menuw): if self.state == State.PLAYING: dialog.show_message(_('Recording stopped')) self.recording = False def __playing_seconds_left(self, event, menuw): if self.player.paused: self.player.resume() dialog.show_message(_('Out of buffer space, playback resumed')) else: logger.debug('while playing ~%d seconds left in buffer before overwrite', event.arg) return True def __playing_reader_overtaken(self, event, menuw): if self.player.paused: self.player.resume() dialog.show_message(_('Out of buffer space, playback resumed')) else: dialog.show_message(_('Out of buffer space')) logger.debug('Out of buffer space while playing!') return True def __playing_button_pressed(self, event, menuw): consumed = False logger.debug('Button %s', event.arg) if event.arg == 'SUBTITLE': self.__playing_toggle_subtitles(event, menuw) consumed = True return consumed def __playing_toggle_subtitles(self, event, menuw): # Enable/Disable subtitles if self.subtitles: self.subtitle_index += 1 else: self.subtitles = self.player.get_subtitles() self.subtitle_index = 0 if self.subtitles: if self.subtitle_index >= len(self.subtitles): self.subtitle_index = -1 self.player.set_subtitles(self.subtitle_index) if self.subtitle_index == -1: subtitle_text = _('Disabled') else: subtitle_text = self.subtitles[self.subtitle_index] dialog.show_message(_('Subtitles: %s') % subtitle_text) else: dialog.show_message(_('Subtitles not supported')) return True def __playing_toggle_audio_lang(self, event, menuw): if self.audio_langs: self.audio_lang_index += 1 else: self.audio_langs = self.player.get_audio_langs() self.audio_lang_index = 0 if self.audio_langs: if self.audio_lang_index >= len(self.audio_langs): self.audio_lang_index = -1 self.player.set_audio_lang(self.audio_lang_index) if self.audio_lang_index == -1: audio_lang_text = _('Default') else: audio_lang_text = self.subtitles[self.subtitle_index] dialog.show_message(_('Audio language: %s') % audio_lang_text) else: dialog.show_message(_('Audio language selection not supported')) return True def __playing_display_info(self, event, menuw): self.osd.display_info(self.__get_display_info) return True def __get_display_info(self): info_dict = {} info_dict['channel'] = self.__get_display_channel() buffer_info = self.backend.get_buffer_info() info_dict['current_time'] = buffer_info[3] info_dict['start_time'] = buffer_info[1] info_dict['end_time'] = buffer_info[2] info_dict['percent_through_buffer'] = float(buffer_info[3] - buffer_info[1]) / float(buffer_info[2] - buffer_info[1]) info_dict['percent_buffer_full'] = buffer_info[0] info_dict['paused'] = self.player.paused info_dict['recording'] = self.recording return info_dict def __playing_seek(self, event, menuw): steps = int(event.arg) buffer_info = self.backend.get_buffer_info() if steps > 0: can_seek = buffer_info[2] != buffer_info[3] steps = min(buffer_info[2] - buffer_info[3], steps) else: steps = max(buffer_info[1] - buffer_info[3], steps) can_seek = buffer_info[1] != buffer_info[3] if can_seek: self.backend.seek(steps) self.player.restart() time.sleep(0.2) self.osd.display_buffer_pos(self.__get_display_info) return True ########################################################################### # State Management ########################################################################### 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 logger.debug('Changing state from %s to %s', self.state, new_state) old_state = self.state self.state = new_state self.current_event_map = self.event_maps[new_state] # State Initialisation code if self.state == State.IDLE: self.state_dialog.hide() rc.remove_app(self) rc.post_event(PLAY_END) self.backend.set_events_enabled(False) elif self.state == State.TUNING: # Display the current state on the OSD self.__draw_state_screen() elif self.state == State.BUFFERING: self.wait_for_data_count = WAIT_FOR_DATA_COUNT # Display the current state on the OSD self.__draw_state_screen() elif self.state == State.PLAYING: # Display the current state on the OSD self.__draw_state_screen() self.player.start((self.backend.get_server_address(), config.LIVE_PAUSE2_PORT)) dialog.enable_overlay_display(self.player.get_display()) self.osd = display.get_osd() def __draw_state_screen(self): if self.state_dialog is None: self.state_dialog = StateDialog() percent = 0.0 if self.state == State.BUFFERING: percent = float(WAIT_FOR_DATA_COUNT - self.wait_for_data_count) / float(WAIT_FOR_DATA_COUNT) elif self.state == State.PLAYING: percent = 1.0 channel = self.__get_display_channel() self.state_dialog.set_state(self.state, percent, channel) self.state_dialog.show() def __get_display_channel(self): channel = self.last_channel for entry in config.TV_CHANNELS: if self.last_channel == entry[2]: channel = entry[1] return channel def __fire_channel_number(self): rc.post_event(TV_CHANNEL_NUMBER) self.channel_number_timer = None def __disable_buffering_timeout(self): self.disable_buffering()
class Xine: """ the main class to control xine """ def __init__(self, type): self.name = 'xine' self.app_mode = 'tv' self.xine_type = type self.app = None self.fc = FreevoChannels() 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(' ') def ShowMessage(self, msg): """ Show a message on the OSD """ _debug_("XINE: Show OSD Message: '%s'" % msg) self.app.write("OSDWriteText$ %s\n" % msg) 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() command = copy.copy(self.command) if not config.XINE_HAS_NO_LIRC and '--no-lirc' in command: command.remove('--no-lirc') command.append('dvb://' + tuner_channel) _debug_('Xine.play(): Starting cmd=%s' % command) rc.app(self) self.app = childapp.ChildApp2(command) return None def stop(self, channel_change=0): """ Stop xine """ if self.app: self.app.stop('quit\n') rc.app(None) if not channel_change: pass 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 """ if event in ( PLAY_END, USER_END, STOP ): self.stop() rc.post_event(PLAY_END) return True if event == PAUSE or event == PLAY: self.app.write('pause\n') return True elif event in [ TV_CHANNEL_UP, TV_CHANNEL_DOWN] or str(event).startswith('INPUT_'): 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(channel_change=1) self.fc.chanSet(nextchan, True) self.Play('tv', nextchan) return True if event == TOGGLE_OSD: self.app.write('PartMenu\n') return True if event == VIDEO_TOGGLE_INTERLACE: self.app.write('ToggleInterleave\n') return True if event == OSD_MESSAGE: self.ShowMessage(event.arg) return True # nothing found return False
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)