static GstFlowReturn
gst_geometric_transform_transform (GstBaseTransform * trans, GstBuffer * buf,
    GstBuffer * outbuf)
{
  GstGeometricTransform *gt;
  GstGeometricTransformClass *klass;
  gint x, y;
  GstFlowReturn ret = GST_FLOW_OK;
  gdouble *ptr;

  gt = GST_GEOMETRIC_TRANSFORM_CAST (trans);
  klass = GST_GEOMETRIC_TRANSFORM_GET_CLASS (gt);

  memset (GST_BUFFER_DATA (outbuf), 0, GST_BUFFER_SIZE (outbuf));

  GST_OBJECT_LOCK (gt);
  if (gt->precalc_map) {
    if (gt->needs_remap) {
      if (klass->prepare_func)
        if (!klass->prepare_func (gt)) {
          GST_OBJECT_UNLOCK (gt);
          return FALSE;
        }
      gst_geometric_transform_generate_map (gt);
    }
    g_return_val_if_fail (gt->map, GST_FLOW_ERROR);
    ptr = gt->map;
    for (y = 0; y < gt->height; y++) {
      for (x = 0; x < gt->width; x++) {
        /* do the mapping */
        gst_geometric_transform_do_map (gt, buf, outbuf, x, y, ptr[0], ptr[1]);
        ptr += 2;
      }
    }
  } else {
    for (y = 0; y < gt->height; y++) {
      for (x = 0; x < gt->width; x++) {
        gdouble in_x, in_y;

        if (klass->map_func (gt, x, y, &in_x, &in_y)) {
          gst_geometric_transform_do_map (gt, buf, outbuf, x, y, in_x, in_y);
        } else {
          GST_WARNING_OBJECT (gt, "Failed to do mapping for %d %d", x, y);
          ret = GST_FLOW_ERROR;
          goto end;
        }
      }
    }
  }
end:
  GST_OBJECT_UNLOCK (gt);
  return ret;
}
static gboolean
gst_geometric_transform_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
    GstCaps * outcaps)
{
  GstGeometricTransform *gt;
  gboolean ret;
  gint old_width;
  gint old_height;
  GstGeometricTransformClass *klass;

  gt = GST_GEOMETRIC_TRANSFORM_CAST (btrans);
  klass = GST_GEOMETRIC_TRANSFORM_GET_CLASS (gt);

  old_width = gt->width;
  old_height = gt->height;

  ret = gst_video_format_parse_caps (incaps, &gt->format, &gt->width,
      &gt->height);
  if (ret) {
    gt->row_stride = gst_video_format_get_row_stride (gt->format, 0, gt->width);
    gt->pixel_stride = gst_video_format_get_pixel_stride (gt->format, 0);

    /* regenerate the map */
    GST_OBJECT_LOCK (gt);
    if (gt->map == NULL || old_width == 0 || old_height == 0
        || gt->width != old_width || gt->height != old_height) {
      if (klass->prepare_func)
        if (!klass->prepare_func (gt)) {
          GST_OBJECT_UNLOCK (gt);
          return FALSE;
        }
      if (gt->precalc_map)
        gst_geometric_transform_generate_map (gt);
    }
    GST_OBJECT_UNLOCK (gt);
  }
  return ret;
}
static gboolean
gst_geometric_transform_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
    GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
{
  GstGeometricTransform *gt;
  gboolean ret = TRUE;
  gint old_width;
  gint old_height;
  GstGeometricTransformClass *klass;

  gt = GST_GEOMETRIC_TRANSFORM_CAST (vfilter);
  klass = GST_GEOMETRIC_TRANSFORM_GET_CLASS (gt);

  old_width = gt->width;
  old_height = gt->height;

  gt->width = in_info->width;
  gt->height = in_info->height;
  gt->row_stride = in_info->stride[0];
  gt->pixel_stride = GST_VIDEO_INFO_COMP_PSTRIDE (in_info, 0);

  /* regenerate the map */
  GST_OBJECT_LOCK (gt);
  if (gt->map == NULL || old_width == 0 || old_height == 0
      || gt->width != old_width || gt->height != old_height) {
    if (klass->prepare_func)
      if (!klass->prepare_func (gt)) {
        GST_OBJECT_UNLOCK (gt);
        return FALSE;
      }
    if (gt->precalc_map)
      gst_geometric_transform_generate_map (gt);
  }
  GST_OBJECT_UNLOCK (gt);
  return ret;
}
static GstFlowReturn
gst_geometric_transform_transform_frame (GstVideoFilter * vfilter,
    GstVideoFrame * in_frame, GstVideoFrame * out_frame)
{
  GstGeometricTransform *gt;
  GstGeometricTransformClass *klass;
  gint x, y;
  GstFlowReturn ret = GST_FLOW_OK;
  gdouble *ptr;
  guint8 *in_data;
  guint8 *out_data;

  gt = GST_GEOMETRIC_TRANSFORM_CAST (vfilter);
  klass = GST_GEOMETRIC_TRANSFORM_GET_CLASS (gt);

  in_data = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0);
  out_data = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0);

  if (GST_VIDEO_FRAME_FORMAT (out_frame) == GST_VIDEO_FORMAT_AYUV) {
    /* in AYUV black is not just all zeros:
     * 0x10 is black for Y,
     * 0x80 is black for Cr and Cb */
    for (int i = 0; i < out_frame->map[0].size; i += 4)
      GST_WRITE_UINT32_BE (out_data + i, 0xff108080);
  } else {
    memset (out_data, 0, out_frame->map[0].size);
  }

  GST_OBJECT_LOCK (gt);
  if (gt->precalc_map) {
    if (gt->needs_remap) {
      if (klass->prepare_func)
        if (!klass->prepare_func (gt)) {
          ret = FALSE;
          goto end;
        }
      gst_geometric_transform_generate_map (gt);
    }
    g_return_val_if_fail (gt->map, GST_FLOW_ERROR);
    ptr = gt->map;
    for (y = 0; y < gt->height; y++) {
      for (x = 0; x < gt->width; x++) {
        /* do the mapping */
        gst_geometric_transform_do_map (gt, in_data, out_data, x, y, ptr[0],
            ptr[1]);
        ptr += 2;
      }
    }
  } else {
    for (y = 0; y < gt->height; y++) {
      for (x = 0; x < gt->width; x++) {
        gdouble in_x, in_y;

        if (klass->map_func (gt, x, y, &in_x, &in_y)) {
          gst_geometric_transform_do_map (gt, in_data, out_data, x, y, in_x,
              in_y);
        } else {
          GST_WARNING_OBJECT (gt, "Failed to do mapping for %d %d", x, y);
          ret = GST_FLOW_ERROR;
          goto end;
        }
      }
    }
  }
end:
  GST_OBJECT_UNLOCK (gt);
  return ret;
}