Changes

Jump to navigation Jump to search
m
no edit summary
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? ===
 
* [[Programming_the_camera]]
 
* [[Programming_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.
 +
 +
<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>
6

edits

Navigation menu