video-converter: Fix v210->I420 last line conversion
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / gst-libs / gst / video / gstvideometa.c
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
27 /**
28  * SECTION:gstvideometa
29  * @title: GstMeta for video
30  * @short_description: Video related GstMeta
31  *
32  */
33
34 #ifndef GST_DISABLE_GST_DEBUG
35 #define GST_CAT_DEFAULT ensure_debug_category()
36 static GstDebugCategory *
37 ensure_debug_category (void)
38 {
39   static gsize cat_gonce = 0;
40
41   if (g_once_init_enter (&cat_gonce)) {
42     gsize cat_done;
43
44     cat_done = (gsize) _gst_debug_category_new ("videometa", 0, "videometa");
45
46     g_once_init_leave (&cat_gonce, cat_done);
47   }
48
49   return (GstDebugCategory *) cat_gonce;
50 }
51 #else
52 #define ensure_debug_category() /* NOOP */
53 #endif /* GST_DISABLE_GST_DEBUG */
54
55 static gboolean
56 gst_video_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
57 {
58   GstVideoMeta *emeta = (GstVideoMeta *) meta;
59
60   emeta->buffer = NULL;
61   emeta->flags = GST_VIDEO_FRAME_FLAG_NONE;
62   emeta->format = GST_VIDEO_FORMAT_UNKNOWN;
63   emeta->id = 0;
64   emeta->width = emeta->height = emeta->n_planes = 0;
65   memset (emeta->offset, 0, sizeof (emeta->offset));
66   memset (emeta->stride, 0, sizeof (emeta->stride));
67   gst_video_alignment_reset (&emeta->alignment);
68   emeta->map = NULL;
69   emeta->unmap = NULL;
70
71   return TRUE;
72 }
73
74 static gboolean
75 gst_video_meta_transform (GstBuffer * dest, GstMeta * meta,
76     GstBuffer * buffer, GQuark type, gpointer data)
77 {
78   GstVideoMeta *dmeta, *smeta;
79   guint i;
80
81   smeta = (GstVideoMeta *) meta;
82
83   if (GST_META_TRANSFORM_IS_COPY (type)) {
84     GstMetaTransformCopy *copy = data;
85
86     if (!copy->region) {
87       /* only copy if the complete data is copied as well */
88       dmeta =
89           (GstVideoMeta *) gst_buffer_add_meta (dest, GST_VIDEO_META_INFO,
90           NULL);
91
92       if (!dmeta)
93         return FALSE;
94
95       dmeta->buffer = dest;
96
97       GST_DEBUG ("copy video metadata");
98       dmeta->flags = smeta->flags;
99       dmeta->format = smeta->format;
100       dmeta->id = smeta->id;
101       dmeta->width = smeta->width;
102       dmeta->height = smeta->height;
103
104       dmeta->n_planes = smeta->n_planes;
105       for (i = 0; i < dmeta->n_planes; i++) {
106         dmeta->offset[i] = smeta->offset[i];
107         dmeta->stride[i] = smeta->stride[i];
108         dmeta->alignment = smeta->alignment;
109       }
110       dmeta->map = smeta->map;
111       dmeta->unmap = smeta->unmap;
112     }
113   } else {
114     /* return FALSE, if transform type is not supported */
115     return FALSE;
116   }
117   return TRUE;
118 }
119
120 GType
121 gst_video_meta_api_get_type (void)
122 {
123   static GType type = 0;
124   static const gchar *tags[] =
125       { GST_META_TAG_VIDEO_STR, GST_META_TAG_MEMORY_STR,
126     GST_META_TAG_VIDEO_COLORSPACE_STR,
127     GST_META_TAG_VIDEO_SIZE_STR, NULL
128   };
129
130   if (g_once_init_enter (&type)) {
131     GType _type = gst_meta_api_type_register ("GstVideoMetaAPI", tags);
132     g_once_init_leave (&type, _type);
133   }
134   return type;
135 }
136
137 /* video metadata */
138 const GstMetaInfo *
139 gst_video_meta_get_info (void)
140 {
141   static const GstMetaInfo *video_meta_info = NULL;
142
143   if (g_once_init_enter ((GstMetaInfo **) & video_meta_info)) {
144     const GstMetaInfo *meta =
145         gst_meta_register (GST_VIDEO_META_API_TYPE, "GstVideoMeta",
146         sizeof (GstVideoMeta), (GstMetaInitFunction) gst_video_meta_init,
147         (GstMetaFreeFunction) NULL, gst_video_meta_transform);
148     g_once_init_leave ((GstMetaInfo **) & video_meta_info,
149         (GstMetaInfo *) meta);
150   }
151   return video_meta_info;
152 }
153
154 /**
155  * gst_buffer_get_video_meta:
156  * @buffer: a #GstBuffer
157  *
158  * Find the #GstVideoMeta on @buffer with the lowest @id.
159  *
160  * Buffers can contain multiple #GstVideoMeta metadata items when dealing with
161  * multiview buffers.
162  *
163  * Returns: (transfer none): the #GstVideoMeta with lowest id (usually 0) or %NULL when there
164  * is no such metadata on @buffer.
165  */
166 GstVideoMeta *
167 gst_buffer_get_video_meta (GstBuffer * buffer)
168 {
169   gpointer state = NULL;
170   GstVideoMeta *out = NULL;
171   GstMeta *meta;
172   const GstMetaInfo *info = GST_VIDEO_META_INFO;
173
174   while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
175     if (meta->info->api == info->api) {
176       GstVideoMeta *vmeta = (GstVideoMeta *) meta;
177       if (vmeta->id == 0)
178         return vmeta;           /* Early out for id 0 */
179       if (out == NULL || vmeta->id < out->id)
180         out = vmeta;
181     }
182   }
183   return out;
184 }
185
186 /**
187  * gst_buffer_get_video_meta_id:
188  * @buffer: a #GstBuffer
189  * @id: a metadata id
190  *
191  * Find the #GstVideoMeta on @buffer with the given @id.
192  *
193  * Buffers can contain multiple #GstVideoMeta metadata items when dealing with
194  * multiview buffers.
195  *
196  * Returns: (transfer none): the #GstVideoMeta with @id or %NULL when there is no such metadata
197  * on @buffer.
198  */
199 GstVideoMeta *
200 gst_buffer_get_video_meta_id (GstBuffer * buffer, gint id)
201 {
202   gpointer state = NULL;
203   GstMeta *meta;
204   const GstMetaInfo *info = GST_VIDEO_META_INFO;
205
206   while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
207     if (meta->info->api == info->api) {
208       GstVideoMeta *vmeta = (GstVideoMeta *) meta;
209       if (vmeta->id == id)
210         return vmeta;
211     }
212   }
213   return NULL;
214 }
215
216 static gboolean
217 default_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
218     gpointer * data, gint * stride, GstMapFlags flags)
219 {
220   guint idx, length;
221   gsize offset, skip;
222   GstBuffer *buffer = meta->buffer;
223
224   offset = meta->offset[plane];
225
226   /* find the memory block for this plane, this is the memory block containing
227    * the plane offset. FIXME use plane size */
228   if (!gst_buffer_find_memory (buffer, offset, 1, &idx, &length, &skip))
229     goto no_memory;
230
231   if (!gst_buffer_map_range (buffer, idx, length, info, flags))
232     goto cannot_map;
233
234   *stride = meta->stride[plane];
235   *data = (guint8 *) info->data + skip;
236
237   return TRUE;
238
239   /* ERRORS */
240 no_memory:
241   {
242     GST_ERROR ("plane %u, no memory at offset %" G_GSIZE_FORMAT, plane, offset);
243     return FALSE;
244   }
245 cannot_map:
246   {
247     GST_ERROR ("cannot map memory range %u-%u", idx, length);
248     return FALSE;
249   }
250 }
251
252 static gboolean
253 default_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
254 {
255   GstBuffer *buffer = meta->buffer;
256
257   gst_buffer_unmap (buffer, info);
258
259   return TRUE;
260 }
261
262 /**
263  * gst_buffer_add_video_meta:
264  * @buffer: a #GstBuffer
265  * @flags: #GstVideoFrameFlags
266  * @format: a #GstVideoFormat
267  * @width: the width
268  * @height: the height
269  *
270  * Attaches GstVideoMeta metadata to @buffer with the given parameters and the
271  * default offsets and strides for @format and @width x @height.
272  *
273  * This function calculates the default offsets and strides and then calls
274  * gst_buffer_add_video_meta_full() with them.
275  *
276  * Returns: (transfer none): the #GstVideoMeta on @buffer.
277  */
278 GstVideoMeta *
279 gst_buffer_add_video_meta (GstBuffer * buffer,
280     GstVideoFrameFlags flags, GstVideoFormat format, guint width, guint height)
281 {
282   GstVideoMeta *meta;
283   GstVideoInfo info;
284
285   if (!gst_video_info_set_format (&info, format, width, height))
286     return NULL;
287
288   meta =
289       gst_buffer_add_video_meta_full (buffer, flags, format, width,
290       height, info.finfo->n_planes, info.offset, info.stride);
291
292   return meta;
293 }
294
295 /**
296  * gst_buffer_add_video_meta_full:
297  * @buffer: a #GstBuffer
298  * @flags: #GstVideoFrameFlags
299  * @format: a #GstVideoFormat
300  * @width: the width
301  * @height: the height
302  * @n_planes: number of planes
303  * @offset: (array fixed-size=4): offset of each plane
304  * @stride: (array fixed-size=4): stride of each plane
305  *
306  * Attaches GstVideoMeta metadata to @buffer with the given parameters.
307  *
308  * Returns: (transfer none): the #GstVideoMeta on @buffer.
309  */
310 GstVideoMeta *
311 gst_buffer_add_video_meta_full (GstBuffer * buffer,
312     GstVideoFrameFlags flags, GstVideoFormat format, guint width,
313     guint height, guint n_planes, gsize offset[GST_VIDEO_MAX_PLANES],
314     gint stride[GST_VIDEO_MAX_PLANES])
315 {
316   GstVideoMeta *meta;
317   guint i;
318
319   meta =
320       (GstVideoMeta *) gst_buffer_add_meta (buffer, GST_VIDEO_META_INFO, NULL);
321
322   if (!meta)
323     return NULL;
324
325   meta->flags = flags;
326   meta->format = format;
327   meta->id = 0;
328   meta->width = width;
329   meta->height = height;
330   meta->buffer = buffer;
331
332   meta->n_planes = n_planes;
333   for (i = 0; i < n_planes; i++) {
334     meta->offset[i] = offset[i];
335     meta->stride[i] = stride[i];
336     GST_LOG ("plane %d, offset %" G_GSIZE_FORMAT ", stride %d", i, offset[i],
337         stride[i]);
338   }
339   meta->map = default_map;
340   meta->unmap = default_unmap;
341
342   return meta;
343 }
344
345 /**
346  * gst_video_meta_map:
347  * @meta: a #GstVideoMeta
348  * @plane: a plane
349  * @info: a #GstMapInfo
350  * @data: (out): the data of @plane
351  * @stride: (out): the stride of @plane
352  * @flags: @GstMapFlags
353  *
354  * Map the video plane with index @plane in @meta and return a pointer to the
355  * first byte of the plane and the stride of the plane.
356  *
357  * Returns: TRUE if the map operation was successful.
358  */
359 gboolean
360 gst_video_meta_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
361     gpointer * data, gint * stride, GstMapFlags flags)
362 {
363   g_return_val_if_fail (meta != NULL, FALSE);
364   g_return_val_if_fail (meta->map != NULL, FALSE);
365   g_return_val_if_fail (plane < meta->n_planes, FALSE);
366   g_return_val_if_fail (info != NULL, FALSE);
367   g_return_val_if_fail (data != NULL, FALSE);
368   g_return_val_if_fail (stride != NULL, FALSE);
369   g_return_val_if_fail (meta->buffer != NULL, FALSE);
370   g_return_val_if_fail (!(flags & GST_MAP_WRITE)
371       || gst_buffer_is_writable (meta->buffer), FALSE);
372
373   return meta->map (meta, plane, info, data, stride, flags);
374 }
375
376 /**
377  * gst_video_meta_unmap:
378  * @meta: a #GstVideoMeta
379  * @plane: a plane
380  * @info: a #GstMapInfo
381  *
382  * Unmap a previously mapped plane with gst_video_meta_map().
383  *
384  * Returns: TRUE if the memory was successfully unmapped.
385  */
386 gboolean
387 gst_video_meta_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
388 {
389   g_return_val_if_fail (meta != NULL, FALSE);
390   g_return_val_if_fail (meta->unmap != NULL, FALSE);
391   g_return_val_if_fail (plane < meta->n_planes, FALSE);
392   g_return_val_if_fail (info != NULL, FALSE);
393
394   return meta->unmap (meta, plane, info);
395 }
396
397 static gboolean
398 gst_video_meta_validate_alignment (GstVideoMeta * meta,
399     gsize plane_size[GST_VIDEO_MAX_PLANES])
400 {
401   GstVideoInfo info;
402   guint i;
403
404   gst_video_info_init (&info);
405   gst_video_info_set_format (&info, meta->format, meta->width, meta->height);
406
407   if (!gst_video_info_align_full (&info, &meta->alignment, plane_size)) {
408     GST_WARNING ("Failed to align meta with its alignment");
409     return FALSE;
410   }
411
412   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&info); i++) {
413     if (GST_VIDEO_INFO_PLANE_STRIDE (&info, i) != meta->stride[i]) {
414       GST_WARNING
415           ("Stride of plane %d defined in meta (%d) is different from the one computed from the alignment (%d)",
416           i, meta->stride[i], GST_VIDEO_INFO_PLANE_STRIDE (&info, i));
417       return FALSE;
418     }
419   }
420
421   return TRUE;
422 }
423
424 /**
425  * gst_video_meta_set_alignment:
426  * @meta: a #GstVideoMeta
427  * @alignment: a #GstVideoAlignment
428  *
429  * Set the alignment of @meta to @alignment. This function checks that
430  * the paddings defined in @alignment are compatible with the strides
431  * defined in @meta and will fail to update if they are not.
432  *
433  * Returns: %TRUE if @alignment's meta has been updated, %FALSE if not
434  *
435  * Since: 1.18
436  */
437 gboolean
438 gst_video_meta_set_alignment (GstVideoMeta * meta, GstVideoAlignment alignment)
439 {
440   GstVideoAlignment old;
441
442   g_return_val_if_fail (meta, FALSE);
443
444   old = meta->alignment;
445   meta->alignment = alignment;
446
447   if (!gst_video_meta_validate_alignment (meta, NULL)) {
448     /* Invalid alignment, restore the previous one */
449     meta->alignment = old;
450     return FALSE;
451   }
452
453   GST_LOG ("Set alignment on meta: padding %u-%ux%u-%u", alignment.padding_top,
454       alignment.padding_left, alignment.padding_right,
455       alignment.padding_bottom);
456
457   return TRUE;
458 }
459
460 /**
461  * gst_video_meta_get_plane_size:
462  * @meta: a #GstVideoMeta
463  * @plane_size: (out caller-allocates) (array fixed-size=4): array used to store the plane sizes
464  *
465  * Compute the size, in bytes, of each video plane described in @meta including
466  * any padding and alignment constraint defined in @meta->alignment.
467  *
468  * Returns: %TRUE if @meta's alignment is valid and @plane_size has been
469  * updated, %FALSE otherwise
470  *
471  * Since: 1.18
472  */
473 gboolean
474 gst_video_meta_get_plane_size (GstVideoMeta * meta,
475     gsize plane_size[GST_VIDEO_MAX_PLANES])
476 {
477   g_return_val_if_fail (meta, FALSE);
478   g_return_val_if_fail (plane_size, FALSE);
479
480   return gst_video_meta_validate_alignment (meta, plane_size);
481 }
482
483 /**
484  * gst_video_meta_get_plane_height:
485  * @meta: a #GstVideoMeta
486  * @plane_height: (out caller-allocates) (array fixed-size=4): array used to store the plane height
487  *
488  * Compute the padded height of each plane from @meta (padded size
489  * divided by stride).
490  *
491  * It is not valid to call this function with a meta associated to a
492  * TILED video format.
493  *
494  * Returns: %TRUE if @meta's alignment is valid and @plane_height has been
495  * updated, %FALSE otherwise
496  *
497  * Since: 1.18
498  */
499 gboolean
500 gst_video_meta_get_plane_height (GstVideoMeta * meta,
501     guint plane_height[GST_VIDEO_MAX_PLANES])
502 {
503   gsize plane_size[GST_VIDEO_MAX_PLANES];
504   guint i;
505   GstVideoInfo info;
506
507   g_return_val_if_fail (meta, FALSE);
508   g_return_val_if_fail (plane_height, FALSE);
509
510   gst_video_info_init (&info);
511   gst_video_info_set_format (&info, meta->format, meta->width, meta->height);
512   g_return_val_if_fail (!GST_VIDEO_FORMAT_INFO_IS_TILED (&info), FALSE);
513
514   if (!gst_video_meta_get_plane_size (meta, plane_size))
515     return FALSE;
516
517   for (i = 0; i < meta->n_planes; i++) {
518     if (!meta->stride[i])
519       plane_height[i] = 0;
520     else
521       plane_height[i] = plane_size[i] / meta->stride[i];
522   }
523
524   for (; i < GST_VIDEO_MAX_PLANES; i++)
525     plane_height[i] = 0;
526
527   return TRUE;
528 }
529
530 static gboolean
531 gst_video_crop_meta_transform (GstBuffer * dest, GstMeta * meta,
532     GstBuffer * buffer, GQuark type, gpointer data)
533 {
534   GstVideoCropMeta *dmeta, *smeta;
535
536   if (GST_META_TRANSFORM_IS_COPY (type)) {
537     smeta = (GstVideoCropMeta *) meta;
538     dmeta = gst_buffer_add_video_crop_meta (dest);
539     if (!dmeta)
540       return FALSE;
541
542     GST_DEBUG ("copy crop metadata");
543     dmeta->x = smeta->x;
544     dmeta->y = smeta->y;
545     dmeta->width = smeta->width;
546     dmeta->height = smeta->height;
547   } else if (GST_VIDEO_META_TRANSFORM_IS_SCALE (type)) {
548     GstVideoMetaTransform *trans = data;
549     gint ow, oh, nw, nh;
550
551     smeta = (GstVideoCropMeta *) meta;
552     dmeta = gst_buffer_add_video_crop_meta (dest);
553     if (!dmeta)
554       return FALSE;
555
556     ow = GST_VIDEO_INFO_WIDTH (trans->in_info);
557     nw = GST_VIDEO_INFO_WIDTH (trans->out_info);
558     oh = GST_VIDEO_INFO_HEIGHT (trans->in_info);
559     nh = GST_VIDEO_INFO_HEIGHT (trans->out_info);
560
561     GST_DEBUG ("scaling crop metadata %dx%d -> %dx%d", ow, oh, nw, nh);
562     dmeta->x = (smeta->x * nw) / ow;
563     dmeta->y = (smeta->y * nh) / oh;
564     dmeta->width = (smeta->width * nw) / ow;
565     dmeta->height = (smeta->height * nh) / oh;
566     GST_DEBUG ("crop offset %dx%d -> %dx%d", smeta->x, smeta->y, dmeta->x,
567         dmeta->y);
568     GST_DEBUG ("crop size   %dx%d -> %dx%d", smeta->width, smeta->height,
569         dmeta->width, dmeta->height);
570   } else {
571     /* return FALSE, if transform type is not supported */
572     return FALSE;
573   }
574   return TRUE;
575 }
576
577 GType
578 gst_video_crop_meta_api_get_type (void)
579 {
580   static GType type = 0;
581   static const gchar *tags[] =
582       { GST_META_TAG_VIDEO_STR, GST_META_TAG_VIDEO_SIZE_STR,
583     GST_META_TAG_VIDEO_ORIENTATION_STR, NULL
584   };
585
586   if (g_once_init_enter (&type)) {
587     GType _type = gst_meta_api_type_register ("GstVideoCropMetaAPI", tags);
588     g_once_init_leave (&type, _type);
589   }
590   return type;
591 }
592
593 static gboolean
594 gst_video_crop_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer)
595 {
596   GstVideoCropMeta *emeta = (GstVideoCropMeta *) meta;
597   emeta->x = emeta->y = emeta->width = emeta->height = 0;
598
599   return TRUE;
600 }
601
602 const GstMetaInfo *
603 gst_video_crop_meta_get_info (void)
604 {
605   static const GstMetaInfo *video_crop_meta_info = NULL;
606
607   if (g_once_init_enter ((GstMetaInfo **) & video_crop_meta_info)) {
608     const GstMetaInfo *meta =
609         gst_meta_register (GST_VIDEO_CROP_META_API_TYPE, "GstVideoCropMeta",
610         sizeof (GstVideoCropMeta),
611         (GstMetaInitFunction) gst_video_crop_meta_init,
612         (GstMetaFreeFunction) NULL, gst_video_crop_meta_transform);
613     g_once_init_leave ((GstMetaInfo **) & video_crop_meta_info,
614         (GstMetaInfo *) meta);
615   }
616   return video_crop_meta_info;
617 }
618
619 /**
620  * gst_video_meta_transform_scale_get_quark:
621  *
622  * Get the #GQuark for the "gst-video-scale" metadata transform operation.
623  *
624  * Returns: a #GQuark
625  */
626 GQuark
627 gst_video_meta_transform_scale_get_quark (void)
628 {
629   static GQuark _value = 0;
630
631   if (_value == 0) {
632     _value = g_quark_from_static_string ("gst-video-scale");
633   }
634   return _value;
635 }
636
637
638 GType
639 gst_video_gl_texture_upload_meta_api_get_type (void)
640 {
641   static GType type = 0;
642   static const gchar *tags[] =
643       { GST_META_TAG_VIDEO_STR, GST_META_TAG_MEMORY_STR, NULL };
644
645   if (g_once_init_enter (&type)) {
646     GType _type =
647         gst_meta_api_type_register ("GstVideoGLTextureUploadMetaAPI", tags);
648     g_once_init_leave (&type, _type);
649   }
650   return type;
651 }
652
653 static gboolean
654 gst_video_gl_texture_upload_meta_init (GstMeta * meta, gpointer params,
655     GstBuffer * buffer)
656 {
657   GstVideoGLTextureUploadMeta *vmeta = (GstVideoGLTextureUploadMeta *) meta;
658
659   vmeta->texture_orientation =
660       GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL;
661   vmeta->n_textures = 0;
662   memset (vmeta->texture_type, 0, sizeof (vmeta->texture_type));
663   vmeta->buffer = NULL;
664   vmeta->upload = NULL;
665   vmeta->user_data = NULL;
666   vmeta->user_data_copy = NULL;
667   vmeta->user_data_free = NULL;
668
669   return TRUE;
670 }
671
672 static void
673 gst_video_gl_texture_upload_meta_free (GstMeta * meta, GstBuffer * buffer)
674 {
675   GstVideoGLTextureUploadMeta *vmeta = (GstVideoGLTextureUploadMeta *) meta;
676
677   if (vmeta->user_data_free)
678     vmeta->user_data_free (vmeta->user_data);
679 }
680
681 static gboolean
682 gst_video_gl_texture_upload_meta_transform (GstBuffer * dest, GstMeta * meta,
683     GstBuffer * buffer, GQuark type, gpointer data)
684 {
685   GstVideoGLTextureUploadMeta *dmeta, *smeta;
686
687   smeta = (GstVideoGLTextureUploadMeta *) meta;
688
689   if (GST_META_TRANSFORM_IS_COPY (type)) {
690     GstMetaTransformCopy *copy = data;
691
692     if (!copy->region) {
693       /* only copy if the complete data is copied as well */
694       dmeta =
695           (GstVideoGLTextureUploadMeta *) gst_buffer_add_meta (dest,
696           GST_VIDEO_GL_TEXTURE_UPLOAD_META_INFO, NULL);
697
698       if (!dmeta)
699         return FALSE;
700
701       dmeta->texture_orientation = smeta->texture_orientation;
702       dmeta->n_textures = smeta->n_textures;
703       memcpy (dmeta->texture_type, smeta->texture_type,
704           sizeof (smeta->texture_type[0]) * 4);
705       dmeta->buffer = dest;
706       dmeta->upload = smeta->upload;
707       dmeta->user_data = smeta->user_data;
708       dmeta->user_data_copy = smeta->user_data_copy;
709       dmeta->user_data_free = smeta->user_data_free;
710       if (dmeta->user_data_copy)
711         dmeta->user_data = dmeta->user_data_copy (dmeta->user_data);
712     }
713   } else {
714     /* return FALSE, if transform type is not supported */
715     return FALSE;
716   }
717   return TRUE;
718 }
719
720 const GstMetaInfo *
721 gst_video_gl_texture_upload_meta_get_info (void)
722 {
723   static const GstMetaInfo *info = NULL;
724
725   if (g_once_init_enter ((GstMetaInfo **) & info)) {
726     const GstMetaInfo *meta =
727         gst_meta_register (GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE,
728         "GstVideoGLTextureUploadMeta",
729         sizeof (GstVideoGLTextureUploadMeta),
730         gst_video_gl_texture_upload_meta_init,
731         gst_video_gl_texture_upload_meta_free,
732         gst_video_gl_texture_upload_meta_transform);
733     g_once_init_leave ((GstMetaInfo **) & info, (GstMetaInfo *) meta);
734   }
735   return info;
736 }
737
738 /**
739  * gst_buffer_add_video_gl_texture_upload_meta:
740  * @buffer: a #GstBuffer
741  * @texture_orientation: the #GstVideoGLTextureOrientation
742  * @n_textures: the number of textures
743  * @texture_type: array of #GstVideoGLTextureType
744  * @upload: (scope call): the function to upload the buffer to a specific texture ID
745  * @user_data: user data for the implementor of @upload
746  * @user_data_copy: (scope call): function to copy @user_data
747  * @user_data_free: (scope call): function to free @user_data
748  *
749  * Attaches GstVideoGLTextureUploadMeta metadata to @buffer with the given
750  * parameters.
751  *
752  * Returns: (transfer none): the #GstVideoGLTextureUploadMeta on @buffer.
753  */
754 GstVideoGLTextureUploadMeta *
755 gst_buffer_add_video_gl_texture_upload_meta (GstBuffer * buffer,
756     GstVideoGLTextureOrientation texture_orientation, guint n_textures,
757     GstVideoGLTextureType texture_type[4], GstVideoGLTextureUpload upload,
758     gpointer user_data, GBoxedCopyFunc user_data_copy,
759     GBoxedFreeFunc user_data_free)
760 {
761   GstVideoGLTextureUploadMeta *meta;
762
763   g_return_val_if_fail (buffer != NULL, NULL);
764   g_return_val_if_fail (upload != NULL, NULL);
765   g_return_val_if_fail (n_textures > 0 && n_textures < 5, NULL);
766
767   meta =
768       (GstVideoGLTextureUploadMeta *) gst_buffer_add_meta (buffer,
769       GST_VIDEO_GL_TEXTURE_UPLOAD_META_INFO, NULL);
770
771   if (!meta)
772     return NULL;
773
774   meta->texture_orientation = texture_orientation;
775   meta->n_textures = n_textures;
776   memcpy (meta->texture_type, texture_type, sizeof (texture_type[0]) * 4);
777   meta->buffer = buffer;
778   meta->upload = upload;
779   meta->user_data = user_data;
780   meta->user_data_copy = user_data_copy;
781   meta->user_data_free = user_data_free;
782
783   return meta;
784 }
785
786 /**
787  * gst_video_gl_texture_upload_meta_upload:
788  * @meta: a #GstVideoGLTextureUploadMeta
789  * @texture_id: the texture IDs to upload to
790  *
791  * Uploads the buffer which owns the meta to a specific texture ID.
792  *
793  * Returns: %TRUE if uploading succeeded, %FALSE otherwise.
794  */
795 gboolean
796 gst_video_gl_texture_upload_meta_upload (GstVideoGLTextureUploadMeta * meta,
797     guint texture_id[4])
798 {
799   g_return_val_if_fail (meta != NULL, FALSE);
800
801   return meta->upload (meta, texture_id);
802 }
803
804 /* Region of Interest Meta implementation *******************************************/
805
806 GType
807 gst_video_region_of_interest_meta_api_get_type (void)
808 {
809   static GType type;
810   static const gchar *tags[] =
811       { GST_META_TAG_VIDEO_STR, GST_META_TAG_VIDEO_ORIENTATION_STR,
812     GST_META_TAG_VIDEO_SIZE_STR, NULL
813   };
814
815   if (g_once_init_enter (&type)) {
816     GType _type =
817         gst_meta_api_type_register ("GstVideoRegionOfInterestMetaAPI", tags);
818     GST_INFO ("registering");
819     g_once_init_leave (&type, _type);
820   }
821   return type;
822 }
823
824
825 static gboolean
826 gst_video_region_of_interest_meta_transform (GstBuffer * dest, GstMeta * meta,
827     GstBuffer * buffer, GQuark type, gpointer data)
828 {
829   GstVideoRegionOfInterestMeta *dmeta, *smeta;
830
831   if (GST_META_TRANSFORM_IS_COPY (type)) {
832     smeta = (GstVideoRegionOfInterestMeta *) meta;
833
834     GST_DEBUG ("copy region of interest metadata");
835     dmeta =
836         gst_buffer_add_video_region_of_interest_meta_id (dest,
837         smeta->roi_type, smeta->x, smeta->y, smeta->w, smeta->h);
838     if (!dmeta)
839       return FALSE;
840
841     dmeta->id = smeta->id;
842     dmeta->parent_id = smeta->parent_id;
843     dmeta->params = g_list_copy_deep (smeta->params,
844         (GCopyFunc) gst_structure_copy, NULL);
845   } else if (GST_VIDEO_META_TRANSFORM_IS_SCALE (type)) {
846     GstVideoMetaTransform *trans = data;
847     gint ow, oh, nw, nh;
848     ow = GST_VIDEO_INFO_WIDTH (trans->in_info);
849     nw = GST_VIDEO_INFO_WIDTH (trans->out_info);
850     oh = GST_VIDEO_INFO_HEIGHT (trans->in_info);
851     nh = GST_VIDEO_INFO_HEIGHT (trans->out_info);
852     GST_DEBUG ("scaling region of interest metadata %dx%d -> %dx%d", ow, oh, nw,
853         nh);
854
855     smeta = (GstVideoRegionOfInterestMeta *) meta;
856     dmeta =
857         gst_buffer_add_video_region_of_interest_meta_id (dest,
858         smeta->roi_type, (smeta->x * nw) / ow, (smeta->y * nh) / oh,
859         (smeta->w * nw) / ow, (smeta->h * nh) / oh);
860     if (!dmeta)
861       return FALSE;
862
863     dmeta->id = smeta->id;
864     dmeta->parent_id = smeta->parent_id;
865
866     GST_DEBUG ("region of interest (id:%d, parent id:%d) offset %dx%d -> %dx%d",
867         smeta->id, smeta->parent_id, smeta->x, smeta->y, dmeta->x, dmeta->y);
868     GST_DEBUG ("region of interest size   %dx%d -> %dx%d", smeta->w, smeta->h,
869         dmeta->w, dmeta->h);
870   } else {
871     /* return FALSE, if transform type is not supported */
872     return FALSE;
873   }
874   return TRUE;
875 }
876
877 static gboolean
878 gst_video_region_of_interest_meta_init (GstMeta * meta, gpointer params,
879     GstBuffer * buffer)
880 {
881   GstVideoRegionOfInterestMeta *emeta = (GstVideoRegionOfInterestMeta *) meta;
882   emeta->roi_type = 0;
883   emeta->id = 0;
884   emeta->parent_id = 0;
885   emeta->x = emeta->y = emeta->w = emeta->h = 0;
886   emeta->params = NULL;
887
888   return TRUE;
889 }
890
891 static void
892 gst_video_region_of_interest_meta_free (GstMeta * meta, GstBuffer * buffer)
893 {
894   GstVideoRegionOfInterestMeta *emeta = (GstVideoRegionOfInterestMeta *) meta;
895
896   g_list_free_full (emeta->params, (GDestroyNotify) gst_structure_free);
897 }
898
899 const GstMetaInfo *
900 gst_video_region_of_interest_meta_get_info (void)
901 {
902   static const GstMetaInfo *meta_info = NULL;
903
904   if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
905     const GstMetaInfo *mi =
906         gst_meta_register (GST_VIDEO_REGION_OF_INTEREST_META_API_TYPE,
907         "GstVideoRegionOfInterestMeta",
908         sizeof (GstVideoRegionOfInterestMeta),
909         gst_video_region_of_interest_meta_init,
910         gst_video_region_of_interest_meta_free,
911         gst_video_region_of_interest_meta_transform);
912     g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi);
913   }
914   return meta_info;
915 }
916
917 /**
918  * gst_buffer_get_video_region_of_interest_meta_id:
919  * @buffer: a #GstBuffer
920  * @id: a metadata id
921  *
922  * Find the #GstVideoRegionOfInterestMeta on @buffer with the given @id.
923  *
924  * Buffers can contain multiple #GstVideoRegionOfInterestMeta metadata items if
925  * multiple regions of interests are marked on a frame.
926  *
927  * Returns: (transfer none): the #GstVideoRegionOfInterestMeta with @id or %NULL when there is
928  * no such metadata on @buffer.
929  */
930 GstVideoRegionOfInterestMeta *
931 gst_buffer_get_video_region_of_interest_meta_id (GstBuffer * buffer, gint id)
932 {
933   gpointer state = NULL;
934   GstMeta *meta;
935   const GstMetaInfo *info = GST_VIDEO_REGION_OF_INTEREST_META_INFO;
936
937   while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
938     if (meta->info->api == info->api) {
939       GstVideoRegionOfInterestMeta *vmeta =
940           (GstVideoRegionOfInterestMeta *) meta;
941       if (vmeta->id == id)
942         return vmeta;
943     }
944   }
945   return NULL;
946 }
947
948 /**
949  * gst_buffer_add_video_region_of_interest_meta:
950  * @buffer: a #GstBuffer
951  * @roi_type: Type of the region of interest (e.g. "face")
952  * @x: X position
953  * @y: Y position
954  * @w: width
955  * @h: height
956  *
957  * Attaches #GstVideoRegionOfInterestMeta metadata to @buffer with the given
958  * parameters.
959  *
960  * Returns: (transfer none): the #GstVideoRegionOfInterestMeta on @buffer.
961  */
962 GstVideoRegionOfInterestMeta *
963 gst_buffer_add_video_region_of_interest_meta (GstBuffer * buffer,
964     const gchar * roi_type, guint x, guint y, guint w, guint h)
965 {
966   return gst_buffer_add_video_region_of_interest_meta_id (buffer,
967       g_quark_from_string (roi_type), x, y, w, h);
968 }
969
970 /**
971  * gst_buffer_add_video_region_of_interest_meta_id:
972  * @buffer: a #GstBuffer
973  * @roi_type: Type of the region of interest (e.g. "face")
974  * @x: X position
975  * @y: Y position
976  * @w: width
977  * @h: height
978  *
979  * Attaches #GstVideoRegionOfInterestMeta metadata to @buffer with the given
980  * parameters.
981  *
982  * Returns: (transfer none): the #GstVideoRegionOfInterestMeta on @buffer.
983  */
984 GstVideoRegionOfInterestMeta *
985 gst_buffer_add_video_region_of_interest_meta_id (GstBuffer * buffer,
986     GQuark roi_type, guint x, guint y, guint w, guint h)
987 {
988   GstVideoRegionOfInterestMeta *meta;
989
990   g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
991
992   meta = (GstVideoRegionOfInterestMeta *) gst_buffer_add_meta (buffer,
993       GST_VIDEO_REGION_OF_INTEREST_META_INFO, NULL);
994   meta->roi_type = roi_type;
995   meta->x = x;
996   meta->y = y;
997   meta->w = w;
998   meta->h = h;
999
1000   return meta;
1001 }
1002
1003 /**
1004  * gst_video_region_of_interest_meta_add_param:
1005  * @meta: a #GstVideoRegionOfInterestMeta
1006  * @s: (transfer full): a #GstStructure
1007  *
1008  * Attach element-specific parameters to @meta meant to be used by downstream
1009  * elements which may handle this ROI.
1010  * The name of @s is used to identify the element these parameters are meant for.
1011  *
1012  * This is typically used to tell encoders how they should encode this specific region.
1013  * For example, a structure named "roi/x264enc" could be used to give the
1014  * QP offsets this encoder should use when encoding the region described in @meta.
1015  * Multiple parameters can be defined for the same meta so different encoders
1016  * can be supported by cross platform applications).
1017  *
1018  * Since: 1.14
1019  */
1020 void
1021 gst_video_region_of_interest_meta_add_param (GstVideoRegionOfInterestMeta *
1022     meta, GstStructure * s)
1023 {
1024   g_return_if_fail (meta);
1025   g_return_if_fail (s);
1026
1027   meta->params = g_list_append (meta->params, s);
1028 }
1029
1030 /**
1031  * gst_video_region_of_interest_meta_get_param:
1032  * @meta: a #GstVideoRegionOfInterestMeta
1033  * @name: a name.
1034  *
1035  * Retrieve the parameter for @meta having @name as structure name,
1036  * or %NULL if there is none.
1037  *
1038  * Returns: (transfer none) (nullable): a #GstStructure
1039  *
1040  * Since: 1.14
1041  * See also: gst_video_region_of_interest_meta_add_param()
1042  */
1043 GstStructure *
1044 gst_video_region_of_interest_meta_get_param (GstVideoRegionOfInterestMeta *
1045     meta, const gchar * name)
1046 {
1047   GList *l;
1048
1049   g_return_val_if_fail (meta, NULL);
1050   g_return_val_if_fail (name, NULL);
1051
1052   for (l = meta->params; l; l = g_list_next (l)) {
1053     GstStructure *s = l->data;
1054
1055     if (gst_structure_has_name (s, name))
1056       return s;
1057   }
1058
1059   return NULL;
1060 }
1061
1062 /* Time Code Meta implementation *******************************************/
1063
1064 GType
1065 gst_video_time_code_meta_api_get_type (void)
1066 {
1067   static GType type;
1068
1069   if (g_once_init_enter (&type)) {
1070     static const gchar *tags[] = { NULL };
1071     GType _type = gst_meta_api_type_register ("GstVideoTimeCodeMetaAPI", tags);
1072     GST_INFO ("registering");
1073     g_once_init_leave (&type, _type);
1074   }
1075   return type;
1076 }
1077
1078
1079 static gboolean
1080 gst_video_time_code_meta_transform (GstBuffer * dest, GstMeta * meta,
1081     GstBuffer * buffer, GQuark type, gpointer data)
1082 {
1083   GstVideoTimeCodeMeta *dmeta, *smeta;
1084
1085   if (GST_META_TRANSFORM_IS_COPY (type)) {
1086     smeta = (GstVideoTimeCodeMeta *) meta;
1087
1088     GST_DEBUG ("copy time code metadata");
1089     dmeta =
1090         gst_buffer_add_video_time_code_meta_full (dest, smeta->tc.config.fps_n,
1091         smeta->tc.config.fps_d, smeta->tc.config.latest_daily_jam,
1092         smeta->tc.config.flags, smeta->tc.hours, smeta->tc.minutes,
1093         smeta->tc.seconds, smeta->tc.frames, smeta->tc.field_count);
1094     if (!dmeta)
1095       return FALSE;
1096   } else {
1097     /* return FALSE, if transform type is not supported */
1098     return FALSE;
1099   }
1100   return TRUE;
1101 }
1102
1103 static gboolean
1104 gst_video_time_code_meta_init (GstMeta * meta, gpointer params,
1105     GstBuffer * buffer)
1106 {
1107   GstVideoTimeCodeMeta *emeta = (GstVideoTimeCodeMeta *) meta;
1108   memset (&emeta->tc, 0, sizeof (emeta->tc));
1109   gst_video_time_code_clear (&emeta->tc);
1110
1111   return TRUE;
1112 }
1113
1114 static void
1115 gst_video_time_code_meta_free (GstMeta * meta, GstBuffer * buffer)
1116 {
1117   GstVideoTimeCodeMeta *emeta = (GstVideoTimeCodeMeta *) meta;
1118
1119   gst_video_time_code_clear (&emeta->tc);
1120 }
1121
1122 const GstMetaInfo *
1123 gst_video_time_code_meta_get_info (void)
1124 {
1125   static const GstMetaInfo *meta_info = NULL;
1126
1127   if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
1128     const GstMetaInfo *mi =
1129         gst_meta_register (GST_VIDEO_TIME_CODE_META_API_TYPE,
1130         "GstVideoTimeCodeMeta",
1131         sizeof (GstVideoTimeCodeMeta),
1132         gst_video_time_code_meta_init,
1133         gst_video_time_code_meta_free,
1134         gst_video_time_code_meta_transform);
1135     g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi);
1136   }
1137   return meta_info;
1138 }
1139
1140 /**
1141  * gst_buffer_add_video_time_code_meta:
1142  * @buffer: a #GstBuffer
1143  * @tc: a #GstVideoTimeCode
1144  *
1145  * Attaches #GstVideoTimeCodeMeta metadata to @buffer with the given
1146  * parameters.
1147  *
1148  * Returns: (transfer none) (nullable): the #GstVideoTimeCodeMeta on @buffer, or
1149  * (since 1.16) %NULL if the timecode was invalid.
1150  *
1151  * Since: 1.10
1152  */
1153 GstVideoTimeCodeMeta *
1154 gst_buffer_add_video_time_code_meta (GstBuffer * buffer,
1155     const GstVideoTimeCode * tc)
1156 {
1157   if (!gst_video_time_code_is_valid (tc))
1158     return NULL;
1159
1160   return gst_buffer_add_video_time_code_meta_full (buffer, tc->config.fps_n,
1161       tc->config.fps_d, tc->config.latest_daily_jam, tc->config.flags,
1162       tc->hours, tc->minutes, tc->seconds, tc->frames, tc->field_count);
1163 }
1164
1165 /**
1166  * gst_buffer_add_video_time_code_meta_full:
1167  * @buffer: a #GstBuffer
1168  * @fps_n: framerate numerator
1169  * @fps_d: framerate denominator
1170  * @latest_daily_jam: a #GDateTime for the latest daily jam
1171  * @flags: a #GstVideoTimeCodeFlags
1172  * @hours: hours since the daily jam
1173  * @minutes: minutes since the daily jam
1174  * @seconds: seconds since the daily jam
1175  * @frames: frames since the daily jam
1176  * @field_count: fields since the daily jam
1177  *
1178  * Attaches #GstVideoTimeCodeMeta metadata to @buffer with the given
1179  * parameters.
1180  *
1181  * Returns: (transfer none): the #GstVideoTimeCodeMeta on @buffer, or
1182  * (since 1.16) %NULL if the timecode was invalid.
1183  *
1184  * Since: 1.10
1185  */
1186 GstVideoTimeCodeMeta *
1187 gst_buffer_add_video_time_code_meta_full (GstBuffer * buffer, guint fps_n,
1188     guint fps_d, GDateTime * latest_daily_jam, GstVideoTimeCodeFlags flags,
1189     guint hours, guint minutes, guint seconds, guint frames, guint field_count)
1190 {
1191   GstVideoTimeCodeMeta *meta;
1192
1193   g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL);
1194
1195   meta = (GstVideoTimeCodeMeta *) gst_buffer_add_meta (buffer,
1196       GST_VIDEO_TIME_CODE_META_INFO, NULL);
1197   g_return_val_if_fail (meta != NULL, NULL);
1198
1199   gst_video_time_code_init (&meta->tc, fps_n, fps_d, latest_daily_jam, flags,
1200       hours, minutes, seconds, frames, field_count);
1201
1202   if (!gst_video_time_code_is_valid (&meta->tc)) {
1203     gst_buffer_remove_meta (buffer, (GstMeta *) meta);
1204     return NULL;
1205   }
1206
1207   return meta;
1208 }