/tinyletter

The Programs of the Hap-Happiest Season of All

This Week’s Program: Nov 27 - Dec 1

Hello there friends! It’s been awhile! I haven’t written my Tinyletter since the week of Halloween; I’ve been focused on writing some code. Actually, focused isn’t quite right. It would be more accurate to say I’m nearing Ahab-levels of obsession, picking at some unexpected behavior and working deep in the bowels of GStreamer.

Here’s the main problem: Recall that GStreamer moves media in a pipeline from a source to a sink. The primary video sink available to me is the osxvideosink element, which is designed to display a video inside of a macOS window. Whenever I use this element either in DrRacket or the racket command line, the program crashes.

The alternative to osxvideosink, without pulling in X11, is to use something called a glimagesink. As you might tell from the name, this sink processes video as OpenGL textures. For this sink to work well, I need to call a function from GStreamer’s GstVideoOverlay class called gst_video_overlay_set_window_handle. I can pass a pointer to a window to this function and let my window manage the sink.

To get this machinery set-up for using and understanding this sink, I first need to get a better grasp of using Signals with my GObject Introspection library.

5e7b48bc73bb7f9e7de42de6e4d1a9324d119b9f

Introspection data only gives a picture of what the program is doing. It describes the types a thing could be, but not what it actually is. In the case of making a glimagesink, creating one actually gives me an instance of an object that extends several different C interfaces, including the GstVideoOverlay interface. To connect a signal, instead of using the introspected type of this instance, I need to get its true type — it’s GType. This commit helps me get that.

Some type punning and some ffi wiring helps me write the gobject-gtype function. Given a GObject instance, I can now get to its true GType.

303a9da2c3833a932a51b61417779627a4fedcd7

From there, I can look up and query a signal for more information, like the GTypes of its parameters and return type.

gstreamer/gui

This is where the madness begins. I do some other stuff in the following weeks: set up devices, wire up some message parsing… it’s all build up to this. This most frustrating behavior.

I create gstreamer/gui to house some of my work on bridging GstVideoOverlay with glimagesink. This pulls in the racket/gui metalanguage. My thinking here is that I should use the windowing toolkit provided by Racket itself in order to house this GStreamer element.

It works and it also doesn’t.

I create a window by instantiating a frame% object, then have that be a parent to a canvas% object. I call (send canvas get-client-handle) to get a pointer to the canvas and attach that to the above described set_window_handle function.

It works in that I can see an image in my freshly created Racket GUI window. It doesn’t work in that the image is not animating. It just sits there.

Why isn’t it animating? This question would drive me absolutely nuts for a week+ (it’s still driving me nuts). Here are the names of some of the methods I call to try to understand this behavior better: refresh, refresh-now, flush, resume-flush, swap-gl-buffers, on-paint. These are all real method names in the Racket GUI framework and none of them seemed to do anything. When I had a friend try this code on Linux, their window animated without much fanfare but then crashed. What on earth is going on?

The layers of indirection are so dense that, at this point, I have no idea what’s going on. Between the Racket code interfacing with Cocoa working with the GStreamer framework that’s being called though my Introspection layer there’s just too many variables to reason about what the hell is going on. And somewhere in the middle of all this is a double buffer waiting to be swapped.

I’ve learned more about OpenGL and GUI toolkits in the past week than over my entire career.

I gave up on glimagesink. Here’s where I am now:

appsink%

I create a new subclass of element% called appsink%. This class will correspond to a GStreamer element conveniently called appsink. The appsink element is used to extract data out from the pipeline into the application. GStreamer provides an API, GstAppSink, for grabbing that data from the sink.

My goal is to use this element, along with some of these other GStreamer gimmicks I’ve picked up over the past couple of weeks, to find a way to draw a video to a window.

The result will hopefully be a reliable, cross-platform way to view a video as it streams. This seems like an awful lot of hard work to do that, but it really seems important.

Check back in next week for some sick C/Racket FFI kickflips.

— Mark