Coverage Report

Created: 2026-05-28 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/video/gstvideometa.c
Line
Count
Source
1
/* GStreamer
2
 * Copyright (C) <2011> Wim Taymans <wim.taymans@gmail.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
#ifdef HAVE_CONFIG_H
20
#include "config.h"
21
#endif
22
23
#include "gstvideometa.h"
24
25
#include <string.h>
26
#include <gst/base/base.h>
27
28
/**
29
 * SECTION:gstvideometa
30
 * @title: GstMeta for video
31
 * @short_description: Video related GstMeta
32
 *
33
 */
34
35
static gboolean
36
default_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
37
    gpointer * data, gint * stride, GstMapFlags flags);
38
static gboolean
39
default_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info);
40
41
#ifndef GST_DISABLE_GST_DEBUG
42
#define GST_CAT_DEFAULT ensure_debug_category()
43
static GstDebugCategory *
44
ensure_debug_category (void)
45
0
{
46
0
  static gsize cat_gonce = 0;
47
48
0
  if (g_once_init_enter (&cat_gonce)) {
49
0
    gsize cat_done;
50
51
0
    cat_done = (gsize) _gst_debug_category_new ("videometa", 0, "videometa");
52
53
0
    g_once_init_leave (&cat_gonce, cat_done);
54
0
  }
55
56
0
  return (GstDebugCategory *) cat_gonce;
57
0
}
58
#else
59
#define ensure_debug_category() /* NOOP */
60
#endif /* GST_DISABLE_GST_DEBUG */
61
62
static gboolean
63
gst_video_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
64
0
{
65
0
  GstVideoMeta *emeta = (GstVideoMeta *) meta;
66
67
0
  emeta->buffer = NULL;
68
0
  emeta->flags = GST_VIDEO_FRAME_FLAG_NONE;
69
0
  emeta->format = GST_VIDEO_FORMAT_UNKNOWN;
70
0
  emeta->id = 0;
71
0
  emeta->width = emeta->height = emeta->n_planes = 0;
72
0
  memset (emeta->offset, 0, sizeof (emeta->offset));
73
0
  memset (emeta->stride, 0, sizeof (emeta->stride));
74
0
  gst_video_alignment_reset (&emeta->alignment);
75
0
  emeta->map = NULL;
76
0
  emeta->unmap = NULL;
77
78
0
  return TRUE;
79
0
}
80
81
static gboolean
82
gst_video_meta_transform (GstBuffer * dest, GstMeta * meta,
83
    GstBuffer * buffer, GQuark type, gpointer data)
84
0
{
85
0
  GstVideoMeta *dmeta, *smeta;
86
0
  guint i;
87
88
0
  smeta = (GstVideoMeta *) meta;
89
90
0
  if (GST_META_TRANSFORM_IS_COPY (type)) {
91
0
    GstMetaTransformCopy *copy = data;
92
93
0
    if (!copy->region) {
94
      /* only copy if the complete data is copied as well */
95
0
      dmeta =
96
0
          (GstVideoMeta *) gst_buffer_add_meta (dest, GST_VIDEO_META_INFO,
97
0
          NULL);
98
99
0
      if (!dmeta)
100
0
        return FALSE;
101
102
0
      dmeta->buffer = dest;
103
104
0
      GST_DEBUG ("copy video metadata");
105
0
      dmeta->flags = smeta->flags;
106
0
      dmeta->format = smeta->format;
107
0
      dmeta->id = smeta->id;
108
0
      dmeta->width = smeta->width;
109
0
      dmeta->height = smeta->height;
110
111
0
      dmeta->n_planes = smeta->n_planes;
112
0
      for (i = 0; i < dmeta->n_planes; i++) {
113
0
        dmeta->offset[i] = smeta->offset[i];
114
0
        dmeta->stride[i] = smeta->stride[i];
115
0
        dmeta->alignment = smeta->alignment;
116
0
      }
117
0
      dmeta->map = smeta->map;
118
0
      dmeta->unmap = smeta->unmap;
119
0
    }
120
0
  } else {
121
    /* return FALSE, if transform type is not supported */
122
0
    return FALSE;
123
0
  }
124
0
  return TRUE;
125
0
}
126
127
static gboolean
128
gst_video_meta_api_params_aggregator (GstStructure ** aggregated_params,
129
    const GstStructure * params0, const GstStructure * params1)
130
0
{
131
0
  GstVideoAlignment align0;
132
0
  GstVideoAlignment align1;
133
0
  GstVideoAlignment aggregated_align;
134
135
0
  gst_video_alignment_reset (&align0);
136
0
  gst_video_alignment_reset (&align1);
137
0
  gst_video_alignment_reset (&aggregated_align);
138
139
0
  if (params0 && (!gst_structure_has_name (params0, "video-meta") ||
140
0
          !gst_buffer_pool_config_get_video_alignment (params0, &align0))) {
141
0
    GST_WARNING ("Invalid params");
142
0
    params0 = NULL;
143
0
  }
144
145
0
  if (params1 && (!gst_structure_has_name (params1, "video-meta") ||
146
0
          !gst_buffer_pool_config_get_video_alignment (params1, &align1))) {
147
0
    GST_WARNING ("Invalid params");
148
0
    params1 = NULL;
149
0
  }
150
151
0
  if (!params0 && !params1) {
152
0
    *aggregated_params = NULL;
153
0
    return TRUE;
154
0
  }
155
156
0
  if (params0 && !params1) {
157
0
    *aggregated_params = gst_structure_copy (params0);
158
0
    return TRUE;
159
0
  }
160
161
0
  if (!params0 && params1) {
162
0
    *aggregated_params = gst_structure_copy (params1);
163
0
    return TRUE;
164
0
  }
165
166
0
  aggregated_align.padding_top = MAX (align0.padding_top, align1.padding_top);
167
168
0
  aggregated_align.padding_bottom =
169
0
      MAX (align0.padding_bottom, align1.padding_bottom);
170
171
0
  aggregated_align.padding_left =
172
0
      MAX (align0.padding_left, align1.padding_left);
173
174
0
  aggregated_align.padding_right =
175
0
      MAX (align0.padding_right, align1.padding_right);
176
177
0
  for (int n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
178
0
    aggregated_align.stride_align[n] =
179
0
        align0.stride_align[n] | align1.stride_align[n];
180
181
0
  *aggregated_params = gst_structure_new_empty ("video-meta");
182
183
0
  gst_buffer_pool_config_set_video_alignment (*aggregated_params,
184
0
      &aggregated_align);
185
186
0
  return TRUE;
187
0
}
188
189
GType
190
gst_video_meta_api_get_type (void)
191
5
{
192
5
  static GType type = 0;
193
5
  static const gchar *tags[] =
194
5
      { GST_META_TAG_VIDEO_STR, GST_META_TAG_MEMORY_STR,
195
5
    GST_META_TAG_VIDEO_COLORSPACE_STR,
196
5
    GST_META_TAG_VIDEO_SIZE_STR, NULL
197
5
  };
198
199
5
  if (g_once_init_enter (&type)) {
200
5
    GType _type = gst_meta_api_type_register ("GstVideoMetaAPI", tags);
201
202
5
    gst_meta_api_type_set_params_aggregator (_type,
203
5
        gst_video_meta_api_params_aggregator);
204
5
    g_once_init_leave (&type, _type);
205
5
  }
206
5
  return type;
207
5
}
208
209
static gboolean
210
video_meta_serialize (const GstMeta * meta, GstByteArrayInterface * data,
211
    guint8 * version)
212
0
{
213
0
  GstVideoMeta *vmeta = (GstVideoMeta *) meta;
214
215
0
  if (vmeta->map != default_map || vmeta->unmap != default_unmap) {
216
0
    GST_WARNING ("Cannot serialize video meta with custom map/unmap functions");
217
0
    return FALSE;
218
0
  }
219
220
0
  gsize size = 36 + vmeta->n_planes * 16;
221
0
  guint8 *ptr = gst_byte_array_interface_append (data, size);
222
0
  if (ptr == NULL)
223
0
    return FALSE;
224
225
0
  GstByteWriter bw;
226
0
  gboolean success GST_UNUSED_ASSERT = TRUE;
227
0
  gst_byte_writer_init_with_data (&bw, ptr, size, FALSE);
228
0
  success &= gst_byte_writer_put_int32_le (&bw, vmeta->flags);
229
0
  success &= gst_byte_writer_put_int32_le (&bw, vmeta->format);
230
0
  success &= gst_byte_writer_put_uint32_le (&bw, vmeta->width);
231
0
  success &= gst_byte_writer_put_uint32_le (&bw, vmeta->height);
232
0
  success &= gst_byte_writer_put_uint32_le (&bw, vmeta->n_planes);
233
0
  for (int n = 0; n < vmeta->n_planes; n++)
234
0
    success &= gst_byte_writer_put_uint64_le (&bw, vmeta->offset[n]);
235
0
  for (int n = 0; n < vmeta->n_planes; n++)
236
0
    success &= gst_byte_writer_put_int32_le (&bw, vmeta->stride[n]);
237
0
  success &= gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_top);
238
0
  success &=
239
0
      gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_bottom);
240
0
  success &= gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_left);
241
0
  success &=
242
0
      gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_right);
243
0
  for (int n = 0; n < vmeta->n_planes; n++)
244
0
    success &=
245
0
        gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.stride_align[n]);
246
0
  g_assert (success);
247
248
0
  return TRUE;
249
0
}
250
251
static GstMeta *
252
video_meta_deserialize (const GstMetaInfo * info, GstBuffer * buffer,
253
    const guint8 * data, gsize size, guint8 version)
254
0
{
255
0
  GstVideoMeta *vmeta = NULL;
256
0
  gint32 flags;
257
0
  gint32 format;
258
0
  guint width;
259
0
  guint height;
260
0
  guint n_planes;
261
0
  GstVideoAlignment align;
262
0
  guint64 offset64[GST_VIDEO_MAX_PLANES];
263
0
  gint32 stride[GST_VIDEO_MAX_PLANES];
264
265
0
  if (version != 0)
266
0
    return NULL;
267
268
0
  gst_video_alignment_reset (&align);
269
270
0
  GstByteReader br;
271
0
  gboolean success = TRUE;
272
0
  gst_byte_reader_init (&br, data, size);
273
0
  success &= gst_byte_reader_get_int32_le (&br, &flags);
274
0
  success &= gst_byte_reader_get_int32_le (&br, &format);
275
0
  success &= gst_byte_reader_get_uint32_le (&br, &width);
276
0
  success &= gst_byte_reader_get_uint32_le (&br, &height);
277
0
  success &= gst_byte_reader_get_uint32_le (&br, &n_planes);
278
279
0
  if (!success || n_planes > GST_VIDEO_MAX_PLANES)
280
0
    return NULL;
281
282
0
  for (int n = 0; n < n_planes; n++)
283
0
    success &= gst_byte_reader_get_uint64_le (&br, &offset64[n]);
284
0
  for (int n = 0; n < n_planes; n++)
285
0
    success &= gst_byte_reader_get_int32_le (&br, &stride[n]);
286
0
  success &= gst_byte_reader_get_uint32_le (&br, &align.padding_top);
287
0
  success &= gst_byte_reader_get_uint32_le (&br, &align.padding_bottom);
288
0
  success &= gst_byte_reader_get_uint32_le (&br, &align.padding_left);
289
0
  success &= gst_byte_reader_get_uint32_le (&br, &align.padding_right);
290
0
  for (int n = 0; n < n_planes; n++)
291
0
    success &= gst_byte_reader_get_uint32_le (&br, &align.stride_align[n]);
292
293
0
  if (!success)
294
0
    return NULL;
295
296
#if GLIB_SIZEOF_SIZE_T != 8
297
  gsize offset[GST_VIDEO_MAX_PLANES];
298
  for (int i = 0; i < n_planes; i++) {
299
    if (offset64[i] > G_MAXSIZE)
300
      return NULL;
301
    offset[i] = offset64[i];
302
  }
303
#else
304
0
  gsize *offset = (gsize *) offset64;
305
0
#endif
306
307
0
  vmeta =
308
0
      gst_buffer_add_video_meta_full (buffer, flags, format, width, height,
309
0
      n_planes, offset, stride);
310
0
  gst_video_meta_set_alignment (vmeta, align);
311
312
0
  return (GstMeta *) vmeta;
313
0
}
314
315
/* video metadata */
316
const GstMetaInfo *
317
gst_video_meta_get_info (void)
318
5
{
319
5
  static const GstMetaInfo *video_meta_info = NULL;
320
321
5
  if (g_once_init_enter ((GstMetaInfo **) & video_meta_info)) {
322
5
    GstMetaInfo *info = gst_meta_info_new (GST_VIDEO_META_API_TYPE,
323
5
        "GstVideoMeta",
324
5
        sizeof (GstVideoMeta));
325
5
    info->init_func = gst_video_meta_init;
326
5
    info->transform_func = gst_video_meta_transform;
327
5
    info->serialize_func = video_meta_serialize;
328
5
    info->deserialize_func = video_meta_deserialize;
329
5
    const GstMetaInfo *meta = gst_meta_info_register (info);
330
5
    g_once_init_leave ((GstMetaInfo **) & video_meta_info,
331
5
        (GstMetaInfo *) meta);
332
5
  }
333
5
  return video_meta_info;
334
5
}
335
336
/**
337
 * gst_buffer_get_video_meta:
338
 * @buffer: a #GstBuffer
339
 *
340
 * Find the #GstVideoMeta on @buffer with the lowest @id.
341
 *
342
 * Buffers can contain multiple #GstVideoMeta metadata items when dealing with
343
 * multiview buffers.
344
 *
345
 * Returns: (transfer none) (nullable): the #GstVideoMeta with lowest id (usually 0) or %NULL when there
346
 * is no such metadata on @buffer.
347
 */
348
GstVideoMeta *
349
gst_buffer_get_video_meta (GstBuffer * buffer)
350
0
{
351
0
  gpointer state = NULL;
352
0
  GstVideoMeta *out = NULL;
353
0
  GstMeta *meta;
354
0
  const GstMetaInfo *info = GST_VIDEO_META_INFO;
355
356
0
  while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
357
0
    if (meta->info->api == info->api) {
358
0
      GstVideoMeta *vmeta = (GstVideoMeta *) meta;
359
0
      if (vmeta->id == 0)
360
0
        return vmeta;           /* Early out for id 0 */
361
0
      if (out == NULL || vmeta->id < out->id)
362
0
        out = vmeta;
363
0
    }
364
0
  }
365
0
  return out;
366
0
}
367
368
/**
369
 * gst_buffer_get_video_meta_id:
370
 * @buffer: a #GstBuffer
371
 * @id: a metadata id
372
 *
373
 * Find the #GstVideoMeta on @buffer with the given @id.
374
 *
375
 * Buffers can contain multiple #GstVideoMeta metadata items when dealing with
376
 * multiview buffers.
377
 *
378
 * Returns: (transfer none) (nullable): the #GstVideoMeta with @id or %NULL when there is no such metadata
379
 * on @buffer.
380
 */
381
GstVideoMeta *
382
gst_buffer_get_video_meta_id (GstBuffer * buffer, gint id)
383
0
{
384
0
  gpointer state = NULL;
385
0
  GstMeta *meta;
386
0
  const GstMetaInfo *info = GST_VIDEO_META_INFO;
387
388
0
  while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
389
0
    if (meta->info->api == info->api) {
390
0
      GstVideoMeta *vmeta = (GstVideoMeta *) meta;
391
0
      if (vmeta->id == id)
392
0
        return vmeta;
393
0
    }
394
0
  }
395
0
  return NULL;
396
0
}
397
398
static gboolean
399
default_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
400
    gpointer * data, gint * stride, GstMapFlags flags)
401
0
{
402
0
  guint idx, length;
403
0
  gsize offset, skip;
404
0
  GstBuffer *buffer = meta->buffer;
405
406
0
  offset = meta->offset[plane];
407
408
  /* find the memory block for this plane, this is the memory block containing
409
   * the plane offset. FIXME use plane size */
410
0
  if (!gst_buffer_find_memory (buffer, offset, 1, &idx, &length, &skip))
411
0
    goto no_memory;
412
413
0
  if (!gst_buffer_map_range (buffer, idx, length, info, flags))
414
0
    goto cannot_map;
415
416
0
  *stride = meta->stride[plane];
417
0
  *data = (guint8 *) info->data + skip;
418
419
0
  return TRUE;
420
421
  /* ERRORS */
422
0
no_memory:
423
0
  {
424
0
    GST_ERROR ("plane %u, no memory at offset %" G_GSIZE_FORMAT, plane, offset);
425
0
    return FALSE;
426
0
  }
427
0
cannot_map:
428
0
  {
429
0
    GST_ERROR ("cannot map memory range %u-%u", idx, length);
430
0
    return FALSE;
431
0
  }
432
0
}
433
434
static gboolean
435
default_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
436
0
{
437
0
  GstBuffer *buffer = meta->buffer;
438
439
0
  gst_buffer_unmap (buffer, info);
440
441
0
  return TRUE;
442
0
}
443
444
/**
445
 * gst_buffer_add_video_meta:
446
 * @buffer: a #GstBuffer
447
 * @flags: #GstVideoFrameFlags
448
 * @format: a #GstVideoFormat
449
 * @width: the width
450
 * @height: the height
451
 *
452
 * Attaches GstVideoMeta metadata to @buffer with the given parameters and the
453
 * default offsets and strides for @format and @width x @height.
454
 *
455
 * This function calculates the default offsets and strides and then calls
456
 * gst_buffer_add_video_meta_full() with them.
457
 *
458
 * Returns: (transfer none): the #GstVideoMeta on @buffer.
459
 */
460
GstVideoMeta *
461
gst_buffer_add_video_meta (GstBuffer * buffer,
462
    GstVideoFrameFlags flags, GstVideoFormat format, guint width, guint height)
463
0
{
464
0
  GstVideoMeta *meta;
465
0
  GstVideoInfo info;
466
467
0
  if (!gst_video_info_set_format (&info, format, width, height))
468
0
    return NULL;
469
470
0
  meta =
471
0
      gst_buffer_add_video_meta_full (buffer, flags, format, width,
472
0
      height, info.finfo->n_planes, info.offset, info.stride);
473
474
0
  return meta;
475
0
}
476
477
/**
478
 * gst_buffer_add_video_meta_full:
479
 * @buffer: a #GstBuffer
480
 * @flags: #GstVideoFrameFlags
481
 * @format: a #GstVideoFormat
482
 * @width: the width
483
 * @height: the height
484
 * @n_planes: number of planes
485
 * @offset: (array fixed-size=4): offset of each plane
486
 * @stride: (array fixed-size=4): stride of each plane
487
 *
488
 * Attaches GstVideoMeta metadata to @buffer with the given parameters.
489
 *
490
 * Returns: (transfer none): the #GstVideoMeta on @buffer.
491
 */
492
GstVideoMeta *
493
gst_buffer_add_video_meta_full (GstBuffer * buffer,
494
    GstVideoFrameFlags flags, GstVideoFormat format, guint width,
495
    guint height, guint n_planes, const gsize offset[GST_VIDEO_MAX_PLANES],
496
    const gint stride[GST_VIDEO_MAX_PLANES])
497
0
{
498
0
  GstVideoMeta *meta;
499
0
  guint i;
500
501
0
  meta =
502
0
      (GstVideoMeta *) gst_buffer_add_meta (buffer, GST_VIDEO_META_INFO, NULL);
503
504
0
  if (!meta)
505
0
    return NULL;
506
507
0
  meta->flags = flags;
508
0
  meta->format = format;
509
0
  meta->id = 0;
510
0
  meta->width = width;
511
0
  meta->height = height;
512
0
  meta->buffer = buffer;
513
514
0
  meta->n_planes = n_planes;
515
0
  for (i = 0; i < n_planes; i++) {
516
0
    meta->offset[i] = offset[i];
517
0
    meta->stride[i] = stride[i];
518
0
    GST_LOG ("plane %d, offset %" G_GSIZE_FORMAT ", stride %d", i, offset[i],
519
0
        stride[i]);
520
0
  }
521
0
  meta->map = default_map;
522
0
  meta->unmap = default_unmap;
523
524
0
  return meta;
525
0
}
526
527
/**
528
 * gst_video_meta_map:
529
 * @meta: a #GstVideoMeta
530
 * @plane: a plane
531
 * @info: a #GstMapInfo
532
 * @data: (out): the data of @plane
533
 * @stride: (out): the stride of @plane
534
 * @flags: @GstMapFlags
535
 *
536
 * Map the video plane with index @plane in @meta and return a pointer to the
537
 * first byte of the plane and the stride of the plane.
538
 *
539
 * Returns: TRUE if the map operation was successful.
540
 */
541
gboolean
542
gst_video_meta_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
543
    gpointer * data, gint * stride, GstMapFlags flags)
544
0
{
545
0
  g_return_val_if_fail (meta != NULL, FALSE);
546
0
  g_return_val_if_fail (meta->map != NULL, FALSE);
547
0
  g_return_val_if_fail (plane < meta->n_planes, FALSE);
548
0
  g_return_val_if_fail (info != NULL, FALSE);
549
0
  g_return_val_if_fail (data != NULL, FALSE);
550
0
  g_return_val_if_fail (stride != NULL, FALSE);
551
0
  g_return_val_if_fail (meta->buffer != NULL, FALSE);
552
0
  g_return_val_if_fail (!(flags & GST_MAP_WRITE)
553
0
      || gst_buffer_is_writable (meta->buffer), FALSE);
554
555
0
  return meta->map (meta, plane, info, data, stride, flags);
556
0
}
557
558
/**
559
 * gst_video_meta_unmap:
560
 * @meta: a #GstVideoMeta
561
 * @plane: a plane
562
 * @info: a #GstMapInfo
563
 *
564
 * Unmap a previously mapped plane with gst_video_meta_map().
565
 *
566
 * Returns: TRUE if the memory was successfully unmapped.
567
 */
568
gboolean
569
gst_video_meta_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
570
0
{
571
0
  g_return_val_if_fail (meta != NULL, FALSE);
572
0
  g_return_val_if_fail (meta->unmap != NULL, FALSE);
573
0
  g_return_val_if_fail (plane < meta->n_planes, FALSE);
574
0
  g_return_val_if_fail (info != NULL, FALSE);
575
576
0
  return meta->unmap (meta, plane, info);
577
0
}
578
579
static gboolean
580
gst_video_meta_is_alignment_valid (GstVideoAlignment * align)
581
0
{
582
0
  gint i;
583
584
0
  g_return_val_if_fail (align != NULL, FALSE);
585
586
0
  if (align->padding_top != 0 || align->padding_bottom != 0 ||
587
0
      align->padding_left != 0 || align->padding_right != 0)
588
0
    return TRUE;
589
590
0
  for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
591
0
    if (align->stride_align[i] != 0)
592
0
      return TRUE;
593
0
  }
594
595
0
  return FALSE;
596
0
}
597
598
static gboolean
599
gst_video_meta_validate_alignment (GstVideoMeta * meta,
600
    gsize plane_size[GST_VIDEO_MAX_PLANES])
601
0
{
602
0
  GstVideoInfo info;
603
0
  guint i;
604
605
0
  if (!gst_video_meta_is_alignment_valid (&meta->alignment)) {
606
0
    GST_LOG ("Set alignment on meta to all zero");
607
608
    /* When alignment is invalid, no further check is needed,
609
       unless user wants to calculate the pitch for each plane. */
610
0
    if (!plane_size)
611
0
      return TRUE;
612
0
  }
613
614
0
  gst_video_info_init (&info);
615
0
  gst_video_info_set_format (&info, meta->format, meta->width, meta->height);
616
617
0
  if (!gst_video_info_align_full (&info, &meta->alignment, plane_size)) {
618
0
    GST_WARNING ("Failed to align meta with its alignment");
619
0
    return FALSE;
620
0
  }
621
622
0
  for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&info); i++) {
623
0
    if (GST_VIDEO_INFO_PLANE_STRIDE (&info, i) != meta->stride[i]) {
624
0
      GST_WARNING
625
0
          ("Stride of plane %d defined in meta (%d) is different from the one computed from the alignment (%d)",
626
0
          i, meta->stride[i], GST_VIDEO_INFO_PLANE_STRIDE (&info, i));
627
0
      return FALSE;
628
0
    }
629
0
  }
630
631
0
  return TRUE;
632
0
}
633
634
/**
635
 * gst_video_meta_set_alignment:
636
 * @meta: a #GstVideoMeta
637
 * @alignment: a #GstVideoAlignment
638
 *
639
 * Set the alignment of @meta to @alignment. This function checks that
640
 * the paddings defined in @alignment are compatible with the strides
641
 * defined in @meta and will fail to update if they are not.
642
 *
643
 * Returns: %TRUE if @alignment's meta has been updated, %FALSE if not
644
 *
645
 * Since: 1.18
646
 * Deprecated: 1.28: Use gst_video_meta_set_alignment_full() instead
647
 */
648
gboolean
649
gst_video_meta_set_alignment (GstVideoMeta * meta, GstVideoAlignment alignment)
650
0
{
651
0
  return gst_video_meta_set_alignment_full (meta, &alignment);
652
0
}
653
654
/**
655
 * gst_video_meta_set_alignment_full:
656
 * @meta: a #GstVideoMeta
657
 * @alignment: a #GstVideoAlignment
658
 *
659
 * Set the alignment of @meta to @alignment. This function checks that
660
 * the paddings defined in @alignment are compatible with the strides
661
 * defined in @meta and will fail to update if they are not.
662
 *
663
 * Returns: %TRUE if @alignment's meta has been updated, %FALSE if not
664
 *
665
 * Since: 1.28
666
 */
667
gboolean
668
gst_video_meta_set_alignment_full (GstVideoMeta * meta,
669
    const GstVideoAlignment * alignment)
670
0
{
671
0
  GstVideoAlignment old;
672
673
0
  g_return_val_if_fail (meta, FALSE);
674
675
0
  old = meta->alignment;
676
0
  meta->alignment = *alignment;
677
678
0
  if (!gst_video_meta_validate_alignment (meta, NULL)) {
679
    /* Invalid alignment, restore the previous one */
680
0
    meta->alignment = old;
681
0
    return FALSE;
682
0
  }
683
684
0
  GST_LOG ("Set alignment on meta: padding %u-%ux%u-%u", alignment->padding_top,
685
0
      alignment->padding_left, alignment->padding_right,
686
0
      alignment->padding_bottom);
687
688
0
  return TRUE;
689
0
}
690
691
/**
692
 * gst_video_meta_get_plane_size:
693
 * @meta: a #GstVideoMeta
694
 * @plane_size: (out caller-allocates) (array fixed-size=4): array used to store the plane sizes
695
 *
696
 * Compute the size, in bytes, of each video plane described in @meta including
697
 * any padding and alignment constraint defined in @meta->alignment.
698
 *
699
 * Returns: %TRUE if @meta's alignment is valid and @plane_size has been
700
 * updated, %FALSE otherwise
701
 *
702
 * Since: 1.18
703
 */
704
gboolean
705
gst_video_meta_get_plane_size (GstVideoMeta * meta,
706
    gsize plane_size[GST_VIDEO_MAX_PLANES])
707
0
{
708
0
  g_return_val_if_fail (meta, FALSE);
709
0
  g_return_val_if_fail (plane_size, FALSE);
710
711
0
  return gst_video_meta_validate_alignment (meta, plane_size);
712
0
}
713
714
/**
715
 * gst_video_meta_get_plane_height:
716
 * @meta: a #GstVideoMeta
717
 * @plane_height: (out caller-allocates) (array fixed-size=4): array used to store the plane height
718
 *
719
 * Compute the padded height of each plane from @meta (padded size
720
 * divided by stride).
721
 *
722
 * It is not valid to call this function with a meta associated to a
723
 * TILED video format.
724
 *
725
 * Returns: %TRUE if @meta's alignment is valid and @plane_height has been
726
 * updated, %FALSE otherwise
727
 *
728
 * Since: 1.18
729
 */
730
gboolean
731
gst_video_meta_get_plane_height (GstVideoMeta * meta,
732
    guint plane_height[GST_VIDEO_MAX_PLANES])
733
0
{
734
0
  gsize plane_size[GST_VIDEO_MAX_PLANES];
735
0
  guint i;
736
0
  GstVideoInfo info;
737
738
0
  g_return_val_if_fail (meta, FALSE);
739
0
  g_return_val_if_fail (plane_height, FALSE);
740
741
0
  gst_video_info_init (&info);
742
0
  gst_video_info_set_format (&info, meta->format, meta->width, meta->height);
743
0
  g_return_val_if_fail (!GST_VIDEO_FORMAT_INFO_IS_TILED (&info), FALSE);
744
745
0
  if (!gst_video_meta_get_plane_size (meta, plane_size))
746
0
    return FALSE;
747
748
0
  for (i = 0; i < meta->n_planes; i++) {
749
0
    if (!meta->stride[i])
750
0
      plane_height[i] = 0;
751
0
    else
752
0
      plane_height[i] = plane_size[i] / meta->stride[i];
753
0
  }
754
755
0
  for (; i < GST_VIDEO_MAX_PLANES; i++)
756
0
    plane_height[i] = 0;
757
758
0
  return TRUE;
759
0
}
760
761
static gboolean
762
gst_video_crop_meta_transform (GstBuffer * dest, GstMeta * meta,
763
    GstBuffer * buffer, GQuark type, gpointer data)
764
0
{
765
0
  GstVideoCropMeta *dmeta, *smeta;
766
767
0
  if (GST_META_TRANSFORM_IS_COPY (type)) {
768
0
    smeta = (GstVideoCropMeta *) meta;
769
0
    dmeta = gst_buffer_add_video_crop_meta (dest);
770
0
    if (!dmeta)
771
0
      return FALSE;
772
773
0
    GST_DEBUG ("copy crop metadata");
774
0
    dmeta->x = smeta->x;
775
0
    dmeta->y = smeta->y;
776
0
    dmeta->width = smeta->width;
777
0
    dmeta->height = smeta->height;
778
0
  } else if (GST_VIDEO_META_TRANSFORM_IS_SCALE (type)) {
779
0
    GstVideoMetaTransform *trans = data;
780
0
    gint ow, oh, nw, nh;
781
782
0
    smeta = (GstVideoCropMeta *) meta;
783
0
    dmeta = gst_buffer_add_video_crop_meta (dest);
784
0
    if (!dmeta)
785
0
      return FALSE;
786
787
0
    ow = GST_VIDEO_INFO_WIDTH (trans->in_info);
788
0
    nw = GST_VIDEO_INFO_WIDTH (trans->out_info);
789
0
    oh = GST_VIDEO_INFO_HEIGHT (trans->in_info);
790
0
    nh = GST_VIDEO_INFO_HEIGHT (trans->out_info);
791
792
0
    GST_DEBUG ("scaling crop metadata %dx%d -> %dx%d", ow, oh, nw, nh);
793
0
    dmeta->x = (smeta->x * nw) / ow;
794
0
    dmeta->y = (smeta->y * nh) / oh;
795
0
    dmeta->width = (smeta->width * nw) / ow;
796
0
    dmeta->height = (smeta->height * nh) / oh;
797
0
    GST_DEBUG ("crop offset %dx%d -> %dx%d", smeta->x, smeta->y, dmeta->x,
798
0
        dmeta->y);
799
0
    GST_DEBUG ("crop size   %dx%d -> %dx%d", smeta->width, smeta->height,
800
0
        dmeta->width, dmeta->height);
801
0
  } else {
802
    /* return FALSE, if transform type is not supported */
803
0
    return FALSE;
804
0
  }
805
0
  return TRUE;
806
0
}
807
808
GType
809
gst_video_crop_meta_api_get_type (void)
810
0
{
811
0
  static GType type = 0;
812
0
  static const gchar *tags[] =
813
0
      { GST_META_TAG_VIDEO_STR, GST_META_TAG_VIDEO_SIZE_STR,
814
0
    GST_META_TAG_VIDEO_ORIENTATION_STR, NULL
815
0
  };
816
817
0
  if (g_once_init_enter (&type)) {
818
0
    GType _type = gst_meta_api_type_register ("GstVideoCropMetaAPI", tags);
819
0
    g_once_init_leave (&type, _type);
820
0
  }
821
0
  return type;
822
0
}
823
824
static gboolean
825
gst_video_crop_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
826
0
{
827
0
  GstVideoCropMeta *emeta = (GstVideoCropMeta *) meta;
828
0
  emeta->x = emeta->y = emeta->width = emeta->height = 0;
829
830
0
  return TRUE;
831
0
}
832
833
const GstMetaInfo *
834
gst_video_crop_meta_get_info (void)
835
0
{
836
0
  static const GstMetaInfo *video_crop_meta_info = NULL;
837
838
0
  if (g_once_init_enter ((GstMetaInfo **) & video_crop_meta_info)) {
839
0
    const GstMetaInfo *meta =
840
0
        gst_meta_register (GST_VIDEO_CROP_META_API_TYPE, "GstVideoCropMeta",
841
0
        sizeof (GstVideoCropMeta),
842
0
        (GstMetaInitFunction) gst_video_crop_meta_init,
843
0
        (GstMetaFreeFunction) NULL, gst_video_crop_meta_transform);
844
0
    g_once_init_leave ((GstMetaInfo **) & video_crop_meta_info,
845
0
        (GstMetaInfo *) meta);
846
0
  }
847
0
  return video_crop_meta_info;
848
0
}
849
850
/**
851
 * gst_video_meta_transform_scale_get_quark:
852
 *
853
 * Get the #GQuark for the "gst-video-scale" metadata transform operation.
854
 *
855
 * Returns: a #GQuark
856
 */
857
GQuark
858
gst_video_meta_transform_scale_get_quark (void)
859
5
{
860
5
  static GQuark _value = 0;
861
862
5
  if (_value == 0) {
863
5
    _value = g_quark_from_static_string ("gst-video-scale");
864
5
  }
865
5
  return _value;
866
5
}
867
868
/**
869
 * gst_video_meta_transform_matrix_get_quark:
870
 *
871
 * Get the #GQuark for the "gst-video-matrix" metadata transform operation.
872
 *
873
 * Returns: a #GQuark
874
 *
875
 * Since: 1.28
876
 */
877
GQuark
878
gst_video_meta_transform_matrix_get_quark (void)
879
5
{
880
5
  static GQuark _value = 0;
881
882
5
  if (_value == 0) {
883
5
    _value = g_quark_from_static_string ("gst-video-matrix");
884
5
  }
885
5
  return _value;
886
5
}
887
888
/**
889
 * gst_video_meta_transform_matrix_init:
890
 * @trans: a #GstVideoMetaTransformMatrix with in_rectangle and out_rectangle
891
 *  filled
892
 * @in_info: (in): The #GstVideoInfo of the input image
893
 * @in_rectangle: the input #GstVideoRectangle
894
 * @out_info: (in): the output #GstVideoInfo
895
 * @out_rectangle: the output #GstVideoRectangle
896
 *
897
 * Based on the rectangles, initializes the matrix to do a translation and
898
 * scaling from @in_rectangle to @out_rectangle
899
 *
900
 * Since: 1.28
901
 */
902
void
903
gst_video_meta_transform_matrix_init (GstVideoMetaTransformMatrix * trans,
904
    const GstVideoInfo * in_info, const GstVideoRectangle * in_rectangle,
905
    const GstVideoInfo * out_info, const GstVideoRectangle * out_rectangle)
906
0
{
907
0
  g_return_if_fail (in_info != NULL);
908
0
  g_return_if_fail (out_info != NULL);
909
0
  g_return_if_fail (in_rectangle->w > 0);
910
0
  g_return_if_fail (in_rectangle->h > 0);
911
0
  g_return_if_fail (out_rectangle->w > 0);
912
0
  g_return_if_fail (out_rectangle->h > 0);
913
914
0
  trans->in_info = in_info;
915
0
  trans->out_info = out_info;
916
0
  trans->in_rectangle = *in_rectangle;
917
0
  trans->out_rectangle = *out_rectangle;
918
919
0
  memset (trans->matrix, 0, sizeof (gfloat) * 9);
920
921
0
  trans->matrix[0][0] = (gfloat) trans->out_rectangle.w /
922
0
      (gfloat) trans->in_rectangle.w;
923
0
  trans->matrix[1][1] = (gfloat) trans->out_rectangle.h /
924
0
      (gfloat) trans->in_rectangle.h;
925
0
  trans->matrix[2][2] = 1;
926
0
}
927
928
static gboolean
929
_gst_video_meta_transform_matrix_point (const GstVideoMetaTransformMatrix *
930
    transform, gint * x, gint * y, gboolean clip)
931
0
{
932
0
  gboolean ret = TRUE;
933
0
  gdouble x_in = *x - transform->in_rectangle.x;
934
0
  gdouble y_in = *y - transform->in_rectangle.y;
935
0
  gdouble x_temp, y_temp;
936
0
  gdouble w_prime = transform->matrix[2][0] * x_in +
937
0
      transform->matrix[2][1] * y_in + transform->matrix[2][2];
938
939
0
  if (w_prime == 0.0f) {
940
0
    *x = transform->out_rectangle.x;
941
0
    *y = transform->out_rectangle.y;
942
0
    g_return_val_if_fail (w_prime != 0.0, FALSE);
943
0
  }
944
945
0
  if (w_prime == 1.0f) {
946
0
    x_temp = transform->matrix[0][0] * x_in +
947
0
        transform->matrix[0][1] * y_in + transform->matrix[0][2];
948
0
    y_temp = transform->matrix[1][0] * x_in +
949
0
        transform->matrix[1][1] * y_in + transform->matrix[1][2];
950
0
  } else {
951
0
    x_temp = (transform->matrix[0][0] * x_in +
952
0
        transform->matrix[0][1] * y_in + transform->matrix[0][2]) / w_prime;
953
0
    y_temp = (transform->matrix[1][0] * x_in +
954
0
        transform->matrix[1][1] * y_in + transform->matrix[1][2]) / w_prime;
955
0
  }
956
957
0
  *x = x_temp + 0.5f + transform->out_rectangle.x;
958
0
  *y = y_temp + 0.5f + transform->out_rectangle.y;
959
960
0
  if (clip) {
961
0
    if (*x < transform->out_rectangle.x) {
962
0
      *x = transform->out_rectangle.x;
963
0
      ret = FALSE;
964
0
    }
965
966
0
    if (*x >= transform->out_rectangle.x + transform->out_rectangle.w) {
967
0
      *x = transform->out_rectangle.x + transform->out_rectangle.w - 1;
968
0
      ret = FALSE;
969
0
    }
970
971
0
    if (*y < transform->out_rectangle.y) {
972
0
      *y = transform->out_rectangle.y;
973
0
      ret = FALSE;
974
0
    }
975
976
0
    if (*y >= transform->out_rectangle.y + transform->out_rectangle.h) {
977
0
      *y = transform->out_rectangle.y + transform->out_rectangle.h - 1;
978
0
      ret = FALSE;
979
0
    }
980
0
  }
981
982
0
  return ret;
983
0
}
984
985
/**
986
 * gst_video_meta_transform_matrix_point:
987
 * @transform: a #GstVideoMetaTransformMatrix
988
 * @x: (inout): a non-NULL pointer to the X value of the coordinate
989
 * @y: (inout): a non-NULL pointer to the Y value of the coordinate
990
 *
991
 * Transforms the (@x, @y) point from the input coordinates to the
992
 * output ones.  The point's coordinates are transformed by first
993
 * applying the @transform.matrix to it using the top left (x, y) of
994
 * @transform.in_rectangle as the origin, then translate it to use
995
 * the top-left of @transform.out_rectangle as new origin.
996
 *
997
 * Returns: %FALSE if the point is outside of @transform.out_rectangle
998
 * after the transformation has been applied
999
 *
1000
 * Since: 1.28
1001
 */
1002
gboolean
1003
gst_video_meta_transform_matrix_point (const GstVideoMetaTransformMatrix *
1004
    transform, gint * x, gint * y)
1005
0
{
1006
0
  return _gst_video_meta_transform_matrix_point (transform, x, y, FALSE);
1007
0
}
1008
1009
/**
1010
 * gst_video_meta_transform_matrix_point_clipped:
1011
 * @transform: a #GstVideoMetaTransformMatrix
1012
 * @x: (inout): a non-NULL pointer to the X value of the coordinate
1013
 * @y: (inout): a non-NULL pointer to the Y value of the coordinate
1014
 *
1015
 * Transforms the (@x, @y) point from the input coordinates to the
1016
 * output ones.  The point's coordinates are transformed by first
1017
 * applying the @transform.matrix to it using the top left (x, y) of
1018
 * @transform.in_rectangle as the origin, then translate it to use
1019
 * the top-left of @transform.out_rectangle as new origin.
1020
 *
1021
 * Returns: %FALSE if the point is outside of @transform.out_rectangle
1022
 * after the transformation has been applied
1023
 *
1024
 * Since: 1.28
1025
 */
1026
gboolean
1027
gst_video_meta_transform_matrix_point_clipped (const GstVideoMetaTransformMatrix
1028
    * transform, gint * x, gint * y)
1029
0
{
1030
0
  return _gst_video_meta_transform_matrix_point (transform, x, y, TRUE);
1031
0
}
1032
1033
static gboolean
1034
_gst_video_meta_transform_matrix_rectangle (const
1035
    GstVideoMetaTransformMatrix * transform, GstVideoRectangle * rect,
1036
    gboolean clip)
1037
0
{
1038
0
  gboolean ret = TRUE;
1039
0
  gint x1, y1;
1040
0
  gint x2, y2;
1041
1042
  /* If the transformation is not affine, can't do it on a rectangle */
1043
0
  if (transform->matrix[2][0] != 0 ||
1044
0
      transform->matrix[2][1] != 0 || transform->matrix[2][2] != 1)
1045
0
    return FALSE;
1046
1047
  /* If there is shearing, it won't preserve the rectangle either */
1048
0
  if ((transform->matrix[0][0] != 0 || transform->matrix[1][1] != 0) &&
1049
0
      (transform->matrix[0][1] != 0 || transform->matrix[1][0] != 0))
1050
0
    return FALSE;
1051
1052
0
  x1 = rect->x;
1053
0
  y1 = rect->y;
1054
0
  x2 = rect->x + rect->w;
1055
0
  y2 = rect->y + rect->h;
1056
1057
0
  ret = _gst_video_meta_transform_matrix_point (transform, &x1, &y1, clip) &&
1058
0
      _gst_video_meta_transform_matrix_point (transform, &x2, &y2, clip);
1059
1060
0
  rect->x = MIN (x1, x2);
1061
0
  rect->y = MIN (y1, y2);
1062
1063
0
  rect->w = MAX (x1, x2) - rect->x;
1064
0
  rect->h = MAX (y1, y2) - rect->y;
1065
1066
0
  return ret;
1067
0
}
1068
1069
/**
1070
 * gst_video_meta_transform_matrix_rectangle:
1071
 * @transform: A #GstVideoMetaTransformMatrix
1072
 * @rect: (inout): a rectangle in the coordinate of the original image
1073
 *
1074
 * Transforms @rect from the input coordinates to the
1075
 * output ones.  The point's coordinates are transformed by first
1076
 * applying the @transform.matrix to it using the top left (x, y) of
1077
 * @transform.in_rectangle as the origin, then translate it to use
1078
 * the top-left of @transform.out_rectangle as new origin.
1079
 *
1080
 * @rect is always axis aligned at input and this function only returns
1081
 * axis aligned rectangles as output, otherwise it returns FALSE.
1082
 *
1083
 * Output rectangle could be in partially or totally outside of
1084
 * @transform.out_rectangle.
1085
 *
1086
 * Returns: %FALSE is the output rectangle is not axis aligned
1087
 *
1088
 * Since: 1.28
1089
 */
1090
gboolean
1091
gst_video_meta_transform_matrix_rectangle (const
1092
    GstVideoMetaTransformMatrix * transform, GstVideoRectangle * rect)
1093
0
{
1094
0
  return _gst_video_meta_transform_matrix_rectangle (transform, rect, FALSE);
1095
0
}
1096
1097
/**
1098
 * gst_video_meta_transform_matrix_rectangle_clipped:
1099
 * @transform: A #GstVideoMetaTransformMatrix
1100
 * @rect: (inout): a rectangle in the coordinate of the original image
1101
 *
1102
 * Transforms @rect from the input coordinates to the
1103
 * output ones.  The point's coordinates are transformed by first
1104
 * applying the @transform.matrix to it using the top left (x, y) of
1105
 * @transform.in_rectangle as the origin, then translate it to use
1106
 * the top-left of @transform.out_rectangle as new origin.
1107
 *
1108
 * @rect is always axis aligned at input and this function only returns
1109
 * axis aligned rectangles as output, otherwise it returns FALSE.
1110
 *
1111
 * Output rectangle will be clipped to fit inside @transform.out_rectangle.
1112
 *
1113
 * Returns: %FALSE if the output rectangle is not axis aligned or if
1114
 *  the rectangle is entirely outside of the out_rectangle.
1115
 *
1116
 * Since: 1.28
1117
 */
1118
gboolean
1119
gst_video_meta_transform_matrix_rectangle_clipped (const
1120
    GstVideoMetaTransformMatrix * transform, GstVideoRectangle * rect)
1121
0
{
1122
0
  return _gst_video_meta_transform_matrix_rectangle (transform, rect, TRUE);
1123
0
}
1124
1125
1126
GType
1127
gst_video_gl_texture_upload_meta_api_get_type (void)
1128
0
{
1129
0
  static GType type = 0;
1130
0
  static const gchar *tags[] =
1131
0
      { GST_META_TAG_VIDEO_STR, GST_META_TAG_MEMORY_STR, NULL };
1132
1133
0
  if (g_once_init_enter (&type)) {
1134
0
    GType _type =
1135
0
        gst_meta_api_type_register ("GstVideoGLTextureUploadMetaAPI", tags);
1136
0
    g_once_init_leave (&type, _type);
1137
0
  }
1138
0
  return type;
1139
0
}
1140
1141
static gboolean
1142
gst_video_gl_texture_upload_meta_init (GstMeta * meta, gpointer params,
1143
    GstBuffer * buffer)
1144
0
{
1145
0
  GstVideoGLTextureUploadMeta *vmeta = (GstVideoGLTextureUploadMeta *) meta;
1146
1147
0
  vmeta->texture_orientation =
1148
0
      GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL;
1149
0
  vmeta->n_textures = 0;
1150
0
  memset (vmeta->texture_type, 0, sizeof (vmeta->texture_type));
1151
0
  vmeta->buffer = NULL;
1152
0
  vmeta->upload = NULL;
1153
0
  vmeta->user_data = NULL;
1154
0
  vmeta->user_data_copy = NULL;
1155
0
  vmeta->user_data_free = NULL;
1156
1157
0
  return TRUE;
1158
0
}
1159
1160
static void
1161
gst_video_gl_texture_upload_meta_free (GstMeta * meta, GstBuffer * buffer)
1162
0
{
1163
0
  GstVideoGLTextureUploadMeta *vmeta = (GstVideoGLTextureUploadMeta *) meta;
1164
1165
0
  if (vmeta->user_data_free)
1166
0
    vmeta->user_data_free (vmeta->user_data);
1167
0
}
1168
1169
static gboolean
1170
gst_video_gl_texture_upload_meta_transform (GstBuffer * dest, GstMeta * meta,
1171
    GstBuffer * buffer, GQuark type, gpointer data)
1172
0
{
1173
0
  GstVideoGLTextureUploadMeta *dmeta, *smeta;
1174
1175
0
  smeta = (GstVideoGLTextureUploadMeta *) meta;
1176
1177
0
  if (GST_META_TRANSFORM_IS_COPY (type)) {
1178
0
    GstMetaTransformCopy *copy = data;
1179
1180
0
    if (!copy->region) {
1181
      /* only copy if the complete data is copied as well */
1182
0
      dmeta =
1183
0
          (GstVideoGLTextureUploadMeta *) gst_buffer_add_meta (dest,
1184
0
          GST_VIDEO_GL_TEXTURE_UPLOAD_META_INFO, NULL);
1185
1186
0
      if (!dmeta)
1187
0
        return FALSE;
1188
1189
0
      dmeta->texture_orientation = smeta->texture_orientation;
1190
0
      dmeta->n_textures = smeta->n_textures;
1191
0
      memcpy (dmeta->texture_type, smeta->texture_type,
1192
0
          sizeof (smeta->texture_type[0]) * 4);
1193
0
      dmeta->buffer = dest;
1194
0
      dmeta->upload = smeta->upload;
1195
0
      dmeta->user_data = smeta->user_data;
1196
0
      dmeta->user_data_copy = smeta->user_data_copy;
1197
0
      dmeta->user_data_free = smeta->user_data_free;
1198
0
      if (dmeta->user_data_copy)
1199
0
        dmeta->user_data = dmeta->user_data_copy (dmeta->user_data);
1200
0
    }
1201
0
  } else {
1202
    /* return FALSE, if transform type is not supported */
1203
0
    return FALSE;
1204
0
  }
1205
0
  return TRUE;
1206
0
}
1207
1208
const GstMetaInfo *
1209
gst_video_gl_texture_upload_meta_get_info (void)
1210
0
{
1211
0
  static const GstMetaInfo *info = NULL;
1212
1213
0
  if (g_once_init_enter ((GstMetaInfo **) & info)) {
1214
0
    const GstMetaInfo *meta =
1215
0
        gst_meta_register (GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE,
1216
0
        "GstVideoGLTextureUploadMeta",
1217
0
        sizeof (GstVideoGLTextureUploadMeta),
1218
0
        gst_video_gl_texture_upload_meta_init,
1219
0
        gst_video_gl_texture_upload_meta_free,
1220
0
        gst_video_gl_texture_upload_meta_transform);
1221
0
    g_once_init_leave ((GstMetaInfo **) & info, (GstMetaInfo *) meta);
1222
0
  }
1223
0
  return info;
1224
0
}
1225
1226
/**
1227
 * gst_buffer_add_video_gl_texture_upload_meta:
1228
 * @buffer: a #GstBuffer
1229
 * @texture_orientation: the #GstVideoGLTextureOrientation
1230
 * @n_textures: the number of textures
1231
 * @texture_type: array of #GstVideoGLTextureType
1232
 * @upload: (scope call): the function to upload the buffer to a specific texture ID
1233
 * @user_data: user data for the implementor of @upload
1234
 * @user_data_copy: (scope call): function to copy @user_data
1235
 * @user_data_free: (scope call): function to free @user_data
1236
 *
1237
 * Attaches GstVideoGLTextureUploadMeta metadata to @buffer with the given
1238
 * parameters.
1239
 *
1240
 * Returns: (transfer none): the #GstVideoGLTextureUploadMeta on @buffer.
1241
 */
1242
GstVideoGLTextureUploadMeta *
1243
gst_buffer_add_video_gl_texture_upload_meta (GstBuffer * buffer,
1244
    GstVideoGLTextureOrientation texture_orientation, guint n_textures,
1245
    GstVideoGLTextureType texture_type[4], GstVideoGLTextureUpload upload,
1246
    gpointer user_data, GBoxedCopyFunc user_data_copy,
1247
    GBoxedFreeFunc user_data_free)
1248
0
{
1249
0
  GstVideoGLTextureUploadMeta *meta;
1250
1251
0
  g_return_val_if_fail (buffer != NULL, NULL);
1252
0
  g_return_val_if_fail (upload != NULL, NULL);
1253
0
  g_return_val_if_fail (n_textures > 0 && n_textures < 5, NULL);
1254
1255
0
  meta =
1256
0
      (GstVideoGLTextureUploadMeta *) gst_buffer_add_meta (buffer,
1257
0
      GST_VIDEO_GL_TEXTURE_UPLOAD_META_INFO, NULL);
1258
1259
0
  if (!meta)
1260
0
    return NULL;
1261
1262
0
  meta->texture_orientation = texture_orientation;
1263
0
  meta->n_textures = n_textures;
1264
0
  memcpy (meta->texture_type, texture_type, sizeof (texture_type[0]) * 4);
1265
0
  meta->buffer = buffer;
1266
0
  meta->upload = upload;
1267
0
  meta->user_data = user_data;
1268
0
  meta->user_data_copy = user_data_copy;
1269
0
  meta->user_data_free = user_data_free;
1270
1271
0
  return meta;
1272
0
}
1273
1274
/**
1275
 * gst_video_gl_texture_upload_meta_upload:
1276
 * @meta: a #GstVideoGLTextureUploadMeta
1277
 * @texture_id: the texture IDs to upload to
1278
 *
1279
 * Uploads the buffer which owns the meta to a specific texture ID.
1280
 *
1281
 * Returns: %TRUE if uploading succeeded, %FALSE otherwise.
1282
 */
1283
gboolean
1284
gst_video_gl_texture_upload_meta_upload (GstVideoGLTextureUploadMeta * meta,
1285
    guint texture_id[4])
1286
0
{
1287
0
  g_return_val_if_fail (meta != NULL, FALSE);
1288
1289
0
  return meta->upload (meta, texture_id);
1290
0
}
1291
1292
/* Region of Interest Meta implementation *******************************************/
1293
1294
GType
1295
gst_video_region_of_interest_meta_api_get_type (void)
1296
0
{
1297
0
  static GType type;
1298
0
  static const gchar *tags[] =
1299
0
      { GST_META_TAG_VIDEO_STR, GST_META_TAG_VIDEO_ORIENTATION_STR,
1300
0
    GST_META_TAG_VIDEO_SIZE_STR, NULL
1301
0
  };
1302
1303
0
  if (g_once_init_enter (&type)) {
1304
0
    GType _type =
1305
0
        gst_meta_api_type_register ("GstVideoRegionOfInterestMetaAPI", tags);
1306
0
    GST_INFO ("registering");
1307
0
    g_once_init_leave (&type, _type);
1308
0
  }
1309
0
  return type;
1310
0
}
1311
1312
1313
static gboolean
1314
gst_video_region_of_interest_meta_transform (GstBuffer * dest, GstMeta * meta,
1315
    GstBuffer * buffer, GQuark type, gpointer data)
1316
0
{
1317
0
  GstVideoRegionOfInterestMeta *smeta = (GstVideoRegionOfInterestMeta *) meta;
1318
0
  GstVideoRegionOfInterestMeta *dmeta;
1319
1320
0
  if (GST_META_TRANSFORM_IS_COPY (type)) {
1321
0
    GST_DEBUG ("copy region of interest metadata (seqnum: %" G_GUINT64_FORMAT
1322
0
        ")", gst_meta_get_seqnum (meta));
1323
0
    dmeta =
1324
0
        gst_buffer_add_video_region_of_interest_meta_id (dest,
1325
0
        smeta->roi_type, smeta->x, smeta->y, smeta->w, smeta->h);
1326
0
    if (!dmeta)
1327
0
      return FALSE;
1328
1329
0
    dmeta->id = smeta->id;
1330
0
    dmeta->parent_id = smeta->parent_id;
1331
0
    dmeta->params = g_list_copy_deep (smeta->params,
1332
0
        (GCopyFunc) gst_structure_copy, NULL);
1333
0
  } else if (GST_VIDEO_META_TRANSFORM_IS_MATRIX (type)) {
1334
0
    GstVideoMetaTransformMatrix *trans = data;
1335
0
    GstVideoRectangle rect = { smeta->x, smeta->y, smeta->w, smeta->h };
1336
1337
0
    GST_LOG ("Scaling and cropping region of interest metadata %dx%d+%d+%d"
1338
0
        " in (%dx%d) -> %dx%d+%d+%d in %dx%d (seqnum: %" G_GUINT64_FORMAT ")",
1339
0
        trans->in_rectangle.w,
1340
0
        trans->in_rectangle.h, trans->in_rectangle.x, trans->in_rectangle.y,
1341
0
        GST_VIDEO_INFO_WIDTH (trans->in_info),
1342
0
        GST_VIDEO_INFO_HEIGHT (trans->in_info), trans->out_rectangle.w,
1343
0
        trans->out_rectangle.h, trans->out_rectangle.x,
1344
0
        trans->out_rectangle.y, GST_VIDEO_INFO_WIDTH (trans->out_info),
1345
0
        GST_VIDEO_INFO_HEIGHT (trans->out_info), gst_meta_get_seqnum (meta));
1346
1347
0
    if (!gst_video_meta_transform_matrix_rectangle_clipped (trans, &rect))
1348
0
      return FALSE;
1349
1350
0
    dmeta = gst_buffer_add_video_region_of_interest_meta_id (dest,
1351
0
        smeta->roi_type, rect.x, rect.y, rect.w, rect.h);
1352
0
    if (!dmeta)
1353
0
      return FALSE;
1354
1355
0
    dmeta->id = smeta->id;
1356
0
    dmeta->parent_id = smeta->parent_id;
1357
1358
0
    GST_LOG ("region of interest (id:%d, parent id:%d) offset %dx%d -> %dx%d "
1359
0
        "(seqnum: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT ")",
1360
0
        smeta->id, smeta->parent_id, smeta->x, smeta->y, dmeta->x, dmeta->y,
1361
0
        gst_meta_get_seqnum (meta),
1362
0
        gst_meta_get_seqnum (GST_META_CAST (dmeta)));
1363
0
    GST_LOG ("region of interest size   %dx%d -> %dx%d (seqnum: %"
1364
0
        G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT " ))", smeta->w, smeta->h,
1365
0
        dmeta->w, dmeta->h, gst_meta_get_seqnum (meta),
1366
0
        gst_meta_get_seqnum (GST_META_CAST (dmeta)));
1367
0
  } else if (GST_VIDEO_META_TRANSFORM_IS_SCALE (type)) {
1368
0
    GstVideoMetaTransform *trans = data;
1369
0
    gint ow, oh, nw, nh;
1370
0
    ow = GST_VIDEO_INFO_WIDTH (trans->in_info);
1371
0
    nw = GST_VIDEO_INFO_WIDTH (trans->out_info);
1372
0
    oh = GST_VIDEO_INFO_HEIGHT (trans->in_info);
1373
0
    nh = GST_VIDEO_INFO_HEIGHT (trans->out_info);
1374
1375
0
    GST_LOG ("scaling region of interest metadata %dx%d -> %dx%d "
1376
0
        "(seqnum: %" G_GUINT64_FORMAT ")", ow, oh, nw, nh,
1377
0
        gst_meta_get_seqnum (meta));
1378
1379
0
    dmeta =
1380
0
        gst_buffer_add_video_region_of_interest_meta_id (dest,
1381
0
        smeta->roi_type, (smeta->x * nw) / ow, (smeta->y * nh) / oh,
1382
0
        (smeta->w * nw) / ow, (smeta->h * nh) / oh);
1383
0
    if (!dmeta)
1384
0
      return FALSE;
1385
1386
0
    dmeta->id = smeta->id;
1387
0
    dmeta->parent_id = smeta->parent_id;
1388
1389
0
    GST_LOG ("region of interest (id:%d, parent id:%d) offset %dx%d -> %dx%d"
1390
0
        " (seqnum: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT ")",
1391
0
        smeta->id, smeta->parent_id, smeta->x, smeta->y, dmeta->x, dmeta->y,
1392
0
        gst_meta_get_seqnum (meta),
1393
0
        gst_meta_get_seqnum (GST_META_CAST (dmeta)));
1394
0
    GST_LOG ("region of interest size   %dx%d -> %dx%d (seqnum: %"
1395
0
        G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT ")", smeta->w, smeta->h,
1396
0
        dmeta->w, dmeta->h, gst_meta_get_seqnum (meta),
1397
0
        gst_meta_get_seqnum (GST_META_CAST (dmeta)));
1398
0
  } else {
1399
    /* return FALSE, if transform type is not supported */
1400
0
    return FALSE;
1401
0
  }
1402
0
  return TRUE;
1403
0
}
1404
1405
static gboolean
1406
gst_video_region_of_interest_meta_init (GstMeta * meta, gpointer params,
1407
    GstBuffer * buffer)
1408
0
{
1409
0
  GstVideoRegionOfInterestMeta *emeta = (GstVideoRegionOfInterestMeta *) meta;
1410
0
  emeta->roi_type = 0;
1411
0
  emeta->id = 0;
1412
0
  emeta->parent_id = 0;
1413
0
  emeta->x = emeta->y = emeta->w = emeta->h = 0;
1414
0
  emeta->params = NULL;
1415
1416
0
  return TRUE;
1417
0
}
1418
1419
static void
1420
gst_video_region_of_interest_meta_free (GstMeta * meta, GstBuffer * buffer)
1421
0
{
1422
0
  GstVideoRegionOfInterestMeta *emeta = (GstVideoRegionOfInterestMeta *) meta;
1423
1424
0
  g_list_free_full (emeta->params, (GDestroyNotify) gst_structure_free);
1425
0
}
1426
1427
const GstMetaInfo *
1428
gst_video_region_of_interest_meta_get_info (void)
1429
0
{
1430
0
  static const GstMetaInfo *meta_info = NULL;
1431
1432
0
  if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
1433
0
    const GstMetaInfo *mi =
1434
0
        gst_meta_register (GST_VIDEO_REGION_OF_INTEREST_META_API_TYPE,
1435
0
        "GstVideoRegionOfInterestMeta",
1436
0
        sizeof (GstVideoRegionOfInterestMeta),
1437
0
        gst_video_region_of_interest_meta_init,
1438
0
        gst_video_region_of_interest_meta_free,
1439
0
        gst_video_region_of_interest_meta_transform);
1440
0
    g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi);
1441
0
  }
1442
0
  return meta_info;
1443
0
}
1444
1445
/**
1446
 * gst_buffer_get_video_region_of_interest_meta_id:
1447
 * @buffer: a #GstBuffer
1448
 * @id: a metadata id
1449
 *
1450
 * Find the #GstVideoRegionOfInterestMeta on @buffer with the given @id.
1451
 *
1452
 * Buffers can contain multiple #GstVideoRegionOfInterestMeta metadata items if
1453
 * multiple regions of interests are marked on a frame.
1454
 *
1455
 * Returns: (transfer none) (nullable): the #GstVideoRegionOfInterestMeta with @id or %NULL when there is
1456
 * no such metadata on @buffer.
1457
 */
1458
GstVideoRegionOfInterestMeta *
1459
gst_buffer_get_video_region_of_interest_meta_id (GstBuffer * buffer, gint id)
1460
0
{
1461
0
  gpointer state = NULL;
1462
0
  GstMeta *meta;
1463
0
  const GstMetaInfo *info = GST_VIDEO_REGION_OF_INTEREST_META_INFO;
1464
1465
0
  while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
1466
0
    if (meta->info->api == info->api) {
1467
0
      GstVideoRegionOfInterestMeta *vmeta =
1468
0
          (GstVideoRegionOfInterestMeta *) meta;
1469
0
      if (vmeta->id == id)
1470
0
        return vmeta;
1471
0
    }
1472
0
  }
1473
0
  return NULL;
1474
0
}
1475
1476
/**
1477
 * gst_buffer_add_video_region_of_interest_meta:
1478
 * @buffer: a #GstBuffer
1479
 * @roi_type: Type of the region of interest (e.g. "face")
1480
 * @x: X position
1481
 * @y: Y position
1482
 * @w: width
1483
 * @h: height
1484
 *
1485
 * Attaches #GstVideoRegionOfInterestMeta metadata to @buffer with the given
1486
 * parameters.
1487
 *
1488
 * Returns: (transfer none): the #GstVideoRegionOfInterestMeta on @buffer.
1489
 */
1490
GstVideoRegionOfInterestMeta *
1491
gst_buffer_add_video_region_of_interest_meta (GstBuffer * buffer,
1492
    const gchar * roi_type, guint x, guint y, guint w, guint h)
1493
0
{
1494
0
  return gst_buffer_add_video_region_of_interest_meta_id (buffer,
1495
0
      g_quark_from_string (roi_type), x, y, w, h);
1496
0
}
1497
1498
/**
1499
 * gst_buffer_add_video_region_of_interest_meta_id:
1500
 * @buffer: a #GstBuffer
1501
 * @roi_type: Type of the region of interest (e.g. "face")
1502
 * @x: X position
1503
 * @y: Y position
1504
 * @w: width
1505
 * @h: height
1506
 *
1507
 * Attaches #GstVideoRegionOfInterestMeta metadata to @buffer with the given
1508
 * parameters.
1509
 *
1510
 * Returns: (transfer none): the #GstVideoRegionOfInterestMeta on @buffer.
1511
 */
1512
GstVideoRegionOfInterestMeta *
1513
gst_buffer_add_video_region_of_interest_meta_id (GstBuffer * buffer,
1514
    GQuark roi_type, guint x, guint y, guint w, guint h)
1515
0
{
1516
0
  GstVideoRegionOfInterestMeta *meta;
1517
1518
0
  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
1519
1520
0
  meta = (GstVideoRegionOfInterestMeta *) gst_buffer_add_meta (buffer,
1521
0
      GST_VIDEO_REGION_OF_INTEREST_META_INFO, NULL);
1522
0
  meta->roi_type = roi_type;
1523
0
  meta->x = x;
1524
0
  meta->y = y;
1525
0
  meta->w = w;
1526
0
  meta->h = h;
1527
1528
0
  return meta;
1529
0
}
1530
1531
/**
1532
 * gst_video_region_of_interest_meta_add_param:
1533
 * @meta: a #GstVideoRegionOfInterestMeta
1534
 * @s: (transfer full): a #GstStructure
1535
 *
1536
 * Attach element-specific parameters to @meta meant to be used by downstream
1537
 * elements which may handle this ROI.
1538
 * The name of @s is used to identify the element these parameters are meant for.
1539
 *
1540
 * This is typically used to tell encoders how they should encode this specific region.
1541
 * For example, a structure named "roi/x264enc" could be used to give the
1542
 * QP offsets this encoder should use when encoding the region described in @meta.
1543
 * Multiple parameters can be defined for the same meta so different encoders
1544
 * can be supported by cross platform applications).
1545
 *
1546
 * Since: 1.14
1547
 */
1548
void
1549
gst_video_region_of_interest_meta_add_param (GstVideoRegionOfInterestMeta *
1550
    meta, GstStructure * s)
1551
0
{
1552
0
  g_return_if_fail (meta);
1553
0
  g_return_if_fail (s);
1554
1555
0
  meta->params = g_list_append (meta->params, s);
1556
0
}
1557
1558
/**
1559
 * gst_video_region_of_interest_meta_get_param:
1560
 * @meta: a #GstVideoRegionOfInterestMeta
1561
 * @name: a name.
1562
 *
1563
 * Retrieve the parameter for @meta having @name as structure name,
1564
 * or %NULL if there is none.
1565
 *
1566
 * Returns: (transfer none) (nullable): a #GstStructure
1567
 *
1568
 * Since: 1.14
1569
 * See also: gst_video_region_of_interest_meta_add_param()
1570
 */
1571
GstStructure *
1572
gst_video_region_of_interest_meta_get_param (GstVideoRegionOfInterestMeta *
1573
    meta, const gchar * name)
1574
0
{
1575
0
  GList *l;
1576
1577
0
  g_return_val_if_fail (meta, NULL);
1578
0
  g_return_val_if_fail (name, NULL);
1579
1580
0
  for (l = meta->params; l; l = g_list_next (l)) {
1581
0
    GstStructure *s = l->data;
1582
1583
0
    if (gst_structure_has_name (s, name))
1584
0
      return s;
1585
0
  }
1586
1587
0
  return NULL;
1588
0
}
1589
1590
/* Time Code Meta implementation *******************************************/
1591
1592
GType
1593
gst_video_time_code_meta_api_get_type (void)
1594
0
{
1595
0
  static GType type;
1596
1597
0
  if (g_once_init_enter (&type)) {
1598
0
    static const gchar *tags[] = { NULL };
1599
0
    GType _type = gst_meta_api_type_register ("GstVideoTimeCodeMetaAPI", tags);
1600
0
    GST_INFO ("registering");
1601
0
    g_once_init_leave (&type, _type);
1602
0
  }
1603
0
  return type;
1604
0
}
1605
1606
1607
static gboolean
1608
gst_video_time_code_meta_transform (GstBuffer * dest, GstMeta * meta,
1609
    GstBuffer * buffer, GQuark type, gpointer data)
1610
0
{
1611
0
  GstVideoTimeCodeMeta *dmeta, *smeta;
1612
1613
0
  if (GST_META_TRANSFORM_IS_COPY (type)) {
1614
0
    smeta = (GstVideoTimeCodeMeta *) meta;
1615
1616
0
    GST_DEBUG ("copy time code metadata");
1617
0
    dmeta =
1618
0
        gst_buffer_add_video_time_code_meta_full (dest, smeta->tc.config.fps_n,
1619
0
        smeta->tc.config.fps_d, smeta->tc.config.latest_daily_jam,
1620
0
        smeta->tc.config.flags, smeta->tc.hours, smeta->tc.minutes,
1621
0
        smeta->tc.seconds, smeta->tc.frames, smeta->tc.field_count);
1622
0
    if (!dmeta)
1623
0
      return FALSE;
1624
0
  } else {
1625
    /* return FALSE, if transform type is not supported */
1626
0
    return FALSE;
1627
0
  }
1628
0
  return TRUE;
1629
0
}
1630
1631
static gboolean
1632
gst_video_time_code_meta_init (GstMeta * meta, gpointer params,
1633
    GstBuffer * buffer)
1634
0
{
1635
0
  GstVideoTimeCodeMeta *emeta = (GstVideoTimeCodeMeta *) meta;
1636
0
  memset (&emeta->tc, 0, sizeof (emeta->tc));
1637
0
  gst_video_time_code_clear (&emeta->tc);
1638
1639
0
  return TRUE;
1640
0
}
1641
1642
static void
1643
gst_video_time_code_meta_free (GstMeta * meta, GstBuffer * buffer)
1644
0
{
1645
0
  GstVideoTimeCodeMeta *emeta = (GstVideoTimeCodeMeta *) meta;
1646
1647
0
  gst_video_time_code_clear (&emeta->tc);
1648
0
}
1649
1650
const GstMetaInfo *
1651
gst_video_time_code_meta_get_info (void)
1652
0
{
1653
0
  static const GstMetaInfo *meta_info = NULL;
1654
1655
0
  if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
1656
0
    const GstMetaInfo *mi =
1657
0
        gst_meta_register (GST_VIDEO_TIME_CODE_META_API_TYPE,
1658
0
        "GstVideoTimeCodeMeta",
1659
0
        sizeof (GstVideoTimeCodeMeta),
1660
0
        gst_video_time_code_meta_init,
1661
0
        gst_video_time_code_meta_free,
1662
0
        gst_video_time_code_meta_transform);
1663
0
    g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi);
1664
0
  }
1665
0
  return meta_info;
1666
0
}
1667
1668
/**
1669
 * gst_buffer_add_video_time_code_meta:
1670
 * @buffer: a #GstBuffer
1671
 * @tc: a #GstVideoTimeCode
1672
 *
1673
 * Attaches #GstVideoTimeCodeMeta metadata to @buffer with the given
1674
 * parameters.
1675
 *
1676
 * Returns: (transfer none) (nullable): the #GstVideoTimeCodeMeta on @buffer, or
1677
 * (since 1.16) %NULL if the timecode was invalid.
1678
 *
1679
 * Since: 1.10
1680
 */
1681
GstVideoTimeCodeMeta *
1682
gst_buffer_add_video_time_code_meta (GstBuffer * buffer,
1683
    const GstVideoTimeCode * tc)
1684
0
{
1685
0
  if (!gst_video_time_code_is_valid (tc))
1686
0
    return NULL;
1687
1688
0
  return gst_buffer_add_video_time_code_meta_full (buffer, tc->config.fps_n,
1689
0
      tc->config.fps_d, tc->config.latest_daily_jam, tc->config.flags,
1690
0
      tc->hours, tc->minutes, tc->seconds, tc->frames, tc->field_count);
1691
0
}
1692
1693
/**
1694
 * gst_buffer_add_video_time_code_meta_full:
1695
 * @buffer: a #GstBuffer
1696
 * @fps_n: framerate numerator
1697
 * @fps_d: framerate denominator
1698
 * @latest_daily_jam: a #GDateTime for the latest daily jam
1699
 * @flags: a #GstVideoTimeCodeFlags
1700
 * @hours: hours since the daily jam
1701
 * @minutes: minutes since the daily jam
1702
 * @seconds: seconds since the daily jam
1703
 * @frames: frames since the daily jam
1704
 * @field_count: fields since the daily jam
1705
 *
1706
 * Attaches #GstVideoTimeCodeMeta metadata to @buffer with the given
1707
 * parameters.
1708
 *
1709
 * Returns: (transfer none) (nullable): the #GstVideoTimeCodeMeta on @buffer, or
1710
 * (since 1.16) %NULL if the timecode was invalid.
1711
 *
1712
 * Since: 1.10
1713
 */
1714
GstVideoTimeCodeMeta *
1715
gst_buffer_add_video_time_code_meta_full (GstBuffer * buffer, guint fps_n,
1716
    guint fps_d, GDateTime * latest_daily_jam, GstVideoTimeCodeFlags flags,
1717
    guint hours, guint minutes, guint seconds, guint frames, guint field_count)
1718
0
{
1719
0
  GstVideoTimeCodeMeta *meta;
1720
1721
0
  g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
1722
1723
0
  meta = (GstVideoTimeCodeMeta *) gst_buffer_add_meta (buffer,
1724
0
      GST_VIDEO_TIME_CODE_META_INFO, NULL);
1725
0
  g_return_val_if_fail (meta != NULL, NULL);
1726
1727
0
  gst_video_time_code_init (&meta->tc, fps_n, fps_d, latest_daily_jam, flags,
1728
0
      hours, minutes, seconds, frames, field_count);
1729
1730
0
  if (!gst_video_time_code_is_valid (&meta->tc)) {
1731
0
    gst_buffer_remove_meta (buffer, (GstMeta *) meta);
1732
0
    return NULL;
1733
0
  }
1734
1735
0
  return meta;
1736
0
}