/**
 * gst_vaapi_subpicture_new_from_overlay_rectangle:
 * @display: a #GstVaapiDisplay
 * @rect: a #GstVideoOverlayRectangle
 *
 * Helper function that creates a new #GstVaapiSubpicture from a
 * #GstVideoOverlayRectangle. A new #GstVaapiImage is also created
 * along the way and attached to the resulting subpicture. The
 * subpicture holds a unique reference to the underlying image.
 *
 * Return value: the newly allocated #GstVaapiSubpicture object
 */
GstVaapiSubpicture *
gst_vaapi_subpicture_new_from_overlay_rectangle (GstVaapiDisplay * display,
    GstVideoOverlayRectangle * rect)
{
  GstVaapiSubpicture *subpicture;
  GstVideoFormat format;
  GstVaapiImage *image;
  GstVaapiImageRaw raw_image;
  GstBuffer *buffer;
  guint8 *data;
  gfloat global_alpha;
  guint width, height, stride;
  guint hw_flags, flags;
  GstVideoMeta *vmeta;
  GstMapInfo map_info;

  g_return_val_if_fail (GST_IS_VIDEO_OVERLAY_RECTANGLE (rect), NULL);

  /* XXX: use gst_vaapi_image_format_from_video() */
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
  format = GST_VIDEO_FORMAT_BGRA;
#else
  format = GST_VIDEO_FORMAT_ARGB;
#endif
  if (!gst_vaapi_display_has_subpicture_format (display, format, &hw_flags))
    return NULL;

  flags =
      hw_flags &
      from_GstVideoOverlayFormatFlags (gst_video_overlay_rectangle_get_flags
      (rect));

  buffer = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rect,
      to_GstVideoOverlayFormatFlags (flags));
  if (!buffer)
    return NULL;

  vmeta = gst_buffer_get_video_meta (buffer);
  if (!vmeta)
    return NULL;
  width = vmeta->width;
  height = vmeta->height;

  if (!gst_video_meta_map (vmeta, 0, &map_info, (gpointer *) & data,
          (gint *) & stride, GST_MAP_READ))
    return NULL;

  image = gst_vaapi_image_new (display, format, width, height);
  if (!image)
    return NULL;

  raw_image.format = format;
  raw_image.width = width;
  raw_image.height = height;
  raw_image.num_planes = 1;
  raw_image.pixels[0] = data;
  raw_image.stride[0] = stride;
  if (!gst_vaapi_image_update_from_raw (image, &raw_image, NULL)) {
    GST_WARNING ("could not update VA image with subtitle data");
    gst_vaapi_object_unref (image);
    return NULL;
  }

  subpicture = gst_vaapi_subpicture_new (image, flags);
  gst_vaapi_object_unref (image);
  gst_video_meta_unmap (vmeta, 0, &map_info);
  if (!subpicture)
    return NULL;

  if (flags & GST_VAAPI_SUBPICTURE_FLAG_GLOBAL_ALPHA) {
    global_alpha = gst_video_overlay_rectangle_get_global_alpha (rect);
    if (!gst_vaapi_subpicture_set_global_alpha (subpicture, global_alpha))
      return NULL;
  }
  return subpicture;
}
/**
 * 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) {
        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 < info->finfo->n_planes; 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 = gst_buffer_ref (buffer);
    frame->meta = meta;

    /* buffer flags enhance the frame flags */
    if (GST_VIDEO_INFO_IS_INTERLACED (info)) {
        if (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED))
            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);
        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]);
        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]);
        return FALSE;
    }
}