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