#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <gst/video/video.h>

/* gcc -Wall $(pkg-config --libs --cflags gstreamer-1.0 gstreamer-app-1.0 gstreamer-video-1.0) test.c -o test */

static GstPadProbeReturn
caps_event_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
  GMainLoop *loop = user_data;

  if (GST_EVENT_TYPE (info->data) == GST_EVENT_CAPS) {
    GstEvent *event = info->data;
    GstCaps *caps;
    GstVideoInfo info;

    gst_event_parse_caps (event, &caps);
    gst_video_info_from_caps (&info, caps);

    if (info.width == 180 && info.height == 120)
      g_main_loop_quit (loop);
  }

  return GST_PAD_PROBE_OK;
}

gint
main (gint argc, gchar **argv)
{
  g_autoptr(GstElement) pipeline = NULL, app = NULL;
  g_autoptr(GstPad) pad = NULL;
  g_autoptr(GError) error = NULL;
  GstVideoInfo info;
  GMainLoop *loop;

  gst_init (&argc, &argv);

  loop = g_main_loop_new (NULL, FALSE);

  pipeline =
    gst_parse_launch ("appsrc is-live=1 format=time max-bytes=0 name=app "
                      "! videoscale ! video/x-raw,width=800,height=600 "
                      "! queue max-size-buffers=1 ! fakesink "
                      "fakesink", &error);
  g_assert_no_error (error);
  g_object_ref_sink (pipeline);

  app = gst_bin_get_by_name (GST_BIN (pipeline), "app");
  pad = gst_element_get_static_pad (app, "src");

  gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
      caps_event_probe_cb, loop, NULL);

  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  /* - Setup caps
   * - Push two buffers
   *
   * This will make the queue block on wait_for_preroll in fakesink. Then we
   * fill the queue. */
  {
    g_autoptr(GstCaps) caps = NULL;
    g_autoptr(GstElement) app2 = NULL;
    GstBuffer *buf;

    gst_video_info_set_format (&info, GST_VIDEO_FORMAT_BGRx, 320, 240);
    caps = gst_video_info_to_caps (&info);

    gst_app_src_set_caps (GST_APP_SRC (app), caps);
    buf = gst_buffer_new_allocate (NULL, info.size, NULL);
    GST_BUFFER_PTS (buf) = 0;
    GST_BUFFER_DURATION (buf) = GST_SECOND;
    gst_app_src_push_buffer (GST_APP_SRC (app), buf);

    buf = gst_buffer_new_allocate (NULL, info.size, NULL);
    GST_BUFFER_PTS (buf) = GST_SECOND;
    GST_BUFFER_DURATION (buf) = GST_SECOND;
    gst_app_src_push_buffer (GST_APP_SRC (app), buf);
  }

  /* - Change the caps
   *
   * The caps event will be blocked until the queue is emptied. */
  {
    g_autoptr(GstCaps) caps = NULL;

    gst_video_info_set_format (&info, GST_VIDEO_FORMAT_BGRx, 180, 120);
    caps = gst_video_info_to_caps (&info);
    gst_app_src_set_caps (GST_APP_SRC (app), caps);
  }

  g_main_loop_run (loop);

#if 0
  /* This  work around the issue by deblocking the source */
  gst_element_send_event (app, gst_event_new_flush_start ());
#endif

  /* As appsrc change the caps GstBaseSrc::create() virtual function, the live
   * lock remains held and prevent the state change from happending. */
  gst_element_set_state (pipeline, GST_STATE_NULL);

  return 0;
}

