This very simple example demonstrates how to use the tee element in Gstreamer, by outputting a video stream from a v4l2src to two xvimagesinks.
This example requiers
Gstreamer-1.x.
To compile the code, try the following line:
$ g++ `pkg-config gstreamer-1.0 --cflags` tee.cpp -o tee_example `pkg-config gstreamer-1.0 --libs` -fPIC -I /usr/include -L /usr/lib
------
// (c) Tord Wessman 2013
// Feel free to do what you like with code.
//
// This simple example demonstrates how to use the tee elements to
// display two xvimagesink windows containing one web-cam input (v4l2src).
//
#include <cstdio>
#include <gst/gst.h>
static GMainLoop *loop;
static GstElement *bin, // the containing all the elements
*pipeline,
*src,
*csp,
*tee,
*q1,*q2,
*testsink,
*sink;
static GstBus *bus; //the bus element te transport messages from/to the pipeline
static gboolean bus_call(GstBus *bus, GstMessage *msg, void *user_data);
int init() {
gst_init (NULL, NULL);
GstCaps *caps;
/* create the main loop */
loop = g_main_loop_new(NULL, FALSE);
pipeline = gst_pipeline_new ("video_pipeline");
/* create the bus for the pipeline */
bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
/* add the bus handler method */
gst_bus_add_watch(bus, bus_call, NULL);
gst_object_unref(bus);
bin = gst_bin_new ("video_bin");
//initializing elements
src = gst_element_factory_make ("v4l2src", "src");
sink = gst_element_factory_make ("xvimagesink", "xvimagesinkONE");
testsink = gst_element_factory_make ("xvimagesink", "testsinkTWO");
csp = gst_element_factory_make("videoconvert", "csp");
tee = gst_element_factory_make ("tee", "videotee");
q1 = gst_element_factory_make ("queue", "qone");
q2 = gst_element_factory_make ("queue", "qtwo");
if (src == NULL || sink == NULL || testsink == NULL) {
g_critical ("Unable to create src/sink elements.");
return 0;
} else if (csp == NULL) {
g_critical ("Unable to create csp");
return 0;
} else if (!q1 && !q2 && !tee) {
g_critical ("Unable to create other elements");
return 0;
}
/* Add the elements to the pipeline prior to linking them */
gst_bin_add_many(GST_BIN(pipeline), src, csp, tee, q1, sink, q2, testsink, NULL);
/* Specify caps for the csp-filter (modify this if your hardware requires) */
caps = gst_caps_new_simple("video/x-raw",
"width", G_TYPE_INT, 640,
"height", G_TYPE_INT, 480,
NULL);
/* Link the camera source and csp filter using capabilities
* specified */
if(!gst_element_link_many(src, csp, NULL))
{
gst_object_unref (pipeline);
g_critical ("Unable to link src to csp ");
return 0;
}
/* link the tee element */
if(!gst_element_link_filtered(csp, tee, caps))
{
gst_object_unref (pipeline);
g_critical ("Unable to link csp to tee. check your caps.");
return 0;
}
/* Link the first sink */
if(!gst_element_link_many(q1, sink, NULL))
{
gst_object_unref (pipeline);
g_critical ("Unable to link csp->tee->queue->sink for the queue 1");
return 0;
}
/* Link the second sink */
if(!gst_element_link_many(q2, testsink, NULL))
{
gst_object_unref (pipeline);
g_critical ("Unable to link csp->tee->queue->sink for the queue 2.");
return 0;
}
GstPadTemplate *tee_src_pad_template;
GstPad *tee_q1_pad, *tee_q2_pad;
GstPad *q1_pad, *q2_pad;
/* Manually link the Tee, which has "Request" pads */
if ( !(tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src_%u"))) {
gst_object_unref (pipeline);
g_critical ("Unable to get pad template");
return 0;
}
/* Obtaining request pads for the tee elements*/
tee_q1_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
g_print ("Obtained request pad %s for q1 branch.\n", gst_pad_get_name (tee_q1_pad));
q1_pad = gst_element_get_static_pad (q1, "sink");
tee_q2_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
g_print ("Obtained request pad %s for q2 branch.\n", gst_pad_get_name (tee_q2_pad));
q2_pad = gst_element_get_static_pad (q2, "sink");
/* Link the tee to the queue 1 */
if (gst_pad_link (tee_q1_pad, q1_pad) != GST_PAD_LINK_OK ){
g_critical ("Tee for q1 could not be linked.\n");
gst_object_unref (pipeline);
return 0;
}
/* Link the tee to the queue 2 */
if (gst_pad_link (tee_q2_pad, q2_pad) != GST_PAD_LINK_OK) {
g_critical ("Tee for q2 could not be linked.\n");
gst_object_unref (pipeline);
return 0;
}
gst_object_unref (q1_pad);
gst_object_unref (q2_pad);
return 1;
}
void start() {
gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
g_main_loop_run(loop);
gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);
}
void stop() {
g_main_loop_quit(loop);
gst_object_unref(GST_OBJECT(pipeline));
g_main_loop_unref (loop);
}
static gboolean bus_call(GstBus *bus, GstMessage *msg, void *user_data)
{
switch (GST_MESSAGE_TYPE(msg)) {
case GST_MESSAGE_EOS: {
g_main_loop_quit(loop);
break;
}
case GST_MESSAGE_ERROR: {
GError *err;
gst_message_parse_error(msg, &err, NULL);
//report error
printf ("ERROR: %s", err->message);
g_error_free(err);
g_main_loop_quit(loop);
break;
}
case GST_MESSAGE_APPLICATION: {
const GstStructure *str;
str = gst_message_get_structure (msg);
if (gst_structure_has_name(str,"turn_off"))
{
g_main_loop_quit(loop);
}
break;
}
default:
break;
}
if (msg->type == GST_MESSAGE_STATE_CHANGED ) {
GstState old, news, pending;
gst_message_parse_state_changed (msg, &old, &news, &pending);
printf ("State changed. Old: %i New: %i Pending: %i.\n", old, news, pending);
} else {
printf("info: %i %s type: %i\n", (int)(msg->timestamp), GST_MESSAGE_TYPE_NAME (msg), msg->type);
}
return true;
}
int main (int argc, char** argv) {
if (init()) {
start ();
stop();
}
else {
printf ("unable to initialize");
return -1;
}
return 0;
}