Revert "video-frame: avoid possible out of bound memory access"
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / gst-libs / gst / video / video-frame.c
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 {
39   static GstDebugCategory *cat = NULL;
40
41   if (g_once_init_enter (&cat)) {
42     GstDebugCategory *c;
43
44     GST_DEBUG_CATEGORY_GET (c, "GST_PERFORMANCE");
45     g_once_init_leave (&cat, c);
46   }
47   return cat;
48 }
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 {
73   GstVideoMeta *meta;
74   gint i;
75
76   g_return_val_if_fail (frame != NULL, FALSE);
77   g_return_val_if_fail (info != NULL, FALSE);
78   g_return_val_if_fail (info->finfo != NULL, FALSE);
79   g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
80
81   if (id == -1)
82     meta = gst_buffer_get_video_meta (buffer);
83   else
84     meta = gst_buffer_get_video_meta_id (buffer, id);
85
86   /* copy the info */
87   frame->info = *info;
88
89   if (meta) {
90     /* All these values must be consistent */
91     g_return_val_if_fail (info->finfo->format == meta->format, FALSE);
92     g_return_val_if_fail (info->width <= meta->width, FALSE);
93     g_return_val_if_fail (info->height <= meta->height, FALSE);
94     g_return_val_if_fail (info->finfo->n_planes == meta->n_planes, FALSE);
95
96     frame->info.finfo = gst_video_format_get_info (meta->format);
97     frame->info.width = meta->width;
98     frame->info.height = meta->height;
99     frame->id = meta->id;
100     frame->flags = meta->flags;
101
102     for (i = 0; i < meta->n_planes; i++) {
103       frame->info.offset[i] = meta->offset[i];
104       if (!gst_video_meta_map (meta, i, &frame->map[i], &frame->data[i],
105               &frame->info.stride[i], flags))
106         goto frame_map_failed;
107     }
108   } else {
109     /* no metadata, we really need to have the metadata when the id is
110      * specified. */
111     if (id != -1)
112       goto no_metadata;
113
114     frame->id = id;
115     frame->flags = 0;
116
117     if (!gst_buffer_map (buffer, &frame->map[0], flags))
118       goto map_failed;
119
120     /* do some sanity checks */
121     if (frame->map[0].size < info->size)
122       goto invalid_size;
123
124     /* set up pointers */
125     for (i = 0; i < info->finfo->n_planes; i++) {
126       frame->data[i] = frame->map[0].data + info->offset[i];
127     }
128   }
129   frame->buffer = buffer;
130   if ((flags & GST_VIDEO_FRAME_MAP_FLAG_NO_REF) == 0)
131     gst_buffer_ref (frame->buffer);
132
133   frame->meta = meta;
134
135   /* buffer flags enhance the frame flags */
136   if (GST_VIDEO_INFO_IS_INTERLACED (info)) {
137     if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED) {
138       if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
139         frame->flags |= GST_VIDEO_FRAME_FLAG_INTERLACED;
140       }
141     } else {
142       frame->flags |= GST_VIDEO_FRAME_FLAG_INTERLACED;
143     }
144
145     if (GST_VIDEO_INFO_FIELD_ORDER (info) ==
146         GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST) {
147       frame->flags |= GST_VIDEO_FRAME_FLAG_TFF;
148     } else {
149       if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF))
150         frame->flags |= GST_VIDEO_FRAME_FLAG_TFF;
151       if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_RFF))
152         frame->flags |= GST_VIDEO_FRAME_FLAG_RFF;
153       if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_ONEFIELD))
154         frame->flags |= GST_VIDEO_FRAME_FLAG_ONEFIELD;
155     }
156   }
157   return TRUE;
158
159   /* ERRORS */
160 no_metadata:
161   {
162     GST_ERROR ("no GstVideoMeta for id %d", id);
163     memset (frame, 0, sizeof (GstVideoFrame));
164     return FALSE;
165   }
166 frame_map_failed:
167   {
168     GST_ERROR ("failed to map video frame plane %d", i);
169     while (--i >= 0)
170       gst_video_meta_unmap (meta, i, &frame->map[i]);
171     memset (frame, 0, sizeof (GstVideoFrame));
172     return FALSE;
173   }
174 map_failed:
175   {
176     GST_ERROR ("failed to map buffer");
177     return FALSE;
178   }
179 invalid_size:
180   {
181     GST_ERROR ("invalid buffer size %" G_GSIZE_FORMAT " < %" G_GSIZE_FORMAT,
182         frame->map[0].size, info->size);
183     gst_buffer_unmap (buffer, &frame->map[0]);
184     memset (frame, 0, sizeof (GstVideoFrame));
185     return FALSE;
186   }
187 }
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 {
246   return gst_video_frame_map_id (frame, info, buffer, -1, flags);
247 }
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 {
258   GstBuffer *buffer;
259   GstVideoMeta *meta;
260   gint i;
261   GstMapFlags flags;
262
263   g_return_if_fail (frame != NULL);
264
265   buffer = frame->buffer;
266   meta = frame->meta;
267   flags = frame->map[0].flags;
268
269   if (meta) {
270     for (i = 0; i < frame->info.finfo->n_planes; i++) {
271       gst_video_meta_unmap (meta, i, &frame->map[i]);
272     }
273   } else {
274     gst_buffer_unmap (buffer, &frame->map[0]);
275   }
276
277   if ((flags & GST_VIDEO_FRAME_MAP_FLAG_NO_REF) == 0)
278     gst_buffer_unref (frame->buffer);
279 }
280
281 static void
282 scale_tile_shifts (const GstVideoFormatInfo * finfo, gint plane, guint * ws,
283     guint * hs)
284 {
285   gint comp[GST_VIDEO_MAX_COMPONENTS];
286   gint i;
287
288   gst_video_format_info_component (finfo, plane, comp);
289
290   /* scale the tile size according to the subsampling */
291   *ws -= finfo->w_sub[comp[0]];
292   *hs -= finfo->h_sub[comp[0]];
293
294   /* for each additional component in the same plane, double the tile width,
295    * this should provide the appropriate tile size when the tile size varies
296    * base on the subsampling. */
297   for (i = 1; comp[i] >= 0; i++)
298     *ws += 1;
299 }
300
301
302 /**
303  * gst_video_frame_copy_plane:
304  * @dest: a #GstVideoFrame
305  * @src: a #GstVideoFrame
306  * @plane: a plane
307  *
308  * Copy the plane with index @plane from @src to @dest.
309  *
310  * Note: Since: 1.18, @dest dimensions are allowed to be
311  * smaller than @src dimensions.
312  *
313  * Returns: TRUE if the contents could be copied.
314  */
315 gboolean
316 gst_video_frame_copy_plane (GstVideoFrame * dest, const GstVideoFrame * src,
317     guint plane)
318 {
319   const GstVideoInfo *sinfo;
320   GstVideoInfo *dinfo;
321   const GstVideoFormatInfo *finfo;
322   gint comp[GST_VIDEO_MAX_COMPONENTS];
323   guint8 *sp, *dp;
324   guint w, h;
325   gint ss, ds;
326
327   g_return_val_if_fail (dest != NULL, FALSE);
328   g_return_val_if_fail (src != NULL, FALSE);
329
330   sinfo = &src->info;
331   dinfo = &dest->info;
332
333   g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE);
334
335   finfo = dinfo->finfo;
336
337   g_return_val_if_fail (dinfo->width <= sinfo->width
338       && dinfo->height <= sinfo->height, FALSE);
339   g_return_val_if_fail (finfo->n_planes > plane, FALSE);
340
341   sp = src->data[plane];
342   dp = dest->data[plane];
343
344   if (GST_VIDEO_FORMAT_INFO_HAS_PALETTE (finfo) && plane == 1) {
345     /* copy the palette and we're done */
346     memcpy (dp, sp, 256 * 4);
347     return TRUE;
348   }
349
350   gst_video_format_info_component (finfo, plane, comp);
351   w = GST_VIDEO_FRAME_COMP_WIDTH (dest,
352       comp[0]) * GST_VIDEO_FRAME_COMP_PSTRIDE (dest, comp[0]);
353   /* FIXME: workaround for complex formats like v210, UYVP and IYU1 that have
354    * pstride == 0 */
355   if (w == 0)
356     w = MIN (GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane),
357         GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane));
358
359   h = GST_VIDEO_FRAME_COMP_HEIGHT (dest, comp[0]);
360
361   ss = GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane);
362   ds = GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane);
363
364   if (GST_VIDEO_FORMAT_INFO_IS_TILED (finfo)) {
365     gint tile_size;
366     gint sx_tiles, sy_tiles, dx_tiles, dy_tiles;
367     guint i, j, ws, hs, ts;
368     GstVideoTileMode mode;
369
370     ws = GST_VIDEO_FORMAT_INFO_TILE_WS (finfo);
371     hs = GST_VIDEO_FORMAT_INFO_TILE_HS (finfo);
372
373     if (GST_VIDEO_FORMAT_INFO_HAS_SUBTILES (finfo))
374       scale_tile_shifts (finfo, plane, &ws, &hs);
375
376     ts = ws + hs;
377
378     tile_size = 1 << ts;
379
380     mode = GST_VIDEO_FORMAT_INFO_TILE_MODE (finfo);
381
382     sx_tiles = GST_VIDEO_TILE_X_TILES (ss);
383     sy_tiles = GST_VIDEO_TILE_Y_TILES (ss);
384
385     dx_tiles = GST_VIDEO_TILE_X_TILES (ds);
386     dy_tiles = GST_VIDEO_TILE_Y_TILES (ds);
387
388     /* this is the amount of tiles to copy */
389     w = ((w - 1) >> ws) + 1;
390     h = ((h - 1) >> hs) + 1;
391
392     /* FIXME can possibly do better when no retiling is needed, it depends on
393      * the stride and the tile_size */
394     for (j = 0; j < h; j++) {
395       for (i = 0; i < w; i++) {
396         guint si, di;
397
398         si = gst_video_tile_get_index (mode, i, j, sx_tiles, sy_tiles);
399         di = gst_video_tile_get_index (mode, i, j, dx_tiles, dy_tiles);
400
401         memcpy (dp + (di << ts), sp + (si << ts), tile_size);
402       }
403     }
404   } else {
405     guint j;
406
407     GST_CAT_DEBUG (CAT_PERFORMANCE, "copy plane %d, w:%d h:%d ", plane, w, h);
408
409     for (j = 0; j < h; j++) {
410       memcpy (dp, sp, w);
411       dp += ds;
412       sp += ss;
413     }
414   }
415
416   return TRUE;
417 }
418
419 /**
420  * gst_video_frame_copy:
421  * @dest: a #GstVideoFrame
422  * @src: a #GstVideoFrame
423  *
424  * Copy the contents from @src to @dest.
425  *
426  * Note: Since: 1.18, @dest dimensions are allowed to be
427  * smaller than @src dimensions.
428  *
429  * Returns: TRUE if the contents could be copied.
430  */
431 gboolean
432 gst_video_frame_copy (GstVideoFrame * dest, const GstVideoFrame * src)
433 {
434   guint i, n_planes;
435   const GstVideoInfo *sinfo;
436   GstVideoInfo *dinfo;
437
438   g_return_val_if_fail (dest != NULL, FALSE);
439   g_return_val_if_fail (src != NULL, FALSE);
440
441   sinfo = &src->info;
442   dinfo = &dest->info;
443
444   g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE);
445   g_return_val_if_fail (dinfo->width <= sinfo->width
446       && dinfo->height <= sinfo->height, FALSE);
447
448   n_planes = dinfo->finfo->n_planes;
449
450   for (i = 0; i < n_planes; i++)
451     gst_video_frame_copy_plane (dest, src, i);
452
453   return TRUE;
454 }