Author: duncan
Date: Sun Jan 20 07:57:51 2008
New Revision: 10300
Log:
[ 1875427 ] vlc live pause plugin
New plug-in from Jonathan Isom added
Added:
branches/rel-1/freevo/src/tv/plugins/dvbstreamer/vlc_live_pause.py
(contents, props changed)
Modified:
branches/rel-1/freevo/ChangeLog
branches/rel-1/freevo/src/event.py
Modified: branches/rel-1/freevo/ChangeLog
==============================================================================
--- branches/rel-1/freevo/ChangeLog (original)
+++ branches/rel-1/freevo/ChangeLog Sun Jan 20 07:57:51 2008
@@ -16,6 +16,8 @@
== Release 1.8.0rc2 (2008-01-24) ==
--------------------------------
+ * New tv vlc live pause plug-in for DVB (F#1875427)
+
== Release 1.7.6 (2008-??-??) ==
--------------------------------
Modified: branches/rel-1/freevo/src/event.py
==============================================================================
--- branches/rel-1/freevo/src/event.py (original)
+++ branches/rel-1/freevo/src/event.py Sun Jan 20 07:57:51 2008
@@ -154,6 +154,9 @@
TV_CHANNEL_DOWN = Event('TV_CHANNEL_DOWN')
TV_CHANNEL_LAST = Event('TV_CHANNEL_LAST')
TV_SEND_TVTIME_CMD = Event('TV_SEND_TVTIME_CMD')
+TV_GOTO_LIVE_PLAY = Event('TV_GOTO_LIVE_PLAY')
+VIDEO_NEXT_FILLMODE = Event('VIDEO_NEXT_FILLMODE')
+VIDEO_NEXT_AUDIOMODE = Event('VIDEO_NEXT_AUDIOMODE')
#
# Global playing events
Added: branches/rel-1/freevo/src/tv/plugins/dvbstreamer/vlc_live_pause.py
==============================================================================
--- (empty file)
+++ branches/rel-1/freevo/src/tv/plugins/dvbstreamer/vlc_live_pause.py Sun Jan
20 07:57:51 2008
@@ -0,0 +1,1009 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------
+# Freevo DVBStreamer module for tv
+# -----------------------------------------------------------------------
+# $Id$
+#
+# Notes:
+#
+# This only works with vlc, try live_pause for xine
+# Only supports DVBStreamer instance on localhost.
+#
+# Todo:
+#
+#
+# -----------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2002 Krister Lagerstrom, et al.
+# Please see the file freevo/Docs/CREDITS for a complete list of authors.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
+# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# ----------------------------------------------------------------------- */
+
+
+import time
+import os
+import re
+import copy
+import sys
+import traceback
+import math
+
+from threading import *
+from socket import *
+
+import config # Configuration handler. reads config file.
+import rc # The RemoteControl class.
+import util
+import osd
+import childapp
+import kaa.notifier
+
+from tv.channels import FreevoChannels
+
+from event import *
+import plugin
+
+from tv.plugins.dvbstreamer.manager import DVBStreamerManager
+from tv.plugins.dvbstreamer import ring_buffer
+
+WAIT_FOR_DATA_COUNT = 3
+WAIT_FOR_DATA_TIMEOUT = 20 # Seconds
+
+EVENT_DATA_STARTED = Event('Data Started')
+EVENT_DATA_ACQUIRED = Event('Data Acquired')
+EVENT_DATA_TIMEDOUT = Event('Data Timed Out')
+EVENT_READER_OVERTAKEN = Event('Reader Overtaken')
+
+STATE_IDLE = 'Idle'
+STATE_TUNING = 'Tuning'
+STATE_BUFFERING = 'Buffering'
+STATE_PLAYING = 'Playing'
+
+class PluginInterface(plugin.DaemonPlugin):
+ """
+ DVBStreamer plugin use the dvbstreamer application to minimise channel
change time.
+ """
+
+ def __init__(self):
+ plugin.DaemonPlugin.__init__(self)
+ self.event_listener = True
+ _debug_('dvbstreamer plugin starting')
+
+ try:
+ config.VLC_COMMAND
+ except:
+ print String(_( 'ERROR' )) + ': ' + \
+ String(_("'VLC_COMMAND' not defined, plugin 'DVBStreamer'
deactivated.\n" \
+ 'please check the vlc section in freevo_config.py'
))
+ return
+
+ # Create DVBStreamer objects
+ username = 'dvbstreamer'
+ password = 'control'
+
+ try:
+ username = config.DVBSTREAMER_USERNAME
+ password = config.DVBSTREAMER_PASSWORD
+ except:
+ pass
+
+ manager = DVBStreamerManager(username, password)
+
+ # Determine size and location of the live buffer
+ bitrate, duration = config.LIVE_PAUSE_BUFFER_SIZE
+ size = int((((bitrate * 1000000 * duration) / (7* 188 * 8)) + 1) * (7
* 188))
+ path = config.LIVE_PAUSE_BUFFER_PATH
+
+ # register vlc as the object to play
+ self.vlc = Vlc( manager, path, size)
+ plugin.register(self.vlc, plugin.TV, False)
+
+ def shutdown(self):
+ """
+ Shutdown callback.
+ """
+ self.vlc.shutdown()
+
+
+ def eventhandler(self, event=None, menuw=None):
+ if event == PLAY_START and self.vlc.state == STATE_IDLE:
+ print 'Disabling buffering!'
+ self.vlc.disable_buffering()
+ return False
+
+
+ def config(self):
+ """
+ Gets the required configuration variables
+ """
+ return [
+ ('DVBSTREAMER_USERNAME', 'dvbstreamer', 'Username to use when
connecting to a DVBStreamer server'),
+ ('DVBSTREAMER_PASSWORD', 'control', 'Password to use when
connecting to a DVBStreamer server'),
+ ('LIVE_PAUSE_BUFFER_PATH','/tmp/freevo/live.buf', 'Location of the
file to use for pausing live TV'),
+ ('LIVE_PAUSE_BUFFER_SIZE', (6.25, 30*60), 'Size of the live buffer
as a tuple of max Mbps of the TV and seconds'),
+ ('LIVE_PAUSE_BUFFER_TIMEOUT', 5*60, 'Timeout to disable buffering
after exiting watching tv'),
+ ]
+
+###############################################################################
+# Vlc Control Class
+###############################################################################
+
+
+
+
+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.notifier.Timer(self.__update_data_from_vlc)
+ self.timer_start = kaa.notifier.OneShotTimer(
self.__update_data_from_vlc)
+ self.timer_disablebuffering = kaa.notifier.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
+
+###############################################################################
+# Live Buffer Filler/Emptier Classes
+###############################################################################
+
+class UDPReceiver:
+ """
+ Class to receive UDP packets sent by DVBStreamer and write them into
+ a ring buffer.
+ """
+
+ def __init__(self, buffer, timeout_count):
+ """
+ Create a UDP Receiver to write into the ring buffer specified in
buffer.
+ """
+ # Create socket and bind to address
+ self.socket = socket(AF_INET,SOCK_DGRAM)
+ self.socket.bind(('', 1234))
+ self.socket.settimeout(1.0)
+ self.buffer = buffer
+ self.quit = False
+ self.thread = None
+ self.send_events = False
+ self.data_timeout_count = timeout_count
+ self.pause = False
+ self.average_pps = 0 # Average packets per second
+
+
+ def start(self):
+ """
+ Start a thread to receive UDP packets.
+ """
+ if self.thread is None:
+ self.quit = False
+
+ self.thread = Thread(target=self.run, name='UDP Receiver')
+ self.thread.setDaemon(True)
+ self.thread.start()
+
+
+ def stop(self):
+ """
+ Stop the thread receiving UDP packets.
+ """
+ if self.thread is not None:
+ self.quit = True
+ self.socket.close()
+
+ def reset(self):
+ self.average_pps = 0
+ self.data_received = False
+
+ def run(self):
+ """
+ Thread method to receive UDP packets.
+ """
+ buffer_size = 188*7
+ ringbuffer = self.buffer
+ last_time = 0
+ self.data_received = False
+ byte_count = 0
+ timeout_count = 0
+
+ while not self.quit:
+ try:
+ data,addr = self.socket.recvfrom(buffer_size)
+ if data and not self.pause:
+ timeout_count = 0
+
+
+ if not self.data_received:
+ # We've not had any data yet on the current channel so
+ # clear some state and send an event to say the data
+ # has started.
+
+ self.data_received = True
+ last_time = time.time()
+ byte_count = 0
+
+ if self.send_events:
+ _debug_('Sending data started')
+ rc.post_event(EVENT_DATA_STARTED)
+
+ ringbuffer.write(data)
+
+ # Keep track of how much data we've received to determine
+ # the average number of packets per second.
+ byte_count += len(data)
+
+ now = time.time()
+ time_diff = now - last_time
+ if time_diff >= 1.0:
+ last_time = now
+ if self.average_pps == 0:
+ self.average_pps = int(byte_count / 188)
+ else:
+ self.average_pps = int((((self.average_pps * 188)
+ byte_count) / 2) / 188)
+ byte_count = 0
+
+ if self.send_events:
+ rc.post_event(EVENT_DATA_ACQUIRED)
+
+ except timeout:
+ if not self.pause:
+ timeout_count += 1
+ if (timeout_count > self.data_timeout_count) and
self.send_events:
+ rc.post_event(EVENT_DATA_TIMEDOUT)
+ except:
+ traceback.print_exc()
+ self.thread = None
+
+
+class SlaveServer:
+ """
+ Class to serve data to vlc http://
+ """
+
+ def __init__(self, buffer, controller):
+ """
+ Initialise a server to provide data to vlc.
+ """
+ self.socket = socket(AF_INET, SOCK_STREAM)
+ self.socket.bind(('', 50007))
+ self.socket.listen(1)
+ self.buffer = buffer
+ self.controller = controller
+ self.quit = False
+ self.thread = None
+ self.connection = None
+ self.reader_at_end = False
+ self.end_offset = 0
+
+
+ def get_vlc_mrl(self):
+ # using http:// rather than tcp://
+ # Playback is smoother
+ return 'http://@127.0.0.1:50007'
+
+
+ def start(self, reader_at_end = False):
+ """
+ Start a server to serve VLC http://
+ """
+ if self.thread is None:
+ self.quit = False
+ self.reader_at_end = reader_at_end
+ self.thread = Thread(target=self.run, name='Slave Server')
+ self.thread.setDaemon(True)
+ self.thread.start()
+
+
+ def stop(self):
+ """
+ Stop the server.
+ """
+ if self.thread is not None:
+ self.quit = True
+ self.socket.close()
+ if self.connection:
+ self.connection.close()
+
+ def get_percent(self):
+ """
+ Return the percentage the slave server has read through the buffer.
+ """
+ rb_len = len(self.buffer)
+ pos = self.reader.available_backward()
+ return int(math.ceil(float(pos) / float(rb_len) * 100.0))
+
+
+ def run(self):
+ """
+ Thread method to listen for connection from vlc http:// and serve data
from the ring buffer.
+ """
+ reader = ring_buffer.Reader(self.buffer)
+ reader.overtaken = self.overtaken
+ self.reader = reader
+
+ if self.reader_at_end:
+ reader.pos = self.buffer.end
+ reader.seek( - self.end_offset)
+
+ while not self.quit:
+ conn, addr = self.socket.accept()
+ self.connection = conn
+
+ conn.send('HTTP/1.0 200 OK\n')
+ conn.send('Content-type: application/octet-stream\n')
+ conn.send('Cache-Control: no-cache\n\n')
+
+ if self.reader_at_end:
+ reader.pos = self.buffer.end
+ reader.seek( - self.end_offset)
+ self.reader_at_end = False
+
+ try:
+ last_time = time.time()
+ count = 0
+ starved_count = 0
+ while not self.quit:
+ if count == 0:
+ _debug_('In data loop')
+ data = reader.read(188 * 7)
+
+ if not data:
+ _debug_('Starved!!! count=%d starved_count=%d' %
(count,starved_count))
+ starved_count += 1
+ if starved_count >= 5:
+ self.controller.pause()
+ time.sleep(3.0)
+ self.controller.resume()
+ else:
+ time.sleep(0.25)
+ else:
+ starved_count = 0
+ now = time.time()
+ if last_time - now > 1.0:
+ last_time = now
+ _debug_('Sent %d bytes' % count)
+ count += len(data)
+ conn.send(data)
+
+ except error:
+ pass
+ except :
+ traceback.print_exc()
+ try:
+ conn.close()
+ except:
+ pass
+ _debug_('Slave server exited')
+ reader.close()
+ self.thread = None
+
+
+ def overtaken(self, reader):
+ # Todo: would be nice to warn the user somehow that they've missed
some of the program.
+ # print 'Reader was overtaken new position = %d' % reader.pos
+ rc.post_event(EVENT_READER_OVERTAKEN)
+
+
+class VlcApp(childapp.ChildApp2):
+ def __init__ (self, command, vlc):
+ self.vlc = vlc
+ self.RE_EXIT = re.compile("status change: \( quit \)")
+ self.RE_PARSE_HEAD_AUDIO = re.compile("^\+----\[ Audio Track \]")
+ self.RE_PARSE_HEAD_SUB = re.compile("^\+----\[ Subtitles Track \]")
+ self.RE_PARSE_HEAD_ACHAN = re.compile("^\+----\[ Audio Device \]")
+ self.RE_PARSE_FOOT = re.compile("^\+----\[ end of")
+ self.RE_PARSE_DATA = re.compile("^\|")
+ self.exit_type = None
+ self.audio_tracks = []
+ self.sub_tracks = []
+ self.audio_chans = []
+ self.stat= {
+ 'none' : '0',
+ 'audio' : '1',
+ 'subs' : '2',
+ 'achan' : '3',
+ }
+ self.parsecur = None
+ childapp.ChildApp2.__init__(self, command)
+
+
+
+ def stdout_cb(self, line):
+ '''Override this method to receive stdout from the child app
+ The function receives complete lines
+ '''
+ if self.RE_EXIT.search(line):
+ self.exit_type = 'Quit'
+
+ elif self.RE_PARSE_HEAD_AUDIO.search(line):
+ self.parsecur = self.stat['audio']
+ self.audio_tracks = []
+
+ elif self.RE_PARSE_HEAD_SUB.search(line):
+ self.parsecur = self.stat['subs']
+ self.sub_tracks = []
+
+ elif self.RE_PARSE_HEAD_ACHAN.search(line):
+ self.parsecur = self.stat['achan']
+ self.audio_chans = []
+
+ elif self.RE_PARSE_FOOT.search(line):
+ self.parsecur = self.stat['none']
+
+ elif self.RE_PARSE_DATA.search(line):
+ if self.parsecur == self.stat['audio']:
+ self.audio_tracks.append( line.split(" ")[1] )
+ self.audio_tracks = util.misc.unique( self.audio_tracks )
+
+ elif self.parsecur == self.stat['achan']:
+ self.audio_chans.append( line.split(" ")[1])
+ self.audio_chans = util.misc.unique( self.audio_chans )
+
+ elif self.parsecur == self.stat['subs']:
+ self.sub_tracks.append(line.split(" ")[1])
+ self.sub_tracks = util.misc.unique(self.sub_tracks)
+
+
+
+
+ def stop_event(self):
+ """
+ return the stop event send through the eventhandler
+ """
+ if self.exit_type == 'Quit':
+ return PLAY_END
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog