#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 */

gint
main (gint argc, gchar **argv)
{
  g_autoptr(GstElement) pipeline = NULL, app1 = NULL;
  g_autoptr(GError) error = NULL;
  GstVideoInfo info;

  gst_init (&argc, &argv);

  pipeline = gst_parse_launch ("appsrc is-live=1 format=time max-bytes=0 name=app1 ! videoscale ! video/x-raw,width=800,height=600 ! agg. "
                               "appsrc is-live=1 format=time max-bytes=0 name=app2 ! agg. "
                               "compositor name=agg ! appsink fakesink", &error);
  g_assert_no_error (error);
  g_object_ref_sink (pipeline);

  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  app1 = gst_bin_get_by_name (GST_BIN (pipeline), "app1");

  /* - Setup caps on app1
   * - Push two buffers on app1
   * - Setup caps on app2
   * - Push one buffer on app2
   *
   *   This should result in one buffer being aggregated. Appsink will endup
   *   blocking in wait_preroll due to the second unlinked sink in the
   *   pipeline */
  {
    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 (app1), 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 (app1), 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 (app1), buf);

    app2 = gst_bin_get_by_name (GST_BIN (pipeline), "app2");
    gst_app_src_set_caps (GST_APP_SRC (app2), 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 (app2), buf);
  }

  /* - Change the caps on app1
   *
   * This will trigger an allocation query in videoscale (basetransform when
   * not in passthrough, will blindly always run an allocation query when
   * upstream caps change, we are using this minor bug here */
  {
    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 (app1), caps);
  }

  /* Racy, wait for state change to PAUSED instead */
  g_usleep (G_USEC_PER_SEC / 3);

#if 0
  /* This  work around the issue by deblocking the source */
  gst_element_send_event (app1, 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;
}

