21.3. Time, clocking and synchronization

The above example does not provide any timing info, but will suffice for elementary data sources such as a file source or network data source element. Things become slightly more complicated, but still very simple, if we create artificial video or audio data sources, such as a video test image source or an artificial audio source (e.g. sinesrc or silence). It will become more complicated if we want the element to be a realtime capture source, such as a video4linux source (for reading video frames from a TV card) or an ALSA source (for reading data from soundcards supported by an ALSA-driver). Here, we will need to make the element aware of timing and clocking.

Timestamps can essentially be generated from all the information given above without any difficulty. We could add a very small amount of code to generate perfectly timestamped buffers from our _get ()-function:


static void
gst_my_source_init (GstMySource *src)
{
[..]
  src->total_bytes = 0;
}

static GstData *
gst_my_source_get (GstPad *pad)
{
  GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));
  GstBuffer *buf;
  GstFormat fmt = GST_FORMAT_TIME;
[..]
  GST_BUFFER_DURATION (buf) = GST_BUFFER_SIZE (buf) * (GST_SECOND / (44100 * 2));
  GST_BUFFER_TIMESTAMP (buf) = src->total_bytes * (GST_SECOND / (44100 * 2));
  src->total_bytes += GST_BUFFER_SIZE (buf);

  return GST_DATA (buf);
}

static GstStateReturn
gst_my_source_change_state (GstElement *element)
{
  GstMySource *src = GST_MY_SOURCE (element);

  switch (GST_STATE_PENDING (element)) {
    case GT_STATE_PAUSED_TO_READY:
      src->total_bytes = 0;
      break;

    default:
      break;
  }

  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);

  return GST_STATE_SUCCESS;
}
    

That wasn't too hard. Now, let's assume real-time elements. Those can either have hardware-timing, in which case we can rely on backends to provide sync for us (in which case you probably want to provide a clock), or we will have to emulate that internally (e.g. to acquire sync in artificial data elements such as sinesrc). Let's first look at the second option (software sync). The first option (hardware sync + providing a clock) does not require any special code with respect to timing, and the clocking section already explained how to provide a clock.


enum {
  ARG_0,
[..]
  ARG_SYNC,
[..]
};

static void
gst_my_source_class_init (GstMySourceClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
[..]
  g_object_class_install_property (object_class, ARG_SYNC,
      g_param_spec_boolean ("sync", "Sync", "Synchronize to clock",
                            FALSE, G_PARAM_READWRITE));
[..]
}

static void
gst_my_source_init (GstMySource *src)
{
[..]
  src->sync = FALSE;
}

static GstData *
gst_my_source_get (GstPad *pad)
{
  GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));
  GstBuffer *buf;
[..]
  if (src->sync) {
    /* wait on clock */
    gst_element_wait (GST_ELEMENT (src), GST_BUFFER_TIMESTAMP (buf));
  }

  return GST_DATA (buf);
}

static void
gst_my_source_get_property (GObject    *object,
			    guint       prop_id,
			    GParamSpec *pspec,
			    GValue     *value)
{
  GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));

  switch (prop_id) {
[..]
    case ARG_SYNC:
      g_value_set_boolean (value, src->sync);
      break;
[..]
  }
}

static void
gst_my_source_get_property (GObject      *object,
			    guint         prop_id,
			    GParamSpec   *pspec,
			    const GValue *value)
{
  GstMySource *src = GST_MY_SOURCE (gst_pad_get_parent (pad));

  switch (prop_id) {
[..]
    case ARG_SYNC:
      src->sync = g_value_get_boolean (value);
      break;
[..]
  }
}
    

Most of this is GObject wrapping code. The actual code to do software-sync (in the _get ()-function) is relatively small.