/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/video/gstvideometa.c
Line | Count | Source |
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 | | #include <gst/base/base.h> |
27 | | |
28 | | /** |
29 | | * SECTION:gstvideometa |
30 | | * @title: GstMeta for video |
31 | | * @short_description: Video related GstMeta |
32 | | * |
33 | | */ |
34 | | |
35 | | static gboolean |
36 | | default_map (GstVideoMeta * meta, guint plane, GstMapInfo * info, |
37 | | gpointer * data, gint * stride, GstMapFlags flags); |
38 | | static gboolean |
39 | | default_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info); |
40 | | |
41 | | #ifndef GST_DISABLE_GST_DEBUG |
42 | | #define GST_CAT_DEFAULT ensure_debug_category() |
43 | | static GstDebugCategory * |
44 | | ensure_debug_category (void) |
45 | 0 | { |
46 | 0 | static gsize cat_gonce = 0; |
47 | |
|
48 | 0 | if (g_once_init_enter (&cat_gonce)) { |
49 | 0 | gsize cat_done; |
50 | |
|
51 | 0 | cat_done = (gsize) _gst_debug_category_new ("videometa", 0, "videometa"); |
52 | |
|
53 | 0 | g_once_init_leave (&cat_gonce, cat_done); |
54 | 0 | } |
55 | |
|
56 | 0 | return (GstDebugCategory *) cat_gonce; |
57 | 0 | } |
58 | | #else |
59 | | #define ensure_debug_category() /* NOOP */ |
60 | | #endif /* GST_DISABLE_GST_DEBUG */ |
61 | | |
62 | | static gboolean |
63 | | gst_video_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer) |
64 | 0 | { |
65 | 0 | GstVideoMeta *emeta = (GstVideoMeta *) meta; |
66 | |
|
67 | 0 | emeta->buffer = NULL; |
68 | 0 | emeta->flags = GST_VIDEO_FRAME_FLAG_NONE; |
69 | 0 | emeta->format = GST_VIDEO_FORMAT_UNKNOWN; |
70 | 0 | emeta->id = 0; |
71 | 0 | emeta->width = emeta->height = emeta->n_planes = 0; |
72 | 0 | memset (emeta->offset, 0, sizeof (emeta->offset)); |
73 | 0 | memset (emeta->stride, 0, sizeof (emeta->stride)); |
74 | 0 | gst_video_alignment_reset (&emeta->alignment); |
75 | 0 | emeta->map = NULL; |
76 | 0 | emeta->unmap = NULL; |
77 | |
|
78 | 0 | return TRUE; |
79 | 0 | } |
80 | | |
81 | | static gboolean |
82 | | gst_video_meta_transform (GstBuffer * dest, GstMeta * meta, |
83 | | GstBuffer * buffer, GQuark type, gpointer data) |
84 | 0 | { |
85 | 0 | GstVideoMeta *dmeta, *smeta; |
86 | 0 | guint i; |
87 | |
|
88 | 0 | smeta = (GstVideoMeta *) meta; |
89 | |
|
90 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
91 | 0 | GstMetaTransformCopy *copy = data; |
92 | |
|
93 | 0 | if (!copy->region) { |
94 | | /* only copy if the complete data is copied as well */ |
95 | 0 | dmeta = |
96 | 0 | (GstVideoMeta *) gst_buffer_add_meta (dest, GST_VIDEO_META_INFO, |
97 | 0 | NULL); |
98 | |
|
99 | 0 | if (!dmeta) |
100 | 0 | return FALSE; |
101 | | |
102 | 0 | dmeta->buffer = dest; |
103 | |
|
104 | 0 | GST_DEBUG ("copy video metadata"); |
105 | 0 | dmeta->flags = smeta->flags; |
106 | 0 | dmeta->format = smeta->format; |
107 | 0 | dmeta->id = smeta->id; |
108 | 0 | dmeta->width = smeta->width; |
109 | 0 | dmeta->height = smeta->height; |
110 | |
|
111 | 0 | dmeta->n_planes = smeta->n_planes; |
112 | 0 | for (i = 0; i < dmeta->n_planes; i++) { |
113 | 0 | dmeta->offset[i] = smeta->offset[i]; |
114 | 0 | dmeta->stride[i] = smeta->stride[i]; |
115 | 0 | dmeta->alignment = smeta->alignment; |
116 | 0 | } |
117 | 0 | dmeta->map = smeta->map; |
118 | 0 | dmeta->unmap = smeta->unmap; |
119 | 0 | } |
120 | 0 | } else { |
121 | | /* return FALSE, if transform type is not supported */ |
122 | 0 | return FALSE; |
123 | 0 | } |
124 | 0 | return TRUE; |
125 | 0 | } |
126 | | |
127 | | static gboolean |
128 | | gst_video_meta_api_params_aggregator (GstStructure ** aggregated_params, |
129 | | const GstStructure * params0, const GstStructure * params1) |
130 | 0 | { |
131 | 0 | GstVideoAlignment align0; |
132 | 0 | GstVideoAlignment align1; |
133 | 0 | GstVideoAlignment aggregated_align; |
134 | |
|
135 | 0 | gst_video_alignment_reset (&align0); |
136 | 0 | gst_video_alignment_reset (&align1); |
137 | 0 | gst_video_alignment_reset (&aggregated_align); |
138 | |
|
139 | 0 | if (params0 && (!gst_structure_has_name (params0, "video-meta") || |
140 | 0 | !gst_buffer_pool_config_get_video_alignment (params0, &align0))) { |
141 | 0 | GST_WARNING ("Invalid params"); |
142 | 0 | params0 = NULL; |
143 | 0 | } |
144 | |
|
145 | 0 | if (params1 && (!gst_structure_has_name (params1, "video-meta") || |
146 | 0 | !gst_buffer_pool_config_get_video_alignment (params1, &align1))) { |
147 | 0 | GST_WARNING ("Invalid params"); |
148 | 0 | params1 = NULL; |
149 | 0 | } |
150 | |
|
151 | 0 | if (!params0 && !params1) { |
152 | 0 | *aggregated_params = NULL; |
153 | 0 | return TRUE; |
154 | 0 | } |
155 | | |
156 | 0 | if (params0 && !params1) { |
157 | 0 | *aggregated_params = gst_structure_copy (params0); |
158 | 0 | return TRUE; |
159 | 0 | } |
160 | | |
161 | 0 | if (!params0 && params1) { |
162 | 0 | *aggregated_params = gst_structure_copy (params1); |
163 | 0 | return TRUE; |
164 | 0 | } |
165 | | |
166 | 0 | aggregated_align.padding_top = MAX (align0.padding_top, align1.padding_top); |
167 | |
|
168 | 0 | aggregated_align.padding_bottom = |
169 | 0 | MAX (align0.padding_bottom, align1.padding_bottom); |
170 | |
|
171 | 0 | aggregated_align.padding_left = |
172 | 0 | MAX (align0.padding_left, align1.padding_left); |
173 | |
|
174 | 0 | aggregated_align.padding_right = |
175 | 0 | MAX (align0.padding_right, align1.padding_right); |
176 | |
|
177 | 0 | for (int n = 0; n < GST_VIDEO_MAX_PLANES; ++n) |
178 | 0 | aggregated_align.stride_align[n] = |
179 | 0 | align0.stride_align[n] | align1.stride_align[n]; |
180 | |
|
181 | 0 | *aggregated_params = gst_structure_new_empty ("video-meta"); |
182 | |
|
183 | 0 | gst_buffer_pool_config_set_video_alignment (*aggregated_params, |
184 | 0 | &aggregated_align); |
185 | |
|
186 | 0 | return TRUE; |
187 | 0 | } |
188 | | |
189 | | GType |
190 | | gst_video_meta_api_get_type (void) |
191 | 5 | { |
192 | 5 | static GType type = 0; |
193 | 5 | static const gchar *tags[] = |
194 | 5 | { GST_META_TAG_VIDEO_STR, GST_META_TAG_MEMORY_STR, |
195 | 5 | GST_META_TAG_VIDEO_COLORSPACE_STR, |
196 | 5 | GST_META_TAG_VIDEO_SIZE_STR, NULL |
197 | 5 | }; |
198 | | |
199 | 5 | if (g_once_init_enter (&type)) { |
200 | 5 | GType _type = gst_meta_api_type_register ("GstVideoMetaAPI", tags); |
201 | | |
202 | 5 | gst_meta_api_type_set_params_aggregator (_type, |
203 | 5 | gst_video_meta_api_params_aggregator); |
204 | 5 | g_once_init_leave (&type, _type); |
205 | 5 | } |
206 | 5 | return type; |
207 | 5 | } |
208 | | |
209 | | static gboolean |
210 | | video_meta_serialize (const GstMeta * meta, GstByteArrayInterface * data, |
211 | | guint8 * version) |
212 | 0 | { |
213 | 0 | GstVideoMeta *vmeta = (GstVideoMeta *) meta; |
214 | |
|
215 | 0 | if (vmeta->map != default_map || vmeta->unmap != default_unmap) { |
216 | 0 | GST_WARNING ("Cannot serialize video meta with custom map/unmap functions"); |
217 | 0 | return FALSE; |
218 | 0 | } |
219 | | |
220 | 0 | gsize size = 36 + vmeta->n_planes * 16; |
221 | 0 | guint8 *ptr = gst_byte_array_interface_append (data, size); |
222 | 0 | if (ptr == NULL) |
223 | 0 | return FALSE; |
224 | | |
225 | 0 | GstByteWriter bw; |
226 | 0 | gboolean success GST_UNUSED_ASSERT = TRUE; |
227 | 0 | gst_byte_writer_init_with_data (&bw, ptr, size, FALSE); |
228 | 0 | success &= gst_byte_writer_put_int32_le (&bw, vmeta->flags); |
229 | 0 | success &= gst_byte_writer_put_int32_le (&bw, vmeta->format); |
230 | 0 | success &= gst_byte_writer_put_uint32_le (&bw, vmeta->width); |
231 | 0 | success &= gst_byte_writer_put_uint32_le (&bw, vmeta->height); |
232 | 0 | success &= gst_byte_writer_put_uint32_le (&bw, vmeta->n_planes); |
233 | 0 | for (int n = 0; n < vmeta->n_planes; n++) |
234 | 0 | success &= gst_byte_writer_put_uint64_le (&bw, vmeta->offset[n]); |
235 | 0 | for (int n = 0; n < vmeta->n_planes; n++) |
236 | 0 | success &= gst_byte_writer_put_int32_le (&bw, vmeta->stride[n]); |
237 | 0 | success &= gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_top); |
238 | 0 | success &= |
239 | 0 | gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_bottom); |
240 | 0 | success &= gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_left); |
241 | 0 | success &= |
242 | 0 | gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.padding_right); |
243 | 0 | for (int n = 0; n < vmeta->n_planes; n++) |
244 | 0 | success &= |
245 | 0 | gst_byte_writer_put_uint32_le (&bw, vmeta->alignment.stride_align[n]); |
246 | 0 | g_assert (success); |
247 | | |
248 | 0 | return TRUE; |
249 | 0 | } |
250 | | |
251 | | static GstMeta * |
252 | | video_meta_deserialize (const GstMetaInfo * info, GstBuffer * buffer, |
253 | | const guint8 * data, gsize size, guint8 version) |
254 | 0 | { |
255 | 0 | GstVideoMeta *vmeta = NULL; |
256 | 0 | gint32 flags; |
257 | 0 | gint32 format; |
258 | 0 | guint width; |
259 | 0 | guint height; |
260 | 0 | guint n_planes; |
261 | 0 | GstVideoAlignment align; |
262 | 0 | guint64 offset64[GST_VIDEO_MAX_PLANES]; |
263 | 0 | gint32 stride[GST_VIDEO_MAX_PLANES]; |
264 | |
|
265 | 0 | if (version != 0) |
266 | 0 | return NULL; |
267 | | |
268 | 0 | gst_video_alignment_reset (&align); |
269 | |
|
270 | 0 | GstByteReader br; |
271 | 0 | gboolean success = TRUE; |
272 | 0 | gst_byte_reader_init (&br, data, size); |
273 | 0 | success &= gst_byte_reader_get_int32_le (&br, &flags); |
274 | 0 | success &= gst_byte_reader_get_int32_le (&br, &format); |
275 | 0 | success &= gst_byte_reader_get_uint32_le (&br, &width); |
276 | 0 | success &= gst_byte_reader_get_uint32_le (&br, &height); |
277 | 0 | success &= gst_byte_reader_get_uint32_le (&br, &n_planes); |
278 | |
|
279 | 0 | if (!success || n_planes > GST_VIDEO_MAX_PLANES) |
280 | 0 | return NULL; |
281 | | |
282 | 0 | for (int n = 0; n < n_planes; n++) |
283 | 0 | success &= gst_byte_reader_get_uint64_le (&br, &offset64[n]); |
284 | 0 | for (int n = 0; n < n_planes; n++) |
285 | 0 | success &= gst_byte_reader_get_int32_le (&br, &stride[n]); |
286 | 0 | success &= gst_byte_reader_get_uint32_le (&br, &align.padding_top); |
287 | 0 | success &= gst_byte_reader_get_uint32_le (&br, &align.padding_bottom); |
288 | 0 | success &= gst_byte_reader_get_uint32_le (&br, &align.padding_left); |
289 | 0 | success &= gst_byte_reader_get_uint32_le (&br, &align.padding_right); |
290 | 0 | for (int n = 0; n < n_planes; n++) |
291 | 0 | success &= gst_byte_reader_get_uint32_le (&br, &align.stride_align[n]); |
292 | |
|
293 | 0 | if (!success) |
294 | 0 | return NULL; |
295 | | |
296 | | #if GLIB_SIZEOF_SIZE_T != 8 |
297 | | gsize offset[GST_VIDEO_MAX_PLANES]; |
298 | | for (int i = 0; i < n_planes; i++) { |
299 | | if (offset64[i] > G_MAXSIZE) |
300 | | return NULL; |
301 | | offset[i] = offset64[i]; |
302 | | } |
303 | | #else |
304 | 0 | gsize *offset = (gsize *) offset64; |
305 | 0 | #endif |
306 | |
|
307 | 0 | vmeta = |
308 | 0 | gst_buffer_add_video_meta_full (buffer, flags, format, width, height, |
309 | 0 | n_planes, offset, stride); |
310 | 0 | gst_video_meta_set_alignment (vmeta, align); |
311 | |
|
312 | 0 | return (GstMeta *) vmeta; |
313 | 0 | } |
314 | | |
315 | | /* video metadata */ |
316 | | const GstMetaInfo * |
317 | | gst_video_meta_get_info (void) |
318 | 5 | { |
319 | 5 | static const GstMetaInfo *video_meta_info = NULL; |
320 | | |
321 | 5 | if (g_once_init_enter ((GstMetaInfo **) & video_meta_info)) { |
322 | 5 | GstMetaInfo *info = gst_meta_info_new (GST_VIDEO_META_API_TYPE, |
323 | 5 | "GstVideoMeta", |
324 | 5 | sizeof (GstVideoMeta)); |
325 | 5 | info->init_func = gst_video_meta_init; |
326 | 5 | info->transform_func = gst_video_meta_transform; |
327 | 5 | info->serialize_func = video_meta_serialize; |
328 | 5 | info->deserialize_func = video_meta_deserialize; |
329 | 5 | const GstMetaInfo *meta = gst_meta_info_register (info); |
330 | 5 | g_once_init_leave ((GstMetaInfo **) & video_meta_info, |
331 | 5 | (GstMetaInfo *) meta); |
332 | 5 | } |
333 | 5 | return video_meta_info; |
334 | 5 | } |
335 | | |
336 | | /** |
337 | | * gst_buffer_get_video_meta: |
338 | | * @buffer: a #GstBuffer |
339 | | * |
340 | | * Find the #GstVideoMeta on @buffer with the lowest @id. |
341 | | * |
342 | | * Buffers can contain multiple #GstVideoMeta metadata items when dealing with |
343 | | * multiview buffers. |
344 | | * |
345 | | * Returns: (transfer none) (nullable): the #GstVideoMeta with lowest id (usually 0) or %NULL when there |
346 | | * is no such metadata on @buffer. |
347 | | */ |
348 | | GstVideoMeta * |
349 | | gst_buffer_get_video_meta (GstBuffer * buffer) |
350 | 0 | { |
351 | 0 | gpointer state = NULL; |
352 | 0 | GstVideoMeta *out = NULL; |
353 | 0 | GstMeta *meta; |
354 | 0 | const GstMetaInfo *info = GST_VIDEO_META_INFO; |
355 | |
|
356 | 0 | while ((meta = gst_buffer_iterate_meta (buffer, &state))) { |
357 | 0 | if (meta->info->api == info->api) { |
358 | 0 | GstVideoMeta *vmeta = (GstVideoMeta *) meta; |
359 | 0 | if (vmeta->id == 0) |
360 | 0 | return vmeta; /* Early out for id 0 */ |
361 | 0 | if (out == NULL || vmeta->id < out->id) |
362 | 0 | out = vmeta; |
363 | 0 | } |
364 | 0 | } |
365 | 0 | return out; |
366 | 0 | } |
367 | | |
368 | | /** |
369 | | * gst_buffer_get_video_meta_id: |
370 | | * @buffer: a #GstBuffer |
371 | | * @id: a metadata id |
372 | | * |
373 | | * Find the #GstVideoMeta on @buffer with the given @id. |
374 | | * |
375 | | * Buffers can contain multiple #GstVideoMeta metadata items when dealing with |
376 | | * multiview buffers. |
377 | | * |
378 | | * Returns: (transfer none) (nullable): the #GstVideoMeta with @id or %NULL when there is no such metadata |
379 | | * on @buffer. |
380 | | */ |
381 | | GstVideoMeta * |
382 | | gst_buffer_get_video_meta_id (GstBuffer * buffer, gint id) |
383 | 0 | { |
384 | 0 | gpointer state = NULL; |
385 | 0 | GstMeta *meta; |
386 | 0 | const GstMetaInfo *info = GST_VIDEO_META_INFO; |
387 | |
|
388 | 0 | while ((meta = gst_buffer_iterate_meta (buffer, &state))) { |
389 | 0 | if (meta->info->api == info->api) { |
390 | 0 | GstVideoMeta *vmeta = (GstVideoMeta *) meta; |
391 | 0 | if (vmeta->id == id) |
392 | 0 | return vmeta; |
393 | 0 | } |
394 | 0 | } |
395 | 0 | return NULL; |
396 | 0 | } |
397 | | |
398 | | static gboolean |
399 | | default_map (GstVideoMeta * meta, guint plane, GstMapInfo * info, |
400 | | gpointer * data, gint * stride, GstMapFlags flags) |
401 | 0 | { |
402 | 0 | guint idx, length; |
403 | 0 | gsize offset, skip; |
404 | 0 | GstBuffer *buffer = meta->buffer; |
405 | |
|
406 | 0 | offset = meta->offset[plane]; |
407 | | |
408 | | /* find the memory block for this plane, this is the memory block containing |
409 | | * the plane offset. FIXME use plane size */ |
410 | 0 | if (!gst_buffer_find_memory (buffer, offset, 1, &idx, &length, &skip)) |
411 | 0 | goto no_memory; |
412 | | |
413 | 0 | if (!gst_buffer_map_range (buffer, idx, length, info, flags)) |
414 | 0 | goto cannot_map; |
415 | | |
416 | 0 | *stride = meta->stride[plane]; |
417 | 0 | *data = (guint8 *) info->data + skip; |
418 | |
|
419 | 0 | return TRUE; |
420 | | |
421 | | /* ERRORS */ |
422 | 0 | no_memory: |
423 | 0 | { |
424 | 0 | GST_ERROR ("plane %u, no memory at offset %" G_GSIZE_FORMAT, plane, offset); |
425 | 0 | return FALSE; |
426 | 0 | } |
427 | 0 | cannot_map: |
428 | 0 | { |
429 | 0 | GST_ERROR ("cannot map memory range %u-%u", idx, length); |
430 | 0 | return FALSE; |
431 | 0 | } |
432 | 0 | } |
433 | | |
434 | | static gboolean |
435 | | default_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info) |
436 | 0 | { |
437 | 0 | GstBuffer *buffer = meta->buffer; |
438 | |
|
439 | 0 | gst_buffer_unmap (buffer, info); |
440 | |
|
441 | 0 | return TRUE; |
442 | 0 | } |
443 | | |
444 | | /** |
445 | | * gst_buffer_add_video_meta: |
446 | | * @buffer: a #GstBuffer |
447 | | * @flags: #GstVideoFrameFlags |
448 | | * @format: a #GstVideoFormat |
449 | | * @width: the width |
450 | | * @height: the height |
451 | | * |
452 | | * Attaches GstVideoMeta metadata to @buffer with the given parameters and the |
453 | | * default offsets and strides for @format and @width x @height. |
454 | | * |
455 | | * This function calculates the default offsets and strides and then calls |
456 | | * gst_buffer_add_video_meta_full() with them. |
457 | | * |
458 | | * Returns: (transfer none): the #GstVideoMeta on @buffer. |
459 | | */ |
460 | | GstVideoMeta * |
461 | | gst_buffer_add_video_meta (GstBuffer * buffer, |
462 | | GstVideoFrameFlags flags, GstVideoFormat format, guint width, guint height) |
463 | 0 | { |
464 | 0 | GstVideoMeta *meta; |
465 | 0 | GstVideoInfo info; |
466 | |
|
467 | 0 | if (!gst_video_info_set_format (&info, format, width, height)) |
468 | 0 | return NULL; |
469 | | |
470 | 0 | meta = |
471 | 0 | gst_buffer_add_video_meta_full (buffer, flags, format, width, |
472 | 0 | height, info.finfo->n_planes, info.offset, info.stride); |
473 | |
|
474 | 0 | return meta; |
475 | 0 | } |
476 | | |
477 | | /** |
478 | | * gst_buffer_add_video_meta_full: |
479 | | * @buffer: a #GstBuffer |
480 | | * @flags: #GstVideoFrameFlags |
481 | | * @format: a #GstVideoFormat |
482 | | * @width: the width |
483 | | * @height: the height |
484 | | * @n_planes: number of planes |
485 | | * @offset: (array fixed-size=4): offset of each plane |
486 | | * @stride: (array fixed-size=4): stride of each plane |
487 | | * |
488 | | * Attaches GstVideoMeta metadata to @buffer with the given parameters. |
489 | | * |
490 | | * Returns: (transfer none): the #GstVideoMeta on @buffer. |
491 | | */ |
492 | | GstVideoMeta * |
493 | | gst_buffer_add_video_meta_full (GstBuffer * buffer, |
494 | | GstVideoFrameFlags flags, GstVideoFormat format, guint width, |
495 | | guint height, guint n_planes, const gsize offset[GST_VIDEO_MAX_PLANES], |
496 | | const gint stride[GST_VIDEO_MAX_PLANES]) |
497 | 0 | { |
498 | 0 | GstVideoMeta *meta; |
499 | 0 | guint i; |
500 | |
|
501 | 0 | meta = |
502 | 0 | (GstVideoMeta *) gst_buffer_add_meta (buffer, GST_VIDEO_META_INFO, NULL); |
503 | |
|
504 | 0 | if (!meta) |
505 | 0 | return NULL; |
506 | | |
507 | 0 | meta->flags = flags; |
508 | 0 | meta->format = format; |
509 | 0 | meta->id = 0; |
510 | 0 | meta->width = width; |
511 | 0 | meta->height = height; |
512 | 0 | meta->buffer = buffer; |
513 | |
|
514 | 0 | meta->n_planes = n_planes; |
515 | 0 | for (i = 0; i < n_planes; i++) { |
516 | 0 | meta->offset[i] = offset[i]; |
517 | 0 | meta->stride[i] = stride[i]; |
518 | 0 | GST_LOG ("plane %d, offset %" G_GSIZE_FORMAT ", stride %d", i, offset[i], |
519 | 0 | stride[i]); |
520 | 0 | } |
521 | 0 | meta->map = default_map; |
522 | 0 | meta->unmap = default_unmap; |
523 | |
|
524 | 0 | return meta; |
525 | 0 | } |
526 | | |
527 | | /** |
528 | | * gst_video_meta_map: |
529 | | * @meta: a #GstVideoMeta |
530 | | * @plane: a plane |
531 | | * @info: a #GstMapInfo |
532 | | * @data: (out): the data of @plane |
533 | | * @stride: (out): the stride of @plane |
534 | | * @flags: @GstMapFlags |
535 | | * |
536 | | * Map the video plane with index @plane in @meta and return a pointer to the |
537 | | * first byte of the plane and the stride of the plane. |
538 | | * |
539 | | * Returns: TRUE if the map operation was successful. |
540 | | */ |
541 | | gboolean |
542 | | gst_video_meta_map (GstVideoMeta * meta, guint plane, GstMapInfo * info, |
543 | | gpointer * data, gint * stride, GstMapFlags flags) |
544 | 0 | { |
545 | 0 | g_return_val_if_fail (meta != NULL, FALSE); |
546 | 0 | g_return_val_if_fail (meta->map != NULL, FALSE); |
547 | 0 | g_return_val_if_fail (plane < meta->n_planes, FALSE); |
548 | 0 | g_return_val_if_fail (info != NULL, FALSE); |
549 | 0 | g_return_val_if_fail (data != NULL, FALSE); |
550 | 0 | g_return_val_if_fail (stride != NULL, FALSE); |
551 | 0 | g_return_val_if_fail (meta->buffer != NULL, FALSE); |
552 | 0 | g_return_val_if_fail (!(flags & GST_MAP_WRITE) |
553 | 0 | || gst_buffer_is_writable (meta->buffer), FALSE); |
554 | | |
555 | 0 | return meta->map (meta, plane, info, data, stride, flags); |
556 | 0 | } |
557 | | |
558 | | /** |
559 | | * gst_video_meta_unmap: |
560 | | * @meta: a #GstVideoMeta |
561 | | * @plane: a plane |
562 | | * @info: a #GstMapInfo |
563 | | * |
564 | | * Unmap a previously mapped plane with gst_video_meta_map(). |
565 | | * |
566 | | * Returns: TRUE if the memory was successfully unmapped. |
567 | | */ |
568 | | gboolean |
569 | | gst_video_meta_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info) |
570 | 0 | { |
571 | 0 | g_return_val_if_fail (meta != NULL, FALSE); |
572 | 0 | g_return_val_if_fail (meta->unmap != NULL, FALSE); |
573 | 0 | g_return_val_if_fail (plane < meta->n_planes, FALSE); |
574 | 0 | g_return_val_if_fail (info != NULL, FALSE); |
575 | | |
576 | 0 | return meta->unmap (meta, plane, info); |
577 | 0 | } |
578 | | |
579 | | static gboolean |
580 | | gst_video_meta_is_alignment_valid (GstVideoAlignment * align) |
581 | 0 | { |
582 | 0 | gint i; |
583 | |
|
584 | 0 | g_return_val_if_fail (align != NULL, FALSE); |
585 | | |
586 | 0 | if (align->padding_top != 0 || align->padding_bottom != 0 || |
587 | 0 | align->padding_left != 0 || align->padding_right != 0) |
588 | 0 | return TRUE; |
589 | | |
590 | 0 | for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { |
591 | 0 | if (align->stride_align[i] != 0) |
592 | 0 | return TRUE; |
593 | 0 | } |
594 | | |
595 | 0 | return FALSE; |
596 | 0 | } |
597 | | |
598 | | static gboolean |
599 | | gst_video_meta_validate_alignment (GstVideoMeta * meta, |
600 | | gsize plane_size[GST_VIDEO_MAX_PLANES]) |
601 | 0 | { |
602 | 0 | GstVideoInfo info; |
603 | 0 | guint i; |
604 | |
|
605 | 0 | if (!gst_video_meta_is_alignment_valid (&meta->alignment)) { |
606 | 0 | GST_LOG ("Set alignment on meta to all zero"); |
607 | | |
608 | | /* When alignment is invalid, no further check is needed, |
609 | | unless user wants to calculate the pitch for each plane. */ |
610 | 0 | if (!plane_size) |
611 | 0 | return TRUE; |
612 | 0 | } |
613 | | |
614 | 0 | gst_video_info_init (&info); |
615 | 0 | gst_video_info_set_format (&info, meta->format, meta->width, meta->height); |
616 | |
|
617 | 0 | if (!gst_video_info_align_full (&info, &meta->alignment, plane_size)) { |
618 | 0 | GST_WARNING ("Failed to align meta with its alignment"); |
619 | 0 | return FALSE; |
620 | 0 | } |
621 | | |
622 | 0 | for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&info); i++) { |
623 | 0 | if (GST_VIDEO_INFO_PLANE_STRIDE (&info, i) != meta->stride[i]) { |
624 | 0 | GST_WARNING |
625 | 0 | ("Stride of plane %d defined in meta (%d) is different from the one computed from the alignment (%d)", |
626 | 0 | i, meta->stride[i], GST_VIDEO_INFO_PLANE_STRIDE (&info, i)); |
627 | 0 | return FALSE; |
628 | 0 | } |
629 | 0 | } |
630 | | |
631 | 0 | return TRUE; |
632 | 0 | } |
633 | | |
634 | | /** |
635 | | * gst_video_meta_set_alignment: |
636 | | * @meta: a #GstVideoMeta |
637 | | * @alignment: a #GstVideoAlignment |
638 | | * |
639 | | * Set the alignment of @meta to @alignment. This function checks that |
640 | | * the paddings defined in @alignment are compatible with the strides |
641 | | * defined in @meta and will fail to update if they are not. |
642 | | * |
643 | | * Returns: %TRUE if @alignment's meta has been updated, %FALSE if not |
644 | | * |
645 | | * Since: 1.18 |
646 | | * Deprecated: 1.28: Use gst_video_meta_set_alignment_full() instead |
647 | | */ |
648 | | gboolean |
649 | | gst_video_meta_set_alignment (GstVideoMeta * meta, GstVideoAlignment alignment) |
650 | 0 | { |
651 | 0 | return gst_video_meta_set_alignment_full (meta, &alignment); |
652 | 0 | } |
653 | | |
654 | | /** |
655 | | * gst_video_meta_set_alignment_full: |
656 | | * @meta: a #GstVideoMeta |
657 | | * @alignment: a #GstVideoAlignment |
658 | | * |
659 | | * Set the alignment of @meta to @alignment. This function checks that |
660 | | * the paddings defined in @alignment are compatible with the strides |
661 | | * defined in @meta and will fail to update if they are not. |
662 | | * |
663 | | * Returns: %TRUE if @alignment's meta has been updated, %FALSE if not |
664 | | * |
665 | | * Since: 1.28 |
666 | | */ |
667 | | gboolean |
668 | | gst_video_meta_set_alignment_full (GstVideoMeta * meta, |
669 | | const GstVideoAlignment * alignment) |
670 | 0 | { |
671 | 0 | GstVideoAlignment old; |
672 | |
|
673 | 0 | g_return_val_if_fail (meta, FALSE); |
674 | | |
675 | 0 | old = meta->alignment; |
676 | 0 | meta->alignment = *alignment; |
677 | |
|
678 | 0 | if (!gst_video_meta_validate_alignment (meta, NULL)) { |
679 | | /* Invalid alignment, restore the previous one */ |
680 | 0 | meta->alignment = old; |
681 | 0 | return FALSE; |
682 | 0 | } |
683 | | |
684 | 0 | GST_LOG ("Set alignment on meta: padding %u-%ux%u-%u", alignment->padding_top, |
685 | 0 | alignment->padding_left, alignment->padding_right, |
686 | 0 | alignment->padding_bottom); |
687 | |
|
688 | 0 | return TRUE; |
689 | 0 | } |
690 | | |
691 | | /** |
692 | | * gst_video_meta_get_plane_size: |
693 | | * @meta: a #GstVideoMeta |
694 | | * @plane_size: (out caller-allocates) (array fixed-size=4): array used to store the plane sizes |
695 | | * |
696 | | * Compute the size, in bytes, of each video plane described in @meta including |
697 | | * any padding and alignment constraint defined in @meta->alignment. |
698 | | * |
699 | | * Returns: %TRUE if @meta's alignment is valid and @plane_size has been |
700 | | * updated, %FALSE otherwise |
701 | | * |
702 | | * Since: 1.18 |
703 | | */ |
704 | | gboolean |
705 | | gst_video_meta_get_plane_size (GstVideoMeta * meta, |
706 | | gsize plane_size[GST_VIDEO_MAX_PLANES]) |
707 | 0 | { |
708 | 0 | g_return_val_if_fail (meta, FALSE); |
709 | 0 | g_return_val_if_fail (plane_size, FALSE); |
710 | | |
711 | 0 | return gst_video_meta_validate_alignment (meta, plane_size); |
712 | 0 | } |
713 | | |
714 | | /** |
715 | | * gst_video_meta_get_plane_height: |
716 | | * @meta: a #GstVideoMeta |
717 | | * @plane_height: (out caller-allocates) (array fixed-size=4): array used to store the plane height |
718 | | * |
719 | | * Compute the padded height of each plane from @meta (padded size |
720 | | * divided by stride). |
721 | | * |
722 | | * It is not valid to call this function with a meta associated to a |
723 | | * TILED video format. |
724 | | * |
725 | | * Returns: %TRUE if @meta's alignment is valid and @plane_height has been |
726 | | * updated, %FALSE otherwise |
727 | | * |
728 | | * Since: 1.18 |
729 | | */ |
730 | | gboolean |
731 | | gst_video_meta_get_plane_height (GstVideoMeta * meta, |
732 | | guint plane_height[GST_VIDEO_MAX_PLANES]) |
733 | 0 | { |
734 | 0 | gsize plane_size[GST_VIDEO_MAX_PLANES]; |
735 | 0 | guint i; |
736 | 0 | GstVideoInfo info; |
737 | |
|
738 | 0 | g_return_val_if_fail (meta, FALSE); |
739 | 0 | g_return_val_if_fail (plane_height, FALSE); |
740 | | |
741 | 0 | gst_video_info_init (&info); |
742 | 0 | gst_video_info_set_format (&info, meta->format, meta->width, meta->height); |
743 | 0 | g_return_val_if_fail (!GST_VIDEO_FORMAT_INFO_IS_TILED (&info), FALSE); |
744 | | |
745 | 0 | if (!gst_video_meta_get_plane_size (meta, plane_size)) |
746 | 0 | return FALSE; |
747 | | |
748 | 0 | for (i = 0; i < meta->n_planes; i++) { |
749 | 0 | if (!meta->stride[i]) |
750 | 0 | plane_height[i] = 0; |
751 | 0 | else |
752 | 0 | plane_height[i] = plane_size[i] / meta->stride[i]; |
753 | 0 | } |
754 | |
|
755 | 0 | for (; i < GST_VIDEO_MAX_PLANES; i++) |
756 | 0 | plane_height[i] = 0; |
757 | |
|
758 | 0 | return TRUE; |
759 | 0 | } |
760 | | |
761 | | static gboolean |
762 | | gst_video_crop_meta_transform (GstBuffer * dest, GstMeta * meta, |
763 | | GstBuffer * buffer, GQuark type, gpointer data) |
764 | 0 | { |
765 | 0 | GstVideoCropMeta *dmeta, *smeta; |
766 | |
|
767 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
768 | 0 | smeta = (GstVideoCropMeta *) meta; |
769 | 0 | dmeta = gst_buffer_add_video_crop_meta (dest); |
770 | 0 | if (!dmeta) |
771 | 0 | return FALSE; |
772 | | |
773 | 0 | GST_DEBUG ("copy crop metadata"); |
774 | 0 | dmeta->x = smeta->x; |
775 | 0 | dmeta->y = smeta->y; |
776 | 0 | dmeta->width = smeta->width; |
777 | 0 | dmeta->height = smeta->height; |
778 | 0 | } else if (GST_VIDEO_META_TRANSFORM_IS_SCALE (type)) { |
779 | 0 | GstVideoMetaTransform *trans = data; |
780 | 0 | gint ow, oh, nw, nh; |
781 | |
|
782 | 0 | smeta = (GstVideoCropMeta *) meta; |
783 | 0 | dmeta = gst_buffer_add_video_crop_meta (dest); |
784 | 0 | if (!dmeta) |
785 | 0 | return FALSE; |
786 | | |
787 | 0 | ow = GST_VIDEO_INFO_WIDTH (trans->in_info); |
788 | 0 | nw = GST_VIDEO_INFO_WIDTH (trans->out_info); |
789 | 0 | oh = GST_VIDEO_INFO_HEIGHT (trans->in_info); |
790 | 0 | nh = GST_VIDEO_INFO_HEIGHT (trans->out_info); |
791 | |
|
792 | 0 | GST_DEBUG ("scaling crop metadata %dx%d -> %dx%d", ow, oh, nw, nh); |
793 | 0 | dmeta->x = (smeta->x * nw) / ow; |
794 | 0 | dmeta->y = (smeta->y * nh) / oh; |
795 | 0 | dmeta->width = (smeta->width * nw) / ow; |
796 | 0 | dmeta->height = (smeta->height * nh) / oh; |
797 | 0 | GST_DEBUG ("crop offset %dx%d -> %dx%d", smeta->x, smeta->y, dmeta->x, |
798 | 0 | dmeta->y); |
799 | 0 | GST_DEBUG ("crop size %dx%d -> %dx%d", smeta->width, smeta->height, |
800 | 0 | dmeta->width, dmeta->height); |
801 | 0 | } else { |
802 | | /* return FALSE, if transform type is not supported */ |
803 | 0 | return FALSE; |
804 | 0 | } |
805 | 0 | return TRUE; |
806 | 0 | } |
807 | | |
808 | | GType |
809 | | gst_video_crop_meta_api_get_type (void) |
810 | 0 | { |
811 | 0 | static GType type = 0; |
812 | 0 | static const gchar *tags[] = |
813 | 0 | { GST_META_TAG_VIDEO_STR, GST_META_TAG_VIDEO_SIZE_STR, |
814 | 0 | GST_META_TAG_VIDEO_ORIENTATION_STR, NULL |
815 | 0 | }; |
816 | |
|
817 | 0 | if (g_once_init_enter (&type)) { |
818 | 0 | GType _type = gst_meta_api_type_register ("GstVideoCropMetaAPI", tags); |
819 | 0 | g_once_init_leave (&type, _type); |
820 | 0 | } |
821 | 0 | return type; |
822 | 0 | } |
823 | | |
824 | | static gboolean |
825 | | gst_video_crop_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer) |
826 | 0 | { |
827 | 0 | GstVideoCropMeta *emeta = (GstVideoCropMeta *) meta; |
828 | 0 | emeta->x = emeta->y = emeta->width = emeta->height = 0; |
829 | |
|
830 | 0 | return TRUE; |
831 | 0 | } |
832 | | |
833 | | const GstMetaInfo * |
834 | | gst_video_crop_meta_get_info (void) |
835 | 0 | { |
836 | 0 | static const GstMetaInfo *video_crop_meta_info = NULL; |
837 | |
|
838 | 0 | if (g_once_init_enter ((GstMetaInfo **) & video_crop_meta_info)) { |
839 | 0 | const GstMetaInfo *meta = |
840 | 0 | gst_meta_register (GST_VIDEO_CROP_META_API_TYPE, "GstVideoCropMeta", |
841 | 0 | sizeof (GstVideoCropMeta), |
842 | 0 | (GstMetaInitFunction) gst_video_crop_meta_init, |
843 | 0 | (GstMetaFreeFunction) NULL, gst_video_crop_meta_transform); |
844 | 0 | g_once_init_leave ((GstMetaInfo **) & video_crop_meta_info, |
845 | 0 | (GstMetaInfo *) meta); |
846 | 0 | } |
847 | 0 | return video_crop_meta_info; |
848 | 0 | } |
849 | | |
850 | | /** |
851 | | * gst_video_meta_transform_scale_get_quark: |
852 | | * |
853 | | * Get the #GQuark for the "gst-video-scale" metadata transform operation. |
854 | | * |
855 | | * Returns: a #GQuark |
856 | | */ |
857 | | GQuark |
858 | | gst_video_meta_transform_scale_get_quark (void) |
859 | 5 | { |
860 | 5 | static GQuark _value = 0; |
861 | | |
862 | 5 | if (_value == 0) { |
863 | 5 | _value = g_quark_from_static_string ("gst-video-scale"); |
864 | 5 | } |
865 | 5 | return _value; |
866 | 5 | } |
867 | | |
868 | | /** |
869 | | * gst_video_meta_transform_matrix_get_quark: |
870 | | * |
871 | | * Get the #GQuark for the "gst-video-matrix" metadata transform operation. |
872 | | * |
873 | | * Returns: a #GQuark |
874 | | * |
875 | | * Since: 1.28 |
876 | | */ |
877 | | GQuark |
878 | | gst_video_meta_transform_matrix_get_quark (void) |
879 | 5 | { |
880 | 5 | static GQuark _value = 0; |
881 | | |
882 | 5 | if (_value == 0) { |
883 | 5 | _value = g_quark_from_static_string ("gst-video-matrix"); |
884 | 5 | } |
885 | 5 | return _value; |
886 | 5 | } |
887 | | |
888 | | /** |
889 | | * gst_video_meta_transform_matrix_init: |
890 | | * @trans: a #GstVideoMetaTransformMatrix with in_rectangle and out_rectangle |
891 | | * filled |
892 | | * @in_info: (in): The #GstVideoInfo of the input image |
893 | | * @in_rectangle: the input #GstVideoRectangle |
894 | | * @out_info: (in): the output #GstVideoInfo |
895 | | * @out_rectangle: the output #GstVideoRectangle |
896 | | * |
897 | | * Based on the rectangles, initializes the matrix to do a translation and |
898 | | * scaling from @in_rectangle to @out_rectangle |
899 | | * |
900 | | * Since: 1.28 |
901 | | */ |
902 | | void |
903 | | gst_video_meta_transform_matrix_init (GstVideoMetaTransformMatrix * trans, |
904 | | const GstVideoInfo * in_info, const GstVideoRectangle * in_rectangle, |
905 | | const GstVideoInfo * out_info, const GstVideoRectangle * out_rectangle) |
906 | 0 | { |
907 | 0 | g_return_if_fail (in_info != NULL); |
908 | 0 | g_return_if_fail (out_info != NULL); |
909 | 0 | g_return_if_fail (in_rectangle->w > 0); |
910 | 0 | g_return_if_fail (in_rectangle->h > 0); |
911 | 0 | g_return_if_fail (out_rectangle->w > 0); |
912 | 0 | g_return_if_fail (out_rectangle->h > 0); |
913 | | |
914 | 0 | trans->in_info = in_info; |
915 | 0 | trans->out_info = out_info; |
916 | 0 | trans->in_rectangle = *in_rectangle; |
917 | 0 | trans->out_rectangle = *out_rectangle; |
918 | |
|
919 | 0 | memset (trans->matrix, 0, sizeof (gfloat) * 9); |
920 | |
|
921 | 0 | trans->matrix[0][0] = (gfloat) trans->out_rectangle.w / |
922 | 0 | (gfloat) trans->in_rectangle.w; |
923 | 0 | trans->matrix[1][1] = (gfloat) trans->out_rectangle.h / |
924 | 0 | (gfloat) trans->in_rectangle.h; |
925 | 0 | trans->matrix[2][2] = 1; |
926 | 0 | } |
927 | | |
928 | | static gboolean |
929 | | _gst_video_meta_transform_matrix_point (const GstVideoMetaTransformMatrix * |
930 | | transform, gint * x, gint * y, gboolean clip) |
931 | 0 | { |
932 | 0 | gboolean ret = TRUE; |
933 | 0 | gdouble x_in = *x - transform->in_rectangle.x; |
934 | 0 | gdouble y_in = *y - transform->in_rectangle.y; |
935 | 0 | gdouble x_temp, y_temp; |
936 | 0 | gdouble w_prime = transform->matrix[2][0] * x_in + |
937 | 0 | transform->matrix[2][1] * y_in + transform->matrix[2][2]; |
938 | |
|
939 | 0 | if (w_prime == 0.0f) { |
940 | 0 | *x = transform->out_rectangle.x; |
941 | 0 | *y = transform->out_rectangle.y; |
942 | 0 | g_return_val_if_fail (w_prime != 0.0, FALSE); |
943 | 0 | } |
944 | | |
945 | 0 | if (w_prime == 1.0f) { |
946 | 0 | x_temp = transform->matrix[0][0] * x_in + |
947 | 0 | transform->matrix[0][1] * y_in + transform->matrix[0][2]; |
948 | 0 | y_temp = transform->matrix[1][0] * x_in + |
949 | 0 | transform->matrix[1][1] * y_in + transform->matrix[1][2]; |
950 | 0 | } else { |
951 | 0 | x_temp = (transform->matrix[0][0] * x_in + |
952 | 0 | transform->matrix[0][1] * y_in + transform->matrix[0][2]) / w_prime; |
953 | 0 | y_temp = (transform->matrix[1][0] * x_in + |
954 | 0 | transform->matrix[1][1] * y_in + transform->matrix[1][2]) / w_prime; |
955 | 0 | } |
956 | |
|
957 | 0 | *x = x_temp + 0.5f + transform->out_rectangle.x; |
958 | 0 | *y = y_temp + 0.5f + transform->out_rectangle.y; |
959 | |
|
960 | 0 | if (clip) { |
961 | 0 | if (*x < transform->out_rectangle.x) { |
962 | 0 | *x = transform->out_rectangle.x; |
963 | 0 | ret = FALSE; |
964 | 0 | } |
965 | |
|
966 | 0 | if (*x >= transform->out_rectangle.x + transform->out_rectangle.w) { |
967 | 0 | *x = transform->out_rectangle.x + transform->out_rectangle.w - 1; |
968 | 0 | ret = FALSE; |
969 | 0 | } |
970 | |
|
971 | 0 | if (*y < transform->out_rectangle.y) { |
972 | 0 | *y = transform->out_rectangle.y; |
973 | 0 | ret = FALSE; |
974 | 0 | } |
975 | |
|
976 | 0 | if (*y >= transform->out_rectangle.y + transform->out_rectangle.h) { |
977 | 0 | *y = transform->out_rectangle.y + transform->out_rectangle.h - 1; |
978 | 0 | ret = FALSE; |
979 | 0 | } |
980 | 0 | } |
981 | |
|
982 | 0 | return ret; |
983 | 0 | } |
984 | | |
985 | | /** |
986 | | * gst_video_meta_transform_matrix_point: |
987 | | * @transform: a #GstVideoMetaTransformMatrix |
988 | | * @x: (inout): a non-NULL pointer to the X value of the coordinate |
989 | | * @y: (inout): a non-NULL pointer to the Y value of the coordinate |
990 | | * |
991 | | * Transforms the (@x, @y) point from the input coordinates to the |
992 | | * output ones. The point's coordinates are transformed by first |
993 | | * applying the @transform.matrix to it using the top left (x, y) of |
994 | | * @transform.in_rectangle as the origin, then translate it to use |
995 | | * the top-left of @transform.out_rectangle as new origin. |
996 | | * |
997 | | * Returns: %FALSE if the point is outside of @transform.out_rectangle |
998 | | * after the transformation has been applied |
999 | | * |
1000 | | * Since: 1.28 |
1001 | | */ |
1002 | | gboolean |
1003 | | gst_video_meta_transform_matrix_point (const GstVideoMetaTransformMatrix * |
1004 | | transform, gint * x, gint * y) |
1005 | 0 | { |
1006 | 0 | return _gst_video_meta_transform_matrix_point (transform, x, y, FALSE); |
1007 | 0 | } |
1008 | | |
1009 | | /** |
1010 | | * gst_video_meta_transform_matrix_point_clipped: |
1011 | | * @transform: a #GstVideoMetaTransformMatrix |
1012 | | * @x: (inout): a non-NULL pointer to the X value of the coordinate |
1013 | | * @y: (inout): a non-NULL pointer to the Y value of the coordinate |
1014 | | * |
1015 | | * Transforms the (@x, @y) point from the input coordinates to the |
1016 | | * output ones. The point's coordinates are transformed by first |
1017 | | * applying the @transform.matrix to it using the top left (x, y) of |
1018 | | * @transform.in_rectangle as the origin, then translate it to use |
1019 | | * the top-left of @transform.out_rectangle as new origin. |
1020 | | * |
1021 | | * Returns: %FALSE if the point is outside of @transform.out_rectangle |
1022 | | * after the transformation has been applied |
1023 | | * |
1024 | | * Since: 1.28 |
1025 | | */ |
1026 | | gboolean |
1027 | | gst_video_meta_transform_matrix_point_clipped (const GstVideoMetaTransformMatrix |
1028 | | * transform, gint * x, gint * y) |
1029 | 0 | { |
1030 | 0 | return _gst_video_meta_transform_matrix_point (transform, x, y, TRUE); |
1031 | 0 | } |
1032 | | |
1033 | | static gboolean |
1034 | | _gst_video_meta_transform_matrix_rectangle (const |
1035 | | GstVideoMetaTransformMatrix * transform, GstVideoRectangle * rect, |
1036 | | gboolean clip) |
1037 | 0 | { |
1038 | 0 | gboolean ret = TRUE; |
1039 | 0 | gint x1, y1; |
1040 | 0 | gint x2, y2; |
1041 | | |
1042 | | /* If the transformation is not affine, can't do it on a rectangle */ |
1043 | 0 | if (transform->matrix[2][0] != 0 || |
1044 | 0 | transform->matrix[2][1] != 0 || transform->matrix[2][2] != 1) |
1045 | 0 | return FALSE; |
1046 | | |
1047 | | /* If there is shearing, it won't preserve the rectangle either */ |
1048 | 0 | if ((transform->matrix[0][0] != 0 || transform->matrix[1][1] != 0) && |
1049 | 0 | (transform->matrix[0][1] != 0 || transform->matrix[1][0] != 0)) |
1050 | 0 | return FALSE; |
1051 | | |
1052 | 0 | x1 = rect->x; |
1053 | 0 | y1 = rect->y; |
1054 | 0 | x2 = rect->x + rect->w; |
1055 | 0 | y2 = rect->y + rect->h; |
1056 | |
|
1057 | 0 | ret = _gst_video_meta_transform_matrix_point (transform, &x1, &y1, clip) && |
1058 | 0 | _gst_video_meta_transform_matrix_point (transform, &x2, &y2, clip); |
1059 | |
|
1060 | 0 | rect->x = MIN (x1, x2); |
1061 | 0 | rect->y = MIN (y1, y2); |
1062 | |
|
1063 | 0 | rect->w = MAX (x1, x2) - rect->x; |
1064 | 0 | rect->h = MAX (y1, y2) - rect->y; |
1065 | |
|
1066 | 0 | return ret; |
1067 | 0 | } |
1068 | | |
1069 | | /** |
1070 | | * gst_video_meta_transform_matrix_rectangle: |
1071 | | * @transform: A #GstVideoMetaTransformMatrix |
1072 | | * @rect: (inout): a rectangle in the coordinate of the original image |
1073 | | * |
1074 | | * Transforms @rect from the input coordinates to the |
1075 | | * output ones. The point's coordinates are transformed by first |
1076 | | * applying the @transform.matrix to it using the top left (x, y) of |
1077 | | * @transform.in_rectangle as the origin, then translate it to use |
1078 | | * the top-left of @transform.out_rectangle as new origin. |
1079 | | * |
1080 | | * @rect is always axis aligned at input and this function only returns |
1081 | | * axis aligned rectangles as output, otherwise it returns FALSE. |
1082 | | * |
1083 | | * Output rectangle could be in partially or totally outside of |
1084 | | * @transform.out_rectangle. |
1085 | | * |
1086 | | * Returns: %FALSE is the output rectangle is not axis aligned |
1087 | | * |
1088 | | * Since: 1.28 |
1089 | | */ |
1090 | | gboolean |
1091 | | gst_video_meta_transform_matrix_rectangle (const |
1092 | | GstVideoMetaTransformMatrix * transform, GstVideoRectangle * rect) |
1093 | 0 | { |
1094 | 0 | return _gst_video_meta_transform_matrix_rectangle (transform, rect, FALSE); |
1095 | 0 | } |
1096 | | |
1097 | | /** |
1098 | | * gst_video_meta_transform_matrix_rectangle_clipped: |
1099 | | * @transform: A #GstVideoMetaTransformMatrix |
1100 | | * @rect: (inout): a rectangle in the coordinate of the original image |
1101 | | * |
1102 | | * Transforms @rect from the input coordinates to the |
1103 | | * output ones. The point's coordinates are transformed by first |
1104 | | * applying the @transform.matrix to it using the top left (x, y) of |
1105 | | * @transform.in_rectangle as the origin, then translate it to use |
1106 | | * the top-left of @transform.out_rectangle as new origin. |
1107 | | * |
1108 | | * @rect is always axis aligned at input and this function only returns |
1109 | | * axis aligned rectangles as output, otherwise it returns FALSE. |
1110 | | * |
1111 | | * Output rectangle will be clipped to fit inside @transform.out_rectangle. |
1112 | | * |
1113 | | * Returns: %FALSE if the output rectangle is not axis aligned or if |
1114 | | * the rectangle is entirely outside of the out_rectangle. |
1115 | | * |
1116 | | * Since: 1.28 |
1117 | | */ |
1118 | | gboolean |
1119 | | gst_video_meta_transform_matrix_rectangle_clipped (const |
1120 | | GstVideoMetaTransformMatrix * transform, GstVideoRectangle * rect) |
1121 | 0 | { |
1122 | 0 | return _gst_video_meta_transform_matrix_rectangle (transform, rect, TRUE); |
1123 | 0 | } |
1124 | | |
1125 | | |
1126 | | GType |
1127 | | gst_video_gl_texture_upload_meta_api_get_type (void) |
1128 | 0 | { |
1129 | 0 | static GType type = 0; |
1130 | 0 | static const gchar *tags[] = |
1131 | 0 | { GST_META_TAG_VIDEO_STR, GST_META_TAG_MEMORY_STR, NULL }; |
1132 | |
|
1133 | 0 | if (g_once_init_enter (&type)) { |
1134 | 0 | GType _type = |
1135 | 0 | gst_meta_api_type_register ("GstVideoGLTextureUploadMetaAPI", tags); |
1136 | 0 | g_once_init_leave (&type, _type); |
1137 | 0 | } |
1138 | 0 | return type; |
1139 | 0 | } |
1140 | | |
1141 | | static gboolean |
1142 | | gst_video_gl_texture_upload_meta_init (GstMeta * meta, gpointer params, |
1143 | | GstBuffer * buffer) |
1144 | 0 | { |
1145 | 0 | GstVideoGLTextureUploadMeta *vmeta = (GstVideoGLTextureUploadMeta *) meta; |
1146 | |
|
1147 | 0 | vmeta->texture_orientation = |
1148 | 0 | GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL; |
1149 | 0 | vmeta->n_textures = 0; |
1150 | 0 | memset (vmeta->texture_type, 0, sizeof (vmeta->texture_type)); |
1151 | 0 | vmeta->buffer = NULL; |
1152 | 0 | vmeta->upload = NULL; |
1153 | 0 | vmeta->user_data = NULL; |
1154 | 0 | vmeta->user_data_copy = NULL; |
1155 | 0 | vmeta->user_data_free = NULL; |
1156 | |
|
1157 | 0 | return TRUE; |
1158 | 0 | } |
1159 | | |
1160 | | static void |
1161 | | gst_video_gl_texture_upload_meta_free (GstMeta * meta, GstBuffer * buffer) |
1162 | 0 | { |
1163 | 0 | GstVideoGLTextureUploadMeta *vmeta = (GstVideoGLTextureUploadMeta *) meta; |
1164 | |
|
1165 | 0 | if (vmeta->user_data_free) |
1166 | 0 | vmeta->user_data_free (vmeta->user_data); |
1167 | 0 | } |
1168 | | |
1169 | | static gboolean |
1170 | | gst_video_gl_texture_upload_meta_transform (GstBuffer * dest, GstMeta * meta, |
1171 | | GstBuffer * buffer, GQuark type, gpointer data) |
1172 | 0 | { |
1173 | 0 | GstVideoGLTextureUploadMeta *dmeta, *smeta; |
1174 | |
|
1175 | 0 | smeta = (GstVideoGLTextureUploadMeta *) meta; |
1176 | |
|
1177 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
1178 | 0 | GstMetaTransformCopy *copy = data; |
1179 | |
|
1180 | 0 | if (!copy->region) { |
1181 | | /* only copy if the complete data is copied as well */ |
1182 | 0 | dmeta = |
1183 | 0 | (GstVideoGLTextureUploadMeta *) gst_buffer_add_meta (dest, |
1184 | 0 | GST_VIDEO_GL_TEXTURE_UPLOAD_META_INFO, NULL); |
1185 | |
|
1186 | 0 | if (!dmeta) |
1187 | 0 | return FALSE; |
1188 | | |
1189 | 0 | dmeta->texture_orientation = smeta->texture_orientation; |
1190 | 0 | dmeta->n_textures = smeta->n_textures; |
1191 | 0 | memcpy (dmeta->texture_type, smeta->texture_type, |
1192 | 0 | sizeof (smeta->texture_type[0]) * 4); |
1193 | 0 | dmeta->buffer = dest; |
1194 | 0 | dmeta->upload = smeta->upload; |
1195 | 0 | dmeta->user_data = smeta->user_data; |
1196 | 0 | dmeta->user_data_copy = smeta->user_data_copy; |
1197 | 0 | dmeta->user_data_free = smeta->user_data_free; |
1198 | 0 | if (dmeta->user_data_copy) |
1199 | 0 | dmeta->user_data = dmeta->user_data_copy (dmeta->user_data); |
1200 | 0 | } |
1201 | 0 | } else { |
1202 | | /* return FALSE, if transform type is not supported */ |
1203 | 0 | return FALSE; |
1204 | 0 | } |
1205 | 0 | return TRUE; |
1206 | 0 | } |
1207 | | |
1208 | | const GstMetaInfo * |
1209 | | gst_video_gl_texture_upload_meta_get_info (void) |
1210 | 0 | { |
1211 | 0 | static const GstMetaInfo *info = NULL; |
1212 | |
|
1213 | 0 | if (g_once_init_enter ((GstMetaInfo **) & info)) { |
1214 | 0 | const GstMetaInfo *meta = |
1215 | 0 | gst_meta_register (GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, |
1216 | 0 | "GstVideoGLTextureUploadMeta", |
1217 | 0 | sizeof (GstVideoGLTextureUploadMeta), |
1218 | 0 | gst_video_gl_texture_upload_meta_init, |
1219 | 0 | gst_video_gl_texture_upload_meta_free, |
1220 | 0 | gst_video_gl_texture_upload_meta_transform); |
1221 | 0 | g_once_init_leave ((GstMetaInfo **) & info, (GstMetaInfo *) meta); |
1222 | 0 | } |
1223 | 0 | return info; |
1224 | 0 | } |
1225 | | |
1226 | | /** |
1227 | | * gst_buffer_add_video_gl_texture_upload_meta: |
1228 | | * @buffer: a #GstBuffer |
1229 | | * @texture_orientation: the #GstVideoGLTextureOrientation |
1230 | | * @n_textures: the number of textures |
1231 | | * @texture_type: array of #GstVideoGLTextureType |
1232 | | * @upload: (scope call): the function to upload the buffer to a specific texture ID |
1233 | | * @user_data: user data for the implementor of @upload |
1234 | | * @user_data_copy: (scope call): function to copy @user_data |
1235 | | * @user_data_free: (scope call): function to free @user_data |
1236 | | * |
1237 | | * Attaches GstVideoGLTextureUploadMeta metadata to @buffer with the given |
1238 | | * parameters. |
1239 | | * |
1240 | | * Returns: (transfer none): the #GstVideoGLTextureUploadMeta on @buffer. |
1241 | | */ |
1242 | | GstVideoGLTextureUploadMeta * |
1243 | | gst_buffer_add_video_gl_texture_upload_meta (GstBuffer * buffer, |
1244 | | GstVideoGLTextureOrientation texture_orientation, guint n_textures, |
1245 | | GstVideoGLTextureType texture_type[4], GstVideoGLTextureUpload upload, |
1246 | | gpointer user_data, GBoxedCopyFunc user_data_copy, |
1247 | | GBoxedFreeFunc user_data_free) |
1248 | 0 | { |
1249 | 0 | GstVideoGLTextureUploadMeta *meta; |
1250 | |
|
1251 | 0 | g_return_val_if_fail (buffer != NULL, NULL); |
1252 | 0 | g_return_val_if_fail (upload != NULL, NULL); |
1253 | 0 | g_return_val_if_fail (n_textures > 0 && n_textures < 5, NULL); |
1254 | | |
1255 | 0 | meta = |
1256 | 0 | (GstVideoGLTextureUploadMeta *) gst_buffer_add_meta (buffer, |
1257 | 0 | GST_VIDEO_GL_TEXTURE_UPLOAD_META_INFO, NULL); |
1258 | |
|
1259 | 0 | if (!meta) |
1260 | 0 | return NULL; |
1261 | | |
1262 | 0 | meta->texture_orientation = texture_orientation; |
1263 | 0 | meta->n_textures = n_textures; |
1264 | 0 | memcpy (meta->texture_type, texture_type, sizeof (texture_type[0]) * 4); |
1265 | 0 | meta->buffer = buffer; |
1266 | 0 | meta->upload = upload; |
1267 | 0 | meta->user_data = user_data; |
1268 | 0 | meta->user_data_copy = user_data_copy; |
1269 | 0 | meta->user_data_free = user_data_free; |
1270 | |
|
1271 | 0 | return meta; |
1272 | 0 | } |
1273 | | |
1274 | | /** |
1275 | | * gst_video_gl_texture_upload_meta_upload: |
1276 | | * @meta: a #GstVideoGLTextureUploadMeta |
1277 | | * @texture_id: the texture IDs to upload to |
1278 | | * |
1279 | | * Uploads the buffer which owns the meta to a specific texture ID. |
1280 | | * |
1281 | | * Returns: %TRUE if uploading succeeded, %FALSE otherwise. |
1282 | | */ |
1283 | | gboolean |
1284 | | gst_video_gl_texture_upload_meta_upload (GstVideoGLTextureUploadMeta * meta, |
1285 | | guint texture_id[4]) |
1286 | 0 | { |
1287 | 0 | g_return_val_if_fail (meta != NULL, FALSE); |
1288 | | |
1289 | 0 | return meta->upload (meta, texture_id); |
1290 | 0 | } |
1291 | | |
1292 | | /* Region of Interest Meta implementation *******************************************/ |
1293 | | |
1294 | | GType |
1295 | | gst_video_region_of_interest_meta_api_get_type (void) |
1296 | 0 | { |
1297 | 0 | static GType type; |
1298 | 0 | static const gchar *tags[] = |
1299 | 0 | { GST_META_TAG_VIDEO_STR, GST_META_TAG_VIDEO_ORIENTATION_STR, |
1300 | 0 | GST_META_TAG_VIDEO_SIZE_STR, NULL |
1301 | 0 | }; |
1302 | |
|
1303 | 0 | if (g_once_init_enter (&type)) { |
1304 | 0 | GType _type = |
1305 | 0 | gst_meta_api_type_register ("GstVideoRegionOfInterestMetaAPI", tags); |
1306 | 0 | GST_INFO ("registering"); |
1307 | 0 | g_once_init_leave (&type, _type); |
1308 | 0 | } |
1309 | 0 | return type; |
1310 | 0 | } |
1311 | | |
1312 | | |
1313 | | static gboolean |
1314 | | gst_video_region_of_interest_meta_transform (GstBuffer * dest, GstMeta * meta, |
1315 | | GstBuffer * buffer, GQuark type, gpointer data) |
1316 | 0 | { |
1317 | 0 | GstVideoRegionOfInterestMeta *smeta = (GstVideoRegionOfInterestMeta *) meta; |
1318 | 0 | GstVideoRegionOfInterestMeta *dmeta; |
1319 | |
|
1320 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
1321 | 0 | GST_DEBUG ("copy region of interest metadata (seqnum: %" G_GUINT64_FORMAT |
1322 | 0 | ")", gst_meta_get_seqnum (meta)); |
1323 | 0 | dmeta = |
1324 | 0 | gst_buffer_add_video_region_of_interest_meta_id (dest, |
1325 | 0 | smeta->roi_type, smeta->x, smeta->y, smeta->w, smeta->h); |
1326 | 0 | if (!dmeta) |
1327 | 0 | return FALSE; |
1328 | | |
1329 | 0 | dmeta->id = smeta->id; |
1330 | 0 | dmeta->parent_id = smeta->parent_id; |
1331 | 0 | dmeta->params = g_list_copy_deep (smeta->params, |
1332 | 0 | (GCopyFunc) gst_structure_copy, NULL); |
1333 | 0 | } else if (GST_VIDEO_META_TRANSFORM_IS_MATRIX (type)) { |
1334 | 0 | GstVideoMetaTransformMatrix *trans = data; |
1335 | 0 | GstVideoRectangle rect = { smeta->x, smeta->y, smeta->w, smeta->h }; |
1336 | |
|
1337 | 0 | GST_LOG ("Scaling and cropping region of interest metadata %dx%d+%d+%d" |
1338 | 0 | " in (%dx%d) -> %dx%d+%d+%d in %dx%d (seqnum: %" G_GUINT64_FORMAT ")", |
1339 | 0 | trans->in_rectangle.w, |
1340 | 0 | trans->in_rectangle.h, trans->in_rectangle.x, trans->in_rectangle.y, |
1341 | 0 | GST_VIDEO_INFO_WIDTH (trans->in_info), |
1342 | 0 | GST_VIDEO_INFO_HEIGHT (trans->in_info), trans->out_rectangle.w, |
1343 | 0 | trans->out_rectangle.h, trans->out_rectangle.x, |
1344 | 0 | trans->out_rectangle.y, GST_VIDEO_INFO_WIDTH (trans->out_info), |
1345 | 0 | GST_VIDEO_INFO_HEIGHT (trans->out_info), gst_meta_get_seqnum (meta)); |
1346 | |
|
1347 | 0 | if (!gst_video_meta_transform_matrix_rectangle_clipped (trans, &rect)) |
1348 | 0 | return FALSE; |
1349 | | |
1350 | 0 | dmeta = gst_buffer_add_video_region_of_interest_meta_id (dest, |
1351 | 0 | smeta->roi_type, rect.x, rect.y, rect.w, rect.h); |
1352 | 0 | if (!dmeta) |
1353 | 0 | return FALSE; |
1354 | | |
1355 | 0 | dmeta->id = smeta->id; |
1356 | 0 | dmeta->parent_id = smeta->parent_id; |
1357 | |
|
1358 | 0 | GST_LOG ("region of interest (id:%d, parent id:%d) offset %dx%d -> %dx%d " |
1359 | 0 | "(seqnum: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT ")", |
1360 | 0 | smeta->id, smeta->parent_id, smeta->x, smeta->y, dmeta->x, dmeta->y, |
1361 | 0 | gst_meta_get_seqnum (meta), |
1362 | 0 | gst_meta_get_seqnum (GST_META_CAST (dmeta))); |
1363 | 0 | GST_LOG ("region of interest size %dx%d -> %dx%d (seqnum: %" |
1364 | 0 | G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT " ))", smeta->w, smeta->h, |
1365 | 0 | dmeta->w, dmeta->h, gst_meta_get_seqnum (meta), |
1366 | 0 | gst_meta_get_seqnum (GST_META_CAST (dmeta))); |
1367 | 0 | } else if (GST_VIDEO_META_TRANSFORM_IS_SCALE (type)) { |
1368 | 0 | GstVideoMetaTransform *trans = data; |
1369 | 0 | gint ow, oh, nw, nh; |
1370 | 0 | ow = GST_VIDEO_INFO_WIDTH (trans->in_info); |
1371 | 0 | nw = GST_VIDEO_INFO_WIDTH (trans->out_info); |
1372 | 0 | oh = GST_VIDEO_INFO_HEIGHT (trans->in_info); |
1373 | 0 | nh = GST_VIDEO_INFO_HEIGHT (trans->out_info); |
1374 | |
|
1375 | 0 | GST_LOG ("scaling region of interest metadata %dx%d -> %dx%d " |
1376 | 0 | "(seqnum: %" G_GUINT64_FORMAT ")", ow, oh, nw, nh, |
1377 | 0 | gst_meta_get_seqnum (meta)); |
1378 | |
|
1379 | 0 | dmeta = |
1380 | 0 | gst_buffer_add_video_region_of_interest_meta_id (dest, |
1381 | 0 | smeta->roi_type, (smeta->x * nw) / ow, (smeta->y * nh) / oh, |
1382 | 0 | (smeta->w * nw) / ow, (smeta->h * nh) / oh); |
1383 | 0 | if (!dmeta) |
1384 | 0 | return FALSE; |
1385 | | |
1386 | 0 | dmeta->id = smeta->id; |
1387 | 0 | dmeta->parent_id = smeta->parent_id; |
1388 | |
|
1389 | 0 | GST_LOG ("region of interest (id:%d, parent id:%d) offset %dx%d -> %dx%d" |
1390 | 0 | " (seqnum: %" G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT ")", |
1391 | 0 | smeta->id, smeta->parent_id, smeta->x, smeta->y, dmeta->x, dmeta->y, |
1392 | 0 | gst_meta_get_seqnum (meta), |
1393 | 0 | gst_meta_get_seqnum (GST_META_CAST (dmeta))); |
1394 | 0 | GST_LOG ("region of interest size %dx%d -> %dx%d (seqnum: %" |
1395 | 0 | G_GUINT64_FORMAT " -> %" G_GUINT64_FORMAT ")", smeta->w, smeta->h, |
1396 | 0 | dmeta->w, dmeta->h, gst_meta_get_seqnum (meta), |
1397 | 0 | gst_meta_get_seqnum (GST_META_CAST (dmeta))); |
1398 | 0 | } else { |
1399 | | /* return FALSE, if transform type is not supported */ |
1400 | 0 | return FALSE; |
1401 | 0 | } |
1402 | 0 | return TRUE; |
1403 | 0 | } |
1404 | | |
1405 | | static gboolean |
1406 | | gst_video_region_of_interest_meta_init (GstMeta * meta, gpointer params, |
1407 | | GstBuffer * buffer) |
1408 | 0 | { |
1409 | 0 | GstVideoRegionOfInterestMeta *emeta = (GstVideoRegionOfInterestMeta *) meta; |
1410 | 0 | emeta->roi_type = 0; |
1411 | 0 | emeta->id = 0; |
1412 | 0 | emeta->parent_id = 0; |
1413 | 0 | emeta->x = emeta->y = emeta->w = emeta->h = 0; |
1414 | 0 | emeta->params = NULL; |
1415 | |
|
1416 | 0 | return TRUE; |
1417 | 0 | } |
1418 | | |
1419 | | static void |
1420 | | gst_video_region_of_interest_meta_free (GstMeta * meta, GstBuffer * buffer) |
1421 | 0 | { |
1422 | 0 | GstVideoRegionOfInterestMeta *emeta = (GstVideoRegionOfInterestMeta *) meta; |
1423 | |
|
1424 | 0 | g_list_free_full (emeta->params, (GDestroyNotify) gst_structure_free); |
1425 | 0 | } |
1426 | | |
1427 | | const GstMetaInfo * |
1428 | | gst_video_region_of_interest_meta_get_info (void) |
1429 | 0 | { |
1430 | 0 | static const GstMetaInfo *meta_info = NULL; |
1431 | |
|
1432 | 0 | if (g_once_init_enter ((GstMetaInfo **) & meta_info)) { |
1433 | 0 | const GstMetaInfo *mi = |
1434 | 0 | gst_meta_register (GST_VIDEO_REGION_OF_INTEREST_META_API_TYPE, |
1435 | 0 | "GstVideoRegionOfInterestMeta", |
1436 | 0 | sizeof (GstVideoRegionOfInterestMeta), |
1437 | 0 | gst_video_region_of_interest_meta_init, |
1438 | 0 | gst_video_region_of_interest_meta_free, |
1439 | 0 | gst_video_region_of_interest_meta_transform); |
1440 | 0 | g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi); |
1441 | 0 | } |
1442 | 0 | return meta_info; |
1443 | 0 | } |
1444 | | |
1445 | | /** |
1446 | | * gst_buffer_get_video_region_of_interest_meta_id: |
1447 | | * @buffer: a #GstBuffer |
1448 | | * @id: a metadata id |
1449 | | * |
1450 | | * Find the #GstVideoRegionOfInterestMeta on @buffer with the given @id. |
1451 | | * |
1452 | | * Buffers can contain multiple #GstVideoRegionOfInterestMeta metadata items if |
1453 | | * multiple regions of interests are marked on a frame. |
1454 | | * |
1455 | | * Returns: (transfer none) (nullable): the #GstVideoRegionOfInterestMeta with @id or %NULL when there is |
1456 | | * no such metadata on @buffer. |
1457 | | */ |
1458 | | GstVideoRegionOfInterestMeta * |
1459 | | gst_buffer_get_video_region_of_interest_meta_id (GstBuffer * buffer, gint id) |
1460 | 0 | { |
1461 | 0 | gpointer state = NULL; |
1462 | 0 | GstMeta *meta; |
1463 | 0 | const GstMetaInfo *info = GST_VIDEO_REGION_OF_INTEREST_META_INFO; |
1464 | |
|
1465 | 0 | while ((meta = gst_buffer_iterate_meta (buffer, &state))) { |
1466 | 0 | if (meta->info->api == info->api) { |
1467 | 0 | GstVideoRegionOfInterestMeta *vmeta = |
1468 | 0 | (GstVideoRegionOfInterestMeta *) meta; |
1469 | 0 | if (vmeta->id == id) |
1470 | 0 | return vmeta; |
1471 | 0 | } |
1472 | 0 | } |
1473 | 0 | return NULL; |
1474 | 0 | } |
1475 | | |
1476 | | /** |
1477 | | * gst_buffer_add_video_region_of_interest_meta: |
1478 | | * @buffer: a #GstBuffer |
1479 | | * @roi_type: Type of the region of interest (e.g. "face") |
1480 | | * @x: X position |
1481 | | * @y: Y position |
1482 | | * @w: width |
1483 | | * @h: height |
1484 | | * |
1485 | | * Attaches #GstVideoRegionOfInterestMeta metadata to @buffer with the given |
1486 | | * parameters. |
1487 | | * |
1488 | | * Returns: (transfer none): the #GstVideoRegionOfInterestMeta on @buffer. |
1489 | | */ |
1490 | | GstVideoRegionOfInterestMeta * |
1491 | | gst_buffer_add_video_region_of_interest_meta (GstBuffer * buffer, |
1492 | | const gchar * roi_type, guint x, guint y, guint w, guint h) |
1493 | 0 | { |
1494 | 0 | return gst_buffer_add_video_region_of_interest_meta_id (buffer, |
1495 | 0 | g_quark_from_string (roi_type), x, y, w, h); |
1496 | 0 | } |
1497 | | |
1498 | | /** |
1499 | | * gst_buffer_add_video_region_of_interest_meta_id: |
1500 | | * @buffer: a #GstBuffer |
1501 | | * @roi_type: Type of the region of interest (e.g. "face") |
1502 | | * @x: X position |
1503 | | * @y: Y position |
1504 | | * @w: width |
1505 | | * @h: height |
1506 | | * |
1507 | | * Attaches #GstVideoRegionOfInterestMeta metadata to @buffer with the given |
1508 | | * parameters. |
1509 | | * |
1510 | | * Returns: (transfer none): the #GstVideoRegionOfInterestMeta on @buffer. |
1511 | | */ |
1512 | | GstVideoRegionOfInterestMeta * |
1513 | | gst_buffer_add_video_region_of_interest_meta_id (GstBuffer * buffer, |
1514 | | GQuark roi_type, guint x, guint y, guint w, guint h) |
1515 | 0 | { |
1516 | 0 | GstVideoRegionOfInterestMeta *meta; |
1517 | |
|
1518 | 0 | g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); |
1519 | | |
1520 | 0 | meta = (GstVideoRegionOfInterestMeta *) gst_buffer_add_meta (buffer, |
1521 | 0 | GST_VIDEO_REGION_OF_INTEREST_META_INFO, NULL); |
1522 | 0 | meta->roi_type = roi_type; |
1523 | 0 | meta->x = x; |
1524 | 0 | meta->y = y; |
1525 | 0 | meta->w = w; |
1526 | 0 | meta->h = h; |
1527 | |
|
1528 | 0 | return meta; |
1529 | 0 | } |
1530 | | |
1531 | | /** |
1532 | | * gst_video_region_of_interest_meta_add_param: |
1533 | | * @meta: a #GstVideoRegionOfInterestMeta |
1534 | | * @s: (transfer full): a #GstStructure |
1535 | | * |
1536 | | * Attach element-specific parameters to @meta meant to be used by downstream |
1537 | | * elements which may handle this ROI. |
1538 | | * The name of @s is used to identify the element these parameters are meant for. |
1539 | | * |
1540 | | * This is typically used to tell encoders how they should encode this specific region. |
1541 | | * For example, a structure named "roi/x264enc" could be used to give the |
1542 | | * QP offsets this encoder should use when encoding the region described in @meta. |
1543 | | * Multiple parameters can be defined for the same meta so different encoders |
1544 | | * can be supported by cross platform applications). |
1545 | | * |
1546 | | * Since: 1.14 |
1547 | | */ |
1548 | | void |
1549 | | gst_video_region_of_interest_meta_add_param (GstVideoRegionOfInterestMeta * |
1550 | | meta, GstStructure * s) |
1551 | 0 | { |
1552 | 0 | g_return_if_fail (meta); |
1553 | 0 | g_return_if_fail (s); |
1554 | | |
1555 | 0 | meta->params = g_list_append (meta->params, s); |
1556 | 0 | } |
1557 | | |
1558 | | /** |
1559 | | * gst_video_region_of_interest_meta_get_param: |
1560 | | * @meta: a #GstVideoRegionOfInterestMeta |
1561 | | * @name: a name. |
1562 | | * |
1563 | | * Retrieve the parameter for @meta having @name as structure name, |
1564 | | * or %NULL if there is none. |
1565 | | * |
1566 | | * Returns: (transfer none) (nullable): a #GstStructure |
1567 | | * |
1568 | | * Since: 1.14 |
1569 | | * See also: gst_video_region_of_interest_meta_add_param() |
1570 | | */ |
1571 | | GstStructure * |
1572 | | gst_video_region_of_interest_meta_get_param (GstVideoRegionOfInterestMeta * |
1573 | | meta, const gchar * name) |
1574 | 0 | { |
1575 | 0 | GList *l; |
1576 | |
|
1577 | 0 | g_return_val_if_fail (meta, NULL); |
1578 | 0 | g_return_val_if_fail (name, NULL); |
1579 | | |
1580 | 0 | for (l = meta->params; l; l = g_list_next (l)) { |
1581 | 0 | GstStructure *s = l->data; |
1582 | |
|
1583 | 0 | if (gst_structure_has_name (s, name)) |
1584 | 0 | return s; |
1585 | 0 | } |
1586 | | |
1587 | 0 | return NULL; |
1588 | 0 | } |
1589 | | |
1590 | | /* Time Code Meta implementation *******************************************/ |
1591 | | |
1592 | | GType |
1593 | | gst_video_time_code_meta_api_get_type (void) |
1594 | 0 | { |
1595 | 0 | static GType type; |
1596 | |
|
1597 | 0 | if (g_once_init_enter (&type)) { |
1598 | 0 | static const gchar *tags[] = { NULL }; |
1599 | 0 | GType _type = gst_meta_api_type_register ("GstVideoTimeCodeMetaAPI", tags); |
1600 | 0 | GST_INFO ("registering"); |
1601 | 0 | g_once_init_leave (&type, _type); |
1602 | 0 | } |
1603 | 0 | return type; |
1604 | 0 | } |
1605 | | |
1606 | | |
1607 | | static gboolean |
1608 | | gst_video_time_code_meta_transform (GstBuffer * dest, GstMeta * meta, |
1609 | | GstBuffer * buffer, GQuark type, gpointer data) |
1610 | 0 | { |
1611 | 0 | GstVideoTimeCodeMeta *dmeta, *smeta; |
1612 | |
|
1613 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
1614 | 0 | smeta = (GstVideoTimeCodeMeta *) meta; |
1615 | |
|
1616 | 0 | GST_DEBUG ("copy time code metadata"); |
1617 | 0 | dmeta = |
1618 | 0 | gst_buffer_add_video_time_code_meta_full (dest, smeta->tc.config.fps_n, |
1619 | 0 | smeta->tc.config.fps_d, smeta->tc.config.latest_daily_jam, |
1620 | 0 | smeta->tc.config.flags, smeta->tc.hours, smeta->tc.minutes, |
1621 | 0 | smeta->tc.seconds, smeta->tc.frames, smeta->tc.field_count); |
1622 | 0 | if (!dmeta) |
1623 | 0 | return FALSE; |
1624 | 0 | } else { |
1625 | | /* return FALSE, if transform type is not supported */ |
1626 | 0 | return FALSE; |
1627 | 0 | } |
1628 | 0 | return TRUE; |
1629 | 0 | } |
1630 | | |
1631 | | static gboolean |
1632 | | gst_video_time_code_meta_init (GstMeta * meta, gpointer params, |
1633 | | GstBuffer * buffer) |
1634 | 0 | { |
1635 | 0 | GstVideoTimeCodeMeta *emeta = (GstVideoTimeCodeMeta *) meta; |
1636 | 0 | memset (&emeta->tc, 0, sizeof (emeta->tc)); |
1637 | 0 | gst_video_time_code_clear (&emeta->tc); |
1638 | |
|
1639 | 0 | return TRUE; |
1640 | 0 | } |
1641 | | |
1642 | | static void |
1643 | | gst_video_time_code_meta_free (GstMeta * meta, GstBuffer * buffer) |
1644 | 0 | { |
1645 | 0 | GstVideoTimeCodeMeta *emeta = (GstVideoTimeCodeMeta *) meta; |
1646 | |
|
1647 | 0 | gst_video_time_code_clear (&emeta->tc); |
1648 | 0 | } |
1649 | | |
1650 | | const GstMetaInfo * |
1651 | | gst_video_time_code_meta_get_info (void) |
1652 | 0 | { |
1653 | 0 | static const GstMetaInfo *meta_info = NULL; |
1654 | |
|
1655 | 0 | if (g_once_init_enter ((GstMetaInfo **) & meta_info)) { |
1656 | 0 | const GstMetaInfo *mi = |
1657 | 0 | gst_meta_register (GST_VIDEO_TIME_CODE_META_API_TYPE, |
1658 | 0 | "GstVideoTimeCodeMeta", |
1659 | 0 | sizeof (GstVideoTimeCodeMeta), |
1660 | 0 | gst_video_time_code_meta_init, |
1661 | 0 | gst_video_time_code_meta_free, |
1662 | 0 | gst_video_time_code_meta_transform); |
1663 | 0 | g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi); |
1664 | 0 | } |
1665 | 0 | return meta_info; |
1666 | 0 | } |
1667 | | |
1668 | | /** |
1669 | | * gst_buffer_add_video_time_code_meta: |
1670 | | * @buffer: a #GstBuffer |
1671 | | * @tc: a #GstVideoTimeCode |
1672 | | * |
1673 | | * Attaches #GstVideoTimeCodeMeta metadata to @buffer with the given |
1674 | | * parameters. |
1675 | | * |
1676 | | * Returns: (transfer none) (nullable): the #GstVideoTimeCodeMeta on @buffer, or |
1677 | | * (since 1.16) %NULL if the timecode was invalid. |
1678 | | * |
1679 | | * Since: 1.10 |
1680 | | */ |
1681 | | GstVideoTimeCodeMeta * |
1682 | | gst_buffer_add_video_time_code_meta (GstBuffer * buffer, |
1683 | | const GstVideoTimeCode * tc) |
1684 | 0 | { |
1685 | 0 | if (!gst_video_time_code_is_valid (tc)) |
1686 | 0 | return NULL; |
1687 | | |
1688 | 0 | return gst_buffer_add_video_time_code_meta_full (buffer, tc->config.fps_n, |
1689 | 0 | tc->config.fps_d, tc->config.latest_daily_jam, tc->config.flags, |
1690 | 0 | tc->hours, tc->minutes, tc->seconds, tc->frames, tc->field_count); |
1691 | 0 | } |
1692 | | |
1693 | | /** |
1694 | | * gst_buffer_add_video_time_code_meta_full: |
1695 | | * @buffer: a #GstBuffer |
1696 | | * @fps_n: framerate numerator |
1697 | | * @fps_d: framerate denominator |
1698 | | * @latest_daily_jam: a #GDateTime for the latest daily jam |
1699 | | * @flags: a #GstVideoTimeCodeFlags |
1700 | | * @hours: hours since the daily jam |
1701 | | * @minutes: minutes since the daily jam |
1702 | | * @seconds: seconds since the daily jam |
1703 | | * @frames: frames since the daily jam |
1704 | | * @field_count: fields since the daily jam |
1705 | | * |
1706 | | * Attaches #GstVideoTimeCodeMeta metadata to @buffer with the given |
1707 | | * parameters. |
1708 | | * |
1709 | | * Returns: (transfer none) (nullable): the #GstVideoTimeCodeMeta on @buffer, or |
1710 | | * (since 1.16) %NULL if the timecode was invalid. |
1711 | | * |
1712 | | * Since: 1.10 |
1713 | | */ |
1714 | | GstVideoTimeCodeMeta * |
1715 | | gst_buffer_add_video_time_code_meta_full (GstBuffer * buffer, guint fps_n, |
1716 | | guint fps_d, GDateTime * latest_daily_jam, GstVideoTimeCodeFlags flags, |
1717 | | guint hours, guint minutes, guint seconds, guint frames, guint field_count) |
1718 | 0 | { |
1719 | 0 | GstVideoTimeCodeMeta *meta; |
1720 | |
|
1721 | 0 | g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); |
1722 | | |
1723 | 0 | meta = (GstVideoTimeCodeMeta *) gst_buffer_add_meta (buffer, |
1724 | 0 | GST_VIDEO_TIME_CODE_META_INFO, NULL); |
1725 | 0 | g_return_val_if_fail (meta != NULL, NULL); |
1726 | | |
1727 | 0 | gst_video_time_code_init (&meta->tc, fps_n, fps_d, latest_daily_jam, flags, |
1728 | 0 | hours, minutes, seconds, frames, field_count); |
1729 | |
|
1730 | 0 | if (!gst_video_time_code_is_valid (&meta->tc)) { |
1731 | 0 | gst_buffer_remove_meta (buffer, (GstMeta *) meta); |
1732 | 0 | return NULL; |
1733 | 0 | } |
1734 | | |
1735 | 0 | return meta; |
1736 | 0 | } |