1 /* GStreamer
2 * Copyright (C) 2020 Igalia, S.L.
3 * Author: Víctor Jáquez <vjaquez@igalia.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:gstvadisplay
23 * @title: GstVaDisplay
24 * @short_description: Generic VADisplay wrapper.
25 * @sources:
26 * - gstvadisplay.h
27 *
28 * It is a generic wrapper for VADisplay. To create new instances
29 * subclasses are required, depending on the display type to use
30 * (v.gr. DRM, X11, Wayland, etc.).
31 *
32 * The purpose of this class is to be shared among pipelines via
33 * #GstContext so all the VA processing elements will use the same
34 * display entry. Application developers can create their own
35 * subclass, based on their display, and shared it via the synced bus
36 * message for the application.
37 */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #include "gstvadisplay.h"
44 #include <va/va.h>
45
46 GST_DEBUG_CATEGORY (gst_va_display_debug);
47 #define GST_CAT_DEFAULT gst_va_display_debug
48
49 typedef struct _GstVaDisplayPrivate GstVaDisplayPrivate;
50 struct _GstVaDisplayPrivate
51 {
52 GRecMutex lock;
53 VADisplay display;
54
55 gboolean foreign;
56 gboolean init;
57 GstVaImplementation impl;
58 };
59
60 #define gst_va_display_parent_class parent_class
61 G_DEFINE_TYPE_WITH_CODE (GstVaDisplay, gst_va_display, GST_TYPE_OBJECT,
62 G_ADD_PRIVATE (GstVaDisplay);
63 GST_DEBUG_CATEGORY_INIT (gst_va_display_debug, "vadisplay", 0,
64 "VA Display"));
65 enum
66 {
67 PROP_VA_DISPLAY = 1,
68 N_PROPERTIES
69 };
70
71 static GParamSpec *g_properties[N_PROPERTIES];
72
73 #define GET_PRIV(obj) gst_va_display_get_instance_private (GST_VA_DISPLAY (obj))
74
75 static GstVaImplementation
_get_implementation(const char * vendor)76 _get_implementation (const char *vendor)
77 {
78 if (g_str_has_prefix (vendor, "Mesa Gallium driver"))
79 return GST_VA_IMPLEMENTATION_MESA_GALLIUM;
80 else if (g_str_has_prefix (vendor, "Intel i965 driver"))
81 return GST_VA_IMPLEMENTATION_INTEL_I965;
82 else if (g_str_has_prefix (vendor, "Intel iHD driver"))
83 return GST_VA_IMPLEMENTATION_INTEL_IHD;
84
85 return GST_VA_IMPLEMENTATION_OTHER;
86 }
87
88 static gboolean
_gst_va_display_filter_driver(GstVaDisplay * self,gpointer foreign_display)89 _gst_va_display_filter_driver (GstVaDisplay * self, gpointer foreign_display)
90 {
91 GstVaDisplayPrivate *priv = GET_PRIV (self);
92 VADisplay dpy;
93 const char *vendor;
94
95 g_assert ((foreign_display != NULL) ^ (priv->display != NULL));
96 dpy = foreign_display ? foreign_display : priv->display;
97
98 vendor = vaQueryVendorString (dpy);
99 GST_INFO ("VA-API driver vendor: %s", vendor);
100
101 /* XXX(victor): driver allow list */
102
103 if (foreign_display) {
104 priv->display = foreign_display;
105 priv->foreign = TRUE;
106 }
107 priv->impl = _get_implementation (vendor);
108
109 return TRUE;
110 }
111
112 static void
gst_va_display_set_display(GstVaDisplay * self,gpointer display)113 gst_va_display_set_display (GstVaDisplay * self, gpointer display)
114 {
115 GstVaDisplayPrivate *priv = GET_PRIV (self);
116
117 if (!display)
118 return;
119
120 if (vaDisplayIsValid (display) == 0) {
121 GST_WARNING_OBJECT (self,
122 "User's VA display is invalid. An internal one will be tried.");
123 return;
124 }
125
126 /* assume driver is already initialized */
127 priv->init = TRUE;
128
129 _gst_va_display_filter_driver (self, display);
130 }
131
132 static void
gst_va_display_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)133 gst_va_display_set_property (GObject * object, guint prop_id,
134 const GValue * value, GParamSpec * pspec)
135 {
136 GstVaDisplay *self = GST_VA_DISPLAY (object);
137
138 switch (prop_id) {
139 case PROP_VA_DISPLAY:{
140 gpointer display = g_value_get_pointer (value);
141 gst_va_display_set_display (self, display);
142 break;
143 }
144 default:
145 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
146 break;
147 }
148 }
149
150 static void
gst_va_display_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)151 gst_va_display_get_property (GObject * object, guint prop_id, GValue * value,
152 GParamSpec * pspec)
153 {
154 GstVaDisplayPrivate *priv = GET_PRIV (object);
155
156 switch (prop_id) {
157 case PROP_VA_DISPLAY:
158 g_value_set_pointer (value, priv->display);
159 break;
160 default:
161 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
162 break;
163 }
164 }
165
166 static void
gst_va_display_constructed(GObject * object)167 gst_va_display_constructed (GObject * object)
168 {
169 GstVaDisplay *self = GST_VA_DISPLAY (object);
170 GstVaDisplayPrivate *priv = GET_PRIV (object);
171 GstVaDisplayClass *klass = GST_VA_DISPLAY_GET_CLASS (object);
172
173 if (!priv->display && klass->create_va_display)
174 priv->display = klass->create_va_display (self);
175
176 G_OBJECT_CLASS (parent_class)->constructed (object);
177 }
178
179 static void
gst_va_display_dispose(GObject * object)180 gst_va_display_dispose (GObject * object)
181 {
182 GstVaDisplayPrivate *priv = GET_PRIV (object);
183
184 if (priv->display && !priv->foreign)
185 vaTerminate (priv->display);
186 priv->display = NULL;
187
188 G_OBJECT_CLASS (parent_class)->dispose (object);
189 }
190
191 static void
gst_va_display_finalize(GObject * object)192 gst_va_display_finalize (GObject * object)
193 {
194 GstVaDisplayPrivate *priv = GET_PRIV (object);
195
196 g_rec_mutex_clear (&priv->lock);
197
198 G_OBJECT_CLASS (parent_class)->finalize (object);
199 }
200
201 static void
gst_va_display_class_init(GstVaDisplayClass * klass)202 gst_va_display_class_init (GstVaDisplayClass * klass)
203 {
204 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
205
206 gobject_class->set_property = gst_va_display_set_property;
207 gobject_class->get_property = gst_va_display_get_property;
208 gobject_class->constructed = gst_va_display_constructed;
209 gobject_class->dispose = gst_va_display_dispose;
210 gobject_class->finalize = gst_va_display_finalize;
211
212 g_properties[PROP_VA_DISPLAY] =
213 g_param_spec_pointer ("va-display", "VADisplay", "VA Display handler",
214 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
215
216 g_object_class_install_properties (gobject_class, N_PROPERTIES, g_properties);
217 }
218
219 static void
gst_va_display_init(GstVaDisplay * self)220 gst_va_display_init (GstVaDisplay * self)
221 {
222 GstVaDisplayPrivate *priv = GET_PRIV (self);
223
224 g_rec_mutex_init (&priv->lock);
225 priv->impl = GST_VA_IMPLEMENTATION_INVALID;
226 }
227
228 /**
229 * gst_va_display_lock:
230 * @self: a #GstVaDisplay
231 *
232 * Lock the display. It will be used before we call the
233 * VA API functions to serialize the VA commands.
234 *
235 * Since: 1.20
236 **/
237 void
gst_va_display_lock(GstVaDisplay * self)238 gst_va_display_lock (GstVaDisplay * self)
239 {
240 GstVaDisplayPrivate *priv;
241
242 g_return_if_fail (GST_IS_VA_DISPLAY (self));
243
244 priv = GET_PRIV (self);
245
246 g_rec_mutex_lock (&priv->lock);
247 }
248
249 /**
250 * gst_va_display_unlock:
251 * @self: a #GstVaDisplay
252 *
253 * Unlock the display. It will be used after we call the
254 * VA API functions.
255 *
256 * Since: 1.20
257 **/
258 void
gst_va_display_unlock(GstVaDisplay * self)259 gst_va_display_unlock (GstVaDisplay * self)
260 {
261 GstVaDisplayPrivate *priv;
262
263 g_return_if_fail (GST_IS_VA_DISPLAY (self));
264
265 priv = GET_PRIV (self);
266
267 g_rec_mutex_unlock (&priv->lock);
268 }
269
270 #ifndef GST_DISABLE_GST_DEBUG
271 static gchar *
_strip_msg(const char * message)272 _strip_msg (const char *message)
273 {
274 gchar *msg = g_strdup (message);
275 if (!msg)
276 return NULL;
277 return g_strstrip (msg);
278 }
279
280 static void
_va_warning(gpointer object,const char * message)281 _va_warning (gpointer object, const char *message)
282 {
283 GstVaDisplay *self = GST_VA_DISPLAY (object);
284 gchar *msg;
285
286 if ((msg = _strip_msg (message))) {
287 GST_WARNING_OBJECT (self, "VA error: %s", msg);
288 g_free (msg);
289 }
290 }
291
292 static void
_va_info(gpointer object,const char * message)293 _va_info (gpointer object, const char *message)
294 {
295 GstVaDisplay *self = GST_VA_DISPLAY (object);
296 gchar *msg;
297
298 if ((msg = _strip_msg (message))) {
299 GST_INFO_OBJECT (self, "VA info: %s", msg);
300 g_free (msg);
301 }
302 }
303 #endif
304
305 /**
306 * gst_va_display_initialize:
307 * @self: a #GstVaDisplay
308 *
309 * If the display is set by the user (foreign) it is assumed that the
310 * driver is already initialized, thus this function is noop.
311 *
312 * If the display is opened internally, this function will initialize
313 * the driver and it will set driver's message callbacks.
314 *
315 * NOTE: this function is supposed to be private, only used by
316 * GstVaDisplay descendants.
317 *
318 * Returns: %TRUE if the VA driver can be initialized; %FALSE
319 * otherwise
320 *
321 * Since: 1.20
322 **/
323 gboolean
gst_va_display_initialize(GstVaDisplay * self)324 gst_va_display_initialize (GstVaDisplay * self)
325 {
326 GstVaDisplayPrivate *priv;
327 VAStatus status;
328 int major_version = -1, minor_version = -1;
329
330 g_return_val_if_fail (GST_IS_VA_DISPLAY (self), FALSE);
331
332 priv = GET_PRIV (self);
333
334 if (priv->init)
335 return TRUE;
336
337 if (!priv->display)
338 return FALSE;
339
340 #ifndef GST_DISABLE_GST_DEBUG
341 vaSetErrorCallback (priv->display, _va_warning, self);
342 vaSetInfoCallback (priv->display, _va_info, self);
343 #endif
344
345 status = vaInitialize (priv->display, &major_version, &minor_version);
346 if (status != VA_STATUS_SUCCESS) {
347 GST_WARNING_OBJECT (self, "vaInitialize: %s", vaErrorStr (status));
348 return FALSE;
349 }
350
351 GST_INFO_OBJECT (self, "VA-API version %d.%d", major_version, minor_version);
352
353 priv->init = TRUE;
354
355 if (!_gst_va_display_filter_driver (self, NULL))
356 return FALSE;
357
358 return TRUE;
359 }
360
361 /**
362 * gst_va_display_get_va_dpy:
363 * @self: a #GstVaDisplay type display.
364 *
365 * Get the VA display handle of the @self.
366 *
367 * Returns: the VA display handle.
368 *
369 * Since: 1.20
370 */
371 gpointer
gst_va_display_get_va_dpy(GstVaDisplay * self)372 gst_va_display_get_va_dpy (GstVaDisplay * self)
373 {
374 VADisplay dpy;
375
376 g_return_val_if_fail (GST_IS_VA_DISPLAY (self), NULL);
377
378 g_object_get (self, "va-display", &dpy, NULL);
379 return dpy;
380 }
381
382 /**
383 * gst_va_display_get_implementation:
384 * @self: a #GstVaDisplay type display.
385 *
386 * Get the the #GstVaImplementation type of @self.
387 *
388 * Returns: #GstVaImplementation.
389 *
390 * Since: 1.20
391 */
392 GstVaImplementation
gst_va_display_get_implementation(GstVaDisplay * self)393 gst_va_display_get_implementation (GstVaDisplay * self)
394 {
395 GstVaDisplayPrivate *priv;
396
397 g_return_val_if_fail (GST_IS_VA_DISPLAY (self),
398 GST_VA_IMPLEMENTATION_INVALID);
399
400 priv = GET_PRIV (self);
401 return priv->impl;
402 }
403