gboolean
gst_buffer_add_texture_upload_meta (GstBuffer * buffer)
{
  GstVideoGLTextureUploadMeta *meta = NULL;
  GstVaapiVideoMetaTexture *meta_texture;

  if (!buffer)
    return FALSE;

  meta_texture = meta_texture_new ();
  if (!meta_texture)
    return FALSE;

  if (!meta_texture_ensure_info_from_buffer (meta_texture, buffer))
    goto error;

  meta = gst_buffer_add_video_gl_texture_upload_meta (buffer,
      GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL,
      1, &meta_texture->texture_type, gst_vaapi_texture_upload,
      meta_texture, (GBoxedCopyFunc) meta_texture_copy,
      (GBoxedFreeFunc) meta_texture_free);
  if (!meta)
    goto error;
  return TRUE;

error:
  meta_texture_free (meta_texture);
  return FALSE;
}
gboolean
gst_egl_image_memory_setup_buffer (GstGLContext * ctx, GstVideoInfo * info,
    GstBuffer * buffer)
{
  gint i = 0;
  gint stride[3];
  gsize offset[3];
  GstMemory *mem[3] = { NULL, NULL, NULL };
  guint n_mem = 0;
  GstMemoryFlags flags = 0;
  EGLImageKHR image = EGL_NO_IMAGE_KHR;
  EGLClientBuffer client_buffer_tex[3] = { 0, 0, 0 };
  GstVideoGLTextureType texture_types[] = { 0, 0, 0, 0 };
  GstEGLImageAllocator *allocator = gst_egl_image_allocator_obtain ();
  GstGLContextEGL *context = GST_GL_CONTEXT_EGL (ctx);

  g_return_val_if_fail (ctx, FALSE);
  g_return_val_if_fail (info, FALSE);
  g_return_val_if_fail (buffer, FALSE);
  g_return_val_if_fail (gst_gl_context_check_feature (ctx,
          "EGL_KHR_image_base"), FALSE);

  memset (stride, 0, sizeof (stride));
  memset (offset, 0, sizeof (offset));

  flags |= GST_MEMORY_FLAG_NOT_MAPPABLE;
  flags |= GST_MEMORY_FLAG_NO_SHARE;

  switch (GST_VIDEO_INFO_FORMAT (info)) {
    case GST_VIDEO_FORMAT_RGB:
    case GST_VIDEO_FORMAT_BGR:
    case GST_VIDEO_FORMAT_RGB16:
    case GST_VIDEO_FORMAT_RGBA:
    case GST_VIDEO_FORMAT_BGRA:
    case GST_VIDEO_FORMAT_ARGB:
    case GST_VIDEO_FORMAT_ABGR:
    case GST_VIDEO_FORMAT_RGBx:
    case GST_VIDEO_FORMAT_BGRx:
    case GST_VIDEO_FORMAT_xRGB:
    case GST_VIDEO_FORMAT_xBGR:
    case GST_VIDEO_FORMAT_AYUV:
    {
      gsize size = 0;

      switch (GST_VIDEO_INFO_FORMAT (info)) {
        case GST_VIDEO_FORMAT_RGB:
        case GST_VIDEO_FORMAT_BGR:
        case GST_VIDEO_FORMAT_RGB16:
        {
          texture_types[0] = GST_VIDEO_GL_TEXTURE_TYPE_RGB;
          break;
        }
        case GST_VIDEO_FORMAT_RGBA:
        case GST_VIDEO_FORMAT_BGRA:
        case GST_VIDEO_FORMAT_ARGB:
        case GST_VIDEO_FORMAT_ABGR:
        case GST_VIDEO_FORMAT_RGBx:
        case GST_VIDEO_FORMAT_BGRx:
        case GST_VIDEO_FORMAT_xRGB:
        case GST_VIDEO_FORMAT_xBGR:
        case GST_VIDEO_FORMAT_AYUV:
        {
          texture_types[0] = GST_VIDEO_GL_TEXTURE_TYPE_RGBA;
          break;
        }
        default:
          g_assert_not_reached ();
          break;
      }
#if 0
      mem[0] =
          gst_egl_image_allocator_alloc (allocator, context,
          texture_types[0], GST_VIDEO_INFO_WIDTH (info),
          GST_VIDEO_INFO_HEIGHT (info), size);
      if (mem[0]) {
        stride[0] = size / GST_VIDEO_INFO_HEIGHT (info);
        n_mem = 1;
        GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
      } else
#endif
      {
        gst_gl_generate_texture_full (GST_GL_CONTEXT (context), info, 0, stride,
            offset, &size, (GLuint *) & client_buffer_tex[0]);

        image = context->eglCreateImage (context->egl_display,
            context->egl_context, EGL_GL_TEXTURE_2D_KHR, client_buffer_tex[0],
            NULL);
        if (eglGetError () != EGL_SUCCESS)
          goto mem_error;

        mem[0] =
            gst_egl_image_allocator_wrap (allocator, context,
            image, texture_types[0], flags, size, client_buffer_tex[0],
            (GstEGLImageDestroyNotify) gst_egl_image_memory_del_gl_texture);
        n_mem = 1;
      }
      break;
    }

    case GST_VIDEO_FORMAT_NV12:
    case GST_VIDEO_FORMAT_NV21:
    {
      gsize size[2];

      texture_types[0] = GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE;
      texture_types[1] = GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE_ALPHA;
#if 0
      mem[0] =
          gst_egl_image_allocator_alloc (allocator, context,
          texture_types[0], GST_VIDEO_INFO_COMP_WIDTH (info,
              0), GST_VIDEO_INFO_COMP_HEIGHT (info, 0), size[0]);
      mem[1] =
          gst_egl_image_allocator_alloc (allocator, context,
          texture_types[1],
          GST_VIDEO_INFO_COMP_WIDTH (info, 1),
          GST_VIDEO_INFO_COMP_HEIGHT (info, 1), size[1]);

      if (mem[0] && mem[1]) {
        stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (info);
        offset[1] = size[0];
        stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (info);
        n_mem = 2;
        GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
        GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE);
      } else {
        if (mem[0])
          gst_memory_unref (mem[0]);
        if (mem[1])
          gst_memory_unref (mem[1]);
        mem[0] = mem[1] = NULL;
      }
#endif
      {
        for (i = 0; i < 2; i++) {
          gst_gl_generate_texture_full (GST_GL_CONTEXT (context), info, 0,
              stride, offset, size, (GLuint *) & client_buffer_tex[i]);

          image = context->eglCreateImage (context->egl_display,
              context->egl_context, EGL_GL_TEXTURE_2D_KHR, client_buffer_tex[i],
              NULL);
          if (eglGetError () != EGL_SUCCESS)
            goto mem_error;

          mem[i] =
              gst_egl_image_allocator_wrap (allocator, context,
              image, texture_types[i], flags, size[i], client_buffer_tex[i],
              (GstEGLImageDestroyNotify) gst_egl_image_memory_del_gl_texture);
        }

        n_mem = 2;
      }
      break;
    }
    case GST_VIDEO_FORMAT_I420:
    case GST_VIDEO_FORMAT_YV12:
    case GST_VIDEO_FORMAT_Y444:
    case GST_VIDEO_FORMAT_Y42B:
    case GST_VIDEO_FORMAT_Y41B:
    {
      gsize size[3];

      texture_types[0] = GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE;
      texture_types[1] = GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE;
      texture_types[2] = GST_VIDEO_GL_TEXTURE_TYPE_LUMINANCE;
#if 0
      mem[0] =
          gst_egl_image_allocator_alloc (allocator, context,
          texture_types[0], GST_VIDEO_INFO_COMP_WIDTH (info,
              0), GST_VIDEO_INFO_COMP_HEIGHT (info, 0), size[0]);
      mem[1] =
          gst_egl_image_allocator_alloc (allocator, context,
          texture_types[1], GST_VIDEO_INFO_COMP_WIDTH (info,
              1), GST_VIDEO_INFO_COMP_HEIGHT (info, 1), size[1]);
      mem[2] =
          gst_egl_image_allocator_alloc (allocator, context,
          texture_types[2], GST_VIDEO_INFO_COMP_WIDTH (info,
              2), GST_VIDEO_INFO_COMP_HEIGHT (info, 2), size[2]);

      if (mem[0] && mem[1] && mem[2]) {
        stride[0] = size[0] / GST_VIDEO_INFO_HEIGHT (info);
        offset[1] = size[0];
        stride[1] = size[1] / GST_VIDEO_INFO_HEIGHT (info);
        offset[2] = size[1];
        stride[2] = size[2] / GST_VIDEO_INFO_HEIGHT (info);
        n_mem = 3;
        GST_MINI_OBJECT_FLAG_SET (mem[0], GST_MEMORY_FLAG_NO_SHARE);
        GST_MINI_OBJECT_FLAG_SET (mem[1], GST_MEMORY_FLAG_NO_SHARE);
        GST_MINI_OBJECT_FLAG_SET (mem[2], GST_MEMORY_FLAG_NO_SHARE);
      } else {
        if (mem[0])
          gst_memory_unref (mem[0]);
        if (mem[1])
          gst_memory_unref (mem[1]);
        if (mem[2])
          gst_memory_unref (mem[2]);
        mem[0] = mem[1] = mem[2] = NULL;
      }
#endif
      {
        for (i = 0; i < 3; i++) {
          gst_gl_generate_texture_full (GST_GL_CONTEXT (context), info, i,
              stride, offset, size, (GLuint *) & client_buffer_tex[i]);

          image = context->eglCreateImage (context->egl_display,
              context->egl_context, EGL_GL_TEXTURE_2D_KHR, client_buffer_tex[i],
              NULL);
          if (eglGetError () != EGL_SUCCESS)
            goto mem_error;

          mem[i] =
              gst_egl_image_allocator_wrap (allocator, context,
              image, texture_types[i], flags, size[i], client_buffer_tex[i],
              (GstEGLImageDestroyNotify) gst_egl_image_memory_del_gl_texture);
        }

        n_mem = 3;
      }
      break;
    }
    default:
      g_assert_not_reached ();
      break;
  }

  gst_buffer_add_video_meta_full (buffer, 0, GST_VIDEO_INFO_FORMAT (info),
      GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
      GST_VIDEO_INFO_N_PLANES (info), offset, stride);

  gst_buffer_add_video_gl_texture_upload_meta (buffer,
      gst_egl_image_memory_get_orientation (mem[0]), n_mem, texture_types,
      gst_eglimage_to_gl_texture_upload_meta, NULL, NULL, NULL);

  for (i = 0; i < n_mem; i++)
    gst_buffer_append_memory (buffer, mem[i]);

  return TRUE;

mem_error:
  {
    GST_CAT_ERROR (GST_CAT_DEFAULT, "Failed to create EGLImage");

    for (i = 0; i < 3; i++) {
      if (client_buffer_tex[i])
        gst_gl_context_del_texture (ctx, (GLuint *) & client_buffer_tex[i]);
      if (mem[i])
        gst_memory_unref (mem[i]);
    }

    return FALSE;
  }
}