This commit is contained in:
Akkariin Meiko
2022-03-12 03:16:09 +08:00
Unverified
parent 12b76e0c7a
commit 27c4ec74a1
10075 changed files with 5122287 additions and 1 deletions
@@ -0,0 +1,40 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# audio-controller.py
# (c) 2005 Edward Hervey <edward at fluendo dot com>
# Test case for the GstController on sinesrc -> alsasink
# Inspired from ensonic's examples/controller/audio-controller.c
import pygst
pygst.require('0.10')
import gst
import time
def main():
pipeline = gst.Pipeline("audiocontroller")
src = gst.element_factory_make("audiotestsrc", "src")
sink = gst.element_factory_make("alsasink", "sink")
pipeline.add(src, sink)
src.link(sink)
control = gst.Controller(src, "freq", "volume")
control.set_interpolation_mode("volume", gst.INTERPOLATE_LINEAR)
control.set_interpolation_mode("freq", gst.INTERPOLATE_LINEAR)
control.set("volume", 0, 0.0)
control.set("volume", 2 * gst.SECOND, 1.0)
control.set("volume", 4 * gst.SECOND, 0.0)
control.set("volume", 6 * gst.SECOND, 1.0)
control.set("freq", 0, 440.0)
control.set("freq", 3 * gst.SECOND, 3000.0)
control.set("freq", 6 * gst.SECOND, 880.0)
pipeline.set_state(gst.STATE_PLAYING)
time.sleep(7)
if __name__ == "__main__":
main()
@@ -0,0 +1,192 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# audioconcat.py - Concatenates multiple audio files to single ogg/vorbis file
# Uses the gnonlin elements (http://gnonlin.sf.net/)
# Copyright (C) 2005 Edward Hervey <edward@fluendo.com>
# 2006 Jason Gerard DeRose <jderose@jasonderose.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
from gst.extend.discoverer import Discoverer
class AudioDec(gst.Bin):
'''Decodes audio file, outputs at specified caps'''
def __init__(self, location, caps):
gst.Bin.__init__(self)
# Create elements
src = gst.element_factory_make('filesrc')
dec = gst.element_factory_make('decodebin')
conv = gst.element_factory_make('audioconvert')
rsmpl = gst.element_factory_make('audioresample')
ident = gst.element_factory_make('identity')
# Set 'location' property on filesrc
src.set_property('location', location)
# Connect handler for 'new-decoded-pad' signal
dec.connect('new-decoded-pad', self.__on_new_decoded_pad)
# Add elements to bin
self.add(src, dec, conv, rsmpl, ident)
# Link *some* elements
# This is completed in self.__on_new_decoded_pad()
src.link(dec)
conv.link(rsmpl)
rsmpl.link(ident, caps)
# Reference used in self.__on_new_decoded_pad()
self.__apad = conv.get_pad('sink')
# Add ghost pad
self.add_pad(gst.GhostPad('src', ident.get_pad('src')))
def __on_new_decoded_pad(self, element, pad, last):
caps = pad.get_caps()
name = caps[0].get_name()
print '\n__on_new_decoded_pad:', name
if 'audio' in name:
if not self.__apad.is_linked(): # Only link once
pad.link(self.__apad)
class AudioConcat:
'''Concatenates multiple audio files to single ogg/vorbis file'''
caps = gst.caps_from_string('audio/x-raw-float, rate=44100, channels=2, endianness=1234, width=32')
def __init__(self, infiles, outfile):
# These are used in iteration through infiles
self.infiles = infiles
self.i = 0
self.start = 0L
# The pipeline
self.pipeline = gst.Pipeline()
# Create bus and connect 'eos' and 'error' handlers
self.bus = self.pipeline.get_bus()
self.bus.add_signal_watch()
self.bus.connect('message::eos', self.on_eos)
self.bus.connect('message::error', self.on_error)
# Create elements
self.comp = gst.element_factory_make('gnlcomposition')
self.enc = gst.element_factory_make('vorbisenc')
self.mux = gst.element_factory_make('oggmux')
self.sink = gst.element_factory_make('filesink')
# Connect handler for 'pad-added' signal
self.comp.connect('pad-added', self.on_pad_added)
# Set 'location' property on filesink
self.sink.set_property('location', outfile)
# Add elements to pipeline
self.pipeline.add(self.comp, self.enc, self.mux, self.sink)
# Link *some* elements
# This in completed in self.on_pad_added()
gst.element_link_many(self.enc, self.mux, self.sink)
# Reference used in self.on_pad_added()
self.apad = self.enc.get_pad('sink')
# The MainLoop
self.mainloop = gobject.MainLoop()
# Iterate through infiles
gobject.idle_add(self.discover)
self.mainloop.run()
def discover(self):
infile = self.infiles[self.i]
discoverer = Discoverer(infile)
discoverer.connect('discovered', self.on_discovered, infile)
discoverer.discover()
return False # Don't repeat idle call
def on_discovered(self, discoverer, ismedia, infile):
print '\non_discovered:', infile
discoverer.print_info()
if discoverer.is_audio:
dec = AudioDec(infile, self.caps)
src = gst.element_factory_make('gnlsource')
src.add(dec)
src.set_property('media-start', 0L)
src.set_property('media-duration', discoverer.audiolength)
src.set_property('start', self.start)
src.set_property('duration', discoverer.audiolength)
self.comp.add(src)
self.start += discoverer.audiolength
self.i += 1
if self.i < len(self.infiles):
gobject.idle_add(self.discover)
else:
if self.start > 0: # At least 1 infile is_audio and audiolength > 0
self.pipeline.set_state(gst.STATE_PLAYING)
else:
self.mainloop.quit()
def on_pad_added(self, element, pad):
caps = pad.get_caps()
name = caps[0].get_name()
print '\non_pad_added:', name
if name == 'audio/x-raw-float':
if not self.apad.is_linked(): # Only link once
pad.link(self.apad)
def on_eos(self, bus, msg):
print '\non_eos'
self.mainloop.quit()
def on_error(self, bus, msg):
error = msg.parse_error()
print '\non_error:', error[1]
self.mainloop.quit()
if __name__ == '__main__':
if len(sys.argv) >= 3:
AudioConcat(sys.argv[1:-1], sys.argv[-1])
else:
print 'Usage: %s <input_file(s)> <output_file>' % sys.argv[0]
print 'Example: %s song1.mp3 song2.ogg output.ogg' % sys.argv[0]
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2003 David I. Lehn
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: David I. Lehn <dlehn@users.sourceforge.net>
#
import pygtk
pygtk.require('2.0')
import sys
import time
import gobject
import gtk
import pygst
pygst.require('0.10')
import gst
class BPS(object):
def __init__(self):
self.buffers = 0
self.start = 0
def done(self):
end = time.time()
dt = end - self.start
bps = self.buffers/dt
spb = dt/self.buffers
print '\t%d buffers / %fs\t= %f bps\t= %f spb' % (self.buffers, dt, bps, spb)
def fakesrc(self, buffers):
src = gst.element_factory_make('fakesrc','src')
src.set_property('silent', 1)
src.set_property('num_buffers', buffers)
return src
def fakesink(self):
sink = gst.element_factory_make('fakesink','sink')
sink.set_property('silent', 1)
return sink
def build_pipeline(self, buffers):
pipeline = gst.Pipeline('pipeline')
src = self.fakesrc(buffers)
pipeline.add(src)
sink = self.fakesink()
pipeline.add(sink)
src.link(sink)
return pipeline
def idle(self, pipeline):
return pipeline.iterate()
def test(self):
self.bus = self.pipeline.get_bus()
self.start = time.time()
self.pipeline.set_state(gst.STATE_PLAYING)
while 1:
msg = self.bus.poll(gst.MESSAGE_EOS | gst.MESSAGE_ERROR, gst.SECOND)
if msg:
break
self.pipeline.set_state(gst.STATE_NULL)
self.done()
def run(self, buffers):
self.buffers = buffers
print '# Testing buffer processing rate for "fakesrc ! fakesink"'
print '# bps = buffers per second'
print '# spb = seconds per buffer'
self.pipeline = self.build_pipeline(buffers)
assert self.pipeline
self.test()
def main(args):
"GStreamer Buffers-Per-Second tester"
if len(args) < 2:
print 'usage: %s buffers' % args[0]
return 1
bps = BPS()
buffers = int(args[1])
if buffers < 1:
print 'buffers must be higher than 0'
return
bps.run(buffers)
if __name__ == '__main__':
sys.exit(main(sys.argv))
@@ -0,0 +1,138 @@
#!/usr/bin/env python
import sys
import traceback
from math import pi
import pygtk
pygtk.require ("2.0")
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import cairo
WIDTH, HEIGHT = 640, 480
FRAMES = 300
FRAMERATE = 15
class PyGstBufferDraw(gst.Element):
_sinkpadtemplate = gst.PadTemplate ("sink",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_from_string ("video/x-raw-rgb,bpp=32,depth=32,blue_mask=-16777216,green_mask=16711680, red_mask=65280, alpha_mask=255,width=[ 1, 2147483647 ],height=[ 1, 2147483647 ],framerate=[ 0/1, 2147483647/1 ]"))
_srcpadtemplate = gst.PadTemplate ("src",
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_from_string ("video/x-raw-rgb,bpp=32,depth=32,blue_mask=-16777216,green_mask=16711680, red_mask=65280, alpha_mask=255,width=[ 1, 2147483647 ],height=[ 1, 2147483647 ],framerate=[ 0/1, 2147483647/1 ]"))
def __init__(self):
gst.Element.__init__(self)
self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
self.sinkpad.set_chain_function(self.chainfunc)
self.sinkpad.set_event_function(self.eventfunc)
self.sinkpad.set_getcaps_function(gst.Pad.proxy_getcaps)
self.sinkpad.set_setcaps_function(gst.Pad.proxy_setcaps)
self.add_pad (self.sinkpad)
self.srcpad = gst.Pad(self._srcpadtemplate, "src")
self.srcpad.set_event_function(self.srceventfunc)
self.srcpad.set_query_function(self.srcqueryfunc)
self.srcpad.set_getcaps_function(gst.Pad.proxy_getcaps)
self.srcpad.set_setcaps_function(gst.Pad.proxy_setcaps)
self.add_pad (self.srcpad)
def chainfunc(self, pad, buffer):
try:
outbuf = buffer.copy_on_write ()
self.draw_on (outbuf)
return self.srcpad.push (outbuf)
except:
return GST_FLOW_ERROR
def eventfunc(self, pad, event):
return self.srcpad.push_event (event)
def srcqueryfunc (self, pad, query):
return self.sinkpad.query (query)
def srceventfunc (self, pad, event):
return self.sinkpad.push_event (event)
def draw_on (self, buf):
try:
caps = buf.get_caps()
width = caps[0]['width']
height = caps[0]['height']
framerate = caps[0]['framerate']
surface = cairo.ImageSurface.create_for_data (buf, cairo.FORMAT_ARGB32, width, height, 4 * width)
ctx = cairo.Context(surface)
except:
print "Failed to create cairo surface for buffer"
traceback.print_exc()
return
try:
center_x = width/4
center_y = 3*height/4
# draw a circle
radius = float (min (width, height)) * 0.25
ctx.set_source_rgba (0.0, 0.0, 0.0, 0.9)
ctx.move_to (center_x, center_y)
ctx.arc (center_x, center_y, radius, 0, 2.0*pi)
ctx.close_path()
ctx.fill()
ctx.set_source_rgba (1.0, 1.0, 1.0, 1.0)
ctx.set_font_size(0.3 * radius)
txt = "Hello World"
extents = ctx.text_extents (txt)
ctx.move_to(center_x - extents[2]/2, center_y + extents[3]/2)
ctx.text_path(txt)
ctx.fill()
except:
print "Failed cairo render"
traceback.print_exc()
gobject.type_register(PyGstBufferDraw)
pipe = gst.Pipeline()
vt = gst.element_factory_make ("videotestsrc")
cf = gst.element_factory_make ("capsfilter")
c1 = PyGstBufferDraw()
color = gst.element_factory_make ("ffmpegcolorspace")
scale = gst.element_factory_make ("videoscale")
q1 = gst.element_factory_make ("queue")
sink = gst.element_factory_make ("autovideosink")
caps = gst.caps_from_string ("video/x-raw-rgb,width=%d,height=%d,framerate=%d/1" % (WIDTH, HEIGHT, FRAMERATE))
cf.set_property ("caps", caps)
vt.set_property ("num-buffers", FRAMES)
pipe.add (vt, cf, c1, q1, color, scale, sink)
gst.element_link_many (vt, cf, c1, q1, color, scale, sink)
def on_eos (bus, msg):
mainloop.quit()
bus = pipe.get_bus()
bus.add_signal_watch()
bus.connect('message::eos', on_eos)
pipe.set_state (gst.STATE_PLAYING)
mainloop = gobject.MainLoop()
try:
mainloop.run()
except:
pass
pipe.set_state (gst.STATE_NULL)
pipe.get_state (gst.CLOCK_TIME_NONE)
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2002 David I. Lehn <dlehn@users.sourceforge.net>
# 2004 Johan Dahlin <johan@gnome.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: David I. Lehn <dlehn@users.sourceforge.net>
#
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
mainloop = gobject.MainLoop()
def on_eos(bus, msg):
mainloop.quit()
def filter(input, output):
"A GStreamer copy pipeline which can add arbitrary filters"
# create a new bin to hold the elements
bin = gst.parse_launch('filesrc name=source ! ' +
'progressreport ! ' +
# This 'statistics' element is depreciated in 0.10
#'statistics silent=false buffer-update-freq=1 ' +
#'update_on_eos=true ! ' +
'filesink name=sink')
filesrc = bin.get_by_name('source')
filesrc.set_property('location', input)
filesink = bin.get_by_name('sink')
filesink.set_property('location', output)
bus = bin.get_bus()
bus.add_signal_watch()
bus.connect('message::eos', on_eos)
# start playing
bin.set_state(gst.STATE_PLAYING)
try:
mainloop.run()
except KeyboardInterrupt:
pass
# stop the bin
bin.set_state(gst.STATE_NULL)
def main(args):
"A GStreamer based cp(1) with stats"
if len(args) != 3:
print 'usage: %s source dest' % (sys.argv[0])
return -1
return filter(args[1], args[2])
if __name__ == '__main__':
sys.exit(main(sys.argv))
@@ -0,0 +1,80 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2005 Thomas Vander Stichele
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
import gst
import time
import gobject
#gobject.threads_init() # so we can safely receive signals from threads
count = 0
def on_message_application(cutter, message, loop):
global count
s = message.structure
which = 'below'
if s['above']: which = 'above'
print "%s: %s threshold" % (gst.TIME_ARGS(s['timestamp']), which)
if s['above']: count += 1
if count > 2: loop.quit()
def main():
type = 'async'
loop = gobject.MainLoop()
pipeline = gst.Pipeline("cutter")
src = gst.element_factory_make("sinesrc", "src")
cutter = gst.element_factory_make("cutter")
cutter.set_property('threshold', 0.5)
sink = gst.element_factory_make("fakesink", "sink")
pipeline.add(src, cutter, sink)
src.link(cutter)
cutter.link(sink)
control = gst.Controller(src, "volume")
control.set_interpolation_mode("volume", gst.INTERPOLATE_LINEAR)
control.set("volume", 0, 0.0)
control.set("volume", 2 * gst.SECOND, 1.0)
control.set("volume", 4 * gst.SECOND, 0.0)
control.set("volume", 6 * gst.SECOND, 1.0)
control.set("volume", 8 * gst.SECOND, 0.0)
control.set("volume", 10 * gst.SECOND, 1.0)
bus = pipeline.get_bus()
if type == 'async':
bus.add_signal_watch()
bus.connect('message::element', on_message_application, loop)
else:
# FIXME: needs wrapping in gst-python
bus.set_sync_handler(bus.sync_signal_handler)
bus.connect('sync-message::element', on_message_application, loop)
pipeline.set_state(gst.STATE_PLAYING)
loop.run()
pipeline.set_state(gst.STATE_NULL)
if __name__ == "__main__":
main()
@@ -0,0 +1,59 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2005 Fluendo S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: Andy Wingo <wingo@pobox.com>
import gtk
from gtk import gdk
import gobject
import pygst
pygst.require('0.10')
import gst
class DebugSlider(gtk.HScale):
def __init__(self):
adj = gtk.Adjustment(int(gst.debug_get_default_threshold()),
0, 5, 1, 0, 0)
gtk.HScale.__init__(self, adj)
self.set_digits(0)
self.set_draw_value(True)
self.set_value_pos(gtk.POS_TOP)
def value_changed(self):
newlevel = int(self.get_adjustment().get_value())
gst.debug_set_default_threshold(newlevel)
self.connect('value-changed', value_changed)
if __name__ == '__main__':
p = gst.parse_launch('fakesrc ! fakesink')
p.set_state(gst.STATE_PLAYING)
w = gtk.Window()
s = DebugSlider()
w.add(s)
s.show()
w.set_default_size(200, 40)
w.show()
w.connect('delete-event', lambda *args: gtk.main_quit())
gtk.main()
@@ -0,0 +1,109 @@
#!/usr/bin/env python
# decodebin.py - Audio autopluging example using 'decodebin' element
# Copyright (C) 2006 Jason Gerard DeRose <jderose@jasonderose.org>
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
class Decodebin:
def __init__(self, location):
# The pipeline
self.pipeline = gst.Pipeline()
# Create bus and connect several handlers
self.bus = self.pipeline.get_bus()
self.bus.add_signal_watch()
self.bus.connect('message::eos', self.on_eos)
self.bus.connect('message::tag', self.on_tag)
self.bus.connect('message::error', self.on_error)
# Create elements
self.src = gst.element_factory_make('filesrc')
self.dec = gst.element_factory_make('decodebin')
self.conv = gst.element_factory_make('audioconvert')
self.rsmpl = gst.element_factory_make('audioresample')
self.sink = gst.element_factory_make('alsasink')
# Set 'location' property on filesrc
self.src.set_property('location', location)
# Connect handler for 'new-decoded-pad' signal
self.dec.connect('new-decoded-pad', self.on_new_decoded_pad)
# Add elements to pipeline
self.pipeline.add(self.src, self.dec, self.conv, self.rsmpl, self.sink)
# Link *some* elements
# This is completed in self.on_new_decoded_pad()
self.src.link(self.dec)
gst.element_link_many(self.conv, self.rsmpl, self.sink)
# Reference used in self.on_new_decoded_pad()
self.apad = self.conv.get_pad('sink')
# The MainLoop
self.mainloop = gobject.MainLoop()
# And off we go!
self.pipeline.set_state(gst.STATE_PLAYING)
self.mainloop.run()
def on_new_decoded_pad(self, element, pad, last):
caps = pad.get_caps()
name = caps[0].get_name()
print 'on_new_decoded_pad:', name
if name == 'audio/x-raw-float' or name == 'audio/x-raw-int':
if not self.apad.is_linked(): # Only link once
pad.link(self.apad)
def on_eos(self, bus, msg):
print 'on_eos'
self.mainloop.quit()
def on_tag(self, bus, msg):
taglist = msg.parse_tag()
print 'on_tag:'
for key in taglist.keys():
print '\t%s = %s' % (key, taglist[key])
def on_error(self, bus, msg):
error = msg.parse_error()
print 'on_error:', error[1]
self.mainloop.quit()
if __name__ == '__main__':
if len(sys.argv) == 2:
Decodebin(sys.argv[1])
else:
print 'Usage: %s /path/to/media/file' % sys.argv[0]
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2002 David I. Lehn
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: David I. Lehn <dlehn@users.sourceforge.net>
#
import sys
import pygst
pygst.require('0.10')
import gst
def handoff_cb(sender, *args):
print sender.get_name(), args
def main(args):
# create a new bin to hold the elements
#gst_debug_set_categories(-1)
bin = gst.parse_launch('fakesrc name=source silent=1 num-buffers=10 signal-handoffs=true ! ' +
'fakesink name=sink silent=1 signal-handoffs=true')
source = bin.get_by_name('source')
source.connect('handoff', handoff_cb)
source.get_pad("src").connect("have-data", handoff_cb)
sink = bin.get_by_name('sink')
sink.connect('handoff', handoff_cb)
sink.get_pad("sink").connect('have-data', handoff_cb)
print source, sink
bus = bin.get_bus()
res = bin.set_state(gst.STATE_PLAYING);
assert res
while 1:
msg = bus.poll(gst.MESSAGE_EOS | gst.MESSAGE_ERROR, gst.SECOND)
if msg:
break
res = bin.set_state(gst.STATE_NULL)
assert res
if __name__ == '__main__':
sys.exit(main(sys.argv))
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# GStreamer python bindings
# Copyright (C) 2002 David I. Lehn <dlehn@users.sourceforge.net>
# 2004 Johan Dahlin <johan@gnome.org>
#
# filesrc.py: implements a file source element completely in python
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
import sys
import gobject; gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
class FileSource(gst.BaseSrc):
__gsttemplates__ = (
gst.PadTemplate("src",
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_new_any()),
)
blocksize = 4096
fd = None
def __init__(self, name):
self.__gobject_init__()
self.curoffset = 0
self.set_name(name)
def set_property(self, name, value):
if name == 'location':
self.fd = open(value, 'r')
def do_create(self, offset, size):
if offset != self.curoffset:
self.fd.seek(offset, 0)
data = self.fd.read(self.blocksize)
if data:
self.curoffset += len(data)
return gst.FLOW_OK, gst.Buffer(data)
else:
return gst.FLOW_UNEXPECTED, None
gobject.type_register(FileSource)
def main(args):
if len(args) != 3:
print 'Usage: %s input output' % (args[0])
return -1
bin = gst.Pipeline('pipeline')
filesrc = FileSource('filesource')
assert filesrc
filesrc.set_property('location', args[1])
filesink = gst.element_factory_make('filesink', 'sink')
filesink.set_property('location', args[2])
bin.add(filesrc, filesink)
gst.element_link_many(filesrc, filesink)
bin.set_state(gst.STATE_PLAYING);
mainloop = gobject.MainLoop()
def bus_event(bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
mainloop.quit()
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
mainloop.quit()
return True
bin.get_bus().add_watch(bus_event)
mainloop.run()
bin.set_state(gst.STATE_NULL)
if __name__ == '__main__':
sys.exit(main(sys.argv))
@@ -0,0 +1,239 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2005 Fluendo S.L.
# Originally from the Flumotion streaming server.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# Author: Zaheer Merali <zaheermerali at gmail dot com>
import gtk
from gtk import gdk
import gobject
# this VUMeter respects IEC standard
# BS 6840-18:1996/IEC-268-18
# and is inspired by JACK's meterbridge dpm_meters.c
class FVUMeter(gtk.DrawingArea):
__gsignals__ = { 'expose-event' : 'override',
'size-allocate': 'override',
'size-request': 'override',
'realize' : 'override'
}
__gproperties__ = {
'peak' : (gobject.TYPE_FLOAT,
'peak volume level',
'peak volume level in dB',
-90.0,
0,
-90.0,
gobject.PARAM_READWRITE),
'decay' : (gobject.TYPE_FLOAT,
'decay volume level',
'decay volume level in dB',
-90.0,
0,
-90.0,
gobject.PARAM_READWRITE),
'orange-threshold': (gobject.TYPE_FLOAT,
'threshold for orange',
'threshold for orange use in dB',
-90.0,
0,
-10.0,
gobject.PARAM_READWRITE),
'red-threshold': (gobject.TYPE_FLOAT,
'threshold for red',
'threshold for red use in dB',
-90.0,
0,
-1.0,
gobject.PARAM_READWRITE)
}
green_gc = None
orange_gc = None
red_gc = None
yellow_gc = None
topborder = 7
peaklevel = -90.0
decaylevel = -90.0
orange_threshold = -10.0
red_threshold = -1.0
bottomborder = 25
leftborder = 15
rightborder = 65
# Returns the meter deflection percentage given a db value
def iec_scale(self, db):
pct = 0.0
if db < -70.0:
pct = 0.0
elif db < -60.0:
pct = (db + 70.0) * 0.25
elif db < -50.0:
pct = (db + 60.0) * 0.5 + 2.5
elif db < -40.0:
pct = (db + 50.0) * 0.75 + 7.5
elif db < -30.0:
pct = (db + 40.0) * 1.5 + 15.0
elif db < -20.0:
pct = (db + 30.0) * 2.0 + 30.0
elif db < 0.0:
pct = (db + 20.0) * 2.5 + 50.0
else:
pct = 100.0
return pct
def do_get_property(self, property):
if property.name == 'peak':
return self.peaklevel
elif property.name == 'decay':
return self.decaylevel
elif property.name == 'orange-threshold':
return self.orange_threshold
elif property.name == 'red-threshold':
return self.red_threshold
else:
raise AttributeError, 'unknown property %s' % property.name
def do_set_property(self, property, value):
if property.name == 'peak':
self.peaklevel = value
elif property.name == 'decay':
self.decaylevel = value
elif property.name == 'orange-threshold':
self.orange_threshold = value
elif property.name == 'red-threshold':
self.red_threshold = value
else:
raise AttributeError, 'unknown property %s' % property.name
self.queue_draw()
def do_size_request(self, requisition):
requisition.width = 250
requisition.height = 50
def do_size_allocate(self, allocation):
self.allocation = allocation
if self.flags() & gtk.REALIZED:
self.window.move_resize(*allocation)
def do_realize(self):
self.set_flags(self.flags() | gtk.REALIZED)
self.window = gdk.Window(self.get_parent_window(),
width=self.allocation.width,
height=self.allocation.height,
window_type=gdk.WINDOW_CHILD,
wclass=gdk.INPUT_OUTPUT,
event_mask=self.get_events() | gdk.EXPOSURE_MASK)
colormap = gtk.gdk.colormap_get_system()
green = colormap.alloc_color(0, 65535, 0)
orange = colormap.alloc_color(65535, 32768, 0)
red = colormap.alloc_color(65535, 0, 0)
yellow = colormap.alloc_color(65535, 65535, 0)
self.green_gc = gdk.GC(self.window, foreground=green)
self.orange_gc = gdk.GC(self.window, foreground=orange)
self.red_gc = gdk.GC(self.window, foreground=red)
self.yellow_gc = gdk.GC(self.window, foreground=yellow)
self.window.set_user_data(self)
self.style.attach(self.window)
self.style.set_background(self.window, gtk.STATE_NORMAL)
def do_expose_event(self, event):
self.chain(event)
x, y, w, h = self.allocation
vumeter_width = w - (self.leftborder + self.rightborder)
vumeter_height = h - (self.topborder + self.bottomborder)
self.window.draw_rectangle(self.style.black_gc, True,
self.leftborder, self.topborder,
vumeter_width,
vumeter_height)
# draw peak level
# 0 maps to width of 0, full scale maps to total width
peaklevelpct = self.iec_scale(self.peaklevel)
peakwidth = int(vumeter_width * (peaklevelpct / 100))
draw_gc = self.green_gc
if self.peaklevel >= self.orange_threshold:
draw_gc = self.orange_gc
if self.peaklevel >= self.red_threshold:
draw_gc = self.red_gc
if peakwidth > 0:
self.window.draw_rectangle(draw_gc, True,
self.leftborder, self.topborder,
peakwidth, vumeter_height)
# draw yellow decay level
if self.decaylevel > -90.0:
decaylevelpct = self.iec_scale(self.decaylevel)
decaywidth = int(vumeter_width * (decaylevelpct / 100))
# cheat the geometry by drawing 0% level at pixel 0,
# which is same position as just above 0%
if decaywidth == 0:
decaywidth = 1
self.window.draw_line(self.yellow_gc,
self.leftborder + decaywidth - 1,
self.topborder,
self.leftborder + decaywidth - 1,
self.topborder + vumeter_height - 1)
# draw tick marks
scalers = [
('-90', 0.0),
('-40', 0.15),
('-30', 0.30),
('-20', 0.50),
('-10', 0.75),
( '-5', 0.875),
( '0', 1.0),
]
for level, scale in scalers:
# tick mark, 6 pixels high
# we cheat again here by putting the 0 at the first pixel
self.window.draw_line(self.style.black_gc,
self.leftborder + int(scale * (vumeter_width - 1)),
h - self.bottomborder,
self.leftborder + int(scale * (vumeter_width - 1)),
h - self.bottomborder + 5)
# tick label
layout = self.create_pango_layout(level)
layout_width, layout_height = layout.get_pixel_size()
self.window.draw_layout(self.style.black_gc,
self.leftborder + int(scale * vumeter_width)
- int(layout_width / 2),
h - self.bottomborder + 7, layout)
# draw the peak level to the right
layout = self.create_pango_layout("%.2fdB" % self.peaklevel)
layout_width, layout_height = layout.get_pixel_size()
self.window.draw_layout(self.style.black_gc,
self.leftborder + vumeter_width + 5,
self.topborder + int(vumeter_height / 2 - layout_height / 2),
layout)
gobject.type_register(FVUMeter)
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env python
# gst-python
# Copyright (C) 2006 Andy Wingo <wingo at pobox.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
import os
import sys
import pygtk
pygtk.require('2.0')
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
from gst.extend import discoverer
def fail(path):
print "error: %r does not appear to be a media file" % path
sys.exit(1)
def succeed(d):
def pp(prop, val):
print '%s: %s' % (prop, val)
pp('media type', d.mimetype)
pp('has video', d.is_video)
if d.is_video:
pp('video caps', d.videocaps)
pp('video width (pixels)', d.videowidth)
pp('video height (pixels)', d.videoheight)
pp('video length (hh:mm:ss)', gst.TIME_ARGS(d.videolength))
pp('framerate (fps)', '%s/%s' % (d.videorate.num, d.videorate.denom))
pp('has audio', d.is_audio)
if d.is_audio:
pp('audio caps', d.audiocaps)
pp('audio format', d.audiofloat and 'floating-point' or 'integer')
pp('sample rate (Hz)', d.audiorate)
pp('sample width (bits)', d.audiowidth)
pp('sample depth (bits)', d.audiodepth)
pp('audio length (hh:mm:ss)', gst.TIME_ARGS(d.audiolength))
pp('audio channels', d.audiochannels)
sys.exit(0)
def discover(path):
def discovered(d, is_media):
if is_media:
succeed(d)
else:
fail(path)
d = discoverer.Discoverer(path)
d.connect('discovered', discovered)
d.discover()
gobject.MainLoop().run()
def usage():
print >>sys.stderr, "usage: gst-discover PATH-TO-MEDIA-FILE"
sys.exit(1)
def main(argv):
if len(argv) != 2:
usage()
path = argv.pop()
if not os.path.isfile(path):
print >>sys.stderr, "error: file %r does not exist" % path
usage()
return discover(path)
if __name__ == '__main__':
sys.exit(main(sys.argv))
@@ -0,0 +1,73 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gstfile.py
# (c) 2005 Edward Hervey <edward at fluendo dot com>
# Discovers and prints out multimedia information of files
# This example shows how to use gst-python:
# _ in an object-oriented way (Discoverer class)
# _ subclassing a gst.Pipeline
# _ and overidding existing methods (do_iterate())
import os
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
from gst.extend.discoverer import Discoverer
class GstFile:
"""
Analyses one or more files and prints out the multimedia information of
each file.
"""
def __init__(self, files):
self.files = files
self.mainloop = gobject.MainLoop()
self.current = None
def run(self):
gobject.idle_add(self._discover_one)
self.mainloop.run()
def _discovered(self, discoverer, ismedia):
discoverer.print_info()
self.current = None
if len(self.files):
print "\n"
gobject.idle_add(self._discover_one)
def _discover_one(self):
if not len(self.files):
gobject.idle_add(self.mainloop.quit)
return False
filename = self.files.pop(0)
if not os.path.isfile(filename):
gobject.idle_add(self._discover_one)
return False
print "Running on", filename
# create a discoverer for that file
self.current = Discoverer(filename)
# connect a callback on the 'discovered' signal
self.current.connect('discovered', self._discovered)
# start the discovery
self.current.discover()
return False
def main(args):
if len(args) < 2:
print 'usage: %s files...' % args[0]
return 2
gstfile = GstFile(args[1:])
gstfile.run()
if __name__ == '__main__':
sys.exit(main(sys.argv))
@@ -0,0 +1,54 @@
#!/usr/bin/env python
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
def bus_call(bus, message, loop):
t = message.type
if t == gst.MESSAGE_EOS:
sys.stout.write("End-of-stream\n")
loop.quit()
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
sys.stderr.write("Error: %s: %s\n" % err, debug)
loop.quit()
return True
def main(args):
if len(args) != 2:
sys.stderr.write("usage: %s <media file or uri>\n" % args[0])
sys.exit(1)
playbin = gst.element_factory_make("playbin2", None)
if not playbin:
sys.stderr.write("'playbin2' gstreamer plugin missing\n")
sys.exit(1)
# take the commandline argument and ensure that it is a uri
if gst.uri_is_valid(args[1]):
uri = args[1]
else:
uri = gst.filename_to_uri(args[1])
playbin.set_property('uri', uri)
# create and event loop and feed gstreamer bus mesages to it
loop = gobject.MainLoop()
bus = playbin.get_bus()
bus.add_watch(bus_call, loop)
# start play back and listed to events
playbin.set_state(gst.STATE_PLAYING)
loop.run()
# cleanup
playbin.set_state(gst.STATE_NULL)
if __name__ == '__main__':
sys.exit(main(sys.argv))
@@ -0,0 +1,101 @@
import gobject
gobject.threads_init()
import gtk
gtk.gdk.threads_init()
import hildon
import gst
import sys
# VideoWidget taken from play.py in gst-python examples
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
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)
class MaemoGstView:
def __init__(self):
# hildon has one program instance per app, so get instance
self.p = hildon.Program.get_instance()
# set name of application: this shows in titlebar
gtk.set_application_name("Maemo GStreamer VideoTest")
# stackable window in case we want more windows in future in app
self.w = hildon.StackableWindow()
box = gtk.VBox()
self.video_widget = VideoWidget()
# video widget we want to expand to size
box.pack_start(self.video_widget, True, True, 0)
# a button finger height to play/pause
self.button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
hildon.BUTTON_ARRANGEMENT_VERTICAL, title="Pause")
self.button.connect_after("clicked", self.on_button_clicked)
# don't want button to expand or fill, just stay finger height
box.pack_start(self.button, False, False, 0)
self.w.add(box)
self.w.connect("delete-event", gtk.main_quit)
self.p.add_window(self.w)
self.w.show_all()
self.start_streaming()
def start_streaming(self):
# we use ximagesink solely for screenshotting ability
# less cpu usage would happen with videotestsrc ! xvimagesink
self.pipeline = \
gst.parse_launch("videotestsrc ! videoscale ! ximagesink")
bus = self.pipeline.get_bus()
# need to connect to sync message handler so we get the sink to be
# embedded at the right time and not have a temporary new window
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect("sync-message::element", self.on_sync_message)
bus.connect("message", self.on_message)
self.pipeline.set_state(gst.STATE_PLAYING)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# all this is needed to sync with the X server before giving the
# x id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.video_widget.set_sink(message.src)
message.src.set_property("force-aspect-ratio", True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
if message.type == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
hildon.hildon_banner_show_information(self.w, '',
"Error: %s" % err)
def on_button_clicked(self, widget):
success, state, pending = self.pipeline.get_state(1)
# do not listen if in middle of state change
if not pending:
if state == gst.STATE_PLAYING:
self.pipeline.set_state(gst.STATE_PAUSED)
self.button.set_label("Play")
else:
self.pipeline.set_state(gst.STATE_PLAYING)
self.button.set_label("Pause")
def main():
view = MaemoGstView()
gtk.main()
if __name__ == '__main__':
sys.exit(main())
@@ -0,0 +1,31 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import sys
import gst
import gst.interfaces
pipeline = "alsasrc"
if sys.argv[1:]:
pipeline = " ".join(sys.argv[1:])
a = gst.element_factory_make(pipeline)
print dir(a)
res = a.set_state(gst.STATE_PAUSED)
if res != gst.STATE_CHANGE_SUCCESS:
print "Could not set pipeline %s to PAUSED" % pipeline
print "Inputs:"
for t in a.list_tracks():
if t.flags & gst.interfaces.MIXER_TRACK_INPUT:
sys.stdout.write(t.label)
sys.stdout.write(': %d - %d' % (t.min_volume, t.max_volume))
volumes = a.get_volume(t)
sys.stdout.write(': %r' % (volumes, ))
if t.props.num_channels > 0:
a.set_volume(t, volumes=volumes)
if t.flags & gst.interfaces.MIXER_TRACK_RECORD:
sys.stdout.write(' (selected)')
sys.stdout.write('\n')
@@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import sys
import pygtk
pygtk.require('2.0')
from gobject.option import OptionParser, OptionGroup
import pygst
pygst.require('0.10')
import gstoption
def main(args):
parser = OptionParser()
group = OptionGroup('flumotion', 'Flumotion options',
option_list=[])
group.add_option('-v', '--verbose',
action="store_true", dest="verbose",
help="be verbose")
group.add_option('', '--version',
action="store_true", dest="version",
default=False,
help="show version information")
parser.add_option_group(group)
parser.add_option_group(gstoption.get_group())
options, args = parser.parse_args(args)
if options.verbose:
print 'Verbose mode'
import gst
if options.version:
print sys.version, gst.version
if __name__ == '__main__':
sys.exit(main(sys.argv))
+263
View File
@@ -0,0 +1,263 @@
#!/usr/bin/env python
#
# gst-python
# Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# A test more of gst-plugins than of gst-python.
import sys
import pygtk
pygtk.require('2.0')
import gtk
import gtk.gdk
import pango
import gobject
import pygst
pygst.require('0.10')
import gst
import debugslider
data = (('Video capture via V4L',
'v4lsrc name=source \n'
' ! videorate \n'
' ! ffmpegcolorspace ! autovideosink'),
('Video capture via V4L, fixed frame rate',
'v4lsrc name=source autoprobe=false autoprobe-fps=false \n'
' ! video/x-raw-yuv,format=(fourcc)I420,framerate=(double)7.5 \n'
' ! videorate \n'
' ! ffmpegcolorspace \n'
' ! autovideosink'),
('Sound capture',
'gconfaudiosrc\n'
' ! audio/x-raw-int,rate=22050,depth=16,channels=1,width=16,signed=(boolean)TRUE,endianness=(int)BYTE_ORDER\n'
' ! level message=true\n'
' ! fakesink'),
('Streaming Ogg/Theora+Vorbis playback, tee to disk',
'gnomevfssrc location=http://gstreamer.freedesktop.org/media/small/cooldance.ogg \n'
' ! tee name=tee \n'
' tee. ! oggdemux name=demux \n'
' demux. ! queue ! theoradec ! ffmpegcolorspace ! autovideosink \n'
' demux. ! queue ! vorbisdec ! audioconvert ! autoaudiosink \n'
' tee. ! queue ! filesink location=/tmp/cooldance.ogg'),
('Video test, YUV format',
'videotestsrc \n'
' ! video/x-raw-yuv,format=(fourcc)I420 \n'
' ! ffmpegcolorspace ! autovideosink'),
('Video test, RGB format',
'videotestsrc \n'
' ! video/x-raw-rgb,red_mask=0xff00 \n'
' ! ffmpegcolorspace \n'
' ! autovideosink'),
('Software scaling',
'videotestsrc \n'
' ! video/x-raw-rgb,height=200,width=320 \n'
' ! videoscale method=2 \n'
' ! ffmpegcolorspace ! autovideosink'),
('Reencode Vorbis to mulaw, play',
'filesrc location=/tmp/cooldance.ogg \n'
' ! oggdemux \n'
' ! vorbisdec ! audioconvert \n'
' ! mulawenc ! mulawdec ! autoaudiosink'),
('Capture DV via firewire, transcode into Ogg',
'dv1394src \n'
' ! dvdemux name=demux \n'
' ! queue \n'
' ! video/x-dv,systemstream=(boolean)false \n'
' ! dvdec drop-factor=2 \n'
' ! videorate \n'
' ! videoscale \n'
' ! video/x-raw-yuv,width=360,height=288 \n'
' ! videoscale \n'
' ! video/x-raw-yuv,width=240,height=192,framerate=10.0,format=(fourcc)YUY2 \n'
' ! ffmpegcolorspace \n'
' ! theoraenc \n'
' ! oggmux name=mux \n'
' ! filesink location=/tmp/dv.ogg \n'
' \n'
' demux. \n'
' ! audio/x-raw-int \n'
' ! queue \n'
' ! audioconvert \n'
' ! vorbisenc \n'
' ! mux.'))
def escape(s, chars, escaper='\\'):
for c in chars:
s = s.replace(c, '%s%s' % (escaper, c))
return s
def make_model():
m = gtk.ListStore(str, str)
for pair in data:
i = m.append()
m.set_value(i, 0, pair[0])
m.set_value(i, 1, pair[1])
return m
class Window(gtk.Window):
def __init__(self):
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
self.playing = False
self.selected_pipe = None
self.pipeline = None
self.prepare_ui()
def prepare_ui(self):
self.set_default_size(300,400)
self.set_title('GStreamer Pipeline Tester')
self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
self.connect('delete-event', lambda *x: gtk.main_quit())
self.set_border_width(18)
b = gtk.VBox(False, 12)
b.show()
self.add(b)
l = gtk.Label()
l.set_markup('<big><b>GStreamer Pipeline Tester</b></big>')
l.show()
b.pack_start(l, False, False, 6)
l = gtk.Label('Choose a pipeline below to run.')
l.show()
b.pack_start(l, False, False, 0)
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
sw.set_shadow_type(gtk.SHADOW_IN)
sw.show()
b.pack_start(sw, True, True, 6)
tv = gtk.TreeView(make_model())
tv.set_property('can-default', False)
r = gtk.CellRendererText()
r.set_property('xalign', 0.5)
c = gtk.TreeViewColumn('System', r, text=0)
tv.append_column(c)
tv.set_headers_visible(False)
tv.show()
sw.add(tv)
ds = debugslider.DebugSlider()
ds.show()
b.pack_start(ds, False, False, 0)
l = gtk.Label()
l.set_selectable(True)
l.show()
b.pack_start(l, False, False, 0)
bb = gtk.HButtonBox()
bb.set_layout(gtk.BUTTONBOX_SPREAD)
bb.show()
b.pack_start(bb, False, False, 0)
bu = gtk.Button(stock=gtk.STOCK_MEDIA_PLAY)
bu.set_property('can-default', True)
bu.set_focus_on_click(False)
bu.show()
bb.pack_start(bu, True, False, 0)
bu.set_property('has-default', True)
self.button = bu
def on_changed(s):
m, i = s.get_selected()
if m:
self.selected_pipe = m.get_value(i, 1)
pasteable = escape(self.selected_pipe, '\n)(')
l.set_markup('<small><tt>%s</tt></small>' % pasteable)
else:
self.selected_pipe = None
l.set_markup('')
tv.get_selection().connect('changed', on_changed)
tv.connect('row-activated', lambda *x: self.play_toggled())
bu.connect('clicked', lambda *x: self.play_toggled())
def error(self, message, secondary=None):
m = gtk.MessageDialog(self,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK,
message)
if secondary:
m.format_secondary_text(secondary)
m.run()
m.destroy()
self.stop()
def on_message(self, bus, message):
t = message.type
print message
if t == gst.MESSAGE_STATE_CHANGED:
pass
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
self.error("%s" % err, debug)
elif t == gst.MESSAGE_EOS:
self.play_toggled()
else:
print '%s: %s:' % (message.src.get_path_string(),
message.type.value_nicks[1])
if message.structure:
print ' %s' % message.structure.to_string()
else:
print ' (no structure)'
return True
def play(self):
pipestr = self.selected_pipe
try:
self.set_sensitive(False)
pipeline = gst.parse_launch(pipestr)
self.set_sensitive(True)
except gobject.GError, e:
self.set_sensitive(True)
self.error('Could not create pipeline', str(e))
return False
bus = pipeline.get_bus()
bus.add_signal_watch()
watch_id = bus.connect('message', self.on_message)
self.pipeline = pipeline
self.watch_id = watch_id
pipeline.set_state(gst.STATE_PLAYING)
def stop(self):
bus = self.pipeline.get_bus()
bus.disconnect(self.watch_id)
bus.remove_signal_watch()
self.pipeline.set_state(gst.STATE_NULL)
self.pipeline = None
del self.watch_id
def play_toggled(self):
if self.playing:
self.stop()
self.button.set_label(gtk.STOCK_MEDIA_PLAY)
self.playing = False
else:
self.play()
self.playing = True
self.button.set_label(gtk.STOCK_MEDIA_STOP)
if __name__ == '__main__':
w = Window()
w.show()
gtk.main()
+299
View File
@@ -0,0 +1,299 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import pygtk
pygtk.require('2.0')
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gtk
gtk.gdk.threads_init()
class GstPlayer:
def __init__(self, videowidget):
self.playing = False
self.player = gst.element_factory_make("playbin", "player")
self.videowidget = videowidget
self.on_eos = False
bus = self.player.get_bus()
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect('sync-message::element', self.on_sync_message)
bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# Sync with the X server before giving the X-id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.videowidget.set_sink(message.src)
message.src.set_property('force-aspect-ratio', True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
if self.on_eos:
self.on_eos()
self.playing = False
elif t == gst.MESSAGE_EOS:
if self.on_eos:
self.on_eos()
self.playing = False
def set_location(self, location):
self.player.set_property('uri', location)
def query_position(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, location):
"""
@param location: time to seek to, in nanoseconds
"""
gst.debug("seeking to %r" % location)
event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE,
gst.SEEK_TYPE_SET, location,
gst.SEEK_TYPE_NONE, 0)
res = self.player.send_event(event)
if res:
gst.info("setting new stream time to 0")
self.player.set_new_stream_time(0L)
else:
gst.error("seek to %r failed" % location)
def pause(self):
gst.info("pausing player")
self.player.set_state(gst.STATE_PAUSED)
self.playing = False
def play(self):
gst.info("playing player")
self.player.set_state(gst.STATE_PLAYING)
self.playing = True
def stop(self):
self.player.set_state(gst.STATE_NULL)
gst.info("stopped player")
def get_state(self, timeout=1):
return self.player.get_state(timeout=timeout)
def is_playing(self):
return self.playing
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
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)
class PlayerWindow(gtk.Window):
UPDATE_INTERVAL = 500
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(410, 325)
self.create_ui()
self.player = GstPlayer(self.videowidget)
def on_eos():
self.player.seek(0L)
self.play_toggled()
self.player.on_eos = lambda *x: on_eos()
self.update_id = -1
self.changed_id = -1
self.seek_timeout_id = -1
self.p_position = gst.CLOCK_TIME_NONE
self.p_duration = gst.CLOCK_TIME_NONE
def on_delete_event():
self.player.stop()
gtk.main_quit()
self.connect('delete-event', lambda *x: on_delete_event())
def load_file(self, location):
self.player.set_location(location)
def create_ui(self):
vbox = gtk.VBox()
self.add(vbox)
self.videowidget = VideoWidget()
vbox.pack_start(self.videowidget)
hbox = gtk.HBox()
vbox.pack_start(hbox, fill=False, expand=False)
self.pause_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE,
gtk.ICON_SIZE_BUTTON)
self.pause_image.show()
self.play_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY,
gtk.ICON_SIZE_BUTTON)
self.play_image.show()
self.button = button = gtk.Button()
button.add(self.play_image)
button.set_property('can-default', True)
button.set_focus_on_click(False)
button.show()
hbox.pack_start(button, False)
button.set_property('has-default', True)
button.connect('clicked', lambda *args: self.play_toggled())
self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
hscale = gtk.HScale(self.adjustment)
hscale.set_digits(2)
hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
hscale.connect('button-press-event', self.scale_button_press_cb)
hscale.connect('button-release-event', self.scale_button_release_cb)
hscale.connect('format-value', self.scale_format_value_cb)
hbox.pack_start(hscale)
self.hscale = hscale
self.videowidget.connect_after('realize',
lambda *x: self.play_toggled())
def play_toggled(self):
self.button.remove(self.button.child)
if self.player.is_playing():
self.player.pause()
self.button.add(self.play_image)
else:
self.player.play()
if self.update_id == -1:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
self.button.add(self.pause_image)
def scale_format_value_cb(self, scale, value):
if self.p_duration == -1:
real = 0
else:
real = value * self.p_duration / 100
seconds = real / gst.SECOND
return "%02d:%02d" % (seconds / 60, seconds % 60)
def scale_button_press_cb(self, widget, event):
# see seek.c:start_seek
gst.debug('starting seek')
self.button.set_sensitive(False)
self.was_playing = self.player.is_playing()
if self.was_playing:
self.player.pause()
# don't timeout-update position during seek
if self.update_id != -1:
gobject.source_remove(self.update_id)
self.update_id = -1
# make sure we get changed notifies
if self.changed_id == -1:
self.changed_id = self.hscale.connect('value-changed',
self.scale_value_changed_cb)
def scale_value_changed_cb(self, scale):
# see seek.c:seek_cb
real = long(scale.get_value() * self.p_duration / 100) # in ns
gst.debug('value changed, perform seek to %r' % real)
self.player.seek(real)
# allow for a preroll
self.player.get_state(timeout=50*gst.MSECOND) # 50 ms
def scale_button_release_cb(self, widget, event):
# see seek.cstop_seek
widget.disconnect(self.changed_id)
self.changed_id = -1
self.button.set_sensitive(True)
if self.seek_timeout_id != -1:
gobject.source_remove(self.seek_timeout_id)
self.seek_timeout_id = -1
else:
gst.debug('released slider, setting back to playing')
if self.was_playing:
self.player.play()
if self.update_id != -1:
self.error('Had a previous update timeout id')
else:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
def update_scale_cb(self):
self.p_position, self.p_duration = self.player.query_position()
if self.p_position != gst.CLOCK_TIME_NONE:
value = self.p_position * 100.0 / self.p_duration
self.adjustment.set_value(value)
return True
def main(args):
def usage():
sys.stderr.write("usage: %s URI-OF-MEDIA-FILE\n" % args[0])
sys.exit(1)
# Need to register our derived widget types for implicit event
# handlers to get called.
gobject.type_register(PlayerWindow)
gobject.type_register(VideoWidget)
w = PlayerWindow()
if len(args) != 2:
usage()
if not gst.uri_is_valid(args[1]):
sys.stderr.write("Error: Invalid URI: %s\n" % args[1])
sys.exit(1)
w.load_file(args[1])
w.show_all()
gtk.main()
if __name__ == '__main__':
sys.exit(main(sys.argv))
@@ -0,0 +1,68 @@
#!/usr/bin/env python
import pygtk
pygtk.require ("2.0")
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
class PyIdentity(gst.Element):
_sinkpadtemplate = gst.PadTemplate ("sink",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_new_any())
_srcpadtemplate = gst.PadTemplate ("src",
gst.PAD_SRC,
gst.PAD_ALWAYS,
gst.caps_new_any())
def __init__(self):
gst.Element.__init__(self)
self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
self.sinkpad.set_chain_function(self.chainfunc)
self.sinkpad.set_event_function(self.eventfunc)
self.sinkpad.set_getcaps_function(gst.Pad.proxy_getcaps)
self.sinkpad.set_setcaps_function(gst.Pad.proxy_setcaps)
self.add_pad (self.sinkpad)
self.srcpad = gst.Pad(self._srcpadtemplate, "src")
self.srcpad.set_event_function(self.srceventfunc)
self.srcpad.set_query_function(self.srcqueryfunc)
self.srcpad.set_getcaps_function(gst.Pad.proxy_getcaps)
self.srcpad.set_setcaps_function(gst.Pad.proxy_setcaps)
self.add_pad (self.srcpad)
def chainfunc(self, pad, buffer):
gst.log ("Passing buffer with ts %d" % (buffer.timestamp))
return self.srcpad.push (buffer)
def eventfunc(self, pad, event):
return self.srcpad.push_event (event)
def srcqueryfunc (self, pad, query):
return self.sinkpad.query (query)
def srceventfunc (self, pad, event):
return self.sinkpad.push_event (event)
gobject.type_register(PyIdentity)
pipe = gst.Pipeline()
vt = gst.element_factory_make ("videotestsrc")
i1 = PyIdentity()
color = gst.element_factory_make ("ffmpegcolorspace")
scale = gst.element_factory_make ("videoscale")
q1 = gst.element_factory_make ("queue")
i2 = PyIdentity()
sink = gst.element_factory_make ("autovideosink")
pipe.add (vt, i1, q1, i2, color, scale, sink)
gst.element_link_many (vt, i1, q1, i2, color, scale, sink)
pipe.set_state (gst.STATE_PLAYING)
gobject.MainLoop().run()
@@ -0,0 +1,840 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import pygtk
pygtk.require('2.0')
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gtk
gtk.gdk.threads_init()
class GstPlayer:
def __init__(self, videowidget):
self.playing = False
self.player = gst.element_factory_make("playbin", "player")
self.videowidget = videowidget
bus = self.player.get_bus()
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect('sync-message::element', self.on_sync_message)
bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# Sync with the X server before giving the X-id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.videowidget.set_sink(message.src)
message.src.set_property('force-aspect-ratio', True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
if self.on_eos:
self.on_eos()
self.playing = False
elif t == gst.MESSAGE_EOS:
if self.on_eos:
self.on_eos()
self.playing = False
def set_location(self, location):
self.player.set_state(gst.STATE_NULL)
self.player.set_property('uri', location)
def get_location(self):
return self.player.get_property('uri')
def query_position(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, location):
"""
@param location: time to seek to, in nanoseconds
"""
gst.debug("seeking to %r" % location)
event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
gst.SEEK_FLAG_FLUSH,
gst.SEEK_TYPE_SET, location,
gst.SEEK_TYPE_NONE, 0)
res = self.player.send_event(event)
if res:
gst.info("setting new stream time to 0")
self.player.set_new_stream_time(0L)
else:
gst.error("seek to %r failed" % location)
def pause(self):
gst.info("pausing player")
self.player.set_state(gst.STATE_PAUSED)
self.playing = False
def play(self):
gst.info("playing player")
self.player.set_state(gst.STATE_PLAYING)
self.playing = True
def stop(self):
self.player.set_state(gst.STATE_NULL)
gst.info("stopped player")
def get_state(self, timeout=1):
return self.player.get_state(timeout=timeout)
def is_playing(self):
return self.playing
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
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)
class TimeControl(gtk.HBox):
# all labels same size
sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
__gproperties__ = {'time': (gobject.TYPE_UINT64, 'Time', 'Time',
# not actually usable: see #335854
# kept for .notify() usage
0L, (1<<63)-1, 0L,
gobject.PARAM_READABLE)}
def __init__(self, window, label):
gtk.HBox.__init__(self)
self.pwindow = window
self.label = label
self.create_ui()
def get_property(self, param, pspec):
if param == 'time':
return self.get_time()
else:
assert param in self.__gproperties__, \
'Unknown property: %s' % param
def create_ui(self):
label = gtk.Label(self.label + ": ")
label.show()
a = gtk.Alignment(1.0, 0.5)
a.add(label)
a.set_padding(0, 0, 12, 0)
a.show()
self.sizegroup.add_widget(a)
self.pack_start(a, True, False, 0)
self.minutes = minutes = gtk.Entry(5)
minutes.set_width_chars(5)
minutes.set_alignment(1.0)
minutes.connect('changed', lambda *x: self.notify('time'))
minutes.connect_after('activate', lambda *x: self.activated())
label2 = gtk.Label(":")
self.seconds = seconds = gtk.Entry(2)
seconds.set_width_chars(2)
seconds.set_alignment(1.0)
seconds.connect('changed', lambda *x: self.notify('time'))
seconds.connect_after('activate', lambda *x: self.activated())
label3 = gtk.Label(".")
self.milliseconds = milliseconds = gtk.Entry(3)
milliseconds.set_width_chars(3)
milliseconds.set_alignment(0.0)
milliseconds.connect('changed', lambda *x: self.notify('time'))
milliseconds.connect_after('activate', lambda *x: self.activated())
set = gtk.Button('Set')
goto = gtk.Button('Go')
goto.set_property('image',
gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,
gtk.ICON_SIZE_BUTTON))
for w in minutes, label2, seconds, label3, milliseconds:
w.show()
self.pack_start(w, False)
set.show()
self.pack_start(set, False, False, 6)
goto.show()
self.pack_start(goto, False, False, 0)
set.connect('clicked', lambda *x: self.set_now())
goto.connect('clicked', lambda *x: self.activated())
pad = gtk.Label("")
pad.show()
self.pack_start(pad, True, False, 0)
def get_time(self):
time = 0
for w, multiplier in ((self.minutes, gst.SECOND*60),
(self.seconds, gst.SECOND),
(self.milliseconds, gst.MSECOND)):
text = w.get_text()
try:
val = int(text)
except ValueError:
val = 0
w.set_text(val and str(val) or '0')
time += val * multiplier
return time
def set_time(self, time):
if time == gst.CLOCK_TIME_NONE:
print "Can't set '%s' (invalid time)" % self.label
return
self.freeze_notify()
for w, multiplier in ((self.minutes, gst.SECOND*60),
(self.seconds, gst.SECOND),
(self.milliseconds, gst.MSECOND)):
val = time // multiplier
w.set_text(str(val))
time -= val * multiplier
self.thaw_notify()
def set_now(self):
time, dur = self.pwindow.player.query_position()
self.set_time(time)
def activated(self):
time = self.get_time()
if self.pwindow.player.is_playing():
self.pwindow.play_toggled()
self.pwindow.player.seek(time)
self.pwindow.player.get_state(timeout=gst.MSECOND * 200)
class ProgressDialog(gtk.Dialog):
def __init__(self, title, description, task, parent, flags, buttons):
gtk.Dialog.__init__(self, title, parent, flags, buttons)
self._create_ui(title, description, task)
def _create_ui(self, title, description, task):
self.set_border_width(6)
self.set_resizable(False)
self.set_has_separator(False)
vbox = gtk.VBox()
vbox.set_border_width(6)
vbox.show()
self.vbox.pack_start(vbox, False)
label = gtk.Label('<big><b>%s</b></big>' % title)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.show()
vbox.pack_start(label, False)
label = gtk.Label(description)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.set_line_wrap(True)
label.set_padding(0, 12)
label.show()
vbox.pack_start(label, False)
self.progress = progress = gtk.ProgressBar()
progress.show()
vbox.pack_start(progress, False)
self.progresstext = label = gtk.Label('')
label.set_line_wrap(True)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.show()
vbox.pack_start(label)
self.set_task(task)
def set_task(self, task):
self.progresstext.set_markup('<i>%s</i>' % task)
UNKNOWN = 0
SUCCESS = 1
FAILURE = 2
CANCELLED = 3
class RemuxProgressDialog(ProgressDialog):
def __init__(self, parent, start, stop, fromname, toname):
ProgressDialog.__init__(self,
"Writing to disk",
('Writing the selected segment of <b>%s</b> '
'to <b>%s</b>. This may take some time.'
% (fromname, toname)),
'Starting media pipeline',
parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, CANCELLED,
gtk.STOCK_CLOSE, SUCCESS))
self.start = start
self.stop = stop
self.update_position(start)
self.set_completed(False)
def update_position(self, pos):
pos = min(max(pos, self.start), self.stop)
remaining = self.stop - pos
minutes = remaining // (gst.SECOND * 60)
seconds = (remaining - minutes * gst.SECOND * 60) // gst.SECOND
self.progress.set_text('%d:%02d of video remaining' % (minutes, seconds))
self.progress.set_fraction(1.0 - float(remaining) / (self.stop - self.start))
def set_completed(self, completed):
self.set_response_sensitive(CANCELLED, not completed)
self.set_response_sensitive(SUCCESS, completed)
def set_connection_blocked_async_marshalled(pads, proc, *args, **kwargs):
def clear_list(l):
while l:
l.pop()
to_block = list(pads)
to_relink = [(x, x.get_peer()) for x in pads]
def on_pad_blocked_sync(pad, is_blocked):
if pad not in to_block:
# can happen after the seek and before unblocking -- racy,
# but no prob, bob.
return
to_block.remove(pad)
if not to_block:
# marshal to main thread
gobject.idle_add(on_pads_blocked)
def on_pads_blocked():
for src, sink in to_relink:
src.link(sink)
proc(*args, **kwargs)
for src, sink in to_relink:
src.set_blocked_async(False, lambda *x: None)
clear_list(to_relink)
for src, sink in to_relink:
src.unlink(sink)
src.set_blocked_async(True, on_pad_blocked_sync)
class Remuxer(gst.Pipeline):
__gsignals__ = {'done': (gobject.SIGNAL_RUN_LAST, None, (int,))}
def __init__(self, fromuri, touri, start, stop):
# HACK: should do Pipeline.__init__, but that doesn't do what we
# want; there's a bug open aboooot that
self.__gobject_init__()
assert start >= 0
assert stop > start
self.fromuri = fromuri
self.touri = None
self.start_time = start
self.stop_time = stop
self.src = self.remuxbin = self.sink = None
self.resolution = UNKNOWN
self.window = None
self.pdialog = None
self._query_id = -1
def do_setup_pipeline(self):
self.src = gst.element_make_from_uri(gst.URI_SRC, self.fromuri)
self.remuxbin = RemuxBin(self.start_time, self.stop_time)
self.sink = gst.element_make_from_uri(gst.URI_SINK, self.touri)
self.resolution = UNKNOWN
if gobject.signal_lookup('allow-overwrite', self.sink.__class__):
self.sink.connect('allow-overwrite', lambda *x: True)
self.add(self.src, self.remuxbin, self.sink)
self.src.link(self.remuxbin)
self.remuxbin.link(self.sink)
def do_get_touri(self):
chooser = gtk.FileChooserDialog('Save as...',
self.window,
action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(gtk.STOCK_CANCEL,
CANCELLED,
gtk.STOCK_SAVE,
SUCCESS))
chooser.set_uri(self.fromuri) # to select the folder
chooser.unselect_all()
chooser.set_do_overwrite_confirmation(True)
name = self.fromuri.split('/')[-1][:-4] + '-remuxed.ogg'
chooser.set_current_name(name)
resp = chooser.run()
uri = chooser.get_uri()
chooser.destroy()
if resp == SUCCESS:
return uri
else:
return None
def _start_queries(self):
def do_query():
try:
# HACK: self.remuxbin.query() should do the same
# (requires implementing a vmethod, dunno how to do that
# although i think it's possible)
# HACK: why does self.query_position(..) not give useful
# answers?
pad = self.remuxbin.get_pad('src')
pos, duration = pad.query_position(gst.FORMAT_TIME)
if pos != gst.CLOCK_TIME_NONE:
self.pdialog.update_position(pos)
except:
# print 'query failed'
pass
return True
if self._query_id == -1:
self._query_id = gobject.timeout_add(100, # 10 Hz
do_query)
def _stop_queries(self):
if self._query_id != -1:
gobject.source_remove(self._query_id)
self._query_id = -1
def _bus_watch(self, bus, message):
if message.type == gst.MESSAGE_ERROR:
print 'error', message
self._stop_queries()
m = gtk.MessageDialog(self.window,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE,
"Error processing file")
gerror, debug = message.parse_error()
txt = ('There was an error processing your file: %s\n\n'
'Debug information:\n%s' % (gerror, debug))
m.format_secondary_text(txt)
m.run()
m.destroy()
self.response(FAILURE)
elif message.type == gst.MESSAGE_WARNING:
print 'warning', message
elif message.type == gst.MESSAGE_EOS:
# print 'eos, woot', message.src
name = self.touri
if name.startswith('file://'):
name = name[7:]
self.pdialog.set_task('Finished writing %s' % name)
self.pdialog.update_position(self.stop_time)
self._stop_queries()
self.pdialog.set_completed(True)
elif message.type == gst.MESSAGE_STATE_CHANGED:
if message.src == self:
old, new, pending = message.parse_state_changed()
if ((old, new, pending) ==
(gst.STATE_READY, gst.STATE_PAUSED,
gst.STATE_VOID_PENDING)):
self.pdialog.set_task('Processing file')
self.pdialog.update_position(self.start_time)
self._start_queries()
self.set_state(gst.STATE_PLAYING)
def response(self, response):
assert self.resolution == UNKNOWN
self.resolution = response
self.set_state(gst.STATE_NULL)
self.pdialog.destroy()
self.pdialog = None
self.window.set_sensitive(True)
self.emit('done', response)
def start(self, main_window):
self.window = main_window
self.touri = self.do_get_touri()
if not self.touri:
return False
self.do_setup_pipeline()
bus = self.get_bus()
bus.add_signal_watch()
bus.connect('message', self._bus_watch)
if self.window:
# can be None if we are debugging...
self.window.set_sensitive(False)
fromname = self.fromuri.split('/')[-1]
toname = self.touri.split('/')[-1]
self.pdialog = RemuxProgressDialog(main_window, self.start_time,
self.stop_time, fromname, toname)
self.pdialog.show()
self.pdialog.connect('response', lambda w, r: self.response(r))
self.set_state(gst.STATE_PAUSED)
return True
def run(self, main_window):
if self.start(main_window):
loop = gobject.MainLoop()
self.connect('done', lambda *x: gobject.idle_add(loop.quit))
loop.run()
else:
self.resolution = CANCELLED
return self.resolution
class RemuxBin(gst.Bin):
def __init__(self, start_time, stop_time):
self.__gobject_init__()
self.parsefactories = self._find_parsers()
self.parsers = []
self.demux = gst.element_factory_make('oggdemux')
self.mux = gst.element_factory_make('oggmux')
self.add(self.demux, self.mux)
self.add_pad(gst.GhostPad('sink', self.demux.get_pad('sink')))
self.add_pad(gst.GhostPad('src', self.mux.get_pad('src')))
self.demux.connect('pad-added', self._new_demuxed_pad)
self.demux.connect('no-more-pads', self._no_more_pads)
self.start_time = start_time
self.stop_time = stop_time
def _find_parsers(self):
registry = gst.registry_get_default()
ret = {}
for f in registry.get_feature_list(gst.ElementFactory):
if f.get_klass().find('Parser') >= 0:
for t in f.get_static_pad_templates():
if t.direction == gst.PAD_SINK:
for s in t.get_caps():
ret[s.get_name()] = f.get_name()
break
return ret
def _new_demuxed_pad(self, element, pad):
format = pad.get_caps()[0].get_name()
if format not in self.parsefactories:
self.async_error("Unsupported media type: %s", format)
return
queue = gst.element_factory_make('queue', None);
queue.set_property('max-size-buffers', 1000)
parser = gst.element_factory_make(self.parsefactories[format])
self.add(queue)
self.add(parser)
queue.set_state(gst.STATE_PAUSED)
parser.set_state(gst.STATE_PAUSED)
pad.link(queue.get_compatible_pad(pad))
queue.link(parser)
parser.link(self.mux)
self.parsers.append(parser)
def _do_seek(self):
flags = gst.SEEK_FLAG_FLUSH
# HACK: self.seek should work, should try that at some point
return self.demux.seek(1.0, gst.FORMAT_TIME, flags,
gst.SEEK_TYPE_SET, self.start_time,
gst.SEEK_TYPE_SET, self.stop_time)
def _no_more_pads(self, element):
pads = [x.get_pad('src') for x in self.parsers]
set_connection_blocked_async_marshalled(pads,
self._do_seek)
class PlayerWindow(gtk.Window):
UPDATE_INTERVAL = 500
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(600, 425)
self.create_ui()
self.player = GstPlayer(self.videowidget)
def on_eos():
self.player.seek(0L)
self.play_toggled()
self.player.on_eos = lambda *x: on_eos()
self.update_id = -1
self.changed_id = -1
self.seek_timeout_id = -1
self.p_position = gst.CLOCK_TIME_NONE
self.p_duration = gst.CLOCK_TIME_NONE
def on_delete_event():
self.player.stop()
gtk.main_quit()
self.connect('delete-event', lambda *x: on_delete_event())
def load_file(self, location):
filename = location.split('/')[-1]
self.set_title('%s munger' % filename)
self.player.set_location(location)
if self.videowidget.flags() & gtk.REALIZED:
self.play_toggled()
else:
self.videowidget.connect_after('realize',
lambda *x: self.play_toggled())
def create_ui(self):
vbox = gtk.VBox()
vbox.show()
self.add(vbox)
self.videowidget = VideoWidget()
self.videowidget.show()
vbox.pack_start(self.videowidget)
hbox = gtk.HBox()
hbox.show()
vbox.pack_start(hbox, fill=False, expand=False)
self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
hscale = gtk.HScale(self.adjustment)
hscale.set_digits(2)
hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
hscale.connect('button-press-event', self.scale_button_press_cb)
hscale.connect('button-release-event', self.scale_button_release_cb)
hscale.connect('format-value', self.scale_format_value_cb)
hbox.pack_start(hscale)
hscale.show()
self.hscale = hscale
table = gtk.Table(2,3)
table.show()
vbox.pack_start(table, fill=False, expand=False, padding=6)
self.button = button = gtk.Button(stock=gtk.STOCK_MEDIA_PLAY)
button.set_property('can-default', True)
button.set_focus_on_click(False)
button.show()
# problem: play and paused are of different widths and cause the
# window to re-layout
# "solution": add more buttons to a vbox so that the horizontal
# width is enough
bvbox = gtk.VBox()
bvbox.add(button)
bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PLAY))
bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PAUSE))
sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
for kid in bvbox.get_children():
sizegroup.add_widget(kid)
bvbox.show()
table.attach(bvbox, 0, 1, 0, 2, gtk.FILL, gtk.FILL)
# can't set this property before the button has a window
button.set_property('has-default', True)
button.connect('clicked', lambda *args: self.play_toggled())
self.cutin = cut = TimeControl(self, "Cut in time")
cut.show()
table.attach(cut, 1, 2, 0, 1, gtk.EXPAND, 0, 12)
self.cutout = cut = TimeControl(self, "Cut out time")
cut.show()
table.attach(cut, 1, 2, 1, 2, gtk.EXPAND, 0, 12)
button = gtk.Button("_Open other movie...")
button.show()
button.connect('clicked', lambda *x: self.do_choose_file())
table.attach(button, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
button = gtk.Button("_Write to disk")
button.set_property('image',
gtk.image_new_from_stock(gtk.STOCK_SAVE_AS,
gtk.ICON_SIZE_BUTTON))
button.connect('clicked', lambda *x: self.do_remux())
button.show()
table.attach(button, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
#self.cutin.connect('notify::time', lambda *x: self.check_cutout())
#self.cutout.connect('notify::time', lambda *x: self.check_cutin())
def do_remux(self):
if self.player.is_playing():
self.play_toggled()
in_uri = self.player.get_location()
out_uri = in_uri[:-4] + '-remuxed.ogg'
r = Remuxer(in_uri, out_uri,
self.cutin.get_time(), self.cutout.get_time())
r.run(self)
def do_choose_file(self):
if self.player.is_playing():
self.play_toggled()
chooser = gtk.FileChooserDialog('Choose a movie to cut cut cut',
self,
buttons=(gtk.STOCK_CANCEL,
CANCELLED,
gtk.STOCK_OPEN,
SUCCESS))
chooser.set_local_only(False)
chooser.set_select_multiple(False)
f = gtk.FileFilter()
f.set_name("All files")
f.add_pattern("*")
chooser.add_filter(f)
f = gtk.FileFilter()
f.set_name("Ogg files")
f.add_pattern("*.og[gvax]") # as long as this is the only thing we
# support...
chooser.add_filter(f)
chooser.set_filter(f)
prev = self.player.get_location()
if prev:
chooser.set_uri(prev)
resp = chooser.run()
uri = chooser.get_uri()
chooser.destroy()
if resp == SUCCESS and uri != None:
self.load_file(uri)
return True
else:
return False
def check_cutout(self):
if self.cutout.get_time() <= self.cutin.get_time():
pos, dur = self.player.query_position()
self.cutout.set_time(dur)
def check_cutin(self):
if self.cutin.get_time() >= self.cutout.get_time():
self.cutin.set_time(0)
def play_toggled(self):
if self.player.is_playing():
self.player.pause()
self.button.set_label(gtk.STOCK_MEDIA_PLAY)
else:
self.player.play()
if self.update_id == -1:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
self.button.set_label(gtk.STOCK_MEDIA_PAUSE)
def scale_format_value_cb(self, scale, value):
if self.p_duration == -1:
real = 0
else:
real = value * self.p_duration / 100
seconds = real / gst.SECOND
return "%02d:%02d" % (seconds / 60, seconds % 60)
def scale_button_press_cb(self, widget, event):
# see seek.c:start_seek
gst.debug('starting seek')
self.button.set_sensitive(False)
self.was_playing = self.player.is_playing()
if self.was_playing:
self.player.pause()
# don't timeout-update position during seek
if self.update_id != -1:
gobject.source_remove(self.update_id)
self.update_id = -1
# make sure we get changed notifies
if self.changed_id == -1:
self.changed_id = self.hscale.connect('value-changed',
self.scale_value_changed_cb)
def scale_value_changed_cb(self, scale):
# see seek.c:seek_cb
real = long(scale.get_value() * self.p_duration / 100) # in ns
gst.debug('value changed, perform seek to %r' % real)
self.player.seek(real)
# allow for a preroll
self.player.get_state(timeout=50*gst.MSECOND) # 50 ms
def scale_button_release_cb(self, widget, event):
# see seek.cstop_seek
widget.disconnect(self.changed_id)
self.changed_id = -1
self.button.set_sensitive(True)
if self.seek_timeout_id != -1:
gobject.source_remove(self.seek_timeout_id)
self.seek_timeout_id = -1
else:
gst.debug('released slider, setting back to playing')
if self.was_playing:
self.player.play()
if self.update_id != -1:
self.error('Had a previous update timeout id')
else:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
def update_scale_cb(self):
had_duration = self.p_duration != gst.CLOCK_TIME_NONE
self.p_position, self.p_duration = self.player.query_position()
if self.p_position != gst.CLOCK_TIME_NONE:
value = self.p_position * 100.0 / self.p_duration
self.adjustment.set_value(value)
if not had_duration:
self.cutin.set_time(0)
return True
def main(args):
def usage():
sys.stderr.write("usage: %s [URI-OF-MEDIA-FILE]\n" % args[0])
return 1
w = PlayerWindow()
w.show()
if len(args) == 1:
if not w.do_choose_file():
return 1
elif len(args) == 2:
if not gst.uri_is_valid(args[1]):
sys.stderr.write("Error: Invalid URI: %s\n" % args[1])
return 1
w.load_file(args[1])
else:
return usage()
gtk.main()
if __name__ == '__main__':
sys.exit(main(sys.argv))
+198
View File
@@ -0,0 +1,198 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Segments.py
# Copyright (C) 2006 Artem Popov <artfwo@gmail.com>
#
# This example demonstrates segment seeking
# and seamless looping within playbin.
import pygst
pygst.require ("0.10")
import gst
import pygtk
pygtk.require ("2.0")
import gobject
class Looper (gobject.GObject):
__gproperties__ = {
"loop": (gobject.TYPE_BOOLEAN,
"loop",
"Whether to loop the segment",
False,
gobject.PARAM_READWRITE),
"start-pos": (gobject.TYPE_UINT64,
"start position",
"The segment start marker",
0,
0xfffffffffffffff, # max long possible
0,
gobject.PARAM_READWRITE),
"stop-pos": (gobject.TYPE_UINT64,
"stop position",
"The segment stop marker",
0,
0xfffffffffffffff, # max long possible
0,
gobject.PARAM_READWRITE),
} # __gproperties__
__gsignals__ = {
"stopped": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
"position-updated": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
"error": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
} # __gsignals__
def __init__ (self, location = None):
gobject.GObject.__init__ (self)
self.__playbin = gst.element_factory_make ("playbin")
self.__playbin.props.video_sink = gst.element_factory_make ("fakesink")
bus = self.__playbin.get_bus ()
bus.add_watch (self.__on_bus_message)
self.__loop = False
self.__start_pos = 0
self.__stop_pos = 0
self.__timeout_id = 0
if location:
self.load (location)
def load (self, location):
self.__playbin.props.uri = location
self.__start_position = 0
self.__stop_position = 0
def set_segment (self, start, stop):
self.props.start_pos = start
self.props.stop_pos = stop
def play (self):
if not (self.__start_pos or self.__stop_pos):
raise RuntimeError, "Cannot start playback, segment was not set!"
self.__playbin.set_state (gst.STATE_PLAYING)
def stop (self, silent = False):
self.__playbin.set_state (gst.STATE_NULL)
if not silent:
self.emit ("stopped")
def do_get_property (self, property):
if property.name == "loop":
return self.__loop
elif property.name == "start-pos":
return self.__start_pos
elif property.name == "stop-pos":
return self.__stop_pos
else:
raise AttributeError, "Unknown property %s" % property.name
def do_set_property (self, property, value):
if property.name == "loop":
self.__loop = value
elif property.name == "start-pos":
self.__start_pos = value
elif property.name == "stop-pos":
self.__stop_pos = value
else:
raise AttributeError, "Unknown property %s" % property.name
def do_stopped (self):
if self.__timeout_id:
gobject.source_remove (self.__timeout_id)
self.__timeout_id = 0
def __seek (self, start, stop, flush):
flags = gst.SEEK_FLAG_SEGMENT | gst.SEEK_FLAG_ACCURATE
if flush:
flags = flags | gst.SEEK_FLAG_FLUSH
self.__playbin.seek (1.0, gst.FORMAT_TIME, flags,
gst.SEEK_TYPE_SET, start,
gst.SEEK_TYPE_SET, stop)
def __on_timeout (self):
position = self.__playbin.query_position (gst.FORMAT_TIME) [0]
self.emit ("position-updated", float (position))
return True
def __on_bus_message (self, bus, message):
if message.type == gst.MESSAGE_ERROR:
error, debug = message.parse_error ()
self.stop () # this looks neccessary here
self.emit ("error", (error, debug))
elif message.type == gst.MESSAGE_NEW_CLOCK:
# we connect the timeout handler here to be sure that further queries succeed
interval = int ((self.__stop_position - self.__start_position) / (2 * gst.SECOND) + 50)
self.__timeout_id = gobject.timeout_add (interval, self.__on_timeout)
elif message.type == gst.MESSAGE_STATE_CHANGED:
old_state, new_state, pending = message.parse_state_changed ()
if old_state == gst.STATE_READY and new_state == gst.STATE_PAUSED and message.src == self.__playbin:
self.__seek (self.__start_pos, self.__stop_pos, True)
elif message.type == gst.MESSAGE_SEGMENT_DONE:
if self.__loop:
self.__seek (self.__start_pos, self.__stop_pos, False)
else:
src = self.__playbin.get_property ("source")
pad = src.get_pad ('src')
pad.push_event (gst.event_new_eos ())
# this is the good old way:
#
# pads = src.src_pads ()
# while True:
# try:
# pad = pads.next ()
# pad.push_event (gst.event_new_eos ())
# except:
# break
elif message.type == gst.MESSAGE_EOS:
self.stop ()
return True
mainloop = gobject.MainLoop ()
def on_looper_stopped (looper):
mainloop.quit ()
def on_looper_pos_updated (looper, position):
print round (position / gst.SECOND, 2)
def on_looper_error (looper, error_tuple):
error, debug = error_tuple
print "\n\n%s\n\n%s\n\n" % (error, debug)
mainloop.quit ()
if __name__ == "__main__":
import sys
if len (sys.argv) != 5:
print "Usage: %s <filename|uri> <start_seconds> <stop_seconds> <loop = 0|1>" % sys.argv [0]
sys.exit (1)
if "://" in sys.argv [1]:
uri = sys.argv [1]
else:
import os.path
uri = "file://" + os.path.abspath (sys.argv [1])
looper = Looper (uri)
looper.props.start_pos = long (sys.argv [2]) * gst.SECOND
looper.props.stop_pos = long (sys.argv [3]) * gst.SECOND
looper.props.loop = int (sys.argv [4])
looper.connect ("stopped", on_looper_stopped)
looper.connect ("position-updated", on_looper_pos_updated)
looper.connect ("error", on_looper_error)
looper.play ()
mainloop.run ()
@@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# sinkelement.py
# (c) 2005 Edward Hervey <edward@fluendo.com>
# (c) 2007 Jan Schmidt <jan@fluendo.com>
# Licensed under LGPL
#
# Small test application to show how to write a sink element
# in 20 lines in python and place into the gstreamer registry
# so it can be autoplugged or used from parse_launch.
#
# Run this script with GST_DEBUG=python:5 to see the debug
# messages
import pygst
pygst.require('0.10')
import gst
import gobject
gobject.threads_init ()
#
# Simple Sink element created entirely in python
#
class MySink(gst.Element):
__gstdetails__ = ('CustomSink','Sink', \
'Custom test sink element', 'Edward Hervey')
_sinkpadtemplate = gst.PadTemplate ("sinkpadtemplate",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_new_any())
def __init__(self):
gst.Element.__init__(self)
gst.info('creating sinkpad')
self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
gst.info('adding sinkpad to self')
self.add_pad(self.sinkpad)
gst.info('setting chain/event functions')
self.sinkpad.set_chain_function(self.chainfunc)
self.sinkpad.set_event_function(self.eventfunc)
def chainfunc(self, pad, buffer):
self.info("%s timestamp(buffer):%d" % (pad, buffer.timestamp))
return gst.FLOW_OK
def eventfunc(self, pad, event):
self.info("%s event:%r" % (pad, event.type))
return True
gobject.type_register(MySink)
# Register the element into this process' registry.
gst.element_register (MySink, 'mysink', gst.RANK_MARGINAL)
print "Use --gst-debug=python:3 to see output from this example"
#
# Code to test the MySink class
#
gst.info('About to create MySink')
pipeline = gst.parse_launch ("fakesrc ! mysink")
pipeline.set_state(gst.STATE_PLAYING)
gobject.MainLoop().run()
@@ -0,0 +1,68 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# sinkelement.py
# (c) 2005 Edward Hervey <edward@fluendo.com>
# Licensed under LGPL
#
# Small test application to show how to write a sink element
# in 20 lines in python
#
# Run this script with GST_DEBUG=python:5 to see the debug
# messages
import pygst
pygst.require('0.10')
import gst
import gobject
gobject.threads_init ()
#
# Simple Sink element created entirely in python
#
class MySink(gst.Element):
_sinkpadtemplate = gst.PadTemplate ("sinkpadtemplate",
gst.PAD_SINK,
gst.PAD_ALWAYS,
gst.caps_new_any())
def __init__(self):
gst.Element.__init__(self)
gst.info('creating sinkpad')
self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
gst.info('adding sinkpad to self')
self.add_pad(self.sinkpad)
gst.info('setting chain/event functions')
self.sinkpad.set_chain_function(self.chainfunc)
self.sinkpad.set_event_function(self.eventfunc)
def chainfunc(self, pad, buffer):
self.info("%s timestamp(buffer):%d" % (pad, buffer.timestamp))
return gst.FLOW_OK
def eventfunc(self, pad, event):
self.info("%s event:%r" % (pad, event.type))
return True
gobject.type_register(MySink)
#
# Code to test the MySink class
#
src = gst.element_factory_make('fakesrc')
gst.info('About to create MySink')
sink = MySink()
pipeline = gst.Pipeline()
pipeline.add(src, sink)
src.link(sink)
pipeline.set_state(gst.STATE_PLAYING)
gobject.MainLoop().run()
+192
View File
@@ -0,0 +1,192 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import pygtk
pygtk.require('2.0')
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gtk
gtk.gdk.threads_init()
class SwitchTest:
def __init__(self, videowidget):
self.playing = False
pipestr = ('videotestsrc pattern=0 ! queue ! s.sink0'
' videotestsrc pattern=1 ! queue ! s.sink1'
' input-selector name=s ! autovideosink')
self.pipeline = gst.parse_launch(pipestr)
self.videowidget = videowidget
bus = self.pipeline.get_bus()
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect('sync-message::element', self.on_sync_message)
bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# Sync with the X server before giving the X-id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.videowidget.set_sink(message.src)
message.src.set_property('force-aspect-ratio', True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
if self.on_eos:
self.on_eos()
self.playing = False
elif t == gst.MESSAGE_EOS:
if self.on_eos:
self.on_eos()
self.playing = False
def play(self):
self.playing = True
gst.info("playing player")
self.pipeline.set_state(gst.STATE_PLAYING)
def stop(self):
self.pipeline.set_state(gst.STATE_NULL)
gst.info("stopped player")
self.playing = False
def get_state(self, timeout=1):
return self.pipeline.get_state(timeout=timeout)
def is_playing(self):
return self.playing
def switch(self, padname):
switch = self.pipeline.get_by_name('s')
stop_time = switch.emit('block')
newpad = switch.get_static_pad(padname)
start_time = newpad.get_property('running-time')
gst.warning('stop time = %d' % (stop_time,))
gst.warning('stop time = %s' % (gst.TIME_ARGS(stop_time),))
gst.warning('start time = %d' % (start_time,))
gst.warning('start time = %s' % (gst.TIME_ARGS(start_time),))
gst.warning('switching from %r to %r'
% (switch.get_property('active-pad'), padname))
switch.emit('switch', newpad, stop_time, start_time)
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
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)
class SwitchWindow(gtk.Window):
UPDATE_INTERVAL = 500
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(410, 325)
self.create_ui()
self.player = SwitchTest(self.videowidget)
self.populate_combobox()
self.update_id = -1
self.changed_id = -1
self.seek_timeout_id = -1
self.p_position = gst.CLOCK_TIME_NONE
self.p_duration = gst.CLOCK_TIME_NONE
def on_delete_event():
self.player.stop()
gtk.main_quit()
self.connect('delete-event', lambda *x: on_delete_event())
def load_file(self, location):
self.player.set_location(location)
def play(self):
self.player.play()
def populate_combobox(self):
switch = self.player.pipeline.get_by_name('s')
for i, pad in enumerate([p for p in switch.pads()
if p.get_direction() == gst.PAD_SINK]):
self.combobox.append_text(pad.get_name())
if switch.get_property('active-pad') == pad.get_name():
self.combobox.set_active(i)
if self.combobox.get_active() == -1:
self.combobox.set_active(0)
def combobox_changed(self):
model = self.combobox.get_model()
row = model[self.combobox.get_active()]
padname, = row
self.player.switch(padname)
def create_ui(self):
vbox = gtk.VBox()
self.add(vbox)
self.videowidget = VideoWidget()
vbox.pack_start(self.videowidget)
hbox = gtk.HBox()
vbox.pack_start(hbox, fill=False, expand=False)
self.combobox = combobox = gtk.combo_box_new_text()
combobox.show()
hbox.pack_start(combobox)
self.combobox.connect('changed',
lambda *x: self.combobox_changed())
self.videowidget.connect_after('realize',
lambda *x: self.play())
def main(args):
def usage():
sys.stderr.write("usage: %s\n" % args[0])
return 1
# Need to register our derived widget types for implicit event
# handlers to get called.
gobject.type_register(SwitchWindow)
gobject.type_register(VideoWidget)
if len(args) != 1:
return usage()
w = SwitchWindow()
w.show_all()
gtk.main()
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))
+820
View File
@@ -0,0 +1,820 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import pygtk
pygtk.require('2.0')
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gtk
gtk.gdk.threads_init()
class GstPlayer:
def __init__(self, videowidget):
self.playing = False
self.player = gst.element_factory_make("playbin", "player")
self.videowidget = videowidget
bus = self.player.get_bus()
bus.enable_sync_message_emission()
bus.add_signal_watch()
bus.connect('sync-message::element', self.on_sync_message)
bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
# Sync with the X server before giving the X-id to the sink
gtk.gdk.threads_enter()
gtk.gdk.display_get_default().sync()
self.videowidget.set_sink(message.src)
message.src.set_property('force-aspect-ratio', True)
gtk.gdk.threads_leave()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
if self.on_eos:
self.on_eos()
self.playing = False
elif t == gst.MESSAGE_EOS:
if self.on_eos:
self.on_eos()
self.playing = False
def set_location(self, location):
self.player.set_state(gst.STATE_NULL)
self.player.set_property('uri', location)
def get_location(self):
return self.player.get_property('uri')
def query_position(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, location):
"""
@param location: time to seek to, in nanoseconds
"""
gst.debug("seeking to %r" % location)
event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
gst.SEEK_FLAG_FLUSH,
gst.SEEK_TYPE_SET, location,
gst.SEEK_TYPE_NONE, 0)
res = self.player.send_event(event)
if res:
gst.info("setting new stream time to 0")
self.player.set_new_stream_time(0L)
else:
gst.error("seek to %r failed" % location)
def pause(self):
gst.info("pausing player")
self.player.set_state(gst.STATE_PAUSED)
self.playing = False
def play(self):
gst.info("playing player")
self.player.set_state(gst.STATE_PLAYING)
self.playing = True
def stop(self):
self.player.set_state(gst.STATE_NULL)
gst.info("stopped player")
def get_state(self, timeout=1):
return self.player.get_state(timeout=timeout)
def is_playing(self):
return self.playing
class VideoWidget(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
self.imagesink = None
self.unset_flags(gtk.DOUBLE_BUFFERED)
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)
class SyncPoints(gtk.VBox):
def __init__(self, window):
gtk.VBox.__init__(self)
self.pwindow = window
self.create_ui()
def get_time_as_str(self, iter, i):
value = self.model.get_value(iter, i)
ret = ''
for div, sep, mod, pad in ((gst.SECOND*60, '', 0, 0),
(gst.SECOND, ':', 60, 2),
(gst.MSECOND, '.', 1000, 3)):
n = value // div
if mod:
n %= mod
ret += sep + ('%%0%dd' % pad) % n
return ret
def create_ui(self):
self.model = model = gtk.ListStore(gobject.TYPE_UINT64,
gobject.TYPE_UINT64)
self.view = view = gtk.TreeView(self.model)
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Audio time", renderer)
def time_to_text(column, cell, method, iter, i):
cell.set_property('text', self.get_time_as_str(iter, i))
column.set_cell_data_func(renderer, time_to_text, 0)
column.set_expand(True)
column.set_clickable(True)
view.append_column(column)
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Video time", renderer)
column.set_cell_data_func(renderer, time_to_text, 1)
column.set_expand(True)
view.append_column(column)
view.show()
self.pack_start(view, True, True, 6)
hbox = gtk.HBox(False, 0)
hbox.show()
self.pack_start(hbox, False, False, 0)
add = gtk.Button(stock=gtk.STOCK_ADD)
add.show()
def add_and_select(*x):
iter = model.append()
self.view.get_selection().select_iter(iter)
self.changed()
add.connect("clicked", add_and_select)
hbox.pack_end(add, False, False, 0)
remove = gtk.Button(stock=gtk.STOCK_REMOVE)
remove.show()
def remove_selected(*x):
model, iter = self.view.get_selection().get_selected()
model.remove(iter)
self.changed()
remove.connect("clicked", remove_selected)
hbox.pack_end(remove, False, False, 0)
pad = gtk.Label(' ')
pad.show()
hbox.pack_end(pad)
label = gtk.Label("Set: ")
label.show()
hbox.pack_start(label)
a = gtk.Button("A_udio")
a.show()
a.connect("clicked", lambda *x: self.set_selected_audio_now())
hbox.pack_start(a)
l = gtk.Label(" / ")
l.show()
hbox.pack_start(l)
v = gtk.Button("_Video")
v.show()
v.connect("clicked", lambda *x: self.set_selected_video_now())
hbox.pack_start(v)
def get_sync_points(self):
def get_value(row, i):
return self.model.get_value(row.iter, i)
pairs = [(get_value(row, 1), get_value(row, 0)) for row in self.model]
pairs.sort()
ret = []
maxdiff = 0
for pair in pairs:
maxdiff = max(maxdiff, abs(pair[1] - pair[0]))
ret.extend(pair)
return ret, maxdiff
def changed(self):
print 'Sync times now:'
for index, row in enumerate(self.model):
print 'A/V %d: %s -- %s' % (index,
self.get_time_as_str(row.iter, 0),
self.get_time_as_str(row.iter, 1))
def set_selected_audio(self, time):
sel = self.view.get_selection()
model, iter = sel.get_selected()
if iter:
model.set_value(iter, 0, time)
self.changed()
def set_selected_video(self, time):
sel = self.view.get_selection()
model, iter = sel.get_selected()
if iter:
model.set_value(iter, 1, time)
self.changed()
def set_selected_audio_now(self):
time, dur = self.pwindow.player.query_position()
self.set_selected_audio(time)
def set_selected_video_now(self):
# pause and preroll first
if self.pwindow.player.is_playing():
self.pwindow.play_toggled()
self.pwindow.player.get_state(timeout=gst.MSECOND * 200)
time, dur = self.pwindow.player.query_position()
self.set_selected_video(time)
def seek_and_pause(self, time):
if self.pwindow.player.is_playing():
self.pwindow.play_toggled()
self.pwindow.player.seek(time)
if self.pwindow.player.is_playing():
self.pwindow.play_toggled()
self.pwindow.player.get_state(timeout=gst.MSECOND * 200)
class ProgressDialog(gtk.Dialog):
def __init__(self, title, description, task, parent, flags, buttons):
gtk.Dialog.__init__(self, title, parent, flags, buttons)
self._create_ui(title, description, task)
def _create_ui(self, title, description, task):
self.set_border_width(6)
self.set_resizable(False)
self.set_has_separator(False)
vbox = gtk.VBox()
vbox.set_border_width(6)
vbox.show()
self.vbox.pack_start(vbox, False)
label = gtk.Label('<big><b>%s</b></big>' % title)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.show()
vbox.pack_start(label, False)
label = gtk.Label(description)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.set_line_wrap(True)
label.set_padding(0, 12)
label.show()
vbox.pack_start(label, False)
self.progress = progress = gtk.ProgressBar()
progress.show()
vbox.pack_start(progress, False)
self.progresstext = label = gtk.Label('')
label.set_line_wrap(True)
label.set_use_markup(True)
label.set_alignment(0.0, 0.0)
label.show()
vbox.pack_start(label)
self.set_task(task)
def set_task(self, task):
self.progresstext.set_markup('<i>%s</i>' % task)
UNKNOWN = 0
SUCCESS = 1
FAILURE = 2
CANCELLED = 3
class RemuxProgressDialog(ProgressDialog):
def __init__(self, parent, fromname, toname):
ProgressDialog.__init__(self,
"Writing to disk",
('Writing the newly synchronized <b>%s</b> '
'to <b>%s</b>. This may take some time.'
% (fromname, toname)),
'Starting media pipeline',
parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, CANCELLED,
gtk.STOCK_CLOSE, SUCCESS))
self.set_completed(False)
def update_position(self, pos, dur):
remaining = dur - pos
minutes = remaining // (gst.SECOND * 60)
seconds = (remaining - minutes * gst.SECOND * 60) // gst.SECOND
self.progress.set_text('%d:%02d of video remaining' % (minutes, seconds))
self.progress.set_fraction(1.0 - float(remaining) / dur)
def set_completed(self, completed):
self.set_response_sensitive(CANCELLED, not completed)
self.set_response_sensitive(SUCCESS, completed)
class Resynchronizer(gst.Pipeline):
__gsignals__ = {'done': (gobject.SIGNAL_RUN_LAST, None, (int,))}
def __init__(self, fromuri, touri, (syncpoints, maxdiff)):
# HACK: should do Pipeline.__init__, but that doesn't do what we
# want; there's a bug open aboooot that
self.__gobject_init__()
self.fromuri = fromuri
self.touri = None
self.syncpoints = syncpoints
self.maxdiff = maxdiff
self.src = self.resyncbin = self.sink = None
self.resolution = UNKNOWN
self.window = None
self.pdialog = None
self._query_id = -1
def do_setup_pipeline(self):
self.src = gst.element_make_from_uri(gst.URI_SRC, self.fromuri)
self.resyncbin = ResyncBin(self.syncpoints, self.maxdiff)
self.sink = gst.element_make_from_uri(gst.URI_SINK, self.touri)
self.resolution = UNKNOWN
if gobject.signal_lookup('allow-overwrite', self.sink.__class__):
self.sink.connect('allow-overwrite', lambda *x: True)
self.add(self.src, self.resyncbin, self.sink)
self.src.link(self.resyncbin)
self.resyncbin.link(self.sink)
def do_get_touri(self):
chooser = gtk.FileChooserDialog('Save as...',
self.window,
action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(gtk.STOCK_CANCEL,
CANCELLED,
gtk.STOCK_SAVE,
SUCCESS))
chooser.set_uri(self.fromuri) # to select the folder
chooser.unselect_all()
chooser.set_do_overwrite_confirmation(True)
name = self.fromuri.split('/')[-1][:-4] + '-remuxed.ogg'
chooser.set_current_name(name)
resp = chooser.run()
uri = chooser.get_uri()
chooser.destroy()
if resp == SUCCESS:
return uri
else:
return None
def _start_queries(self):
def do_query():
try:
# HACK: self.remuxbin.query() should do the same
# (requires implementing a vmethod, dunno how to do that
# although i think it's possible)
# HACK: why does self.query_position(..) not give useful
# answers?
pad = self.resyncbin.get_pad('src')
pos, format = pad.query_position(gst.FORMAT_TIME)
dur, format = pad.query_duration(gst.FORMAT_TIME)
if pos != gst.CLOCK_TIME_NONE:
self.pdialog.update_position(pos, duration)
except:
# print 'query failed'
pass
return True
if self._query_id == -1:
self._query_id = gobject.timeout_add(100, # 10 Hz
do_query)
def _stop_queries(self):
if self._query_id != -1:
gobject.source_remove(self._query_id)
self._query_id = -1
def _bus_watch(self, bus, message):
if message.type == gst.MESSAGE_ERROR:
print 'error', message
self._stop_queries()
m = gtk.MessageDialog(self.window,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE,
"Error processing file")
gerror, debug = message.parse_error()
txt = ('There was an error processing your file: %s\n\n'
'Debug information:\n%s' % (gerror, debug))
m.format_secondary_text(txt)
m.run()
m.destroy()
self.response(FAILURE)
elif message.type == gst.MESSAGE_WARNING:
print 'warning', message
elif message.type == gst.MESSAGE_EOS:
# print 'eos, woot', message.src
name = self.touri
if name.startswith('file://'):
name = name[7:]
self.pdialog.set_task('Finished writing %s' % name)
self.pdialog.update_position(1,1)
self._stop_queries()
self.pdialog.set_completed(True)
elif message.type == gst.MESSAGE_STATE_CHANGED:
if message.src == self:
old, new, pending = message.parse_state_changed()
if ((old, new, pending) ==
(gst.STATE_READY, gst.STATE_PAUSED,
gst.STATE_VOID_PENDING)):
self.pdialog.set_task('Processing file')
self._start_queries()
self.set_state(gst.STATE_PLAYING)
def response(self, response):
assert self.resolution == UNKNOWN
self.resolution = response
self.set_state(gst.STATE_NULL)
self.pdialog.destroy()
self.pdialog = None
self.window.set_sensitive(True)
self.emit('done', response)
def start(self, main_window):
self.window = main_window
self.touri = self.do_get_touri()
if not self.touri:
return False
self.do_setup_pipeline()
bus = self.get_bus()
bus.add_signal_watch()
bus.connect('message', self._bus_watch)
if self.window:
# can be None if we are debugging...
self.window.set_sensitive(False)
fromname = self.fromuri.split('/')[-1]
toname = self.touri.split('/')[-1]
self.pdialog = RemuxProgressDialog(main_window, fromname, toname)
self.pdialog.show()
self.pdialog.connect('response', lambda w, r: self.response(r))
self.set_state(gst.STATE_PAUSED)
return True
def run(self, main_window):
if self.start(main_window):
loop = gobject.MainLoop()
self.connect('done', lambda *x: gobject.idle_add(loop.quit))
loop.run()
else:
self.resolution = CANCELLED
return self.resolution
class ResyncBin(gst.Bin):
def __init__(self, sync_points, maxdiff):
self.__gobject_init__()
self.parsefactories = self._find_parsers()
self.parsers = []
self.demux = gst.element_factory_make('oggdemux')
self.mux = gst.element_factory_make('oggmux')
self.add(self.demux, self.mux)
self.add_pad(gst.GhostPad('sink', self.demux.get_pad('sink')))
self.add_pad(gst.GhostPad('src', self.mux.get_pad('src')))
self.demux.connect('pad-added', self._new_demuxed_pad)
self.sync_points = sync_points
self.maxdiff = maxdiff
def _find_parsers(self):
registry = gst.registry_get_default()
ret = {}
for f in registry.get_feature_list(gst.ElementFactory):
if f.get_klass().find('Parser') >= 0:
for t in f.get_static_pad_templates():
if t.direction == gst.PAD_SINK:
for s in t.get_caps():
ret[s.get_name()] = f.get_name()
break
return ret
def _new_demuxed_pad(self, element, pad):
format = pad.get_caps()[0].get_name()
if format not in self.parsefactories:
self.async_error("Unsupported media type: %s", format)
return
queue = gst.element_factory_make('queue', 'queue_' + format)
queue.set_property('max-size-buffers', 0)
queue.set_property('max-size-bytes', 0)
print self.maxdiff
queue.set_property('max-size-time', int(self.maxdiff * 1.5))
parser = gst.element_factory_make(self.parsefactories[format])
self.add(queue)
self.add(parser)
queue.set_state(gst.STATE_PAUSED)
parser.set_state(gst.STATE_PAUSED)
pad.link(queue.get_compatible_pad(pad))
queue.link(parser)
parser.link(self.mux)
self.parsers.append(parser)
print repr(self.sync_points)
if 'video' in format:
parser.set_property('synchronization-points',
self.sync_points)
class PlayerWindow(gtk.Window):
UPDATE_INTERVAL = 500
def __init__(self):
gtk.Window.__init__(self)
self.set_default_size(600, 500)
self.create_ui()
self.player = GstPlayer(self.videowidget)
def on_eos():
self.player.seek(0L)
self.play_toggled()
self.player.on_eos = lambda *x: on_eos()
self.update_id = -1
self.changed_id = -1
self.seek_timeout_id = -1
self.p_position = gst.CLOCK_TIME_NONE
self.p_duration = gst.CLOCK_TIME_NONE
def on_delete_event():
self.player.stop()
gtk.main_quit()
self.connect('delete-event', lambda *x: on_delete_event())
def load_file(self, location):
filename = location.split('/')[-1]
self.set_title('%s munger' % filename)
self.player.set_location(location)
if self.videowidget.flags() & gtk.REALIZED:
self.play_toggled()
else:
self.videowidget.connect_after('realize',
lambda *x: self.play_toggled())
def create_ui(self):
vbox = gtk.VBox()
vbox.show()
self.add(vbox)
self.videowidget = VideoWidget()
self.videowidget.show()
vbox.pack_start(self.videowidget)
hbox = gtk.HBox()
hbox.show()
vbox.pack_start(hbox, fill=False, expand=False)
self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
hscale = gtk.HScale(self.adjustment)
hscale.set_digits(2)
hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)
hscale.connect('button-press-event', self.scale_button_press_cb)
hscale.connect('button-release-event', self.scale_button_release_cb)
hscale.connect('format-value', self.scale_format_value_cb)
hbox.pack_start(hscale)
hscale.show()
self.hscale = hscale
table = gtk.Table(3,3)
table.show()
vbox.pack_start(table, fill=False, expand=False, padding=6)
self.button = button = gtk.Button(stock=gtk.STOCK_MEDIA_PLAY)
button.set_property('can-default', True)
button.set_focus_on_click(False)
button.show()
# problem: play and paused are of different widths and cause the
# window to re-layout
# "solution": add more buttons to a vbox so that the horizontal
# width is enough
bvbox = gtk.VBox()
bvbox.add(button)
bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PLAY))
bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PAUSE))
sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
for kid in bvbox.get_children():
sizegroup.add_widget(kid)
bvbox.show()
table.attach(bvbox, 0, 1, 1, 3, gtk.FILL, gtk.FILL)
# can't set this property before the button has a window
button.set_property('has-default', True)
button.connect('clicked', lambda *args: self.play_toggled())
self.sync = sync = SyncPoints(self)
sync.show()
table.attach(sync, 1, 2, 0, 3, gtk.EXPAND, gtk.EXPAND|gtk.FILL, 12)
# nasty things to get sizes
l = gtk.Label('\n\n\n')
l.show()
table.attach(l, 0, 1, 0, 1, 0, 0, 0)
l = gtk.Label('\n\n\n')
l.show()
table.attach(l, 2, 3, 0, 1, 0, 0, 0)
button = gtk.Button("_Open other movie...")
button.show()
button.connect('clicked', lambda *x: self.do_choose_file())
table.attach(button, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
button = gtk.Button("_Write to disk")
button.set_property('image',
gtk.image_new_from_stock(gtk.STOCK_SAVE_AS,
gtk.ICON_SIZE_BUTTON))
button.connect('clicked', lambda *x: self.do_remux())
button.show()
table.attach(button, 2, 3, 2, 3, gtk.FILL, gtk.FILL)
def do_remux(self):
if self.player.is_playing():
self.play_toggled()
in_uri = self.player.get_location()
out_uri = in_uri[:-4] + '-remuxed.ogg'
r = Resynchronizer(in_uri, out_uri, self.sync.get_sync_points())
r.run(self)
def do_choose_file(self):
if self.player.is_playing():
self.play_toggled()
chooser = gtk.FileChooserDialog('Choose a movie to bork bork bork',
self,
buttons=(gtk.STOCK_CANCEL,
CANCELLED,
gtk.STOCK_OPEN,
SUCCESS))
chooser.set_local_only(False)
chooser.set_select_multiple(False)
f = gtk.FileFilter()
f.set_name("All files")
f.add_pattern("*")
chooser.add_filter(f)
f = gtk.FileFilter()
f.set_name("Ogg files")
f.add_pattern("*.ogg") # as long as this is the only thing we
# support...
chooser.add_filter(f)
chooser.set_filter(f)
prev = self.player.get_location()
if prev:
chooser.set_uri(prev)
resp = chooser.run()
uri = chooser.get_uri()
chooser.destroy()
if resp == SUCCESS:
self.load_file(uri)
return True
else:
return False
def play_toggled(self):
if self.player.is_playing():
self.player.pause()
self.button.set_label(gtk.STOCK_MEDIA_PLAY)
else:
self.player.play()
if self.update_id == -1:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
self.button.set_label(gtk.STOCK_MEDIA_PAUSE)
def scale_format_value_cb(self, scale, value):
if self.p_duration == -1:
real = 0
else:
real = value * self.p_duration / 100
seconds = real / gst.SECOND
return "%02d:%02d" % (seconds / 60, seconds % 60)
def scale_button_press_cb(self, widget, event):
# see seek.c:start_seek
gst.debug('starting seek')
self.button.set_sensitive(False)
self.was_playing = self.player.is_playing()
if self.was_playing:
self.player.pause()
# don't timeout-update position during seek
if self.update_id != -1:
gobject.source_remove(self.update_id)
self.update_id = -1
# make sure we get changed notifies
if self.changed_id == -1:
self.changed_id = self.hscale.connect('value-changed',
self.scale_value_changed_cb)
def scale_value_changed_cb(self, scale):
# see seek.c:seek_cb
real = long(scale.get_value() * self.p_duration / 100) # in ns
gst.debug('value changed, perform seek to %r' % real)
self.player.seek(real)
# allow for a preroll
self.player.get_state(timeout=50*gst.MSECOND) # 50 ms
def scale_button_release_cb(self, widget, event):
# see seek.cstop_seek
widget.disconnect(self.changed_id)
self.changed_id = -1
self.button.set_sensitive(True)
if self.seek_timeout_id != -1:
gobject.source_remove(self.seek_timeout_id)
self.seek_timeout_id = -1
else:
gst.debug('released slider, setting back to playing')
if self.was_playing:
self.player.play()
if self.update_id != -1:
self.error('Had a previous update timeout id')
else:
self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
self.update_scale_cb)
def update_scale_cb(self):
had_duration = self.p_duration != gst.CLOCK_TIME_NONE
self.p_position, self.p_duration = self.player.query_position()
if self.p_position != gst.CLOCK_TIME_NONE:
value = self.p_position * 100.0 / self.p_duration
self.adjustment.set_value(value)
return True
def main(args):
def usage():
sys.stderr.write("usage: %s [URI-OF-MEDIA-FILE]\n" % args[0])
return 1
w = PlayerWindow()
w.show()
if len(args) == 1:
if not w.do_choose_file():
return 1
elif len(args) == 2:
if not gst.uri_is_valid(args[1]):
sys.stderr.write("Error: Invalid URI: %s\n" % args[1])
return 1
w.load_file(args[1])
else:
return usage()
gtk.main()
if __name__ == '__main__':
sys.exit(main(sys.argv))
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2009 Stefan Kost <ensonic@user.sf.net>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
import sys
import gobject
gobject.threads_init()
import pygst
pygst.require('0.10')
import gst
mainloop = gobject.MainLoop()
def on_eos(bus, msg):
mainloop.quit()
def main(args):
"Tagsetter test, test result with:"
"gst-launch -t playbin uri=file://$PWD/test.avi"
# create a new bin to hold the elements
bin = gst.parse_launch('audiotestsrc num-buffers=100 ! ' +
'lame ! ' +
'avimux name=mux ! ' +
'filesink location=test.avi')
mux = bin.get_by_name('mux')
bus = bin.get_bus()
bus.add_signal_watch()
bus.connect('message::eos', on_eos)
# prepare
bin.set_state(gst.STATE_READY)
# send tags
l = gst.TagList()
l[gst.TAG_ARTIST] = "Unknown Genius"
l[gst.TAG_TITLE] = "Unnamed Artwork"
mux.merge_tags(l, gst.TAG_MERGE_APPEND)
# start playing
bin.set_state(gst.STATE_PLAYING)
try:
mainloop.run()
except KeyboardInterrupt:
pass
# stop the bin
bin.set_state(gst.STATE_NULL)
if __name__ == '__main__':
sys.exit(main(sys.argv))
@@ -0,0 +1,44 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# videomixer-controller.py
# (c) 2008 Stefan Kost <ensonic@users.sf.net>
# Test case for the GstController using videomixer and videotestsrc
import pygst
pygst.require('0.10')
import gst
import time
def main():
pipeline = gst.Pipeline("videocontroller")
src = gst.element_factory_make("videotestsrc", "src")
mix = gst.element_factory_make("videomixer", "mix")
conv = gst.element_factory_make("ffmpegcolorspace", "conv")
sink = gst.element_factory_make("autovideosink", "sink")
pipeline.add(src, mix, conv, sink)
spad = src.get_static_pad('src')
dpad = mix.get_request_pad('sink_%d')
spad.link(dpad)
mix.link(conv)
conv.link(sink)
control = gst.Controller(dpad, "xpos", "ypos")
control.set_interpolation_mode("xpos", gst.INTERPOLATE_LINEAR)
control.set_interpolation_mode("ypos", gst.INTERPOLATE_LINEAR)
control.set("xpos", 0, 0)
control.set("xpos", 5 * gst.SECOND, 200)
control.set("ypos", 0, 0)
control.set("ypos", 5 * gst.SECOND, 200)
pipeline.set_state(gst.STATE_PLAYING)
time.sleep(7)
if __name__ == "__main__":
main()
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# gst-python
# Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# A test more of gst-plugins than of gst-python.
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import pygst
pygst.require('0.10')
import gst
import fvumeter
def clamp(x, min, max):
if x < min:
return min
elif x > max:
return max
return x
class Window(gtk.Dialog):
def __init__(self):
gtk.Dialog.__init__(self, 'Volume Level')
self.prepare_ui()
def prepare_ui(self):
self.set_default_size(200,60)
self.set_title('Volume Level')
self.connect('delete-event', lambda *x: gtk.main_quit())
self.vus = []
self.vus.append(fvumeter.FVUMeter())
self.vus.append(fvumeter.FVUMeter())
self.vbox.add(self.vus[0])
self.vbox.add(self.vus[1])
self.vus[0].show()
self.vus[1].show()
def error(self, message, secondary=None):
m = gtk.MessageDialog(self,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK,
message)
if secondary:
m.format_secondary_text(secondary)
m.run()
def on_message(self, bus, message):
if message.structure.get_name() == 'level':
s = message.structure
for i in range(0, len(s['peak'])):
self.vus[i].freeze_notify()
decay = clamp(s['decay'][i], -90.0, 0.0)
peak = clamp(s['peak'][i], -90.0, 0.0)
if peak > decay:
print "ERROR: peak bigger than decay!"
self.vus[i].set_property('decay', decay)
self.vus[i].set_property('peak', peak)
return True
def run(self):
try:
self.set_sensitive(False)
s = 'alsasrc ! level message=true ! fakesink'
pipeline = gst.parse_launch(s)
self.set_sensitive(True)
pipeline.get_bus().add_signal_watch()
i = pipeline.get_bus().connect('message::element', self.on_message)
pipeline.set_state(gst.STATE_PLAYING)
gtk.Dialog.run(self)
pipeline.get_bus().disconnect(i)
pipeline.get_bus().remove_signal_watch()
pipeline.set_state(gst.STATE_NULL)
except gobject.GError, e:
self.set_sensitive(True)
self.error('Could not create pipeline', e.__str__)
if __name__ == '__main__':
w = Window()
w.show_all()
w.run()