playback-tutorial-4.c 5.21 KB
Newer Older
Xavi Artigas's avatar
Xavi Artigas committed
1 2
#include <gst/gst.h>
#include <string.h>
3

4
#define GRAPH_LENGTH 78
5

6
/* playbin flags */
Xavi Artigas's avatar
Xavi Artigas committed
7
typedef enum {
Xavi Artigas's avatar
Xavi Artigas committed
8
  GST_PLAY_FLAG_DOWNLOAD      = (1 << 7) /* Enable progressive download (on selected formats) */
Xavi Artigas's avatar
Xavi Artigas committed
9
} GstPlayFlags;
10

Xavi Artigas's avatar
Xavi Artigas committed
11 12 13 14 15 16
typedef struct _CustomData {
  gboolean is_live;
  GstElement *pipeline;
  GMainLoop *loop;
  gint buffering_level;
} CustomData;
17

Xavi Artigas's avatar
Xavi Artigas committed
18 19 20 21
static void got_location (GstObject *gstobject, GstObject *prop_object, GParamSpec *prop, gpointer data) {
  gchar *location;
  g_object_get (G_OBJECT (prop_object), "temp-location", &location, NULL);
  g_print ("Temporary file: %s\n", location);
22
  g_free (location);
Xavi Artigas's avatar
Xavi Artigas committed
23 24 25
  /* Uncomment this line to keep the temporary file after the program exits */
  /* g_object_set (G_OBJECT (prop_object), "temp-remove", FALSE, NULL); */
}
26

Xavi Artigas's avatar
Xavi Artigas committed
27
static void cb_message (GstBus *bus, GstMessage *msg, CustomData *data) {
28

Xavi Artigas's avatar
Xavi Artigas committed
29 30 31 32
  switch (GST_MESSAGE_TYPE (msg)) {
    case GST_MESSAGE_ERROR: {
      GError *err;
      gchar *debug;
33

Xavi Artigas's avatar
Xavi Artigas committed
34 35 36 37
      gst_message_parse_error (msg, &err, &debug);
      g_print ("Error: %s\n", err->message);
      g_error_free (err);
      g_free (debug);
38

Xavi Artigas's avatar
Xavi Artigas committed
39 40 41 42 43 44 45 46 47 48 49 50
      gst_element_set_state (data->pipeline, GST_STATE_READY);
      g_main_loop_quit (data->loop);
      break;
    }
    case GST_MESSAGE_EOS:
      /* end-of-stream */
      gst_element_set_state (data->pipeline, GST_STATE_READY);
      g_main_loop_quit (data->loop);
      break;
    case GST_MESSAGE_BUFFERING:
      /* If the stream is live, we do not care about buffering. */
      if (data->is_live) break;
51

Xavi Artigas's avatar
Xavi Artigas committed
52
      gst_message_parse_buffering (msg, &data->buffering_level);
53

Xavi Artigas's avatar
Xavi Artigas committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
      /* Wait until buffering is complete before start/resume playing */
      if (data->buffering_level < 100)
        gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
      else
        gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
      break;
    case GST_MESSAGE_CLOCK_LOST:
      /* Get a new clock */
      gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
      gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
      break;
    default:
      /* Unhandled message */
      break;
    }
}
70

Xavi Artigas's avatar
Xavi Artigas committed
71 72 73
static gboolean refresh_ui (CustomData *data) {
  GstQuery *query;
  gboolean result;
74

Xavi Artigas's avatar
Xavi Artigas committed
75 76 77 78 79 80
  query = gst_query_new_buffering (GST_FORMAT_PERCENT);
  result = gst_element_query (data->pipeline, query);
  if (result) {
    gint n_ranges, range, i;
    gchar graph[GRAPH_LENGTH + 1];
    gint64 position = 0, duration = 0;
81

Xavi Artigas's avatar
Xavi Artigas committed
82 83
    memset (graph, ' ', GRAPH_LENGTH);
    graph[GRAPH_LENGTH] = '\0';
84

Xavi Artigas's avatar
Xavi Artigas committed
85 86 87 88
    n_ranges = gst_query_get_n_buffering_ranges (query);
    for (range = 0; range < n_ranges; range++) {
      gint64 start, stop;
      gst_query_parse_nth_buffering_range (query, range, &start, &stop);
89 90
      start = start * GRAPH_LENGTH / (stop - start);
      stop = stop * GRAPH_LENGTH / (stop - start);
Xavi Artigas's avatar
Xavi Artigas committed
91 92 93
      for (i = (gint)start; i < stop; i++)
        graph [i] = '-';
    }
94
    if (gst_element_query_position (data->pipeline, GST_FORMAT_TIME, &position) &&
Xavi Artigas's avatar
Xavi Artigas committed
95
        GST_CLOCK_TIME_IS_VALID (position) &&
96
        gst_element_query_duration (data->pipeline, GST_FORMAT_TIME, &duration) &&
Xavi Artigas's avatar
Xavi Artigas committed
97 98 99 100 101 102 103 104 105 106 107 108
        GST_CLOCK_TIME_IS_VALID (duration)) {
      i = (gint)(GRAPH_LENGTH * (double)position / (double)(duration + 1));
      graph [i] = data->buffering_level < 100 ? 'X' : '>';
    }
    g_print ("[%s]", graph);
    if (data->buffering_level < 100) {
      g_print (" Buffering: %3d%%", data->buffering_level);
    } else {
      g_print ("                ");
    }
    g_print ("\r");
  }
109

Xavi Artigas's avatar
Xavi Artigas committed
110
  return TRUE;
111

Xavi Artigas's avatar
Xavi Artigas committed
112
}
113

Xavi Artigas's avatar
Xavi Artigas committed
114 115 116 117 118 119 120
int main(int argc, char *argv[]) {
  GstElement *pipeline;
  GstBus *bus;
  GstStateChangeReturn ret;
  GMainLoop *main_loop;
  CustomData data;
  guint flags;
121

Xavi Artigas's avatar
Xavi Artigas committed
122 123
  /* Initialize GStreamer */
  gst_init (&argc, &argv);
124

Xavi Artigas's avatar
Xavi Artigas committed
125 126 127
  /* Initialize our data structure */
  memset (&data, 0, sizeof (data));
  data.buffering_level = 100;
128

Xavi Artigas's avatar
Xavi Artigas committed
129
  /* Build the pipeline */
130
  pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);
Xavi Artigas's avatar
Xavi Artigas committed
131
  bus = gst_element_get_bus (pipeline);
132

Xavi Artigas's avatar
Xavi Artigas committed
133 134 135 136
  /* Set the download flag */
  g_object_get (pipeline, "flags", &flags, NULL);
  flags |= GST_PLAY_FLAG_DOWNLOAD;
  g_object_set (pipeline, "flags", flags, NULL);
137

138 139
  /* Uncomment this line to limit the amount of downloaded data */
  /* g_object_set (pipeline, "ring-buffer-max-size", (guint64)4000000, NULL); */
140

Xavi Artigas's avatar
Xavi Artigas committed
141 142 143 144 145 146 147 148 149
  /* Start playing */
  ret = gst_element_set_state (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 (pipeline);
    return -1;
  } else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
    data.is_live = TRUE;
  }
150

Xavi Artigas's avatar
Xavi Artigas committed
151 152 153
  main_loop = g_main_loop_new (NULL, FALSE);
  data.loop = main_loop;
  data.pipeline = pipeline;
154

Xavi Artigas's avatar
Xavi Artigas committed
155
  gst_bus_add_signal_watch (bus);
Xavi Artigas's avatar
Xavi Artigas committed
156
  g_signal_connect (bus, "message", G_CALLBACK (cb_message), &data);
Xavi Artigas's avatar
Xavi Artigas committed
157
  g_signal_connect (pipeline, "deep-notify::temp-location", G_CALLBACK (got_location), NULL);
158

Xavi Artigas's avatar
Xavi Artigas committed
159 160
  /* Register a function that GLib will call every second */
  g_timeout_add_seconds (1, (GSourceFunc)refresh_ui, &data);
161

Xavi Artigas's avatar
Xavi Artigas committed
162
  g_main_loop_run (main_loop);
163

Xavi Artigas's avatar
Xavi Artigas committed
164 165 166 167 168 169 170 171
  /* Free resources */
  g_main_loop_unref (main_loop);
  gst_object_unref (bus);
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);

  g_print ("\n");
  return 0;
172
}