How to run a 2-to-1 element - c

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.

Related

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

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?

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;
}

GLib-GObject-Warning when creating gobject

I am trying to create a flume-thrift client in c (c_glib) but have trouble creating the gobject that shall be sent to the server. I get the following error at the line in main.c:
`GLib-GObject-WARNING **: IA__g_object_new_valist: object class `ThriftFlumeEventType' has no property named `timestamp'`
The code in flume_types.h and flume_types.c is autogenerated from thrift. Tell me if you need to see more code. I am thankful for all the help I can get!
Parts of the code in flume_types.h:
struct _ThriftFlumeEvent
{
ThriftStruct parent;
/* public */
gint64 timestamp;
gboolean __isset_timestamp;
Priority priority;
gboolean __isset_priority;
GByteArray * body;
gboolean __isset_body;
gint64 nanos;
gboolean __isset_nanos;
gchar * host;
gboolean __isset_host;
GHashTable * fields;
gboolean __isset_fields;
};
typedef struct _ThriftFlumeEvent ThriftFlumeEvent;
GType thrift_flume_event_get_type (void);
#define TYPE_THRIFT_FLUME_EVENT (thrift_flume_event_get_type())
Parts of the code in flume_types.c:
void
thrift_flume_event_instance_init (ThriftFlumeEvent * object)
{
printf("thrift_flume_event_instance_init");
/* satisfy -Wall */
THRIFT_UNUSED_VAR (object);
object->timestamp = 0;
object->__isset_timestamp = FALSE;
object->__isset_priority = FALSE;
object->body = NULL;
object->__isset_body = FALSE;
object->nanos = 0;
object->__isset_nanos = FALSE;
object->host = NULL;
object->__isset_host = FALSE;
object->fields = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
object->__isset_fields = FALSE;
}
GType
thrift_flume_event_get_type (void)
{
static GType type = 0;
if (type == 0)
{
static const GTypeInfo type_info =
{
sizeof (ThriftFlumeEventClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) thrift_flume_event_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (ThriftFlumeEvent),
0, /* n_preallocs */
(GInstanceInitFunc) thrift_flume_event_instance_init,
NULL, /* value_table */
};
type = g_type_register_static (THRIFT_TYPE_STRUCT,
"ThriftFlumeEventType",
&type_info, 0);
type.timestamp;
}
return type;
}
Parts of the code in main.c:
gpointer eventObj = g_object_new(TYPE_THRIFT_FLUME_EVENT,
"timestamp", 0,
"__isset_timestamp", 0,
"priority", 0,
"__isset_priority", 0,
"body", 0,
"__isset_body", 0,
"nanos", 0,
"__isset_nanos", 0,
"fields", 0,
"__isset_fields", 0,
0);
This is changing in Thrift 0.9.2: Objects generated by the C (GLib) compiler to represent Thrift structs will now expose their members as GObject properties, obviating the risky (and poor) practice of modifying an object's instance structure directly.
Starting with 0.9.2 the preferred way to initialize a struct object will be essentially what the poster originally expected:
ThriftFlumeEvent *event =
g_object_new (TYPE_THRIFT_FLUME_EVENT,
"timestamp", (gint64)t_stamp.tv_sec * 1000,
"priority", priority,
...
NULL);
Note the __isset_ fields are managed by the object itself and should not be set directly.
This was the solution to the problem:
ThriftFlumeEvent *event = g_object_new(TYPE_THRIFT_FLUME_EVENT, 0);
event->timestamp = (gint64)t_stamp.tv_sec * 1000;
event->__isset_timestamp = TRUE;
event->priority = priority;
event->__isset_priority = TRUE;
...
GObject properties are NOT C struct members. You need to install them via g_object_install_property in your class init function https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html . You also need to derive object from GObject (or from any struct that is derived from a GObject).
Note: derived in C means, that it has to have the parent as its first member so it can be cast seamlessly (NOT a pointer to a GObject!)
You should read a book on the topic, it can get quite complex.

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

Segmentation fault on resume from suspend

I'm writing a very simple battery status icon using GTK.
It's a GtkStatusIcon that shows the current battery status in a tooltip.
In order to obtain battery info I parse the output of the commandacpithat is usually similar to:
Battery 0: Discharging, 70%, 01:00:00 remaining
My battery status icon runs just fine but when I suspend my computer and then I resume it, my program crashes with segmentation fault.
The whole code is this (I've commented it):
#include <gtk/gtk.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define DEFAULT_ARRAY_SIZE 3
#define DEFAULT_TIME_UPDATE 5
const gchar * acpi_command = "acpi";
typedef enum batteryState{
CHARGING,
DISCHARGING,
} BatteryState;
typedef struct batteryTray {
GtkStatusIcon * tray_icon;
gchar * tooltip;
gchar * icon;
} BatteryTray;
typedef struct battery {
gchar * status;
gint percentage;
gchar * extra;
BatteryTray batteryTray;
BatteryState batteryState;
} Battery;
static void update_status_battery(Battery * battery);
static gboolean update_status_tray(Battery * battery);
static gchar * get_status_icon_name(Battery * battery);
static void create_tray_icon(Battery * battery);
static void parse_acpi_output(Battery * battery, gchar * acpi_output);
static char * get_acpi_output(const gchar * acpi_command);
static void update_status_battery(Battery * battery)
{
if(strcmp(battery->status, "Charging") == 0)
battery->batteryState = CHARGING;
else if(strcmp(battery->status, "Discharging") == 0)
battery->batteryState = DISCHARGING;
}
static gboolean update_status_tray(Battery * battery)
{
gchar * icon_name = get_status_icon_name(battery);
gchar * acpi_out = get_acpi_output(acpi_command);
parse_acpi_output(battery, acpi_out);
update_status_battery(battery);
battery->batteryTray.tooltip = g_strdup_printf("%s (%d%%) %s",
battery->status,
battery->percentage,
battery->extra);
gtk_status_icon_set_tooltip_text(battery->batteryTray.tray_icon,
battery->batteryTray.tooltip);
gtk_status_icon_set_from_icon_name(battery->batteryTray.tray_icon,
icon_name);
return TRUE;
}
static gchar * get_status_icon_name(Battery * battery)
{
GString * icon_name = g_string_new("notification-battery");
if (battery->percentage < 20)
g_string_append(icon_name, "-low");
else if (battery->percentage < 40)
g_string_append(icon_name, "-020");
else if (battery->percentage < 80)
g_string_append(icon_name, "-060");
else
g_string_append(icon_name, "-100");
if(battery->batteryState == CHARGING) {
g_string_append(icon_name, "-plugged");
}
return icon_name->str;
}
static void create_tray_icon(Battery * battery)
{
/* create the gtkstatusicon and call the function
`update_status_tray` every 5 seconds */
battery->batteryTray.tray_icon = gtk_status_icon_new();
battery->batteryTray.tooltip = "battery";
gtk_status_icon_set_tooltip(battery->batteryTray.tray_icon,
battery->batteryTray.tooltip);
gtk_status_icon_set_visible(battery->batteryTray.tray_icon,
TRUE);
update_status_tray(battery);
g_timeout_add_seconds(DEFAULT_TIME_UPDATE, (GSourceFunc) update_status_tray, battery);
}
static void parse_acpi_output(Battery * battery, gchar * acpi_output)
{
/* acpi output is like:
Battery 0: Discharging, 70%, 01:00:00 remaining
In this function I assign "Discharging" to battery->status
70 to battery->percentage
and "01:00:00 remaining" to battery->extra
I use strtok to split the acpi output into tokens delimited by ',' and
then, if there's a blank character ' ' in front of a token, i 'remove' it.
*/
gint i = 0;
gchar * t;
gchar ** values_array;
/* find the position of ':' in the string */
int pos = strchr(acpi_output, ':') - acpi_output;
t = strtok(acpi_output + pos + 1, ",");
values_array = malloc(DEFAULT_ARRAY_SIZE * sizeof(gchar));
while(t != NULL) {
/* 'remove' the blank character */
values_array[i++] = t[0] == ' ' ? t + 1 : t;
t = strtok(NULL, ",");
}
/* remove newline */
if(values_array[2][strlen(values_array[2]) - 1] == '\n') {
values_array[2][strlen(values_array[2]) - 1] = '\0';
}
battery->status = values_array[0];
battery->percentage = atoi(values_array[1]);
battery->extra = values_array[2];
free(values_array);
}
static gchar * get_acpi_output(const gchar * acpi_command)
{
gchar * output;
GError * error = NULL;
/* assign the output of the acpi command to 'output' */
g_spawn_command_line_sync(acpi_command, &output, NULL, NULL, &error);
return output;
}
int main(int argc, char ** argv)
{
Battery battery;
gtk_init(&argc, &argv);
create_tray_icon(&battery);
gtk_main();
return 0;
}
I hope that someone can help me because I really can't understand.
There are a number of places where you could do more error checking; perhaps something outside of your code fails during wakeup. For example, you don't check the results of g_spawn_command_line_sync -- you assume that output is pointing to a valid string on return, but perhaps it's not always. And you assume, but don't actually confirm, that the output of the tool you run has a certain number of tokens in it, but perhaps it's not always.

Resources