1 /* GStreamer Intel MSDK plugin
2 * Copyright (c) 2018, Intel Corporation
3 * Copyright (c) 2018, Igalia S.L.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * 3. Neither the name of the copyright holder nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGDECE
29 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include "gstmsdkcontext.h"
37 #include <va/va_drm.h>
38 #include <gudev/gudev.h>
41 GST_DEBUG_CATEGORY_STATIC (gst_debug_msdkcontext);
42 #define GST_CAT_DEFAULT gst_debug_msdkcontext
44 struct _GstMsdkContextPrivate
47 GList *cached_alloc_responses;
50 gboolean has_frame_allocator;
51 GstMsdkContextJobType job_type;
52 gint shared_async_depth;
54 GList *child_session_list;
61 #define gst_msdk_context_parent_class parent_class
62 G_DEFINE_TYPE_WITH_CODE (GstMsdkContext, gst_msdk_context, GST_TYPE_OBJECT,
63 G_ADD_PRIVATE (GstMsdkContext)
64 GST_DEBUG_CATEGORY_INIT (gst_debug_msdkcontext, "msdkcontext", 0,
72 GUdevClient *client = NULL;
73 GUdevEnumerator *e = NULL;
75 GUdevDevice *dev, *parent;
76 const gchar *devnode_path;
77 const gchar *devnode_files[2] = { "renderD[0-9]*", "card[0-9]*" };
80 client = g_udev_client_new (NULL);
84 e = g_udev_enumerator_new (client);
88 g_udev_enumerator_add_match_subsystem (e, "drm");
89 for (i = 0; i < 2; i++) {
90 g_udev_enumerator_add_match_name (e, devnode_files[i]);
91 devices = g_udev_enumerator_execute (e);
93 for (l = devices; l != NULL; l = l->next) {
94 dev = (GUdevDevice *) l->data;
96 parent = g_udev_device_get_parent (dev);
97 if (strcmp (g_udev_device_get_subsystem (parent), "pci") != 0 ||
98 strcmp (g_udev_device_get_driver (parent), "i915") != 0) {
99 g_object_unref (parent);
102 g_object_unref (parent);
104 devnode_path = g_udev_device_get_device_file (dev);
105 fd = open (devnode_path, O_RDWR | O_CLOEXEC);
108 GST_DEBUG ("Opened the drm device node %s", devnode_path);
112 g_list_foreach (devices, (GFunc) gst_object_unref, NULL);
113 g_list_free (devices);
122 g_object_unref (client);
129 gst_msdk_context_use_vaapi (GstMsdkContext * context)
132 gint maj_ver, min_ver;
133 VADisplay va_dpy = NULL;
136 GstMsdkContextPrivate *priv = context->priv;
138 fd = get_device_id ();
140 GST_ERROR ("Couldn't find a drm device node to open");
144 va_dpy = vaGetDisplayDRM (fd);
146 GST_ERROR ("Couldn't get a VA DRM display");
150 va_status = vaInitialize (va_dpy, &maj_ver, &min_ver);
151 if (va_status != VA_STATUS_SUCCESS) {
152 GST_ERROR ("Couldn't initialize VA DRM display");
156 status = MFXVideoCORE_SetHandle (priv->session, MFX_HANDLE_VA_DISPLAY,
158 if (status != MFX_ERR_NONE) {
159 GST_ERROR ("Setting VAAPI handle failed (%s)",
160 msdk_status_to_string (status));
171 vaTerminate (va_dpy);
178 gst_msdk_context_open (GstMsdkContext * context, gboolean hardware,
179 GstMsdkContextJobType job_type)
181 GstMsdkContextPrivate *priv = context->priv;
183 priv->job_type = job_type;
184 priv->hardware = hardware;
186 msdk_open_session (hardware ? MFX_IMPL_HARDWARE_ANY : MFX_IMPL_SOFTWARE);
194 if (!gst_msdk_context_use_vaapi (context))
202 msdk_close_session (priv->session);
207 gst_msdk_context_init (GstMsdkContext * context)
209 GstMsdkContextPrivate *priv = gst_msdk_context_get_instance_private (context);
211 context->priv = priv;
213 g_mutex_init (&priv->mutex);
217 release_child_session (gpointer session)
221 mfxSession _session = session;
222 status = MFXDisjoinSession (_session);
223 if (status != MFX_ERR_NONE)
224 GST_WARNING ("failed to disjoin (%s)", msdk_status_to_string (status));
225 msdk_close_session (_session);
229 gst_msdk_context_finalize (GObject * obj)
231 GstMsdkContext *context = GST_MSDK_CONTEXT_CAST (obj);
232 GstMsdkContextPrivate *priv = context->priv;
234 /* child sessions will be closed when the parent session is closed */
238 g_list_free_full (priv->child_session_list, release_child_session);
240 msdk_close_session (priv->session);
241 g_mutex_clear (&priv->mutex);
245 vaTerminate (priv->dpy);
251 G_OBJECT_CLASS (parent_class)->finalize (obj);
255 gst_msdk_context_class_init (GstMsdkContextClass * klass)
257 GObjectClass *const g_object_class = G_OBJECT_CLASS (klass);
259 g_object_class->finalize = gst_msdk_context_finalize;
263 gst_msdk_context_new (gboolean hardware, GstMsdkContextJobType job_type)
265 GstMsdkContext *obj = g_object_new (GST_TYPE_MSDK_CONTEXT, NULL);
267 if (obj && !gst_msdk_context_open (obj, hardware, job_type)) {
269 gst_object_unref (obj);
277 gst_msdk_context_new_with_parent (GstMsdkContext * parent)
280 GstMsdkContext *obj = g_object_new (GST_TYPE_MSDK_CONTEXT, NULL);
281 GstMsdkContextPrivate *priv = obj->priv;
282 GstMsdkContextPrivate *parent_priv = parent->priv;
284 status = MFXCloneSession (parent_priv->session, &priv->session);
285 if (status != MFX_ERR_NONE) {
286 GST_ERROR ("Failed to clone mfx session");
287 g_object_unref (obj);
291 priv->is_joined = TRUE;
292 priv->hardware = parent_priv->hardware;
293 priv->job_type = parent_priv->job_type;
294 parent_priv->child_session_list =
295 g_list_prepend (parent_priv->child_session_list, priv->session);
297 priv->dpy = parent_priv->dpy;
298 priv->fd = parent_priv->fd;
300 if (priv->hardware) {
301 status = MFXVideoCORE_SetHandle (priv->session, MFX_HANDLE_VA_DISPLAY,
302 (mfxHDL) parent_priv->dpy);
304 if (status != MFX_ERR_NONE) {
305 GST_ERROR ("Setting VA handle failed (%s)",
306 msdk_status_to_string (status));
307 g_object_unref (obj);
318 gst_msdk_context_get_session (GstMsdkContext * context)
320 return context->priv->session;
324 gst_msdk_context_get_handle (GstMsdkContext * context)
327 return context->priv->dpy;
334 gst_msdk_context_get_fd (GstMsdkContext * context)
337 return context->priv->fd;
344 _find_response (gconstpointer resp, gconstpointer comp_resp)
346 GstMsdkAllocResponse *cached_resp = (GstMsdkAllocResponse *) resp;
347 mfxFrameAllocResponse *_resp = (mfxFrameAllocResponse *) comp_resp;
349 return cached_resp ? cached_resp->response.mids != _resp->mids : -1;
353 _find_request (gconstpointer resp, gconstpointer req)
355 GstMsdkAllocResponse *cached_resp = (GstMsdkAllocResponse *) resp;
356 mfxFrameAllocRequest *_req = (mfxFrameAllocRequest *) req;
358 /* Confirm if it's under the size of the cached response */
359 if (_req->Info.Width <= cached_resp->request.Info.Width &&
360 _req->Info.Height <= cached_resp->request.Info.Height &&
361 _req->NumFrameSuggested <= cached_resp->request.NumFrameSuggested) {
362 return _req->Type & cached_resp->
363 request.Type & MFX_MEMTYPE_FROM_DECODE ? 0 : -1;
369 GstMsdkAllocResponse *
370 gst_msdk_context_get_cached_alloc_responses (GstMsdkContext * context,
371 mfxFrameAllocResponse * resp)
373 GstMsdkContextPrivate *priv = context->priv;
375 g_list_find_custom (priv->cached_alloc_responses, resp, _find_response);
383 GstMsdkAllocResponse *
384 gst_msdk_context_get_cached_alloc_responses_by_request (GstMsdkContext *
385 context, mfxFrameAllocRequest * req)
387 GstMsdkContextPrivate *priv = context->priv;
389 g_list_find_custom (priv->cached_alloc_responses, req, _find_request);
398 create_surfaces (GstMsdkContext * context, GstMsdkAllocResponse * resp)
402 mfxFrameSurface1 *surface;
404 for (i = 0; i < resp->response.NumFrameActual; i++) {
405 mem_id = resp->response.mids[i];
406 surface = (mfxFrameSurface1 *) g_slice_new0 (mfxFrameSurface1);
408 GST_ERROR ("failed to allocate surface");
411 surface->Data.MemId = mem_id;
412 resp->surfaces_avail = g_list_prepend (resp->surfaces_avail, surface);
417 free_surface (gpointer surface)
419 g_slice_free1 (sizeof (mfxFrameSurface1), surface);
423 remove_surfaces (GstMsdkContext * context, GstMsdkAllocResponse * resp)
425 g_list_free_full (resp->surfaces_used, free_surface);
426 g_list_free_full (resp->surfaces_avail, free_surface);
427 g_list_free_full (resp->surfaces_locked, free_surface);
431 gst_msdk_context_add_alloc_response (GstMsdkContext * context,
432 GstMsdkAllocResponse * resp)
434 context->priv->cached_alloc_responses =
435 g_list_prepend (context->priv->cached_alloc_responses, resp);
437 create_surfaces (context, resp);
441 gst_msdk_context_remove_alloc_response (GstMsdkContext * context,
442 mfxFrameAllocResponse * resp)
444 GstMsdkAllocResponse *msdk_resp;
445 GstMsdkContextPrivate *priv = context->priv;
447 g_list_find_custom (priv->cached_alloc_responses, resp, _find_response);
454 remove_surfaces (context, msdk_resp);
456 g_slice_free1 (sizeof (GstMsdkAllocResponse), msdk_resp);
457 priv->cached_alloc_responses =
458 g_list_delete_link (priv->cached_alloc_responses, l);
464 check_surfaces_available (GstMsdkContext * context, GstMsdkAllocResponse * resp)
467 mfxFrameSurface1 *surface = NULL;
468 GstMsdkContextPrivate *priv = context->priv;
469 gboolean ret = FALSE;
471 g_mutex_lock (&priv->mutex);
472 for (l = resp->surfaces_locked; l;) {
475 if (!surface->Data.Locked) {
476 resp->surfaces_locked = g_list_remove (resp->surfaces_locked, surface);
477 resp->surfaces_avail = g_list_prepend (resp->surfaces_avail, surface);
481 g_mutex_unlock (&priv->mutex);
487 * There are 3 lists here in GstMsdkContext as the following:
488 * 1. surfaces_avail : surfaces which are free and unused anywhere
489 * 2. surfaces_used : surfaces coupled with a gst buffer and being used now.
490 * 3. surfaces_locked : surfaces still locked even after the gst buffer is released.
492 * Note that they need to be protected by mutex to be thread-safe.
496 gst_msdk_context_get_surface_available (GstMsdkContext * context,
497 mfxFrameAllocResponse * resp)
500 mfxFrameSurface1 *surface = NULL;
501 GstMsdkAllocResponse *msdk_resp =
502 gst_msdk_context_get_cached_alloc_responses (context, resp);
504 GstMsdkContextPrivate *priv = context->priv;
507 g_mutex_lock (&priv->mutex);
508 for (l = msdk_resp->surfaces_avail; l;) {
511 if (!surface->Data.Locked) {
512 msdk_resp->surfaces_avail =
513 g_list_remove (msdk_resp->surfaces_avail, surface);
514 msdk_resp->surfaces_used =
515 g_list_prepend (msdk_resp->surfaces_used, surface);
519 g_mutex_unlock (&priv->mutex);
522 * If a msdk context is shared by multiple msdk elements,
523 * upstream msdk element sometimes needs to wait for a gst buffer
524 * to be released in downstream.
526 * Poll the pool for a maximum of 20 milisecnds.
528 * FIXME: Is there any better way to handle this case?
530 if (!surface && retry < 20) {
531 /* If there's no surface available, find unlocked surfaces in the locked list,
532 * take it back to the available list and then search again.
534 check_surfaces_available (context, msdk_resp);
544 gst_msdk_context_put_surface_locked (GstMsdkContext * context,
545 mfxFrameAllocResponse * resp, mfxFrameSurface1 * surface)
547 GstMsdkContextPrivate *priv = context->priv;
548 GstMsdkAllocResponse *msdk_resp =
549 gst_msdk_context_get_cached_alloc_responses (context, resp);
551 g_mutex_lock (&priv->mutex);
552 if (!g_list_find (msdk_resp->surfaces_locked, surface)) {
553 msdk_resp->surfaces_used =
554 g_list_remove (msdk_resp->surfaces_used, surface);
555 msdk_resp->surfaces_locked =
556 g_list_prepend (msdk_resp->surfaces_locked, surface);
558 g_mutex_unlock (&priv->mutex);
562 gst_msdk_context_put_surface_available (GstMsdkContext * context,
563 mfxFrameAllocResponse * resp, mfxFrameSurface1 * surface)
565 GstMsdkContextPrivate *priv = context->priv;
566 GstMsdkAllocResponse *msdk_resp =
567 gst_msdk_context_get_cached_alloc_responses (context, resp);
569 g_mutex_lock (&priv->mutex);
570 if (!g_list_find (msdk_resp->surfaces_avail, surface)) {
571 msdk_resp->surfaces_used =
572 g_list_remove (msdk_resp->surfaces_used, surface);
573 msdk_resp->surfaces_avail =
574 g_list_prepend (msdk_resp->surfaces_avail, surface);
576 g_mutex_unlock (&priv->mutex);
579 GstMsdkContextJobType
580 gst_msdk_context_get_job_type (GstMsdkContext * context)
582 return context->priv->job_type;
586 gst_msdk_context_add_job_type (GstMsdkContext * context,
587 GstMsdkContextJobType job_type)
589 context->priv->job_type |= job_type;
593 gst_msdk_context_get_shared_async_depth (GstMsdkContext * context)
595 return context->priv->shared_async_depth;
599 gst_msdk_context_add_shared_async_depth (GstMsdkContext * context,
602 context->priv->shared_async_depth += async_depth;
606 gst_msdk_context_set_frame_allocator (GstMsdkContext * context,
607 mfxFrameAllocator * allocator)
609 GstMsdkContextPrivate *priv = context->priv;
611 g_mutex_lock (&priv->mutex);
613 if (!priv->has_frame_allocator) {
616 status = MFXVideoCORE_SetFrameAllocator (priv->session, allocator);
618 if (status != MFX_ERR_NONE)
619 GST_ERROR ("Failed to set frame allocator");
621 priv->has_frame_allocator = 1;
624 g_mutex_unlock (&priv->mutex);