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