在Gstreamer中,如果您要定时让程序去作某件事,则可以使用g_timeout_add()或g_timeout_add_full().
1 函数原型
g_timeout_add
设置要定期调用的函数,默认优先级为G_PRIORITY_DEFAULT。 重复调用该函数,直到返回FALSE,这时超时将自动销毁,并且不会再次调用该函数。 该函数的第一个调用将在第一个间隔的结尾。
请注意,由于其他事件源的处理,超时功能可能会延迟。 因此,不应依赖它们来获得精确的时间。 每次调用超时功能后,都会根据当前时间和给定的间隔重新计算下一次超时的时间(它不会尝试“追赶”因延迟而损失的时间)。
guint g_timeout_add(guint interval,
GSourceFunc function,
gpointer data);
参数 | 说明 |
---|---|
interval | the time between calls to the function, in milliseconds (1/1000ths of a second) |
function | function to call |
data | data to pass to function |
Returns | the ID (greater than 0) of the event source. |
第一个参数是间隔的毫秒数,第二个参数是定时后的callback,第三个是传递给callback的数据。callback的形式如下:
gint timeout_callback(gpointer data);
g_timeout_add
的返回值可以用来结束这个timeout,如下(假如返回放到tag中)
void g_source_remove(gint tag);
也可以让callback返回0或FALSE来结束timeout。
更多的参考可见 glib tutorial 相关章节:
https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-timeout-add
2 示例
2.1 示例1
// test-gmainloop.c
#include <stdio.h>
#include <glib.h>
static gboolean test_timeout (gpointer arg)
{
printf("Hello World!\n");
return TRUE;
}
int main()
{
static GMainLoop *main_loop = NULL;
main_loop = g_main_loop_new (NULL, FALSE);
g_timeout_add (1000, test_timeout, NULL); // 1s
g_main_loop_run (main_loop);
return 0;
}
2.1.1 编译
gcc test-gmainloop.c -o test-gmainloop `pkg-config --cflags --libs glib-2.0`
2.1.2 结果
每隔1s中输出一次“Hello World!”
注意:
- g_timeout_add 必须在 g_main_loop_run 函数前使用,否则无效。
- 如果1s上一个回调未完成,
2.2 示例2
本示例改编自:Gstreamer Basic tutorial 3: Dynamic pipelines
// basic-tutorial-3.c
#include <gst/gst.h>
/* Structure to contain all our information, so we can pass it to callbacks */
typedef struct _CustomData {
GstElement *pipeline;
GstElement *source;
GstElement *convert;
GstElement *sink;
GMainLoop *main_loop; /* GLib's Main Loop */
} CustomData;
/* Handler for the pad-added signal */
static void pad_added_handler (GstElement *src, GstPad *pad, CustomData *data);
static gboolean gtimeout_callback (gpointer data);
static gboolean
bus_callback (GstBus * bus, GstMessage * message, gpointer data)
{
CustomData *appCtx = (CustomData *) data;
/* Parse message */
if (message != NULL) {
GError *err;
gchar *debug_info;
switch (GST_MESSAGE_TYPE (message))
{
case GST_MESSAGE_ERROR:
gst_message_parse_error (message, &err, &debug_info);
g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (message->src), err->message);
// g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
g_clear_error (&err);
g_free (debug_info);
break;
case GST_MESSAGE_EOS:
g_print ("End-Of-Stream reached.\n");
// GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(data.pipeline, GST_DEBUG_GRAPH_SHOW_ALL, "pipeline");
g_main_loop_quit(appCtx->main_loop);
break;
case GST_MESSAGE_STATE_CHANGED:
/* We are only interested in state-changed messages from the pipeline */
if (GST_MESSAGE_SRC (message) == GST_OBJECT (appCtx->pipeline)) {
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed (message, &old_state, &new_state, &pending_state);
g_print ("Pipeline state changed from %s to %s:\n",
gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));
}
break;
default:
/* We should not reach here */
// g_printerr ("Unexpected message received.\n");
break;
}
}
return TRUE;
}
int main(int argc, char *argv[]) {
CustomData data;
GstBus *bus;
GstStateChangeReturn ret;
//g_setenv("GST_DEBUG_DUMP_DOT_DIR", "/home/xxx", TRUE);
/* Initialize GStreamer */
gst_init (&argc, &argv);
/* Create the elements */
data.source = gst_element_factory_make ("uridecodebin", "source");
// data.convert = gst_element_factory_make ("audioconvert", "convert");
//data.sink = gst_element_factory_make ("autoaudiosink", "sink");
data.convert = gst_element_factory_make ("videoconvert", "convert");
data.sink = gst_element_factory_make ("autovideosink", "sink");
/* Create the empty pipeline */
data.pipeline = gst_pipeline_new ("test-pipeline");
if (!data.pipeline || !data.source || !data.convert || !data.sink) {
g_printerr ("Not all elements could be created.\n");
return -1;
}
/* Build the pipeline. Note that we are NOT linking the source at this
* point. We will do it later. */
gst_bin_add_many (GST_BIN (data.pipeline), data.source, data.convert , data.sink, NULL);
if (!gst_element_link (data.convert, data.sink)) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (data.pipeline);
return -1;
}
/* Set the URI to play */
g_object_set (data.source, "uri", "file:///home/xxx/sample.mp4", NULL);
/* Connect to the pad-added signal */
g_signal_connect (data.source, "pad-added", G_CALLBACK (pad_added_handler), &data);
/* Start playing */
ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
g_printerr ("Unable to set the pipeline to the playing state.\n");
gst_object_unref (data.pipeline);
return -1;
}
/* Create a GLib Main Loop and set it to run */
data.main_loop = g_main_loop_new (NULL, FALSE);
/* Listen to the bus */
bus = gst_element_get_bus (data.pipeline);
gst_bus_add_watch (bus, (GstBusFunc)bus_callback, &data);
g_timeout_add(2000, gtimeout_callback, NULL);
g_main_loop_run (data.main_loop);
/* Free resources */
g_main_loop_unref (data.main_loop);
gst_object_unref (bus);
gst_element_set_state (data.pipeline, GST_STATE_NULL);
gst_object_unref (data.pipeline);
return 0;
}
gboolean
gtimeout_callback (gpointer data)
{
g_print ("#### Reset source pipeline %s \n", __func__);
return FALSE;
// return TRUE;
}
/* This function will be called by the pad-added signal */
static void pad_added_handler (GstElement *src, GstPad *new_pad, CustomData *data)
{
GstPad *sink_pad = gst_element_get_static_pad (data->convert, "sink");
GstPadLinkReturn ret;
GstCaps *new_pad_caps = NULL;
GstStructure *new_pad_struct = NULL;
const gchar *new_pad_type = NULL;
g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (new_pad), GST_ELEMENT_NAME (src));
/* If our converter is already linked, we have nothing to do here */
if (gst_pad_is_linked (sink_pad))
{
g_print ("We are already linked. Ignoring.\n");
goto exit;
}
/* Check the new pad's type */
new_pad_caps = gst_pad_get_current_caps (new_pad);
new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
new_pad_type = gst_structure_get_name (new_pad_struct);
if (!g_str_has_prefix (new_pad_type, "video/x-raw"))
{
g_print ("It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
goto exit;
}
/* Attempt the link */
ret = gst_pad_link (new_pad, sink_pad);
if (GST_PAD_LINK_FAILED (ret)) {
g_print ("Type is '%s' but link failed.\n", new_pad_type);
} else {
g_print ("Link succeeded (type '%s').\n", new_pad_type);
}
exit:
/* Unreference the new pad's caps, if we got them */
if (new_pad_caps != NULL)
gst_caps_unref (new_pad_caps);
/* Unreference the sink pad */
gst_object_unref (sink_pad);
}
2.2.1 编译
gcc basic-tutorial-3.c -o basic-tutorial-3 `pkg-config --cflags --libs gstreamer-1.0`
2.2.2 结果
3 说明
g_timeout_add ()的回调函数必须有返回值为TURE。否则结果时钟只执行了一次就退出了。如上所示。