#include <gst/gst.h>

static void
pad_added_cb (GstElement *element, GstPad *new_pad, gpointer user_data)
{
  GstElement *conv = GST_ELEMENT_CAST (user_data);
  GstPad *target = gst_element_get_static_pad (conv, "sink");

  g_print ("decodebin added a pad, linking\n");

  if (gst_pad_can_link (new_pad, target)) {
    gst_pad_link (new_pad, target);
  }

  gst_object_unref (target);
}

static GstElement *
create_bin (gboolean no_preroll)
{
  GstElement *bin, *dbin, *conv, *freeze, *identity;
  GstPad *target, *ghost;

  bin = gst_bin_new (no_preroll ? "no_preroll_bin" : "async_bin");

  dbin = gst_element_factory_make ("uridecodebin", NULL);
  conv = gst_element_factory_make ("videoconvert", NULL);
  freeze = gst_element_factory_make ("imagefreeze", NULL);
  identity =  gst_element_factory_make ("identity", NULL);

  if (no_preroll) {
    g_object_set (G_OBJECT (identity), "sync", TRUE, NULL);
  }

  g_signal_connect (G_OBJECT (dbin), "pad-added",
      G_CALLBACK(pad_added_cb), conv);

  g_object_set (G_OBJECT (dbin), "uri",
      "file:///Users/dolphy/Desktop/snapshot.jpeg", NULL);

  gst_bin_add_many (GST_BIN (bin), dbin, conv, freeze, identity, NULL);
  gst_element_link_many (conv, freeze, identity, NULL);

  target = gst_element_get_static_pad (identity, "src");

  ghost = gst_ghost_pad_new (NULL, target);

  gst_element_add_pad (bin, ghost);

  gst_object_unref (target);

  return bin;
}

int main (int argc, char ** argv)
{
  GstElement *pipeline, *bin1, *bin2, *sink1, *sink2;
  GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;

  gst_init (&argc, &argv);

  pipeline = gst_pipeline_new (NULL);

  /* Create bin with async state change and no preroll */
  bin1 = create_bin (TRUE);
  bin2 = create_bin (FALSE);

  sink1 = gst_element_factory_make ("fakesink", NULL);
  sink2 = gst_element_factory_make ("fakesink", NULL);

  gst_bin_add_many (GST_BIN (pipeline), bin1, bin2, sink1, sink2, NULL);

  gst_element_link (bin1, sink1);
  gst_element_link (bin2, sink2);

  g_print ("going to PLAYING\n");

  gst_element_set_state (pipeline, GST_STATE_PAUSED);

  g_usleep (500000);

  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  gst_debug_bin_to_dot_file (GST_BIN (pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
      "state_changes");

  /* Wait for state to complete */
  do {
    GstState current, pending;
    ret = gst_element_get_state (pipeline, &current, &pending, 2 * GST_SECOND);
    g_print ("get state returned %d current: %s pending: %s\n", ret,
        gst_element_state_get_name (current),
        gst_element_state_get_name (pending));
  } while (ret != GST_STATE_CHANGE_SUCCESS);

  g_print ("completed state change to PLAYING\n");

  gst_element_set_state (pipeline, GST_STATE_NULL);

  gst_object_unref (pipeline);

  return 0;
}
