
/*
 * Example showing usage of cairooverlay gstreamer element
 * Roughly based on gst_x_overlay documentation
 */

// export PKG_CONFIG_PATH=/home/jon/.local/lib/pkgconfig
// export GST_PLUGIN_PATH=/home/jon/.local/lib/gstreamer-0.10
// export LD_LIBRARY_PATH=/home/jon/.local/lib
//
// gcc gst-cairo-overlay-example.c -o gst-cairo-overlay-example `pkg-config gstreamer-0.10 gstreamer-interfaces-0.10 gtk+-2.0 --libs --cflags`
// ./gst-cairo-overlay-example

#include <gst/gst.h>
#include <gst/interfaces/xoverlay.h>
#include <gtk/gtk.h>

#include <cairo.h>
#include <cairo-gobject.h>

#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif

static gulong video_window_xid = 0;

static GstBusSyncReply
bus_sync_handler (GstBus * bus, GstMessage * message, gpointer user_data)
{
  // ignore anything but 'prepare-xwindow-id' element messages
  if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
    return GST_BUS_PASS;
  if (!gst_structure_has_name (message->structure, "prepare-xwindow-id"))
    return GST_BUS_PASS;

  if (video_window_xid != 0) {
    GstXOverlay *xoverlay;

    // GST_MESSAGE_SRC (message) will be the video sink element
    xoverlay = GST_X_OVERLAY (GST_MESSAGE_SRC (message));
    gst_x_overlay_set_window_handle (xoverlay, video_window_xid);
  } else {
    g_warning ("Should have obtained video_window_xid by now!");
  }

  gst_message_unref (message);
  return GST_BUS_DROP;
}

static void
video_widget_realize_cb (GtkWidget * widget, gpointer data)
{
#ifdef GDK_WINDOWING_X11
  video_window_xid = GDK_WINDOW_XID (widget->window);
#endif
}

static void
render_test (GstElement * overlay, cairo_t * cr, int width, int height)
{
  cairo_save (cr);
  cairo_rectangle (cr, 0, 0, width, height / 2);
  cairo_set_source_rgba (cr, 0.9, 0, 0.3, 0.8);
  cairo_fill (cr);
  cairo_restore (cr);
}

GtkWidget *
setup_gtk_window ()
{
  GtkWidget *video_window;
  GtkWidget *app_window;

  app_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  video_window = gtk_drawing_area_new ();
  g_signal_connect (video_window, "realize",
      G_CALLBACK (video_widget_realize_cb), NULL);
  gtk_widget_set_double_buffered (video_window, FALSE);

  gtk_container_add (GTK_CONTAINER (app_window), video_window);
  gtk_widget_show_all (app_window);

  gtk_widget_realize (app_window);
  g_assert (video_window_xid != 0);

  return app_window;
}

GstElement *
setup_gst_pipeline ()
{
  GstElement *pipeline;
  GstElement *cairo_overlay;
  GstElement *source, *adaptor1, *adaptor2, *sink;
  GstBus *bus;

  pipeline = gst_pipeline_new ("cairo-overlay-example");

  source = gst_element_factory_make ("videotestsrc", "source");
  adaptor1 = gst_element_factory_make ("ffmpegcolorspace", "adaptor1");
  cairo_overlay = gst_element_factory_make ("cairooverlay", "overlay");
  adaptor2 = gst_element_factory_make ("ffmpegcolorspace", "adaptor2");
  sink = gst_element_factory_make ("xvimagesink", "sink");

  g_assert (cairo_overlay);

  g_signal_connect (cairo_overlay, "draw", G_CALLBACK (render_test), NULL);

  gst_bin_add_many (GST_BIN (pipeline), source, adaptor1,
      cairo_overlay, adaptor2, sink, NULL);

  if (!gst_element_link_many (source, adaptor1,
          cairo_overlay, adaptor2, sink, NULL)) {
    g_warning ("Failed to link elements!");
  }

  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_set_sync_handler (bus, (GstBusSyncHandler) bus_sync_handler, NULL);
  gst_object_unref (bus);

  return pipeline;
}

int
main (int argc, char **argv)
{
  gtk_init (&argc, &argv);
  gst_init (&argc, &argv);

  GtkWidget *window = setup_gtk_window ();
  GstElement *pipeline = setup_gst_pipeline ();

  gst_element_set_state (pipeline, GST_STATE_PLAYING);
  gtk_main ();
}
