Skip to content

Instantly share code, notes, and snippets.

@dlbas
Created November 10, 2019 16:32
Show Gist options
  • Save dlbas/392dd262fa84c350ae27ec5f85de1102 to your computer and use it in GitHub Desktop.
Save dlbas/392dd262fa84c350ae27ec5f85de1102 to your computer and use it in GitHub Desktop.
//
// Created by daniel on 09/11/2019.
//
#include <gst/gst.h>
typedef struct _CustomData {
GstElement *pipeline;
GstElement *v4l2src;
GstElement *caps_filter1;
GstElement *decodebin;
GstElement *ximagesrc;
GstElement *caps_filter3;
GstElement *videoscale;
GstElement *caps_filter4;
GstElement *alpha;
GstElement *videobox;
GstElement *videotestsrc;
GstElement *caps_filter2;
GstElement *videomixer;
GstElement *videoconvert;
GstElement *xvimagesink;
} CustomData;
#define BALL_PATTERN 18
static void decodebin_pad_added(GstElement *src, GstPad *new_pad, CustomData *data);
int main(int argc, char *argv[]) {
CustomData data;
GstBus *bus;
GstMessage *msg;
GstStateChangeReturn ret;
gboolean terminate = FALSE;
GstCaps *caps;
GstPad *mixerpad_1, *mixerpad_2, *mixerpad_3;
GstPad *videotestsrc_pad, *videobox_pad;
// initialize
gst_init(&argc, &argv);
// create elements
data.v4l2src = gst_element_factory_make("v4l2src", NULL);
data.caps_filter1 = gst_element_factory_make("capsfilter", "caps_filter1");
data.decodebin = gst_element_factory_make("decodebin", NULL);
data.ximagesrc = gst_element_factory_make("ximagesrc", NULL);
data.caps_filter3 = gst_element_factory_make("capsfilter", "caps_filter3");
data.videoscale = gst_element_factory_make("videoscale", NULL);
data.caps_filter4 = gst_element_factory_make("capsfilter", "caps_filter4");
data.alpha = gst_element_factory_make("alpha", NULL);
data.videobox = gst_element_factory_make("videobox", NULL);
data.videotestsrc = gst_element_factory_make("videotestsrc", NULL);
data.caps_filter2 = gst_element_factory_make("capsfilter", "caps_filter2");
data.videomixer = gst_element_factory_make("videomixer", "mix");
data.videoconvert = gst_element_factory_make("videoconvert", NULL);
data.xvimagesink = gst_element_factory_make("xvimagesink", NULL);
// set element properties
g_object_set(data.xvimagesink, "sync", FALSE, NULL);
g_object_set(data.v4l2src, "device", "/dev/video0", NULL);
g_object_set(data.videotestsrc, "pattern", BALL_PATTERN, NULL);
g_object_set(data.ximagesrc, "startx", 0, NULL);
g_object_set(data.ximagesrc, "use-damage", 0, NULL);
g_object_set(data.videoscale, "method", 0, NULL);
g_object_set(data.alpha, "alpha", 1.0, NULL);
g_object_set(data.videobox, "border-alpha", 0.0, NULL);
g_object_set(data.videobox, "top", -600, NULL);
g_object_set(data.videobox, "left", -1280, NULL);
// set appropriate caps
caps = gst_caps_new_simple("image/jpeg",
"framerate", GST_TYPE_FRACTION, 30, 1,
"width", G_TYPE_INT, 640,
"height", G_TYPE_INT, 480,
NULL);
g_object_set(data.caps_filter1, "caps", caps, NULL);
caps = gst_caps_new_simple("video/x-raw",
"framerate", GST_TYPE_FRACTION, 30, 1,
"width", G_TYPE_INT, 1920,
"height", G_TYPE_INT, 1080,
NULL);
g_object_set(data.caps_filter2, "caps", caps, NULL);
caps = gst_caps_new_simple("video/x-raw",
"framerate", GST_TYPE_FRACTION, 30, 1,
NULL);
g_object_set(data.caps_filter3, "caps", caps, NULL);
caps = gst_caps_new_simple("video/x-raw",
"width", G_TYPE_INT, 640,
"height", G_TYPE_INT, 480,
NULL);
g_object_set(data.caps_filter4, "caps", caps, NULL);
data.pipeline = gst_pipeline_new("pipeline");
if (!data.v4l2src || !data.decodebin || !data.ximagesrc || !data.videoscale || !data.alpha || !data.videobox ||
!data.videotestsrc || !data.videomixer || !data.videoconvert || !data.xvimagesink || !data.caps_filter1 ||
!data.caps_filter2 ||
!data.caps_filter3 || !data.caps_filter4) {
g_printerr("Not all element could be created\n");
return -1;
}
gst_bin_add_many(GST_BIN(data.pipeline),
data.v4l2src,
data.caps_filter1,
data.decodebin,
data.ximagesrc,
data.caps_filter3,
data.videoscale,
data.caps_filter4,
data.alpha,
data.videobox,
data.videotestsrc,
data.caps_filter2,
data.videomixer,
data.videoconvert,
data.xvimagesink,
NULL);
if (
!gst_element_link_many(data.videomixer, data.videoconvert, data.xvimagesink, NULL) ||
!gst_element_link_many(data.v4l2src, data.caps_filter1, data.decodebin, NULL) ||
!gst_element_link_many(data.videotestsrc, data.caps_filter2, NULL) ||
!gst_element_link_many(data.ximagesrc, data.caps_filter3, data.videoscale, data.caps_filter4, data.alpha,
data.videobox, NULL)
) {
g_printerr("Elements could not be linked.\n");
gst_object_unref(data.pipeline);
return -1;
}
// link on-request pads
// this one for decodebin
// NOTE: linking of decodebin pad should be done in callback
// this one for videotestsrc
mixerpad_2 = gst_element_get_request_pad(data.videomixer, "sink_%u");
videotestsrc_pad = gst_element_get_static_pad(data.caps_filter2, "src");
if (gst_pad_link(videotestsrc_pad, mixerpad_2) != GST_PAD_LINK_OK) {
g_printerr("Videotestsrc pad could not be linked\n");
g_object_unref(data.pipeline);
return -1;
}
// this one for videobox
mixerpad_3 = gst_element_get_request_pad(data.videomixer, "sink_%u");
videobox_pad = gst_element_get_static_pad(data.videobox, "src");
if (gst_pad_link(videobox_pad, mixerpad_3) != GST_PAD_LINK_OK) {
g_printerr("Videobox pad could not be linked\n");
g_object_unref(data.pipeline);
return -1;
}
// connect to the pad-added signal
g_signal_connect(data.decodebin, "pad-added", G_CALLBACK(decodebin_pad_added), &data);
// start playing
ret = gst_element_set_state(data.pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr("Unable to set the pipeline to the playing state.\n");
gst_object_unref(data.pipeline);
return -1;
}
// listen to the bus
bus = gst_element_get_bus(data.pipeline);
do {
msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE,
GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
// parse message
if (msg != NULL) {
GError *err;
gchar *debug_info;
switch (GST_MESSAGE_TYPE(msg)) {
case GST_MESSAGE_ERROR:
gst_message_parse_error(msg, &err, &debug_info);
g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");
g_clear_error(&err);
g_free(debug_info);
terminate = TRUE;
break;
case GST_MESSAGE_EOS:
g_print("End-Of-Stream reached.\n");
terminate = TRUE;
break;
case GST_MESSAGE_STATE_CHANGED:
/* We are only interested in state-changed messages from the pipeline */
if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data.pipeline)) {
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state);
g_print("Pipeline state changed from %s to %s:\n",
gst_element_state_get_name(old_state), gst_element_state_get_name(new_state));
}
break;
default:
/* We should not reach here */
g_printerr("Unexpected message received.\n");
break;
}
gst_message_unref(msg);
}
} while (!terminate);
// free resources
gst_object_unref(bus);
gst_element_set_state(data.pipeline, GST_STATE_NULL);
gst_object_unref(data.pipeline);
return 0;
}
static void decodebin_pad_added(GstElement *src, GstPad *new_pad, CustomData *data) {
GstPad *mixer_pad = gst_element_get_request_pad(data->videomixer, "sink_%u");
GstPadLinkReturn ret;
const gchar *new_pad_type = NULL;
g_print("Received new pad '%s' from '%s':\n", GST_PAD_NAME (new_pad), GST_ELEMENT_NAME (src));
if (gst_pad_is_linked(new_pad)) {
g_print("Pad is already linked - ignoring\n");
goto exit;
}
ret = gst_pad_link(new_pad, mixer_pad);
if (GST_PAD_LINK_FAILED(ret)) {
g_print("Type is %s, but link has failed.\n", new_pad_type);
goto exit;
} else {
g_print("Link has been successful (type '%s').\n", new_pad_type);
}
exit:
/* Unreference the sink pad */
gst_object_unref(mixer_pad);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment