mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-10-06 10:42:22 +00:00
a62c7bd54c
libgstreamer currently exports some debug category symbols GST_CAT_*, but those are not declared in any public headers. Some plugins and libgstvideo just use GST_DEBUG_CATEGORY_EXTERN() to declare and use those, but that's just not right at all, and it won't work on Windows with MSVC. Instead look up the categories via the API.
377 lines
10 KiB
C
377 lines
10 KiB
C
/* GStreamer
|
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
|
* Library <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
|
|
* Copyright (C) 2007 David A. Schleef <ds@schleef.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include <gst/video/video.h>
|
|
#include "video-frame.h"
|
|
#include "video-tile.h"
|
|
#include "gstvideometa.h"
|
|
|
|
#define CAT_PERFORMANCE video_frame_get_perf_category()
|
|
|
|
static inline GstDebugCategory *
|
|
video_frame_get_perf_category (void)
|
|
{
|
|
static GstDebugCategory *cat = NULL;
|
|
|
|
if (g_once_init_enter (&cat)) {
|
|
GstDebugCategory *c;
|
|
|
|
GST_DEBUG_CATEGORY_GET (c, "GST_PERFORMANCE");
|
|
g_once_init_leave (&cat, c);
|
|
}
|
|
return cat;
|
|
}
|
|
|
|
/**
|
|
* gst_video_frame_map_id:
|
|
* @frame: pointer to #GstVideoFrame
|
|
* @info: a #GstVideoInfo
|
|
* @buffer: the buffer to map
|
|
* @id: the frame id to map
|
|
* @flags: #GstMapFlags
|
|
*
|
|
* Use @info and @buffer to fill in the values of @frame with the video frame
|
|
* information of frame @id.
|
|
*
|
|
* When @id is -1, the default frame is mapped. When @id != -1, this function
|
|
* will return %FALSE when there is no GstVideoMeta with that id.
|
|
*
|
|
* All video planes of @buffer will be mapped and the pointers will be set in
|
|
* @frame->data.
|
|
*
|
|
* Returns: %TRUE on success.
|
|
*/
|
|
gboolean
|
|
gst_video_frame_map_id (GstVideoFrame * frame, GstVideoInfo * info,
|
|
GstBuffer * buffer, gint id, GstMapFlags flags)
|
|
{
|
|
GstVideoMeta *meta;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (frame != NULL, FALSE);
|
|
g_return_val_if_fail (info != NULL, FALSE);
|
|
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
|
|
|
|
if (id == -1)
|
|
meta = gst_buffer_get_video_meta (buffer);
|
|
else
|
|
meta = gst_buffer_get_video_meta_id (buffer, id);
|
|
|
|
/* copy the info */
|
|
frame->info = *info;
|
|
|
|
if (meta) {
|
|
/* All these values must be consistent */
|
|
g_return_val_if_fail (info->finfo->format == meta->format, FALSE);
|
|
g_return_val_if_fail (info->width <= meta->width, FALSE);
|
|
g_return_val_if_fail (info->height <= meta->height, FALSE);
|
|
g_return_val_if_fail (info->finfo->n_planes == meta->n_planes, FALSE);
|
|
|
|
frame->info.finfo = gst_video_format_get_info (meta->format);
|
|
frame->info.width = meta->width;
|
|
frame->info.height = meta->height;
|
|
frame->id = meta->id;
|
|
frame->flags = meta->flags;
|
|
|
|
for (i = 0; i < meta->n_planes; i++) {
|
|
frame->info.offset[i] = meta->offset[i];
|
|
if (!gst_video_meta_map (meta, i, &frame->map[i], &frame->data[i],
|
|
&frame->info.stride[i], flags))
|
|
goto frame_map_failed;
|
|
}
|
|
} else {
|
|
/* no metadata, we really need to have the metadata when the id is
|
|
* specified. */
|
|
if (id != -1)
|
|
goto no_metadata;
|
|
|
|
frame->id = id;
|
|
frame->flags = 0;
|
|
|
|
if (!gst_buffer_map (buffer, &frame->map[0], flags))
|
|
goto map_failed;
|
|
|
|
/* do some sanity checks */
|
|
if (frame->map[0].size < info->size)
|
|
goto invalid_size;
|
|
|
|
/* set up pointers */
|
|
for (i = 0; i < info->finfo->n_planes; i++) {
|
|
frame->data[i] = frame->map[0].data + info->offset[i];
|
|
}
|
|
}
|
|
frame->buffer = buffer;
|
|
if ((flags & GST_VIDEO_FRAME_MAP_FLAG_NO_REF) == 0)
|
|
gst_buffer_ref (frame->buffer);
|
|
|
|
frame->meta = meta;
|
|
|
|
/* buffer flags enhance the frame flags */
|
|
if (GST_VIDEO_INFO_IS_INTERLACED (info)) {
|
|
if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED) {
|
|
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
|
|
frame->flags |= GST_VIDEO_FRAME_FLAG_INTERLACED;
|
|
}
|
|
} else
|
|
frame->flags |= GST_VIDEO_FRAME_FLAG_INTERLACED;
|
|
|
|
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF))
|
|
frame->flags |= GST_VIDEO_FRAME_FLAG_TFF;
|
|
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_RFF))
|
|
frame->flags |= GST_VIDEO_FRAME_FLAG_RFF;
|
|
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_ONEFIELD))
|
|
frame->flags |= GST_VIDEO_FRAME_FLAG_ONEFIELD;
|
|
}
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
no_metadata:
|
|
{
|
|
GST_ERROR ("no GstVideoMeta for id %d", id);
|
|
memset (frame, 0, sizeof (GstVideoFrame));
|
|
return FALSE;
|
|
}
|
|
frame_map_failed:
|
|
{
|
|
GST_ERROR ("failed to map video frame plane %d", i);
|
|
while (--i >= 0)
|
|
gst_video_meta_unmap (meta, i, &frame->map[i]);
|
|
memset (frame, 0, sizeof (GstVideoFrame));
|
|
return FALSE;
|
|
}
|
|
map_failed:
|
|
{
|
|
GST_ERROR ("failed to map buffer");
|
|
return FALSE;
|
|
}
|
|
invalid_size:
|
|
{
|
|
GST_ERROR ("invalid buffer size %" G_GSIZE_FORMAT " < %" G_GSIZE_FORMAT,
|
|
frame->map[0].size, info->size);
|
|
gst_buffer_unmap (buffer, &frame->map[0]);
|
|
memset (frame, 0, sizeof (GstVideoFrame));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gst_video_frame_map:
|
|
* @frame: pointer to #GstVideoFrame
|
|
* @info: a #GstVideoInfo
|
|
* @buffer: the buffer to map
|
|
* @flags: #GstMapFlags
|
|
*
|
|
* Use @info and @buffer to fill in the values of @frame.
|
|
*
|
|
* All video planes of @buffer will be mapped and the pointers will be set in
|
|
* @frame->data.
|
|
*
|
|
* Returns: %TRUE on success.
|
|
*/
|
|
gboolean
|
|
gst_video_frame_map (GstVideoFrame * frame, GstVideoInfo * info,
|
|
GstBuffer * buffer, GstMapFlags flags)
|
|
{
|
|
return gst_video_frame_map_id (frame, info, buffer, -1, flags);
|
|
}
|
|
|
|
/**
|
|
* gst_video_frame_unmap:
|
|
* @frame: a #GstVideoFrame
|
|
*
|
|
* Unmap the memory previously mapped with gst_video_frame_map.
|
|
*/
|
|
void
|
|
gst_video_frame_unmap (GstVideoFrame * frame)
|
|
{
|
|
GstBuffer *buffer;
|
|
GstVideoMeta *meta;
|
|
gint i;
|
|
GstMapFlags flags;
|
|
|
|
g_return_if_fail (frame != NULL);
|
|
|
|
buffer = frame->buffer;
|
|
meta = frame->meta;
|
|
flags = frame->map[0].flags;
|
|
|
|
if (meta) {
|
|
for (i = 0; i < frame->info.finfo->n_planes; i++) {
|
|
gst_video_meta_unmap (meta, i, &frame->map[i]);
|
|
}
|
|
} else {
|
|
gst_buffer_unmap (buffer, &frame->map[0]);
|
|
}
|
|
|
|
if ((flags & GST_VIDEO_FRAME_MAP_FLAG_NO_REF) == 0)
|
|
gst_buffer_unref (frame->buffer);
|
|
}
|
|
|
|
/**
|
|
* gst_video_frame_copy_plane:
|
|
* @dest: a #GstVideoFrame
|
|
* @src: a #GstVideoFrame
|
|
* @plane: a plane
|
|
*
|
|
* Copy the plane with index @plane from @src to @dest.
|
|
*
|
|
* Returns: TRUE if the contents could be copied.
|
|
*/
|
|
gboolean
|
|
gst_video_frame_copy_plane (GstVideoFrame * dest, const GstVideoFrame * src,
|
|
guint plane)
|
|
{
|
|
const GstVideoInfo *sinfo;
|
|
GstVideoInfo *dinfo;
|
|
const GstVideoFormatInfo *finfo;
|
|
guint8 *sp, *dp;
|
|
guint w, h;
|
|
gint ss, ds;
|
|
|
|
g_return_val_if_fail (dest != NULL, FALSE);
|
|
g_return_val_if_fail (src != NULL, FALSE);
|
|
|
|
sinfo = &src->info;
|
|
dinfo = &dest->info;
|
|
|
|
g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE);
|
|
|
|
finfo = dinfo->finfo;
|
|
|
|
g_return_val_if_fail (dinfo->width == sinfo->width
|
|
&& dinfo->height == sinfo->height, FALSE);
|
|
g_return_val_if_fail (finfo->n_planes > plane, FALSE);
|
|
|
|
sp = src->data[plane];
|
|
dp = dest->data[plane];
|
|
|
|
if (GST_VIDEO_FORMAT_INFO_HAS_PALETTE (finfo) && plane == 1) {
|
|
/* copy the palette and we're done */
|
|
memcpy (dp, sp, 256 * 4);
|
|
return TRUE;
|
|
}
|
|
|
|
/* FIXME: assumes subsampling of component N is the same as plane N, which is
|
|
* currently true for all formats we have but it might not be in the future. */
|
|
w = GST_VIDEO_FRAME_COMP_WIDTH (dest,
|
|
plane) * GST_VIDEO_FRAME_COMP_PSTRIDE (dest, plane);
|
|
/* FIXME: workaround for complex formats like v210, UYVP and IYU1 that have
|
|
* pstride == 0 */
|
|
if (w == 0)
|
|
w = MIN (GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane),
|
|
GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane));
|
|
|
|
h = GST_VIDEO_FRAME_COMP_HEIGHT (dest, plane);
|
|
|
|
ss = GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane);
|
|
ds = GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane);
|
|
|
|
if (GST_VIDEO_FORMAT_INFO_IS_TILED (finfo)) {
|
|
gint tile_size;
|
|
gint sx_tiles, sy_tiles, dx_tiles, dy_tiles;
|
|
guint i, j, ws, hs, ts;
|
|
GstVideoTileMode mode;
|
|
|
|
ws = GST_VIDEO_FORMAT_INFO_TILE_WS (finfo);
|
|
hs = GST_VIDEO_FORMAT_INFO_TILE_HS (finfo);
|
|
ts = ws + hs;
|
|
|
|
tile_size = 1 << ts;
|
|
|
|
mode = GST_VIDEO_FORMAT_INFO_TILE_MODE (finfo);
|
|
|
|
sx_tiles = GST_VIDEO_TILE_X_TILES (ss);
|
|
sy_tiles = GST_VIDEO_TILE_Y_TILES (ss);
|
|
|
|
dx_tiles = GST_VIDEO_TILE_X_TILES (ds);
|
|
dy_tiles = GST_VIDEO_TILE_Y_TILES (ds);
|
|
|
|
/* this is the amount of tiles to copy */
|
|
w = ((w - 1) >> ws) + 1;
|
|
h = ((h - 1) >> hs) + 1;
|
|
|
|
/* FIXME can possibly do better when no retiling is needed, it depends on
|
|
* the stride and the tile_size */
|
|
for (j = 0; j < h; j++) {
|
|
for (i = 0; i < w; i++) {
|
|
guint si, di;
|
|
|
|
si = gst_video_tile_get_index (mode, i, j, sx_tiles, sy_tiles);
|
|
di = gst_video_tile_get_index (mode, i, j, dx_tiles, dy_tiles);
|
|
|
|
memcpy (dp + (di << ts), sp + (si << ts), tile_size);
|
|
}
|
|
}
|
|
} else {
|
|
guint j;
|
|
|
|
GST_CAT_DEBUG (CAT_PERFORMANCE, "copy plane %d, w:%d h:%d ", plane, w, h);
|
|
|
|
for (j = 0; j < h; j++) {
|
|
memcpy (dp, sp, w);
|
|
dp += ds;
|
|
sp += ss;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_video_frame_copy:
|
|
* @dest: a #GstVideoFrame
|
|
* @src: a #GstVideoFrame
|
|
*
|
|
* Copy the contents from @src to @dest.
|
|
*
|
|
* Returns: TRUE if the contents could be copied.
|
|
*/
|
|
gboolean
|
|
gst_video_frame_copy (GstVideoFrame * dest, const GstVideoFrame * src)
|
|
{
|
|
guint i, n_planes;
|
|
const GstVideoInfo *sinfo;
|
|
GstVideoInfo *dinfo;
|
|
|
|
g_return_val_if_fail (dest != NULL, FALSE);
|
|
g_return_val_if_fail (src != NULL, FALSE);
|
|
|
|
sinfo = &src->info;
|
|
dinfo = &dest->info;
|
|
|
|
g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE);
|
|
g_return_val_if_fail (dinfo->width == sinfo->width
|
|
&& dinfo->height == sinfo->height, FALSE);
|
|
|
|
n_planes = dinfo->finfo->n_planes;
|
|
|
|
for (i = 0; i < n_planes; i++)
|
|
gst_video_frame_copy_plane (dest, src, i);
|
|
|
|
return TRUE;
|
|
}
|