Blame gst-libs/gst/video/video-frame.c

Packit 971217
/* GStreamer
Packit 971217
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
Packit 971217
 * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
Packit 971217
 * Copyright (C) 2007 David A. Schleef <ds@schleef.org>
Packit 971217
 *
Packit 971217
 * This library is free software; you can redistribute it and/or
Packit 971217
 * modify it under the terms of the GNU Library General Public
Packit 971217
 * License as published by the Free Software Foundation; either
Packit 971217
 * version 2 of the License, or (at your option) any later version.
Packit 971217
 *
Packit 971217
 * This library is distributed in the hope that it will be useful,
Packit 971217
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 971217
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 971217
 * Library General Public License for more details.
Packit 971217
 *
Packit 971217
 * You should have received a copy of the GNU Library General Public
Packit 971217
 * License along with this library; if not, write to the
Packit 971217
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Packit 971217
 * Boston, MA 02110-1301, USA.
Packit 971217
 */
Packit 971217
Packit 971217
#ifdef HAVE_CONFIG_H
Packit 971217
#  include "config.h"
Packit 971217
#endif
Packit 971217
Packit 971217
#include <string.h>
Packit 971217
#include <stdio.h>
Packit 971217
Packit 971217
#include <gst/video/video.h>
Packit 971217
#include "video-frame.h"
Packit 971217
#include "video-tile.h"
Packit 971217
#include "gstvideometa.h"
Packit 971217
Packit 971217
#define CAT_PERFORMANCE video_frame_get_perf_category()
Packit 971217
Packit 971217
static inline GstDebugCategory *
Packit 971217
video_frame_get_perf_category (void)
Packit 971217
{
Packit 971217
  static GstDebugCategory *cat = NULL;
Packit 971217
Packit 971217
  if (g_once_init_enter (&cat)) {
Packit 971217
    GstDebugCategory *c;
Packit 971217
Packit 971217
    GST_DEBUG_CATEGORY_GET (c, "GST_PERFORMANCE");
Packit 971217
    g_once_init_leave (&cat, c);
Packit 971217
  }
Packit 971217
  return cat;
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_video_frame_map_id:
Packit 971217
 * @frame: pointer to #GstVideoFrame
Packit 971217
 * @info: a #GstVideoInfo
Packit 971217
 * @buffer: the buffer to map
Packit 971217
 * @id: the frame id to map
Packit 971217
 * @flags: #GstMapFlags
Packit 971217
 *
Packit 971217
 * Use @info and @buffer to fill in the values of @frame with the video frame
Packit 971217
 * information of frame @id.
Packit 971217
 *
Packit 971217
 * When @id is -1, the default frame is mapped. When @id != -1, this function
Packit 971217
 * will return %FALSE when there is no GstVideoMeta with that id.
Packit 971217
 *
Packit 971217
 * All video planes of @buffer will be mapped and the pointers will be set in
Packit 971217
 * @frame->data.
Packit 971217
 *
Packit 971217
 * Returns: %TRUE on success.
Packit 971217
 */
Packit 971217
gboolean
Packit 971217
gst_video_frame_map_id (GstVideoFrame * frame, GstVideoInfo * info,
Packit 971217
    GstBuffer * buffer, gint id, GstMapFlags flags)
Packit 971217
{
Packit 971217
  GstVideoMeta *meta;
Packit 971217
  gint i;
Packit 971217
Packit 971217
  g_return_val_if_fail (frame != NULL, FALSE);
Packit 971217
  g_return_val_if_fail (info != NULL, FALSE);
Packit 971217
  g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
Packit 971217
Packit 971217
  if (id == -1)
Packit 971217
    meta = gst_buffer_get_video_meta (buffer);
Packit 971217
  else
Packit 971217
    meta = gst_buffer_get_video_meta_id (buffer, id);
Packit 971217
Packit 971217
  /* copy the info */
Packit 971217
  frame->info = *info;
Packit 971217
Packit 971217
  if (meta) {
Packit 971217
    /* All these values must be consistent */
Packit 971217
    g_return_val_if_fail (info->finfo->format == meta->format, FALSE);
Packit 971217
    g_return_val_if_fail (info->width <= meta->width, FALSE);
Packit 971217
    g_return_val_if_fail (info->height <= meta->height, FALSE);
Packit 971217
    g_return_val_if_fail (info->finfo->n_planes == meta->n_planes, FALSE);
Packit 971217
Packit 971217
    frame->info.finfo = gst_video_format_get_info (meta->format);
Packit 971217
    frame->info.width = meta->width;
Packit 971217
    frame->info.height = meta->height;
Packit 971217
    frame->id = meta->id;
Packit 971217
    frame->flags = meta->flags;
Packit 971217
Packit 971217
    for (i = 0; i < meta->n_planes; i++) {
Packit 971217
      frame->info.offset[i] = meta->offset[i];
Packit 971217
      if (!gst_video_meta_map (meta, i, &frame->map[i], &frame->data[i],
Packit 971217
              &frame->info.stride[i], flags))
Packit 971217
        goto frame_map_failed;
Packit 971217
    }
Packit 971217
  } else {
Packit 971217
    /* no metadata, we really need to have the metadata when the id is
Packit 971217
     * specified. */
Packit 971217
    if (id != -1)
Packit 971217
      goto no_metadata;
Packit 971217
Packit 971217
    frame->id = id;
Packit 971217
    frame->flags = 0;
Packit 971217
Packit 971217
    if (!gst_buffer_map (buffer, &frame->map[0], flags))
Packit 971217
      goto map_failed;
Packit 971217
Packit 971217
    /* do some sanity checks */
Packit 971217
    if (frame->map[0].size < info->size)
Packit 971217
      goto invalid_size;
Packit 971217
Packit 971217
    /* set up pointers */
Packit 971217
    for (i = 0; i < info->finfo->n_planes; i++) {
Packit 971217
      frame->data[i] = frame->map[0].data + info->offset[i];
Packit 971217
    }
Packit 971217
  }
Packit 971217
  frame->buffer = buffer;
Packit 971217
  if ((flags & GST_VIDEO_FRAME_MAP_FLAG_NO_REF) == 0)
Packit 971217
    gst_buffer_ref (frame->buffer);
Packit 971217
Packit 971217
  frame->meta = meta;
Packit 971217
Packit 971217
  /* buffer flags enhance the frame flags */
Packit 971217
  if (GST_VIDEO_INFO_IS_INTERLACED (info)) {
Packit 971217
    if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED) {
Packit 971217
      if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
Packit 971217
        frame->flags |= GST_VIDEO_FRAME_FLAG_INTERLACED;
Packit 971217
      }
Packit 971217
    } else
Packit 971217
      frame->flags |= GST_VIDEO_FRAME_FLAG_INTERLACED;
Packit 971217
Packit 971217
    if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF))
Packit 971217
      frame->flags |= GST_VIDEO_FRAME_FLAG_TFF;
Packit 971217
    if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_RFF))
Packit 971217
      frame->flags |= GST_VIDEO_FRAME_FLAG_RFF;
Packit 971217
    if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_ONEFIELD))
Packit 971217
      frame->flags |= GST_VIDEO_FRAME_FLAG_ONEFIELD;
Packit 971217
  }
Packit 971217
  return TRUE;
Packit 971217
Packit 971217
  /* ERRORS */
Packit 971217
no_metadata:
Packit 971217
  {
Packit 971217
    GST_ERROR ("no GstVideoMeta for id %d", id);
Packit 971217
    memset (frame, 0, sizeof (GstVideoFrame));
Packit 971217
    return FALSE;
Packit 971217
  }
Packit 971217
frame_map_failed:
Packit 971217
  {
Packit 971217
    GST_ERROR ("failed to map video frame plane %d", i);
Packit 971217
    while (--i >= 0)
Packit 971217
      gst_video_meta_unmap (meta, i, &frame->map[i]);
Packit 971217
    memset (frame, 0, sizeof (GstVideoFrame));
Packit 971217
    return FALSE;
Packit 971217
  }
Packit 971217
map_failed:
Packit 971217
  {
Packit 971217
    GST_ERROR ("failed to map buffer");
Packit 971217
    return FALSE;
Packit 971217
  }
Packit 971217
invalid_size:
Packit 971217
  {
Packit 971217
    GST_ERROR ("invalid buffer size %" G_GSIZE_FORMAT " < %" G_GSIZE_FORMAT,
Packit 971217
        frame->map[0].size, info->size);
Packit 971217
    gst_buffer_unmap (buffer, &frame->map[0]);
Packit 971217
    memset (frame, 0, sizeof (GstVideoFrame));
Packit 971217
    return FALSE;
Packit 971217
  }
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_video_frame_map:
Packit 971217
 * @frame: pointer to #GstVideoFrame
Packit 971217
 * @info: a #GstVideoInfo
Packit 971217
 * @buffer: the buffer to map
Packit 971217
 * @flags: #GstMapFlags
Packit 971217
 *
Packit 971217
 * Use @info and @buffer to fill in the values of @frame. @frame is usually
Packit 971217
 * allocated on the stack, and you will pass the address to the #GstVideoFrame
Packit 971217
 * structure allocated on the stack; gst_video_frame_map() will then fill in
Packit 971217
 * the structures with the various video-specific information you need to access
Packit 971217
 * the pixels of the video buffer. You can then use accessor macros such as
Packit 971217
 * GST_VIDEO_FRAME_COMP_DATA(), GST_VIDEO_FRAME_PLANE_DATA(),
Packit 971217
 * GST_VIDEO_FRAME_COMP_STRIDE(), GST_VIDEO_FRAME_PLANE_STRIDE() etc.
Packit 971217
 * to get to the pixels.
Packit 971217
 *
Packit 971217
 * |[
Packit 971217
 *   GstVideoFrame vframe;
Packit 971217
 *   ...
Packit 971217
 *   // set RGB pixels to black one at a time
Packit 971217
 *   if (gst_video_frame_map (&vframe, video_info, video_buffer, GST_MAP_WRITE)) {
Packit 971217
 *     guint8 *pixels = GST_VIDEO_FRAME_PLANE_DATA (vframe, 0);
Packit 971217
 *     guint stride = GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 0);
Packit 971217
 *     guint pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (vframe, 0);
Packit 971217
 *
Packit 971217
 *     for (h = 0; h < height; ++h) {
Packit 971217
 *       for (w = 0; w < width; ++w) {
Packit 971217
 *         guint8 *pixel = pixels + h * stride + w * pixel_stride;
Packit 971217
 *
Packit 971217
 *         memset (pixel, 0, pixel_stride);
Packit 971217
 *       }
Packit 971217
 *     }
Packit 971217
 *
Packit 971217
 *     gst_video_frame_unmap (&vframe);
Packit 971217
 *   }
Packit 971217
 *   ...
Packit 971217
 * ]|
Packit 971217
 *
Packit 971217
 * All video planes of @buffer will be mapped and the pointers will be set in
Packit 971217
 * @frame->data.
Packit 971217
 *
Packit 971217
 * The purpose of this function is to make it easy for you to get to the video
Packit 971217
 * pixels in a generic way, without you having to worry too much about details
Packit 971217
 * such as whether the video data is allocated in one contiguous memory chunk
Packit 971217
 * or multiple memory chunks (e.g. one for each plane); or if custom strides
Packit 971217
 * and custom plane offsets are used or not (as signalled by GstVideoMeta on
Packit 971217
 * each buffer). This function will just fill the #GstVideoFrame structure
Packit 971217
 * with the right values and if you use the accessor macros everything will
Packit 971217
 * just work and you can access the data easily. It also maps the underlying
Packit 971217
 * memory chunks for you.
Packit 971217
 *
Packit 971217
 * Returns: %TRUE on success.
Packit 971217
 */
Packit 971217
gboolean
Packit 971217
gst_video_frame_map (GstVideoFrame * frame, GstVideoInfo * info,
Packit 971217
    GstBuffer * buffer, GstMapFlags flags)
Packit 971217
{
Packit 971217
  return gst_video_frame_map_id (frame, info, buffer, -1, flags);
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_video_frame_unmap:
Packit 971217
 * @frame: a #GstVideoFrame
Packit 971217
 *
Packit 971217
 * Unmap the memory previously mapped with gst_video_frame_map.
Packit 971217
 */
Packit 971217
void
Packit 971217
gst_video_frame_unmap (GstVideoFrame * frame)
Packit 971217
{
Packit 971217
  GstBuffer *buffer;
Packit 971217
  GstVideoMeta *meta;
Packit 971217
  gint i;
Packit 971217
  GstMapFlags flags;
Packit 971217
Packit 971217
  g_return_if_fail (frame != NULL);
Packit 971217
Packit 971217
  buffer = frame->buffer;
Packit 971217
  meta = frame->meta;
Packit 971217
  flags = frame->map[0].flags;
Packit 971217
Packit 971217
  if (meta) {
Packit 971217
    for (i = 0; i < frame->info.finfo->n_planes; i++) {
Packit 971217
      gst_video_meta_unmap (meta, i, &frame->map[i]);
Packit 971217
    }
Packit 971217
  } else {
Packit 971217
    gst_buffer_unmap (buffer, &frame->map[0]);
Packit 971217
  }
Packit 971217
Packit 971217
  if ((flags & GST_VIDEO_FRAME_MAP_FLAG_NO_REF) == 0)
Packit 971217
    gst_buffer_unref (frame->buffer);
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_video_frame_copy_plane:
Packit 971217
 * @dest: a #GstVideoFrame
Packit 971217
 * @src: a #GstVideoFrame
Packit 971217
 * @plane: a plane
Packit 971217
 *
Packit 971217
 * Copy the plane with index @plane from @src to @dest.
Packit 971217
 *
Packit 971217
 * Returns: TRUE if the contents could be copied.
Packit 971217
 */
Packit 971217
gboolean
Packit 971217
gst_video_frame_copy_plane (GstVideoFrame * dest, const GstVideoFrame * src,
Packit 971217
    guint plane)
Packit 971217
{
Packit 971217
  const GstVideoInfo *sinfo;
Packit 971217
  GstVideoInfo *dinfo;
Packit 971217
  const GstVideoFormatInfo *finfo;
Packit 971217
  guint8 *sp, *dp;
Packit 971217
  guint w, h;
Packit 971217
  gint ss, ds;
Packit 971217
Packit 971217
  g_return_val_if_fail (dest != NULL, FALSE);
Packit 971217
  g_return_val_if_fail (src != NULL, FALSE);
Packit 971217
Packit 971217
  sinfo = &src->info;
Packit 971217
  dinfo = &dest->info;
Packit 971217
Packit 971217
  g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE);
Packit 971217
Packit 971217
  finfo = dinfo->finfo;
Packit 971217
Packit 971217
  g_return_val_if_fail (dinfo->width == sinfo->width
Packit 971217
      && dinfo->height == sinfo->height, FALSE);
Packit 971217
  g_return_val_if_fail (finfo->n_planes > plane, FALSE);
Packit 971217
Packit 971217
  sp = src->data[plane];
Packit 971217
  dp = dest->data[plane];
Packit 971217
Packit 971217
  if (GST_VIDEO_FORMAT_INFO_HAS_PALETTE (finfo) && plane == 1) {
Packit 971217
    /* copy the palette and we're done */
Packit 971217
    memcpy (dp, sp, 256 * 4);
Packit 971217
    return TRUE;
Packit 971217
  }
Packit 971217
Packit 971217
  /* FIXME: assumes subsampling of component N is the same as plane N, which is
Packit 971217
   * currently true for all formats we have but it might not be in the future. */
Packit 971217
  w = GST_VIDEO_FRAME_COMP_WIDTH (dest,
Packit 971217
      plane) * GST_VIDEO_FRAME_COMP_PSTRIDE (dest, plane);
Packit 971217
  /* FIXME: workaround for complex formats like v210, UYVP and IYU1 that have
Packit 971217
   * pstride == 0 */
Packit 971217
  if (w == 0)
Packit 971217
    w = MIN (GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane),
Packit 971217
        GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane));
Packit 971217
Packit 971217
  h = GST_VIDEO_FRAME_COMP_HEIGHT (dest, plane);
Packit 971217
Packit 971217
  ss = GST_VIDEO_INFO_PLANE_STRIDE (sinfo, plane);
Packit 971217
  ds = GST_VIDEO_INFO_PLANE_STRIDE (dinfo, plane);
Packit 971217
Packit 971217
  if (GST_VIDEO_FORMAT_INFO_IS_TILED (finfo)) {
Packit 971217
    gint tile_size;
Packit 971217
    gint sx_tiles, sy_tiles, dx_tiles, dy_tiles;
Packit 971217
    guint i, j, ws, hs, ts;
Packit 971217
    GstVideoTileMode mode;
Packit 971217
Packit 971217
    ws = GST_VIDEO_FORMAT_INFO_TILE_WS (finfo);
Packit 971217
    hs = GST_VIDEO_FORMAT_INFO_TILE_HS (finfo);
Packit 971217
    ts = ws + hs;
Packit 971217
Packit 971217
    tile_size = 1 << ts;
Packit 971217
Packit 971217
    mode = GST_VIDEO_FORMAT_INFO_TILE_MODE (finfo);
Packit 971217
Packit 971217
    sx_tiles = GST_VIDEO_TILE_X_TILES (ss);
Packit 971217
    sy_tiles = GST_VIDEO_TILE_Y_TILES (ss);
Packit 971217
Packit 971217
    dx_tiles = GST_VIDEO_TILE_X_TILES (ds);
Packit 971217
    dy_tiles = GST_VIDEO_TILE_Y_TILES (ds);
Packit 971217
Packit 971217
    /* this is the amount of tiles to copy */
Packit 971217
    w = ((w - 1) >> ws) + 1;
Packit 971217
    h = ((h - 1) >> hs) + 1;
Packit 971217
Packit 971217
    /* FIXME can possibly do better when no retiling is needed, it depends on
Packit 971217
     * the stride and the tile_size */
Packit 971217
    for (j = 0; j < h; j++) {
Packit 971217
      for (i = 0; i < w; i++) {
Packit 971217
        guint si, di;
Packit 971217
Packit 971217
        si = gst_video_tile_get_index (mode, i, j, sx_tiles, sy_tiles);
Packit 971217
        di = gst_video_tile_get_index (mode, i, j, dx_tiles, dy_tiles);
Packit 971217
Packit 971217
        memcpy (dp + (di << ts), sp + (si << ts), tile_size);
Packit 971217
      }
Packit 971217
    }
Packit 971217
  } else {
Packit 971217
    guint j;
Packit 971217
Packit 971217
    GST_CAT_DEBUG (CAT_PERFORMANCE, "copy plane %d, w:%d h:%d ", plane, w, h);
Packit 971217
Packit 971217
    for (j = 0; j < h; j++) {
Packit 971217
      memcpy (dp, sp, w);
Packit 971217
      dp += ds;
Packit 971217
      sp += ss;
Packit 971217
    }
Packit 971217
  }
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_video_frame_copy:
Packit 971217
 * @dest: a #GstVideoFrame
Packit 971217
 * @src: a #GstVideoFrame
Packit 971217
 *
Packit 971217
 * Copy the contents from @src to @dest.
Packit 971217
 *
Packit 971217
 * Returns: TRUE if the contents could be copied.
Packit 971217
 */
Packit 971217
gboolean
Packit 971217
gst_video_frame_copy (GstVideoFrame * dest, const GstVideoFrame * src)
Packit 971217
{
Packit 971217
  guint i, n_planes;
Packit 971217
  const GstVideoInfo *sinfo;
Packit 971217
  GstVideoInfo *dinfo;
Packit 971217
Packit 971217
  g_return_val_if_fail (dest != NULL, FALSE);
Packit 971217
  g_return_val_if_fail (src != NULL, FALSE);
Packit 971217
Packit 971217
  sinfo = &src->info;
Packit 971217
  dinfo = &dest->info;
Packit 971217
Packit 971217
  g_return_val_if_fail (dinfo->finfo->format == sinfo->finfo->format, FALSE);
Packit 971217
  g_return_val_if_fail (dinfo->width == sinfo->width
Packit 971217
      && dinfo->height == sinfo->height, FALSE);
Packit 971217
Packit 971217
  n_planes = dinfo->finfo->n_planes;
Packit 971217
Packit 971217
  for (i = 0; i < n_planes; i++)
Packit 971217
    gst_video_frame_copy_plane (dest, src, i);
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}