GStreamerPlayer.py :  » RSS » PenguinTV » PenguinTV-4.1.0 » penguintv » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » RSS » PenguinTV 
PenguinTV » PenguinTV 4.1.0 » penguintv » GStreamerPlayer.py
#!/usr/bin/env python
#a basic gstreamer-based player.  Can run standalone or inside the widget of your choice
#much help from the mesk player code, totem, and most of all, google.com/codesearch
#Copyright 2006, Owen Williams
#License: GPL

import sys,os,os.path
import pickle
import urllib
import getopt
from math import ceil,floor

import pygst
pygst.require("0.10")
import gst
from gst.extend.discoverer import Discoverer

import pygtk
pygtk.require("2.0")
import gtk 
import gobject

import locale
import gettext
import logging
logging.basicConfig(level=logging.DEBUG)

#locale.setlocale(locale.LC_ALL, '')
_=gettext.gettext

if os.environ.has_key('SUGAR_PENGUINTV'):
  RUNNING_SUGAR = True
else:
  RUNNING_SUGAR = False
  
try:
  import hildon
  RUNNING_HILDON = True
except:
  RUNNING_HILDON = False

class GStreamerPlayer(gobject.GObject):

  __gsignals__ = {
    'playing': (gobject.SIGNAL_RUN_FIRST, 
                           gobject.TYPE_NONE, 
                           ([])),
    'paused': (gobject.SIGNAL_RUN_FIRST, 
                           gobject.TYPE_NONE, 
                           ([])),
    'tick': (gobject.SIGNAL_RUN_FIRST, 
                           gobject.TYPE_NONE, 
                           ([])),
        'item-queued': (gobject.SIGNAL_RUN_LAST, 
                gobject.TYPE_NONE, 
                [str, str, gobject.TYPE_PYOBJECT]),
        'item-not-supported': (gobject.SIGNAL_RUN_LAST, 
                     gobject.TYPE_NONE, 
                     [str, str, gobject.TYPE_PYOBJECT]),
        'items-removed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [])
  }

  def __init__(self, layout_dock, playlist, tick_interval=1):
    gobject.GObject.__init__(self)
    self._layout_dock = layout_dock
    self._playlist_name = playlist
    self._media_duration = 0
    self._media_position = 0
    self._last_tick = 0
    self._tick_interval = tick_interval * gst.SECOND
    self._last_index = -1
    self._current_index = 0 #index to tree model
    self._resized_pane = False
    self.__no_seek = False
    self.__is_exposed = False
    self._x_overlay = None
    self._prepare_save = False
    self._do_stop_resume = False
    self._has_video = False
    #self._using_playbin2 = True
    #TODO: wait until playbin2 isn't buggy, then reenable
    self._using_playbin2 = False
    
    self._error_dialog = GStreamerErrorDialog()
    
    gobject.timeout_add(300000, self._periodic_save_cb)
    
  ###public functions###
    
  def Show(self):
    main_vbox = gtk.VBox()
    
    vbox = gtk.VBox()
    self._hpaned = gtk.HPaned()
    self._player_vbox = gtk.VBox()
    self._drawing_area = gtk.DrawingArea()
    
    color = gtk.gdk.Color(0, 0, 0)
    self._drawing_area.modify_bg(gtk.STATE_NORMAL, color)
    self._drawing_area.connect('expose-event', self._on_drawing_area_exposed)
    self._player_vbox.pack_start(self._drawing_area)
    vbox.pack_start(self._player_vbox, True)
    
    self._seek_scale = gtk.HScale()
    self._seek_scale.set_range(0, 1)
    self._seek_scale.set_draw_value(False)
    self._seek_scale.connect('value-changed', self._on_seek_value_changed)
    vbox.pack_start(self._seek_scale, False)
    
    self._hpaned.add1(vbox)
    
    self._sidepane_vbox = gtk.VBox()
    s_w = gtk.ScrolledWindow()
    s_w.set_shadow_type(gtk.SHADOW_IN)
    s_w.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    if RUNNING_HILDON:
      hildon.hildon_helper_set_thumb_scrollbar(s_w, True)
    self._queue_listview = gtk.TreeView()
    model = gtk.ListStore(str, str, str, gobject.TYPE_PYOBJECT) #uri, title to display, current track indicator, user data
    self._queue_listview.set_model(model)
    
    column = gtk.TreeViewColumn(_(""))
    renderer = gtk.CellRendererText()
    column.pack_start(renderer, True)
    column.set_attributes(renderer, markup=2)
    self._queue_listview.append_column(column)
    
    column = gtk.TreeViewColumn(_("Playlist"))
    renderer = gtk.CellRendererText()
    column.pack_start(renderer, True)
    column.set_attributes(renderer, markup=1)
    self._queue_listview.append_column(column)
    self._queue_listview.connect('row-activated', self._on_queue_row_activated)
    self._queue_listview.connect('button-press-event', self._on_queue_row_button_press)
    self._queue_listview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
    #dnd reorder
    self._TARGET_TYPE_REORDER = 80
    self._TARGET_TYPE_URI_LIST = 81
    drop_types = [('reorder',gtk.TARGET_SAME_WIDGET, self._TARGET_TYPE_REORDER),
            ('text/uri-list',0,self._TARGET_TYPE_URI_LIST)]
    #for removing items from favorites and reordering
    self._queue_listview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, drop_types, gtk.gdk.ACTION_MOVE)
    self._queue_listview.enable_model_drag_dest(drop_types, gtk.gdk.ACTION_DEFAULT)
    self._queue_listview.connect('drag-data-received', self._on_queue_drag_data_received)
    
    s_w.add(self._queue_listview)
    self._sidepane_vbox.pack_start(s_w, True)
    
    button = gtk.Button(stock='gtk-remove')
    button.connect("clicked", self._on_remove_clicked)
    if not RUNNING_HILDON:
      button_box = gtk.HButtonBox()
      button_box.set_property('layout-style', gtk.BUTTONBOX_END)
      button_box.add(button)
      self._sidepane_vbox.pack_start(button_box, False)
    else:
      list_bottom_hbox = gtk.HBox(False)
      list_bottom_hbox.pack_start(button, False)
      self._sidepane_vbox.pack_start(list_bottom_hbox, False)
    
    self._hpaned.add2(self._sidepane_vbox)
    
    main_vbox.add(self._hpaned)
    
    self._controls_hbox = gtk.HBox()
    self._controls_hbox.set_spacing(6)
    
    button_box = gtk.HButtonBox()
    button_box.set_homogeneous(False)
    button_box.set_property('layout-style', gtk.BUTTONBOX_START)

    if not RUNNING_HILDON:
      image = gtk.Image()
      image.set_from_stock("gtk-media-previous",gtk.ICON_SIZE_BUTTON)
      button = gtk.Button()
      button.set_image(image)
    else:
      button = gtk.Button()
      label = gtk.Label(_("Prev"))
      label.set_use_markup(True)
      button.add(label)
      label.show()
    button.set_property('can-focus', False)
    button.connect("clicked", self._on_prev_clicked)
    button_box.pack_start(button, True, True)
    
    if not RUNNING_HILDON:
      image = gtk.Image()
      image.set_from_stock("gtk-media-rewind",gtk.ICON_SIZE_BUTTON)
      button = gtk.Button()
      button.set_image(image)
    else:
      button = gtk.Button(_("Rew"))
    button.set_property('can-focus', False)
    button.connect("clicked", self._on_rewind_clicked)
    button_box.pack_start(button, True, True)
    
    if not RUNNING_HILDON:
      image = gtk.Image()
      image.set_from_stock("gtk-media-play",gtk.ICON_SIZE_BUTTON)
      self._play_pause_button = gtk.Button()
      self._play_pause_button.set_image(image)
    else:
      self._play_pause_button = gtk.Button(_("Play"))
    self._play_pause_button.set_property('can-focus', False)
    self._play_pause_button.connect("clicked", self._on_play_pause_toggle_clicked)
    button_box.pack_start(self._play_pause_button, True, True)
  
    if not RUNNING_HILDON:
      image = gtk.Image()
      image.set_from_stock("gtk-media-stop",gtk.ICON_SIZE_BUTTON)
      button = gtk.Button()
      button.set_image(image)
    else:
      button = gtk.Button(_("Stop"))
    button.set_property('can-focus', False)
    button.connect("clicked", self._on_stop_clicked)
    button_box.pack_start(button, True, True)
    
    if not RUNNING_HILDON:
      image = gtk.Image()
      image.set_from_stock("gtk-media-forward",gtk.ICON_SIZE_BUTTON)
      button = gtk.Button()
      button.set_image(image)
    else:
      button = gtk.Button(_("FF"))
    button.set_property('can-focus', False)
    button.connect("clicked", self._on_forward_clicked)
    button_box.pack_start(button, True, True)
    
    if not RUNNING_HILDON:
      image = gtk.Image()
      image.set_from_stock("gtk-media-next",gtk.ICON_SIZE_BUTTON)
      button = gtk.Button()
      button.set_image(image)
    else:
      button = gtk.Button(_("Next"))
    button.set_property('can-focus', False)
    button.connect("clicked", self._on_next_clicked)
    button_box.pack_start(button, True, True)
    
    self._controls_hbox.pack_start(button_box, False)
    
    self._time_label = gtk.Label("")
    self._time_label.set_alignment(0.0,0.5)
    
    if not RUNNING_HILDON:
      self._controls_hbox.pack_start(self._time_label, True)
    else:
      list_bottom_hbox.pack_start(self._time_label, True)
      list_bottom_hbox.reorder_child(self._time_label, 0)
    
    main_vbox.pack_start(self._controls_hbox, False)
    self._layout_dock.add(main_vbox)
    
    self.gstreamer_init()
  
    self._layout_dock.show_all()

  def gstreamer_init(self):
    if self._using_playbin2:
      try:
        self._pipeline = gst.element_factory_make("playbin2", "ptv_bin")
      except:
        self._using_playbin2 = False
        self._pipeline = gst.element_factory_make("playbin", "ptv_bin")
    else:
      self._pipeline = gst.element_factory_make("playbin", "ptv_bin")
    #use default audio sink, but get our own video sink
    self._v_sink = self._get_video_sink()
    self._pipeline.set_property('video-sink',self._v_sink)
    #if RUNNING_HILDON:
    #  self._mp3_sink = gst.element_factory_make('dspmp3sink', 'mp3sink')
    #  self._pcm_sink = gst.element_factory_make('dsppcmsink', 'pcmsink')
      
    bus = self._pipeline.get_bus()
    bus.add_signal_watch()
    bus.connect('message', self._on_gst_message)
    #bus.connect('sync-message::element', self._on_sync_message)

  def get_widget(self):
    return self._layout_dock
    
  def is_exposed(self):
    return self.__is_exposed
    
  def has_video(self):
    return self._has_video
    
  def detach(self):
    """video window can detach.  queue stays embedded"""
    pass
  
  def reattach(self):
    """hides external window and reinits embedded window"""
    pass
    
  def toggle_controls(self, show_controls):
    if not show_controls:
      self._controls_hbox.show()
      self._seek_scale.show()
      self._sidepane_vbox.show()
    else:
      self._controls_hbox.hide()
      self._seek_scale.hide()
      self._sidepane_vbox.hide()
      
  def load(self):
    try:
      playlist = open(self._playlist_name, 'r')
    except:
      print "error reading playlist"
      return
      
    self._current_index = pickle.load(playlist)
    self._last_index = -1
    self._media_position = pickle.load(playlist)
    self._media_duration = pickle.load(playlist)
    l = pickle.load(playlist)
    model = self._queue_listview.get_model()
    for uri, name, userdata in l:
      model.append([uri, name, "", userdata])
      filename = gst.uri_get_location(uri)
      self.emit('item-queued', filename, name, userdata)
    if self.__is_exposed:
      if not self._seek_to_saved_position():
        #retry once
        self._seek_to_saved_position()
    playlist.close()
    
  def save(self):
    """saves playlist"""
    try:
      playlist = open(self._playlist_name, 'w')
    except:
      print "error writing playlist"
      return
      
    pickle.dump(self._current_index, playlist)
    pickle.dump(self._media_position, playlist)
    pickle.dump(self._media_duration, playlist)
    l = []
    for uri, name, current, userdata in self._queue_listview.get_model():
      l.append([uri, name, userdata])
    pickle.dump(l, playlist)
    playlist.close()
    
  def queue_file(self, filename, name=None, userdata=None):
    try:
      os.stat(filename)
    except:
      print "file not found",filename
      return

    if name is None:
      name = os.path.split(filename)[1]
    
    if RUNNING_HILDON:
      ext = os.path.splitext(filename)[1][1:]
      known_good = ['mp3', 'wav', 'm4a', 'wma', 'mpg', 'avi', '3gp', 'rm', 'asf', 'mp4']
      try:
        gst.element_factory_make("oggdemux", "test")
        known_good += ['ogg']
      except:
        pass
        
      self._on_type_discovered(None, ext in known_good, filename, name, userdata)
    else:
      #thanks gstfile.py
      d = Discoverer(filename)
      d.connect('discovered', self._on_type_discovered, filename, name, userdata)
      d.discover()
      
  def unqueue(self, filename=None, userdata=None):
    model = self._queue_listview.get_model()
    
    iter_list = []
    
    if filename is not None:
      logging.warning("UNTESTED CODE:")
      it = model.get_iter_first()
      while it is not None:
        data = model.get(it, 0)[0]
        logging.debug("%s %s" % (str(filename), str(data)))
        if data == "file://" + filename:
          iter_list.append(it)
        it = model.iter_next(it)
        
      if len(iter_list) > 0:
        self._remove_items(iter_list)
        
    if userdata is not None:
      it = model.get_iter_first()
      while it is not None:
        data = model.get(it, 3)[0]
        if data == userdata:
          iter_list.append(it)
        it = model.iter_next(it)
        
      if len(iter_list) > 0:
        self._remove_items(iter_list)
        
  def relocate_media(self, old_dir, new_dir):
    if old_dir[-1] == '/' or old_dir[-1] == '\\':
      old_dir = old_dir[:-1]
      
    if new_dir[-1] == '/' or new_dir[-1] == '\\':
      new_dir = new_dir[:-1]
  
    model = self._queue_listview.get_model()
    if len(model) == 0:
      return
      
    self.stop()
      
    for row in model:
      if row[0].startswith("file://" + old_dir):
        row[0] = row[0].replace(old_dir, new_dir)
      
  def get_queue_count(self):
    return len(self._queue_listview.get_model())
    
  def get_queue(self):
    return list(self._queue_listview.get_model())
    
  def play_pause_toggle(self):
    if self._pipeline.get_state()[1] == gst.STATE_PLAYING:
      self.pause()
    else:
      self.play()
    
  def play(self, notick=False):
    model = self._queue_listview.get_model()
    if len(model) == 0:
      return
    if self._current_index < 0: self._current_index = 0
    uri, title, current, userdata = list(model[self._current_index])
    if self._last_index != self._current_index:
      self._last_index = self._current_index
      selection = self._queue_listview.get_selection()
      i = -1
      for row in model:
        i+=1
        if i == self._current_index:
          row[2] = "&#8226;" #bullet
        else:
          row[2] = ""
      if not self._ready_new_uri(uri):
        return
      self._prepare_display()
      self._prepare_save = True
    else:
      if self._do_stop_resume:
        self._do_stop_resume = False
        self._prepare_display()
        if not self._seek_to_saved_position():
          #gstreamer error, recall ourselves
          self.play()
          return
    self._pipeline.set_state(gst.STATE_PLAYING)
    if not RUNNING_HILDON:
      image = gtk.Image()
      image.set_from_stock("gtk-media-pause",gtk.ICON_SIZE_BUTTON)
      self._play_pause_button.set_image(image)
    else:
      self._play_pause_button.set_label(_("Pause"))
    self._media_duration = -1
    if not notick:
      gobject.timeout_add(500, self._tick)
    #self._pipeline.get_property('stream-info')
    self.emit('playing')
    
  def pause(self):
    try: self._media_position = self._pipeline.query_position(gst.FORMAT_TIME)[0]
    except: pass
    self._pipeline.set_state(gst.STATE_PAUSED)
    if not RUNNING_HILDON:
      image = gtk.Image()
      image.set_from_stock("gtk-media-play",gtk.ICON_SIZE_BUTTON)
      self._play_pause_button.set_image(image)
    else:
      self._play_pause_button.set_label(_("Play"))
    self.emit('paused')
    
  def stop(self):
    #this should release the port, but I hate having a stop button on a computer
    #because it doesn't make sense.
    #unbreak: when video is stopped, we need this option to keep the window black
    self._drawing_area.set_flags(gtk.DOUBLE_BUFFERED)
    try: self._media_position = self._pipeline.query_position(gst.FORMAT_TIME)[0]
    except: pass
    self._pipeline.set_state(gst.STATE_READY)
    #self._last_index = -1
    self._seek_scale.set_range(0,1)
    self._seek_scale.set_value(0)
    self._do_stop_resume = True
    if not RUNNING_HILDON:
      image = gtk.Image()
      image.set_from_stock("gtk-media-play",gtk.ICON_SIZE_BUTTON)
      self._play_pause_button.set_image(image)
    else:
      self._play_pause_button.set_label(_("Play"))
  
    if 'gstxvimagesink' in str(type(self._v_sink)).lower():
      #release the xv port
      self._pipeline.unlink(self._v_sink)
      self._v_sink.set_state(gst.STATE_NULL)
    self.emit('paused')
      
  def ff(self):
    if self._pipeline.get_state()[1] != gst.STATE_PLAYING:
      return
    new_pos = self._media_position + 15 * gst.SECOND
    if new_pos > self._media_duration:
      new_pos = self._media_duration
    self.seek(new_pos)
    
  def rew(self):
    if self._pipeline.get_state()[1] != gst.STATE_PLAYING:
      return
    new_pos = self._media_position - 5 * gst.SECOND
    if new_pos < 0:
      new_pos = 0
    self.seek(new_pos)
    
  def next(self):
    model = self._queue_listview.get_model()
    selection = self._queue_listview.get_selection()
    self._pipeline.set_state(gst.STATE_READY)
    self._current_index += 1
    if self._current_index >= len(model):
      self._current_index = 0
    
    selection.unselect_all()
    selection.select_path((self._current_index,))
    
    self._seek_scale.set_range(0,1)
    self._seek_scale.set_value(0)
    self._do_stop_resume = False
    self.play()
    
  def prev(self):
    selection = self._queue_listview.get_selection()
    self._pipeline.set_state(gst.STATE_READY)
    self._current_index -= 1
    if self._current_index <= 0:
      self._current_index = 0
      self.seek(0)
    selection.unselect_all()
    selection.select_path((self._current_index,))
    self._seek_scale.set_range(0,1)
    self._seek_scale.set_value(0)
    self._do_stop_resume = False
    self.play()
    
  def finish(self):
    self.save()
    self._pipeline.set_state(gst.STATE_READY)
    
  def seek(self, time):
    return self._pipeline.seek(1.0, gst.FORMAT_TIME,
              gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE,
              gst.SEEK_TYPE_SET, time,
              gst.SEEK_TYPE_NONE, 0)
              
  def vol_up(self):
    old_vol = floor(self._pipeline.get_property('volume'))
    if old_vol < 10:
      self._pipeline.set_property('volume', old_vol + 1)
    
  def vol_down(self):
    old_vol = ceil(self._pipeline.get_property('volume'))
    if old_vol > 0: 
      self._pipeline.set_property('volume', old_vol - 1)
              
  ###handlers###
  
  def _on_gst_message(self, bus, message):
    #print str(message)
    if message.type == gst.MESSAGE_STATE_CHANGED:
      prev, new, pending = message.parse_state_changed()
      if new == gst.STATE_PLAYING:
        if not self._resized_pane:
          self._resize_pane()
    if message.type == gst.MESSAGE_EOS:
      model = self._queue_listview.get_model()
      if self._current_index < len(model) - 1:
        self.next()
      else:
        self.stop()
    elif message.type == gst.MESSAGE_ERROR:
      gerror, debug = message.parse_error()
      logging.error("GSTREAMER ERROR: %s" % debug)
      if not RUNNING_HILDON:
        self._error_dialog.show_error(debug)
    

    
  def _on_play_clicked(self, b): self.play()
    
  def _on_pause_clicked(self, b): self.pause()
  
  def _on_play_pause_toggle_clicked(self, b): self.play_pause_toggle()
  
  def _on_stop_clicked(self, b): self.stop()
    
  def _on_rewind_clicked(self, b): self.rew()
    
  def _on_forward_clicked(self, b): self.ff()
  
  def _on_next_clicked(self, b): self.next()
  
  def _on_prev_clicked(self, b): self.prev()
  
  def _on_type_discovered(self, discoverer, ismedia, filename, name, userdata):
    if ismedia:
      model = self._queue_listview.get_model()
      uri = 'file://'+urllib.quote(filename)
      if RUNNING_SUGAR or RUNNING_HILDON:
        name = '<span size="x-small">'+name+'</span>'
      model.append([uri, name, "", userdata])
      self.emit('item-queued', filename, name, userdata)
      self.save()
    else:
      self.emit('item-not-supported', filename, name, userdata)
  
  def _on_remove_clicked(self, b):
    model, paths = self._queue_listview.get_selection().get_selected_rows()
    self._remove_items([model.get_iter(path) for path in paths])
      
  def _remove_items(self, iter_list):
    model = self._queue_listview.get_model()
    current_uri = model[self._current_index][0]
    
    for i in iter_list:
      if model.get_path(i)[0] == self._current_index:
        print "stopping current"
        self.stop()
      model.remove(i)
      
    if len(model) == 0:
      self._last_index = -1
      self._current_index = 0
      self._media_position = 0
      self._media_duration = 0
      self._update_time_label()
    else:
      try:
        self._current_index = [r[0] for r in model].index(current_uri)
        self._last_index = self._current_index
      except ValueError:
        # If the current_uri was removed, reset to top of list
        self._current_index = 0
        self._last_index = -1
        self._media_position = 0
        self._media_duration = 0
        self._update_time_label()
    self.emit('items-removed')
    
  def _on_seek_value_changed(self, widget):
    if self.__no_seek:
      return
    pos = widget.get_value()
    self.seek(pos)
    
  def _on_queue_row_activated(self, treeview, path, view_column):
    self.pause()
    self._last_index = -1
    self._current_index = path[0]
    self.play()
    
  def _on_queue_row_button_press(self, widget, event):
    if event.button==3: #right click     
      menu = gtk.Menu()   
      
      path = widget.get_path_at_pos(int(event.x),int(event.y))
      model = widget.get_model()
      if path is None: #nothing selected
        return

      item = gtk.ImageMenuItem(_("_Remove"))
      img = gtk.image_new_from_stock('gtk-remove',gtk.ICON_SIZE_MENU)
      item.set_image(img)
      item.connect('activate',self._on_remove_clicked)
      menu.append(item)
        
      menu.show_all()
      menu.popup(None,None,None, event.button,event.time)
      
  def _on_key_press_event(self, widget, event):
    keyname = gtk.gdk.keyval_name(event.keyval)
    self.handle_key(keyname)
  
  def handle_key(self, keyname):
    #if keyname == 'f':
    #  self.toggle_fullscreen()
    if keyname == 'n':
      self.next()
    elif keyname == 'b':
      self.prev()
    elif keyname == 'space' or keyname == 'p':
      self.play_pause_toggle()
    #FIXME: these don't work when we're embedded in penguintv.  why?
    elif keyname == 'Right':
      self.ff()
    elif keyname == 'Left':
      self.rew()
    else:
      return False
    return True
      
  def _on_drawing_area_exposed(self, widget, event):
    if self._x_overlay is None:
      self._x_overlay = self._pipeline.get_by_interface(gst.interfaces.XOverlay)
    self._v_sink.expose()
    if not self.__is_exposed:
      model = self._queue_listview.get_model()
      if len(model) > 0:
        #self._prepare_display()
        if not self._seek_to_saved_position():
          #retry once
          self._seek_to_saved_position()
      self.__is_exposed = True
        
  ###utility functions###
  def _get_video_sink(self, compat=False):
    if RUNNING_HILDON:
      if compat:
        return none
      try:
        v_sink = self._pipeline.get_by_name('videosink')
        if v_sink is None:
          v_sink = gst.element_factory_make("xvimagesink", "v_sink")
          bus = self._pipeline.get_bus()
          v_sink.set_bus(bus)
          return v_sink
        else:
          return v_sink
      except:
        logging.error("didn't get videosink :(")
        return None
        
    if compat:
      sinks = ["ximagesink"]
    else:
      sinks = ["xvimagesink","ximagesink"]
    for sink_str in sinks:
      try:
        v_sink = gst.element_factory_make(sink_str, "v_sink")
        break
      except:
        print "couldn't init ",sink_str
    #according to totem this helps set things up (bacon-video-widget-gst-0.10:4290)
    bus = self._pipeline.get_bus()
    v_sink.set_bus(bus)
    return v_sink

  def _ready_new_uri(self, uri):
    """load a new uri into the pipeline and prepare the pipeline for playing"""
    #if RUNNING_HILDON:
    #  ext = os.path.splitext(uri)[1]
    #  #if ext == '.mp3':
    #  #  logging.debug("readying for mp3")
    #  #  self._pipeline.set_property('audio-sink', self._mp3_sink)
    #  #else:
    #  #  logging.debug("readying for pcm")
    #  #  self._pipeline.set_property('audio-sink', self._pcm_sink)
    self._pipeline.set_state(gst.STATE_READY)
    self._pipeline.set_property('uri',uri)
    self._x_overlay = None #reset so we grab again when we start playing
    return True
    
  def _prepare_display(self, compat=False):
    #if type(self._v_sink) != GstXVImageSink and not compat:
    #do this right at some point: if we are using a substandard sink
    #and we're not being specifically told to use it, try the better one
    
    if 'gstximagesink' in str(type(self._v_sink)).lower() and not compat:
      self._v_sink = self._get_video_sink()
      self._pipeline.set_property('video-sink',self._v_sink)
    if compat:
      self._v_sink = self._get_video_sink(True)
      self._pipeline.set_property('video-sink',self._v_sink)
      
    self._v_sink.set_state(gst.STATE_READY)  
    change_return, state, pending = self._v_sink.get_state(gst.SECOND * 10)
    if change_return != gst.STATE_CHANGE_SUCCESS:
      if 'gstximagesink' in str(type(self._v_sink)).lower():
        print "couldn't find a valid video sink (do something!)"
        return
      #well that didn't work, try again with compatibility sink
      self._v_sink = self._get_video_sink(True)
      self._pipeline.set_property('video-sink',self._v_sink)
      self._prepare_display(True)
      return
     #maemo throws X Window System errors when doing this -- ignore them
    #http://labs.morpheuz.eng.br/blog/14/08/2007/xv-and-mplayer-on-maemo/
    if RUNNING_HILDON:
      gtk.gdk.error_trap_push()
        
    self._v_sink.set_xwindow_id(self._drawing_area.window.xid)
    #causes expose problems
    #self._v_sink.set_property('sync', True)
    self._v_sink.set_property('force-aspect-ratio', True)
    self._resized_pane = False
    
      if RUNNING_HILDON:
        def pop_trap():
        gtk.gdk.flush()
        gtk.gdk.error_trap_pop()
        return False
        
      gobject.idle_add(pop_trap)
    
  def _resize_pane(self):
    #get video width and height so we can resize the pane
    #see totem
    #if (!(caps = gst_pad_get_negotiated_caps (pad)))
    #unbreakme: if there's no video, it doesn't draw right here either
    self._drawing_area.set_flags(gtk.DOUBLE_BUFFERED)
    
    min_width = 200
    max_width = self._hpaned.get_allocation().width - 200 #-200 for the list box
    
    pad = self._v_sink.get_pad('sink')
    if pad is None:
      logging.debug("didn't get video sink pad")
      return
    
    self._resized_pane = True  
      
     caps = pad.get_negotiated_caps()
     if caps is None: #no big deal, this might be audio only
       self._hpaned.set_position(max_width / 2)
       self._has_video = False
       return
     
     #maemo throws X Window System errors when doing this -- ignore them
    #http://labs.morpheuz.eng.br/blog/14/08/2007/xv-and-mplayer-on-maemo/
    if RUNNING_HILDON:
      gtk.gdk.error_trap_push()

     self._has_video = True
     
     #unbreakme: without this option the video doesn't redraw correctly when exposed
    self._drawing_area.unset_flags(gtk.DOUBLE_BUFFERED)  
       
      s = caps[0]
      movie_aspect = float(s['width']) / s['height']
      display_height = self._drawing_area.get_allocation().height
      
      new_display_width = float(display_height)*movie_aspect
      if new_display_width >= max_width:
        self._hpaned.set_position(max_width)
      elif new_display_width <= min_width:
        self._hpaned.set_position(min_width)
      else:
        self._hpaned.set_position(int(new_display_width))
      
      if RUNNING_HILDON:
      def pop_trap():
        gtk.gdk.flush()
        gtk.gdk.error_trap_pop()
        return False
        
      gobject.idle_add(pop_trap)

  def _seek_to_saved_position(self):
    """many sources don't support seek in ready, so we do it the old fashioned way:
    play, wait for it to play, pause, wait for it to pause, and then seek"""
    model = self._queue_listview.get_model()
    i = -1
    for row in model:
      i+=1
      if i == self._current_index: row[2] = "&#8226;" #bullet
      else: row[2] = ""
    #save, because they may get overwritten when we play and pause
    pos, dur = self._media_position, self._media_duration
    if not RUNNING_HILDON:
      volume = self._pipeline.get_property('volume')
    #temporarily mute to avoid little bloops during this hack
    self._pipeline.set_property('volume',0)
    
    #maemo throws X Window System errors when doing this -- ignore them
    #http://labs.morpheuz.eng.br/blog/14/08/2007/xv-and-mplayer-on-maemo/
    if RUNNING_HILDON:
      gtk.gdk.error_trap_push()

    self.play(True)
    change_return, state, pending = self._pipeline.get_state(gst.SECOND * 10)
    if change_return != gst.STATE_CHANGE_SUCCESS:
      print "some problem changing state to play, may be playbin2 issue?"
      self._using_playbin2 = False
      self.stop()
      self._pipeline.set_state(gst.STATE_NULL)
      self.gstreamer_init()
      self._last_index = -1 #trigger play() to reinit the pipe
      return False
    self.pause()
    if change_return != gst.STATE_CHANGE_SUCCESS:
      print "some problem changing state to pause"
      self._using_playbin2 = False
      self._pipeline.set_state(gst.STATE_NULL)
      self.gstreamer_init()
      self._last_index = -1 #trigger play() to reinit the pipe
      return False
    self._media_position, self._media_duration = pos, dur
    self.seek(self._media_position)
    if self._media_duration <= 0:
      self._media_duration = 1
    self._resize_pane()
    self._seek_scale.set_range(0,self._media_duration)
    self._seek_scale.set_value(self._media_position)
    if not RUNNING_HILDON:
      self._pipeline.set_property('volume',volume)
    else:
      self._pipeline.set_property('volume', 10)
    self._update_time_label()
    
      if RUNNING_HILDON:
      def pop_trap():
        gtk.gdk.flush()
        gtk.gdk.error_trap_pop()
        return False
        
      gobject.idle_add(pop_trap)
    
    return True
    
  def _tick(self):
    self.__no_seek = True
    try:
      now = self._pipeline.query_position(gst.FORMAT_TIME)[0]
      if now - self._last_tick > self._tick_interval:
        self._last_tick = now
        self.emit('tick')
    except:
      pass
    
    self._update_seek_bar()
    self._update_time_label()
    if self._prepare_save:
      self._prepare_save = False
      self.save()
    self.__no_seek = False
    return self._pipeline.get_state()[1] == gst.STATE_PLAYING
    
  def _periodic_save_cb(self):
    self._prepare_save = True
    return True

  def _update_seek_bar(self):
    #for some reason when paused, hildon tells us the position is 0. Ignore it
    if RUNNING_HILDON and self._pipeline.get_state()[1] == gst.STATE_PAUSED:
      return
    try:
      self._media_position = self._pipeline.query_position(gst.FORMAT_TIME)[0]
      #print self._media_position, self._media_duration
      if self._media_position > self._media_duration:
        self._media_duration = self._pipeline.query_duration(gst.FORMAT_TIME)[0]
        self._seek_scale.set_range(0,self._media_duration)
      self._seek_scale.set_value(self._media_position)
      
    except Exception, e:
      print e
      
  def _update_time_label(self):
    def nano_to_string(long_val):
      seconds = long_val / gst.SECOND
      minutes = seconds / 60
      seconds = seconds % 60
      return "%i:%.2i" % (minutes,seconds)
      
    self._time_label.set_text(nano_to_string(self._media_position)+" / "+nano_to_string(self._media_duration))
    
  #def _on_sync_message(self, bus, message):
  #  if message.structure is None:
  #    return
  #  if message.structure.get_name() == 'prepare-xwindow-id':
  #    logging.debug("forcing aspect to true")
  #    message.src.set_property('force-aspect-ratio', True)
  ###drag and drop###
    
  def _on_queue_drag_data_received(self, treeview, context, x, y, selection, targetType, time):
    if targetType == self._TARGET_TYPE_REORDER:
      treeview.emit_stop_by_name('drag-data-received')
      model, paths_to_copy = treeview.get_selection().get_selected_rows()
      if len(paths_to_copy) > 1:
        print "can only move one at a time"
        return
      row = list(model[paths_to_copy[0][0]])
      iter_to_copy = model.get_iter(paths_to_copy[0])
      try:
        path, pos = treeview.get_dest_row_at_pos(x, y)
        target_iter = model.get_iter(path)
        
        playing_uri = model[self._current_index][0]
        
        if self.checkSanity(model, iter_to_copy, target_iter):
          self.iterCopy(model, target_iter, row, pos)
          context.finish(True, True, time) #finishes the move
          i=-1
          for row in model:
            i+=1
            if playing_uri == row[0]:
              self._last_index = self._current_index = i
              row[2]="&#8226;" #bullet
            else:
              row[2]=""              
        else:
          context.finish(False, False, time)
      except:
        model.append(row)
        context.finish(True, True, time)
    elif targetType == self._TARGET_TYPE_URI_LIST:
      uri_list = [s for s in selection.data.split('\r\n') if len(s) > 0]
      for uri in uri_list:
        uri = uri.replace("file://", "")
        uri = urllib.unquote(uri)
        self.queue_file(uri)      

  def checkSanity(self, model, iter_to_copy, target_iter):
    path_of_iter_to_copy = model.get_path(iter_to_copy)
    path_of_target_iter = model.get_path(target_iter)
    if path_of_target_iter[0:len(path_of_iter_to_copy)] == path_of_iter_to_copy:
      return False
    else:
      return True
  
  def iterCopy(self, target_model, target_iter, row, pos):
    if (pos == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE) or (pos == gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
      new_iter = target_model.append(row)
    elif pos == gtk.TREE_VIEW_DROP_BEFORE:
      new_iter = target_model.insert_before(target_iter, row)
    elif pos == gtk.TREE_VIEW_DROP_AFTER:
      new_iter = target_model.insert_after(target_iter, row)    
      
class GStreamerErrorDialog(gtk.Window):
  def __init__(self, type=gtk.WINDOW_TOPLEVEL):
    gtk.Window.__init__(self, type)
    self._last_message = ""
    self._label = gtk.Label()
    
    #gtk preparation
    vbox = gtk.VBox()
    vbox.pack_start(self._label, True, True, 0)
    hbox = gtk.HBox()
    l = gtk.Label("")
    hbox.pack_start(l, True)
    button = gtk.Button(stock='gtk-ok')
    button.connect('clicked', self._on_ok_clicked)
    hbox.pack_start(button, False)
    vbox.pack_start(hbox, False)
    self.add(vbox)
    self.connect('delete-event', self._on_delete_event)
    
  def show_error(self, error_msg):
    if error_msg == self._last_message:
      return
    self._last_message = error_msg
    self._label.set_text(error_msg)
    self.show_all()
      
  def _on_ok_clicked(self, button):
    self.hide()
      
  def _on_delete_event(self, widget, event):
    return self.hide_on_delete()
    
#########app
def do_quit(self, widget, player):
  player.finish()
  gtk.main_quit()
  
def items_removed(player):
  print player.get_queue_count()
  
def item_not_supported(app, player, filename, name):
  print filename,name, "not supported"
  
fullscreen = False
def on_app_key_press_event(widget, event, player, window):
  global fullscreen
  keyname = gtk.gdk.keyval_name(event.keyval)
  if keyname == 'f' or (RUNNING_HILDON and keyname == 'F6'):
    #maemo throws X Window System errors when doing this -- ignore them
    #http://labs.morpheuz.eng.br/blog/14/08/2007/xv-and-mplayer-on-maemo/
    if RUNNING_HILDON:
      gtk.gdk.error_trap_push()
    fullscreen = not fullscreen
    player.toggle_controls(fullscreen)
    if fullscreen:
      window.window.fullscreen()
    else:
      window.window.unfullscreen()
    if RUNNING_HILDON:
      def pop_trap():
        gtk.gdk.flush()
        gtk.gdk.error_trap_pop()
        return False
        
      gobject.idle_add(pop_trap)
    
if __name__ == '__main__': # Here starts the dynamic part of the program 
  window = gtk.Window()
  if RUNNING_SUGAR:
    import sugar.env
    home = os.path.join(sugar.env.get_profile_path(), 'penguintv')
  else:
    home = os.path.join(os.getenv('HOME'), ".penguintv")
  try:
    opts, args = getopt.getopt(sys.argv[1:], "p:", ["--playlist"])
  except getopt.GetoptError, e:
    print "error %s", str(e)
    sys.exit(1)
  playlist = os.path.join(home, 'gst_playlist.pickle')
  if len(opts) > 0:
    for o, a in opts:
      if o in ('-p', '--playlist'):
        playlist = a
                
  app = GStreamerPlayer(window, playlist)
  app.Show()
  
  window.connect('delete-event', do_quit, app)
  window.connect('key-press-event', on_app_key_press_event, app, window)
  window.resize(640,480)
  app.connect('items-removed', items_removed)  
  app.connect('item-not-supported', item_not_supported)
  
        
  app.load()

  for item in args:
    app.queue_file(item)
  gtk.main()
  
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.