1 /*
2  *  gstvaapipluginutil.h - VA-API plugin helpers
3  *
4  *  Copyright (C) 2011-2014 Intel Corporation
5  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
6  *  Copyright (C) 2011 Collabora
7  *    Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
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 "gstvaapivideocontext.h"
27 #if USE_DRM
28 # include <gst/vaapi/gstvaapidisplay_drm.h>
29 #endif
30 #if USE_X11
31 # include <gst/vaapi/gstvaapidisplay_x11.h>
32 #endif
33 #if USE_GLX
34 # include <gst/vaapi/gstvaapidisplay_glx.h>
35 #endif
36 #if USE_EGL
37 # include <gst/vaapi/gstvaapidisplay_egl.h>
38 #endif
39 #if USE_WAYLAND
40 # include <gst/vaapi/gstvaapidisplay_wayland.h>
41 #endif
42 #if USE_GST_GL_HELPERS
43 # include <gst/gl/gl.h>
44 #if USE_EGL && GST_GL_HAVE_PLATFORM_EGL
45 # include <gst/gl/egl/gstgldisplay_egl.h>
46 #endif
47 #endif
48 #include "gstvaapipluginutil.h"
49 #include "gstvaapipluginbase.h"
50 
51 /* Environment variable for disable driver white-list */
52 #define GST_VAAPI_ALL_DRIVERS_ENV "GST_VAAPI_ALL_DRIVERS"
53 
54 typedef GstVaapiDisplay *(*GstVaapiDisplayCreateFunc) (const gchar *);
55 typedef GstVaapiDisplay *(*GstVaapiDisplayCreateFromHandleFunc) (gpointer);
56 
57 typedef struct
58 {
59   const gchar *type_str;
60   GstVaapiDisplayType type;
61   GstVaapiDisplayCreateFunc create_display;
62   GstVaapiDisplayCreateFromHandleFunc create_display_from_handle;
63 } DisplayMap;
64 
65 /* *INDENT-OFF* */
66 static const DisplayMap g_display_map[] = {
67 #if USE_WAYLAND
68   {"wayland",
69    GST_VAAPI_DISPLAY_TYPE_WAYLAND,
70    gst_vaapi_display_wayland_new,
71    (GstVaapiDisplayCreateFromHandleFunc)
72    gst_vaapi_display_wayland_new_with_display},
73 #endif
74 #if USE_GLX
75   {"glx",
76    GST_VAAPI_DISPLAY_TYPE_GLX,
77    gst_vaapi_display_glx_new,
78    (GstVaapiDisplayCreateFromHandleFunc)
79    gst_vaapi_display_glx_new_with_display},
80 #endif
81 #if USE_X11
82   {"x11",
83    GST_VAAPI_DISPLAY_TYPE_X11,
84    gst_vaapi_display_x11_new,
85    (GstVaapiDisplayCreateFromHandleFunc)
86    gst_vaapi_display_x11_new_with_display},
87 #endif
88 #if USE_DRM
89   {"drm",
90    GST_VAAPI_DISPLAY_TYPE_DRM,
91    gst_vaapi_display_drm_new},
92 #endif
93   {NULL,}
94 };
95 /* *INDENT-ON* */
96 
97 static GstVaapiDisplay *
gst_vaapi_create_display(GstVaapiDisplayType display_type,const gchar * display_name)98 gst_vaapi_create_display (GstVaapiDisplayType display_type,
99     const gchar * display_name)
100 {
101   GstVaapiDisplay *display = NULL;
102   const DisplayMap *m;
103 
104   for (m = g_display_map; m->type_str != NULL; m++) {
105     if (display_type != GST_VAAPI_DISPLAY_TYPE_ANY && display_type != m->type)
106       continue;
107 
108     display = m->create_display (display_name);
109     if (display || display_type != GST_VAAPI_DISPLAY_TYPE_ANY)
110       break;
111   }
112   return display;
113 }
114 
115 #if USE_GST_GL_HELPERS
116 static GstVaapiDisplay *
gst_vaapi_create_display_from_handle(GstVaapiDisplayType display_type,gpointer handle)117 gst_vaapi_create_display_from_handle (GstVaapiDisplayType display_type,
118     gpointer handle)
119 {
120   GstVaapiDisplay *display;
121   const DisplayMap *m;
122 
123   if (display_type == GST_VAAPI_DISPLAY_TYPE_ANY)
124     return NULL;
125 
126   for (m = g_display_map; m->type_str != NULL; m++) {
127     if (m->type == display_type) {
128       display = m->create_display_from_handle ?
129           m->create_display_from_handle (handle) : NULL;
130       return display;
131     }
132   }
133   return NULL;
134 }
135 
136 static GstVaapiDisplayType
gst_vaapi_get_display_type_from_gl(GstGLDisplayType gl_display_type,GstGLPlatform gl_platform)137 gst_vaapi_get_display_type_from_gl (GstGLDisplayType gl_display_type,
138     GstGLPlatform gl_platform)
139 {
140   switch (gl_display_type) {
141 #if USE_X11
142     case GST_GL_DISPLAY_TYPE_X11:{
143 #if USE_GLX
144       if (gl_platform == GST_GL_PLATFORM_GLX)
145         return GST_VAAPI_DISPLAY_TYPE_GLX;
146 #endif
147       return GST_VAAPI_DISPLAY_TYPE_X11;
148     }
149 #endif
150 #if USE_WAYLAND
151     case GST_GL_DISPLAY_TYPE_WAYLAND:{
152       return GST_VAAPI_DISPLAY_TYPE_WAYLAND;
153     }
154 #endif
155 #if USE_EGL
156     case GST_GL_DISPLAY_TYPE_EGL:{
157       return GST_VAAPI_DISPLAY_TYPE_EGL;
158     }
159 #endif
160     default:
161       /* unsupported display. Still DRM may work. */
162       break;
163   }
164 
165   return GST_VAAPI_DISPLAY_TYPE_ANY;
166 }
167 
168 static GstVaapiDisplayType
gst_vaapi_get_display_type_from_gl_env(void)169 gst_vaapi_get_display_type_from_gl_env (void)
170 {
171   const gchar *const gl_window_type = g_getenv ("GST_GL_WINDOW");
172 
173   if (!gl_window_type) {
174 #if USE_X11 && GST_GL_HAVE_WINDOW_X11
175     return GST_VAAPI_DISPLAY_TYPE_X11;
176 #elif USE_WAYLAND && GST_GL_HAVE_WINDOW_WAYLAND
177     return GST_VAAPI_DISPLAY_TYPE_WAYLAND;
178 #elif USE_EGL && GST_GL_HAVE_PLATFORM_EGL
179     return GST_VAAPI_DISPLAY_TYPE_EGL;
180 #endif
181   }
182 #if USE_X11
183   if (g_strcmp0 (gl_window_type, "x11") == 0)
184     return GST_VAAPI_DISPLAY_TYPE_X11;
185 #endif
186 #if USE_WAYLAND
187   if (g_strcmp0 (gl_window_type, "wayland") == 0)
188     return GST_VAAPI_DISPLAY_TYPE_WAYLAND;
189 #endif
190 #if USE_EGL
191   {
192     const gchar *const gl_platform_type = g_getenv ("GST_GL_PLATFORM");
193     if (g_strcmp0 (gl_platform_type, "egl") == 0)
194       return GST_VAAPI_DISPLAY_TYPE_EGL;
195   }
196 #endif
197 
198   return GST_VAAPI_DISPLAY_TYPE_ANY;
199 }
200 
201 #if USE_EGL
202 static gint
gst_vaapi_get_gles_version_from_gl_api(GstGLAPI gl_api)203 gst_vaapi_get_gles_version_from_gl_api (GstGLAPI gl_api)
204 {
205   switch (gl_api) {
206     case GST_GL_API_GLES1:
207       return 1;
208     case GST_GL_API_GLES2:
209       return 2;
210     case GST_GL_API_OPENGL:
211     case GST_GL_API_OPENGL3:
212       return 0;
213     default:
214       break;
215   }
216   return -1;
217 }
218 
219 static guintptr
gst_vaapi_get_egl_handle_from_gl_display(GstGLDisplay * gl_display)220 gst_vaapi_get_egl_handle_from_gl_display (GstGLDisplay * gl_display)
221 {
222   guintptr egl_handle = 0;
223   GstGLDisplayEGL *egl_display;
224 
225   egl_display = gst_gl_display_egl_from_gl_display (gl_display);
226   if (egl_display) {
227     egl_handle = gst_gl_display_get_handle (GST_GL_DISPLAY (egl_display));
228     gst_object_unref (egl_display);
229   }
230   return egl_handle;
231 }
232 #endif /* USE_EGL */
233 
234 static GstVaapiDisplay *
gst_vaapi_create_display_from_egl(GstGLDisplay * gl_display,GstGLContext * gl_context,GstVaapiDisplayType display_type,gpointer native_display)235 gst_vaapi_create_display_from_egl (GstGLDisplay * gl_display,
236     GstGLContext * gl_context, GstVaapiDisplayType display_type,
237     gpointer native_display)
238 {
239   GstVaapiDisplay *display = NULL;
240 #if USE_EGL
241   GstGLAPI gl_api;
242   gint gles_version;
243   guintptr egl_handler;
244 
245   gl_api = gst_gl_context_get_gl_api (gl_context);
246   gles_version = gst_vaapi_get_gles_version_from_gl_api (gl_api);
247   if (gles_version == -1)
248     return NULL;
249 
250   egl_handler = gst_vaapi_get_egl_handle_from_gl_display (gl_display);
251   if (egl_handler != 0) {
252     gpointer native_display_egl = GSIZE_TO_POINTER (egl_handler);
253     display = gst_vaapi_display_egl_new_with_native_display (native_display_egl,
254         display_type, gles_version);
255   }
256 
257   if (!display) {
258     GstVaapiDisplay *wrapped_display;
259 
260     wrapped_display =
261         gst_vaapi_create_display_from_handle (display_type, native_display);
262     if (wrapped_display) {
263       display = gst_vaapi_display_egl_new (wrapped_display, gles_version);
264       gst_object_unref (wrapped_display);
265     }
266   }
267 
268   if (display) {
269     gst_vaapi_display_egl_set_gl_context (GST_VAAPI_DISPLAY_EGL (display),
270         GSIZE_TO_POINTER (gst_gl_context_get_gl_context (gl_context)));
271   }
272 #endif
273   return display;
274 }
275 #endif /* USE_GST_GL_HELPERS */
276 
277 static GstVaapiDisplay *
gst_vaapi_create_display_from_gl_context(GstObject * gl_context_object)278 gst_vaapi_create_display_from_gl_context (GstObject * gl_context_object)
279 {
280 #if USE_GST_GL_HELPERS
281   GstGLContext *const gl_context = GST_GL_CONTEXT (gl_context_object);
282   GstGLDisplay *const gl_display = gst_gl_context_get_display (gl_context);
283   GstGLDisplayType gl_display_type;
284   GstGLPlatform gl_platform;
285   gpointer native_display;
286   GstVaapiDisplay *display = NULL;
287   GstVaapiDisplayType display_type;
288 
289   /* Get display type and the native hanler */
290   gl_display_type = gst_gl_display_get_handle_type (gl_display);
291   gl_platform = gst_gl_context_get_gl_platform (gl_context);
292   display_type =
293       gst_vaapi_get_display_type_from_gl (gl_display_type, gl_platform);
294 
295   native_display = GSIZE_TO_POINTER (gst_gl_display_get_handle (gl_display));
296 
297   if (display_type == GST_VAAPI_DISPLAY_TYPE_ANY) {
298     /* derive type and native_display from the active window */
299     GstGLWindow *const gl_window = gst_gl_context_get_window (gl_context);
300     if (gl_window)
301       native_display = GSIZE_TO_POINTER (gst_gl_window_get_display (gl_window));
302     display_type = gst_vaapi_get_display_type_from_gl_env ();
303   }
304 
305   if (gl_platform == GST_GL_PLATFORM_EGL) {
306     display = gst_vaapi_create_display_from_egl (gl_display, gl_context,
307         display_type, native_display);
308   }
309 
310   /* Non-EGL and fallback */
311   if (!display) {
312     display =
313         gst_vaapi_create_display_from_handle (display_type, native_display);
314   }
315 
316   gst_object_unref (gl_display);
317 
318   return display;
319 #endif
320   GST_ERROR ("No GstGL support");
321   return NULL;
322 }
323 
324 static void
gst_vaapi_find_gl_context(GstElement * element)325 gst_vaapi_find_gl_context (GstElement * element)
326 {
327 #if USE_GST_GL_HELPERS
328   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
329 
330   /* if the element is vaapisink or any vaapi encoder it doesn't need
331    * to know a GstGLContext in order to create an appropriate
332    * GstVaapiDisplay. Let's them to choose their own
333    * GstVaapiDisplay */
334   if (GST_IS_VIDEO_SINK (element) || GST_IS_VIDEO_ENCODER (element))
335     return;
336 
337   if (!gst_gl_ensure_element_data (plugin,
338           (GstGLDisplay **) & plugin->gl_display,
339           (GstGLContext **) & plugin->gl_other_context))
340     goto no_valid_gl_display;
341 
342   gst_vaapi_find_gl_local_context (element, &plugin->gl_context);
343 
344   if (plugin->gl_context) {
345     gst_vaapi_plugin_base_set_srcpad_can_dmabuf (plugin, plugin->gl_context);
346   } else {
347     GstObject *gl_context;
348 
349     gl_context = gst_vaapi_plugin_base_create_gl_context (plugin);
350     if (gl_context) {
351       gst_vaapi_plugin_base_set_gl_context (plugin, gl_context);
352       gst_object_unref (gl_context);
353     }
354   }
355 
356   /* ERRORS */
357 no_valid_gl_display:
358   {
359     GST_INFO_OBJECT (plugin, "No valid GL display found");
360     gst_object_replace (&plugin->gl_display, NULL);
361     gst_object_replace (&plugin->gl_other_context, NULL);
362     return;
363   }
364 #endif
365 }
366 
367 gboolean
gst_vaapi_ensure_display(GstElement * element,GstVaapiDisplayType type)368 gst_vaapi_ensure_display (GstElement * element, GstVaapiDisplayType type)
369 {
370   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
371   GstVaapiDisplay *display = NULL;
372 
373   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
374 
375   if (gst_vaapi_video_context_prepare (element, &plugin->display)) {
376     /* Neighbour found and it updated the display */
377     if (gst_vaapi_plugin_base_has_display_type (plugin, type))
378       return TRUE;
379   }
380 
381   /* Query for a local GstGL context. If it's found, it will be used
382    * to create the VA display */
383   if (!plugin->gl_context)
384     gst_vaapi_find_gl_context (element);
385 
386   /* If no neighboor, or application not interested, use system default */
387   if (plugin->gl_context) {
388     display = gst_vaapi_create_display_from_gl_context (plugin->gl_context);
389     /* Cannot instantiate VA display based on GL context. Reset the
390      *  requested display type to ANY to try again */
391     if (!display)
392       gst_vaapi_plugin_base_set_display_type (plugin,
393           GST_VAAPI_DISPLAY_TYPE_ANY);
394   }
395   if (!display)
396     display = gst_vaapi_create_display (type, plugin->display_name);
397   if (!display)
398     return FALSE;
399 
400   gst_vaapi_video_context_propagate (element, display);
401   gst_object_unref (display);
402   return TRUE;
403 }
404 
405 gboolean
gst_vaapi_handle_context_query(GstElement * element,GstQuery * query)406 gst_vaapi_handle_context_query (GstElement * element, GstQuery * query)
407 {
408   GstVaapiPluginBase *const plugin = GST_VAAPI_PLUGIN_BASE (element);
409   const gchar *type = NULL;
410   GstContext *context, *old_context;
411 
412   g_return_val_if_fail (query != NULL, FALSE);
413 
414 #if USE_GST_GL_HELPERS
415   if (plugin->gl_display && plugin->gl_context && plugin->gl_other_context) {
416     if (gst_gl_handle_context_query (element, query,
417             (GstGLDisplay *) plugin->gl_display,
418             (GstGLContext *) plugin->gl_context,
419             (GstGLContext *) plugin->gl_other_context))
420       return TRUE;
421   }
422 #endif
423 
424   if (!plugin->display)
425     return FALSE;
426 
427   if (!gst_query_parse_context_type (query, &type))
428     return FALSE;
429 
430   if (g_strcmp0 (type, GST_VAAPI_DISPLAY_CONTEXT_TYPE_NAME))
431     return FALSE;
432 
433   gst_query_parse_context (query, &old_context);
434   if (old_context) {
435     context = gst_context_copy (old_context);
436     gst_vaapi_video_context_set_display (context, plugin->display);
437   } else {
438     context = gst_vaapi_video_context_new_with_display (plugin->display, FALSE);
439   }
440 
441   gst_query_set_context (query, context);
442   gst_context_unref (context);
443 
444   return TRUE;
445 }
446 
447 gboolean
gst_vaapi_append_surface_caps(GstCaps * out_caps,GstCaps * in_caps)448 gst_vaapi_append_surface_caps (GstCaps * out_caps, GstCaps * in_caps)
449 {
450   GstStructure *structure;
451   const GValue *v_width, *v_height, *v_framerate, *v_par;
452   guint i, n_structures;
453 
454   structure = gst_caps_get_structure (in_caps, 0);
455   v_width = gst_structure_get_value (structure, "width");
456   v_height = gst_structure_get_value (structure, "height");
457   v_framerate = gst_structure_get_value (structure, "framerate");
458   v_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
459   if (!v_width || !v_height)
460     return FALSE;
461 
462   n_structures = gst_caps_get_size (out_caps);
463   for (i = 0; i < n_structures; i++) {
464     structure = gst_caps_get_structure (out_caps, i);
465     gst_structure_set_value (structure, "width", v_width);
466     gst_structure_set_value (structure, "height", v_height);
467     if (v_framerate)
468       gst_structure_set_value (structure, "framerate", v_framerate);
469     if (v_par)
470       gst_structure_set_value (structure, "pixel-aspect-ratio", v_par);
471   }
472   return TRUE;
473 }
474 
475 gboolean
gst_vaapi_apply_composition(GstVaapiSurface * surface,GstBuffer * buffer)476 gst_vaapi_apply_composition (GstVaapiSurface * surface, GstBuffer * buffer)
477 {
478   GstVideoOverlayCompositionMeta *const cmeta =
479       gst_buffer_get_video_overlay_composition_meta (buffer);
480   GstVideoOverlayComposition *composition = NULL;
481 
482   if (cmeta)
483     composition = cmeta->overlay;
484   return gst_vaapi_surface_set_subpictures_from_composition (surface,
485       composition, TRUE);
486 }
487 
488 gboolean
gst_vaapi_value_set_format(GValue * value,GstVideoFormat format)489 gst_vaapi_value_set_format (GValue * value, GstVideoFormat format)
490 {
491   const gchar *str;
492 
493   str = gst_video_format_to_string (format);
494   if (!str)
495     return FALSE;
496 
497   g_value_init (value, G_TYPE_STRING);
498   g_value_set_string (value, str);
499   return TRUE;
500 }
501 
502 gboolean
gst_vaapi_value_set_format_list(GValue * value,GArray * formats)503 gst_vaapi_value_set_format_list (GValue * value, GArray * formats)
504 {
505   GValue v_format = G_VALUE_INIT;
506   guint i;
507 
508   g_value_init (value, GST_TYPE_LIST);
509   for (i = 0; i < formats->len; i++) {
510     GstVideoFormat const format = g_array_index (formats, GstVideoFormat, i);
511 
512     if (!gst_vaapi_value_set_format (&v_format, format))
513       continue;
514     gst_value_list_append_value (value, &v_format);
515     g_value_unset (&v_format);
516   }
517   return TRUE;
518 }
519 
520 static void
set_video_template_caps(GstCaps * caps)521 set_video_template_caps (GstCaps * caps)
522 {
523   GstStructure *const structure = gst_caps_get_structure (caps, 0);
524 
525   gst_structure_set (structure,
526       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
527       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
528       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
529 }
530 
531 GstCaps *
gst_vaapi_video_format_new_template_caps(GstVideoFormat format)532 gst_vaapi_video_format_new_template_caps (GstVideoFormat format)
533 {
534   GstCaps *caps;
535 
536   g_return_val_if_fail (format != GST_VIDEO_FORMAT_UNKNOWN, NULL);
537 
538   caps = gst_caps_new_empty_simple ("video/x-raw");
539   if (!caps)
540     return NULL;
541 
542   gst_caps_set_simple (caps,
543       "format", G_TYPE_STRING, gst_video_format_to_string (format), NULL);
544   set_video_template_caps (caps);
545   return caps;
546 }
547 
548 GstCaps *
gst_vaapi_video_format_new_template_caps_from_list(GArray * formats)549 gst_vaapi_video_format_new_template_caps_from_list (GArray * formats)
550 {
551   GValue v_formats = G_VALUE_INIT;
552   GstCaps *caps;
553 
554   caps = gst_caps_new_empty_simple ("video/x-raw");
555   if (!caps)
556     return NULL;
557 
558   if (!gst_vaapi_value_set_format_list (&v_formats, formats)) {
559     gst_caps_unref (caps);
560     return NULL;
561   }
562 
563   gst_caps_set_value (caps, "format", &v_formats);
564   set_video_template_caps (caps);
565   g_value_unset (&v_formats);
566   return caps;
567 }
568 
569 GstCaps *
gst_vaapi_video_format_new_template_caps_with_features(GstVideoFormat format,const gchar * features_string)570 gst_vaapi_video_format_new_template_caps_with_features (GstVideoFormat format,
571     const gchar * features_string)
572 {
573   GstCapsFeatures *features;
574   GstCaps *caps;
575 
576   caps = gst_vaapi_video_format_new_template_caps (format);
577   if (!caps)
578     return NULL;
579 
580   features = gst_caps_features_new (features_string, NULL);
581   if (!features) {
582     gst_caps_unref (caps);
583     return NULL;
584   }
585   gst_caps_set_features (caps, 0, features);
586   return caps;
587 }
588 
589 static GstVideoFormat
gst_vaapi_find_preferred_format(const GValue * format_list,GstVideoFormat native_format)590 gst_vaapi_find_preferred_format (const GValue * format_list,
591     GstVideoFormat native_format)
592 {
593   const GValue *frmt;
594   GstVideoFormat out_format;
595   guint i;
596 
597   /* if one format, that is the one */
598   if (G_VALUE_HOLDS_STRING (format_list))
599     return gst_video_format_from_string (g_value_get_string (format_list));
600 
601   if (!GST_VALUE_HOLDS_LIST (format_list)) {
602     GST_ERROR ("negotiated caps do not have a valid format");
603     return GST_VIDEO_FORMAT_UNKNOWN;
604   }
605 
606   if (native_format == GST_VIDEO_FORMAT_UNKNOWN
607       || native_format == GST_VIDEO_FORMAT_ENCODED) {
608     native_format = GST_VIDEO_FORMAT_NV12;      /* default VA format */
609   }
610 
611   /* search our native format in the list */
612   for (i = 0; i < gst_value_list_get_size (format_list); i++) {
613     frmt = gst_value_list_get_value (format_list, i);
614     out_format = gst_video_format_from_string (g_value_get_string (frmt));
615 
616     /* GStreamer do not handle encoded formats nicely. Try the next
617      * one. */
618     if (out_format == GST_VIDEO_FORMAT_ENCODED)
619       continue;
620 
621     if (native_format == out_format)
622       return out_format;
623   }
624 
625   /* just pick the first valid format in the list */
626   i = 0;
627   do {
628     frmt = gst_value_list_get_value (format_list, i++);
629     out_format = gst_video_format_from_string (g_value_get_string (frmt));
630   } while (out_format == GST_VIDEO_FORMAT_ENCODED);
631 
632   return out_format;
633 }
634 
635 GstVaapiCapsFeature
gst_vaapi_find_preferred_caps_feature(GstPad * pad,GstCaps * allowed_caps,GstVideoFormat * out_format_ptr)636 gst_vaapi_find_preferred_caps_feature (GstPad * pad, GstCaps * allowed_caps,
637     GstVideoFormat * out_format_ptr)
638 {
639   GstVaapiCapsFeature feature = GST_VAAPI_CAPS_FEATURE_NOT_NEGOTIATED;
640   guint i, j, num_structures;
641   GstCaps *peer_caps, *out_caps = NULL, *caps = NULL;
642   static const guint feature_list[] = { GST_VAAPI_CAPS_FEATURE_VAAPI_SURFACE,
643     GST_VAAPI_CAPS_FEATURE_DMABUF,
644     GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META,
645     GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY,
646   };
647 
648   /* query with no filter */
649   peer_caps = gst_pad_peer_query_caps (pad, NULL);
650   if (!peer_caps)
651     goto cleanup;
652   if (gst_caps_is_empty (peer_caps))
653     goto cleanup;
654 
655   /* filter against our allowed caps */
656   out_caps = gst_caps_intersect_full (allowed_caps, peer_caps,
657       GST_CAPS_INTERSECT_FIRST);
658 
659   /* default feature */
660   feature = GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY;
661 
662   /* if downstream requests caps ANY, system memory is preferred */
663   if (gst_caps_is_any (peer_caps))
664     goto find_format;
665 
666   num_structures = gst_caps_get_size (out_caps);
667   for (i = 0; i < num_structures; i++) {
668     GstCapsFeatures *const features = gst_caps_get_features (out_caps, i);
669     GstStructure *const structure = gst_caps_get_structure (out_caps, i);
670 
671     /* Skip ANY features, we need an exact match for correct evaluation */
672     if (gst_caps_features_is_any (features))
673       continue;
674 
675     gst_caps_replace (&caps, NULL);
676     caps = gst_caps_new_full (gst_structure_copy (structure), NULL);
677     if (!caps)
678       continue;
679     gst_caps_set_features (caps, 0, gst_caps_features_copy (features));
680 
681     for (j = 0; j < G_N_ELEMENTS (feature_list); j++) {
682       if (gst_vaapi_caps_feature_contains (caps, feature_list[j])
683           && feature < feature_list[j]) {
684         feature = feature_list[j];
685         break;
686       }
687     }
688 
689     /* Stop at the first match, the caps should already be sorted out
690        by preference order from downstream elements */
691     if (feature != GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY)
692       break;
693   }
694 
695   if (!caps)
696     goto cleanup;
697 
698 find_format:
699   if (out_format_ptr) {
700     GstVideoFormat out_format;
701     GstStructure *structure = NULL;
702     const GValue *format_list;
703     GstCapsFeatures *features;
704 
705     /* if the best feature is SystemMemory, we should choose the
706      * vidoe/x-raw caps in the filtered peer caps set. If not, use
707      * the first caps, which is the preferred by downstream. */
708     if (feature == GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY) {
709       gst_caps_replace (&caps, out_caps);
710       num_structures = gst_caps_get_size (caps);
711       for (i = 0; i < num_structures; i++) {
712         structure = gst_caps_get_structure (caps, i);
713         features = gst_caps_get_features (caps, i);
714         if (!gst_caps_features_is_any (features)
715             && gst_caps_features_contains (features,
716                 gst_vaapi_caps_feature_to_string
717                 (GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY)))
718           break;
719       }
720     } else {
721       structure = gst_caps_get_structure (caps, 0);
722     }
723     if (!structure)
724       goto cleanup;
725     format_list = gst_structure_get_value (structure, "format");
726     if (!format_list)
727       goto cleanup;
728     out_format = gst_vaapi_find_preferred_format (format_list, *out_format_ptr);
729     if (out_format == GST_VIDEO_FORMAT_UNKNOWN)
730       goto cleanup;
731 
732     *out_format_ptr = out_format;
733   }
734 
735 cleanup:
736   gst_caps_replace (&caps, NULL);
737   gst_caps_replace (&out_caps, NULL);
738   gst_caps_replace (&peer_caps, NULL);
739   return feature;
740 }
741 
742 const gchar *
gst_vaapi_caps_feature_to_string(GstVaapiCapsFeature feature)743 gst_vaapi_caps_feature_to_string (GstVaapiCapsFeature feature)
744 {
745   const gchar *str;
746 
747   switch (feature) {
748     case GST_VAAPI_CAPS_FEATURE_SYSTEM_MEMORY:
749       str = GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY;
750       break;
751     case GST_VAAPI_CAPS_FEATURE_GL_TEXTURE_UPLOAD_META:
752       str = GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META;
753       break;
754     case GST_VAAPI_CAPS_FEATURE_DMABUF:
755       str = GST_CAPS_FEATURE_MEMORY_DMABUF;
756       break;
757     case GST_VAAPI_CAPS_FEATURE_VAAPI_SURFACE:
758       str = GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE;
759       break;
760     default:
761       str = NULL;
762       break;
763   }
764   return str;
765 }
766 
767 gboolean
gst_caps_set_interlaced(GstCaps * caps,GstVideoInfo * vip)768 gst_caps_set_interlaced (GstCaps * caps, GstVideoInfo * vip)
769 {
770   GstVideoInterlaceMode mode;
771   const gchar *mode_str;
772 
773   mode = vip ? GST_VIDEO_INFO_INTERLACE_MODE (vip) :
774       GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
775   switch (mode) {
776     case GST_VIDEO_INTERLACE_MODE_PROGRESSIVE:
777       mode_str = "progressive";
778       break;
779     case GST_VIDEO_INTERLACE_MODE_INTERLEAVED:
780       mode_str = "interleaved";
781       break;
782     case GST_VIDEO_INTERLACE_MODE_MIXED:
783       mode_str = "mixed";
784       break;
785     default:
786       GST_ERROR ("unsupported `interlace-mode' %d", mode);
787       return FALSE;
788   }
789 
790   gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING, mode_str, NULL);
791   return TRUE;
792 }
793 
794 static gboolean
_gst_caps_has_feature(const GstCaps * caps,const gchar * feature)795 _gst_caps_has_feature (const GstCaps * caps, const gchar * feature)
796 {
797   guint i;
798 
799   for (i = 0; i < gst_caps_get_size (caps); i++) {
800     GstCapsFeatures *const features = gst_caps_get_features (caps, i);
801     /* Skip ANY features, we need an exact match for correct evaluation */
802     if (gst_caps_features_is_any (features))
803       continue;
804     if (gst_caps_features_contains (features, feature))
805       return TRUE;
806   }
807 
808   return FALSE;
809 }
810 
811 gboolean
gst_vaapi_caps_feature_contains(const GstCaps * caps,GstVaapiCapsFeature feature)812 gst_vaapi_caps_feature_contains (const GstCaps * caps,
813     GstVaapiCapsFeature feature)
814 {
815   const gchar *feature_str;
816 
817   g_return_val_if_fail (caps != NULL, FALSE);
818 
819   feature_str = gst_vaapi_caps_feature_to_string (feature);
820   if (!feature_str)
821     return FALSE;
822 
823   return _gst_caps_has_feature (caps, feature_str);
824 }
825 
826 /* Checks whether the supplied caps contain VA surfaces */
827 gboolean
gst_caps_has_vaapi_surface(GstCaps * caps)828 gst_caps_has_vaapi_surface (GstCaps * caps)
829 {
830   g_return_val_if_fail (caps != NULL, FALSE);
831 
832   return _gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_VAAPI_SURFACE);
833 }
834 
835 gboolean
gst_caps_is_video_raw(GstCaps * caps)836 gst_caps_is_video_raw (GstCaps * caps)
837 {
838   GstStructure *structure;
839 
840   g_return_val_if_fail (caps != NULL, FALSE);
841 
842   if (!gst_caps_is_fixed (caps))
843     return FALSE;
844   if (!_gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY))
845     return FALSE;
846   structure = gst_caps_get_structure (caps, 0);
847   return gst_structure_has_name (structure, "video/x-raw");
848 }
849 
850 void
gst_video_info_change_format(GstVideoInfo * vip,GstVideoFormat format,guint width,guint height)851 gst_video_info_change_format (GstVideoInfo * vip, GstVideoFormat format,
852     guint width, guint height)
853 {
854   GstVideoInfo vi = *vip;
855 
856   gst_video_info_set_format (vip, format, width, height);
857 
858   GST_VIDEO_INFO_INTERLACE_MODE (vip) = GST_VIDEO_INFO_INTERLACE_MODE (&vi);
859   GST_VIDEO_FORMAT_INFO_FLAGS (vip) = GST_VIDEO_FORMAT_INFO_FLAGS (&vi);
860   GST_VIDEO_INFO_VIEWS (vip) = GST_VIDEO_INFO_VIEWS (&vi);
861   GST_VIDEO_INFO_PAR_N (vip) = GST_VIDEO_INFO_PAR_N (&vi);
862   GST_VIDEO_INFO_PAR_D (vip) = GST_VIDEO_INFO_PAR_D (&vi);
863   GST_VIDEO_INFO_FPS_N (vip) = GST_VIDEO_INFO_FPS_N (&vi);
864   GST_VIDEO_INFO_FPS_D (vip) = GST_VIDEO_INFO_FPS_D (&vi);
865   GST_VIDEO_INFO_MULTIVIEW_MODE (vip) = GST_VIDEO_INFO_MULTIVIEW_MODE (&vi);
866   GST_VIDEO_INFO_MULTIVIEW_FLAGS (vip) = GST_VIDEO_INFO_MULTIVIEW_FLAGS (&vi);
867 }
868 
869 /**
870  * gst_video_info_changed:
871  * @old: old #GstVideoInfo
872  * @new: new #GstVideoInfo
873  *
874  * Compares @old and @new
875  *
876  * Returns: %TRUE if @old has different format/width/height than
877  * @new. Otherwise, %FALSE.
878  **/
879 gboolean
gst_video_info_changed(const GstVideoInfo * old,const GstVideoInfo * new)880 gst_video_info_changed (const GstVideoInfo * old, const GstVideoInfo * new)
881 {
882   if (GST_VIDEO_INFO_FORMAT (old) != GST_VIDEO_INFO_FORMAT (new))
883     return TRUE;
884   if (GST_VIDEO_INFO_WIDTH (old) != GST_VIDEO_INFO_WIDTH (new))
885     return TRUE;
886   if (GST_VIDEO_INFO_HEIGHT (old) != GST_VIDEO_INFO_HEIGHT (new))
887     return TRUE;
888   return FALSE;
889 }
890 
891 /**
892  * gst_video_info_force_nv12_if_encoded:
893  * @vinfo: a #GstVideoInfo
894  *
895  * If the format of @vinfo is %GST_VIDEO_FORMAT_ENCODED it is changed
896  * to %GST_VIDEO_FORMAT_NV12.
897  **/
898 void
gst_video_info_force_nv12_if_encoded(GstVideoInfo * vinfo)899 gst_video_info_force_nv12_if_encoded (GstVideoInfo * vinfo)
900 {
901   if (GST_VIDEO_INFO_FORMAT (vinfo) != GST_VIDEO_FORMAT_ENCODED)
902     return;
903   gst_video_info_set_format (vinfo, GST_VIDEO_FORMAT_NV12,
904       GST_VIDEO_INFO_WIDTH (vinfo), GST_VIDEO_INFO_HEIGHT (vinfo));
905 }
906 
907 /**
908  * gst_vaapi_create_test_display:
909  *
910  * Creates a temporal #GstVaapiDisplay instance, just for testing the
911  * supported features.
912  *
913  * Returns: a new #GstVaapiDisplay instances. Free with
914  * gst_object_unref () after use. Or %NULL if no VA display is
915  * available.
916  **/
917 GstVaapiDisplay *
gst_vaapi_create_test_display(void)918 gst_vaapi_create_test_display (void)
919 {
920   guint i;
921   GstVaapiDisplay *display = NULL;
922   const GstVaapiDisplayType test_display_map[] = {
923 #if USE_DRM
924     GST_VAAPI_DISPLAY_TYPE_DRM,
925 #endif
926 #if USE_WAYLAND
927     GST_VAAPI_DISPLAY_TYPE_WAYLAND,
928 #endif
929 #if USE_X11
930     GST_VAAPI_DISPLAY_TYPE_X11,
931 #endif
932   };
933 
934   for (i = 0; i < G_N_ELEMENTS (test_display_map); i++) {
935     display = gst_vaapi_create_display (test_display_map[i], NULL);
936     if (display)
937       break;
938   }
939   return display;
940 }
941 
942 /**
943  * gst_vaapi_driver_is_whitelisted:
944  * @display: a #GstVaapiDisplay
945  *
946  * Looks the VA-API driver vendors in an internal white-list.
947  *
948  * Returns: %TRUE if driver is in the white-list, otherwise %FALSE
949  **/
950 gboolean
gst_vaapi_driver_is_whitelisted(GstVaapiDisplay * display)951 gst_vaapi_driver_is_whitelisted (GstVaapiDisplay * display)
952 {
953   const gchar *vendor;
954   guint i;
955   static const gchar *whitelist[] = {
956     "Intel i965 driver",
957     NULL
958   };
959 
960   g_return_val_if_fail (display, FALSE);
961 
962   if (g_getenv (GST_VAAPI_ALL_DRIVERS_ENV))
963     return TRUE;
964 
965   vendor = gst_vaapi_display_get_vendor_string (display);
966   if (!vendor)
967     goto no_vendor;
968 
969   for (i = 0; whitelist[i]; i++) {
970     if (g_ascii_strncasecmp (vendor, whitelist[i], strlen (whitelist[i])) == 0)
971       return TRUE;
972   }
973 
974   GST_WARNING ("Unsupported VA driver: %s. Export environment variable "
975       GST_VAAPI_ALL_DRIVERS_ENV " to bypass", vendor);
976   return FALSE;
977 
978   /* ERRORS */
979 no_vendor:
980   {
981     GST_WARNING ("no VA-API driver vendor description");
982     return FALSE;
983   }
984 }
985 
986 /**
987  * gst_vaapi_codecs_has_codec:
988  * @codecs: a #GArray of #GstVaapiCodec
989  * @codec: a #GstVaapiCodec to find in @codec
990  *
991  * Search in the available @codecs for the specific @codec.
992  *
993  * Returns: %TRUE if @codec is in @codecs
994  **/
995 gboolean
gst_vaapi_codecs_has_codec(GArray * codecs,GstVaapiCodec codec)996 gst_vaapi_codecs_has_codec (GArray * codecs, GstVaapiCodec codec)
997 {
998   guint i;
999   GstVaapiCodec c;
1000 
1001   g_return_val_if_fail (codec, FALSE);
1002 
1003   for (i = 0; i < codecs->len; i++) {
1004     c = g_array_index (codecs, GstVaapiCodec, i);
1005     if (c == codec)
1006       return TRUE;
1007   }
1008   return FALSE;
1009 }
1010