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!
Later on, I expanded the tutorial by adding a vertigotv
filter. It looks like this:
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.
From here, it says that the Crashed Thread was number 13. Scrolling down…
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