Coverage Report

Created: 2025-11-16 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/video/video-multiview.c
Line
Count
Source
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
29
gst_video_multiview_flagset_get_type (void)
30
2
{
31
2
  static GType type = 0;
32
33
2
  if (g_once_init_enter (&type)) {
34
2
    GType _type = gst_flagset_register (GST_TYPE_VIDEO_MULTIVIEW_FLAGS);
35
2
    g_once_init_leave (&type, _type);
36
2
  }
37
2
  return type;
38
2
}
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
 * Given a #GstVideoMultiviewMode returns the multiview-mode caps string
69
 * for insertion into a caps structure
70
 *
71
 * Returns: (nullable): The caps string representation of the mode, or NULL if invalid.
72
 *
73
 * Since: 1.6
74
 */
75
const gchar *
76
gst_video_multiview_mode_to_caps_string (GstVideoMultiviewMode mview_mode)
77
0
{
78
0
  gint i;
79
80
0
  for (i = 0; i < G_N_ELEMENTS (gst_multiview_modes); i++) {
81
0
    if (gst_multiview_modes[i].mode == mview_mode) {
82
0
      return gst_multiview_modes[i].caps_repr;
83
0
    }
84
0
  }
85
86
0
  return NULL;
87
0
}
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
102
gst_video_multiview_mode_from_caps_string (const gchar * caps_mview_mode)
103
0
{
104
0
  gint i;
105
106
0
  for (i = 0; i < G_N_ELEMENTS (gst_multiview_modes); i++) {
107
0
    if (g_str_equal (gst_multiview_modes[i].caps_repr, caps_mview_mode)) {
108
0
      return gst_multiview_modes[i].mode;
109
0
    }
110
0
  }
111
112
0
  GST_ERROR ("Invalid multiview info %s", caps_mview_mode);
113
0
  g_warning ("Invalid multiview info %s", caps_mview_mode);
114
115
0
  return GST_VIDEO_MULTIVIEW_MODE_NONE;
116
0
}
117
118
/* Array of mono, unpacked, double-height and double-width modes */
119
static GValue mode_values[5];
120
121
static void
122
init_mview_mode_vals (void)
123
0
{
124
0
  static gsize mview_mode_vals_init = 0;
125
126
0
  if (g_once_init_enter (&mview_mode_vals_init)) {
127
0
    GValue item = { 0, };
128
0
    GValue *list;
129
130
0
    g_value_init (&item, G_TYPE_STRING);
131
132
    /* Mono modes */
133
0
    list = mode_values;
134
0
    g_value_init (list, GST_TYPE_LIST);
135
0
    g_value_set_static_string (&item, "mono");
136
0
    gst_value_list_append_value (list, &item);
137
0
    g_value_set_static_string (&item, "left");
138
0
    gst_value_list_append_value (list, &item);
139
0
    g_value_set_static_string (&item, "right");
140
0
    gst_value_list_append_value (list, &item);
141
142
    /* Unpacked modes - ones split across buffers or memories */
143
0
    list = mode_values + 1;
144
0
    g_value_init (list, GST_TYPE_LIST);
145
0
    g_value_set_static_string (&item, "separated");
146
0
    gst_value_list_append_value (list, &item);
147
0
    g_value_set_static_string (&item, "frame-by-frame");
148
0
    gst_value_list_append_value (list, &item);
149
0
    g_value_set_static_string (&item, "multiview-frame-by-frame");
150
0
    gst_value_list_append_value (list, &item);
151
152
    /* Double height modes */
153
0
    list = mode_values + 2;
154
0
    g_value_init (list, GST_TYPE_LIST);
155
0
    g_value_set_static_string (&item, "top-bottom");
156
0
    gst_value_list_append_value (list, &item);
157
0
    g_value_set_static_string (&item, "row-interleaved");
158
0
    gst_value_list_append_value (list, &item);
159
160
    /* Double width modes */
161
0
    list = mode_values + 3;
162
0
    g_value_init (list, GST_TYPE_LIST);
163
0
    g_value_set_static_string (&item, "side-by-side");
164
0
    gst_value_list_append_value (list, &item);
165
0
    g_value_set_static_string (&item, "side-by-side-quincunx");
166
0
    gst_value_list_append_value (list, &item);
167
0
    g_value_set_static_string (&item, "column-interleaved");
168
0
    gst_value_list_append_value (list, &item);
169
170
    /* Double size (both width & height) modes */
171
0
    list = mode_values + 4;
172
0
    g_value_init (list, GST_TYPE_LIST);
173
0
    g_value_set_static_string (&item, "checkerboard");
174
0
    gst_value_list_append_value (list, &item);
175
176
0
    g_value_unset (&item);
177
0
    g_once_init_leave (&mview_mode_vals_init, 1);
178
0
  }
179
0
}
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 *
192
gst_video_multiview_get_mono_modes (void)
193
0
{
194
0
  init_mview_mode_vals ();
195
0
  return mode_values;
196
0
}
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 *
210
gst_video_multiview_get_unpacked_modes (void)
211
0
{
212
0
  init_mview_mode_vals ();
213
0
  return mode_values + 1;
214
0
}
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 *
228
gst_video_multiview_get_doubled_height_modes (void)
229
0
{
230
0
  init_mview_mode_vals ();
231
0
  return mode_values + 2;
232
0
}
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 *
247
gst_video_multiview_get_doubled_width_modes (void)
248
0
{
249
0
  init_mview_mode_vals ();
250
0
  return mode_values + 3;
251
0
}
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 *
266
gst_video_multiview_get_doubled_size_modes (void)
267
0
{
268
0
  init_mview_mode_vals ();
269
0
  return mode_values + 4;
270
0
}
271
272
static void
273
gst_video_multiview_separated_video_info_from_packed (GstVideoInfo * info)
274
0
{
275
0
  GstVideoMultiviewMode mview_mode;
276
277
0
  mview_mode = GST_VIDEO_INFO_MULTIVIEW_MODE (info);
278
279
  /* Normalise the half-aspect flag by adjusting PAR */
280
0
  switch (mview_mode) {
281
0
    case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
282
0
    case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX:
283
0
    case GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED:
284
0
    case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
285
0
      info->width /= 2;
286
0
      info->views *= 2;
287
0
      GST_VIDEO_INFO_MULTIVIEW_MODE (info) = GST_VIDEO_MULTIVIEW_MODE_SEPARATED;
288
0
      if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &
289
0
          GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT)
290
0
        info->par_n *= 2;
291
0
      break;
292
0
    case GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED:
293
0
    case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
294
0
      info->height /= 2;
295
0
      info->views *= 2;
296
0
      GST_VIDEO_INFO_MULTIVIEW_MODE (info) = GST_VIDEO_MULTIVIEW_MODE_SEPARATED;
297
0
      if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &
298
0
          GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT)
299
0
        info->par_d *= 2;
300
0
      break;
301
0
    default:
302
      /* Mono/left/right/frame-by-frame/already separated */
303
0
      break;
304
0
  }
305
0
  GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &=
306
0
      ~GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT;
307
0
}
308
309
static void
310
gst_video_multiview_separated_video_info_to_packed (GstVideoInfo * info,
311
    GstVideoMultiviewMode packed_mview_mode,
312
    GstVideoMultiviewFlags packed_mview_flags)
313
0
{
314
  /* Convert single-frame info to a packed mode */
315
0
  GST_VIDEO_INFO_MULTIVIEW_MODE (info) = packed_mview_mode;
316
0
  GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) = packed_mview_flags;
317
318
0
  switch (packed_mview_mode) {
319
0
    case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
320
0
    case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX:
321
0
    case GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED:
322
0
    case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
323
0
      info->width *= 2;
324
0
      info->views /= 2;
325
0
      if (packed_mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT)
326
0
        info->par_d *= 2;
327
0
      break;
328
0
    case GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED:
329
0
    case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
330
0
      info->height *= 2;
331
0
      info->views /= 2;
332
0
      if (packed_mview_flags & GST_VIDEO_MULTIVIEW_FLAGS_HALF_ASPECT)
333
0
        info->par_n *= 2;
334
0
      break;
335
0
    default:
336
0
      break;
337
0
  }
338
0
}
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
353
gst_video_multiview_video_info_change_mode (GstVideoInfo * info,
354
    GstVideoMultiviewMode out_mview_mode,
355
    GstVideoMultiviewFlags out_mview_flags)
356
0
{
357
0
  gst_video_multiview_separated_video_info_from_packed (info);
358
0
  gst_video_multiview_separated_video_info_to_packed (info, out_mview_mode,
359
0
      out_mview_flags);
360
0
}
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
381
gst_video_multiview_guess_half_aspect (GstVideoMultiviewMode mv_mode,
382
    guint width, guint height, guint par_n, guint par_d)
383
0
{
384
0
  switch (mv_mode) {
385
0
    case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
386
0
    case GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED:
387
      /* If the video is wider than it is tall, assume half aspect */
388
0
      if (height * par_d <= width * par_n)
389
0
        return TRUE;
390
0
      break;
391
0
    case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
392
0
    case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX:
393
0
    case GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED:
394
      /* If the video DAR is less than 2.39:1, assume half-aspect */
395
0
      if (width * par_n < 2.39 * height * par_d)
396
0
        return TRUE;
397
0
      break;
398
0
    default:
399
0
      break;
400
0
  }
401
0
  return FALSE;
402
0
}
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