/tinyletter

Process This Week's Program segmentation fault 11

This Week’s Program: May 1 - May 5

I made a lot of progress this week and hit a couple of big milestones on this project. I also crashed a whole lot of processes.

ba751844ebdff7c8a0f35a2ad5b9cb9c498dd438

Whenever a GObject pointer is transformed into its Racket representation from C, it is wrapped in a deallocator so that the resource is automatically released when it becomes inaccessible. Notably in this commit, I pull in libgobject as another FFI lib to utilize which will come into play more later.

5f1db6dbac49beddb9ff0d42406c7bcba3edd9f0

I define and expose the connect function, which is for wiring up a GObject Signal. Using libgobject that I required previously, I create an FFI binding for g_signal_connect_data. The connect Racket function takes a GObject, a Signal name, and a Procedure and will cast the procedure to a C function pointer (deriving its type from GObject Introspection). There’s a lot wrong with this initial implementation, as I would soon find out later, but I was able to observe that I could create a Racket lambda and see it called later when C emitted the signal, which was exciting.

395d0d07ee77b2b5836c4e3bad57bbf1e59f18bc

I played a video using my Racket bindings to the GStreamer library. Major milestone accomplished! Using nothing but my own ffi/unsafe/introspection library, I was able to get GStreamer working enough to go through Basic Tutorial 1: Hello World!. I played the movie trailer for Thor: Ragnarok.

What’s more, because this is Racket, I could do that through my REPL within Emacs. I call (send pipeline set-state 'playing) to start the video and (send pipeline set-state 'null) to end it and close the window. It just works. That was Monday.

The official GStreamer C tutorial has you playing a WebM file and I couldn’t get that to work. I fiddled with it a bit before just trying a Mov. Later I would try running this playbin with that WebM file through gst-launch and saw this error message:

ERROR: from element /GstPlayBin:playbin0/GstURIDecodeBin:uridecodebin0: Your GStreamer installation is missing a plug-in.

When I installed the gstreamer plugins through Homebrew, I did not build gst-plugins-base with libogg or libvorbis support. I haven’t tried to do that but I think that would fix this WebM problem. Moving on…

d7ad2a7f4311d2aa79d9a31e1f183d27bf7cb8c2

A major component of GStreamer’s API is setting GObject properties on Elements. That’s how you configure an Element to do a thing. In the previous tutorial, I set the uri property on the playbin Element when constructing it. Because of GStreamer’s plugin nature, GObject Introspection does not know about all the available properties for a particular Element instance.

Using libgobject again, I create bindings for two C functions used for getting and setting GObject properties: g_object_get and g_object_set. To make the names more idiomatic Racket, I call them gobject-get and gobject-set!. Because I don’t know the types of the properties ahead of time, I have the user explicitly pass them in. I use these to complete the next tutorial.

33f962af89bee594c30ac6d7045c122d66142d29

Basic Tutorial 2: GStreamer concepts has us use the videotestsrc to create a stream of a test video. You can set which test pattern by setting a property on the source element. Within Racket, I can do this now with

(gobject-set source 'pattern 'smpte _test-pattern)

Where _test-pattern is a C enum representing the different choices of pattern.

I made a gif for you!

A video test pattern

Later on, I expanded the tutorial by adding a vertigotv filter. It looks like this:

The same video test pattern, but with a funky filter on it

85434b6b813aab59d478757e11aa0255dd62806c

After my great success with Tutorial 2, I began work on Basic Tutorial 3: Dynamic pipelines. This tutorial has you connecting a signal to an element and doing something to that element within the callback so that the video (or in this case, audio) can complete passing through the pipeline.

I crashed my process. Over and over and over and over again.

Segmentation fault: 11

I couldn’t figure out why; I was at a total loss. I was able, through continuously bashing my keyboard and the process of elimination, learn that the source of this fault was in my signal handler. I tried a few different things, like making sure my callback was retained in memory properly. A segfault is typically unearthed when you try to access some memory that isn’t there or is NULL.

Finally, I opened up the macOS Console application (which got a nice little upgrade in Sierra). Diagnostic Reports for program crashes are stored in ~/Library/Logs/DiagnosticReports and from there I could pull up the stacktrace of my Racket program.

The Diagnostic Report of my crashed Racket process

From here, it says that the Crashed Thread was number 13. Scrolling down…

The stack trace of the crashed thread of my Racket process

Here is the stack trace of the crashed thread. I can see here, that the g_element_add_pad is called in C, as I would expect it to. This in turn causes a g_signal_emit call. This continues up into the FFI calls from libffi then up into Racket and then — crash. At ffi_do_callback. So it’s able to get just up to the point of calling my procedure and then poof there’s nothing there.

After some googling I come across this thread on the Racket mailing list from 2010.

There was no way to use a Racket procedure as a C callback that is invoked a foreign thread. That possibility seems useful, though, and it requires an extension to the FFI, so I’ve added something minimal.

Back to this commit! For my Signal handler callback, when determining the C representation of the handler, I add the #:async-apply keyword. With this keyword, my Racket procedure handler can be used in a foreign thread. Because GStreamer is heavily threaded, this little mechanism allows GStreamer’s threads to await my Racket process when the signal callback is being applied. This is some seriously unsafe stuff (in the memory sense), as the callback is limited to what kinds of things it can do. While I’m in this code, I also make a little update to make sure that my Signal handling is correct: the first argument to a signal callback is always the instance it’s connected to and the last argument to the callback is arbitrary user-supplied data.

838471bf94c020d128c281a672c19a2514a09c96

Now I can complete the third tutorial. In this tutorial, I download the Thor movie trailer, but this time I open up the container when it is available and just get the audio portion. And I hear Jeff Goldblum’s sweet melodious voice. And life is good.

The commits I made today allow me to better infer types for the user data passed into the callback function.

I’m really proud of what I was able to accomplish this week. All of this low-level C stuff just to be able to use GStreamer in a way that feels natural in Racket. I’m able to do this without using the functions from ffi/unsafe at all — I’m only interacting with the API provided by my GObject Introspection library. So far, it’s working brilliantly.

So far this only has been working from within the Emacs REPL. Here, I can call the functions to play, pause, and stop with ease. DrRacket crashes when I try this. When I try to run this from the command line with racket, it just hangs forever. I think this is because everything is confined to the main thread, and when I block the main thread to await the audio to complete, I’m also blocking my Signal callback from being invoked. I need to start introducing threading to this program.

But for now I’ll just keep listening to Jeff Goldblum.

🔨 Mark