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