Difference between revisions of "Development Team/Almanac/GStreamer"

From Sugar Labs
Jump to navigation Jump to search
m
 
(13 intermediate revisions by 8 users not shown)
Line 1: Line 1:
{{Sugar Almanac}}
+
{{Almanac}}
  
 
=== Where can I get additional resources and sample code on using the camera? ===
 
=== Where can I get additional resources and sample code on using the camera? ===
Line 5: Line 5:
  
  
=== How can I play back video files in my activity? ===
+
=== How can I embed video files in my activity? ===
 
Sugar, by default, can play back audio (encoded in Vorbis) and video (encoded in Theora) files in ogg containers.
 
Sugar, by default, can play back audio (encoded in Vorbis) and video (encoded in Theora) files in ogg containers.
 +
 +
The following file will be helpful for embedding video or audio files into your activity.  After the code posting is a code snippet showing how to use this.
 +
 +
<pre>
 +
import gtk
 +
import pygtk
 +
pygtk.require('2.0')
 +
import sys
 +
import pygst
 +
pygst.require('0.10')
 +
import gst
 +
import gst.interfaces
 +
import gobject
 +
import time
 +
gobject.threads_init()
 +
 +
 +
class Gplay:
 +
 +
def __init__(self):
 +
self.window = None
 +
self.playing = False
 +
 +
self.player = gst.element_factory_make("playbin", "playbin")
 +
xis = gst.element_factory_make("xvimagesink", "xvimagesink")
 +
self.player.set_property("video-sink", xis)
 +
bus = self.player.get_bus()
 +
bus.enable_sync_message_emission()
 +
bus.add_signal_watch()
 +
self.SYNC_ID = bus.connect('sync-message::element', self._onSyncMessageCb)
 +
 +
 +
def _onSyncMessageCb(self, bus, message):
 +
if message.structure is None:
 +
return True
 +
if message.structure.get_name() == 'prepare-xwindow-id':
 +
self.window.set_sink(message.src)
 +
message.src.set_property('force-aspect-ratio', True)
 +
return True
 +
 +
 +
def setFile(self, path):
 +
uri = "file://" + str( path )
 +
if self.player.get_property('uri') == uri:
 +
self.seek(gst.SECOND*0)
 +
return
 +
 +
self.player.set_state(gst.STATE_READY)
 +
self.player.set_property('uri', uri)
 +
ext = uri[len(uri)-3:]
 +
if ext == "jpg":
 +
self.pause()
 +
else:
 +
self.play()
 +
 +
 +
def queryPosition(self):
 +
#"Returns a (position, duration) tuple"
 +
try:
 +
position, format = self.player.query_position(gst.FORMAT_TIME)
 +
except:
 +
position = gst.CLOCK_TIME_NONE
 +
 +
try:
 +
duration, format = self.player.query_duration(gst.FORMAT_TIME)
 +
except:
 +
duration = gst.CLOCK_TIME_NONE
 +
 +
return (position, duration)
 +
 +
 +
def seek(self, time):
 +
event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, time, gst.SEEK_TYPE_NONE, 0)
 +
res = self.player.send_event(event)
 +
if res:
 +
self.player.set_new_stream_time(0L)
 +
 +
 +
def pause(self):
 +
self.playing = False
 +
self.player.set_state(gst.STATE_PAUSED)
 +
 +
 +
def play(self):
 +
self.playing = True
 +
self.player.set_state(gst.STATE_PLAYING)
 +
 +
 +
def stop(self):
 +
self.playing = False
 +
self.player.set_state(gst.STATE_NULL)
 +
self.nextMovie()
 +
 +
 +
def get_state(self, timeout=1):
 +
return self.player.get_state(timeout=timeout)
 +
 +
 +
def is_playing(self):
 +
return self.playing
 +
 +
 +
class PlayVideoWindow(gtk.Window):
 +
def __init__(self):
 +
gtk.Window.__init__(self)
 +
 +
self.imagesink = None
 +
self.unset_flags(gtk.DOUBLE_BUFFERED)
 +
self.set_flags(gtk.APP_PAINTABLE)
 +
 +
 +
def set_sink(self, sink):
 +
if self.imagesink != None:
 +
assert self.window.xid
 +
self.imagesink = None
 +
del self.imagesink
 +
 +
self.imagesink = sink
 +
self.imagesink.set_xwindow_id(self.window.xid)
 +
 +
</pre>
 +
 +
And here is a code snippet showing how you would use this file to embed media playback in your activity:
 +
 +
<pre>
 +
#this is our video playback window
 +
self.gplay = Gplay( )
 +
self.gplayWin = PlayVideoWindow( )
 +
self.gplay.window = self.gplayWin
 +
 +
self.gplayWin.set_type_hint( gtk.gdk.WINDOW_TYPE_HINT_DIALOG )
 +
self.gplayWin.set_decorated( False )
 +
self.gplayWin.set_transient_for( self ) #activity subclass
 +
self.gplayWin.move( 100, 100 )
 +
self.gplayWin.resize( 300, 400 )
 +
self.gplayWin.show_all( )
 +
</pre>
 +
 +
Alternatively, you could embed your video into a gtk.DrawingArea:
 +
 +
<pre>
 +
class VideoWidget(gtk.DrawingArea):
 +
    def __init__(self):
 +
        gtk.DrawingArea.__init__(self)
 +
        self.set_events(gtk.gdk.POINTER_MOTION_MASK |
 +
        gtk.gdk.POINTER_MOTION_HINT_MASK |
 +
        gtk.gdk.EXPOSURE_MASK |
 +
        gtk.gdk.KEY_PRESS_MASK |
 +
        gtk.gdk.KEY_RELEASE_MASK)
 +
        self.imagesink = None
 +
        self.unset_flags(gtk.DOUBLE_BUFFERED)
 +
        self.set_flags(gtk.APP_PAINTABLE)
 +
 +
    def do_expose_event(self, event):
 +
        if self.imagesink:
 +
            self.imagesink.expose()
 +
            return False
 +
        else:
 +
            return True
 +
 +
    def set_sink(self, sink):
 +
        assert self.window.xid
 +
        self.imagesink = sink
 +
        self.imagesink.set_xwindow_id(self.window.xid)
 +
 +
</pre>
 +
 +
=== How can I add play/pause buttons and a scrubber to my audio or video? ===
 +
 +
The widget below is pre-configured to work with the gplay class above.  This widget takes two gtk.Images in its constructor.
 +
 +
<pre>
 +
import gtk
 +
import pygtk
 +
pygtk.require('2.0')
 +
import pygst
 +
pygst.require('0.10')
 +
import gst
 +
import gst.interfaces
 +
import gobject
 +
 +
import sugar.graphics.style
 +
 +
class GScrub(gtk.Window):
 +
 +
def __init__(self, gplay, playImg, pauseImg):
 +
gtk.Window.__init__(self)
 +
self._gplay = gplay
 +
self._playImg = playImg
 +
self._pauseImg = pauseImg
 +
 +
self.UPDATE_INTERVAL = 500
 +
self.UPDATE_SCALE_ID = 0
 +
self.CHANGED_ID = 0
 +
self.was_playing = False
 +
self.p_position = gst.CLOCK_TIME_NONE
 +
self.p_duration = gst.CLOCK_TIME_NONE
 +
 +
self.hbox = gtk.HBox()
 +
self.hbox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
 +
self.hbox.modify_bg( gtk.STATE_INSENSITIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
 +
self.add( self.hbox )
 +
 +
self.button = gtk.Button()
 +
buttBox = gtk.EventBox()
 +
buttBox.add(self.button)
 +
buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
 +
self.button.set_image( self._playImg )
 +
self.button.set_property('can-default', True)
 +
self.button.set_relief(gtk.RELIEF_NONE)
 +
self.button.set_size_request( 55, 55 )
 +
buttBox.set_size_request( 55, 55 )
 +
self.button.show()
 +
 +
buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
 +
self.button.modify_bg( gtk.STATE_ACTIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
 +
 +
self.button.connect('clicked', self._buttonClickedCb)
 +
self.hbox.pack_start(buttBox, expand=False)
 +
 +
self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
 +
self.hscale = gtk.HScale(self.adjustment)
 +
self.hscale.set_draw_value(False)
 +
self.hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
 +
hscaleBox = gtk.EventBox()
 +
hscaleBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
 +
hscaleBox.add( self.hscale )
 +
self.hscale.connect('button-press-event', self._scaleButtonPressCb)
 +
self.hscale.connect('button-release-event', self._scaleButtonReleaseCb)
 +
self.hbox.pack_start(hscaleBox, expand=True)
 +
 +
 +
def removeCallbacks( self ):
 +
if self.UPDATE_SCALE_ID != 0:
 +
gobject.source_remove(self.UPDATE_SCALE_ID)
 +
self.UPDATE_SCALE_ID = 0
 +
if self.CHANGED_ID != 0:
 +
gobject.source_remove(self.CHANGED_ID)
 +
self.CHANGED_ID = 0
 +
 +
 +
def reset(self):
 +
self.adjustment.set_value(0)
 +
 +
 +
def _buttonClickedCb(self, widget):
 +
self.play_toggled()
 +
 +
 +
def set_button_play(self):
 +
self.button.set_image(self._playImg)
 +
 +
 +
def set_button_pause(self):
 +
self.button.set_image(self._pauseImg)
 +
 +
 +
def play_toggled(self):
 +
self.p_position, self.p_duration = self._gplay.queryPosition()
 +
if self.p_position == self.p_duration:
 +
self._gplay.seek(0)
 +
self._gplay.pause()
 +
 +
if self._gplay.is_playing():
 +
self._gplay.pause()
 +
self.set_button_play()
 +
else:
 +
#if self._gplay.error:
 +
# #todo: check if we have "error", and also to disable everything
 +
# self.button.set_disabled()
 +
#else:
 +
self.doPlay()
 +
 +
 +
def doPlay(self):
 +
self._gplay.play()
 +
if self.UPDATE_SCALE_ID == 0:
 +
self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb)
 +
self.set_button_pause()
 +
 +
 +
def _scaleButtonPressCb(self, widget, event):
 +
#self.button.set_sensitive(False)
 +
self.was_playing = self._gplay.is_playing()
 +
if self.was_playing:
 +
self._gplay.pause()
 +
 +
# don't timeout-update position during seek
 +
if self.UPDATE_SCALE_ID != 0:
 +
gobject.source_remove(self.UPDATE_SCALE_ID)
 +
self.UPDATE_SCALE_ID = 0
 +
 +
# make sure we get changed notifies
 +
if self.CHANGED_ID == 0:
 +
self.CHANGED_ID = self.hscale.connect('value-changed', self._scaleValueChangedCb)
 +
 +
 +
def _scaleButtonReleaseCb(self, widget, event):
 +
# see seek.cstop_seek
 +
widget.disconnect(self.CHANGED_ID)
 +
self.CHANGED_ID = 0
 +
 +
#self.button.set_sensitive(True)
 +
if self.was_playing:
 +
self._gplay.play()
 +
 +
if self.UPDATE_SCALE_ID != 0:
 +
pass
 +
#print('Had a previous update timeout id')
 +
else:
 +
self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb)
 +
 +
 +
def _scaleValueChangedCb(self, scale):
 +
real = long(scale.get_value() * self.p_duration / 100) # in ns
 +
self._gplay.seek(real)
 +
# allow for a preroll
 +
self._gplay.get_state(timeout=50*gst.MSECOND) # 50 ms
 +
 +
 +
def _updateScaleCb(self):
 +
self.p_position, self.p_duration = self._gplay.queryPosition()
 +
if self.p_position != gst.CLOCK_TIME_NONE:
 +
value = self.p_position * 100.0 / self.p_duration
 +
if value > 99:
 +
value = 99
 +
elif value < 0:
 +
value = 0
 +
 +
self.adjustment.set_value(value)
 +
 +
if self._gplay.is_playing() and self.p_position == self.p_duration:
 +
self._gplay.pause()
 +
self.set_button_play()
 +
 +
return True
 +
</pre>

Latest revision as of 14:29, 2 February 2011

  Development Team/Almanac


Where can I get additional resources and sample code on using the camera?


How can I embed video files in my activity?

Sugar, by default, can play back audio (encoded in Vorbis) and video (encoded in Theora) files in ogg containers.

The following file will be helpful for embedding video or audio files into your activity. After the code posting is a code snippet showing how to use this.

import gtk
import pygtk
pygtk.require('2.0')
import sys
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gobject
import time
gobject.threads_init()


class Gplay:

	def __init__(self):
		self.window = None
		self.playing = False

		self.player = gst.element_factory_make("playbin", "playbin")
		xis = gst.element_factory_make("xvimagesink", "xvimagesink")
		self.player.set_property("video-sink", xis)
		bus = self.player.get_bus()
		bus.enable_sync_message_emission()
		bus.add_signal_watch()
		self.SYNC_ID = bus.connect('sync-message::element', self._onSyncMessageCb)


	def _onSyncMessageCb(self, bus, message):
		if message.structure is None:
			return True
		if message.structure.get_name() == 'prepare-xwindow-id':
			self.window.set_sink(message.src)
			message.src.set_property('force-aspect-ratio', True)
			return True


	def setFile(self, path):
		uri = "file://" + str( path )
		if self.player.get_property('uri') == uri:
			self.seek(gst.SECOND*0)
			return

		self.player.set_state(gst.STATE_READY)
		self.player.set_property('uri', uri)
		ext = uri[len(uri)-3:]
		if ext == "jpg":
			self.pause()
		else:
			self.play()


	def queryPosition(self):
		#"Returns a (position, duration) tuple"
		try:
			position, format = self.player.query_position(gst.FORMAT_TIME)
		except:
			position = gst.CLOCK_TIME_NONE

		try:
			duration, format = self.player.query_duration(gst.FORMAT_TIME)
		except:
			duration = gst.CLOCK_TIME_NONE

		return (position, duration)


	def seek(self, time):
		event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, time, gst.SEEK_TYPE_NONE, 0)
		res = self.player.send_event(event)
		if res:
			self.player.set_new_stream_time(0L)


	def pause(self):
		self.playing = False
		self.player.set_state(gst.STATE_PAUSED)


	def play(self):
		self.playing = True
		self.player.set_state(gst.STATE_PLAYING)


	def stop(self):
		self.playing = False
		self.player.set_state(gst.STATE_NULL)
		self.nextMovie()


	def get_state(self, timeout=1):
		return self.player.get_state(timeout=timeout)


	def is_playing(self):
		return self.playing


class PlayVideoWindow(gtk.Window):
	def __init__(self):
		gtk.Window.__init__(self)

		self.imagesink = None
		self.unset_flags(gtk.DOUBLE_BUFFERED)
		self.set_flags(gtk.APP_PAINTABLE)


	def set_sink(self, sink):
		if self.imagesink != None:
			assert self.window.xid
			self.imagesink = None
			del self.imagesink

		self.imagesink = sink
		self.imagesink.set_xwindow_id(self.window.xid)

And here is a code snippet showing how you would use this file to embed media playback in your activity:

		#this is our video playback window
		self.gplay = Gplay( )
		self.gplayWin = PlayVideoWindow( )
		self.gplay.window = self.gplayWin

		self.gplayWin.set_type_hint( gtk.gdk.WINDOW_TYPE_HINT_DIALOG )
		self.gplayWin.set_decorated( False )
		self.gplayWin.set_transient_for( self ) #activity subclass
		self.gplayWin.move( 100, 100 )
		self.gplayWin.resize( 300, 400 )
		self.gplayWin.show_all( )

Alternatively, you could embed your video into a gtk.DrawingArea:

class VideoWidget(gtk.DrawingArea):
    def __init__(self):
        gtk.DrawingArea.__init__(self)
        self.set_events(gtk.gdk.POINTER_MOTION_MASK |
        gtk.gdk.POINTER_MOTION_HINT_MASK |
        gtk.gdk.EXPOSURE_MASK |
        gtk.gdk.KEY_PRESS_MASK |
        gtk.gdk.KEY_RELEASE_MASK) 
        self.imagesink = None
        self.unset_flags(gtk.DOUBLE_BUFFERED)
        self.set_flags(gtk.APP_PAINTABLE)

    def do_expose_event(self, event):
        if self.imagesink:
            self.imagesink.expose()
            return False
        else:
            return True

    def set_sink(self, sink):
        assert self.window.xid
        self.imagesink = sink
        self.imagesink.set_xwindow_id(self.window.xid)

How can I add play/pause buttons and a scrubber to my audio or video?

The widget below is pre-configured to work with the gplay class above. This widget takes two gtk.Images in its constructor.

import gtk
import pygtk
pygtk.require('2.0')
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gobject

import sugar.graphics.style

class GScrub(gtk.Window):

	def __init__(self, gplay, playImg, pauseImg):
		gtk.Window.__init__(self)
		self._gplay = gplay
		self._playImg = playImg
		self._pauseImg = pauseImg

		self.UPDATE_INTERVAL = 500
		self.UPDATE_SCALE_ID = 0
		self.CHANGED_ID = 0
		self.was_playing = False
		self.p_position = gst.CLOCK_TIME_NONE
		self.p_duration = gst.CLOCK_TIME_NONE

		self.hbox = gtk.HBox()
		self.hbox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
		self.hbox.modify_bg( gtk.STATE_INSENSITIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
		self.add( self.hbox )

		self.button = gtk.Button()
		buttBox = gtk.EventBox()
		buttBox.add(self.button)
		buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
		self.button.set_image( self._playImg )
		self.button.set_property('can-default', True)
		self.button.set_relief(gtk.RELIEF_NONE)
		self.button.set_size_request( 55, 55 )
		buttBox.set_size_request( 55, 55 )
		self.button.show()

		buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
		self.button.modify_bg( gtk.STATE_ACTIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )

		self.button.connect('clicked', self._buttonClickedCb)
		self.hbox.pack_start(buttBox, expand=False)

		self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
		self.hscale = gtk.HScale(self.adjustment)
		self.hscale.set_draw_value(False)
		self.hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
		hscaleBox = gtk.EventBox()
		hscaleBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )
		hscaleBox.add( self.hscale )
		self.hscale.connect('button-press-event', self._scaleButtonPressCb)
		self.hscale.connect('button-release-event', self._scaleButtonReleaseCb)
		self.hbox.pack_start(hscaleBox, expand=True)


	def removeCallbacks( self ):
		if self.UPDATE_SCALE_ID != 0:
			gobject.source_remove(self.UPDATE_SCALE_ID)
			self.UPDATE_SCALE_ID = 0
		if self.CHANGED_ID != 0:
			gobject.source_remove(self.CHANGED_ID)
			self.CHANGED_ID = 0


	def reset(self):
		self.adjustment.set_value(0)


	def _buttonClickedCb(self, widget):
		self.play_toggled()


	def set_button_play(self):
		self.button.set_image(self._playImg)


	def set_button_pause(self):
		self.button.set_image(self._pauseImg)


	def play_toggled(self):
		self.p_position, self.p_duration = self._gplay.queryPosition()
		if self.p_position == self.p_duration:
			self._gplay.seek(0)
			self._gplay.pause()

		if self._gplay.is_playing():
			self._gplay.pause()
			self.set_button_play()
		else:
			#if self._gplay.error:
			#	#todo: check if we have "error", and also to disable everything
			#	self.button.set_disabled()
			#else:
			self.doPlay()


	def doPlay(self):
		self._gplay.play()
		if self.UPDATE_SCALE_ID == 0:
			self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb)
		self.set_button_pause()


	def _scaleButtonPressCb(self, widget, event):
		#self.button.set_sensitive(False)
		self.was_playing = self._gplay.is_playing()
		if self.was_playing:
			self._gplay.pause()

		# don't timeout-update position during seek
		if self.UPDATE_SCALE_ID != 0:
			gobject.source_remove(self.UPDATE_SCALE_ID)
			self.UPDATE_SCALE_ID = 0

		# make sure we get changed notifies
		if self.CHANGED_ID == 0:
			self.CHANGED_ID = self.hscale.connect('value-changed', self._scaleValueChangedCb)


	def _scaleButtonReleaseCb(self, widget, event):
		# see seek.cstop_seek
		widget.disconnect(self.CHANGED_ID)
		self.CHANGED_ID = 0

		#self.button.set_sensitive(True)
		if self.was_playing:
			self._gplay.play()

		if self.UPDATE_SCALE_ID != 0:
			pass
			#print('Had a previous update timeout id')
		else:
			self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb)


	def _scaleValueChangedCb(self, scale):
		real = long(scale.get_value() * self.p_duration / 100) # in ns
		self._gplay.seek(real)
		# allow for a preroll
		self._gplay.get_state(timeout=50*gst.MSECOND) # 50 ms


	def _updateScaleCb(self):
		self.p_position, self.p_duration = self._gplay.queryPosition()
		if self.p_position != gst.CLOCK_TIME_NONE:
			value = self.p_position * 100.0 / self.p_duration
			if value > 99:
				value = 99
			elif value < 0:
				value = 0

			self.adjustment.set_value(value)

			if self._gplay.is_playing() and self.p_position == self.p_duration:
				self._gplay.pause()
				self.set_button_play()

		return True