Search code examples
gstreamerframe-rate

Display FPS in Gstreamer's C source


When using gst-launch-1.0 to play a video, we can measure Framerate (FPS) by adding --padprobe v:sink --timer and name=v as below example:

gst-launch-1.0 -e --padprobe v:sink --timer filesrc location=video-h264-bl10-fhd-30p-5m-aac-lc-stereo-124k-48000x264.mp4 \
      ! qtdemux  ! queue ! omxh264dec ! \
      vspfilter ! video/x-raw,format=BGRA ! waylandsink position-x=0 position-y=0 \
      out-width=800 out-height=480 qos=false max-lateness=-1 name=v

And the output to console is

FPS:   9  TIME 11:57:47
FPS:   8  TIME 11:57:48
FPS:   8  TIME 11:57:49
FPS:   8  TIME 11:57:50
FPS:   9  TIME 11:57:51
FPS:   8  TIME 11:57:52
FPS:   8  TIME 11:57:53
FPS:   8  TIME 11:57:54
FPS:   9  TIME 11:57:55
FPS:   8  TIME 11:57:56
FPS:   8  TIME 11:57:57
FPS:   9  TIME 11:57:58
FPS:   8  TIME 11:57:59
Execution ended after 0:00:16.017383800
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Total time: 16.017389 seconds
Frames: 133 processed
Avg. FPS: 8.30
Freeing pipeline ...

How can I do the similar thing in source code of gstreamer written in C langugage ? (i.e in below example)

#include <gst/gst.h>

#define INPUT_FILE "/home/root/videos/vga1.h264"
#define POSITION_X 100
#define POSITION_Y 100

int
main (int argc, char *argv[])
{
  GstElement *pipeline, *source, *parser, *decoder, *sink;
  GstBus *bus;
  GstMessage *msg;

  const gchar *input_file = INPUT_FILE;

  /* Initialization */
  gst_init (&argc, &argv);

  gst_pad_add_probe (

  /* Create gstreamer elements */
  pipeline = gst_pipeline_new ("video-play");
  source = gst_element_factory_make ("filesrc", "file-source");
  parser = gst_element_factory_make ("h264parse", "h264-parser");
  decoder = gst_element_factory_make ("omxh264dec", "h264-decoder");
  sink = gst_element_factory_make ("waylandsink", "video-output");

  if (!pipeline || !source || !parser || !decoder || !sink) {
    g_printerr ("One element could not be created. Exiting.\n");
    return -1;
  }

  /* Set input video file for source element */
  g_object_set (G_OBJECT (source), "location", input_file, NULL);

  /* Set position for displaying (100, 100) */
  g_object_set (G_OBJECT (sink), "position-x", POSITION_X, "position-y", POSITION_Y, NULL);

  /* Add all elements into the pipeline */
  /* pipeline---[ file-source + h264-parser + h264-decoder + video-output ] */
  gst_bin_add_many (GST_BIN (pipeline), source, parser, decoder, sink, NULL);

  /* Link the elements together */
  /* file-source -> h264-parser -> h264-decoder -> video-output */
  if (gst_element_link_many (source, parser, decoder, sink, NULL) != TRUE) {
    g_printerr ("Elements could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }

  /* Set the pipeline to "playing" state */
  g_print ("Now playing: %s\n", input_file);
  if (gst_element_set_state (pipeline,
          GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
    g_printerr ("Unable to set the pipeline to the playing state.\n");
    gst_object_unref (pipeline);
    return -1;
  }

  g_print ("Running...\n");

  /* Wait until error or EOS */
  bus = gst_element_get_bus (pipeline);
  msg =
      gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
      GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

  /* Note that because input timeout is GST_CLOCK_TIME_NONE, 
     the gst_bus_timed_pop_filtered() function will block forever until a 
     matching message was posted on the bus (GST_MESSAGE_ERROR or 
     GST_MESSAGE_EOS). */

  if (msg != NULL) {
    GError *err;
    gchar *debug_info;

    switch (GST_MESSAGE_TYPE (msg)) {
      case GST_MESSAGE_ERROR:
        gst_message_parse_error (msg, &err, &debug_info);
        g_printerr ("Error received from element %s: %s.\n",
            GST_OBJECT_NAME (msg->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");
        break;
      default:
        /* We should not reach here because we only asked for ERRORs and EOS */
        g_printerr ("Unexpected message received.\n");
        break;
    }
    gst_message_unref (msg);
  }

  /* Free resources and change state to NULL */
  gst_object_unref (bus);
  g_print ("Returned, stopping playback...\n");
  gst_element_set_state (pipeline, GST_STATE_NULL);
  g_print ("Freeing pipeline...\n");
  gst_object_unref (GST_OBJECT (pipeline));
  g_print ("Completed. Goodbye!\n");
  return 0;
}

Could you explain HOW to implement padprobe v:sink --timer in this source. Thanks.


Solution

  • Yes, able to use fpsdisplaysink to solve this problem. Thank @Florian Zwoch for the hint

    #include <gst/gst.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define INPUT_FILE "/home/root/videos/vga1.h264"
    #define POSITION_X 100
    #define POSITION_Y 100
    #define SHOW_FPS_VALUE "1"
    #define DELAY_VALUE 1000000
    #define EXPORT_VAR "SHOW_FPS"
    
    int
    main (int argc, char *argv[])
    {
      GstElement *pipeline, *source, *parser, *decoder, *sink, *fpssink;
      GstBus *bus;
      GstMessage *msg;
      gchar *fps_msg;
      guint delay_show_FPS = 0;
      gchar *export_value;
      gboolean flag_fps = 0;
    
      /* Check function show FPS */
      export_value = getenv(EXPORT_VAR);
      if (export_value != NULL)
        if (strcmp(export_value, SHOW_FPS_VALUE) == 0)
          flag_fps = 1;
    
      const gchar *input_file = INPUT_FILE;
    
      /* Initialization */
      gst_init (&argc, &argv);
    
      /* Create gstreamer elements */
      pipeline = gst_pipeline_new ("video-play");
      source = gst_element_factory_make ("filesrc", "file-source");
      parser = gst_element_factory_make ("h264parse", "h264-parser");
      decoder = gst_element_factory_make ("omxh264dec", "h264-decoder");
      sink = gst_element_factory_make ("waylandsink", "video-output");
      fpssink = gst_element_factory_make ("fpsdisplaysink", NULL);
    
      if (!pipeline || !source || !parser || !decoder || !sink || !fpssink) {
        g_printerr ("One element could not be created. Exiting.\n");
        return -1;
      }
    
      /* Set input video file for source element */
      g_object_set (G_OBJECT (source), "location", input_file, NULL);
    
      /* Set position for displaying (100, 100) */
      g_object_set (G_OBJECT (sink), "position-x", POSITION_X, "position-y", POSITION_Y, NULL);
    
      /* Add feature FPS for video-sink */
      if (flag_fps)
        g_object_set (G_OBJECT (fpssink), "text-overlay", FALSE, "video-sink", sink, NULL);
    
      /* Add all elements into the pipeline */
      /* pipeline---[ file-source + h264-parser + h264-decoder + video-output ] */
      if (flag_fps)
        gst_bin_add_many (GST_BIN (pipeline), source, parser, decoder, fpssink, NULL);
      else
        gst_bin_add_many (GST_BIN (pipeline), source, parser, decoder, sink, NULL);
    
      /* Link the elements together */
      /* file-source -> h264-parser -> h264-decoder -> video-output */
      if (flag_fps) {
        if (gst_element_link_many (source, parser, decoder, fpssink, NULL) != TRUE) {
          g_printerr ("Elements could not be linked.\n");
          gst_object_unref (pipeline);
          return -1;
        }
      } else {
        if (gst_element_link_many (source, parser, decoder, sink, NULL) != TRUE) {
          g_printerr ("Elements could not be linked.\n");
          gst_object_unref (pipeline);
          return -1;
        }    
      }
    
      /* Set the pipeline to "playing" state */
      g_print ("Now playing: %s\n", input_file);
      if (gst_element_set_state (pipeline,
              GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
        g_printerr ("Unable to set the pipeline to the playing state.\n");
        gst_object_unref (pipeline);
        return -1;
      }
    
      g_print ("Running...\n");
    
      /* Wait until error or EOS */
      bus = gst_element_get_bus (pipeline);
      while(1) {
        msg = gst_bus_pop (bus);
    
        /* Note that because input timeout is GST_CLOCK_TIME_NONE, 
           the gst_bus_timed_pop_filtered() function will block forever until a 
           matching message was posted on the bus (GST_MESSAGE_ERROR or 
           GST_MESSAGE_EOS). */
        if (msg != NULL) {
          GError *err;
          gchar *debug_info;
          switch (GST_MESSAGE_TYPE (msg)) {
            case GST_MESSAGE_ERROR:
              gst_message_parse_error (msg, &err, &debug_info);
              g_printerr ("Error received from element %s: %s.\n",
                GST_OBJECT_NAME (msg->src), err->message);
              g_printerr ("Debugging information: %s.\n",
                debug_info ? debug_info : "none");
              g_clear_error (&err);
              g_free (debug_info);
              goto stop_pipeline;
            case GST_MESSAGE_EOS:
              g_print ("End-Of-Stream reached.\n");
              goto stop_pipeline;
          }
          gst_message_unref (msg);
        }
    
        /* Display information FPS to console */
        if (flag_fps) {
          g_object_get (G_OBJECT (fpssink), "last-message", &fps_msg, NULL);
          delay_show_FPS++;
          if (fps_msg != NULL) {
            if ((delay_show_FPS % DELAY_VALUE) == 0) {
              g_print ("Frame info: %s\n", fps_msg);
              delay_show_FPS = 0;
            }
          }
        }
      }
    
      /* Free resources and change state to NULL */
      stop_pipeline:
      gst_object_unref (bus);
      g_print ("Returned, stopping playback...\n");
      gst_element_set_state (pipeline, GST_STATE_NULL);
      g_print ("Freeing pipeline...\n");
      gst_object_unref (GST_OBJECT (pipeline));
      g_print ("Completed. Goodbye!\n");
      return 0;
    }
    

    $ export SHOW_FPS=1

    to show fps information to the terminal.