2013-06-05

Gstreamer tee code example


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