2e51619e3e41f19829bb9a0d3580b6a68cc75f7a
[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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, 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 (video_meta_info == NULL) {
80     video_meta_info =
81         gst_meta_register (GST_VIDEO_META_API_TYPE, "GstVideoMeta",
82         sizeof (GstVideoMeta), (GstMetaInitFunction) NULL,
83         (GstMetaFreeFunction) NULL, gst_video_meta_transform);
84   }
85   return video_meta_info;
86 }
87
88 /**
89  * gst_buffer_get_video_meta_id:
90  * @buffer: a #GstBuffer
91  * @id: a metadata id
92  *
93  * Find the #GstVideoMeta on @buffer with the given @id.
94  *
95  * Buffers can contain multiple #GstVideoMeta metadata items when dealing with
96  * multiview buffers.
97  *
98  * Returns: the #GstVideoMeta with @id or %NULL when there is no such metadata
99  * on @buffer.
100  */
101 GstVideoMeta *
102 gst_buffer_get_video_meta_id (GstBuffer * buffer, gint id)
103 {
104   gpointer state = NULL;
105   GstMeta *meta;
106   const GstMetaInfo *info = GST_VIDEO_META_INFO;
107
108   while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
109     if (meta->info->api == info->api) {
110       GstVideoMeta *vmeta = (GstVideoMeta *) meta;
111       if (vmeta->id == id)
112         return vmeta;
113     }
114   }
115   return NULL;
116 }
117
118 static gboolean
119 default_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
120     gpointer * data, gint * stride, GstMapFlags flags)
121 {
122   guint idx, length;
123   gsize offset, skip;
124   GstBuffer *buffer = meta->buffer;
125
126   offset = meta->offset[plane];
127
128   /* find the memory block for this plane, this is the memory block containing
129    * the plane offset. FIXME use plane size */
130   if (!gst_buffer_find_memory (buffer, offset, 1, &idx, &length, &skip))
131     goto no_memory;
132
133   if (!gst_buffer_map_range (buffer, idx, length, info, flags))
134     goto cannot_map;
135
136   *stride = meta->stride[plane];
137   *data = (guint8 *) info->data + skip;
138
139   return TRUE;
140
141   /* ERRORS */
142 no_memory:
143   {
144     GST_DEBUG ("plane %u, no memory at offset %" G_GSIZE_FORMAT, plane, offset);
145     return FALSE;
146   }
147 cannot_map:
148   {
149     GST_DEBUG ("cannot map memory range %u-%u", idx, length);
150     return FALSE;
151   }
152 }
153
154 static gboolean
155 default_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
156 {
157   GstBuffer *buffer = meta->buffer;
158
159   gst_buffer_unmap (buffer, info);
160
161   return TRUE;
162 }
163
164 /**
165  * gst_buffer_add_video_meta:
166  * @buffer: a #GstBuffer
167  * @flags: #GstVideoFrameFlags
168  * @format: a #GstVideoFormat
169  * @width: the width
170  * @height: the height
171  *
172  * Attaches GstVideoMeta metadata to @buffer with the given parameters and the
173  * default offsets and strides for @format and @width x @height.
174  *
175  * This function calculates the default offsets and strides and then calls
176  * gst_buffer_add_video_meta_full() with them.
177  *
178  * Returns: the #GstVideoMeta on @buffer.
179  */
180 GstVideoMeta *
181 gst_buffer_add_video_meta (GstBuffer * buffer,
182     GstVideoFrameFlags flags, GstVideoFormat format, guint width, guint height)
183 {
184   GstVideoMeta *meta;
185   GstVideoInfo info;
186
187   gst_video_info_set_format (&info, format, width, height);
188
189   meta =
190       gst_buffer_add_video_meta_full (buffer, flags, format, width,
191       height, info.finfo->n_planes, info.offset, info.stride);
192
193   return meta;
194 }
195
196 /**
197  * gst_buffer_add_video_meta_full:
198  * @buffer: a #GstBuffer
199  * @flags: #GstVideoFrameFlags
200  * @format: a #GstVideoFormat
201  * @width: the width
202  * @height: the height
203  * @n_planes: number of planes
204  * @offset: offset of each plane
205  * @stride: stride of each plane
206  *
207  * Attaches GstVideoMeta metadata to @buffer with the given parameters.
208  *
209  * Returns: the #GstVideoMeta on @buffer.
210  */
211 GstVideoMeta *
212 gst_buffer_add_video_meta_full (GstBuffer * buffer,
213     GstVideoFrameFlags flags, GstVideoFormat format, guint width,
214     guint height, guint n_planes, gsize offset[GST_VIDEO_MAX_PLANES],
215     gint stride[GST_VIDEO_MAX_PLANES])
216 {
217   GstVideoMeta *meta;
218   guint i;
219
220   meta =
221       (GstVideoMeta *) gst_buffer_add_meta (buffer, GST_VIDEO_META_INFO, NULL);
222
223   meta->flags = flags;
224   meta->format = format;
225   meta->id = 0;
226   meta->width = width;
227   meta->height = height;
228   meta->buffer = buffer;
229
230   meta->n_planes = n_planes;
231   for (i = 0; i < n_planes; i++) {
232     meta->offset[i] = offset[i];
233     meta->stride[i] = stride[i];
234     GST_LOG ("plane %d, offset %u, stride %d", i, offset[i], stride[i]);
235   }
236   meta->map = default_map;
237   meta->unmap = default_unmap;
238
239   return meta;
240 }
241
242 /**
243  * gst_video_meta_map:
244  * @meta: a #GstVideoMeta
245  * @plane: a plane
246  * @info: a #GstMapInfo
247  * @data: the data of @plane
248  * @stride: the stride of @plane
249  * @flags: @GstMapFlags
250  *
251  * Map the video plane with index @plane in @meta and return a pointer to the
252  * first byte of the plane and the stride of the plane.
253  *
254  * Returns: TRUE if the map operation was successful.
255  */
256 gboolean
257 gst_video_meta_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
258     gpointer * data, gint * stride, GstMapFlags flags)
259 {
260   g_return_val_if_fail (meta != NULL, FALSE);
261   g_return_val_if_fail (meta->map != NULL, FALSE);
262   g_return_val_if_fail (plane < meta->n_planes, FALSE);
263   g_return_val_if_fail (info != NULL, FALSE);
264   g_return_val_if_fail (data != NULL, FALSE);
265   g_return_val_if_fail (stride != NULL, FALSE);
266   g_return_val_if_fail (meta->buffer != NULL, FALSE);
267   g_return_val_if_fail (!(flags & GST_MAP_WRITE)
268       || gst_buffer_is_writable (meta->buffer), FALSE);
269
270   return meta->map (meta, plane, info, data, stride, flags);
271 }
272
273 /**
274  * gst_video_meta_unmap:
275  * @meta: a #GstVideoMeta
276  * @plane: a plane
277  * @info: a #GstMapInfo
278  *
279  * Unmap a previously mapped plane with gst_video_meta_map().
280  *
281  * Returns: TRUE if the memory was successfully unmapped.
282  */
283 gboolean
284 gst_video_meta_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
285 {
286   g_return_val_if_fail (meta != NULL, FALSE);
287   g_return_val_if_fail (meta->unmap != NULL, FALSE);
288   g_return_val_if_fail (plane < meta->n_planes, FALSE);
289   g_return_val_if_fail (info != NULL, FALSE);
290
291   return meta->unmap (meta, plane, info);
292 }
293
294 static gboolean
295 gst_video_crop_meta_transform (GstBuffer * dest, GstMeta * meta,
296     GstBuffer * buffer, GQuark type, gpointer data)
297 {
298   GstVideoCropMeta *dmeta, *smeta;
299
300   if (GST_META_TRANSFORM_IS_COPY (type)) {
301     smeta = (GstVideoCropMeta *) meta;
302     dmeta = gst_buffer_add_video_crop_meta (dest);
303
304     GST_DEBUG ("copy crop metadata");
305     dmeta->x = smeta->x;
306     dmeta->y = smeta->y;
307     dmeta->width = smeta->width;
308     dmeta->height = smeta->height;
309   } else if (GST_VIDEO_META_TRANSFORM_IS_SCALE (type)) {
310     GstVideoMetaTransform *trans = data;
311     gint ow, oh, nw, nh;
312
313     smeta = (GstVideoCropMeta *) meta;
314     dmeta = gst_buffer_add_video_crop_meta (dest);
315
316     ow = GST_VIDEO_INFO_WIDTH (trans->in_info);
317     nw = GST_VIDEO_INFO_WIDTH (trans->out_info);
318     oh = GST_VIDEO_INFO_HEIGHT (trans->in_info);
319     nh = GST_VIDEO_INFO_HEIGHT (trans->out_info);
320
321     GST_DEBUG ("scaling crop metadata %dx%d -> %dx%d", ow, oh, nw, nh);
322     dmeta->x = (smeta->x * nw) / ow;
323     dmeta->y = (smeta->y * nh) / oh;
324     dmeta->width = (smeta->width * nw) / ow;
325     dmeta->height = (smeta->height * nh) / oh;
326     GST_DEBUG ("crop offset %dx%d -> %dx%d", smeta->x, smeta->y, dmeta->x,
327         dmeta->y);
328     GST_DEBUG ("crop size   %dx%d -> %dx%d", smeta->width, smeta->height,
329         dmeta->width, dmeta->height);
330   }
331   return TRUE;
332 }
333
334 GType
335 gst_video_crop_meta_api_get_type (void)
336 {
337   static volatile GType type = 0;
338   static const gchar *tags[] = { "size", "orientation", NULL };
339
340   if (g_once_init_enter (&type)) {
341     GType _type = gst_meta_api_type_register ("GstVideoCropMetaAPI", tags);
342     g_once_init_leave (&type, _type);
343   }
344   return type;
345 }
346
347 const GstMetaInfo *
348 gst_video_crop_meta_get_info (void)
349 {
350   static const GstMetaInfo *video_crop_meta_info = NULL;
351
352   if (video_crop_meta_info == NULL) {
353     video_crop_meta_info =
354         gst_meta_register (GST_VIDEO_CROP_META_API_TYPE, "GstVideoCropMeta",
355         sizeof (GstVideoCropMeta), (GstMetaInitFunction) NULL,
356         (GstMetaFreeFunction) NULL, gst_video_crop_meta_transform);
357   }
358   return video_crop_meta_info;
359 }
360
361 /**
362  * gst_video_meta_transform_scale_get_quark:
363  *
364  * Get the #GQuark for the "gst-video-scale" metadata transform operation.
365  *
366  * Returns: a #GQuark
367  */
368 GQuark
369 gst_video_meta_transform_scale_get_quark (void)
370 {
371   static GQuark _value = 0;
372
373   if (_value == 0) {
374     _value = g_quark_from_static_string ("gst-video-scale");
375   }
376   return _value;
377 }