1 /*
2  * GStreamer
3  * Copyright (C) 2014-2015 Jan Schmidt <jan@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "mviewwidget.h"
25 
26 G_DEFINE_TYPE (GstMViewWidget, gst_mview_widget, GTK_TYPE_GRID);
27 
28 static void gst_mview_widget_constructed (GObject * o);
29 static void gst_mview_widget_set_property (GObject * object,
30     guint prop_id, const GValue * value, GParamSpec * pspec);
31 static void gst_mview_widget_get_property (GObject * object,
32     guint prop_id, GValue * value, GParamSpec * pspec);
33 
34 #define DEFAULT_DOWNMIX GST_GL_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS
35 
36 enum
37 {
38   PROP_0,
39   PROP_IS_OUTPUT,
40   PROP_MODE_SELECTOR,
41   PROP_FLAGS,
42   PROP_DOWNMIX_MODE
43 };
44 
45 typedef struct _ToggleClosure
46 {
47   GstMViewWidget *mv;
48   GstVideoMultiviewFlags flag;
49 } ToggleClosure;
50 
51 static GtkWidget *combo_box_from_enum (GType enum_type);
52 
53 static void
gst_mview_widget_class_init(GstMViewWidgetClass * klass)54 gst_mview_widget_class_init (GstMViewWidgetClass * klass)
55 {
56   GObjectClass *object_klass = (GObjectClass *) (klass);
57 
58   object_klass->constructed = gst_mview_widget_constructed;
59   object_klass->set_property = gst_mview_widget_set_property;
60   object_klass->get_property = gst_mview_widget_get_property;
61 
62   g_object_class_install_property (object_klass, PROP_IS_OUTPUT,
63       g_param_spec_boolean ("is-output", "Is an Output widget",
64           "TRUE if the widget should have downmix mode", FALSE,
65           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
66 
67   g_object_class_install_property (object_klass, PROP_MODE_SELECTOR,
68       g_param_spec_object ("mode-selector", "Multiview Mode selector",
69           "Multiview Mode selector widget",
70           GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
71   g_object_class_install_property (object_klass, PROP_FLAGS,
72       g_param_spec_flags ("flags", "Multiview Flags",
73           "multiview flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGS,
74           GST_VIDEO_MULTIVIEW_FLAGS_NONE,
75           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
76   g_object_class_install_property (object_klass, PROP_DOWNMIX_MODE,
77       g_param_spec_enum ("downmix-mode",
78           "Mode for mono downmixed output",
79           "Output anaglyph type to generate when downmixing to mono",
80           GST_TYPE_GL_STEREO_DOWNMIX, DEFAULT_DOWNMIX,
81           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
82 }
83 
84 static void
gst_mview_widget_init(GstMViewWidget * mv)85 gst_mview_widget_init (GstMViewWidget * mv)
86 {
87 }
88 
89 static void
flag_changed(GObject * w,ToggleClosure * c)90 flag_changed (GObject * w, ToggleClosure * c)
91 {
92   GstMViewWidget *mv = GST_MVIEW_WIDGET (c->mv);
93   gboolean flag_set;
94 
95   g_object_get (w, "active", &flag_set, NULL);
96 
97   if (flag_set)
98     mv->flags |= c->flag;
99   else
100     mv->flags &= ~(c->flag);
101   if (!mv->synching)
102     g_object_notify (G_OBJECT (mv), "flags");
103 }
104 
105 static void
link_button_to_flag(GstMViewWidget * mv,GtkWidget * w,GstVideoMultiviewFlags flag)106 link_button_to_flag (GstMViewWidget * mv, GtkWidget * w,
107     GstVideoMultiviewFlags flag)
108 {
109   ToggleClosure *c = g_new0 (ToggleClosure, 1);
110 
111   c->mv = mv;
112   c->flag = flag;
113 
114   g_signal_connect_data (G_OBJECT (w), "toggled", G_CALLBACK (flag_changed),
115       c, (GClosureNotify) g_free, 0);
116 }
117 
118 static void
sync_flags(GstMViewWidget * mv)119 sync_flags (GstMViewWidget * mv)
120 {
121   mv->synching = TRUE;
122   g_object_set (G_OBJECT (mv->lflip), "active",
123       ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED), NULL);
124   g_object_set (G_OBJECT (mv->lflop), "active",
125       ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED), NULL);
126   g_object_set (G_OBJECT (mv->rflip), "active",
127       ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED), NULL);
128   g_object_set (G_OBJECT (mv->rflop), "active",
129       ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED), NULL);
130   g_object_set (G_OBJECT (mv->right_first), "active",
131       ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST), NULL);
132   g_object_set (G_OBJECT (mv->half_aspect), "active",
133       ! !(mv->flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT), NULL);
134   mv->synching = FALSE;
135 }
136 
137 static const gchar *
enum_value_to_nick(GType enum_type,guint value)138 enum_value_to_nick (GType enum_type, guint value)
139 {
140   GEnumClass *enum_info;
141   GEnumValue *v;
142   const gchar *nick;
143 
144   enum_info = (GEnumClass *) (g_type_class_ref (enum_type));
145   g_return_val_if_fail (enum_info != NULL, NULL);
146 
147   v = g_enum_get_value (enum_info, value);
148   g_return_val_if_fail (v != NULL, NULL);
149 
150   nick = v->value_nick;
151 
152   g_type_class_unref (enum_info);
153 
154   return nick;
155 }
156 
157 static void
sync_downmix(GstMViewWidget * mv)158 sync_downmix (GstMViewWidget * mv)
159 {
160   mv->synching = TRUE;
161   gtk_combo_box_set_active_id (GTK_COMBO_BOX (mv->downmix_combo),
162       enum_value_to_nick (GST_TYPE_GL_STEREO_DOWNMIX, mv->downmix_mode));
163   mv->synching = FALSE;
164 }
165 
166 static gboolean
set_downmix_mode(GtkWidget * widget,gpointer data)167 set_downmix_mode (GtkWidget * widget, gpointer data)
168 {
169   GstMViewWidget *mv = GST_MVIEW_WIDGET (data);
170   gchar *downmix_mode = NULL;
171   GEnumClass *p_class;
172   GEnumValue *v;
173   GParamSpec *p =
174       g_object_class_find_property (G_OBJECT_GET_CLASS (mv), "downmix-mode");
175 
176   g_return_val_if_fail (p != NULL, FALSE);
177 
178   p_class = G_PARAM_SPEC_ENUM (p)->enum_class;
179   g_return_val_if_fail (p_class != NULL, FALSE);
180 
181   g_object_get (G_OBJECT (widget), "active-id", &downmix_mode, NULL);
182   g_return_val_if_fail (downmix_mode != NULL, FALSE);
183 
184   v = g_enum_get_value_by_nick (p_class, downmix_mode);
185   g_return_val_if_fail (v != NULL, FALSE);
186 
187   mv->downmix_mode = v->value;
188   if (!mv->synching)
189     g_object_notify (G_OBJECT (mv), "downmix-mode");
190 
191   return FALSE;
192 }
193 
194 static void
gst_mview_widget_constructed(GObject * o)195 gst_mview_widget_constructed (GObject * o)
196 {
197   GstMViewWidget *mv = GST_MVIEW_WIDGET (o);
198   GtkGrid *g = GTK_GRID (mv);
199   GtkWidget *w;
200 
201   gtk_widget_set_has_window (GTK_WIDGET (mv), FALSE);
202 
203   if (mv->is_output) {
204     mv->mode_selector = w = combo_box_from_enum (GST_TYPE_VIDEO_MULTIVIEW_MODE);
205     gtk_grid_attach (g, gtk_label_new ("Output:"), 0, 0, 1, 1);
206   } else {
207     mv->mode_selector = w =
208         combo_box_from_enum (GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING);
209     gtk_grid_attach (g, gtk_label_new ("Input:"), 0, 0, 1, 1);
210   }
211   gtk_grid_attach (g, mv->mode_selector, 1, 0, 3, 1);
212 
213   gtk_grid_attach (g, gtk_label_new (" Left "), 4, 0, 1, 1);
214   mv->lflip = w = gtk_toggle_button_new_with_label ("Flip");
215   link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLIPPED);
216   gtk_grid_attach (g, w, 5, 0, 1, 1);
217   mv->lflop = w = gtk_toggle_button_new_with_label ("Flop");
218   link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_LEFT_FLOPPED);
219   gtk_grid_attach (g, w, 6, 0, 1, 1);
220 
221   gtk_grid_attach (g, gtk_label_new (" Right "), 4, 1, 1, 1);
222   mv->rflip = w = gtk_toggle_button_new_with_label ("Flip");
223   link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLIPPED);
224   gtk_grid_attach (g, w, 5, 1, 1, 1);
225   mv->rflop = w = gtk_toggle_button_new_with_label ("Flop");
226   link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_FLOPPED);
227   gtk_grid_attach (g, w, 6, 1, 1, 1);
228 
229   mv->right_first = w = gtk_toggle_button_new_with_label ("Left/Right swap");
230   link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST);
231   gtk_grid_attach (g, w, 1, 1, 1, 1);
232   mv->half_aspect = w = gtk_toggle_button_new_with_label ("Half-Aspect");
233   link_button_to_flag (mv, w, GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT);
234   gtk_grid_attach (g, w, 2, 1, 1, 1);
235 
236   if (mv->is_output) {
237     mv->downmix_combo = w = combo_box_from_enum (GST_TYPE_GL_STEREO_DOWNMIX);
238     gtk_grid_attach (g, w, 1, 2, 3, 1);
239     sync_downmix (mv);
240     g_signal_connect (G_OBJECT (w), "changed",
241         G_CALLBACK (set_downmix_mode), mv);
242   }
243 }
244 
245 static void
gst_mview_widget_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)246 gst_mview_widget_set_property (GObject * object,
247     guint prop_id, const GValue * value, GParamSpec * pspec)
248 {
249   GstMViewWidget *mv = GST_MVIEW_WIDGET (object);
250   switch (prop_id) {
251     case PROP_IS_OUTPUT:
252       mv->is_output = g_value_get_boolean (value);
253       break;
254     case PROP_FLAGS:
255       mv->flags = (GstVideoMultiviewFlags) g_value_get_flags (value);
256       sync_flags (mv);
257       break;
258     case PROP_DOWNMIX_MODE:
259       mv->downmix_mode = g_value_get_enum (value);
260       sync_downmix (mv);
261       break;
262     default:
263       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
264       break;
265   }
266 }
267 
268 static void
gst_mview_widget_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)269 gst_mview_widget_get_property (GObject * object,
270     guint prop_id, GValue * value, GParamSpec * pspec)
271 {
272   GstMViewWidget *mv = GST_MVIEW_WIDGET (object);
273   switch (prop_id) {
274     case PROP_IS_OUTPUT:
275       g_value_set_boolean (value, mv->is_output);
276       break;
277     case PROP_MODE_SELECTOR:
278       g_value_set_object (value, mv->mode_selector);
279       break;
280     case PROP_FLAGS:
281       g_value_set_flags (value, mv->flags);
282       break;
283     case PROP_DOWNMIX_MODE:
284       g_value_set_enum (value, mv->downmix_mode);
285       break;
286     default:
287       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
288       break;
289   }
290 }
291 
292 static GtkWidget *
combo_box_from_enum(GType enum_type)293 combo_box_from_enum (GType enum_type)
294 {
295   GEnumClass *enum_info;
296   GtkWidget *combo;
297   guint i;
298 
299   enum_info = (GEnumClass *) (g_type_class_ref (enum_type));
300   g_return_val_if_fail (enum_info != NULL, NULL);
301 
302   combo = gtk_combo_box_text_new ();
303   for (i = 0; i < enum_info->n_values; i++) {
304     GEnumValue *v = enum_info->values + i;
305     gtk_combo_box_text_insert (GTK_COMBO_BOX_TEXT (combo),
306         i, v->value_nick, v->value_name);
307   }
308 
309   g_type_class_unref (enum_info);
310 
311   return combo;
312 }
313 
314 GtkWidget *
gst_mview_widget_new(gboolean is_output)315 gst_mview_widget_new (gboolean is_output)
316 {
317   GtkWidget *ret;
318 
319   ret = g_object_new (GST_TYPE_MVIEW_WIDGET, "is-output", is_output, NULL);
320 
321   return ret;
322 }
323