Python gstreamer controller
I needed a GstController to control the alpha channel on a video crossfade. The gstreamer documentation makes it look easy, but it actually tripped me up for quite some time. I am using the python bindings, and they have some subtlies that took me some time to figure out.
First, I got the general usage out of the Pitivi source code. To create a controller, you do the following:
controller = gst.Controller(alpha, "alpha")
controller.set_interpolation_mode("alpha", gst.INTERPOLATE_LINEAR)
controller.set("alpha", 0, 0.0)
controller.set("alpha", 5*gst.SECOND, 1.0)
In the above example, alpha is the element to be controlled. This causes the alpha property on the alpha element to linearly change from 0.0 to 1.0 over the course of 5 seconds.
After adding the above code snippet to my application, however, it did not work. To debug it, I ended up checking out the gstreamer source code and after tracing things through using printf()’s, I finally discovered that my controller was getting attached to the alpha as I wanted but then it was getting detached almost immediately thereafter. That made me realize that in python I needed to hold a reference to the controller for the duration of the stream. I was letting the controller variable in the above example go out of scope, at which point it had no more references and therefore got deleted and removed from the alpha element. So the solution was to hold a reference to it as shown below in the example of a video crossfade:
import os, time
import gobject
import pygst
pygst.require("0.10")
import gst
class Main:
def __init__(self):
src1 = gst.element_factory_make("filesrc")
src2 = gst.element_factory_make("filesrc")
src1.set_property("location", "/home/lane/test1.mp4")
src2.set_property("location", "/home/lane/test2.mp4")
dec1 = gst.element_factory_make("decodebin2")
dec2 = gst.element_factory_make("decodebin2")
alpha1 = gst.element_factory_make("alpha")
alpha2 = gst.element_factory_make("alpha")
self.controller = gst.Controller(alpha2, "alpha")
self.controller.set_interpolation_mode("alpha", gst.INTERPOLATE_LINEAR)
self.controller.set("alpha", 0, 0.0)
self.controller.set("alpha", 5*gst.SECOND, 1.0)
mixer = gst.element_factory_make("videomixer")
queue = gst.element_factory_make("queue")
color = gst.element_factory_make("ffmpegcolorspace")
sink = gst.element_factory_make("autovideosink")
pipeline = gst.Pipeline("pipeline")
pipeline.add(src1, src2, dec1, dec2, alpha1, alpha2, mixer, queue, color, sink)
def on_pad(comp, pad, data, element):
sinkpad = element.get_compatible_pad(pad, pad.get_caps())
pad.link(sinkpad)
dec1.connect("new-decoded-pad", on_pad, alpha1)
dec2.connect("new-decoded-pad", on_pad, alpha2)
src1.link(dec1)
src2.link(dec2)
alpha1.link(mixer)
alpha2.link(mixer)
mixer.link(queue)
queue.link(color)
color.link(sink)
self.pipeline = pipeline
def start(self):
self.running = True
bus = self.pipeline.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect("message", self.on_message)
self.pipeline.set_state(gst.STATE_PLAYING)
def on_exit(self, *args):
self.pipeline.set_state(gst.STATE_NULL)
self.running = False
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.on_exit()
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
self.on_exit()
loop = gobject.MainLoop()
gobject.threads_init()
context = loop.get_context()
m = Main()
m.start()
while m.running:
context.iteration(True)
Some things to note in the above code:
- In order to prevent the
controllerfrom going out of scope and getting deleted, I added it as an attribute to theMainobject (i.e.self.controller). - I am manually iterating the mainloop. This is so that I do not have to create any gtk stuff. I recently discovered this technique and I use it in command line tools that do not require X11 or a
$DISPLAYenvironment variable set.
September 8th, 2011 at 12:42 am
Hi, I am totaly new programming with GStreamer and with audiovisual issues as well. I don’t get the alpha channels strategy. How is the behavior of these channels with the video. I understand de alpha2 get into scene at a certain time to help the crossfade phase. I appreciate your response, thanks.
September 8th, 2011 at 1:00 am
Sorry I refer to crossfade example, but I couldn’t post a comment.
Regards
September 8th, 2011 at 2:34 pm
Hello, actually I saw your crossfade example, I am new programming with GStreamer and with audio/video issues. I don’t get why you don’t use a decodebin2 or player in that example as you use in this one.
Second, could you please explain me which rol and the strategy the alpha channel take?.
Thanks a lot in advance.
Rossana
September 8th, 2011 at 2:47 pm
Rossanna,
See http://en.wikipedia.org/wiki/Alpha_compositing
I do not use a decodebin2 in the crossfade example because I use generated video streams.
September 8th, 2011 at 7:12 pm
First, thanks for your response. Second, sorry for too many posts. I didn’t see the previous one when I check your blog, I thought they got lost.
I wasn’t clear enough. actually I know what the alpha channel is. I don’t know how to ensamble these channel with the video streamer, there’s no connection between the video sources and the operation bin which has the alpha channels. I assume alpha1 ‘runs’ first, since alpha2 has a start time.
My question it’s more focused on how GStreamer elements work in this example. Thanks a lot, best regards.
Rossana