Reputation: 881253
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 aGstPipeline
, 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
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