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.
Related
I'm dynamically repeatedly creating a char[] of fixed length 32 and pass it to function that stores the pointer in a struct and passes it later on to a callback function.
Unforntunately I don't know the correct way to allocate the memory for this string data.
This is the function, where the nonceHex is created in a loop
static int polling_thread(struct pt *pt, uint16_t ticks)
{
static uint16_t timer;
timer += ticks;
PT_BEGIN(pt);
while (1)
{
uint8_t nonce[NONCE_LEN] = {};
char nonceHex[2 * NONCE_LEN] = "";
RNG(nonce, NONCE_LEN);
hex_encode(nonce, sizeof(nonce), nonceHex);
...
struct mg_connection *c = mg_connect_http(mgos_get_mgr(), http_cb, nonceHex, url, (const char *)sEtagHeader, NULL);
...
timer = 0;
PT_WAIT_UNTIL(pt, timer >= 500);
}
PT_END(pt);
}
And this is the callback function where the char[] is passed by the framework (mongoose os) as cb_arg
static void http_cb(struct mg_connection *nc, int ev, void *evd, void *cb_arg)
{
...
case MG_EV_HTTP_REPLY:
{
const char *nonceHex = (const char *)cb_arg;
LOG(LL_DEBUG, ("http_cb nonce: %s", nonceHex));
LOG(LL_DEBUG, ("http_cb nc->user_data: %s", (const char *)nc->user_data));
}
...
}
Unfortunately when I run this code I don't received the nonceHex but something that looks like unitialized char[]
http_cb nonce: Pa�?`B�?�a�?���?�c�?
http_cb nc->user_data: Pa�?`B�?�a�?���?�c�?
Do I need to dynamically malloc the char[]? If I use a static char[] it works, but it's not correct as nonceHex is overwritten by subsequent loop cycles and so cb_arg in the callback function contains invalid data.
Can anyone tell me how this would be programmed correctly?
#######################################################
Changed my function to
static int polling_thread(struct pt *pt, uint16_t ticks)
{
static uint16_t timer;
timer += ticks;
PT_BEGIN(pt);
while (1)
{
...
uint8_t nonce[NONCE_LEN] = {};
char *nonceHex = malloc(2 * NONCE_LEN + 1);
RNG(nonce, NONCE_LEN);
hex_encode(nonce, sizeof(nonce), nonceHex);
*(nonceHex+2 * NONCE_LEN) = '\0';
struct mg_connection *c = mg_connect_http(mgos_get_mgr(), http_cb, nonceHex, url, (const char *)sEtagHeader, NULL);
...
timer = 0;
PT_WAIT_UNTIL(pt, timer >= 500);
}
PT_END(pt);
}
Added call to free in callback function
static void http_cb(struct mg_connection *nc, int ev, void *evd, void *cb_arg)
{
...
case MG_EV_HTTP_REPLY:
{
char *nonceHex = (char *)cb_arg;
LOG(LL_DEBUG, ("http_cb nonce: %s", nonceHex));
LOG(LL_DEBUG, ("http_cb nc->user_data: %s", (const char *)nc->user_data));
free(nonceHex);
}
...
}
This results in a panic
Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.
Can you see my fault?
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?
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;
}
So I'm designing a virtual numpad with glade. It's working now but I wanted the buttons to print letters in a t9 fashion but I can't figure out how to time it... please help. my code so far is:
#include <gtk/gtk.h>
#include <string.h>
GtkWidget *g_entry1;
int main(int argc, char *argv[])
{
GtkBuilder *builder;
GtkWidget *window;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
gtk_builder_add_from_file (builder, "labtest.glade", NULL);
window = GTK_WIDGET(gtk_builder_get_object(builder, "window1"));
gtk_builder_connect_signals(builder, NULL);
g_entry1 = GTK_WIDGET(gtk_builder_get_object(builder,"entry1"));
g_object_unref(builder);
gtk_widget_show(window);
gtk_main();
return 0;
}
void on_btn1_clicked()
{
gchar *num;
char num_arr[20];
num = gtk_entry_get_text(GTK_ENTRY(g_entry1));
strcpy(num_arr,num);
strcat(num_arr,"1");
gtk_entry_set_text(GTK_ENTRY(g_entry1), num_arr);
}
void on_btn2_clicked()
{
gchar *num;
char num_arr[20];
num = gtk_entry_get_text(GTK_ENTRY(g_entry1));
strcpy(num_arr,num);
strcat(num_arr,"2");
gtk_entry_set_text(GTK_ENTRY(g_entry1), num_arr);
}
void on_btn3_clicked()
{
gchar *num;
char num_arr[20];
num = gtk_entry_get_text(GTK_ENTRY(g_entry1));
strcpy(num_arr,num);
strcat(num_arr,"3");
gtk_entry_set_text(GTK_ENTRY(g_entry1), num_arr);
}
void on_btn4_clicked()
{
gchar *num;
char num_arr[20];
num = gtk_entry_get_text(GTK_ENTRY(g_entry1));
strcpy(num_arr,num);
strcat(num_arr,"4");
gtk_entry_set_text(GTK_ENTRY(g_entry1), num_arr);
}
and so on for the rest of the buttons
(I wanted to put this as comment, but sadly I don't have 50 reputation)
Please can you clarify your question.
I wanted the buttons to print letters in a t9 fashion but I can't figure out how to time it.
what does it really mean? also, maybe you can give a link to labtest.glade file
Edit:
Ok, so you can use struct timespec, you may have something like this for your button2:
const gchar btn2_input[4] = { '2', 'A', 'B', 'C' };
struct timespec last_time_clicked;
#define MAX_DELAY 0.5f
void on_btn2_clicked() {
struct timespec actual_time;
float delay;
const gchar *o_text;
gchar *n_text;
GString *s_text;
static int i = 0;
gssize text_len;
clock_gettime(CLOCK_MONOTONIC, &actual_time);
timespec_subtract(&last_time_clicked, &actual_time, &last_time_clicked);
delay = timespec_to_float(&last_time_clicked);
last_time_clicked = actual_time;
o_text = gtk_entry_get_text(GTK_ENTRY(g_entry1));
s_text = g_string_new(o_text);
text_len = s_text->len;
if(delay > MAX_DELAY)
i = 0;
else {
text_len--;
i = (i + 1) % 4;
}
s_text = g_string_overwrite_len(s_text, text_len, btn2_input + i, 1);
n_text = g_string_free (s_text, FALSE);
gtk_entry_set_text(GTK_ENTRY(g_entry1), n_text);
g_free(n_text);
}
you may like to change MAX_DELAY, and there is the code for timespec_subtract and timespec_to_float:
void timespec_normalize(struct timespec *t) {
int n;
const int sn = 1000000000;
n = t->tv_nsec / sn;
t->tv_sec += n;
t->tv_nsec -= n * sn;
return;
}
int timespec_subtract(struct timespec *r, struct timespec *x,
struct timespec *y) {
timespec_normalize(x);
timespec_normalize(y);
if(x->tv_nsec < y->tv_nsec) {
x->tv_sec--;
x->tv_nsec += 1000000000;
}
r->tv_sec = x->tv_sec - y->tv_sec;
r->tv_nsec = x->tv_nsec - y->tv_nsec;
return 0;
}
float timespec_to_float(struct timespec *t) {
return (float)(t->tv_sec) + (float)(t->tv_nsec) / 1000000000.0f;
}
So all you need is to check if actual delay is greater than MAX_DELAY
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.