From 3ea9f51dd75001f046bbaf5549cd54a5c976d090 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 30 May 2015 03:09:17 +1000 Subject: [PATCH] 3dvideo: Add simple gtk example stereoscopic video player https://bugzilla.gnome.org/show_bug.cgi?id=611157 --- configure.ac | 1 + tests/examples/gl/gtk/3dvideo/.gitignore | 1 + tests/examples/gl/gtk/3dvideo/3dvideo.vcproj | 210 ++++++++++ tests/examples/gl/gtk/3dvideo/Makefile.am | 16 + tests/examples/gl/gtk/3dvideo/main.cpp | 417 +++++++++++++++++++ tests/examples/gl/gtk/3dvideo/mviewwidget.c | 324 ++++++++++++++ tests/examples/gl/gtk/3dvideo/mviewwidget.h | 78 ++++ tests/examples/gl/gtk/Makefile.am | 2 +- 8 files changed, 1048 insertions(+), 1 deletion(-) create mode 100644 tests/examples/gl/gtk/3dvideo/.gitignore create mode 100644 tests/examples/gl/gtk/3dvideo/3dvideo.vcproj create mode 100644 tests/examples/gl/gtk/3dvideo/Makefile.am create mode 100644 tests/examples/gl/gtk/3dvideo/main.cpp create mode 100644 tests/examples/gl/gtk/3dvideo/mviewwidget.c create mode 100644 tests/examples/gl/gtk/3dvideo/mviewwidget.h diff --git a/configure.ac b/configure.ac index 1cccf2adf5..bbf79885e3 100644 --- a/configure.ac +++ b/configure.ac @@ -3285,6 +3285,7 @@ tests/examples/gl/generic/doublecube/Makefile tests/examples/gl/generic/recordgraphic/Makefile tests/examples/gl/gtk/Makefile tests/examples/gl/gtk/gtkvideooverlay/Makefile +tests/examples/gl/gtk/3dvideo/Makefile tests/examples/gl/gtk/filternovideooverlay/Makefile tests/examples/gl/gtk/filtervideooverlay/Makefile tests/examples/gl/gtk/fxtest/Makefile diff --git a/tests/examples/gl/gtk/3dvideo/.gitignore b/tests/examples/gl/gtk/3dvideo/.gitignore new file mode 100644 index 0000000000..7c46573b54 --- /dev/null +++ b/tests/examples/gl/gtk/3dvideo/.gitignore @@ -0,0 +1 @@ +3dvideo diff --git a/tests/examples/gl/gtk/3dvideo/3dvideo.vcproj b/tests/examples/gl/gtk/3dvideo/3dvideo.vcproj new file mode 100644 index 0000000000..bc0299a8b5 --- /dev/null +++ b/tests/examples/gl/gtk/3dvideo/3dvideo.vcproj @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/examples/gl/gtk/3dvideo/Makefile.am b/tests/examples/gl/gtk/3dvideo/Makefile.am new file mode 100644 index 0000000000..94c8d16199 --- /dev/null +++ b/tests/examples/gl/gtk/3dvideo/Makefile.am @@ -0,0 +1,16 @@ +noinst_PROGRAMS = 3dvideo + +noinst_HEADERS = mviewwidget.h + +3dvideo_SOURCES = mviewwidget.c main.cpp + +3dvideo_CXXFLAGS=$(GST_CXXFLAGS) + +3dvideo_CPPFLAGS=-I$(top_srcdir)/gst-libs -I$(top_builddir)/gst-libs \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GL_CFLAGS) $(GTK3_CFLAGS) +3dvideo_LDADD=../libgstgtkhelper.la \ + $(top_builddir)/gst-libs/gst/gl/libgstgl-$(GST_API_VERSION).la \ + $(GTK3_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) \ + $(GL_LIBS) -lgstvideo-$(GST_API_VERSION) + diff --git a/tests/examples/gl/gtk/3dvideo/main.cpp b/tests/examples/gl/gtk/3dvideo/main.cpp new file mode 100644 index 0000000000..1d8e5b724d --- /dev/null +++ b/tests/examples/gl/gtk/3dvideo/main.cpp @@ -0,0 +1,417 @@ +/* + * GStreamer + * Copyright (C) 2008-2009 Julien Isorce + * Copyright (C) 2014-2015 Jan Schmidt + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include + +#include "../gstgtk.h" +#include "mviewwidget.h" + +/* Until playbin properties support dynamic changes, + * use our own glviewconvert */ +#define USE_GLCONVERT_FOR_INPUT 1 + +typedef struct _localstate +{ + GstVideoMultiviewFramePacking in_mode; + GstVideoMultiviewFlags out_mode; + GstVideoMultiviewFlags in_flags, out_flags; +} LocalState; + +static GstBusSyncReply +create_window (GstBus * bus, GstMessage * message, GtkWidget * widget) +{ + /* ignore anything but 'prepare-window-handle' element messages */ + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return GST_BUS_PASS; + + if (!gst_is_video_overlay_prepare_window_handle_message (message)) + return GST_BUS_PASS; + + /* do not call gdk_window_ensure_native for the first time here because + * we are in a different thread than the main thread */ + gst_video_overlay_set_gtk_window (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC + (message)), widget); + + gst_message_unref (message); + + return GST_BUS_DROP; +} + +static void +end_stream_cb (GstBus * bus, GstMessage * message, GstElement * pipeline) +{ + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_EOS: + g_print ("End of stream\n"); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + gtk_main_quit (); + break; + case GST_MESSAGE_ERROR: + { + gchar *debug = NULL; + GError *err = NULL; + + gst_message_parse_error (message, &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 (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + gtk_main_quit (); + break; + } + default: + break; + } +} + +static gboolean +draw_cb (GtkWidget * widget, cairo_t * cr, GstElement * videosink) +{ + gst_video_overlay_expose (GST_VIDEO_OVERLAY (videosink)); + return FALSE; +} + +static void +destroy_cb (GtkWidget * widget, GdkEvent * event, GstElement * pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + gtk_main_quit (); +} + +static void +button_state_ready_cb (GtkWidget * widget, GstElement * pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_READY); +} + +static void +button_state_paused_cb (GtkWidget * widget, GstElement * pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PAUSED); +} + +static void +button_state_playing_cb (GtkWidget * widget, GstElement * pipeline) +{ + gst_element_set_state (pipeline, GST_STATE_PLAYING); +} + +static gboolean +set_mview_mode (GtkWidget * combo, GObject * target, const gchar * prop_name) +{ + gchar *mview_mode = NULL; + GEnumClass *p_class; + GEnumValue *v; + GParamSpec *p = + g_object_class_find_property (G_OBJECT_GET_CLASS (target), prop_name); + + g_return_val_if_fail (p != NULL, FALSE); + + p_class = G_PARAM_SPEC_ENUM (p)->enum_class; + g_return_val_if_fail (p_class != NULL, FALSE); + + g_object_get (G_OBJECT (combo), "active-id", &mview_mode, NULL); + g_return_val_if_fail (mview_mode != NULL, FALSE); + + v = g_enum_get_value_by_nick (p_class, mview_mode); + g_return_val_if_fail (v != NULL, FALSE); + + g_object_set (target, prop_name, v->value, NULL); + + return FALSE; +} + +static gboolean +set_mview_input_mode (GtkWidget * widget, gpointer data) +{ +#if USE_GLCONVERT_FOR_INPUT + return set_mview_mode (widget, G_OBJECT (data), "input-mode-override"); +#else + return set_mview_mode (widget, G_OBJECT (data), "video-multiview-mode"); +#endif +} + +static gboolean +set_mview_output_mode (GtkWidget * widget, gpointer data) +{ + GstElement *sink = gst_bin_get_by_name (GST_BIN (data), "sink"); + set_mview_mode (widget, G_OBJECT (sink), "output-multiview-mode"); + gst_object_unref (GST_OBJECT (sink)); + return FALSE; +} + +static void +input_flags_changed (GObject * gobject, GParamSpec * pspec, gpointer user_data) +{ + GObject *target = G_OBJECT (user_data); + GstVideoMultiviewFlags flags; + + g_object_get (gobject, "flags", &flags, NULL); +#if USE_GLCONVERT_FOR_INPUT + g_object_set (target, "input-flags-override", flags, NULL); +#else + g_object_set (target, "video-multiview-flags", flags, NULL); +#endif +} + +static void +output_flags_changed (GObject * gobject, GParamSpec * pspec, gpointer user_data) +{ + GObject *target = G_OBJECT (user_data); + GstVideoMultiviewFlags flags; + GstElement *sink = gst_bin_get_by_name (GST_BIN (target), "sink"); + + g_object_get (gobject, "flags", &flags, NULL); + g_object_set (G_OBJECT (sink), "output-multiview-flags", flags, NULL); + + gst_object_unref (GST_OBJECT (sink)); +} + +static void +downmix_method_changed (GObject * gobject, GParamSpec * pspec, gpointer user_data) +{ + GObject *target = G_OBJECT (user_data); + GstGLStereoDownmix downmix_method; + GstElement *sink = gst_bin_get_by_name (GST_BIN (target), "sink"); + + g_object_get (gobject, "downmix-mode", &downmix_method, NULL); + g_object_set (sink, "output-multiview-downmix-mode", downmix_method, NULL); + gst_object_unref (GST_OBJECT (sink)); +} + +static const gchar * +enum_value_to_nick (GType enum_type, guint value) +{ + GEnumClass *enum_info; + GEnumValue *v; + const gchar *nick; + + enum_info = (GEnumClass *) (g_type_class_ref (enum_type)); + g_return_val_if_fail (enum_info != NULL, NULL); + + v = g_enum_get_value (enum_info, value); + g_return_val_if_fail (v != NULL, NULL); + + nick = v->value_nick; + + g_type_class_unref (enum_info); + + return nick; +} + +static void +detect_mode_from_uri (LocalState * state, const gchar * uri) +{ + if (strstr (uri, "HSBS")) { + state->in_mode = GST_VIDEO_MULTIVIEW_FRAME_PACKING_SIDE_BY_SIDE; + state->in_flags = GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT; + } else if (strstr (uri, "SBS")) { + state->in_mode = GST_VIDEO_MULTIVIEW_FRAME_PACKING_SIDE_BY_SIDE; + if (g_regex_match_simple ("half", uri, G_REGEX_CASELESS, + (GRegexMatchFlags) 0)) { + state->in_flags = GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT; + } + } +} + +gint +main (gint argc, gchar * argv[]) +{ + LocalState state; + GtkWidget *area, *combo, *w; + const gchar *uri; + + XInitThreads (); + + gst_init (&argc, &argv); + gtk_init (&argc, &argv); + + if (argc < 2) { + g_print ("Usage: 3dvideo \n"); + return 1; + } + + uri = argv[1]; + + GstElement *pipeline = gst_element_factory_make ("playbin", NULL); + GstBin *sinkbin = (GstBin *) gst_parse_bin_from_description ("glupload ! glcolorconvert ! glviewconvert name=viewconvert ! glimagesink name=sink", TRUE, NULL); +#if USE_GLCONVERT_FOR_INPUT + GstElement *glconvert = gst_bin_get_by_name (sinkbin, "viewconvert"); +#endif + GstElement *videosink = gst_bin_get_by_name (sinkbin, "sink"); + + /* Get defaults */ + g_object_get (pipeline, "video-multiview-mode", &state.in_mode, + "video-multiview-flags", &state.in_flags, NULL); + gst_child_proxy_get (GST_CHILD_PROXY (videosink), "sink::output-multiview-mode", &state.out_mode, + "sink::output-multiview-flags", &state.out_flags, NULL); + + detect_mode_from_uri (&state, uri); + + g_return_val_if_fail (pipeline != NULL, 1); + g_return_val_if_fail (videosink != NULL, 1); + + g_object_set (G_OBJECT (pipeline), "video-sink", sinkbin, NULL); + g_object_set (G_OBJECT (pipeline), "uri", uri, NULL); + +#if USE_GLCONVERT_FOR_INPUT + g_object_set (G_OBJECT (glconvert), "input-mode-override", state.in_mode, + NULL); + g_object_set (G_OBJECT (glconvert), "input-flags-override", state.in_flags, + NULL); +#else + g_object_set (G_OBJECT (pipeline), "video-multiview-mode", state.in_mode, + NULL); + g_object_set (G_OBJECT (pipeline), "video-multiview-flags", state.in_flags, + NULL); +#endif + + /* Connect to bus for signal handling */ + GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message::error", G_CALLBACK (end_stream_cb), + pipeline); + g_signal_connect (bus, "message::warning", G_CALLBACK (end_stream_cb), + pipeline); + g_signal_connect (bus, "message::eos", G_CALLBACK (end_stream_cb), pipeline); + + gst_element_set_state (pipeline, GST_STATE_READY); + + area = gtk_drawing_area_new (); + gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, area, NULL); + gst_object_unref (bus); + + /* Toplevel window */ + GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size (GTK_WINDOW (window), 800, 600); + gtk_window_set_title (GTK_WINDOW (window), "Stereoscopic video demo"); + GdkGeometry geometry; + geometry.min_width = 1; + geometry.min_height = 1; + geometry.max_width = -1; + geometry.max_height = -1; + gtk_window_set_geometry_hints (GTK_WINDOW (window), window, &geometry, + GDK_HINT_MIN_SIZE); + + GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + gtk_container_add (GTK_CONTAINER (window), vbox); + + /* area where the video is drawn */ + gtk_box_pack_start (GTK_BOX (vbox), area, TRUE, TRUE, 0); + + /* Buttons to control the pipeline state */ + GtkWidget *table = gtk_grid_new (); + gtk_container_add (GTK_CONTAINER (vbox), table); + + GtkWidget *button_state_ready = gtk_button_new_with_label ("Stop"); + g_signal_connect (G_OBJECT (button_state_ready), "clicked", + G_CALLBACK (button_state_ready_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_ready, 1, 0, 1, 1); + gtk_widget_show (button_state_ready); + + //control state paused + GtkWidget *button_state_paused = gtk_button_new_with_label ("Pause"); + g_signal_connect (G_OBJECT (button_state_paused), "clicked", + G_CALLBACK (button_state_paused_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_paused, 2, 0, 1, 1); + gtk_widget_show (button_state_paused); + + //control state playing + GtkWidget *button_state_playing = gtk_button_new_with_label ("Play"); + g_signal_connect (G_OBJECT (button_state_playing), "clicked", + G_CALLBACK (button_state_playing_cb), pipeline); + gtk_grid_attach (GTK_GRID (table), button_state_playing, 3, 0, 1, 1); + //gtk_widget_show (button_state_playing); + + w = gst_mview_widget_new (FALSE); + combo = GST_MVIEW_WIDGET (w)->mode_selector; + gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo), + enum_value_to_nick (GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING, + state.in_mode)); +#if USE_GLCONVERT_FOR_INPUT + g_signal_connect (G_OBJECT (combo), "changed", + G_CALLBACK (set_mview_input_mode), glconvert); +#else + g_signal_connect (G_OBJECT (combo), "changed", + G_CALLBACK (set_mview_input_mode), pipeline); +#endif + + g_object_set (G_OBJECT (w), "flags", state.in_flags, NULL); +#if USE_GLCONVERT_FOR_INPUT + g_signal_connect (G_OBJECT (w), "notify::flags", + G_CALLBACK (input_flags_changed), glconvert); +#else + g_signal_connect (G_OBJECT (w), "notify::flags", + G_CALLBACK (input_flags_changed), pipeline); +#endif + gtk_container_add (GTK_CONTAINER (vbox), w); + + w = gst_mview_widget_new (TRUE); + combo = GST_MVIEW_WIDGET (w)->mode_selector; + gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo), + enum_value_to_nick (GST_TYPE_VIDEO_MULTIVIEW_MODE, state.out_mode)); + g_signal_connect (G_OBJECT (combo), "changed", + G_CALLBACK (set_mview_output_mode), videosink); + + g_object_set (G_OBJECT (w), "flags", state.out_flags, NULL); + g_signal_connect (G_OBJECT (w), "notify::flags", + G_CALLBACK (output_flags_changed), videosink); + g_signal_connect (G_OBJECT (w), "notify::downmix-mode", + G_CALLBACK (downmix_method_changed), videosink); + gtk_container_add (GTK_CONTAINER (vbox), w); + + //configure the pipeline + g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (destroy_cb), + pipeline); + + gtk_widget_realize (area); + + /* Redraw needed when paused or stopped (PAUSED or READY) */ + g_signal_connect (area, "draw", G_CALLBACK (draw_cb), videosink); + + gtk_widget_show_all (window); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + gtk_main (); + + return 0; +} diff --git a/tests/examples/gl/gtk/3dvideo/mviewwidget.c b/tests/examples/gl/gtk/3dvideo/mviewwidget.c new file mode 100644 index 0000000000..b74409c135 --- /dev/null +++ b/tests/examples/gl/gtk/3dvideo/mviewwidget.c @@ -0,0 +1,324 @@ +/* + * GStreamer + * Copyright (C) 2014-2015 Jan Schmidt + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mviewwidget.h" + +G_DEFINE_TYPE (GstMViewWidget, gst_mview_widget, GTK_TYPE_GRID); + +static void gst_mview_widget_constructed (GObject * o); +static void gst_mview_widget_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_mview_widget_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +#define DEFAULT_DOWNMIX GST_GL_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS + +enum +{ + PROP_0, + PROP_IS_OUTPUT, + PROP_MODE_SELECTOR, + PROP_FLAGS, + PROP_DOWNMIX_MODE +}; + +typedef struct _ToggleClosure +{ + GstMViewWidget *mv; + GstVideoMultiviewFlags flag; +} ToggleClosure; + +static GtkWidget *combo_box_from_enum (GType enum_type); + +static void +gst_mview_widget_class_init (GstMViewWidgetClass * klass) +{ + GObjectClass *object_klass = (GObjectClass *) (klass); + + object_klass->constructed = gst_mview_widget_constructed; + object_klass->set_property = gst_mview_widget_set_property; + object_klass->get_property = gst_mview_widget_get_property; + + g_object_class_install_property (object_klass, PROP_IS_OUTPUT, + g_param_spec_boolean ("is-output", "Is an Output widget", + "TRUE if the widget should have downmix mode", FALSE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_klass, PROP_MODE_SELECTOR, + g_param_spec_object ("mode-selector", "Multiview Mode selector", + "Multiview Mode selector widget", + GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_klass, PROP_FLAGS, + g_param_spec_flags ("flags", "Multiview Flags", + "multiview flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGS, + GST_VIDEO_MULTIVIEW_FLAGS_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_klass, PROP_DOWNMIX_MODE, + g_param_spec_enum ("downmix-mode", + "Mode for mono downmixed output", + "Output anaglyph type to generate when downmixing to mono", + GST_TYPE_GL_STEREO_DOWNMIX_MODE_TYPE, DEFAULT_DOWNMIX, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_mview_widget_init (GstMViewWidget * mv) +{ +} + +static void +flag_changed (GObject * w, ToggleClosure * c) +{ + GstMViewWidget *mv = GST_MVIEW_WIDGET (c->mv); + gboolean flag_set; + + g_object_get (w, "active", &flag_set, NULL); + + if (flag_set) + mv->flags |= c->flag; + else + mv->flags &= ~(c->flag); + if (!mv->synching) + g_object_notify (G_OBJECT (mv), "flags"); +} + +static void +link_button_to_flag (GstMViewWidget * mv, GtkWidget * w, + GstVideoMultiviewFlags flag) +{ + ToggleClosure *c = g_new0 (ToggleClosure, 1); + + c->mv = mv; + c->flag = flag; + + g_signal_connect_data (G_OBJECT (w), "toggled", G_CALLBACK (flag_changed), + c, (GClosureNotify) g_free, 0); +} + +static void +sync_flags (GstMViewWidget * mv) +{ + mv->synching = TRUE; + g_object_set (G_OBJECT (mv->lflip), "active", + ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED), NULL); + g_object_set (G_OBJECT (mv->lflop), "active", + ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED), NULL); + g_object_set (G_OBJECT (mv->rflip), "active", + ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED), NULL); + g_object_set (G_OBJECT (mv->rflop), "active", + ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED), NULL); + g_object_set (G_OBJECT (mv->right_first), "active", + ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST), NULL); + g_object_set (G_OBJECT (mv->half_aspect), "active", + ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT), NULL); + mv->synching = FALSE; +} + +static const gchar * +enum_value_to_nick (GType enum_type, guint value) +{ + GEnumClass *enum_info; + GEnumValue *v; + const gchar *nick; + + enum_info = (GEnumClass *) (g_type_class_ref (enum_type)); + g_return_val_if_fail (enum_info != NULL, NULL); + + v = g_enum_get_value (enum_info, value); + g_return_val_if_fail (v != NULL, NULL); + + nick = v->value_nick; + + g_type_class_unref (enum_info); + + return nick; +} + +static void +sync_downmix (GstMViewWidget * mv) +{ + mv->synching = TRUE; + gtk_combo_box_set_active_id (GTK_COMBO_BOX (mv->downmix_combo), + enum_value_to_nick (GST_TYPE_GL_STEREO_DOWNMIX_MODE_TYPE, + mv->downmix_mode)); + mv->synching = FALSE; +} + +static gboolean +set_downmix_mode (GtkWidget * widget, gpointer data) +{ + GstMViewWidget *mv = GST_MVIEW_WIDGET (data); + gchar *downmix_mode = NULL; + GEnumClass *p_class; + GEnumValue *v; + GParamSpec *p = + g_object_class_find_property (G_OBJECT_GET_CLASS (mv), "downmix-mode"); + + g_return_val_if_fail (p != NULL, FALSE); + + p_class = G_PARAM_SPEC_ENUM (p)->enum_class; + g_return_val_if_fail (p_class != NULL, FALSE); + + g_object_get (G_OBJECT (widget), "active-id", &downmix_mode, NULL); + g_return_val_if_fail (downmix_mode != NULL, FALSE); + + v = g_enum_get_value_by_nick (p_class, downmix_mode); + g_return_val_if_fail (v != NULL, FALSE); + + mv->downmix_mode = v->value; + if (!mv->synching) + g_object_notify (G_OBJECT (mv), "downmix-mode"); + + return FALSE; +} + +static void +gst_mview_widget_constructed (GObject * o) +{ + GstMViewWidget *mv = GST_MVIEW_WIDGET (o); + GtkGrid *g = GTK_GRID (mv); + GtkWidget *w; + + gtk_widget_set_has_window (GTK_WIDGET (mv), FALSE); + + if (mv->is_output) { + mv->mode_selector = w = combo_box_from_enum (GST_TYPE_VIDEO_MULTIVIEW_MODE); + gtk_grid_attach (g, gtk_label_new ("Output:"), 0, 0, 1, 1); + } else { + mv->mode_selector = w = + combo_box_from_enum (GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING); + gtk_grid_attach (g, gtk_label_new ("Input:"), 0, 0, 1, 1); + } + gtk_grid_attach (g, mv->mode_selector, 1, 0, 3, 1); + + gtk_grid_attach (g, gtk_label_new (" Left "), 4, 0, 1, 1); + mv->lflip = w = gtk_toggle_button_new_with_label ("Flip"); + link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED); + gtk_grid_attach (g, w, 5, 0, 1, 1); + mv->lflop = w = gtk_toggle_button_new_with_label ("Flop"); + link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED); + gtk_grid_attach (g, w, 6, 0, 1, 1); + + gtk_grid_attach (g, gtk_label_new (" Right "), 4, 1, 1, 1); + mv->rflip = w = gtk_toggle_button_new_with_label ("Flip"); + link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED); + gtk_grid_attach (g, w, 5, 1, 1, 1); + mv->rflop = w = gtk_toggle_button_new_with_label ("Flop"); + link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED); + gtk_grid_attach (g, w, 6, 1, 1, 1); + + mv->right_first = w = gtk_toggle_button_new_with_label ("Left/Right swap"); + link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST); + gtk_grid_attach (g, w, 1, 1, 1, 1); + mv->half_aspect = w = gtk_toggle_button_new_with_label ("Half-Aspect"); + link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT); + gtk_grid_attach (g, w, 2, 1, 1, 1); + + if (mv->is_output) { + mv->downmix_combo = w = + combo_box_from_enum (GST_TYPE_GL_STEREO_DOWNMIX_MODE_TYPE); + gtk_grid_attach (g, w, 1, 2, 3, 1); + sync_downmix (mv); + g_signal_connect (G_OBJECT (w), "changed", + G_CALLBACK (set_downmix_mode), mv); + } +} + +static void +gst_mview_widget_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstMViewWidget *mv = GST_MVIEW_WIDGET (object); + switch (prop_id) { + case PROP_IS_OUTPUT: + mv->is_output = g_value_get_boolean (value); + break; + case PROP_FLAGS: + mv->flags = (GstVideoMultiviewFlags) g_value_get_flags (value); + sync_flags (mv); + break; + case PROP_DOWNMIX_MODE: + mv->downmix_mode = g_value_get_enum (value); + sync_downmix (mv); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mview_widget_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstMViewWidget *mv = GST_MVIEW_WIDGET (object); + switch (prop_id) { + case PROP_IS_OUTPUT: + g_value_set_boolean (value, mv->is_output); + break; + case PROP_MODE_SELECTOR: + g_value_set_object (value, mv->mode_selector); + break; + case PROP_FLAGS: + g_value_set_flags (value, mv->flags); + break; + case PROP_DOWNMIX_MODE: + g_value_set_enum (value, mv->downmix_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GtkWidget * +combo_box_from_enum (GType enum_type) +{ + GEnumClass *enum_info; + GtkWidget *combo; + guint i; + + enum_info = (GEnumClass *) (g_type_class_ref (enum_type)); + g_return_val_if_fail (enum_info != NULL, NULL); + + combo = gtk_combo_box_text_new (); + for (i = 0; i < enum_info->n_values; i++) { + GEnumValue *v = enum_info->values + i; + gtk_combo_box_text_insert (GTK_COMBO_BOX_TEXT (combo), + i, v->value_nick, v->value_name); + } + + g_type_class_unref (enum_info); + + return combo; +} + +GtkWidget * +gst_mview_widget_new (gboolean is_output) +{ + GtkWidget *ret; + + ret = g_object_new (GST_TYPE_MVIEW_WIDGET, "is-output", is_output, NULL); + + return ret; +} diff --git a/tests/examples/gl/gtk/3dvideo/mviewwidget.h b/tests/examples/gl/gtk/3dvideo/mviewwidget.h new file mode 100644 index 0000000000..b63d7a9447 --- /dev/null +++ b/tests/examples/gl/gtk/3dvideo/mviewwidget.h @@ -0,0 +1,78 @@ +/* + * GStreamer + * Copyright (C) 2014-2015 Jan Schmidt + * + * 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 + +#define GST_USE_UNSTABLE_API 1 + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_MVIEW_WIDGET (gst_mview_widget_get_type()) +#define GST_MVIEW_WIDGET(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MVIEW_WIDGET, GstMViewWidget)) +#define GST_MVIEW_WIDGET_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MVIEW_WIDGET, GstMViewWidgetClass)) +#define GST_IS_MVIEW_WIDGET(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MVIEW_WIDGET)) +#define GST_IS_MVIEW_WIDGET_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MVIEW_WIDGET)) +#define GST_MVIEW_WIDGET_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_MVIEW_WIDGET,GstMViewWidgetClass)) + +typedef struct _GstMViewWidget GstMViewWidget; +typedef struct _GstMViewWidgetClass GstMViewWidgetClass; + +struct _GstMViewWidget { + GtkGrid parent; + + gboolean is_output; + + GtkWidget *mode_selector; + + GstVideoMultiviewMode mode; + GstVideoMultiviewFlags flags; + GstGLStereoDownmix downmix_mode; + + /* Array of toggle buttons for flags */ + GtkWidget *lflip; + GtkWidget *lflop; + GtkWidget *rflip; + GtkWidget *rflop; + GtkWidget *half_aspect; + GtkWidget *right_first; + + GtkWidget *downmix_combo; + + gboolean synching; +}; + +struct _GstMViewWidgetClass { + GtkGridClass parent; +}; + +GType gst_mview_widget_get_type (); +GtkWidget *gst_mview_widget_new (gboolean is_output); + +G_END_DECLS diff --git a/tests/examples/gl/gtk/Makefile.am b/tests/examples/gl/gtk/Makefile.am index a41566bc0b..3c1867926c 100644 --- a/tests/examples/gl/gtk/Makefile.am +++ b/tests/examples/gl/gtk/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = . gtkvideooverlay filternovideooverlay filtervideooverlay fxtest switchvideooverlay +SUBDIRS = . gtkvideooverlay filternovideooverlay filtervideooverlay fxtest switchvideooverlay 3dvideo noinst_LTLIBRARIES = libgstgtkhelper.la