Coverage Report

Created: 2026-07-12 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}