videometa: Implement copying of GstVideoGLTextureUploadMeta
[platform/upstream/gstreamer.git] / 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
20 #include "gstvideometa.h"
21
22 static gboolean
23 gst_video_meta_transform (GstBuffer * dest, GstMeta * meta,
24     GstBuffer * buffer, GQuark type, gpointer data)
25 {
26   GstVideoMeta *dmeta, *smeta;
27   guint i;
28
29   smeta = (GstVideoMeta *) meta;
30
31   if (GST_META_TRANSFORM_IS_COPY (type)) {
32     GstMetaTransformCopy *copy = data;
33
34     if (!copy->region) {
35       /* only copy if the complete data is copied as well */
36       dmeta =
37           (GstVideoMeta *) gst_buffer_add_meta (dest, GST_VIDEO_META_INFO,
38           NULL);
39       dmeta->buffer = dest;
40
41       GST_DEBUG ("copy video metadata");
42       dmeta->flags = smeta->flags;
43       dmeta->format = smeta->format;
44       dmeta->id = smeta->id;
45       dmeta->width = smeta->width;
46       dmeta->height = smeta->height;
47
48       dmeta->n_planes = smeta->n_planes;
49       for (i = 0; i < dmeta->n_planes; i++) {
50         dmeta->offset[i] = smeta->offset[i];
51         dmeta->stride[i] = smeta->stride[i];
52       }
53       dmeta->map = smeta->map;
54       dmeta->unmap = smeta->unmap;
55     }
56   }
57   return TRUE;
58 }
59
60 GType
61 gst_video_meta_api_get_type (void)
62 {
63   static volatile GType type = 0;
64   static const gchar *tags[] = { "memory", "colorspace", "size", NULL };
65
66   if (g_once_init_enter (&type)) {
67     GType _type = gst_meta_api_type_register ("GstVideoMetaAPI", tags);
68     g_once_init_leave (&type, _type);
69   }
70   return type;
71 }
72
73 /* video metadata */
74 const GstMetaInfo *
75 gst_video_meta_get_info (void)
76 {
77   static const GstMetaInfo *video_meta_info = NULL;
78
79   if (g_once_init_enter (&video_meta_info)) {
80     const GstMetaInfo *meta =
81         gst_meta_register (GST_VIDEO_META_API_TYPE, "GstVideoMeta",
82         sizeof (GstVideoMeta), (GstMetaInitFunction) NULL,
83         (GstMetaFreeFunction) NULL, gst_video_meta_transform);
84     g_once_init_leave (&video_meta_info, meta);
85   }
86   return video_meta_info;
87 }
88
89 /**
90  * gst_buffer_get_video_meta_id:
91  * @buffer: a #GstBuffer
92  * @id: a metadata id
93  *
94  * Find the #GstVideoMeta on @buffer with the given @id.
95  *
96  * Buffers can contain multiple #GstVideoMeta metadata items when dealing with
97  * multiview buffers.
98  *
99  * Returns: the #GstVideoMeta with @id or %NULL when there is no such metadata
100  * on @buffer.
101  */
102 GstVideoMeta *
103 gst_buffer_get_video_meta_id (GstBuffer * buffer, gint id)
104 {
105   gpointer state = NULL;
106   GstMeta *meta;
107   const GstMetaInfo *info = GST_VIDEO_META_INFO;
108
109   while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
110     if (meta->info->api == info->api) {
111       GstVideoMeta *vmeta = (GstVideoMeta *) meta;
112       if (vmeta->id == id)
113         return vmeta;
114     }
115   }
116   return NULL;
117 }
118
119 static gboolean
120 default_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
121     gpointer * data, gint * stride, GstMapFlags flags)
122 {
123   guint idx, length;
124   gsize offset, skip;
125   GstBuffer *buffer = meta->buffer;
126
127   offset = meta->offset[plane];
128
129   /* find the memory block for this plane, this is the memory block containing
130    * the plane offset. FIXME use plane size */
131   if (!gst_buffer_find_memory (buffer, offset, 1, &idx, &length, &skip))
132     goto no_memory;
133
134   if (!gst_buffer_map_range (buffer, idx, length, info, flags))
135     goto cannot_map;
136
137   *stride = meta->stride[plane];
138   *data = (guint8 *) info->data + skip;
139
140   return TRUE;
141
142   /* ERRORS */
143 no_memory:
144   {
145     GST_DEBUG ("plane %u, no memory at offset %" G_GSIZE_FORMAT, plane, offset);
146     return FALSE;
147   }
148 cannot_map:
149   {
150     GST_DEBUG ("cannot map memory range %u-%u", idx, length);
151     return FALSE;
152   }
153 }
154
155 static gboolean
156 default_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
157 {
158   GstBuffer *buffer = meta->buffer;
159
160   gst_buffer_unmap (buffer, info);
161
162   return TRUE;
163 }
164
165 /**
166  * gst_buffer_add_video_meta:
167  * @buffer: a #GstBuffer
168  * @flags: #GstVideoFrameFlags
169  * @format: a #GstVideoFormat
170  * @width: the width
171  * @height: the height
172  *
173  * Attaches GstVideoMeta metadata to @buffer with the given parameters and the
174  * default offsets and strides for @format and @width x @height.
175  *
176  * This function calculates the default offsets and strides and then calls
177  * gst_buffer_add_video_meta_full() with them.
178  *
179  * Returns: the #GstVideoMeta on @buffer.
180  */
181 GstVideoMeta *
182 gst_buffer_add_video_meta (GstBuffer * buffer,
183     GstVideoFrameFlags flags, GstVideoFormat format, guint width, guint height)
184 {
185   GstVideoMeta *meta;
186   GstVideoInfo info;
187
188   gst_video_info_set_format (&info, format, width, height);
189
190   meta =
191       gst_buffer_add_video_meta_full (buffer, flags, format, width,
192       height, info.finfo->n_planes, info.offset, info.stride);
193
194   return meta;
195 }
196
197 /**
198  * gst_buffer_add_video_meta_full:
199  * @buffer: a #GstBuffer
200  * @flags: #GstVideoFrameFlags
201  * @format: a #GstVideoFormat
202  * @width: the width
203  * @height: the height
204  * @n_planes: number of planes
205  * @offset: offset of each plane
206  * @stride: stride of each plane
207  *
208  * Attaches GstVideoMeta metadata to @buffer with the given parameters.
209  *
210  * Returns: the #GstVideoMeta on @buffer.
211  */
212 GstVideoMeta *
213 gst_buffer_add_video_meta_full (GstBuffer * buffer,
214     GstVideoFrameFlags flags, GstVideoFormat format, guint width,
215     guint height, guint n_planes, gsize offset[GST_VIDEO_MAX_PLANES],
216     gint stride[GST_VIDEO_MAX_PLANES])
217 {
218   GstVideoMeta *meta;
219   guint i;
220
221   meta =
222       (GstVideoMeta *) gst_buffer_add_meta (buffer, GST_VIDEO_META_INFO, NULL);
223
224   meta->flags = flags;
225   meta->format = format;
226   meta->id = 0;
227   meta->width = width;
228   meta->height = height;
229   meta->buffer = buffer;
230
231   meta->n_planes = n_planes;
232   for (i = 0; i < n_planes; i++) {
233     meta->offset[i] = offset[i];
234     meta->stride[i] = stride[i];
235     GST_LOG ("plane %d, offset %" G_GSIZE_FORMAT ", stride %d", i, offset[i],
236         stride[i]);
237   }
238   meta->map = default_map;
239   meta->unmap = default_unmap;
240
241   return meta;
242 }
243
244 /**
245  * gst_video_meta_map:
246  * @meta: a #GstVideoMeta
247  * @plane: a plane
248  * @info: a #GstMapInfo
249  * @data: the data of @plane
250  * @stride: the stride of @plane
251  * @flags: @GstMapFlags
252  *
253  * Map the video plane with index @plane in @meta and return a pointer to the
254  * first byte of the plane and the stride of the plane.
255  *
256  * Returns: TRUE if the map operation was successful.
257  */
258 gboolean
259 gst_video_meta_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
260     gpointer * data, gint * stride, GstMapFlags flags)
261 {
262   g_return_val_if_fail (meta != NULL, FALSE);
263   g_return_val_if_fail (meta->map != NULL, FALSE);
264   g_return_val_if_fail (plane < meta->n_planes, FALSE);
265   g_return_val_if_fail (info != NULL, FALSE);
266   g_return_val_if_fail (data != NULL, FALSE);
267   g_return_val_if_fail (stride != NULL, FALSE);
268   g_return_val_if_fail (meta->buffer != NULL, FALSE);
269   g_return_val_if_fail (!(flags & GST_MAP_WRITE)
270       || gst_buffer_is_writable (meta->buffer), FALSE);
271
272   return meta->map (meta, plane, info, data, stride, flags);
273 }
274
275 /**
276  * gst_video_meta_unmap:
277  * @meta: a #GstVideoMeta
278  * @plane: a plane
279  * @info: a #GstMapInfo
280  *
281  * Unmap a previously mapped plane with gst_video_meta_map().
282  *
283  * Returns: TRUE if the memory was successfully unmapped.
284  */
285 gboolean
286 gst_video_meta_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
287 {
288   g_return_val_if_fail (meta != NULL, FALSE);
289   g_return_val_if_fail (meta->unmap != NULL, FALSE);
290   g_return_val_if_fail (plane < meta->n_planes, FALSE);
291   g_return_val_if_fail (info != NULL, FALSE);
292
293   return meta->unmap (meta, plane, info);
294 }
295
296 static gboolean
297 gst_video_crop_meta_transform (GstBuffer * dest, GstMeta * meta,
298     GstBuffer * buffer, GQuark type, gpointer data)
299 {
300   GstVideoCropMeta *dmeta, *smeta;
301
302   if (GST_META_TRANSFORM_IS_COPY (type)) {
303     smeta = (GstVideoCropMeta *) meta;
304     dmeta = gst_buffer_add_video_crop_meta (dest);
305
306     GST_DEBUG ("copy crop metadata");
307     dmeta->x = smeta->x;
308     dmeta->y = smeta->y;
309     dmeta->width = smeta->width;
310     dmeta->height = smeta->height;
311   } else if (GST_VIDEO_META_TRANSFORM_IS_SCALE (type)) {
312     GstVideoMetaTransform *trans = data;
313     gint ow, oh, nw, nh;
314
315     smeta = (GstVideoCropMeta *) meta;
316     dmeta = gst_buffer_add_video_crop_meta (dest);
317
318     ow = GST_VIDEO_INFO_WIDTH (trans->in_info);
319     nw = GST_VIDEO_INFO_WIDTH (trans->out_info);
320     oh = GST_VIDEO_INFO_HEIGHT (trans->in_info);
321     nh = GST_VIDEO_INFO_HEIGHT (trans->out_info);
322
323     GST_DEBUG ("scaling crop metadata %dx%d -> %dx%d", ow, oh, nw, nh);
324     dmeta->x = (smeta->x * nw) / ow;
325     dmeta->y = (smeta->y * nh) / oh;
326     dmeta->width = (smeta->width * nw) / ow;
327     dmeta->height = (smeta->height * nh) / oh;
328     GST_DEBUG ("crop offset %dx%d -> %dx%d", smeta->x, smeta->y, dmeta->x,
329         dmeta->y);
330     GST_DEBUG ("crop size   %dx%d -> %dx%d", smeta->width, smeta->height,
331         dmeta->width, dmeta->height);
332   }
333   return TRUE;
334 }
335
336 GType
337 gst_video_crop_meta_api_get_type (void)
338 {
339   static volatile GType type = 0;
340   static const gchar *tags[] = { "size", "orientation", NULL };
341
342   if (g_once_init_enter (&type)) {
343     GType _type = gst_meta_api_type_register ("GstVideoCropMetaAPI", tags);
344     g_once_init_leave (&type, _type);
345   }
346   return type;
347 }
348
349 const GstMetaInfo *
350 gst_video_crop_meta_get_info (void)
351 {
352   static const GstMetaInfo *video_crop_meta_info = NULL;
353
354   if (g_once_init_enter (&video_crop_meta_info)) {
355     const GstMetaInfo *meta =
356         gst_meta_register (GST_VIDEO_CROP_META_API_TYPE, "GstVideoCropMeta",
357         sizeof (GstVideoCropMeta), (GstMetaInitFunction) NULL,
358         (GstMetaFreeFunction) NULL, gst_video_crop_meta_transform);
359     g_once_init_leave (&video_crop_meta_info, meta);
360   }
361   return video_crop_meta_info;
362 }
363
364 /**
365  * gst_video_meta_transform_scale_get_quark:
366  *
367  * Get the #GQuark for the "gst-video-scale" metadata transform operation.
368  *
369  * Returns: a #GQuark
370  */
371 GQuark
372 gst_video_meta_transform_scale_get_quark (void)
373 {
374   static GQuark _value = 0;
375
376   if (_value == 0) {
377     _value = g_quark_from_static_string ("gst-video-scale");
378   }
379   return _value;
380 }
381
382
383 GType
384 gst_video_gl_texture_upload_meta_api_get_type (void)
385 {
386   static volatile GType type = 0;
387   static const gchar *tags[] = { "memory", NULL };
388
389   if (g_once_init_enter (&type)) {
390     GType _type =
391         gst_meta_api_type_register ("GstVideoGLTextureUploadMetaAPI", tags);
392     g_once_init_leave (&type, _type);
393   }
394   return type;
395 }
396
397 static void
398 gst_video_gl_texture_upload_meta_free (GstMeta * meta, GstBuffer * buffer)
399 {
400   GstVideoGLTextureUploadMeta *vmeta = (GstVideoGLTextureUploadMeta *) meta;
401
402   if (vmeta->user_data_free)
403     vmeta->user_data_free (vmeta->user_data);
404 }
405
406 static gboolean
407 gst_video_gl_texture_upload_meta_transform (GstBuffer * dest, GstMeta * meta,
408     GstBuffer * buffer, GQuark type, gpointer data)
409 {
410   GstVideoGLTextureUploadMeta *dmeta, *smeta;
411
412   smeta = (GstVideoGLTextureUploadMeta *) meta;
413
414   if (GST_META_TRANSFORM_IS_COPY (type)) {
415     GstMetaTransformCopy *copy = data;
416
417     if (!copy->region) {
418       /* only copy if the complete data is copied as well */
419       dmeta =
420           (GstVideoGLTextureUploadMeta *) gst_buffer_add_meta (dest,
421           GST_VIDEO_GL_TEXTURE_UPLOAD_META_INFO, NULL);
422       dmeta->buffer = dest;
423       dmeta->upload = smeta->upload;
424       dmeta->user_data = smeta->user_data;
425       dmeta->user_data_copy = smeta->user_data_copy;
426       dmeta->user_data_free = smeta->user_data_free;
427       if (dmeta->user_data_copy)
428         dmeta->user_data = dmeta->user_data_copy (dmeta->user_data);
429     }
430   }
431   return TRUE;
432 }
433
434 const GstMetaInfo *
435 gst_video_gl_texture_upload_meta_get_info (void)
436 {
437   static const GstMetaInfo *info = NULL;
438
439   if (g_once_init_enter (&info)) {
440     const GstMetaInfo *meta =
441         gst_meta_register (GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE,
442         "GstVideoGLTextureUploadMeta",
443         sizeof (GstVideoGLTextureUploadMeta),
444         NULL,
445         gst_video_gl_texture_upload_meta_free,
446         gst_video_gl_texture_upload_meta_transform);
447     g_once_init_leave (&info, meta);
448   }
449   return info;
450 }
451
452 /**
453  * gst_buffer_add_video_meta:
454  * @buffer: a #GstBuffer
455  * @flags: #GstVideoFrameFlags
456  * @format: a #GstVideoFormat
457  * @width: the width
458  * @height: the height
459  *
460  * Attaches GstVideoMeta metadata to @buffer with the given parameters and the
461  * default offsets and strides for @format and @width x @height.
462  *
463  * This function calculates the default offsets and strides and then calls
464  * gst_buffer_add_video_meta_full() with them.
465  *
466  * Returns: the #GstVideoMeta on @buffer.
467  */
468 GstVideoGLTextureUploadMeta *
469 gst_buffer_add_video_gl_texture_upload_meta (GstBuffer * buffer,
470     GstVideoGLTextureUpload upload, gpointer user_data,
471     GBoxedCopyFunc user_data_copy, GBoxedFreeFunc user_data_free)
472 {
473   GstVideoGLTextureUploadMeta *meta;
474
475   g_return_val_if_fail (buffer != NULL, NULL);
476   g_return_val_if_fail (upload != NULL, NULL);
477
478   meta =
479       (GstVideoGLTextureUploadMeta *) gst_buffer_add_meta (buffer,
480       GST_VIDEO_GL_TEXTURE_UPLOAD_META_INFO, NULL);
481
482   meta->buffer = buffer;
483   meta->upload = upload;
484   meta->user_data = user_data;
485   meta->user_data_copy = user_data_copy;
486   meta->user_data_free = user_data_free;
487
488   return meta;
489 }
490
491 gboolean
492 gst_video_gl_texture_upload_meta_upload (GstVideoGLTextureUploadMeta * meta,
493     guint format, guint texture_id)
494 {
495   g_return_val_if_fail (meta != NULL, FALSE);
496
497   return meta->upload (meta, format, texture_id);
498 }