summaryrefslogtreecommitdiff
path: root/gst-libs/gst/vaapi/gstvaapisurface_egl.c
blob: f5aaa746d7387bf35328d20fdd7183e31a429c90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*
 *  gstvaapisurface_egl.c - VA surface abstraction (EGL interop)
 *
 *  Copyright (C) 2014 Intel Corporation
 *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2.1
 *  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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301 USA
 */

#include "sysdeps.h"
#include "gstvaapisurface_egl.h"
#include "gstvaapisurface_drm.h"
#include "gstvaapisurface_priv.h"
#include "gstvaapiimage_priv.h"
#include "gstvaapibufferproxy_priv.h"
#include "gstvaapidisplay_egl_priv.h"
#include "gstvaapifilter.h"

typedef struct
{
  GstVaapiDisplayEGL *display;
  EGLImageKHR image;
  GstVideoFormat format;
  guint width;
  guint height;
  GstVaapiSurface *surface;     /* result */
} CreateSurfaceWithEGLImageArgs;

static GstVaapiSurface *
do_create_surface_with_egl_image_unlocked (GstVaapiDisplayEGL * display,
    EGLImageKHR image, GstVideoFormat format, guint width, guint height)
{
  GstVaapiDisplay *const base_display = GST_VAAPI_DISPLAY_CAST (display);
  EglContext *const ctx = GST_VAAPI_DISPLAY_EGL_CONTEXT (display);
  EglVTable *vtable;
  gsize size, offset[GST_VIDEO_MAX_PLANES];
  gint name, stride[GST_VIDEO_MAX_PLANES];

  if (!ctx || !(vtable = egl_context_get_vtable (ctx, FALSE)))
    return NULL;

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

  if (!vtable->has_EGL_MESA_drm_image)
    goto error_mission_extension;

  /* EGL_MESA_drm_image extension */
  if (!vtable->eglExportDRMImageMESA (ctx->display->base.handle.p, image,
          &name, NULL, &stride[0]))
    goto error_export_image_gem_buf;

  size = height * stride[0];
  /*
   * XXX: The below surface creation may fail on Intel due to:
   *   https://github.com/01org/intel-vaapi-driver/issues/222
   * A permanent fix for that problem will be released in intel-vaapi-driver
   * version 1.8.4 and later, and also in 1.8.3-1ubuntu1.
   * However if you don't have that fix then a simple workaround is to
   * uncomment this line of code:
   *   size = GST_ROUND_UP_32 (height) * stride[0];
   */

  return gst_vaapi_surface_new_with_gem_buf_handle (base_display, name, size,
      format, width, height, offset, stride);

  /* ERRORS */
error_export_image_gem_buf:
  {
    GST_ERROR ("failed to export EGL image to GEM buffer");
    return NULL;
  }

error_mission_extension:
  {
    GST_ERROR ("missing EGL_MESA_drm_image extension");
    return NULL;
  }
}

static void
do_create_surface_with_egl_image (CreateSurfaceWithEGLImageArgs * args)
{
  GST_VAAPI_DISPLAY_LOCK (args->display);
  args->surface = do_create_surface_with_egl_image_unlocked (args->display,
      args->image, args->format, args->width, args->height);
  GST_VAAPI_DISPLAY_UNLOCK (args->display);
}

// Creates VA surface with EGLImage buffer as backing storage (internal)
static inline GstVaapiSurface *
create_surface_with_egl_image (GstVaapiDisplayEGL * display, EGLImageKHR image,
    GstVideoFormat format, guint width, guint height)
{
  CreateSurfaceWithEGLImageArgs args =
      { display, image, format, width, height };

  if (!egl_context_run (GST_VAAPI_DISPLAY_EGL_CONTEXT (display),
          (EglContextRunFunc) do_create_surface_with_egl_image, &args))
    return NULL;
  return args.surface;
}

// Creates VA surface from an EGLImage buffer copy (internal)
static GstVaapiSurface *
create_surface_from_egl_image (GstVaapiDisplayEGL * display,
    const GstVideoInfo * vip, EGLImageKHR image, GstVideoFormat format,
    guint width, guint height, guint flags)
{
  GstVaapiDisplay *const base_display = GST_VAAPI_DISPLAY_CAST (display);
  GstVaapiSurface *img_surface = NULL, *out_surface = NULL;
  gboolean use_native_format = TRUE;
  GstVaapiFilter *filter = NULL;
  GstVaapiFilterStatus filter_status;

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

  if (vip) {
    use_native_format =
        GST_VIDEO_INFO_FORMAT (vip) == GST_VIDEO_FORMAT_ENCODED ||
        GST_VIDEO_INFO_FORMAT (vip) == GST_VIDEO_FORMAT_UNKNOWN;

    if (GST_VIDEO_INFO_WIDTH (vip) && GST_VIDEO_INFO_HEIGHT (vip)) {
      width = GST_VIDEO_INFO_WIDTH (vip);
      height = GST_VIDEO_INFO_HEIGHT (vip);
    }
  }

  if (use_native_format) {
    out_surface = gst_vaapi_surface_new (base_display,
        GST_VAAPI_CHROMA_TYPE_YUV420, width, height);
  } else {
    out_surface = gst_vaapi_surface_new_with_format (base_display,
        GST_VIDEO_INFO_FORMAT (vip), width, height, 0);
  }
  if (!out_surface)
    goto error_create_surface;

  filter = gst_vaapi_filter_new (base_display);
  if (!filter)
    goto error_create_filter;

  filter_status = gst_vaapi_filter_process (filter,
      img_surface, out_surface, flags);
  if (filter_status != GST_VAAPI_FILTER_STATUS_SUCCESS)
    goto error_convert_surface;

  gst_vaapi_surface_unref (img_surface);
  gst_object_unref (filter);
  return out_surface;

  /* ERRORS */
error_create_surface:
  GST_ERROR ("failed to create output surface format:%s size:%dx%d",
      gst_vaapi_video_format_to_string (vip ? GST_VIDEO_INFO_FORMAT (vip) :
          GST_VIDEO_FORMAT_ENCODED), width, height);
  goto error_cleanup;
error_convert_surface:
  GST_ERROR ("failed to transfer EGL image to VA surface (status = %d)",
      filter_status);
  goto error_cleanup;
error_create_filter:
  GST_ERROR ("failed to create video processing filter");
  // fall-through
error_cleanup:
  gst_mini_object_replace ((GstMiniObject **) & img_surface, NULL);
  gst_mini_object_replace ((GstMiniObject **) & out_surface, NULL);
  gst_vaapi_filter_replace (&filter, NULL);
  return NULL;
}

/**
 * gst_vaapi_surface_new_from_egl_image:
 * @display: a #GstVaapiDisplay
 * @vip: the desired (optional) #GstVideoInfo constraints
 * @image: the EGL image to import
 * @format: the EGL @image format
 * @width: the EGL @image width in pixels
 * @height: the EGL @image height in pixels
 * @flags: postprocessing flags. See #GstVaapiSurfaceRenderFlags
 *
 * Creates a new #GstVaapiSurface with a copy of the EGL image
 * contents. i.e. the input EGL @image can be disposed and the
 * resulting VA surface would still be valid with the contents at the
 * time this function was called.
 *
 * If @vip is %NULL, then the resulting VA surface will be created
 * with the same video format and size as the original @image. If @vip
 * is non-%NULL and the desired format is GST_VIDEO_FORMAT_ENCODED,
 * then the resulting VA surface will have the best "native" HW
 * format, usually NV12.
 *
 * Return value: the newly allocated #GstVaapiSurface object, or %NULL
 *   if creation from EGL image failed, or is not supported
 */
GstVaapiSurface *
gst_vaapi_surface_new_from_egl_image (GstVaapiDisplay * base_display,
    const GstVideoInfo * vip, EGLImageKHR image, GstVideoFormat format,
    guint width, guint height, guint flags)
{
  GstVaapiDisplayEGL *display;

  g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_EGL (base_display), NULL);
  g_return_val_if_fail (image != EGL_NO_IMAGE_KHR, NULL);
  g_return_val_if_fail (width > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);

  display = GST_VAAPI_DISPLAY_EGL (base_display);
  if (!display || !GST_VAAPI_IS_DISPLAY_EGL (display))
    goto error_invalid_display;
  return create_surface_from_egl_image (display, vip, image, format,
      width, height, flags);

  /* ERRORS */
error_invalid_display:
  GST_ERROR ("invalid display (NULL or not of EGL class");
  return NULL;
}

/**
 * gst_vaapi_surface_new_with_egl_image:
 * @display: a #GstVaapiDisplay
 * @image: the EGL image to import
 * @format: the EGL @image format
 * @width: the EGL @image width in pixels
 * @height: the EGL @image height in pixels
 *
 * Creates a new #GstVaapiSurface bound to an external EGL image.
 *
 * The caller maintains the lifetime of the EGL image object. In
 * particular, the EGL image shall not be destroyed before the last
 * reference to the resulting VA surface is released.
 *
 * Return value: the newly allocated #GstVaapiSurface object, or %NULL
 *   if creation from EGL image failed, or is not supported
 */
GstVaapiSurface *
gst_vaapi_surface_new_with_egl_image (GstVaapiDisplay * base_display,
    EGLImageKHR image, GstVideoFormat format, guint width, guint height)
{
  GstVaapiDisplayEGL *display;

  g_return_val_if_fail (GST_VAAPI_IS_DISPLAY_EGL (base_display), NULL);
  g_return_val_if_fail (image != EGL_NO_IMAGE_KHR, NULL);
  g_return_val_if_fail (width > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);

  display = GST_VAAPI_DISPLAY_EGL (base_display);
  if (!display || !GST_VAAPI_IS_DISPLAY_EGL (display))
    goto error_invalid_display;
  return create_surface_with_egl_image (display, image, format, width, height);

  /* ERRORS */
error_invalid_display:
  GST_ERROR ("invalid display (NULL or not of EGL class");
  return NULL;
}