1 /* GStreamer
2 * Copyright (C) <2015> Jan Schmidt <jan@centricular.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <string.h>
25
26 #include "video.h"
27
28 GType
gst_video_multiview_flagset_get_type(void)29 gst_video_multiview_flagset_get_type (void)
30 {
31 static GType type = 0;
32
33 if (g_once_init_enter (&type)) {
34 GType _type = gst_flagset_register (GST_TYPE_VIDEO_MULTIVIEW_FLAGS);
35 g_once_init_leave (&type, _type);
36 }
37 return type;
38 }
39
40
41 /* Caps mnemonics for the various multiview representations */
42
43 static const struct mview_map_t
44 {
45 const gchar *caps_repr;
46 GstVideoMultiviewMode mode;
47 } gst_multiview_modes[] = {
48 {
49 "mono", GST_VIDEO_MULTIVIEW_MODE_MONO}, {
50 "left", GST_VIDEO_MULTIVIEW_MODE_LEFT}, {
51 "right", GST_VIDEO_MULTIVIEW_MODE_RIGHT}, {
52 "side-by-side", GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE}, {
53 "side-by-side-quincunx", GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX}, {
54 "column-interleaved", GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED}, {
55 "row-interleaved", GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED}, {
56 "top-bottom", GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM}, {
57 "checkerboard", GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD}, {
58 "frame-by-frame", GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME}, {
59 "multiview-frame-by-frame",
60 GST_VIDEO_MULTIVIEW_MODE_MULTIVIEW_FRAME_BY_FRAME}, {
61 "separated", GST_VIDEO_MULTIVIEW_MODE_SEPARATED}
62 };
63
64 /**
65 * gst_video_multiview_mode_to_caps_string:
66 * @mview_mode: A #GstVideoMultiviewMode value
67 *
68 * Returns: The caps string representation of the mode, or NULL if invalid.
69 *
70 * Given a #GstVideoMultiviewMode returns the multiview-mode caps string
71 * for insertion into a caps structure
72 *
73 * Since: 1.6
74 */
75 const gchar *
gst_video_multiview_mode_to_caps_string(GstVideoMultiviewMode mview_mode)76 gst_video_multiview_mode_to_caps_string (GstVideoMultiviewMode mview_mode)
77 {
78 gint i;
79
80 for (i = 0; i < G_N_ELEMENTS (gst_multiview_modes); i++) {
81 if (gst_multiview_modes[i].mode == mview_mode) {
82 return gst_multiview_modes[i].caps_repr;
83 }
84 }
85
86 return NULL;
87 }
88
89 /**
90 * gst_video_multiview_mode_from_caps_string:
91 * @caps_mview_mode: multiview-mode field string from caps
92 *
93 * Returns: The #GstVideoMultiviewMode value
94 *
95 * Given a string from a caps multiview-mode field,
96 * output the corresponding #GstVideoMultiviewMode
97 * or #GST_VIDEO_MULTIVIEW_MODE_NONE
98 *
99 * Since: 1.6
100 */
101 GstVideoMultiviewMode
gst_video_multiview_mode_from_caps_string(const gchar * caps_mview_mode)102 gst_video_multiview_mode_from_caps_string (const gchar * caps_mview_mode)
103 {
104 gint i;
105
106 for (i = 0; i < G_N_ELEMENTS (gst_multiview_modes); i++) {
107 if (g_str_equal (gst_multiview_modes[i].caps_repr, caps_mview_mode)) {
108 return gst_multiview_modes[i].mode;
109 }
110 }
111
112 GST_ERROR ("Invalid multiview info %s", caps_mview_mode);
113 g_warning ("Invalid multiview info %s", caps_mview_mode);
114
115 return GST_VIDEO_MULTIVIEW_MODE_NONE;
116 }
117
118 /* Array of mono, unpacked, double-height and double-width modes */
119 static GValue mode_values[5];
120
121 static void
init_mview_mode_vals(void)122 init_mview_mode_vals (void)
123 {
124 static gsize mview_mode_vals_init = 0;
125
126 if (g_once_init_enter (&mview_mode_vals_init)) {
127 GValue item = { 0, };
128 GValue *list;
129
130 g_value_init (&item, G_TYPE_STRING);
131
132 /* Mono modes */
133 list = mode_values;
134 g_value_init (list, GST_TYPE_LIST);
135 g_value_set_static_string (&item, "mono");
136 gst_value_list_append_value (list, &item);
137 g_value_set_static_string (&item, "left");
138 gst_value_list_append_value (list, &item);
139 g_value_set_static_string (&item, "right");
140 gst_value_list_append_value (list, &item);
141
142 /* Unpacked modes - ones split across buffers or memories */
143 list = mode_values + 1;
144 g_value_init (list, GST_TYPE_LIST);
145 g_value_set_static_string (&item, "separated");
146 gst_value_list_append_value (list, &item);
147 g_value_set_static_string (&item, "frame-by-frame");
148 gst_value_list_append_value (list, &item);
149 g_value_set_static_string (&item, "multiview-frame-by-frame");
150 gst_value_list_append_value (list, &item);
151
152 /* Double height modes */
153 list = mode_values + 2;
154 g_value_init (list, GST_TYPE_LIST);
155 g_value_set_static_string (&item, "top-bottom");
156 gst_value_list_append_value (list, &item);
157 g_value_set_static_string (&item, "row-interleaved");
158 gst_value_list_append_value (list, &item);
159
160 /* Double width modes */
161 list = mode_values + 3;
162 g_value_init (list, GST_TYPE_LIST);
163 g_value_set_static_string (&item, "side-by-side");
164 gst_value_list_append_value (list, &item);
165 g_value_set_static_string (&item, "side-by-side-quincunx");
166 gst_value_list_append_value (list, &item);
167 g_value_set_static_string (&item, "column-interleaved");
168 gst_value_list_append_value (list, &item);
169
170 /* Double size (both width & height) modes */
171 list = mode_values + 4;
172 g_value_init (list, GST_TYPE_LIST);
173 g_value_set_static_string (&item, "checkerboard");
174 gst_value_list_append_value (list, &item);
175
176 g_value_unset (&item);
177 g_once_init_leave (&mview_mode_vals_init, 1);
178 }
179 }
180
181 /**
182 * gst_video_multiview_get_mono_modes:
183 *
184 * Returns: A const #GValue containing a list of mono video modes
185 *
186 * Utility function that returns a #GValue with a GstList of mono video
187 * modes (mono/left/right) for use in caps negotiations.
188 *
189 * Since: 1.6
190 */
191 const GValue *
gst_video_multiview_get_mono_modes(void)192 gst_video_multiview_get_mono_modes (void)
193 {
194 init_mview_mode_vals ();
195 return mode_values;
196 }
197
198 /**
199 * gst_video_multiview_get_unpacked_modes:
200 *
201 * Returns: A const #GValue containing a list of 'unpacked' stereo video modes
202 *
203 * Utility function that returns a #GValue with a GstList of unpacked
204 * stereo video modes (separated/frame-by-frame/frame-by-frame-multiview)
205 * for use in caps negotiations.
206 *
207 * Since: 1.6
208 */
209 const GValue *
gst_video_multiview_get_unpacked_modes(void)210 gst_video_multiview_get_unpacked_modes (void)
211 {
212 init_mview_mode_vals ();
213 return mode_values + 1;
214 }
215
216 /**
217 * gst_video_multiview_get_doubled_height_modes:
218 *
219 * Returns: A const #GValue containing a list of stereo video modes
220 *
221 * Utility function that returns a #GValue with a GstList of packed stereo
222 * video modes with double the height of a single view for use in
223 * caps negotiations. Currently this is top-bottom and row-interleaved.
224 *
225 * Since: 1.6
226 */
227 const GValue *
gst_video_multiview_get_doubled_height_modes(void)228 gst_video_multiview_get_doubled_height_modes (void)
229 {
230 init_mview_mode_vals ();
231 return mode_values + 2;
232 }
233
234 /**
235 * gst_video_multiview_get_doubled_width_modes:
236 *
237 * Returns: A const #GValue containing a list of stereo video modes
238 *
239 * Utility function that returns a #GValue with a GstList of packed stereo
240 * video modes with double the width of a single view for use in
241 * caps negotiations. Currently this is side-by-side, side-by-side-quincunx
242 * and column-interleaved.
243 *
244 * Since: 1.6
245 */
246 const GValue *
gst_video_multiview_get_doubled_width_modes(void)247 gst_video_multiview_get_doubled_width_modes (void)
248 {
249 init_mview_mode_vals ();
250 return mode_values + 3;
251 }
252
253 /**
254 * gst_video_multiview_get_doubled_size_modes:
255 *
256 * Returns: A const #GValue containing a list of stereo video modes
257 *
258 * Utility function that returns a #GValue with a GstList of packed
259 * stereo video modes that have double the width/height of a single
260 * view for use in caps negotiation. Currently this is just
261 * 'checkerboard' layout.
262 *
263 * Since: 1.6
264 */
265 const GValue *
gst_video_multiview_get_doubled_size_modes(void)266 gst_video_multiview_get_doubled_size_modes (void)
267 {
268 init_mview_mode_vals ();
269 return mode_values + 4;
270 }
271
272 static void
gst_video_multiview_separated_video_info_from_packed(GstVideoInfo * info)273 gst_video_multiview_separated_video_info_from_packed (GstVideoInfo * info)
274 {
275 GstVideoMultiviewMode mview_mode;
276
277 mview_mode = GST_VIDEO_INFO_MULTIVIEW_MODE (info);
278
279 /* Normalise the half-aspect flag by adjusting PAR */
280 switch (mview_mode) {
281 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
282 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX:
283 case GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED:
284 case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
285 info->width /= 2;
286 info->views *= 2;
287 GST_VIDEO_INFO_MULTIVIEW_MODE (info) = GST_VIDEO_MULTIVIEW_MODE_SEPARATED;
288 if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &
289 GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT)
290 info->par_n *= 2;
291 break;
292 case GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED:
293 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
294 info->height /= 2;
295 info->views *= 2;
296 GST_VIDEO_INFO_MULTIVIEW_MODE (info) = GST_VIDEO_MULTIVIEW_MODE_SEPARATED;
297 if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &
298 GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT)
299 info->par_d *= 2;
300 break;
301 default:
302 /* Mono/left/right/frame-by-frame/already separated */
303 break;
304 }
305 GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &=
306 ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
307 }
308
309 static void
gst_video_multiview_separated_video_info_to_packed(GstVideoInfo * info,GstVideoMultiviewMode packed_mview_mode,GstVideoMultiviewFlags packed_mview_flags)310 gst_video_multiview_separated_video_info_to_packed (GstVideoInfo * info,
311 GstVideoMultiviewMode packed_mview_mode,
312 GstVideoMultiviewFlags packed_mview_flags)
313 {
314 /* Convert single-frame info to a packed mode */
315 GST_VIDEO_INFO_MULTIVIEW_MODE (info) = packed_mview_mode;
316 GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) = packed_mview_flags;
317
318 switch (packed_mview_mode) {
319 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
320 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX:
321 case GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED:
322 case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
323 info->width *= 2;
324 info->views /= 2;
325 if (packed_mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT)
326 info->par_d *= 2;
327 break;
328 case GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED:
329 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
330 info->height *= 2;
331 info->views /= 2;
332 if (packed_mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT)
333 info->par_n *= 2;
334 break;
335 default:
336 break;
337 }
338 }
339
340 /**
341 * gst_video_multiview_video_info_change_mode:
342 * @info: A #GstVideoInfo structure to operate on
343 * @out_mview_mode: A #GstVideoMultiviewMode value
344 * @out_mview_flags: A set of #GstVideoMultiviewFlags
345 *
346 * Utility function that transforms the width/height/PAR
347 * and multiview mode and flags of a #GstVideoInfo into
348 * the requested mode.
349 *
350 * Since: 1.6
351 */
352 void
gst_video_multiview_video_info_change_mode(GstVideoInfo * info,GstVideoMultiviewMode out_mview_mode,GstVideoMultiviewFlags out_mview_flags)353 gst_video_multiview_video_info_change_mode (GstVideoInfo * info,
354 GstVideoMultiviewMode out_mview_mode,
355 GstVideoMultiviewFlags out_mview_flags)
356 {
357 gst_video_multiview_separated_video_info_from_packed (info);
358 gst_video_multiview_separated_video_info_to_packed (info, out_mview_mode,
359 out_mview_flags);
360 }
361
362 /**
363 * gst_video_multiview_guess_half_aspect:
364 * @mv_mode: A #GstVideoMultiviewMode
365 * @width: Video frame width in pixels
366 * @height: Video frame height in pixels
367 * @par_n: Numerator of the video pixel-aspect-ratio
368 * @par_d: Denominator of the video pixel-aspect-ratio
369 *
370 * Returns: A boolean indicating whether the
371 * #GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT flag should be set.
372 *
373 * Utility function that heuristically guess whether a
374 * frame-packed stereoscopic video contains half width/height
375 * encoded views, or full-frame views by looking at the
376 * overall display aspect ratio.
377 *
378 * Since: 1.6
379 */
380 gboolean
gst_video_multiview_guess_half_aspect(GstVideoMultiviewMode mv_mode,guint width,guint height,guint par_n,guint par_d)381 gst_video_multiview_guess_half_aspect (GstVideoMultiviewMode mv_mode,
382 guint width, guint height, guint par_n, guint par_d)
383 {
384 switch (mv_mode) {
385 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
386 case GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED:
387 /* If the video is wider than it is tall, assume half aspect */
388 if (height * par_d <= width * par_n)
389 return TRUE;
390 break;
391 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
392 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX:
393 case GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED:
394 /* If the video DAR is less than 2.39:1, assume half-aspect */
395 if (width * par_n < 2.39 * height * par_d)
396 return TRUE;
397 break;
398 default:
399 break;
400 }
401 return FALSE;
402 }
403
404 #if 0 /* Multiview meta disabled for now */
405 GType
406 gst_video_multiview_meta_api_get_type (void)
407 {
408 static GType type = 0;
409 static const gchar *tags[] =
410 { GST_META_TAG_VIDEO_STR, GST_META_TAG_MEMORY_STR,
411 NULL
412 };
413
414 if (g_once_init_enter (&type)) {
415 GType _type = gst_meta_api_type_register ("GstVideoMultiviewMetaAPI", tags);
416 g_once_init_leave (&type, _type);
417 }
418 return type;
419 }
420
421 static gboolean
422 gst_video_multiview_meta_init (GstVideoMultiviewMeta * mview_meta,
423 gpointer params, GstBuffer * buffer)
424 {
425 mview_meta->n_views = 0;
426 mview_meta->view_info = NULL;
427
428 return TRUE;
429 }
430
431 static void
432 gst_video_multiview_meta_free (GstVideoMultiviewMeta * mview_meta,
433 GstBuffer * buffer)
434 {
435 g_free (mview_meta->view_info);
436 }
437
438 /* video multiview metadata */
439 const GstMetaInfo *
440 gst_video_multiview_meta_get_info (void)
441 {
442 static const GstMetaInfo *video_meta_info = NULL;
443
444 if (g_once_init_enter (&video_meta_info)) {
445 const GstMetaInfo *meta =
446 gst_meta_register (GST_VIDEO_MULTIVIEW_META_API_TYPE,
447 "GstVideoMultiviewMeta",
448 sizeof (GstVideoMultiviewMeta),
449 (GstMetaInitFunction) gst_video_multiview_meta_init,
450 (GstMetaFreeFunction) gst_video_multiview_meta_free,
451 NULL);
452 g_once_init_leave (&video_meta_info, meta);
453 }
454
455 return video_meta_info;
456 }
457
458
459 GstVideoMultiviewMeta *
460 gst_buffer_add_video_multiview_meta (GstBuffer * buffer, guint n_views)
461 {
462 GstVideoMultiviewMeta *meta;
463
464 meta =
465 (GstVideoMultiviewMeta *) gst_buffer_add_meta (buffer,
466 GST_VIDEO_MULTIVIEW_META_INFO, NULL);
467
468 if (!meta)
469 return NULL;
470
471 meta->view_info = g_new0 (GstVideoMultiviewViewInfo, n_views);
472 meta->n_views = n_views;
473
474 return meta;
475 }
476
477 void
478 gst_video_multiview_meta_set_n_views (GstVideoMultiviewMeta * mview_meta,
479 guint n_views)
480 {
481 guint i;
482
483 mview_meta->view_info =
484 g_renew (GstVideoMultiviewViewInfo, mview_meta->view_info, n_views);
485
486 if (mview_meta->view_info == NULL) {
487 if (n_views > 0)
488 g_warning ("Failed to allocate GstVideoMultiview data");
489 mview_meta->n_views = 0;
490 return;
491 }
492
493 /* Make sure new entries are zero */
494 for (i = mview_meta->n_views; i < n_views; i++) {
495 GstVideoMultiviewViewInfo *info = mview_meta->view_info + i;
496
497 info->meta_id = 0;
498 info->view_label = GST_VIDEO_MULTIVIEW_VIEW_UNKNOWN;
499 }
500 mview_meta->n_views = n_views;
501 }
502
503 #endif
504