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

Reply via email to