GTK app has huge memory footprint, can't find any leaks - c

I have a simple GTK-app. I created the design with glade (it is linked as RO-data into the binary) and load it with gtk_builder_new_from_string. In another c-file all the callback-functions are implemented.
Loading the glade xml-file:
builder = gtk_builder_new_from_string(GUI_CRAZYCART_GLADE_FILE, (gssize) GUI_CRAZYCART_GLADE_SIZE);
widgets->window = GTK_WINDOW(gtk_builder_get_object(builder, "CrazyCart"));
assert(widgets->window != NULL);
widgets->win_print = GTK_WINDOW(gtk_builder_get_object(builder, "win_print"));
assert(widgets->win_print != NULL);
... // more of the lines above, all the widgets I need
gtk_builder_connect_signals(builder, widgets);
g_object_unref(builder);
gtk_widget_show_all(GTK_WIDGET(widgets->window));
gtk_widget_hide(widgets->wdgt_email_pw);
gtk_window_maximize(widgets->window);
gtk_window_fullscreen(widgets->window);
// MAIN LOOP - APP WAITS HERE UNTIL EXIT
gtk_main();
// MAIN LOOP - APP WAITS HERE UNTIL EXIT
Similarly with the CSS-files, etc.
However, when I press just random buttons and watch the application with htop I can see the used memory (RES) rise up very quickly. The buttons essentially don't do anything.
I would like to run it on a raspberry pi3, but after some time executing it (it is controlling a racing game on karts) it crashes, because no memory is left.
I checked already with valgrind, but it outputs a logfile several MB large, it makes it very difficult to comb through it (using online available supp-files didn't help much) and I could only find "leaks" which are one time inits by external libraries, like gstreamer, libfontconfig, gtk, etc. Also, gcc's sanitizer outputs not much of significance (mostly libfontconfig because I am using the toy-functions).
It is approaching 1GB RES-memory after 30min of usage. It slowing down after that a little, however still rising. Also, repeatedly focusing and unfocusing the window rises the memory usage.
Sample-callback:
void VISIBILITY_DEFAULT on_kart_color_editing (
GtkTreeView *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column,
App_Widgets *widgets)
{
const char *title = gtk_tree_view_column_get_title(column);
if (hash(title) != hash(_("Colour"))) return;
GtkTreeIter iter;
GdkRGBA color;
guint cart_id;
guint id;
char *name;
gui_show_color_dialog(widgets->window, &color);
gtk_tree_model_get_iter(GTK_TREE_MODEL(widgets->tv_car_store), &iter, path);
gtk_list_store_set(widgets->tv_car_store, &iter, COL_CAR_COLOR, &color, -1);
gtk_tree_model_get(GTK_TREE_MODEL(widgets->tv_car_store), &iter, COL_CAR_DRIVER, &name, COL_KARTNUM, &cart_id, COL_CAR_ID, &id, -1);
gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(tree_view));
game_change_player((uint8_t) id, (uint8_t) (cart_id - 1U), gdkrgba_to_argb(&color), name);
g_free(name);
}
I have also tried finding glib objects with reference-counter > 0 using this post however again most leaks were due to objects from external libs (and a huge file).
It seems as gstreamer/my sound playing code is one of the reasons for the increasing memory usage
sound.c, for gstreamer:
struct snd_data_t
{
GstElement *pipeline, *pitch;
GstElement *source;
guint bus_watch_id;
void (*cb_start)(void *);
void (*cb_end)(void *);
void *data_start;
void *data_end;
};
// return const, because user mustn't free or alter it!
const void *snd_play_mem(const char *buf, size_t len, void (*cb_start)(void *), void *data_start, void (*cb_end)(void *), void *data_end)
{
GstElement *source;
GstBuffer *gst_buffer;
char *buf_cloned;
struct snd_data_t *snd_data;
GstCaps *caps_ogg;
snd_data = emalloc(sizeof *snd_data);
source = gst_element_factory_make("appsrc", "mem-source");
caps_ogg = gst_caps_new_empty_simple("audio/ogg");
gst_app_src_set_caps((GstAppSrc*) source, caps_ogg);
gst_app_src_set_size((GstAppSrc*) source, (gint64) len);
gst_app_src_set_stream_type((GstAppSrc*) source, GST_APP_STREAM_TYPE_STREAM);
gst_app_src_set_max_bytes((GstAppSrc*) source, len);
gst_caps_unref(caps_ogg);
buf_cloned = g_memdup(buf, (guint) len);
gst_buffer = gst_buffer_new_wrapped(buf_cloned, len);
gst_app_src_push_buffer((GstAppSrc*) source, gst_buffer);
gst_app_src_end_of_stream((GstAppSrc*) source);
snd_data->source = source;
snd_data->cb_start = cb_start;
snd_data->cb_end = cb_end;
snd_data->data_start = data_start;
snd_data->data_end = data_end;
snd_thread_play(snd_data);
return snd_data;
}
// return const, because user mustn't free or alter it!
const void *snd_play_file(const char *path, void (*cb)(void *), void *data)
{
GstElement *source;
struct snd_data_t *snd_data;
snd_data = emalloc(sizeof *snd_data);
source = gst_element_factory_make("filesrc", "file-source");
g_object_set(G_OBJECT(source), "location", path, NULL);
snd_data->source = source;
snd_data->cb_end = cb;
snd_data->data_end = data;
snd_data->cb_start = NULL;
snd_data->data_start = NULL;
snd_thread_play(snd_data);
return snd_data;
}
void snd_stop(const void *data)
{
if (data == NULL) return;
// ugly hack, because const (user mustn't free or alter it)!
struct snd_data_t *snd_data = (struct snd_data_t *)(intptr_t) data;
gst_element_set_state(snd_data->pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(snd_data->pipeline));
g_source_remove(snd_data->bus_watch_id);
free(snd_data);
}
void snd_set_speed(const void *data, double speed)
{
if (data == NULL) return;
// ugly hack, because const (user mustn't free or alter it)!
struct snd_data_t *snd_data = (struct snd_data_t *)(intptr_t) data;
g_object_set(snd_data->pitch, "pitch", speed, "tempo", speed, NULL);
}
static void *snd_thread_play(void *data)
{
GstElement *source, *decoder, *conv, *pitch, *sink;
GstBus *bus;
struct snd_data_t *snd_data;
snd_data = data;
snd_data->pipeline = gst_pipeline_new("CrazyCart");
source = snd_data->source;
decoder = gst_element_factory_make("decodebin", "decoder");
conv = gst_element_factory_make("audioconvert", "converter");
pitch = gst_element_factory_make("pitch", "pitcher");
sink = gst_element_factory_make("autoaudiosink", "audio-output");
if (!snd_data->pipeline || !source || !decoder || !conv || !sink || !pitch)
{
fprintf(stderr, "%s\n", _("'gst_element_factory_make' failed"));
exit(EXIT_FAILURE);
}
snd_data->pitch = pitch;
bus = gst_pipeline_get_bus(GST_PIPELINE(snd_data->pipeline));
snd_data->bus_watch_id = gst_bus_add_watch(bus, snd_bus_call, snd_data);
gst_object_unref(bus);
gst_bin_add_many(GST_BIN(snd_data->pipeline), source, decoder, conv, pitch, sink, NULL);
gst_element_link_many(source, decoder, NULL);
gst_element_link_many(conv, pitch, sink, NULL);
g_signal_connect(decoder, "pad-added", G_CALLBACK(snd_on_pad_added), conv);
gst_element_set_state(snd_data->pipeline, GST_STATE_PLAYING);
#ifdef DEBUG
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(snd_data->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "crazycart");
#endif
return NULL;
}
static void snd_on_pad_added(
__attribute__ ((unused)) GstElement *element,
GstPad *pad,
gpointer data
)
{
GstPad *sinkpad;
GstElement *conv = (GstElement *) data;
sinkpad = gst_element_get_static_pad(conv, "sink");
gst_pad_link(pad, sinkpad);
gst_object_unref(sinkpad);
}
static gboolean snd_bus_call(
UNUSED GstBus *bus,
GstMessage *msg,
gpointer data
)
{
struct snd_data_t *snd_data = data;
switch (GST_MESSAGE_TYPE(msg))
{
case GST_MESSAGE_EOS:
gst_element_set_state(snd_data->pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(snd_data->pipeline));
g_source_remove(snd_data->bus_watch_id);
void (*cb_end)(void *) = snd_data->cb_end;
void *cb_data = snd_data->data_end;
free(snd_data);
if (cb_end != NULL)
cb_end(cb_data);
return false;
break;
case GST_MESSAGE_ERROR:
{
gchar *debug;
GError *error;
gst_message_parse_error(msg, &error, &debug);
g_free(debug);
g_printerr("Error: %s\n", error->message);
g_error_free(error);
exit(EXIT_FAILURE);
} break;
case GST_MESSAGE_STREAM_START:
if (snd_data->cb_start != NULL)
snd_data->cb_start(snd_data->data_start);
break;
default:
//DPRINT_VERB("GST_MESSAGE_TYPE %d\n", GST_MESSAGE_TYPE(msg));
break;
}
return true;
}
I have created a simple test app to test my gstreamer code, to be able to debug it seperately. Also, I followed this presentation, to get a few tipps how to find leaks. So this post doesn't get too long, I uploaded my log files to github.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wcast-align"
#pragma GCC diagnostic ignored "-Wconversion"
#include <gst/gst.h>
#pragma GCC diagnostic pop
#include <glib.h>
#include "utils.h"
#include "sound.h"
#include "debug.h"
static char **argv_s;
void next_song(GMainLoop *loop)
{
snd_play_file(argv_s[2], (void (*)(void *)) &g_main_loop_quit, loop);
}
int main(int argc, char **argv)
{
argv_s = argv;
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
gst_init(&argc, &argv);
char *mime;
get_mime_type(&mime, argv[1]);
printf("Mime [%s]: %s\n", argv[1], mime);
free(mime);
const void *p = snd_play_file(argv[1], (void (*)(void *)) &next_song, loop);
DPRINT_VERB("sleeping 10!\n");
sleep(10);
snd_set_speed(p, 1.5);
g_main_loop_run(loop);
DPRINT_VERB("deinit!\n");
gst_deinit();
g_main_loop_unref(loop);
return 0;
}
What am I missing here? Why is my (in my opinion) rather small and simple app using so much memory and how can I reduce it?

Related

GTK 3 GUI updated from separate thread text countdown in text box

First post, so will try and be brief until need to add more. Ported an app from macOS to NI LabWindows/CVI, in "C", then to port to GTK3 and trying to grasp the updating GUI from external thread concept. I've read the gnome documentation and searched on here, and everywhere, but not finding similar usage, or not grasping the updating the GUI from separate thread. I have experimented with g_idle_add() as follows.
int main(int argc, char* argv[])
{
gtk_init(&argc, &argv); // init Gtk
gtk_start_button = GTK_WIDGET(gtk_builder_get_object(builder,"start_button"));
gtk_main();
return EXIT_SUCCESS;
}
void start_button_clicked_cb(GtkWidget *widget, gpointer data)
{
printf("\nStart Button Pressed\n");
run_tests();
}
void run_tests( void )
{
GThread *start_testing_thread;
start_testing_thread = g_thread_new("", &start_testing, NULL);
}
void *start_testing (void *data)
{
pause(5);
}
void pause( double pause_time)
{
char string[33];
while( (double)pause_time > (double)0 )
{
sprintf( string, "Pausing %02.1f", pause_time );
printf(string);
//test_name( string );
g_idle_add(test_name_gui, string);
g_usleep(100000); // uSecs for 100 mSecs
pause_time -= 0.1;
}
}
void test_name_gui(gpointer user_data)
{
GtkTextBuffer* buffer = gtk_text_buffer_new(NULL);
char temp[99];
int error = -1;
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW (gtktextview_test_name));
sprintf(temp,"\n%s gui\n",(char*)user_data);
printf(temp);
if(!g_utf8_validate(user_data,-1,NULL))
{
error = 3;
}
gtk_text_buffer_set_text (buffer, (char*)user_data, -1);
printf("\nTest Name gui\n");
g_object_unref(buffer);
return G_SOURCE_REMOVE;
}

Bluetooth Low Energy on Linux: Implement a GATT-Client

I´m new in Bluetooth Low Energy and I´m trying to build my own C-Program on Linux which connects and interacts with an Bluetooth Low Energy Device. For that I'm using the Linux Bluetooth Protocol Stack Bluez.
After reading this blog (https://www.jaredwolff.com/blog/get-started-with-bluetooth-low-energy/) it´s possible for me to connect and communicate with my Device over Command-Line. But now I stuck in doing this with my Code. I looked at the Gatttool Sourcefiles but it´s hard for me to understand..
I tried to connect to the device with gatt_conncect(const char *src, const char *dst,
const char *dst_type, const char *sec_level,
int psm, int mtu, BtIOConnect connect_cb,
GError **gerr), but I think something is wrong with the Arguments which I pass to the function.
char dst[] = "XX:XX:XX:XX:XX:XX"; //device address
char dst_type = BDADDR_LE_RANDOM;
char sec_level = BT_IO_SEC_LOW;
GError *gerr;
BtIOConnect connect_cb; //Compiler says it´s uninitialized, but I don´t in what way I have how to initialize it
GIOhannel *chan;
chan = gatt_connect(NULL, dst, &dst_type, &sec_level, 0, 0, connect_cb, &gerr);
The function crashes. I think something is wrong with connect_cb functionpointer. But after looking in the Gattool Sourcecode I don´t how to fix it.
Definition for gatt_connect is :
GIOChannel *gatt_connect(const char *src, const char *dst,
const char *dst_type, const char *sec_level,
int psm, int mtu, BtIOConnect connect_cb,
GError **gerr);
connect_cb is callback function (typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);) which will be called when connected. since you have garbage callback probably that’s the reason its crashing!
following is my callback function which I am using:
static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
{
VVBOX_BLE_DEV_t *pdev = (VVBOX_BLE_DEV_t *)user_data;
uint16_t mtu;
uint16_t cid;
if (err) {
if (reply_to_listner) {
mq_send_probe_ble_rsp(listener_id, 0, 5, "");
reply_to_listner = 0;
}
printf("%s failed %s",__FUNCTION__, err->message);
return;
}
bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu,
BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
if (err) {
printf("%s Can't detect MTU, using default: %s", __FUNCTION__, err->message);
g_error_free(err);
mtu = ATT_DEFAULT_LE_MTU;
}
if (cid == ATT_CID)
mtu = ATT_DEFAULT_LE_MTU;
pdev->attrib = g_attrib_new(pdev->iochannel, mtu, false);
printf("Connection successful\n");
gatt_discover_primary(pdev->attrib, NULL, primary_all_cb, user_data);
return;
}

I would like some help getting criteria of gtk programming. What could I do about this?

Could anyone help me? I have programming skills in C and I'm trying to program gui's using gtk. I wish, from firing an action (a callback function) from a widget (eg a button), I can change any condition or characteristic belonging to any other widget. Ie something like allow full visibility from within the callback function as if all variables in all gui widgets (say a single main application window) were global. How can I address the closest way to accomplish this? So far, the steps I tried to discover underlying concepts in programming with gtk were frustrating me, when I try to make something more complex than simple Hello World tutorial.I also tried using:
/* out of main */
struct data_widgets
{
struct data_widgets * myp;
gchar **loc ; // LOCAL name
gchar **rem ; // REMOTE name
gchar **pmte ; // Plain message to encrypt filename
gchar **lem ; // Local encrypted message filename
gchar **emr ; // Encrypted message received filename
gchar **pmr ; // Plain message received filename
gchar **lopk ; // Local owner public key filename
gchar **crpk ; // Current remote public key filename
};
int main(int argc, char *argv[])
{
gchar loc_str[100] = "*"; /* LOCAL name for filechoose filter */
gchar rem_str[100] = "*"; /* REMOTE name idem */
gchar pmte_str[100]= "plainmsgtoencrypt"; /* Plain message to encrypt filename */
gchar lem_str[100] = "localencmsg"; /* Local encrypted message filename */
gchar emr_str[100] = "encmsgreceiv"; /* Encrypted message received filename */
gchar pmr_str[100] = "plainreceiv"; /* Plain message received filename */
gchar lopk_str[100]= "locownpubkey"; /* Local owner public key filename */
gchar crpk_str[100]= "remotpubkey"; /* Current remote public key filename */
struct data_widgets mydata;
mydata.loc = &loc_str;
mydata.rem = &rem_str;
mydata.pmte = &pmte_str;
mydata.lem = &lem_str;
mydata.emr = &emr_str;
mydata.pmr = &pmr_str;
mydata.lopk = &lopk_str;
mydata.crpk = &crpk_str;
mydata.myp = &mydata;
/* in main */
....
/* in my callback */
struct data_widgets *pp = (struct data_widgets *) data;
/*passing gpointer data as &mydata.myp, and doing (*pp)->(any pointer) this try fail*/
From what I understood you want to access widgets from within the callback without having to carry around a bunch of globals. You have some option.
You can pack data and widgets inside a single struct and pass it as user_data to the callback.
struct snapshot {
struct data_widgets data;
GtkWidget *entry;
GtkWidget *box;
GtkWidget *label;
GtkWidget *whatever;
};
/* Now fill and use the above struct as user_data */
The GtkWidget fields should be initialized in the code you use to create the UI stuff.
You can also set a name for each relevant widget with gtk_widget_set_name() (Glade automatically sets a name on every element). Once you need a widget, just get it with gtk_widget_get_name().
static void a_callback(GtkWidget *widget, gpointer user_data)
GtkWidget *an_entry = NULL;
GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
if (gtk_widget_is_toplevel(toplevel))
an_entry = gtk_widget_get_name(toplevel, "name of the entry");
g_return_if_fail(an_entry != NULL);
/* ... do something with an_entry ... */
}
You can also traverse the widget hierarchy as if it was a DOM or directly bind your data to the widgets with g_object_set_data() or any valid mixture of the above.
Addendum
With g_object_set_data() you can attach arbitrary data to any GObject instance, e.g.:
gchar *text;
/* Bind some data to widget */
g_object_set_data(G_OBJECT(widget), "pmte", "plainmsgtoencrypt");
g_object_set_data(G_OBJECT(widget), "lem", "localencmsg");
text = g_object_get_data(G_OBJECT(widget), "pmte");
g_print("%s\n", text); /* Print "plainmsgtoencrypt" */
g_free(text);
text = g_object_get_data(G_OBJECT(widget), "lme");
g_print("%s\n", text); /* Print "localencmsg" */
g_free(text);
Code cleanup
Your code is messy in many ways: I suggest you forget what you learnt for C and restart from zero. Your data has one level of indirection more than what it is needed, myp is bogus, you are specifying size of arrays and initializing it.
Answering your comment: the following code is self-contained and does not throw the error you see.
#include <gtk/gtk.h>
struct data_widgets {
gchar **crpk;
};
void callback(GtkWidget *widget, gpointer user_data) {
struct data_widgets *data = user_data;
g_print("%s\n", data->crpk); /* Prints remotpubkey */
}
int main(int argc, char *argv[]) {
gchar crpk_str[100] = "remotpubkey";
struct data_widgets mydata;
mydata.crpk = &crpk_str;
callback(NULL, &mydata);
return 0;
}
The same code, cleaned up in a sane way:
#include <gtk/gtk.h>
typedef struct {
gchar *crpk;
} data_t;
void callback(GtkWidget *widget, gpointer user_data) {
data_t *data = user_data;
g_print("%s\n", data->crpk); /* Prints remotpubkey */
}
int main() {
data_t mydata;
mydata.crpk = "remotpubkey";
callback(NULL, &mydata);
return 0;
}

Optimise queries file information and attributes inside of function called at regular intervals

I'm developing an application that records audio files with GStreamer.
One feature of this app is to display a GTK dialog with a label
that contains information about the file recording process (name, type and size).
code:
static int timeout_id = -1;
static GtkWidget *file_label, *type_label, *size_label;
static gboolean timeout_cb(gpointer data)
{
GFile *session_file;
GFileInfo *info;
session_file = g_file_new_for_path (path);
info = g_file_query_info (session_file,
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
G_FILE_ATTRIBUTE_STANDARD_SIZE,
G_FILE_QUERY_INFO_NONE,
NULL,
NULL);
if (info != NULL) {
/* name */
const gchar *display_name = g_file_info_get_attribute_string (info,
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
gtk_label_set_text (GTK_LABEL (file_label), display_name);
/* type */
const gchar *type = g_file_info_get_attribute_string (info,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
gtk_label_set_text (GTK_LABEL (type_label), type);
/* size */
guint64 size = g_file_info_get_attribute_uint64 (info,
G_FILE_ATTRIBUTE_STANDARD_SIZE);
gchar *tmp = g_format_size_full (size, G_FORMAT_SIZE_LONG_FORMAT);
gtk_label_set_text (GTK_LABEL (size_label), tmp);
g_free (tmp);
g_object_unref (info);
}
g_object_unref (file);
return TRUE;
}
void run_status_window(Record *record)
{
timeout_id = g_timeout_add(500, (GSourceFunc) timeout_cb, record);
}
I'm use queries for file information and attributes and calling this at 500 ms intervals.
My question is how can optimise this feature because the name and type aren't likely to change during the recording process.
Thanks

How to run a 2-to-1 element

I have created a 2-to-1 simple element called ntoone. My question now is how to run it (an example gst-launch using 2 videotestsrc and 1 autosink). It has two static "any" sink pads called video_sink and klv_sink and are added to a collection. Here is the code:
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gst/gst.h>
#include "gstntoone.h"
#include <opencv/cv.h>
#include <opencv/highgui.h>
GST_DEBUG_CATEGORY_STATIC (gst_ntoone_debug);
#define GST_CAT_DEFAULT gst_ntoone_debug
enum
{
PROP_0,
PROP_SILENT,
LINE_COLOR
};
/* the capabilities of the inputs and outputs.
*
* describe the real formats here.
*/
//Creates a template for the pads. In the _init() function, you can create
//as many pads you want from these templates.
static GstStaticPadTemplate video_sink_factory = GST_STATIC_PAD_TEMPLATE ("video_sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("ANY")
);
static GstStaticPadTemplate klv_sink_factory = GST_STATIC_PAD_TEMPLATE ("klv_sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("ANY")
);
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("ANY")
);
GST_BOILERPLATE (GstNtoone, gst_ntoone, GstElement,
GST_TYPE_ELEMENT);
//function prototypes
static void gst_ntoone_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_ntoone_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_ntoone_set_caps (GstPad * pad, GstCaps * caps);
static GstFlowReturn gst_ntoone_collected (GstCollectPads * pads, GstNtoone * filter);
/* GObject vmethod implementations */
static void
gst_ntoone_base_init (gpointer gclass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
//Describe the element's details
// Plugin name
// Plugin type
// A brief description
// Author and email (email is optional)
gst_element_class_set_details_simple(element_class,
"Plugin Template",
"Ntoone",
"Generic Chain Element",
"Jason Trinidad jtrinidad#eoir.com");
//Register the tamplates. They can be used
//in the init() function to create pads
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&video_sink_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&klv_sink_factory));
}
/* initialize the plugin's class */
static void
gst_ntoone_class_init (GstNtooneClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->set_property = gst_ntoone_set_property;
gobject_class->get_property = gst_ntoone_get_property;
g_object_class_install_property (gobject_class, PROP_SILENT,
g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",FALSE, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, LINE_COLOR,
g_param_spec_string ("line_color", "Line_color", "Chenge the color of the line", "red", G_PARAM_READWRITE));
}
/* initialize the new element
* instantiate pads and add them to element
*/
static void
gst_ntoone_init (GstNtoone * filter,
GstNtooneClass * gclass)
{
filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
gst_pad_set_getcaps_function (filter->srcpad,
GST_DEBUG_FUNCPTR(gst_pad_proxy_getcaps));
filter->sinkpad1 = gst_pad_new_from_static_template (&video_sink_factory, "video_sink");
gst_pad_set_setcaps_function (filter->sinkpad1,
GST_DEBUG_FUNCPTR(gst_ntoone_set_caps));
gst_pad_set_getcaps_function (filter->sinkpad1,
GST_DEBUG_FUNCPTR(gst_pad_proxy_getcaps));
filter->sinkpad2 = gst_pad_new_from_static_template (&klv_sink_factory, "klv_sink");
gst_pad_set_setcaps_function (filter->sinkpad2,
GST_DEBUG_FUNCPTR(gst_ntoone_set_caps));
gst_pad_set_getcaps_function (filter->sinkpad2,
GST_DEBUG_FUNCPTR(gst_pad_proxy_getcaps));
filter->collect = gst_collect_pads_new ();
gst_collect_pads_set_function (filter->collect,
(GstCollectPadsFunction) gst_ntoone_collected, filter);
gst_collect_pads_add_pad (filter->collect, filter->sinkpad1, sizeof (GstCollectData));
gst_collect_pads_add_pad (filter->collect, filter->sinkpad2, sizeof (GstCollectData));
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad1);
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad2);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
filter->silent = FALSE;
}
/*-------------------------------------------------------------------
* _set_property() is used to set arguments in the element.
* they can be used when running a pipelin by just typing the
* property name and the value right next to the plugin
* e.g. gst-launch -v -m videontoonesrc pattern=snow ! ntoone line_color=green ! autovideosink
* where pattern and line_color are properties
-------------------------------------------------------------------*/
static void
gst_ntoone_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstNtoone *filter = GST_NTOONE (object);
switch (prop_id) {
case PROP_SILENT:
filter->silent = g_value_get_boolean (value);
break;
case LINE_COLOR:
g_free (filter->line_color);
filter->line_color = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_ntoone_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstNtoone *filter = GST_NTOONE (object);
switch (prop_id) {
case PROP_SILENT:
g_value_set_boolean (value, filter->silent);
break;
case LINE_COLOR:
g_value_set_string (value, filter->line_color);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/* GstElement vmethod implementations */
/* this function handles the link with other elements */
static gboolean
gst_ntoone_set_caps (GstPad * pad, GstCaps * caps)
{
GstStructure *structure = gst_caps_get_structure(caps,0);
GstNtoone *filter;
GstPad *otherpad;
filter = GST_NTOONE (gst_pad_get_parent (pad));
gst_structure_get_int (structure, "rate", &filter->srcpad);
otherpad = (pad == filter->srcpad) ? filter->sinkpad1 : filter->srcpad;
gst_object_unref (filter);
return gst_pad_set_caps (otherpad, caps);
}
static GstFlowReturn
gst_ntoone_collected (GstCollectPads * pads, GstNtoone * filter)
{
guint size;
GstCollectData *cdata;
GstBuffer *outbuf, *sink1buf, *sink2buf;
GstFlowReturn ret = GST_FLOW_OK;
GSList *collected;
guint nsamples;
guint ncollected = 0;
gboolean empty = TRUE;
size = gst_collect_pads_available (pads); //Query how much bytes can be read from each queued buffer.
//This means that the result of this call is the maximum
//number of bytes that can be read from each of the pads.
GST_DEBUG_OBJECT (filter, "Starting to collect %u bytes", size);
collected = pads->data;
cdata = (GstCollectData *) collected->data;
sink1buf = gst_collect_pads_take_buffer (pads, cdata, size);
collected = collected->next;
cdata = (GstCollectData *) collected->data;
sink2buf = gst_collect_pads_take_buffer (pads, cdata, size);
gst_pad_push(filter->srcpad,sink1buf);
return ret;
goto eos;
eos:
{
GST_DEBUG_OBJECT (filter, "no data available, must be EOS");
gst_buffer_unref (outbuf);
gst_pad_push_event (filter->srcpad, gst_event_new_eos ());
return -3;
}
}
/* entry point to initialize the plug-in
* initialize the plug-in itself
* register the element factories and other features
*/
static gboolean
plugin_init (GstPlugin * plugin)
{
/* debug category for filtering log messages
*
* exchange the string 'Ntoone plugin' with your description
*/
GST_DEBUG_CATEGORY_INIT (gst_ntoone_debug, "ntoone",
0, "Template plugin");
return gst_element_register (plugin, "ntoone", GST_RANK_NONE,
GST_TYPE_NTOONE);
}
#ifndef PACKAGE
#define PACKAGE "pluginntoone"
#endif
GST_PLUGIN_DEFINE (
GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"ntoone",
"Template Example",
plugin_init,
"0.10.28",
"GPL",
"GStreamer",
"http://gstreamer.net/"
)
Thanks
First check that your element shows up in gst-inspect.
Then you can use it using gst-launch as below:
gst-launch videotestsrc ! ntoone name=mix ! autovideosink videotestsrc ! ntoone.
You can also specify the pads directly
gst-launch ntoone name=mix ! autovideosink videotestsrc ! ntoone.video_sink videotestsrc ! ntoone.klv_sink
When there is no '!' a new branch of the media graph is started.

Resources