paxdiablo
paxdiablo

Reputation: 881253

Certain GStreamer pipelines not considered to be a bin?

I can run a very simple launch pipeline from the command line thus:

gst-launch-1.0 videotestsrc ! ximagesink

and, from gst-inspect-1.0, the ximagesink appears to support the GstVideoOverlay interface so that I can bind it to a specific Gtk widget.

However, when trying to do that from within some code I happened to find lying around on the net, it seems that the pipeline is not considered to be a bin (and hence, the widget ID is not being given to it).

The code to do it is as follows, first to create the pipeline and set it up to capture bus messages:

Gst.Element playbin = Gst.Parse.Launch("videotestsrc ! ximagesink");
Gst.Bus bus = playbin.Bus;
bus.AddSignalWatch();
bus.Message += MsgCallback;

Then to actually process the bus messages:

private void MsgCallback(object o, MessageArgs args) {
    // Only care about window ID binding requests.

    Gst.Message msg = args.Message;
    if (! Gst.Video.Global.IsVideoOverlayPrepareWindowHandleMessage(msg))
        return;

    // Get source of message.

    Gst.Element src = msg.Src as Gst.Element;
    if (src == null)
        return;

    // Find element supporting interface and notify it to bind.

    Gst.Element ov = null;
    if (src is Gst.Bin) {
        ov = ((Gst.Bin) src).GetByInterface(VideoOverlayAdapter.GType);
        VideoOverlayAdapter ad = new VideoOverlayAdapter(ov.Handle);
        ad.WindowHandle = windowXId;
    }
}

Now, for some reason, the src is Gst.Bin is false, meaning that the windowXId (the widget ID I've previously set) is never being communicated to GStreamer.

However, if I provide a playbin pipeline (playbin uri=XYZZY videosink='videoconvert ! videoflip method=none ! videoconvert ! autovideosink', if you're interested), it works fine.

As far as I can tell from the documentation for Gst.Parse.Launch(), it should give me a pipeline which is a special case of a bin, as per here (after fixing the atrocious grammar):

Returns a new element on success, NULL on failure. If more than one top level element is specified by the pipeline description , all elements are put into a GstPipeline, which is then returned.

I'm pretty certain that videotestsrc and ximagesink are two separate top-level elements but, when I add the following code, after the check for src being null:

Console.WriteLine("is bin      " + (src is Gst.Bin));
Console.WriteLine("is element  " + (src is Gst.Element));
Console.WriteLine("is pipeline " + (src is Gst.Pipeline));
Console.WriteLine(type is      " + src.GetType());

I see:

is bin      False
is element  True
is pipeline False
type is     Gst.Element

for the problematic videotestsrc pipeline and the following for the good playbin one:

is bin      True
is element  True
is pipeline False
type is     Gst.Bin

So everything points to the problematic one giving an element rather than a bin, despite what the documentation states.

What am I missing here? What is the difference between the two pipelines that would cause different behaviour?

Upvotes: 0

Views: 1577

Answers (1)

paxdiablo
paxdiablo

Reputation: 881253

Okay, it turns out that the Gst.Parse.Launch function is actually returning a pipeline. This can be seen if you copy those checking statements immediately after the bin creation:

Gst.Element playbin = Gst.Parse.Launch("videotestsrc ! ximagesink");
Console.WriteLine("is bin      " + (playbin is Gst.Bin));
Console.WriteLine("is element  " + (playbin is Gst.Element));
Console.WriteLine("is pipeline " + (playbin is Gst.Pipeline));
Console.WriteLine(type is      " + playbin.GetType());

and you see:

is bin      True
is element  True
is pipeline True
type is     Gst.Pipeline

It's just that, when we get the message in the callback, the source seems to be set to an element instead. I haven't actually figured out why the message source is coming through as an element (or even which element it is) but I at least have a workaround, thanks partly to my advanced intelligence but mostly to a sneaky look at the way Banshee does it :-)

Turns out that the callback can simply access the bin directly rather than trying to get it out of the source. This works in my case since I only have the one video stream (bin) per instance. If you have multiples, it may be a little more difficult.

To achieve this, you first make playbin a member variable (if it isn't already), and you can then modify the callback thus:

private void MsgCallback(object o, MessageArgs args) {
    // Only care about window ID binding requests.

    Gst.Message msg = args.Message;
    if (! Gst.Video.Global.IsVideoOverlayPrepareWindowHandleMessage(msg))
        return;

    // Get instance bin, interface element, then notify it to bind.

    Gst.Bin src = (Gst.Bin)(this.playbin);
    if (src == null) return;

    Gst.Element ov = src.GetByInterface(VideoOverlayAdapter.GType);
    if (ov == null) return;

    VideoOverlayAdapter ad = new VideoOverlayAdapter(ov.Handle);
    if (ad == null) return;

    ad.WindowHandle = windowXId;
}

Upvotes: 1

Related Questions