diff --git a/.gitignore b/.gitignore index 508357b400..3cef8b5033 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,7 @@ gst*orc.h /tests/examples/opencv/gsthanddetect_test /tests/examples/opencv/gstfacedetect_test /tests/examples/playout +/tests/examples/waylandsink/gtkwaylandsink Build *.user diff --git a/configure.ac b/configure.ac index e71f9dd257..2ecc868e99 100644 --- a/configure.ac +++ b/configure.ac @@ -3559,6 +3559,7 @@ tests/examples/mpegts/Makefile tests/examples/mxf/Makefile tests/examples/opencv/Makefile tests/examples/uvch264/Makefile +tests/examples/waylandsink/Makefile tests/icles/Makefile ext/voamrwbenc/Makefile ext/voaacenc/Makefile diff --git a/tests/examples/Makefile.am b/tests/examples/Makefile.am index c26d3d9fd1..57ed6ddc3a 100644 --- a/tests/examples/Makefile.am +++ b/tests/examples/Makefile.am @@ -40,6 +40,16 @@ else GTK3_DIR= endif +if USE_WAYLAND +if HAVE_GTK3 +WAYLAND_DIR=waylandsink +else +WAYLAND_DIR= +endif +else +WAYLAND_DIR= +endif + noinst_PROGRAMS = playout playout_SOURCES = playout.c @@ -47,7 +57,8 @@ playout_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) playout_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) $(GST_LIBS) SUBDIRS= codecparsers mpegts $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) \ - $(GL_DIR) $(GTK3_DIR) $(AVSAMPLE_DIR) -DIST_SUBDIRS= codecparsers mpegts camerabin2 directfb mxf opencv uvch264 gl gtk avsamplesink + $(GL_DIR) $(GTK3_DIR) $(AVSAMPLE_DIR) $(WAYLAND_DIR) +DIST_SUBDIRS= codecparsers mpegts camerabin2 directfb mxf opencv uvch264 gl gtk \ + avsamplesink waylandsink include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/tests/examples/waylandsink/Makefile.am b/tests/examples/waylandsink/Makefile.am new file mode 100644 index 0000000000..4a6649e21e --- /dev/null +++ b/tests/examples/waylandsink/Makefile.am @@ -0,0 +1,9 @@ +noinst_PROGRAMS = gtkwaylandsink + +gtkwaylandsink_SOURCES = main.c + +gtkwaylandsink_CFLAGS=-I$(top_srcdir)/gst-libs -I$(top_builddir)/gst-libs \ + $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(GTK3_CFLAGS) +gtkwaylandsink_LDADD=$(GTK3_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + -lgstvideo-$(GST_API_VERSION) \ + -L$(top_srcdir)/gst-libs/gst/wayland -lgstwayland-$(GST_API_VERSION) diff --git a/tests/examples/waylandsink/main.c b/tests/examples/waylandsink/main.c new file mode 100644 index 0000000000..2f9bf8954a --- /dev/null +++ b/tests/examples/waylandsink/main.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2014-2015 Collabora Ltd. + * @author George Kiagiadakis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#ifdef GDK_WINDOWING_WAYLAND +#include +#else +#error "Wayland is not supported in GTK+" +#endif + +#include +#include + + +static gboolean live = FALSE; + +static GOptionEntry entries[] = { + {"live", 'l', 0, G_OPTION_ARG_NONE, &live, "Use a live source", NULL}, + {NULL} +}; + +typedef struct +{ + GtkWidget *app_widget; + GtkWidget *video_widget; + + GstElement *pipeline; + GstVideoOverlay *overlay; + + gchar **argv; + gint current_uri; /* index for argv */ +} DemoApp; + +static void +on_about_to_finish (GstElement * playbin, DemoApp * d) +{ + if (d->argv[++d->current_uri] == NULL) + d->current_uri = 1; + + g_print ("Now playing %s\n", d->argv[d->current_uri]); + g_object_set (playbin, "uri", d->argv[d->current_uri], NULL); +} + +static void +error_cb (GstBus * bus, GstMessage * msg, gpointer user_data) +{ + DemoApp *d = user_data; + gchar *debug = NULL; + GError *err = NULL; + + gst_message_parse_error (msg, &err, &debug); + + g_print ("Error: %s\n", err->message); + g_error_free (err); + + if (debug) { + g_print ("Debug details: %s\n", debug); + g_free (debug); + } + + gst_element_set_state (d->pipeline, GST_STATE_NULL); +} + +static GstBusSyncReply +bus_sync_handler (GstBus * bus, GstMessage * message, gpointer user_data) +{ + DemoApp *d = user_data; + + if (gst_is_wayland_display_handle_need_context_message (message)) { + GstContext *context; + GdkDisplay *display; + struct wl_display *display_handle; + + display = gtk_widget_get_display (d->video_widget); + display_handle = gdk_wayland_display_get_wl_display (display); + context = gst_wayland_display_handle_context_new (display_handle); + gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (message)), context); + + goto drop; + } else if (gst_is_video_overlay_prepare_window_handle_message (message)) { + GtkAllocation allocation; + GdkWindow *window; + struct wl_surface *window_handle; + + /* GST_MESSAGE_SRC (message) will be the overlay object that we have to + * use. This may be waylandsink, but it may also be playbin. In the latter + * case, we must make sure to use playbin instead of waylandsink, because + * playbin resets the window handle and render_rectangle after restarting + * playback and the actual window size is lost */ + d->overlay = GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message)); + + gtk_widget_get_allocation (d->video_widget, &allocation); + window = gtk_widget_get_window (d->video_widget); + window_handle = gdk_wayland_window_get_wl_surface (window); + + g_print ("setting window handle and size (%d x %d)\n", + allocation.width, allocation.height); + + gst_video_overlay_set_window_handle (d->overlay, (guintptr) window_handle); + gst_video_overlay_set_render_rectangle (d->overlay, allocation.x, + allocation.y, allocation.width, allocation.height); + + goto drop; + } + + return GST_BUS_PASS; + +drop: + gst_message_unref (message); + return GST_BUS_DROP; +} + +/* We use the "draw" callback to change the size of the sink + * because the "configure-event" is only sent to top-level widgets. */ +static gboolean +video_widget_draw_cb (GtkWidget * widget, cairo_t * cr, gpointer user_data) +{ + DemoApp *d = user_data; + GtkAllocation allocation; + + gtk_widget_get_allocation (widget, &allocation); + + g_print ("draw_cb x %d, y %d, w %d, h %d\n", + allocation.x, allocation.y, allocation.width, allocation.height); + + if (d->overlay) { + gst_video_overlay_set_render_rectangle (d->overlay, allocation.x, + allocation.y, allocation.width, allocation.height); + } + + /* There is no need to call gst_video_overlay_expose(). + * The wayland compositor can always re-draw the window + * based on its last contents if necessary */ + + return FALSE; +} + +static void +playing_clicked_cb (GtkButton * button, DemoApp * d) +{ + gst_element_set_state (d->pipeline, GST_STATE_PLAYING); +} + +static void +paused_clicked_cb (GtkButton * button, DemoApp * d) +{ + gst_element_set_state (d->pipeline, GST_STATE_PAUSED); +} + +static void +ready_clicked_cb (GtkButton * button, DemoApp * d) +{ + gst_element_set_state (d->pipeline, GST_STATE_READY); +} + +static void +null_clicked_cb (GtkButton * button, DemoApp * d) +{ + gst_element_set_state (d->pipeline, GST_STATE_NULL); +} + +static void +build_window (DemoApp * d) +{ + GtkBuilder *builder; + GtkWidget *button; + GError *error = NULL; + + builder = gtk_builder_new (); + if (!gtk_builder_add_from_file (builder, "window.ui", &error)) { + g_error ("Failed to load window.ui: %s", error->message); + g_error_free (error); + goto exit; + } + + d->app_widget = GTK_WIDGET (gtk_builder_get_object (builder, "window")); + g_object_ref (d->app_widget); + g_signal_connect (d->app_widget, "destroy", G_CALLBACK (gtk_main_quit), NULL); + + d->video_widget = GTK_WIDGET (gtk_builder_get_object (builder, "videoarea")); + g_signal_connect (d->video_widget, "draw", + G_CALLBACK (video_widget_draw_cb), d); + + button = GTK_WIDGET (gtk_builder_get_object (builder, "button_playing")); + g_signal_connect (button, "clicked", G_CALLBACK (playing_clicked_cb), d); + + button = GTK_WIDGET (gtk_builder_get_object (builder, "button_paused")); + g_signal_connect (button, "clicked", G_CALLBACK (paused_clicked_cb), d); + + button = GTK_WIDGET (gtk_builder_get_object (builder, "button_ready")); + g_signal_connect (button, "clicked", G_CALLBACK (ready_clicked_cb), d); + + button = GTK_WIDGET (gtk_builder_get_object (builder, "button_null")); + g_signal_connect (button, "clicked", G_CALLBACK (null_clicked_cb), d); + + gtk_widget_show_all (d->app_widget); + +exit: + g_object_unref (builder); +} + +int +main (int argc, char **argv) +{ + DemoApp *d; + GOptionContext *context; + GstBus *bus; + GError *error = NULL; + + gtk_init (&argc, &argv); + gst_init (&argc, &argv); + + context = g_option_context_new ("- waylandsink gtk demo"); + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) { + g_printerr ("option parsing failed: %s\n", error->message); + return 1; + } + + d = g_slice_new0 (DemoApp); + build_window (d); + + if (argc > 1) { + d->argv = argv; + d->current_uri = 1; + + d->pipeline = gst_parse_launch ("playbin video-sink=waylandsink", NULL); + g_object_set (d->pipeline, "uri", argv[d->current_uri], NULL); + + /* enable looping */ + g_signal_connect (d->pipeline, "about-to-finish", + G_CALLBACK (on_about_to_finish), d); + } else { + if (live) { + d->pipeline = gst_parse_launch ("videotestsrc pattern=18 " + "background-color=0x000062FF is-live=true ! waylandsink", NULL); + } else { + d->pipeline = gst_parse_launch ("videotestsrc pattern=18 " + "background-color=0x000062FF ! waylandsink", NULL); + } + } + + bus = gst_pipeline_get_bus (GST_PIPELINE (d->pipeline)); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message::error", G_CALLBACK (error_cb), d); + gst_bus_set_sync_handler (bus, bus_sync_handler, d, NULL); + gst_object_unref (bus); + + gst_element_set_state (d->pipeline, GST_STATE_PLAYING); + + gtk_main (); + + gst_element_set_state (d->pipeline, GST_STATE_NULL); + gst_object_unref (d->pipeline); + g_object_unref (d->app_widget); + g_slice_free (DemoApp, d); + + return 0; +} diff --git a/tests/examples/waylandsink/window.ui b/tests/examples/waylandsink/window.ui new file mode 100644 index 0000000000..ce6cf823cd --- /dev/null +++ b/tests/examples/waylandsink/window.ui @@ -0,0 +1,99 @@ + + + + + + False + GStreamer Wayland GTK Demo + + + True + False + vertical + + + 400 + 300 + True + True + False + False + True + + + + + + False + True + 0 + + + + + True + False + center + + + PLAYING + True + True + True + + + False + True + 0 + + + + + PAUSED + True + True + True + + + False + True + 1 + + + + + READY + True + True + True + + + False + True + 2 + + + + + NULL + True + True + True + + + False + True + 3 + + + + + False + True + 1 + + + + + +