I was writing some tests for a project at Igalia and I need to mock the convert-frame playbin2 element action. The code to invoke it is something like this:
GstElement *pipeline = /* get pipeline */; GstCaps *caps = /* create caps to adapt the conversion */; GstBuffer *buffer = NULL; g_signal_emit_by_name (pipeline, "convert-frame", caps, &buffer);
When you are writing tests, what you want to do is testing just your code and not to depend on something external, so in this case the idea would be providing a fake implementation for that GStreamer element action.
The way you can do this kind of things is providing the symbol in your code so that the linker when doing its job does not look any further and uses that instead of the one in the external library, so the natural solution coming to your mind would be rewriting g_signal_emit_by_name
. The problem with this is that though you are not using it in your code, it is too general, so it is not a good idea.
I thought I could replace the convert-frame action in the playbin2 class, so I wrote this code:
typedef struct { GstPipelineClass parent_class; void (*about_to_finish) (gpointer playbin); void (*video_changed) (gpointer playbin); void (*audio_changed) (gpointer playbin); void (*text_changed) (gpointer playbin); void (*video_tags_changed) (gpointer playbin, gint stream); void (*audio_tags_changed) (gpointer playbin, gint stream); void (*text_tags_changed) (gpointer playbin, gint stream); GstTagList *(*get_video_tags) (gpointer playbin, gint stream); GstTagList *(*get_audio_tags) (gpointer playbin, gint stream); GstTagList *(*get_text_tags) (gpointer playbin, gint stream); GstBuffer *(*convert_frame) (gpointer playbin, GstCaps * caps); GstPad *(*get_video_pad) (gpointer playbin, gint stream); GstPad *(*get_audio_pad) (gpointer playbin, gint stream); GstPad *(*get_text_pad) (gpointer playbin, gint stream); } GstPlayBinClass; static gpointer gst_play_bin_convert_frame (G_GNUC_UNUSED gpointer playbin, G_GNUC_UNUSED gpointer caps) { GstBuffer *buffer; /* Create my own GstBuffer with the data I need */ return buffer; } void simulator_gst_reset(GstElement *new_pipeline, GstBus *new_bus) { /* ... */ GstPlayBinClass *klass = G_TYPE_INSTANCE_GET_CLASS(new_pipeline, GST_PLAY_BIN_TYPE, GstPlayBinClass); klass->convert_frame = (gpointer) gst_play_bin_convert_frame; /* ... */ }
First I declared the GstPlayBinClass copying it from the GStreamer code. I didn’t change any parameters order, just replaced some pointers with gpointer as we don’t need them. This way you don’t break the ABI. Then you can declare your own element action code and finally you get the Class, assign the method and voilĂ !.
As I said, the solution is far from being the best, but if you know a better way, drop me a comment.