1 /*
2  *  gstvaapipluginbase.c - Base GStreamer VA-API Plugin element
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *    Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
6  *  Copyright (C) 2011-2014 Intel Corporation
7  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public License
11  *  as published by the Free Software Foundation; either version 2.1
12  *  of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free
21  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  *  Boston, MA 02110-1301 USA
23  */
24 
25 #include "gstcompat.h"
26 #include <gst/vaapi/gstvaapisurface_drm.h>
27 #include <gst/base/gstpushsrc.h>
28 #include "gstvaapipluginbase.h"
29 #include "gstvaapipluginutil.h"
30 #include "gstvaapivideocontext.h"
31 #include "gstvaapivideometa.h"
32 #include "gstvaapivideobufferpool.h"
33 #if USE_GST_GL_HELPERS
34 # include <gst/gl/gl.h>
35 #endif
36 
37 GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE);
38 /* Default debug category is from the subclass */
39 #define GST_CAT_DEFAULT (plugin->debug_category)
40 
41 #define BUFFER_POOL_SINK_MIN_BUFFERS 2
42 
43 /* GstVideoContext interface */
44 static void
plugin_set_display(GstVaapiPluginBase * plugin,GstVaapiDisplay * display)45 plugin_set_display (GstVaapiPluginBase * plugin, GstVaapiDisplay * display)
46 {
47   const gchar *const display_name =
48       gst_vaapi_display_get_display_name (display);
49 
50   if (plugin->display_name && g_strcmp0 (plugin->display_name, display_name)) {
51     GST_DEBUG_OBJECT (plugin, "incompatible display name '%s', requested '%s'",
52         display_name, plugin->display_name);
53     gst_vaapi_display_replace (&plugin->display, NULL);
54   } else {
55     GST_INFO_OBJECT (plugin, "set display %" GST_PTR_FORMAT, display);
56     gst_vaapi_display_replace (&plugin->display, display);
57     plugin->display_type = gst_vaapi_display_get_display_type (display);
58     gst_vaapi_plugin_base_set_display_name (plugin, display_name);
59   }
60   gst_object_unref (display);
61 }
62 
63 /**
64  * gst_vaapi_plugin_base_set_context:
65  * @plugin: a #GstVaapiPluginBase instance
66  * @context: a #GstContext to set
67  *
68  * This is a common set_context() element's vmethod for all the
69  * GStreamer VA-API elements.
70  *
71  * It normally should be used through the macro
72  * #GST_VAAPI_PLUGIN_BASE_DEFINE_SET_CONTEXT()
73  **/
74 void
gst_vaapi_plugin_base_set_context(GstVaapiPluginBase * plugin,GstContext * context)75 gst_vaapi_plugin_base_set_context (GstVaapiPluginBase * plugin,
76     GstContext * context)
77 {
78   GstVaapiDisplay *display = NULL;
79 
80   /* gst.vaapi.app.Display is only attended _if_ the element is
81    * vaapisink and it doesn't have a display set yet */
82   if (gst_vaapi_video_context_get_display (context,
83           GST_IS_VIDEO_SINK (plugin) && !plugin->display, &display)) {
84     plugin_set_display (plugin, display);
85   }
86 #if USE_GST_GL_HELPERS
87   gst_gl_handle_set_context (GST_ELEMENT_CAST (plugin), context,
88       (GstGLDisplay **) & plugin->gl_display,
89       (GstGLContext **) & plugin->gl_other_context);
90 #endif
91 }
92 
93 void
gst_vaapi_plugin_base_init_interfaces(GType g_define_type_id)94 gst_vaapi_plugin_base_init_interfaces (GType g_define_type_id)
95 {
96 }
97 
98 static gboolean
default_has_interface(GstVaapiPluginBase * plugin,GType type)99 default_has_interface (GstVaapiPluginBase * plugin, GType type)
100 {
101   return FALSE;
102 }
103 
104 static void
default_display_changed(GstVaapiPluginBase * plugin)105 default_display_changed (GstVaapiPluginBase * plugin)
106 {
107 }
108 
109 static GstVaapiSurface *
_get_cached_surface(GstBuffer * buf)110 _get_cached_surface (GstBuffer * buf)
111 {
112   return gst_mini_object_get_qdata (GST_MINI_OBJECT (buf),
113       g_quark_from_static_string ("GstVaapiDMABufSurface"));
114 }
115 
116 static void
_set_cached_surface(GstBuffer * buf,GstVaapiSurface * surface)117 _set_cached_surface (GstBuffer * buf, GstVaapiSurface * surface)
118 {
119   return gst_mini_object_set_qdata (GST_MINI_OBJECT (buf),
120       g_quark_from_static_string ("GstVaapiDMABufSurface"), surface,
121       (GDestroyNotify) gst_vaapi_object_unref);
122 }
123 
124 static gboolean
plugin_update_sinkpad_info_from_buffer(GstVaapiPluginBase * plugin,GstBuffer * buf)125 plugin_update_sinkpad_info_from_buffer (GstVaapiPluginBase * plugin,
126     GstBuffer * buf)
127 {
128   GstVideoInfo *const vip = &plugin->sinkpad_info;
129   GstVideoMeta *vmeta;
130   guint i;
131 
132   vmeta = gst_buffer_get_video_meta (buf);
133   if (!vmeta)
134     return TRUE;
135 
136   if (GST_VIDEO_INFO_FORMAT (vip) != vmeta->format ||
137       GST_VIDEO_INFO_WIDTH (vip) != vmeta->width ||
138       GST_VIDEO_INFO_HEIGHT (vip) != vmeta->height ||
139       GST_VIDEO_INFO_N_PLANES (vip) != vmeta->n_planes)
140     return FALSE;
141 
142   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (vip); ++i) {
143     GST_VIDEO_INFO_PLANE_OFFSET (vip, i) = vmeta->offset[i];
144     GST_VIDEO_INFO_PLANE_STRIDE (vip, i) = vmeta->stride[i];
145   }
146   GST_VIDEO_INFO_SIZE (vip) = gst_buffer_get_size (buf);
147   return TRUE;
148 }
149 
150 static gboolean
is_dma_buffer(GstBuffer * buf)151 is_dma_buffer (GstBuffer * buf)
152 {
153   GstMemory *mem;
154 
155   if (gst_buffer_n_memory (buf) < 1)
156     return FALSE;
157 
158   mem = gst_buffer_peek_memory (buf, 0);
159   if (!mem || !gst_is_dmabuf_memory (mem))
160     return FALSE;
161   return TRUE;
162 }
163 
164 static gboolean
plugin_bind_dma_to_vaapi_buffer(GstVaapiPluginBase * plugin,GstBuffer * inbuf,GstBuffer * outbuf)165 plugin_bind_dma_to_vaapi_buffer (GstVaapiPluginBase * plugin,
166     GstBuffer * inbuf, GstBuffer * outbuf)
167 {
168   GstVideoInfo *const vip = &plugin->sinkpad_info;
169   GstVaapiVideoMeta *meta;
170   GstVaapiSurface *surface;
171   GstVaapiSurfaceProxy *proxy;
172   gint fd;
173 
174   fd = gst_dmabuf_memory_get_fd (gst_buffer_peek_memory (inbuf, 0));
175   if (fd < 0)
176     return FALSE;
177 
178   if (!plugin_update_sinkpad_info_from_buffer (plugin, inbuf))
179     goto error_update_sinkpad_info;
180 
181   meta = gst_buffer_get_vaapi_video_meta (outbuf);
182   g_return_val_if_fail (meta != NULL, FALSE);
183 
184   /* Check for a VASurface cached in the buffer */
185   surface = _get_cached_surface (inbuf);
186   if (!surface) {
187     /* otherwise create one and cache it */
188     surface =
189         gst_vaapi_surface_new_with_dma_buf_handle (plugin->display, fd, vip);
190     if (!surface)
191       goto error_create_surface;
192     _set_cached_surface (inbuf, surface);
193   }
194 
195   proxy = gst_vaapi_surface_proxy_new (surface);
196   if (!proxy)
197     goto error_create_proxy;
198   gst_vaapi_video_meta_set_surface_proxy (meta, proxy);
199   gst_vaapi_surface_proxy_unref (proxy);
200   gst_buffer_add_parent_buffer_meta (outbuf, inbuf);
201   return TRUE;
202 
203   /* ERRORS */
204 error_update_sinkpad_info:
205   {
206     GST_ERROR_OBJECT (plugin,
207         "failed to update sink pad video info from video meta");
208     return FALSE;
209   }
210 error_create_surface:
211   {
212     GST_ERROR_OBJECT (plugin,
213         "failed to create VA surface from dma_buf handle");
214     return FALSE;
215   }
216 error_create_proxy:
217   {
218     GST_ERROR_OBJECT (plugin,
219         "failed to create VA surface proxy from wrapped VA surface");
220     return FALSE;
221   }
222 }
223 
224 static void
plugin_reset_texture_map(GstVaapiPluginBase * plugin)225 plugin_reset_texture_map (GstVaapiPluginBase * plugin)
226 {
227   if (plugin->display)
228     gst_vaapi_display_reset_texture_map (plugin->display);
229 }
230 
231 void
gst_vaapi_plugin_base_class_init(GstVaapiPluginBaseClass * klass)232 gst_vaapi_plugin_base_class_init (GstVaapiPluginBaseClass * klass)
233 {
234   klass->has_interface = default_has_interface;
235   klass->display_changed = default_display_changed;
236 }
237 
238 void
gst_vaapi_plugin_base_init(GstVaapiPluginBase * plugin,GstDebugCategory * debug_category)239 gst_vaapi_plugin_base_init (GstVaapiPluginBase * plugin,
240     GstDebugCategory * debug_category)
241 {
242   plugin->debug_category = debug_category;
243   plugin->display_type = GST_VAAPI_DISPLAY_TYPE_ANY;
244   plugin->display_type_req = GST_VAAPI_DISPLAY_TYPE_ANY;
245 
246   /* sink pad */
247   plugin->sinkpad = gst_element_get_static_pad (GST_ELEMENT (plugin), "sink");
248   gst_video_info_init (&plugin->sinkpad_info);
249 
250   /* src pad */
251   if (!(GST_OBJECT_FLAGS (plugin) & GST_ELEMENT_FLAG_SINK))
252     plugin->srcpad = gst_element_get_static_pad (GST_ELEMENT (plugin), "src");
253   gst_video_info_init (&plugin->srcpad_info);
254 
255   plugin->enable_direct_rendering =
256       (g_getenv ("GST_VAAPI_ENABLE_DIRECT_RENDERING") != NULL);
257 }
258 
259 void
gst_vaapi_plugin_base_finalize(GstVaapiPluginBase * plugin)260 gst_vaapi_plugin_base_finalize (GstVaapiPluginBase * plugin)
261 {
262   gst_vaapi_plugin_base_close (plugin);
263   g_free (plugin->display_name);
264   if (plugin->sinkpad)
265     gst_object_unref (plugin->sinkpad);
266   if (plugin->srcpad)
267     gst_object_unref (plugin->srcpad);
268 }
269 
270 /**
271  * gst_vaapi_plugin_base_open:
272  * @plugin: a #GstVaapiPluginBase
273  *
274  * Allocates any internal resources needed for correct operation from
275  * the subclass.
276  *
277  * Returns: %TRUE if successful, %FALSE otherwise.
278  */
279 gboolean
gst_vaapi_plugin_base_open(GstVaapiPluginBase * plugin)280 gst_vaapi_plugin_base_open (GstVaapiPluginBase * plugin)
281 {
282   return TRUE;
283 }
284 
285 /**
286  * gst_vaapi_plugin_base_close:
287  * @plugin: a #GstVaapiPluginBase
288  *
289  * Deallocates all internal resources that were allocated so
290  * far. i.e. put the base plugin object into a clean state.
291  */
292 void
gst_vaapi_plugin_base_close(GstVaapiPluginBase * plugin)293 gst_vaapi_plugin_base_close (GstVaapiPluginBase * plugin)
294 {
295   /* Release vaapi textures first if exist, which refs display object */
296   plugin_reset_texture_map (plugin);
297 
298   gst_vaapi_display_replace (&plugin->display, NULL);
299   gst_object_replace (&plugin->gl_context, NULL);
300   gst_object_replace (&plugin->gl_display, NULL);
301   gst_object_replace (&plugin->gl_other_context, NULL);
302 
303   gst_caps_replace (&plugin->sinkpad_caps, NULL);
304   gst_video_info_init (&plugin->sinkpad_info);
305 
306   g_clear_object (&plugin->sinkpad_buffer_pool);
307   g_clear_object (&plugin->srcpad_buffer_pool);
308 
309   g_clear_object (&plugin->sinkpad_allocator);
310   g_clear_object (&plugin->srcpad_allocator);
311   g_clear_object (&plugin->other_srcpad_allocator);
312 
313   gst_caps_replace (&plugin->srcpad_caps, NULL);
314   gst_video_info_init (&plugin->srcpad_info);
315   gst_caps_replace (&plugin->allowed_raw_caps, NULL);
316 }
317 
318 /**
319  * gst_vaapi_plugin_base_has_display_type:
320  * @plugin: a #GstVaapiPluginBase
321  * @display_type_req: the desired #GstVaapiDisplayType
322  *
323  * Checks whether the @plugin elements already has a #GstVaapiDisplay
324  * instance compatible with type @display_type_req.
325  *
326  * Return value: %TRUE if @plugin has a compatible display, %FALSE otherwise
327  */
328 gboolean
gst_vaapi_plugin_base_has_display_type(GstVaapiPluginBase * plugin,GstVaapiDisplayType display_type_req)329 gst_vaapi_plugin_base_has_display_type (GstVaapiPluginBase * plugin,
330     GstVaapiDisplayType display_type_req)
331 {
332   GstVaapiDisplayType display_type;
333 
334   if (!plugin->display)
335     return FALSE;
336 
337   display_type = plugin->display_type;
338   if (gst_vaapi_display_type_is_compatible (display_type, display_type_req))
339     return TRUE;
340 
341   display_type = gst_vaapi_display_get_class_type (plugin->display);
342   if (gst_vaapi_display_type_is_compatible (display_type, display_type_req))
343     return TRUE;
344   return FALSE;
345 }
346 
347 /**
348  * gst_vaapi_plugin_base_set_display_type:
349  * @plugin: a #GstVaapiPluginBase
350  * @display_type: the new request #GstVaapiDisplayType
351  *
352  * Requests a new display type. The change is effective at the next
353  * call to gst_vaapi_plugin_base_ensure_display().
354  */
355 void
gst_vaapi_plugin_base_set_display_type(GstVaapiPluginBase * plugin,GstVaapiDisplayType display_type)356 gst_vaapi_plugin_base_set_display_type (GstVaapiPluginBase * plugin,
357     GstVaapiDisplayType display_type)
358 {
359   plugin->display_type_req = display_type;
360 }
361 
362 /**
363  * gst_vaapi_plugin_base_set_display_name:
364  * @plugin: a #GstVaapiPluginBase
365  * @display_name: the new display name to match
366  *
367  * Sets the name of the display to look for. The change is effective
368  * at the next call to gst_vaapi_plugin_base_ensure_display().
369  */
370 void
gst_vaapi_plugin_base_set_display_name(GstVaapiPluginBase * plugin,const gchar * display_name)371 gst_vaapi_plugin_base_set_display_name (GstVaapiPluginBase * plugin,
372     const gchar * display_name)
373 {
374   g_free (plugin->display_name);
375   plugin->display_name = g_strdup (display_name);
376 }
377 
378 /**
379  * gst_vaapi_plugin_base_ensure_display:
380  * @plugin: a #GstVaapiPluginBase
381  *
382  * Ensures the display stored in @plugin complies with the requested
383  * display type constraints.
384  *
385  * Returns: %TRUE if the display was created to match the requested
386  *   type, %FALSE otherwise.
387  */
388 gboolean
gst_vaapi_plugin_base_ensure_display(GstVaapiPluginBase * plugin)389 gst_vaapi_plugin_base_ensure_display (GstVaapiPluginBase * plugin)
390 {
391   if (gst_vaapi_plugin_base_has_display_type (plugin, plugin->display_type_req))
392     return TRUE;
393   gst_vaapi_display_replace (&plugin->display, NULL);
394 
395   if (!gst_vaapi_ensure_display (GST_ELEMENT (plugin),
396           plugin->display_type_req))
397     return FALSE;
398   plugin->display_type = gst_vaapi_display_get_display_type (plugin->display);
399 
400   GST_VAAPI_PLUGIN_BASE_GET_CLASS (plugin)->display_changed (plugin);
401   return TRUE;
402 }
403 
404 static gboolean
gst_vaapi_buffer_pool_caps_is_equal(GstBufferPool * pool,GstCaps * newcaps)405 gst_vaapi_buffer_pool_caps_is_equal (GstBufferPool * pool, GstCaps * newcaps)
406 {
407   GstStructure *config;
408   GstCaps *caps;
409   gboolean ret;
410 
411   caps = NULL;
412   ret = FALSE;
413   config = gst_buffer_pool_get_config (pool);
414   if (gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
415     ret = gst_caps_is_equal (newcaps, caps);
416   gst_structure_free (config);
417 
418   return ret;
419 }
420 
421 static inline gboolean
reset_allocator(GstAllocator * allocator,GstVideoInfo * vinfo)422 reset_allocator (GstAllocator * allocator, GstVideoInfo * vinfo)
423 {
424   const GstVideoInfo *orig_vi;
425 
426   if (!allocator)
427     return TRUE;
428 
429   orig_vi = gst_allocator_get_vaapi_video_info (allocator, NULL);
430   if (!gst_video_info_changed (orig_vi, vinfo))
431     return FALSE;
432 
433   gst_object_unref (allocator);
434   return TRUE;
435 }
436 
437 static gboolean
ensure_sinkpad_allocator(GstVaapiPluginBase * plugin,GstCaps * caps,guint * size)438 ensure_sinkpad_allocator (GstVaapiPluginBase * plugin, GstCaps * caps,
439     guint * size)
440 {
441   GstVideoInfo vinfo;
442   const GstVideoInfo *image_info;
443   GstVaapiImageUsageFlags usage_flag =
444       GST_VAAPI_IMAGE_USAGE_FLAG_NATIVE_FORMATS;
445 
446   if (!gst_video_info_from_caps (&vinfo, caps))
447     goto error_invalid_caps;
448 
449   if (!reset_allocator (plugin->sinkpad_allocator, &vinfo))
450     goto bail;
451 
452   /* enable direct upload if upstream requests raw video */
453   if (gst_caps_is_video_raw (caps)) {
454     usage_flag = GST_VAAPI_IMAGE_USAGE_FLAG_DIRECT_UPLOAD;
455     GST_INFO_OBJECT (plugin, "enabling direct upload in sink allocator");
456   }
457   plugin->sinkpad_allocator =
458       gst_vaapi_video_allocator_new (plugin->display, &vinfo, 0, usage_flag);
459 
460 bail:
461   if (!plugin->sinkpad_allocator)
462     goto error_create_allocator;
463 
464   image_info =
465       gst_allocator_get_vaapi_video_info (plugin->sinkpad_allocator, NULL);
466   g_assert (image_info);        /* allocator ought set its image info */
467 
468   /* update the size with the one generated by the allocator */
469   *size = GST_VIDEO_INFO_SIZE (image_info);
470 
471   return TRUE;
472 
473   /* ERRORS */
474 error_invalid_caps:
475   {
476     GST_ERROR_OBJECT (plugin, "invalid caps %" GST_PTR_FORMAT, caps);
477     return FALSE;
478   }
479 error_create_allocator:
480   {
481     GST_ERROR_OBJECT (plugin, "failed to create sink pad's allocator");
482     return FALSE;
483   }
484 }
485 
486 static inline guint
get_dmabuf_surface_allocation_flags(void)487 get_dmabuf_surface_allocation_flags (void)
488 {
489   /* @FIXME: fetch the real devices ids */
490   /* Pair vendor/device identifies an unique physical device. */
491   guint va_vendor_id = 0x00;
492   guint va_device_id = 0x00;
493   guint gl_vendor_id = 0x00;
494   guint gl_device_id = 0x00;
495 
496   /* Requires linear memory only if fd export is done on a different
497    * device than the device where the fd is imported. */
498   gboolean same_physical_device = va_vendor_id == gl_vendor_id
499       && va_device_id == gl_device_id;
500 
501   if (same_physical_device)
502     return 0;
503   return GST_VAAPI_SURFACE_ALLOC_FLAG_LINEAR_STORAGE;
504 }
505 
506 static inline GstAllocator *
create_dmabuf_srcpad_allocator(GstVaapiPluginBase * plugin,GstVideoInfo * vinfo,gboolean check_for_map)507 create_dmabuf_srcpad_allocator (GstVaapiPluginBase * plugin,
508     GstVideoInfo * vinfo, gboolean check_for_map)
509 {
510   GstAllocator *allocator;
511 
512   if (!GST_IS_VIDEO_DECODER (plugin) && !GST_IS_BASE_TRANSFORM (plugin))
513     return NULL;
514 
515   allocator = gst_vaapi_dmabuf_allocator_new (plugin->display, vinfo,
516       get_dmabuf_surface_allocation_flags (), GST_PAD_SRC);
517   if (!allocator || !check_for_map)
518     return allocator;
519 
520   /* the dmabuf allocator *must* be capable to map a buffer with raw
521    * caps and the there's no evidence of downstream dmabuf
522    * importation */
523   if (!gst_vaapi_dmabuf_can_map (plugin->display, allocator)) {
524     GST_INFO_OBJECT (plugin, "dmabuf allocator generates unmappable buffers");
525     gst_object_replace ((GstObject **) & allocator, NULL);
526   }
527 
528   return allocator;
529 }
530 
531 static gboolean
ensure_srcpad_allocator(GstVaapiPluginBase * plugin,GstVideoInfo * vinfo,GstCaps * caps)532 ensure_srcpad_allocator (GstVaapiPluginBase * plugin, GstVideoInfo * vinfo,
533     GstCaps * caps)
534 {
535   gboolean different_caps;
536   const GstVideoInfo *image_info;
537 
538   if (!reset_allocator (plugin->srcpad_allocator, vinfo))
539     goto valid_allocator;
540 
541   plugin->srcpad_allocator = NULL;
542   if (caps && gst_caps_is_video_raw (caps)) {
543     GstAllocator *allocator = create_dmabuf_srcpad_allocator (plugin, vinfo,
544         !plugin->srcpad_can_dmabuf);
545     plugin->srcpad_allocator = allocator;
546   } else if (caps && gst_vaapi_caps_feature_contains (caps,
547           GST_VAAPI_CAPS_FEATURE_DMABUF)) {
548     plugin->srcpad_allocator =
549         create_dmabuf_srcpad_allocator (plugin, vinfo, FALSE);
550     if (!plugin->srcpad_allocator)
551       goto error_create_allocator;
552   }
553 
554   if (!plugin->srcpad_allocator) {
555     GstVaapiImageUsageFlags usage_flag =
556         GST_VAAPI_IMAGE_USAGE_FLAG_NATIVE_FORMATS;
557 
558     if (plugin->enable_direct_rendering) {
559       usage_flag = GST_VAAPI_IMAGE_USAGE_FLAG_DIRECT_RENDER;
560       GST_INFO_OBJECT (plugin, "enabling direct rendering in source allocator");
561     }
562 
563     plugin->srcpad_allocator =
564         gst_vaapi_video_allocator_new (plugin->display, vinfo, 0, usage_flag);
565   }
566 
567   if (!plugin->srcpad_allocator)
568     goto error_create_allocator;
569 
570 valid_allocator:
571   image_info =
572       gst_allocator_get_vaapi_video_info (plugin->srcpad_allocator, NULL);
573   g_assert (image_info);        /* both allocators ought set its image
574                                  * info */
575 
576   /* update the size with the one generated by the allocator */
577   GST_VIDEO_INFO_SIZE (vinfo) = GST_VIDEO_INFO_SIZE (image_info);
578 
579   /* the received caps are the "allocation caps" which may be
580    * different from the "negotiation caps". In this case, we should
581    * indicate the allocator to store the negotiation caps since they
582    * are the one should be used for frame mapping with GstVideoMeta */
583   different_caps = GST_IS_VIDEO_DECODER (plugin) && plugin->srcpad_caps &&
584       !gst_caps_is_strictly_equal (plugin->srcpad_caps, caps);
585 
586   if (different_caps) {
587     guint i;
588     GstVideoInfo vi = plugin->srcpad_info;
589 
590     /* update the planes and the size with the allocator image/surface
591      * info, but not the resolution */
592     for (i = 0; i < GST_VIDEO_INFO_N_PLANES (image_info); i++) {
593       GST_VIDEO_INFO_PLANE_OFFSET (&vi, i) =
594           GST_VIDEO_INFO_PLANE_OFFSET (image_info, i);
595       GST_VIDEO_INFO_PLANE_STRIDE (&vi, i) =
596           GST_VIDEO_INFO_PLANE_STRIDE (image_info, i);
597     }
598     GST_VIDEO_INFO_SIZE (&vi) = GST_VIDEO_INFO_SIZE (image_info);
599     gst_allocator_set_vaapi_negotiated_video_info (plugin->srcpad_allocator,
600         &vi);
601   }
602 
603   return TRUE;
604 
605   /* ERRORS */
606 error_create_allocator:
607   {
608     GST_ERROR_OBJECT (plugin, "failed to create src pad's allocator");
609     return FALSE;
610   }
611 }
612 
613 /**
614  * gst_vaapi_plugin_base_create_pool:
615  * @plugin: a #GstVaapiPluginBase
616  * @caps: the initial #GstCaps for the resulting buffer pool
617  * @size: the size of each buffer, not including prefix and padding
618  * @options: a set of #GstVaapiVideoBufferPoolOption encoded as bit-wise
619  * @allocator: (allow-none): the #GstAllocator to use or %NULL
620  *
621  * Create an instance of #GstVaapiVideoBufferPool
622  *
623  * Returns: (transfer full): a new allocated #GstBufferPool
624  **/
625 static GstBufferPool *
gst_vaapi_plugin_base_create_pool(GstVaapiPluginBase * plugin,GstCaps * caps,gsize size,guint min_buffers,guint max_buffers,guint options,GstAllocator * allocator)626 gst_vaapi_plugin_base_create_pool (GstVaapiPluginBase * plugin, GstCaps * caps,
627     gsize size, guint min_buffers, guint max_buffers, guint options,
628     GstAllocator * allocator)
629 {
630   GstBufferPool *pool;
631   GstStructure *config;
632 
633   if (!(pool = gst_vaapi_video_buffer_pool_new (plugin->display)))
634     goto error_create_pool;
635 
636   config = gst_buffer_pool_get_config (pool);
637   gst_buffer_pool_config_set_params (config, caps, size, min_buffers,
638       max_buffers);
639   gst_buffer_pool_config_add_option (config,
640       GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META);
641   if (options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_META) {
642     gst_buffer_pool_config_add_option (config,
643         GST_BUFFER_POOL_OPTION_VIDEO_META);
644   }
645   if (options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT) {
646     gst_buffer_pool_config_add_option (config,
647         GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
648   }
649 #if (USE_GLX || USE_EGL)
650   if (options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_GL_TEXTURE_UPLOAD) {
651     gst_buffer_pool_config_add_option (config,
652         GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META);
653   }
654 #endif
655   if (allocator)
656     gst_buffer_pool_config_set_allocator (config, allocator, NULL);
657   if (!gst_buffer_pool_set_config (pool, config)) {
658     config = gst_buffer_pool_get_config (pool);
659 
660     if (!gst_buffer_pool_config_validate_params (config, caps, size,
661             min_buffers, max_buffers)) {
662       gst_structure_free (config);
663       goto error_pool_config;
664     }
665 
666     if (!gst_buffer_pool_set_config (pool, config))
667       goto error_pool_config;
668   }
669   return pool;
670 
671   /* ERRORS */
672 error_create_pool:
673   {
674     GST_ERROR_OBJECT (plugin, "failed to create buffer pool");
675     return NULL;
676   }
677 error_pool_config:
678   {
679     gst_object_unref (pool);
680     GST_ELEMENT_ERROR (plugin, RESOURCE, SETTINGS,
681         ("Failed to configure the buffer pool"),
682         ("Configuration is most likely invalid, please report this issue."));
683     return NULL;
684   }
685 }
686 
687 /**
688  * ensure_sinkpad_buffer_pool:
689  * @plugin: a #GstVaapiPluginBase
690  * @caps: the initial #GstCaps for the resulting buffer pool
691  *
692  * Makes sure the sink pad video buffer pool is created with the
693  * appropriate @caps.
694  *
695  * Returns: %TRUE if successful, %FALSE otherwise.
696  */
697 static gboolean
ensure_sinkpad_buffer_pool(GstVaapiPluginBase * plugin,GstCaps * caps)698 ensure_sinkpad_buffer_pool (GstVaapiPluginBase * plugin, GstCaps * caps)
699 {
700   GstBufferPool *pool;
701   guint size;
702 
703   /* video decoders don't use a buffer pool in the sink pad */
704   if (GST_IS_VIDEO_DECODER (plugin))
705     return TRUE;
706 
707   if (!gst_vaapi_plugin_base_ensure_display (plugin))
708     return FALSE;
709 
710   if (plugin->sinkpad_buffer_pool) {
711     if (gst_vaapi_buffer_pool_caps_is_equal (plugin->sinkpad_buffer_pool, caps))
712       return TRUE;
713     gst_buffer_pool_set_active (plugin->sinkpad_buffer_pool, FALSE);
714     g_clear_object (&plugin->sinkpad_buffer_pool);
715     g_clear_object (&plugin->sinkpad_allocator);
716     plugin->sinkpad_buffer_size = 0;
717   }
718 
719   if (!ensure_sinkpad_allocator (plugin, caps, &size))
720     return FALSE;
721 
722   pool =
723       gst_vaapi_plugin_base_create_pool (plugin, caps, size,
724       BUFFER_POOL_SINK_MIN_BUFFERS, 0,
725       GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_META, plugin->sinkpad_allocator);
726   if (!pool)
727     return FALSE;
728 
729   plugin->sinkpad_buffer_pool = pool;
730   plugin->sinkpad_buffer_size = size;
731   return TRUE;
732 }
733 
734 /**
735  * gst_vaapi_plugin_base_set_caps:
736  * @plugin: a #GstVaapiPluginBase
737  * @incaps: the sink pad (input) caps
738  * @outcaps: the src pad (output) caps
739  *
740  * Notifies the base plugin object of the new input and output caps,
741  * obtained from the subclass.
742  *
743  * Returns: %TRUE if the update of caps was successful, %FALSE otherwise.
744  */
745 gboolean
gst_vaapi_plugin_base_set_caps(GstVaapiPluginBase * plugin,GstCaps * incaps,GstCaps * outcaps)746 gst_vaapi_plugin_base_set_caps (GstVaapiPluginBase * plugin, GstCaps * incaps,
747     GstCaps * outcaps)
748 {
749   if (incaps && incaps != plugin->sinkpad_caps) {
750     if (!gst_video_info_from_caps (&plugin->sinkpad_info, incaps))
751       return FALSE;
752     gst_caps_replace (&plugin->sinkpad_caps, incaps);
753     plugin->sinkpad_caps_is_raw = !gst_caps_has_vaapi_surface (incaps);
754   }
755 
756   if (outcaps && outcaps != plugin->srcpad_caps) {
757     if (!gst_video_info_from_caps (&plugin->srcpad_info, outcaps))
758       return FALSE;
759     if (plugin->srcpad_buffer_pool
760         && !gst_vaapi_buffer_pool_caps_is_equal (plugin->srcpad_buffer_pool,
761             outcaps)) {
762       gst_buffer_pool_set_active (plugin->srcpad_buffer_pool, FALSE);
763       g_clear_object (&plugin->srcpad_buffer_pool);
764       g_clear_object (&plugin->srcpad_allocator);
765       plugin_reset_texture_map (plugin);
766     }
767     gst_caps_replace (&plugin->srcpad_caps, outcaps);
768   }
769 
770   if (!ensure_sinkpad_buffer_pool (plugin, plugin->sinkpad_caps))
771     return FALSE;
772   return TRUE;
773 }
774 
775 /**
776  * gst_vaapi_plugin_base_propose_allocation:
777  * @plugin: a #GstVaapiPluginBase
778  * @query: the allocation query to configure
779  *
780  * Proposes allocation parameters to the upstream elements.
781  *
782  * Returns: %TRUE if successful, %FALSE otherwise.
783  */
784 gboolean
gst_vaapi_plugin_base_propose_allocation(GstVaapiPluginBase * plugin,GstQuery * query)785 gst_vaapi_plugin_base_propose_allocation (GstVaapiPluginBase * plugin,
786     GstQuery * query)
787 {
788   GstCaps *caps = NULL;
789   GstBufferPool *pool = NULL;
790   gboolean need_pool;
791   guint size = 0, n_allocators;
792 
793   gst_query_parse_allocation (query, &caps, &need_pool);
794   if (!caps)
795     goto error_no_caps;
796 
797   if (!ensure_sinkpad_allocator (plugin, caps, &size))
798     return FALSE;
799 
800   if (need_pool) {
801     pool = gst_vaapi_plugin_base_create_pool (plugin, caps, size,
802         BUFFER_POOL_SINK_MIN_BUFFERS, 0,
803         GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_META,
804         plugin->sinkpad_allocator);
805     if (!pool)
806       return FALSE;
807   }
808 
809   /* Set sinkpad allocator as the last allocation param.
810    *
811    * If there's none, set system's allocator first and VAAPI allocator
812    * second
813    */
814   n_allocators = gst_query_get_n_allocation_params (query);
815   if (n_allocators == 0) {
816     GstAllocator *allocator;
817 
818     allocator = gst_allocator_find (GST_ALLOCATOR_SYSMEM);
819     gst_query_add_allocation_param (query, allocator, NULL);
820     gst_object_unref (allocator);
821   }
822   gst_query_add_allocation_param (query, plugin->sinkpad_allocator, NULL);
823 
824   gst_query_add_allocation_pool (query, pool, size,
825       BUFFER_POOL_SINK_MIN_BUFFERS, 0);
826   if (pool)
827     gst_object_unref (pool);
828 
829   gst_query_add_allocation_meta (query, GST_VAAPI_VIDEO_META_API_TYPE, NULL);
830   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
831   return TRUE;
832 
833   /* ERRORS */
834 error_no_caps:
835   {
836     GST_INFO_OBJECT (plugin, "no caps specified");
837     return FALSE;
838   }
839 }
840 
841 /**
842  * gst_vaapi_plugin_base_decide_allocation:
843  * @plugin: a #GstVaapiPluginBase
844  * @query: the allocation query to parse
845  * @feature: the desired #GstVaapiCapsFeature, or zero to find the
846  *   preferred one
847  *
848  * Decides allocation parameters for the downstream elements.
849  *
850  * Returns: %TRUE if successful, %FALSE otherwise.
851  */
852 gboolean
gst_vaapi_plugin_base_decide_allocation(GstVaapiPluginBase * plugin,GstQuery * query)853 gst_vaapi_plugin_base_decide_allocation (GstVaapiPluginBase * plugin,
854     GstQuery * query)
855 {
856   GstCaps *caps = NULL;
857   GstBufferPool *pool;
858   GstVideoInfo vi;
859   guint i, size, min, max, pool_options, num_allocators;
860   gint index_allocator;
861   gboolean update_pool = FALSE;
862 #if (USE_GLX || USE_EGL)
863   guint idx;
864 #endif
865 
866   gst_query_parse_allocation (query, &caps, NULL);
867   if (!caps)
868     goto error_no_caps;
869 
870   pool_options = 0;
871   if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL))
872     pool_options |= GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_META;
873 
874 #if (USE_GLX || USE_EGL)
875   if (gst_query_find_allocation_meta (query,
876           GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, &idx) &&
877       gst_vaapi_caps_feature_contains (caps,
878           GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META))
879     pool_options |= GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_GL_TEXTURE_UPLOAD;
880 
881 #if USE_GST_GL_HELPERS
882   if (!plugin->gl_context &&
883       (pool_options & GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_GL_TEXTURE_UPLOAD)) {
884     const GstStructure *params;
885     GstObject *gl_context;
886 
887     gst_query_parse_nth_allocation_meta (query, idx, &params);
888     if (params) {
889       if (gst_structure_get (params, "gst.gl.GstGLContext", GST_TYPE_GL_CONTEXT,
890               &gl_context, NULL) && gl_context) {
891         gst_vaapi_plugin_base_set_gl_context (plugin, gl_context);
892         gst_vaapi_plugin_base_set_srcpad_can_dmabuf (plugin, gl_context);
893         gst_object_unref (gl_context);
894       }
895     }
896   }
897 #endif
898 #endif
899 
900   /* Make sure the display we pass down to the buffer pool is actually
901      the expected one, especially when the downstream element requires
902      a GLX or EGL display */
903   if (!gst_vaapi_plugin_base_ensure_display (plugin))
904     goto error_ensure_display;
905 
906   if (!gst_video_info_from_caps (&vi, caps))
907     goto error_invalid_caps;
908   gst_video_info_force_nv12_if_encoded (&vi);
909 
910   index_allocator = -1;
911   num_allocators = gst_query_get_n_allocation_params (query);
912   for (i = 0; i < num_allocators; i++) {
913     GstAllocator *allocator = NULL;
914     GstAllocationParams params;
915 
916     gst_query_parse_nth_allocation_param (query, i, &allocator, &params);
917     if (!allocator)
918       continue;
919 
920     /* Let's keep the the first allocator if it is not VA-API. It
921      * might be used if it is required to copy the output frame to a
922      * new buffer */
923     if (i == 0
924         && g_strcmp0 (allocator->mem_type, GST_VAAPI_VIDEO_MEMORY_NAME) != 0) {
925       if (plugin->other_srcpad_allocator)
926         gst_object_unref (plugin->other_srcpad_allocator);
927       plugin->other_srcpad_allocator = allocator;
928       plugin->other_allocator_params = params;
929       continue;
930     }
931 
932     if (g_strcmp0 (allocator->mem_type, GST_VAAPI_VIDEO_MEMORY_NAME) == 0) {
933       GST_DEBUG_OBJECT (plugin, "found vaapi allocator in query %"
934           GST_PTR_FORMAT, allocator);
935       index_allocator = i;
936       if (plugin->srcpad_allocator)
937         gst_object_unref (plugin->srcpad_allocator);
938       plugin->srcpad_allocator = allocator;
939       break;
940     }
941     gst_object_unref (allocator);
942   }
943 
944   if (gst_query_get_n_allocation_pools (query) > 0) {
945     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
946     update_pool = TRUE;
947     size = MAX (size, GST_VIDEO_INFO_SIZE (&vi));
948     if (pool) {
949       /* Check whether downstream element proposed a bufferpool but did
950          not provide a correct propose_allocation() implementation */
951       if (gst_buffer_pool_has_option (pool,
952               GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT))
953         pool_options |= GST_VAAPI_VIDEO_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT;
954 
955       /* GstVaapiVideoMeta is mandatory, and this implies VA surface memory */
956       if (!gst_buffer_pool_has_option (pool,
957               GST_BUFFER_POOL_OPTION_VAAPI_VIDEO_META)) {
958         GST_INFO_OBJECT (plugin, "ignoring non-VAAPI pool: %" GST_PTR_FORMAT,
959             pool);
960         g_clear_object (&pool);
961       }
962     }
963   } else {
964     pool = NULL;
965     size = GST_VIDEO_INFO_SIZE (&vi);
966     min = max = 0;
967   }
968 
969   if (!pool) {
970     if (!ensure_srcpad_allocator (plugin, &vi, caps))
971       goto error;
972     size = GST_VIDEO_INFO_SIZE (&vi);   /* size might be updated by
973                                          * allocator */
974     pool = gst_vaapi_plugin_base_create_pool (plugin, caps, size, min, max,
975         pool_options, plugin->srcpad_allocator);
976     if (!pool)
977       goto error;
978   }
979 
980   if (update_pool)
981     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
982   else
983     gst_query_add_allocation_pool (query, pool, size, min, max);
984 
985   /* allocator might be updated by ensure_srcpad_allocator() */
986   if (plugin->srcpad_allocator) {
987     if (index_allocator > 0) {
988       gst_query_set_nth_allocation_param (query, index_allocator,
989           plugin->srcpad_allocator, NULL);
990     } else {
991       GST_DEBUG_OBJECT (plugin, "adding allocator in query %" GST_PTR_FORMAT,
992           plugin->srcpad_allocator);
993       gst_query_add_allocation_param (query, plugin->srcpad_allocator, NULL);
994     }
995   }
996 
997   g_clear_object (&plugin->srcpad_buffer_pool);
998   plugin->srcpad_buffer_pool = pool;
999 
1000   /* if downstream doesn't support GstVideoMeta, and the negotiated
1001    * caps are raw video, and the used allocator is the VA-API one, we
1002    * should copy the VA-API frame into a dumb buffer */
1003   plugin->copy_output_frame = gst_vaapi_video_buffer_pool_copy_buffer (pool);
1004 
1005   return TRUE;
1006 
1007   /* ERRORS */
1008 error_no_caps:
1009   {
1010     GST_ERROR_OBJECT (plugin, "no caps specified");
1011     return FALSE;
1012   }
1013 error_invalid_caps:
1014   {
1015     GST_ERROR_OBJECT (plugin, "invalid caps %" GST_PTR_FORMAT, caps);
1016     return FALSE;
1017   }
1018 error_ensure_display:
1019   {
1020     GST_ERROR_OBJECT (plugin, "failed to ensure display of type %d",
1021         plugin->display_type_req);
1022     return FALSE;
1023   }
1024 error:
1025   {
1026     /* error message already sent */
1027     return FALSE;
1028   }
1029 }
1030 
1031 /**
1032  * gst_vaapi_plugin_base_get_input_buffer:
1033  * @plugin: a #GstVaapiPluginBase
1034  * @inbuf: the sink pad (input) buffer
1035  * @outbuf_ptr: the pointer to location to the VA surface backed buffer
1036  *
1037  * Acquires the sink pad (input) buffer as a VA surface backed
1038  * buffer. This is mostly useful for raw YUV buffers, as source
1039  * buffers that are already backed as a VA surface are passed
1040  * verbatim.
1041  *
1042  * Returns: #GST_FLOW_OK if the buffer could be acquired
1043  */
1044 GstFlowReturn
gst_vaapi_plugin_base_get_input_buffer(GstVaapiPluginBase * plugin,GstBuffer * inbuf,GstBuffer ** outbuf_ptr)1045 gst_vaapi_plugin_base_get_input_buffer (GstVaapiPluginBase * plugin,
1046     GstBuffer * inbuf, GstBuffer ** outbuf_ptr)
1047 {
1048   GstVaapiVideoMeta *meta;
1049   GstBuffer *outbuf;
1050   GstVideoFrame src_frame, out_frame;
1051   gboolean success;
1052 
1053   g_return_val_if_fail (inbuf != NULL, GST_FLOW_ERROR);
1054   g_return_val_if_fail (outbuf_ptr != NULL, GST_FLOW_ERROR);
1055 
1056   meta = gst_buffer_get_vaapi_video_meta (inbuf);
1057   if (meta) {
1058     *outbuf_ptr = gst_buffer_ref (inbuf);
1059     return GST_FLOW_OK;
1060   }
1061 
1062   if (!plugin->sinkpad_caps_is_raw)
1063     goto error_invalid_buffer;
1064 
1065   if (!plugin->sinkpad_buffer_pool)
1066     goto error_no_pool;
1067 
1068   if (!gst_buffer_pool_is_active (plugin->sinkpad_buffer_pool) &&
1069       !gst_buffer_pool_set_active (plugin->sinkpad_buffer_pool, TRUE))
1070     goto error_active_pool;
1071 
1072   outbuf = NULL;
1073   if (gst_buffer_pool_acquire_buffer (plugin->sinkpad_buffer_pool,
1074           &outbuf, NULL) != GST_FLOW_OK)
1075     goto error_create_buffer;
1076 
1077   if (is_dma_buffer (inbuf)) {
1078     if (!plugin_bind_dma_to_vaapi_buffer (plugin, inbuf, outbuf))
1079       goto error_bind_dma_buffer;
1080     goto done;
1081   }
1082 
1083   if (!gst_video_frame_map (&src_frame, &plugin->sinkpad_info, inbuf,
1084           GST_MAP_READ))
1085     goto error_map_src_buffer;
1086 
1087   if (!gst_video_frame_map (&out_frame, &plugin->sinkpad_info, outbuf,
1088           GST_MAP_WRITE))
1089     goto error_map_dst_buffer;
1090 
1091   success = gst_video_frame_copy (&out_frame, &src_frame);
1092   gst_video_frame_unmap (&out_frame);
1093   gst_video_frame_unmap (&src_frame);
1094   if (!success)
1095     goto error_copy_buffer;
1096 
1097 done:
1098   if (!gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_FLAGS |
1099           GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_META, 0, -1))
1100     return GST_FLOW_ERROR;
1101   *outbuf_ptr = outbuf;
1102   return GST_FLOW_OK;
1103 
1104   /* ERRORS */
1105 error_no_pool:
1106   {
1107     GST_ELEMENT_ERROR (plugin, STREAM, FAILED,
1108         ("no buffer pool was negotiated"), ("no buffer pool was negotiated"));
1109     return GST_FLOW_ERROR;
1110   }
1111 error_active_pool:
1112   {
1113     GST_ELEMENT_ERROR (plugin, STREAM, FAILED,
1114         ("failed to activate buffer pool"), ("failed to activate buffer pool"));
1115     return GST_FLOW_ERROR;
1116   }
1117 error_map_dst_buffer:
1118   {
1119     gst_video_frame_unmap (&src_frame);
1120     // fall-through
1121   }
1122 error_map_src_buffer:
1123   {
1124     GST_WARNING ("failed to map buffer");
1125     gst_buffer_unref (outbuf);
1126     return GST_FLOW_NOT_SUPPORTED;
1127   }
1128 
1129   /* ERRORS */
1130 error_invalid_buffer:
1131   {
1132     GST_ELEMENT_ERROR (plugin, STREAM, FAILED,
1133         ("failed to validate source buffer"),
1134         ("failed to validate source buffer"));
1135     return GST_FLOW_ERROR;
1136   }
1137 error_create_buffer:
1138   {
1139     GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"),
1140         ("failed to create buffer"));
1141     return GST_FLOW_ERROR;
1142   }
1143 error_bind_dma_buffer:
1144   {
1145     GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"),
1146         ("failed to bind dma_buf to VA surface buffer"));
1147     gst_buffer_unref (outbuf);
1148     return GST_FLOW_ERROR;
1149   }
1150 error_copy_buffer:
1151   {
1152     GST_WARNING_OBJECT (plugin, "failed to upload buffer to VA surface");
1153     gst_buffer_unref (outbuf);
1154     return GST_FLOW_NOT_SUPPORTED;
1155   }
1156 }
1157 
1158 /**
1159  * gst_vaapi_plugin_base_set_gl_context:
1160  * @plugin: a #GstVaapiPluginBase
1161  * @object: the new GL context from downstream
1162  *
1163  * Registers the new GL context. The change is effective at the next
1164  * call to gst_vaapi_plugin_base_ensure_display(), where the
1165  * underlying display object could be re-allocated to fit the GL
1166  * context needs
1167  */
1168 void
gst_vaapi_plugin_base_set_gl_context(GstVaapiPluginBase * plugin,GstObject * object)1169 gst_vaapi_plugin_base_set_gl_context (GstVaapiPluginBase * plugin,
1170     GstObject * object)
1171 {
1172 #if USE_GST_GL_HELPERS
1173   GstGLContext *const gl_context = GST_GL_CONTEXT (object);
1174   GstVaapiDisplayType display_type;
1175 
1176   if (plugin->gl_context == object)
1177     return;
1178 
1179   gst_object_replace (&plugin->gl_context, object);
1180 
1181   switch (gst_gl_context_get_gl_platform (gl_context)) {
1182 #if USE_GLX
1183     case GST_GL_PLATFORM_GLX:
1184       display_type = GST_VAAPI_DISPLAY_TYPE_GLX;
1185       break;
1186 #endif
1187     case GST_GL_PLATFORM_EGL:
1188 #if USE_EGL
1189       display_type = GST_VAAPI_DISPLAY_TYPE_EGL;
1190       break;
1191 #endif
1192     default:
1193       display_type = plugin->display_type;
1194       break;
1195   }
1196   GST_INFO_OBJECT (plugin, "GL context: %" GST_PTR_FORMAT, plugin->gl_context);
1197   gst_vaapi_plugin_base_set_display_type (plugin, display_type);
1198 #endif
1199 }
1200 
1201 /**
1202  * gst_vaapi_plugin_base_create_gl_context:
1203  * @plugin: a #GstVaapiPluginBase
1204  *
1205  * It queries downstream and upstream for a #GstGLDisplay and a other
1206  * #GstGLContext. If not found, a new #GstGLDisplay and #GstGLContext
1207  * are created, if it is possible.
1208  *
1209  * Returns: (transfer full) a new created #GstGLContext or %NULL
1210  **/
1211 GstObject *
gst_vaapi_plugin_base_create_gl_context(GstVaapiPluginBase * plugin)1212 gst_vaapi_plugin_base_create_gl_context (GstVaapiPluginBase * plugin)
1213 {
1214 #if USE_GST_GL_HELPERS
1215   GstGLContext *gl_other_context = NULL, *gl_context = NULL;
1216   GstGLDisplay *gl_display = NULL;
1217 
1218   if (!plugin->gl_display)
1219     return NULL;
1220 
1221   gl_display = (GstGLDisplay *) plugin->gl_display;
1222   if (gst_gl_display_get_handle_type (gl_display) == GST_GL_DISPLAY_TYPE_ANY)
1223     goto no_valid_gl_display;
1224   gl_other_context = (GstGLContext *) plugin->gl_other_context;
1225 
1226   GST_INFO_OBJECT (plugin, "creating a new GstGL context");
1227 
1228   GST_OBJECT_LOCK (gl_display);
1229   do {
1230     if (gl_context)
1231       gst_object_unref (gl_context);
1232     gl_context = gst_gl_display_get_gl_context_for_thread (gl_display, NULL);
1233     if (!gl_context) {
1234       if (!gst_gl_display_create_context (gl_display, gl_other_context,
1235               &gl_context, NULL))
1236         break;
1237     }
1238   } while (!gst_gl_display_add_context (gl_display, gl_context));
1239   GST_OBJECT_UNLOCK (gl_display);
1240 
1241   return GST_OBJECT_CAST (gl_context);
1242 
1243   /* ERRORS */
1244 no_valid_gl_display:
1245   {
1246     GST_INFO_OBJECT (plugin, "No valid GL display found");
1247     gst_object_replace (&plugin->gl_display, NULL);
1248     gst_object_replace (&plugin->gl_other_context, NULL);
1249     return NULL;
1250   }
1251 #else
1252   return NULL;
1253 #endif
1254 }
1255 
1256 static GArray *
extract_allowed_surface_formats(GstVaapiDisplay * display,GArray * img_formats,GstVideoFormat specified_format,GstPadDirection direction)1257 extract_allowed_surface_formats (GstVaapiDisplay * display,
1258     GArray * img_formats, GstVideoFormat specified_format,
1259     GstPadDirection direction)
1260 {
1261   guint i;
1262   GArray *out_formats;
1263   GstVaapiSurface *surface = NULL;
1264 
1265   g_assert (direction == GST_PAD_SRC || direction == GST_PAD_SINK);
1266 
1267   out_formats =
1268       g_array_sized_new (FALSE, FALSE, sizeof (GstVideoFormat),
1269       img_formats->len);
1270   if (!out_formats)
1271     return NULL;
1272 
1273   for (i = 0; i < img_formats->len; i++) {
1274     const GstVideoFormat img_format =
1275         g_array_index (img_formats, GstVideoFormat, i);
1276     GstVaapiImage *image;
1277     GstVideoInfo vi;
1278     GstVideoFormat surface_format;
1279     gboolean res;
1280 
1281     if (img_format == GST_VIDEO_FORMAT_UNKNOWN)
1282       continue;
1283 
1284     surface_format =
1285         (specified_format == GST_VIDEO_FORMAT_UNKNOWN) ?
1286         img_format : specified_format;
1287     if (!surface) {
1288       gst_video_info_set_format (&vi, surface_format, 64, 64);
1289       surface = gst_vaapi_surface_new_full (display, &vi, 0);
1290       if (!surface)
1291         continue;
1292     }
1293 
1294     image = gst_vaapi_image_new (display, img_format, 64, 64);
1295     if (!image) {
1296       /* Just reuse the surface if the format is specified */
1297       if (specified_format == GST_VIDEO_FORMAT_UNKNOWN)
1298         gst_vaapi_object_replace (&surface, NULL);
1299 
1300       continue;
1301     }
1302 
1303     res = FALSE;
1304     if (direction == GST_PAD_SRC) {
1305       res = gst_vaapi_surface_get_image (surface, image);
1306     } else {
1307       res = gst_vaapi_surface_put_image (surface, image);
1308     }
1309     if (res)
1310       g_array_append_val (out_formats, img_format);
1311 
1312     gst_vaapi_object_unref (image);
1313     /* Just reuse the surface if the format is specified */
1314     if (specified_format == GST_VIDEO_FORMAT_UNKNOWN)
1315       gst_vaapi_object_replace (&surface, NULL);
1316   }
1317 
1318   if (surface)
1319     gst_vaapi_object_unref (surface);
1320 
1321   if (out_formats->len == 0) {
1322     g_array_unref (out_formats);
1323     return NULL;
1324   }
1325   return out_formats;
1326 }
1327 
1328 static gboolean
ensure_allowed_raw_caps(GstVaapiPluginBase * plugin,GstVideoFormat format,GstPadDirection direction)1329 ensure_allowed_raw_caps (GstVaapiPluginBase * plugin, GstVideoFormat format,
1330     GstPadDirection direction)
1331 {
1332   GArray *formats, *out_formats;
1333   GstVaapiDisplay *display;
1334   GstCaps *out_caps;
1335   gboolean ret = FALSE;
1336 
1337   if (plugin->allowed_raw_caps)
1338     return TRUE;
1339 
1340   out_formats = NULL;
1341   display = gst_object_ref (plugin->display);
1342   formats = gst_vaapi_display_get_image_formats (display);
1343   if (!formats)
1344     goto bail;
1345   out_formats =
1346       extract_allowed_surface_formats (display, formats, format, direction);
1347   if (!out_formats)
1348     goto bail;
1349   out_caps = gst_vaapi_video_format_new_template_caps_from_list (out_formats);
1350   if (!out_caps)
1351     goto bail;
1352 
1353   gst_caps_replace (&plugin->allowed_raw_caps, out_caps);
1354   gst_caps_unref (out_caps);
1355   ret = TRUE;
1356 
1357 bail:
1358   if (formats)
1359     g_array_unref (formats);
1360   if (out_formats)
1361     g_array_unref (out_formats);
1362   gst_object_unref (display);
1363 
1364   return ret;
1365 }
1366 
1367 /**
1368  * gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps:
1369  * @plugin: a #GstVaapiPluginBase
1370  *
1371  * Returns the raw #GstCaps allowed by the element.
1372  *
1373  * Returns: the allowed raw #GstCaps or %NULL
1374  **/
1375 GstCaps *
gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps(GstVaapiPluginBase * plugin)1376 gst_vaapi_plugin_base_get_allowed_sinkpad_raw_caps (GstVaapiPluginBase * plugin)
1377 {
1378   if (!ensure_allowed_raw_caps (plugin, GST_VIDEO_FORMAT_UNKNOWN, GST_PAD_SINK))
1379     return NULL;
1380   return plugin->allowed_raw_caps;
1381 }
1382 
1383 /**
1384  * gst_vaapi_plugin_base_get_allowed_srcpad_raw_caps:
1385  * @plugin: a #GstVaapiPluginBase
1386  * @format: a #GstVideoFormat, the format we need to check
1387  *
1388  * Returns the raw #GstCaps allowed by the element.
1389  *
1390  * Returns: the allowed raw #GstCaps or %NULL
1391  **/
1392 GstCaps *
gst_vaapi_plugin_base_get_allowed_srcpad_raw_caps(GstVaapiPluginBase * plugin,GstVideoFormat format)1393 gst_vaapi_plugin_base_get_allowed_srcpad_raw_caps (GstVaapiPluginBase *
1394     plugin, GstVideoFormat format)
1395 {
1396   if (!ensure_allowed_raw_caps (plugin, format, GST_PAD_SRC))
1397     return NULL;
1398   return plugin->allowed_raw_caps;
1399 }
1400 
1401 /**
1402  * gst_vaapi_plugin_base_set_srcpad_can_dmabuf:
1403  * @plugin: a #GstVaapiPluginBase
1404  * @object: the GL context from gst-gl
1405  *
1406  * This function will determine if @object supports dmabuf
1407  * importing.
1408  *
1409  * Please note that the context @object should come from downstream.
1410  **/
1411 void
gst_vaapi_plugin_base_set_srcpad_can_dmabuf(GstVaapiPluginBase * plugin,GstObject * object)1412 gst_vaapi_plugin_base_set_srcpad_can_dmabuf (GstVaapiPluginBase * plugin,
1413     GstObject * object)
1414 {
1415 #if USE_EGL && USE_GST_GL_HELPERS
1416   GstGLContext *const gl_context = GST_GL_CONTEXT (object);
1417 
1418   plugin->srcpad_can_dmabuf =
1419       (!(gst_gl_context_get_gl_api (gl_context) & GST_GL_API_GLES1)
1420       && gst_gl_context_check_feature (gl_context,
1421           "EGL_EXT_image_dma_buf_import"));
1422 #endif
1423 }
1424 
1425 static void
_init_performance_debug(void)1426 _init_performance_debug (void)
1427 {
1428 #ifndef GST_DISABLE_GST_DEBUG
1429   static volatile gsize _init = 0;
1430 
1431   if (g_once_init_enter (&_init)) {
1432     GST_DEBUG_CATEGORY_GET (CAT_PERFORMANCE, "GST_PERFORMANCE");
1433     g_once_init_leave (&_init, 1);
1434   }
1435 #endif
1436 }
1437 
1438 /**
1439  * gst_vaapi_plugin_copy_va_buffer:
1440  * @plugin: a #GstVaapiPluginBase
1441  * @inbuf: a #GstBuffer with VA memory type
1442  * @outbuf: a #GstBuffer with system allocated memory
1443  *
1444  * Copy @inbuf to @outbuf. This if required when downstream doesn't
1445  * support GstVideoMeta, and since VA memory may have custom strides a
1446  * frame copy is required.
1447  *
1448  * Returns: %FALSE if the copy failed, otherwise %TRUE. Also returns
1449  *          %TRUE if it is not required to do the copy
1450  **/
1451 gboolean
gst_vaapi_plugin_copy_va_buffer(GstVaapiPluginBase * plugin,GstBuffer * inbuf,GstBuffer * outbuf)1452 gst_vaapi_plugin_copy_va_buffer (GstVaapiPluginBase * plugin,
1453     GstBuffer * inbuf, GstBuffer * outbuf)
1454 {
1455   GstVideoMeta *vmeta;
1456   GstVideoFrame src_frame, dst_frame;
1457   gboolean success;
1458 
1459   if (!plugin->copy_output_frame)
1460     return TRUE;
1461 
1462   /* inbuf shall have video meta */
1463   vmeta = gst_buffer_get_video_meta (inbuf);
1464   if (!vmeta)
1465     return FALSE;
1466 
1467   _init_performance_debug ();
1468   GST_CAT_INFO (CAT_PERFORMANCE, "copying VA buffer to system memory buffer");
1469 
1470   if (!gst_video_frame_map (&src_frame, &plugin->srcpad_info, inbuf,
1471           GST_MAP_READ))
1472     return FALSE;
1473   if (!gst_video_frame_map (&dst_frame, &plugin->srcpad_info, outbuf,
1474           GST_MAP_WRITE)) {
1475     gst_video_frame_unmap (&src_frame);
1476     return FALSE;
1477   }
1478   success = gst_video_frame_copy (&dst_frame, &src_frame);
1479   gst_video_frame_unmap (&dst_frame);
1480   gst_video_frame_unmap (&src_frame);
1481 
1482   if (success) {
1483     gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_TIMESTAMPS
1484         | GST_BUFFER_COPY_FLAGS, 0, -1);
1485   }
1486 
1487   return success;
1488 }
1489