/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/video/video-frame.c
Line | Count | Source |
1 | | /* GStreamer |
2 | | * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> |
3 | | * Library <2002> Ronald Bultje <rbultje@ronald.bitfreak.net> |
4 | | * Copyright (C) 2007 David A. Schleef <ds@schleef.org> |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Library General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Library General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Library General Public |
17 | | * License along with this library; if not, write to the |
18 | | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
19 | | * Boston, MA 02110-1301, USA. |
20 | | */ |
21 | | |
22 | | #ifdef HAVE_CONFIG_H |
23 | | # include "config.h" |
24 | | #endif |
25 | | |
26 | | #include <string.h> |
27 | | #include <stdio.h> |
28 | | |
29 | | #include <gst/video/video.h> |
30 | | #include "video-frame.h" |
31 | | #include "video-tile.h" |
32 | | #include "gstvideometa.h" |
33 | | |
34 | | #define CAT_PERFORMANCE video_frame_get_perf_category() |
35 | | |
36 | | static inline GstDebugCategory * |
37 | | video_frame_get_perf_category (void) |
38 | 0 | { |
39 | 0 | static GstDebugCategory *cat = NULL; |
40 | |
|
41 | 0 | if (g_once_init_enter (&cat)) { |
42 | 0 | GstDebugCategory *c = NULL; |
43 | |
|
44 | 0 | GST_DEBUG_CATEGORY_GET (c, "GST_PERFORMANCE"); |
45 | 0 | g_once_init_leave (&cat, c); |
46 | 0 | } |
47 | 0 | return cat; |
48 | 0 | } |
49 | | |
50 | | /** |
51 | | * gst_video_frame_map_id: |
52 | | * @frame: (out caller-allocates): pointer to #GstVideoFrame |
53 | | * @info: a #GstVideoInfo |
54 | | * @buffer: the buffer to map |
55 | | * @id: the frame id to map |
56 | | * @flags: #GstMapFlags |
57 | | * |
58 | | * Use @info and @buffer to fill in the values of @frame with the video frame |
59 | | * information of frame @id. |
60 | | * |
61 | | * When @id is -1, the default frame is mapped. When @id != -1, this function |
62 | | * will return %FALSE when there is no GstVideoMeta with that id. |
63 | | * |
64 | | * All video planes of @buffer will be mapped and the pointers will be set in |
65 | | * @frame->data. |
66 | | * |
67 | | * Returns: %TRUE on success. |
68 | | */ |
69 | | gboolean |
70 | | gst_video_frame_map_id (GstVideoFrame * frame, const GstVideoInfo * info, |
71 | | GstBuffer * buffer, gint id, GstMapFlags flags) |
72 | 0 | { |
73 | 0 | GstVideoMeta *meta; |
74 | 0 | gint i; |
75 | |
|
76 | 0 | g_return_val_if_fail (frame != NULL, FALSE); |
77 | 0 | g_return_val_if_fail (info != NULL, FALSE); |
78 | 0 | g_return_val_if_fail (info->finfo != NULL, FALSE); |
79 | 0 | g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); |
80 | | |
81 | 0 | if (id == -1) |
82 | 0 | meta = gst_buffer_get_video_meta (buffer); |
83 | 0 | else |
84 | 0 | meta = gst_buffer_get_video_meta_id (buffer, id); |
85 | | |
86 | | /* copy the info */ |
87 | 0 | frame->info = *info; |
88 | |
|
89 | 0 | if (meta) { |
90 | | /* All these values must be consistent */ |
91 | 0 | g_return_val_if_fail (info->finfo->format == meta->format, FALSE); |
92 | 0 | g_return_val_if_fail (info->width <= meta->width, FALSE); |
93 | 0 | g_return_val_if_fail (info->height <= meta->height, FALSE); |
94 | 0 | g_return_val_if_fail (info->finfo->n_planes == meta->n_planes, FALSE); |
95 | | |
96 | 0 | frame->info.finfo = gst_video_format_get_info (meta->format); |
97 | 0 | frame->info.width = meta->width; |
98 | 0 | frame->info.height = meta->height; |
99 | 0 | frame->id = meta->id; |
100 | 0 | frame->flags = meta->flags; |
101 | |
|
102 | 0 | for (i = 0; i < meta->n_planes; i++) { |
103 | 0 | frame->info.offset[i] = meta->offset[i]; |
104 | 0 | if (!gst_video_meta_map (meta, i, &frame->map[i], &frame->data[i], |
105 | 0 | &frame->info.stride[i], flags)) |
106 | 0 | goto frame_map_failed; |
107 | 0 | } |
108 | 0 | } else { |
109 | | /* no metadata, we really need to have the metadata when the id is |
110 | | * specified. */ |
111 | 0 | if (id != -1) |
112 | 0 | goto no_metadata; |
113 | | |
114 | 0 | frame->id = id; |
115 | 0 | frame->flags = 0; |
116 | |
|
117 | 0 | if (!gst_buffer_map (buffer, &frame->map[0], flags)) |
118 | 0 | goto map_failed; |
119 | | |
120 | | /* do some sanity checks */ |
121 | 0 | if (frame->map[0].size < info->size) |
122 | 0 | goto invalid_size; |
123 | | |
124 | | /* set up pointers */ |
125 | 0 | for (i = 0; i < info->finfo->n_planes; i++) { |
126 | 0 | frame->data[i] = frame->map[0].data + info->offset[i]; |
127 | 0 | } |
128 | 0 | } |
129 | 0 | frame->buffer = buffer; |
130 | 0 | if ((flags & GST_VIDEO_FRAME_MAP_FLAG_NO_REF) == 0) |
131 | 0 | gst_buffer_ref (frame->buffer); |
132 | |
|
133 | 0 | frame->meta = meta; |
134 | | |
135 | | /* buffer flags enhance the frame flags */ |
136 | 0 | if (GST_VIDEO_INFO_IS_INTERLACED (info)) { |
137 | 0 | if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED) { |
138 | 0 | if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED)) { |
139 | 0 | frame->flags |= GST_VIDEO_FRAME_FLAG_INTERLACED; |
140 | 0 | } |
141 | 0 | } else { |
142 | 0 | frame->flags |= GST_VIDEO_FRAME_FLAG_INTERLACED; |
143 | 0 | } |
144 | |
|
145 | 0 | if (GST_VIDEO_INFO_FIELD_ORDER (info) == |
146 | 0 | GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST) { |
147 | 0 | frame->flags |= GST_VIDEO_FRAME_FLAG_TFF; |
148 | 0 | } else { |
149 | 0 | if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF)) |
150 | 0 | frame->flags |= GST_VIDEO_FRAME_FLAG_TFF; |
151 | 0 | if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_RFF)) |
152 | 0 | frame->flags |= GST_VIDEO_FRAME_FLAG_RFF; |
153 | 0 | if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_ONEFIELD)) |
154 | 0 | frame->flags |= GST_VIDEO_FRAME_FLAG_ONEFIELD; |
155 | 0 | } |
156 | 0 | } |
157 | 0 | return TRUE; |
158 | | |
159 | | /* ERRORS */ |
160 | 0 | no_metadata: |
161 | 0 | { |
162 | 0 | GST_ERROR ("no GstVideoMeta for id %d", id); |
163 | 0 | memset (frame, 0, sizeof (GstVideoFrame)); |
164 | 0 | return FALSE; |
165 | 0 | } |
166 | 0 | frame_map_failed: |
167 | 0 | { |
168 | 0 | GST_ERROR ("failed to map video frame plane %d", i); |
169 | 0 | while (--i >= 0) |
170 | 0 | gst_video_meta_unmap (meta, i, &frame->map[i]); |
171 | 0 | memset (frame, 0, sizeof (GstVideoFrame)); |
172 | 0 | return FALSE; |
173 | 0 | } |
174 | 0 | map_failed: |
175 | 0 | { |
176 | 0 | GST_ERROR ("failed to map buffer"); |
177 | 0 | return FALSE; |
178 | 0 | } |
179 | 0 | invalid_size: |
180 | 0 | { |
181 | 0 | GST_ERROR ("invalid buffer size %" G_GSIZE_FORMAT " < %" G_GSIZE_FORMAT, |
182 | 0 | frame->map[0].size, info->size); |
183 | 0 | gst_buffer_unmap (buffer, &frame->map[0]); |
184 | 0 | memset (frame, 0, sizeof (GstVideoFrame)); |
185 | 0 | return FALSE; |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | | /** |
190 | | * gst_video_frame_map: |
191 | | * @frame: (out caller-allocates): pointer to #GstVideoFrame |
192 | | * @info: a #GstVideoInfo |
193 | | * @buffer: the buffer to map |
194 | | * @flags: #GstMapFlags |
195 | | * |
196 | | * Use @info and @buffer to fill in the values of @frame. @frame is usually |
197 | | * allocated on the stack, and you will pass the address to the #GstVideoFrame |
198 | | * structure allocated on the stack; gst_video_frame_map() will then fill in |
199 | | * the structures with the various video-specific information you need to access |
200 | | * the pixels of the video buffer. You can then use accessor macros such as |
201 | | * GST_VIDEO_FRAME_COMP_DATA(), GST_VIDEO_FRAME_PLANE_DATA(), |
202 | | * GST_VIDEO_FRAME_COMP_STRIDE(), GST_VIDEO_FRAME_PLANE_STRIDE() etc. |
203 | | * to get to the pixels. |
204 | | * |
205 | | * |[<!-- language="C" --> |
206 | | * GstVideoFrame vframe; |
207 | | * ... |
208 | | * // set RGB pixels to black one at a time |
209 | | * if (gst_video_frame_map (&vframe, video_info, video_buffer, GST_MAP_WRITE)) { |
210 | | * guint8 *pixels = GST_VIDEO_FRAME_PLANE_DATA (vframe, 0); |
211 | | * guint stride = GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 0); |
212 | | * guint pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (vframe, 0); |
213 | | * |
214 | | * for (h = 0; h < height; ++h) { |
215 | | * for (w = 0; w < width; ++w) { |
216 | | * guint8 *pixel = pixels + h * stride + w * pixel_stride; |
217 | | * |
218 | | * memset (pixel, 0, pixel_stride); |
219 | | * } |
220 | | * } |
221 | | * |
222 | | * gst_video_frame_unmap (&vframe); |
223 | | * } |
224 | | * ... |
225 | | * ]| |
226 | | * |
227 | | * All video planes of @buffer will be mapped and the pointers will be set in |
228 | | * @frame->data. |
229 | | * |
230 | | * The purpose of this function is to make it easy for you to get to the video |
231 | | * pixels in a generic way, without you having to worry too much about details |
232 | | * such as whether the video data is allocated in one contiguous memory chunk |
233 | | * or multiple memory chunks (e.g. one for each plane); or if custom strides |
234 | | * and custom plane offsets are used or not (as signalled by GstVideoMeta on |
235 | | * each buffer). This function will just fill the #GstVideoFrame structure |
236 | | * with the right values and if you use the accessor macros everything will |
237 | | * just work and you can access the data easily. It also maps the underlying |
238 | | * memory chunks for you. |
239 | | * |
240 | | * Returns: %TRUE on success. |
241 | | */ |
242 | | gboolean |
243 | | gst_video_frame_map (GstVideoFrame * frame, const GstVideoInfo * info, |
244 | | GstBuffer * buffer, GstMapFlags flags) |
245 | 0 | { |
246 | 0 | return gst_video_frame_map_id (frame, info, buffer, -1, flags); |
247 | 0 | } |
248 | | |
249 | | /** |
250 | | * gst_video_frame_unmap: |
251 | | * @frame: a #GstVideoFrame |
252 | | * |
253 | | * Unmap the memory previously mapped with gst_video_frame_map. |
254 | | */ |
255 | | void |
256 | | gst_video_frame_unmap (GstVideoFrame * frame) |
257 | 0 | { |
258 | 0 | GstBuffer *buffer; |
259 | 0 | GstVideoMeta *meta; |
260 | 0 | gint i; |
261 | 0 | GstMapFlags flags; |
262 | |
|
263 | 0 | g_return_if_fail (frame != NULL); |
264 | | |
265 | 0 | buffer = frame->buffer; |
266 | 0 | meta = frame->meta; |
267 | 0 | flags = frame->map[0].flags; |
268 | | |
269 | | /* Allow to unmap even if not mapped, to work nicely with |
270 | | * g_auto (GstVideoFrame) frame = GST_VIDEO_FRAME_INIT; |
271 | | * This is also more consistent with gst_buffer_unmap() */ |
272 | 0 | if (G_UNLIKELY (buffer == NULL)) |
273 | 0 | return; |
274 | | |
275 | 0 | if (meta) { |
276 | 0 | for (i = 0; i < frame->info.finfo->n_planes; i++) { |
277 | 0 | gst_video_meta_unmap (meta, i, &frame->map[i]); |
278 | 0 | } |
279 | 0 | } else { |
280 | 0 | gst_buffer_unmap (buffer, &frame->map[0]); |
281 | 0 | } |
282 | |
|
283 | 0 | if ((flags & GST_VIDEO_FRAME_MAP_FLAG_NO_REF) == 0) |
284 | 0 | gst_buffer_unref (frame->buffer); |
285 | | |
286 | | /* Reset various fields to avoid use-after-frees. |
287 | | * This also makes it possible to call unmap() twice. */ |
288 | 0 | frame->buffer = NULL; |
289 | 0 | memset (&frame->data, 0, sizeof (frame->data)); |
290 | 0 | } |
291 | | |
292 | | /** |
293 | | * gst_video_frame_copy_plane: |
294 | | * @dest: a #GstVideoFrame |
295 | | * @src: a #GstVideoFrame |
296 | | * @plane: a plane |
297 | | * |
298 | | * Copy the plane with index @plane from @src to @dest. |
299 | | * |
300 | | * Note: Since: 1.18, @dest dimensions are allowed to be |
301 | | * smaller than @src dimensions. |
302 | | * |
303 | | * Returns: TRUE if the contents could be copied. |
304 | | */ |
305 | | gboolean |
306 | | gst_video_frame_copy_plane (GstVideoFrame * dest, const GstVideoFrame * src, |
307 | | guint plane) |
308 | 0 | { |
309 | 0 | const GstVideoInfo *sinfo; |
310 | 0 | GstVideoInfo *dinfo; |
311 | 0 | const GstVideoFormatInfo *finfo; |
312 | 0 | gint comp[GST_VIDEO_MAX_COMPONENTS]; |
313 | 0 | guint8 *sp, *dp; |
314 | 0 | guint w, h; |
315 | 0 | gint ss, ds; |
316 | |
|
317 | 0 | g_return_val_if_fail (dest != NULL, FALSE); |
318 | 0 | g_return_val_if_fail (src != NULL, FALSE); |
319 | | |
320 | 0 | sinfo = &src->info; |
321 | 0 | dinfo = &dest->info; |
322 | |
|
323 | 0 | g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE); |
324 | | |
325 | 0 | finfo = dinfo->finfo; |
326 | |
|
327 | 0 | g_return_val_if_fail (dinfo->width <= sinfo->width |
328 | 0 | && dinfo->height <= sinfo->height, FALSE); |
329 | 0 | g_return_val_if_fail (finfo->n_planes > plane, FALSE); |
330 | | |
331 | 0 | sp = src->data[plane]; |
332 | 0 | dp = dest->data[plane]; |
333 | |
|
334 | 0 | if (GST_VIDEO_FORMAT_INFO_HAS_PALETTE (finfo) && plane == 1) { |
335 | | /* copy the palette and we're done */ |
336 | 0 | memcpy (dp, sp, 256 * 4); |
337 | 0 | return TRUE; |
338 | 0 | } |
339 | | |
340 | 0 | gst_video_format_info_component (finfo, plane, comp); |
341 | 0 | w = GST_VIDEO_FRAME_COMP_WIDTH (dest, |
342 | 0 | comp[0]) * GST_VIDEO_FRAME_COMP_PSTRIDE (dest, comp[0]); |
343 | | /* FIXME: workaround for complex formats like v210, UYVP and IYU1 that have |
344 | | * pstride == 0 */ |
345 | 0 | if (w == 0) |
346 | 0 | w = MIN (GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane), |
347 | 0 | GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane)); |
348 | |
|
349 | 0 | h = GST_VIDEO_FRAME_COMP_HEIGHT (dest, comp[0]); |
350 | |
|
351 | 0 | ss = GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane); |
352 | 0 | ds = GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane); |
353 | |
|
354 | 0 | if (GST_VIDEO_FORMAT_INFO_IS_TILED (finfo)) { |
355 | 0 | gint tile_size; |
356 | 0 | gint sx_tiles, sy_tiles, dx_tiles, dy_tiles; |
357 | 0 | guint i, j; |
358 | 0 | GstVideoTileMode mode; |
359 | |
|
360 | 0 | tile_size = GST_VIDEO_FORMAT_INFO_TILE_SIZE (finfo, plane); |
361 | |
|
362 | 0 | mode = GST_VIDEO_FORMAT_INFO_TILE_MODE (finfo); |
363 | |
|
364 | 0 | sx_tiles = GST_VIDEO_TILE_X_TILES (ss); |
365 | 0 | sy_tiles = GST_VIDEO_TILE_Y_TILES (ss); |
366 | |
|
367 | 0 | dx_tiles = GST_VIDEO_TILE_X_TILES (ds); |
368 | 0 | dy_tiles = GST_VIDEO_TILE_Y_TILES (ds); |
369 | | |
370 | | /* this is the amount of tiles to copy */ |
371 | 0 | w = MIN (sx_tiles, dx_tiles); |
372 | 0 | h = MIN (sy_tiles, dy_tiles); |
373 | | |
374 | | /* FIXME can possibly do better when no retiling is needed, it depends on |
375 | | * the stride and the tile_size */ |
376 | 0 | for (j = 0; j < h; j++) { |
377 | 0 | for (i = 0; i < w; i++) { |
378 | 0 | guint si, di; |
379 | |
|
380 | 0 | si = gst_video_tile_get_index (mode, i, j, sx_tiles, sy_tiles); |
381 | 0 | di = gst_video_tile_get_index (mode, i, j, dx_tiles, dy_tiles); |
382 | |
|
383 | 0 | memcpy (dp + (di * tile_size), sp + (si * tile_size), tile_size); |
384 | 0 | } |
385 | 0 | } |
386 | 0 | } else { |
387 | 0 | guint j; |
388 | |
|
389 | 0 | GST_CAT_DEBUG (CAT_PERFORMANCE, "copy plane %d, w:%d h:%d ", plane, w, h); |
390 | |
|
391 | 0 | if (GST_VIDEO_INFO_PLANE_STRIDE (dinfo, |
392 | 0 | plane) == GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane)) { |
393 | 0 | memcpy (dp, sp, GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane) * h); |
394 | 0 | } else { |
395 | 0 | for (j = 0; j < h; j++) { |
396 | 0 | memcpy (dp, sp, w); |
397 | 0 | dp += ds; |
398 | 0 | sp += ss; |
399 | 0 | } |
400 | 0 | } |
401 | 0 | } |
402 | |
|
403 | 0 | return TRUE; |
404 | 0 | } |
405 | | |
406 | | /** |
407 | | * gst_video_frame_copy: |
408 | | * @dest: a #GstVideoFrame |
409 | | * @src: a #GstVideoFrame |
410 | | * |
411 | | * Copy the contents from @src to @dest. |
412 | | * |
413 | | * Note: Since: 1.18, @dest dimensions are allowed to be |
414 | | * smaller than @src dimensions. |
415 | | * |
416 | | * Returns: TRUE if the contents could be copied. |
417 | | */ |
418 | | gboolean |
419 | | gst_video_frame_copy (GstVideoFrame * dest, const GstVideoFrame * src) |
420 | 0 | { |
421 | 0 | guint i, n_planes; |
422 | 0 | const GstVideoInfo *sinfo GST_UNUSED_CHECKS; |
423 | 0 | GstVideoInfo *dinfo; |
424 | |
|
425 | 0 | g_return_val_if_fail (dest != NULL, FALSE); |
426 | 0 | g_return_val_if_fail (src != NULL, FALSE); |
427 | | |
428 | 0 | sinfo = &src->info; |
429 | 0 | dinfo = &dest->info; |
430 | |
|
431 | 0 | g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE); |
432 | 0 | g_return_val_if_fail (dinfo->width <= sinfo->width |
433 | 0 | && dinfo->height <= sinfo->height, FALSE); |
434 | | |
435 | 0 | n_planes = dinfo->finfo->n_planes; |
436 | |
|
437 | 0 | for (i = 0; i < n_planes; i++) |
438 | 0 | gst_video_frame_copy_plane (dest, src, i); |
439 | |
|
440 | 0 | return TRUE; |
441 | 0 | } |