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.
Related
I am currently working on a project where we have a C thread implementation for UNIX systems using pthreads. Now we want to be able to run this entire project on Windows as well, and I am translating all the threading for WIN32. Now I encountered a problem for which I could not come up with a decent solution.
I have the thrd_create() function:
static inline int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
Args* args = malloc(sizeof(Args));
args->arg = arg;
args->function = func;
*thr = CreateThread(NULL, 0, wrapper_function, (LPVOID) args, 0, NULL);
if (!*thr) {
free (args);
return thrd_error;
}
return thrd_success;
}
This function is supposed to create a new thread, and the user provides a start function. For convenience, I would like to leave the implementation that calls thrd_create() untouched if possible. For this reason, I created a wrapper_function:
static inline DWORD wrapper_function(LPVOID arg) {
Args * args;
args = (Args*) arg;
DWORD res = args->function(args->arg); //This does obviously not work
return res;
}
My question is: What DWORD should my wrapper function return? The function provided by the user for the pthread implementation has void return type, so I won't get any result from that. Any suggestions?
EDIT
Args looks like this:
struct Args {
void (*function)(void * aArg);
void* arg;
};
typedef struct Args Args;
According to manuals it is better to stick to a correct signature and use return value:
Windows
Pthreads
The other matter of concern would be the lifetime of args, I'd say the best way is for a caller to clean up, so they need to be tracked with your thread until it terminates.
An approximate API could be something along the lines of the following:
/* Your general error codes enumeration
* which should probably reside in a general
* header
*/
typedef enum {
OK = 0,
// Your application specific error codes
} error_t;
#ifdef _WIN32
#include <Windows.h>
typedef HANDLE thread_handle_t;
#else // assume pthreads
#include <pthread.h>
typedef pthread_t thread_handle_t;
#endif
typedef error_t(*entry_func_t)(void*);
typedef struct {
entry_func_t func;
void *args;
error_t _result;
thread_handle_t _handle;
} thread_t;
// returns OK(0) on success
// returns error code indicating a problem
error_t thread_create(thread_t *t);
An aproximate implementation would be:
#ifdef _WIN32
DWORD _win_entry_f(void *args) {
thread_t *t = args;
t->_result = t->func(t->args);
return 0; // Or some other Windows-specific value
}
error_t thread_create(thread_t *t) {
error_t err = OK;
if(!(t->_handle = ThreadCreate(NULL, 0, _win_entry_f, t, 0, NULL))) {
switch (GetLastError()) {
// Populate error with code
}
}
return err;
}
#else
void * _pthread_entry_f(void *args) {
thread_t *t = args;
t->_result = t->func(t->args);
return NULL; // Or some other pthreads specific value
}
error_t thread_create(thread_t *t, entry_func_t func, void *args) {
error_t err = OK;
switch(pthread_create(&t->_handle, NULL, _pthread_entry_f, t)) {
case 0: break;
// other cases populate err
}
return err;
}
#endif
Invokation would look somewhat like this.
error_t func(void* args) {
return OK;
}
.....................
thread_t t = { .func = func, .args = NULL };
thread_create(&t);
Obviously you'll need to implement your own cancelation, result collection, join, ...
Sorry for my english. I am writing in C language simple editor.
I can not understand how to implement autocompletion words using GtkSourceCompletion.
static void set_completion(Page *page)
{
GtkSourceCompletionWords *words = gtk_source_completion_words_new("words_current_page", NULL);
gtk_source_completion_words_register(words, GTK_TEXT_BUFFER(page->buffer));
GtkSourceCompletion *comp = gtk_source_view_get_completion(GTK_SOURCE_VIEW(page->text_edit));
GtkSourceCompletionContext *context = gtk_source_completion_create_context(comp, NULL);
GtkSourceCompletionProvider *provider;
gtk_source_completion_add_provider(comp, provider, NULL);
}
I try, but all very confusing.
Please tell me how to do it.
Some explainations:
You need to consider the GtkSourceCompletionWords as a GtkSourceCompletionProvider, with the macro GTK_SOURCE_COMPLETION_PROVIDER(words) and add it as a provider for the completion:
GtkSourceCompletion *comp = gtk_source_view_get_completion(GTK_SOURCE_VIEW(page->text_edit));
gtk_source_completion_add_provider(comp,GTK_SOURCE_COMPLETION_PROVIDER(words),NULL);
Before to add it as provider, the GtkSourceCompletionWords needs to be registered with the buffer of the GtkSourceView:
GtkSourceCompletionWords *words = gtk_source_completion_words_new("wds_current_page", NULL);
gtk_source_completion_words_register(words, GTK_TEXT_BUFFER(page->buffer));
You can get the buffer with:
GtkTextBuffer * buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text_edit));
You also can create the buffer independently and pass it as argument for the creation of the GtkSourceView:
GtkSourceBuffer * sBuf = gtk_source_buffer_new(NULL);
GtkWidget * sview = gtk_source_view_new_with_buffer(sBuf);
Try this code:
static void set_completion(Page *page)
{
GtkSourceCompletionWords *words = gtk_source_completion_words_new("words_current_page", NULL);
gtk_source_completion_words_register(words, GTK_TEXT_BUFFER(page->buffer));
GtkSourceCompletion *comp = gtk_source_view_get_completion(GTK_SOURCE_VIEW(page->text_edit));
gtk_source_completion_add_provider(comp, GTK_SOURCE_COMPLETION_PROVIDER(words), NULL);
}
I am reasonably new to sysfs with respect to driver development and I seem to be observing some rather odd behavior. To make a long story short, it seems that the kernel is not passing back complete structs to my callback. This driver is a rather simple SPI-ADC driver that is being used to read analog thermal / voltage data.
Now, I have a hard time believing that I just found a bug in the Linux kernel in such a widely-used subsystem. I have scoured the internet for anything that might help, but all signs indicate that this should work as is. Other passed structures seem to be populated correctly, it is only the attr->attr member that appears to be NULL.
I should also mention that this is against the 3.2 kernel.
So, in short, what would causes sysfs callbacks to not receive a fully populated kobj_attribute struct?
Sample Code:
static ssize_t ads7960_sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
int var, rc, i = 0, j = 0;
long channel = 0;
//DEBUG
printk("%s: kobj->name = %s\n", __func__, kobj->name);
printk("%s: attr = %p\n", __func__, attr);
printk("%s: attr->attr = %p\n", __func__, attr->attr);
printk("%s: attr->attr.name = %s\n", __func__, attr->attr.name);
printk("%s: attr->attr.mode = %o\n", __func__, attr->attr.mode);
/* check for silly things */
if ((attr->attr.name == NULL) || (strlen(attr->attr.name) < 1)) {
printk("%s: invalid channel number. = %s\n", __func__, attr->attr.name);
return -EINVAL;
}
... snip (We never get past here) ...
static struct kobj_attribute ads7960_ch0 = __ATTR(0, 0444, ads7960_sysfs_show, NULL);
static struct kobj_attribute ads7960_ch1 = __ATTR(1, 0444, ads7960_sysfs_show, NULL);
static struct kobj_attribute ads7960_ch2 = __ATTR(2, 0444, ads7960_sysfs_show, NULL);
... snip (there are 12 total ADC channels in the same format) ...
static struct attribute *ch_attrs[] = {
&ads7960_ch0.attr,
&ads7960_ch1.attr,
&ads7960_ch2.attr,
... snip (same 12 channels as above)...
NULL,
};
static struct attribute_group attr_group = {
.attrs = ch_attrs,
};
static struct attribute_group *attr_group_ptr = &attr_group;
... snip ...
static struct spi_driver ads7960_driver = {
.driver = {
.name = "ads7960",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.groups = &attr_group_ptr,
},
.probe = ads7960_probe,
.remove = __devexit_p(ads7960_remove),
.id_table = ads7960_id,
};
... snip ...
Output Produced:
[root#172.17.152.42: ]# cat /sys/bus/spi/drivers/ads7960/4
[ 65.344789] ads7960_sysfs_show: kobj->name = ads7960
[ 65.350026] ads7960_sysfs_show: attr = dc934000
[ 65.354859] ads7960_sysfs_show: attr->attr = (null)
[ 65.360155] ads7960_sysfs_show: attr->attr.name = (null)
[ 65.365746] ads7960_sysfs_show: attr->attr.mode = 0
[ 65.370861] ads7960_sysfs_show: invalid channel number. = (null)
cat: read error: Invalid argument
References
http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/samples/kobject/kobject-example.c
http://kroah.com/log/blog/2013/06/26/how-to-create-a-sysfs-file-correctly/
Edit 1:
To summarize the comments below, I manually called sysfs_create_group from my _init and the kobj_attribute struct passed to the callback appeared to be correctly populated. The callback now works without issue (or modification, for that matter). As was stated below, spi_register_driver just calls sysfs_create_group. So, why would one invoke the callback properly while the other does not?
Per comments below, below is the complete _init function and spi_driver structure. The test that I did to manually create the paths myself was based off the _init code in the first reference with almost no modification.
static struct spi_driver ads7960_driver = {
.driver = {
.name = "ads7960",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.groups = attr_groups,
},
.probe = ads7960_probe,
.remove = __devexit_p(ads7960_remove),
.id_table = ads7960_id,
};
static int __init ads7960_init(void)
{
return spi_register_driver(&ads7960_driver);
}
module_init(ads7960_init);
The .groups member of struct device_driver must point to a NULL-terminated list of pointers to attribute groups, not to a single pointer to an attribute group. So, instead of attr_group_ptr, you need:
static struct attribute_group *attr_groups[] = { &attr_group, NULL };
...and then
static struct spi_driver ads7960_driver = {
.driver = {
.name = "ads7960",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.groups = attr_groups,
},
However, there's a helper macro to declare both the attribute group and list of attribute groups that you can use instead:
static struct attribute *ch_attrs[] = {
&ads7960_ch0.attr,
&ads7960_ch1.attr,
&ads7960_ch2.attr,
/* ... */
NULL,
};
ATTRIBUTE_GROUPS(ch); /* declares ch_group and ch_groups */
static struct spi_driver ads7960_driver = {
.driver = {
.name = "ads7960",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.groups = ch_groups,
},
I put here a brief source code because the source is too long. you can find the full source code is at this git repository: https://github.com/strophe/libstrophe
In fact I use strophe as a library in my main C program which is an openWrt package.
source code of common.h (libstrophe)
/** run-time context **/
typedef enum {
XMPP_LOOP_NOTSTARTED,
XMPP_LOOP_RUNNING,
XMPP_LOOP_QUIT
} xmpp_loop_status_t;
typedef struct _xmpp_connlist_t {
xmpp_conn_t *conn;
struct _xmpp_connlist_t *next;
} xmpp_connlist_t;
struct _xmpp_ctx_t {
const xmpp_mem_t *mem;
const xmpp_log_t *log;
xmpp_loop_status_t loop_status;
xmpp_connlist_t *connlist;
};
this is the header file strophe.h source code (libstrophe) :
/
* user-replaceable memory allocator */
typedef struct _xmpp_mem_t xmpp_mem_t;
/* user-replaceable log object */
typedef struct _xmpp_log_t xmpp_log_t;
/* opaque run time context containing the above hooks */
typedef struct _xmpp_ctx_t xmpp_ctx_t;
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
const xmpp_log_t * const log);
void xmpp_ctx_free(xmpp_ctx_t * const ctx);
struct _xmpp_log_t {
xmpp_log_handler handler;
void *userdata;
/* mutex_t lock; */
};
ctx.c brief source code (libstrophe):
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
const xmpp_log_t * const log)
{
xmpp_ctx_t *ctx = NULL;
if (mem == NULL)
ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t), NULL);
else
ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
if (ctx != NULL) {
if (mem != NULL)
ctx->mem = mem;
else
ctx->mem = &xmpp_default_mem;
if (log == NULL)
ctx->log = &xmpp_default_log;
else
ctx->log = log;
ctx->connlist = NULL;
ctx->loop_status = 0;//XMPP_LOOP_NOTSTARTED;
}
return ctx;
}
the main C program
#include <strophe.h>
void main()
{
xmpp_ctx_t *ctx;
xmpp_conn_t *conn;
xmpp_log_t *log;
char *jid, *pass;
create a context */
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */
ctx = xmpp_ctx_new(NULL, log);
/* create a connection */
conn = xmpp_conn_new(ctx);
/* setup authentication information */
xmpp_conn_set_jid(conn, "jid");
xmpp_conn_set_pass(conn, "pass");
/* initiate connection */
xmpp_connect_client(conn, "alt3.xmpp.l.google.com", 5222, conn_handler, ctx);
ctx->loop_status = 1; // error error: dereferencing pointer to incomplete type
/* enter the event loop -
our connect handler will trigger an exit */
xmpp_run(ctx);
/* release our connection and context */
xmpp_conn_release(conn);
xmpp_ctx_free(ctx);
/* final shutdown of the library */
xmpp_shutdown();
}
when compiling using this source code I got this error :
error: dereferencing pointer to incomplete type
Is a circular dependency header problem.
file strophe.h has to be include before common.h
That because of types in strophe.h has to be known by common.h
#include "strophe.h"
#include "common.h"
void main()
{
xmpp_ctx_t *ctx;
xmpp_initialize();
ctx = xmpp_ctx_new(NULL, log);
/* create a connection */
conn = xmpp_conn_new(ctx);
xmpp_connect_client(conn, "alt3.xmpp.l.google.com", 5222, conn_handler,
ctx);
ctx->loop_status = 1; // error error: dereferencing pointer to incomplete type
xmpp_run(ctx);
}
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.