Development Team/Almanac/GStreamer: Difference between revisions
New page: {{Sugar Almanac}} === Where can I get additional resources and sample code on using the camera? === * Programming_the_camera |
mNo edit summary |
||
| (14 intermediate revisions by 8 users not shown) | |||
| Line 1: | Line 1: | ||
{{ | {{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> | |||