taglist, plugins: fix compiler warnings with GLib >= 2.76
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / msdk / gstmsdkcontext.c
1 /* GStreamer Intel MSDK plugin
2  * Copyright (c) 2018, Intel Corporation
3  * Copyright (c) 2018, Igalia S.L.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  *    this list of conditions and the following disclaimer.
11  *
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.
15  *
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.
19  *
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.
31  */
32
33 #include "gstmsdkcontext.h"
34 #ifndef _WIN32
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <xf86drm.h>
38 #include <va/va_drm.h>
39 #include <gudev/gudev.h>
40 #include <gst/va/gstvadisplay_drm.h>
41 #else
42 #include <gst/d3d11/gstd3d11.h>
43 #endif
44
45 GST_DEBUG_CATEGORY_STATIC (gst_debug_msdkcontext);
46 #define GST_CAT_DEFAULT gst_debug_msdkcontext
47
48 struct _GstMsdkContextPrivate
49 {
50   MsdkSession session;
51   GList *cached_alloc_responses;
52   gboolean hardware;
53   gboolean has_frame_allocator;
54   GstMsdkContextJobType job_type;
55   gint shared_async_depth;
56   GMutex mutex;
57   GList *child_session_list;
58   GstMsdkContext *parent_context;
59 #ifndef _WIN32
60   GstVaDisplay *display;
61 #else
62   GstD3D11Device *device;
63 #endif
64 };
65
66 #define gst_msdk_context_parent_class parent_class
67 G_DEFINE_TYPE_WITH_CODE (GstMsdkContext, gst_msdk_context, GST_TYPE_OBJECT,
68     G_ADD_PRIVATE (GstMsdkContext)
69     GST_DEBUG_CATEGORY_INIT (gst_debug_msdkcontext, "msdkcontext", 0,
70         "MSDK Context"));
71
72 #ifndef _WIN32
73
74 static char *
75 get_device_path (void)
76 {
77   GUdevClient *client = NULL;
78   GUdevEnumerator *e = NULL;
79   GList *devices, *l;
80   GUdevDevice *dev, *parent;
81   const gchar *devnode_path;
82   const gchar *devnode_files[2] = { "renderD[0-9]*", "card[0-9]*" };
83   int fd = -1, i;
84   const gchar *user_choice = g_getenv ("GST_MSDK_DRM_DEVICE");
85   gchar *ret_path = NULL;
86
87   if (user_choice) {
88     if (g_str_has_prefix (user_choice, "/dev/dri/"))
89       fd = open (user_choice, O_RDWR | O_CLOEXEC);
90
91     if (fd >= 0) {
92       drmVersionPtr drm_version = drmGetVersion (fd);
93
94       if (!drm_version || strncmp (drm_version->name, "i915", 4)) {
95         GST_ERROR ("The specified device isn't an Intel device");
96         drmFreeVersion (drm_version);
97         close (fd);
98         fd = -1;
99       } else {
100         GST_DEBUG ("Opened the specified drm device %s", user_choice);
101         drmFreeVersion (drm_version);
102       }
103     } else {
104       GST_ERROR ("The specified device isn't a valid drm device");
105     }
106
107     if (fd >= 0) {
108       ret_path = g_strdup (user_choice);
109       close (fd);
110     }
111
112     return ret_path;
113   }
114
115   client = g_udev_client_new (NULL);
116   if (!client)
117     goto done;
118
119   e = g_udev_enumerator_new (client);
120   if (!e)
121     goto done;
122
123   g_udev_enumerator_add_match_subsystem (e, "drm");
124   for (i = 0; i < 2; i++) {
125     g_udev_enumerator_add_match_name (e, devnode_files[i]);
126     devices = g_udev_enumerator_execute (e);
127
128     for (l = devices; l != NULL; l = l->next) {
129       dev = (GUdevDevice *) l->data;
130
131       parent = g_udev_device_get_parent (dev);
132       if (strcmp (g_udev_device_get_subsystem (parent), "pci") != 0 ||
133           strcmp (g_udev_device_get_driver (parent), "i915") != 0) {
134         g_object_unref (parent);
135         continue;
136       }
137       g_object_unref (parent);
138
139       devnode_path = g_udev_device_get_device_file (dev);
140       fd = open (devnode_path, O_RDWR | O_CLOEXEC);
141       if (fd < 0)
142         continue;
143       GST_DEBUG ("Opened the drm device node %s", devnode_path);
144       ret_path = g_strdup (devnode_path);
145       break;
146     }
147
148     g_list_foreach (devices, (GFunc) gst_object_unref, NULL);
149     g_list_free (devices);
150     if (fd >= 0)
151       goto done;
152   }
153
154 done:
155   if (fd >= 0)
156     close (fd);
157
158   if (e)
159     g_object_unref (e);
160   if (client)
161     g_object_unref (client);
162
163   return ret_path;
164 }
165
166 static gboolean
167 gst_msdk_context_use_vaapi (GstMsdkContext * context)
168 {
169   char *path;
170   VADisplay va_dpy = NULL;
171   GstVaDisplay *display_drm = NULL;
172   mfxStatus status;
173   GstMsdkContextPrivate *priv = context->priv;
174
175   path = get_device_path ();
176   if (path == NULL) {
177     GST_WARNING ("Couldn't find a drm device node to open");
178     return FALSE;
179   }
180
181   display_drm = gst_va_display_drm_new_from_path (path);
182   if (!display_drm) {
183     GST_ERROR ("Couldn't create a VA DRM display");
184     goto failed;
185   }
186   g_free (path);
187
188   va_dpy = gst_va_display_get_va_dpy (display_drm);
189
190   status = MFXVideoCORE_SetHandle (priv->session.session, MFX_HANDLE_VA_DISPLAY,
191       (mfxHDL) va_dpy);
192   if (status != MFX_ERR_NONE) {
193     GST_ERROR ("Setting VAAPI handle failed (%s)",
194         msdk_status_to_string (status));
195     goto failed;
196   }
197
198   priv->display = display_drm;
199
200   return TRUE;
201
202 failed:
203   if (display_drm)
204     gst_object_unref (display_drm);
205
206   return FALSE;
207 }
208 #else
209 static GstD3D11Device *
210 get_device_by_index (IDXGIFactory1 * factory, guint idx)
211 {
212   HRESULT hr;
213   IDXGIAdapter1 *adapter;
214   ID3D11Device *device_handle;
215   ID3D10Multithread *multi_thread;
216   DXGI_ADAPTER_DESC desc;
217   GstD3D11Device *device = NULL;
218   gint64 luid;
219
220   hr = IDXGIFactory1_EnumAdapters1 (factory, idx, &adapter);
221   if (FAILED (hr)) {
222     return NULL;
223   }
224
225   hr = IDXGIAdapter1_GetDesc (adapter, &desc);
226   if (FAILED (hr)) {
227     IDXGIAdapter1_Release (adapter);
228     return NULL;
229   }
230
231   if (desc.VendorId != 0x8086) {
232     IDXGIAdapter1_Release (adapter);
233     return NULL;
234   }
235
236   luid = gst_d3d11_luid_to_int64 (&desc.AdapterLuid);
237   device = gst_d3d11_device_new_for_adapter_luid (luid,
238       D3D11_CREATE_DEVICE_BGRA_SUPPORT);
239   IDXGIAdapter1_Release (adapter);
240
241   device_handle = gst_d3d11_device_get_device_handle (device);
242   hr = ID3D11Device_QueryInterface (device_handle,
243       &IID_ID3D10Multithread, (void **) &multi_thread);
244   if (FAILED (hr)) {
245     gst_object_unref (device);
246     return NULL;
247   }
248
249   hr = ID3D10Multithread_SetMultithreadProtected (multi_thread, TRUE);
250   ID3D10Multithread_Release (multi_thread);
251
252   return device;
253 }
254
255 static gboolean
256 gst_msdk_context_use_d3d11 (GstMsdkContext * context)
257 {
258   HRESULT hr;
259   IDXGIFactory1 *factory = NULL;
260   GstD3D11Device *device = NULL;
261   ID3D11Device *device_handle;
262   GstMsdkContextPrivate *priv = context->priv;
263   mfxStatus status;
264   guint idx = 0;
265   gint user_idx = -1;
266   const gchar *user_choice = g_getenv ("GST_MSDK_DEVICE");
267
268   hr = CreateDXGIFactory1 (&IID_IDXGIFactory1, (void **) &factory);
269   if (FAILED (hr)) {
270     GST_ERROR ("Couldn't create DXGI factory");
271     return FALSE;
272   }
273
274   if (user_choice) {
275     user_idx = atoi (user_choice);
276     if (!(device = get_device_by_index (factory, user_idx)))
277       GST_WARNING
278           ("Failed to get device by user index, try to pick the first available device");
279   }
280
281   /* Pick the first available device */
282   while (!device) {
283     device = get_device_by_index (factory, idx++);
284   }
285
286   IDXGIFactory1_Release (factory);
287   device_handle = gst_d3d11_device_get_device_handle (device);
288
289   status =
290       MFXVideoCORE_SetHandle (priv->session.session, MFX_HANDLE_D3D11_DEVICE,
291       gst_d3d11_device_get_device_handle (device));
292   if (status != MFX_ERR_NONE) {
293     GST_ERROR ("Setting D3D11VA handle failed (%s)",
294         msdk_status_to_string (status));
295     gst_object_unref (device);
296     return FALSE;
297   }
298
299   priv->device = device;
300
301   return TRUE;
302 }
303 #endif
304
305 static gboolean
306 gst_msdk_context_open (GstMsdkContext * context, gboolean hardware,
307     GstMsdkContextJobType job_type)
308 {
309   mfxU16 codename;
310   GstMsdkContextPrivate *priv = context->priv;
311   MsdkSession msdk_session;
312   mfxIMPL impl;
313
314   priv->job_type = job_type;
315   priv->hardware = hardware;
316
317   impl = hardware ? MFX_IMPL_HARDWARE_ANY : MFX_IMPL_SOFTWARE;
318
319 #ifdef _WIN32
320   impl |= MFX_IMPL_VIA_D3D11;
321 #endif
322
323   msdk_session = msdk_open_session (impl);
324   priv->session = msdk_session;
325   if (!priv->session.session)
326     goto failed;
327
328 #ifndef _WIN32
329   if (hardware) {
330     if (!gst_msdk_context_use_vaapi (context))
331       goto failed;
332   }
333 #else
334   if (hardware) {
335     if (!gst_msdk_context_use_d3d11 (context))
336       goto failed;
337   }
338 #endif
339
340   codename = msdk_get_platform_codename (priv->session.session);
341
342   if (codename != MFX_PLATFORM_UNKNOWN)
343     GST_INFO ("Detected MFX platform with device code %d", codename);
344   else
345     GST_WARNING ("Unknown MFX platform");
346
347   return TRUE;
348
349 failed:
350   return FALSE;
351 }
352
353 static void
354 gst_msdk_context_init (GstMsdkContext * context)
355 {
356   GstMsdkContextPrivate *priv = gst_msdk_context_get_instance_private (context);
357
358   context->priv = priv;
359
360   g_mutex_init (&priv->mutex);
361 }
362
363 static void
364 release_child_session (gpointer session)
365 {
366   mfxStatus status;
367
368   mfxSession _session = session;
369   status = MFXDisjoinSession (_session);
370   if (status != MFX_ERR_NONE)
371     GST_WARNING ("failed to disjoin (%s)", msdk_status_to_string (status));
372   msdk_close_mfx_session (_session);
373 }
374
375 static void
376 gst_msdk_context_finalize (GObject * obj)
377 {
378   GstMsdkContext *context = GST_MSDK_CONTEXT_CAST (obj);
379   GstMsdkContextPrivate *priv = context->priv;
380
381   /* child sessions will be closed when the parent session is closed */
382   if (priv->parent_context) {
383     gst_object_unref (priv->parent_context);
384     goto done;
385   } else
386     g_list_free_full (priv->child_session_list, release_child_session);
387
388   msdk_close_session (&priv->session);
389   g_mutex_clear (&priv->mutex);
390
391 #ifndef _WIN32
392   if (priv->display)
393     gst_object_unref (priv->display);
394 #else
395   if (priv->device)
396     gst_object_unref (priv->device);
397 #endif
398
399 done:
400   G_OBJECT_CLASS (parent_class)->finalize (obj);
401 }
402
403 static void
404 gst_msdk_context_class_init (GstMsdkContextClass * klass)
405 {
406   GObjectClass *const g_object_class = G_OBJECT_CLASS (klass);
407
408   g_object_class->finalize = gst_msdk_context_finalize;
409 }
410
411 GstMsdkContext *
412 gst_msdk_context_new (gboolean hardware, GstMsdkContextJobType job_type)
413 {
414   GstMsdkContext *obj = g_object_new (GST_TYPE_MSDK_CONTEXT, NULL);
415
416   if (obj && !gst_msdk_context_open (obj, hardware, job_type)) {
417     if (obj)
418       gst_object_unref (obj);
419     return NULL;
420   }
421
422   return obj;
423 }
424
425 GstMsdkContext *
426 gst_msdk_context_new_with_parent (GstMsdkContext * parent)
427 {
428   mfxStatus status;
429   GstMsdkContext *obj = g_object_new (GST_TYPE_MSDK_CONTEXT, NULL);
430   GstMsdkContextPrivate *priv = obj->priv;
431   GstMsdkContextPrivate *parent_priv = parent->priv;
432   mfxVersion version;
433   mfxIMPL impl;
434   MsdkSession child_msdk_session;
435   mfxHandleType handle_type = 0;
436   mfxHDL handle = NULL;
437
438   status = MFXQueryIMPL (parent_priv->session.session, &impl);
439
440   if (status == MFX_ERR_NONE)
441     status = MFXQueryVersion (parent_priv->session.session, &version);
442
443   if (status != MFX_ERR_NONE) {
444     GST_ERROR ("Failed to query the session attributes (%s)",
445         msdk_status_to_string (status));
446     gst_object_unref (obj);
447     return NULL;
448   }
449
450   if (MFX_IMPL_VIA_VAAPI == (0x0f00 & (impl)))
451     handle_type = MFX_HANDLE_VA_DISPLAY;
452   else if (MFX_IMPL_VIA_D3D11 == (0x0f00 & (impl)))
453     handle_type = MFX_HANDLE_D3D11_DEVICE;
454
455   if (handle_type) {
456     status =
457         MFXVideoCORE_GetHandle (parent_priv->session.session, handle_type,
458         &handle);
459
460     if (status != MFX_ERR_NONE || !handle) {
461       GST_ERROR ("Failed to get session handle (%s)",
462           msdk_status_to_string (status));
463       gst_object_unref (obj);
464       return NULL;
465     }
466   }
467
468   child_msdk_session.loader = parent_priv->session.loader;
469   child_msdk_session.session = NULL;
470   status = msdk_init_msdk_session (impl, &version, &child_msdk_session);
471
472   if (status != MFX_ERR_NONE) {
473     GST_ERROR ("Failed to create a child mfx session (%s)",
474         msdk_status_to_string (status));
475     gst_object_unref (obj);
476     return NULL;
477   }
478
479   if (handle) {
480     status =
481         MFXVideoCORE_SetHandle (child_msdk_session.session, handle_type,
482         handle);
483
484     if (status != MFX_ERR_NONE) {
485       GST_ERROR ("Failed to set a HW handle (%s)",
486           msdk_status_to_string (status));
487       MFXClose (child_msdk_session.session);
488       gst_object_unref (obj);
489       return NULL;
490     }
491   }
492 #if (MFX_VERSION >= 1025)
493   status =
494       MFXJoinSession (parent_priv->session.session, child_msdk_session.session);
495
496   if (status != MFX_ERR_NONE) {
497     GST_ERROR ("Failed to join two sessions (%s)",
498         msdk_status_to_string (status));
499     MFXClose (child_msdk_session.session);
500     gst_object_unref (obj);
501     return NULL;
502   }
503 #endif
504
505   /* Set loader to NULL for child session */
506   priv->session.loader = NULL;
507   priv->session.session = child_msdk_session.session;
508   priv->hardware = parent_priv->hardware;
509   priv->job_type = parent_priv->job_type;
510   parent_priv->child_session_list =
511       g_list_prepend (parent_priv->child_session_list, priv->session.session);
512 #ifndef _WIN32
513   priv->display = parent_priv->display;
514 #else
515   priv->device = parent_priv->device;
516 #endif
517   priv->parent_context = gst_object_ref (parent);
518
519   return obj;
520 }
521
522 #ifndef _WIN32
523 GstMsdkContext *
524 gst_msdk_context_new_with_va_display (GstObject * display_obj,
525     gboolean hardware, GstMsdkContextJobType job_type)
526 {
527   GstMsdkContext *obj = NULL;
528
529   GstMsdkContextPrivate *priv;
530   mfxU16 codename;
531   mfxStatus status;
532   GstVaDisplay *va_display;
533
534   va_display = GST_VA_DISPLAY (display_obj);
535   if (!va_display)
536     return NULL;
537
538   obj = g_object_new (GST_TYPE_MSDK_CONTEXT, NULL);
539
540   priv = obj->priv;
541   priv->display = gst_object_ref (va_display);
542
543   priv->job_type = job_type;
544   priv->hardware = hardware;
545   priv->session =
546       msdk_open_session (hardware ? MFX_IMPL_HARDWARE_ANY : MFX_IMPL_SOFTWARE);
547   if (!priv->session.session) {
548     gst_object_unref (obj);
549     return NULL;
550   }
551
552   if (hardware) {
553     status =
554         MFXVideoCORE_SetHandle (priv->session.session, MFX_HANDLE_VA_DISPLAY,
555         (mfxHDL) gst_va_display_get_va_dpy (priv->display));
556     if (status != MFX_ERR_NONE) {
557       GST_ERROR ("Setting VAAPI handle failed (%s)",
558           msdk_status_to_string (status));
559       gst_object_unref (obj);
560       return NULL;
561     }
562   }
563
564   codename = msdk_get_platform_codename (priv->session.session);
565
566   if (codename != MFX_PLATFORM_UNKNOWN)
567     GST_INFO ("Detected MFX platform with device code %d", codename);
568   else
569     GST_WARNING ("Unknown MFX platform");
570
571   return obj;
572 }
573 #else
574 GstMsdkContext *
575 gst_msdk_context_new_with_d3d11_device (GstD3D11Device * device,
576     gboolean hardware, GstMsdkContextJobType job_type)
577 {
578   GstMsdkContext *obj = NULL;
579   GstMsdkContextPrivate *priv;
580   mfxU16 codename;
581   mfxStatus status;
582   ID3D10Multithread *multi_thread;
583   ID3D11Device *device_handle;
584   HRESULT hr;
585
586   obj = g_object_new (GST_TYPE_MSDK_CONTEXT, NULL);
587
588   priv = obj->priv;
589   priv->device = gst_object_ref (device);
590
591   priv->job_type = job_type;
592   priv->hardware = hardware;
593   priv->session =
594       msdk_open_session (hardware ? MFX_IMPL_HARDWARE_ANY : MFX_IMPL_SOFTWARE);
595   if (!priv->session.session) {
596     goto failed;
597   }
598
599   device_handle = gst_d3d11_device_get_device_handle (device);
600   hr = ID3D11Device_QueryInterface (device_handle,
601       &IID_ID3D10Multithread, (void **) &multi_thread);
602   if (FAILED (hr)) {
603     GST_ERROR ("ID3D10Multithread interface is unavailable");
604     goto failed;
605   }
606
607   hr = ID3D10Multithread_SetMultithreadProtected (multi_thread, TRUE);
608   ID3D10Multithread_Release (multi_thread);
609
610   if (hardware) {
611     status =
612         MFXVideoCORE_SetHandle (priv->session.session, MFX_HANDLE_D3D11_DEVICE,
613         device_handle);
614     if (status != MFX_ERR_NONE) {
615       GST_ERROR ("Setting D3D11VA handle failed (%s)",
616           msdk_status_to_string (status));
617       goto failed;
618     }
619   }
620
621   codename = msdk_get_platform_codename (priv->session.session);
622
623   if (codename != MFX_PLATFORM_UNKNOWN)
624     GST_INFO ("Detected MFX platform with device code %d", codename);
625   else
626     GST_WARNING ("Unknown MFX platform");
627
628   return obj;
629
630 failed:
631   gst_object_unref (obj);
632   gst_object_unref (device);
633   return NULL;
634 }
635 #endif
636
637 mfxSession
638 gst_msdk_context_get_session (GstMsdkContext * context)
639 {
640   return context->priv->session.session;
641 }
642
643 gpointer
644 gst_msdk_context_get_handle (GstMsdkContext * context)
645 {
646 #ifndef _WIN32
647   return gst_va_display_get_va_dpy (context->priv->display);
648 #else
649   return NULL;
650 #endif
651 }
652
653 #ifndef _WIN32
654 GstObject *
655 gst_msdk_context_get_va_display (GstMsdkContext * context)
656 {
657   if (context->priv->display)
658     return gst_object_ref (GST_OBJECT_CAST (context->priv->display));
659   return NULL;
660 }
661 #else
662 GstD3D11Device *
663 gst_msdk_context_get_d3d11_device (GstMsdkContext * context)
664 {
665   if (context->priv->device)
666     return gst_object_ref (context->priv->device);
667   return NULL;
668 }
669 #endif
670
671 static gint
672 _find_response (gconstpointer resp, gconstpointer comp_resp)
673 {
674   GstMsdkAllocResponse *cached_resp = (GstMsdkAllocResponse *) resp;
675   mfxFrameAllocResponse *_resp = (mfxFrameAllocResponse *) comp_resp;
676
677   return cached_resp ? cached_resp->response.mids != _resp->mids : -1;
678 }
679
680 static inline gboolean
681 _requested_frame_size_is_equal_or_lower (mfxFrameAllocRequest * _req,
682     GstMsdkAllocResponse * cached_resp)
683 {
684   if (((_req->Type & MFX_MEMTYPE_EXPORT_FRAME) &&
685           _req->Info.Width == cached_resp->request.Info.Width &&
686           _req->Info.Height == cached_resp->request.Info.Height) ||
687       (!(_req->Type & MFX_MEMTYPE_EXPORT_FRAME) &&
688           _req->Info.Width <= cached_resp->request.Info.Width &&
689           _req->Info.Height <= cached_resp->request.Info.Height))
690     return TRUE;
691
692   return FALSE;
693 }
694
695 static gint
696 _find_request (gconstpointer resp, gconstpointer req)
697 {
698   GstMsdkAllocResponse *cached_resp = (GstMsdkAllocResponse *) resp;
699   mfxFrameAllocRequest *_req = (mfxFrameAllocRequest *) req;
700
701   /* Confirm if it's under the size of the cached response */
702   if (_req->NumFrameSuggested <= cached_resp->request.NumFrameSuggested &&
703       _requested_frame_size_is_equal_or_lower (_req, cached_resp))
704     return _req->Type & cached_resp->
705         request.Type & MFX_MEMTYPE_FROM_DECODE ? 0 : -1;
706
707   return -1;
708 }
709
710 GstMsdkAllocResponse *
711 gst_msdk_context_get_cached_alloc_responses (GstMsdkContext * context,
712     mfxFrameAllocResponse * resp)
713 {
714   GstMsdkContextPrivate *priv = context->priv;
715   GList *l =
716       g_list_find_custom (priv->cached_alloc_responses, resp, _find_response);
717
718   if (l)
719     return l->data;
720   else
721     return NULL;
722 }
723
724 GstMsdkAllocResponse *
725 gst_msdk_context_get_cached_alloc_responses_by_request (GstMsdkContext *
726     context, mfxFrameAllocRequest * req)
727 {
728   GstMsdkContextPrivate *priv = context->priv;
729   GList *l =
730       g_list_find_custom (priv->cached_alloc_responses, req, _find_request);
731
732   if (l)
733     return l->data;
734   else
735     return NULL;
736 }
737
738 static void
739 create_surfaces (GstMsdkContext * context, GstMsdkAllocResponse * resp)
740 {
741   gint i;
742   mfxMemId *mem_id;
743   mfxFrameSurface1 *surface;
744
745   for (i = 0; i < resp->response.NumFrameActual; i++) {
746     mem_id = resp->response.mids[i];
747     surface = (mfxFrameSurface1 *) g_slice_new0 (mfxFrameSurface1);
748     if (!surface) {
749       GST_ERROR ("failed to allocate surface");
750       break;
751     }
752     surface->Data.MemId = mem_id;
753     resp->surfaces_avail = g_list_prepend (resp->surfaces_avail, surface);
754   }
755 }
756
757 static void
758 free_surface (gpointer surface)
759 {
760   g_slice_free1 (sizeof (mfxFrameSurface1), surface);
761 }
762
763 static void
764 remove_surfaces (GstMsdkContext * context, GstMsdkAllocResponse * resp)
765 {
766   g_list_free_full (resp->surfaces_used, free_surface);
767   g_list_free_full (resp->surfaces_avail, free_surface);
768   g_list_free_full (resp->surfaces_locked, free_surface);
769 }
770
771 void
772 gst_msdk_context_add_alloc_response (GstMsdkContext * context,
773     GstMsdkAllocResponse * resp)
774 {
775   context->priv->cached_alloc_responses =
776       g_list_prepend (context->priv->cached_alloc_responses, resp);
777
778   create_surfaces (context, resp);
779 }
780
781 gboolean
782 gst_msdk_context_remove_alloc_response (GstMsdkContext * context,
783     mfxFrameAllocResponse * resp)
784 {
785   GstMsdkAllocResponse *msdk_resp;
786   GstMsdkContextPrivate *priv = context->priv;
787   GList *l =
788       g_list_find_custom (priv->cached_alloc_responses, resp, _find_response);
789
790   if (!l)
791     return FALSE;
792
793   msdk_resp = l->data;
794
795   remove_surfaces (context, msdk_resp);
796
797   g_slice_free1 (sizeof (GstMsdkAllocResponse), msdk_resp);
798   priv->cached_alloc_responses =
799       g_list_delete_link (priv->cached_alloc_responses, l);
800
801   return TRUE;
802 }
803
804 static gboolean
805 check_surfaces_available (GstMsdkContext * context, GstMsdkAllocResponse * resp)
806 {
807   GList *l;
808   mfxFrameSurface1 *surface = NULL;
809   GstMsdkContextPrivate *priv = context->priv;
810   gboolean ret = FALSE;
811
812   g_mutex_lock (&priv->mutex);
813   for (l = resp->surfaces_locked; l;) {
814     surface = l->data;
815     l = l->next;
816     if (!surface->Data.Locked) {
817       resp->surfaces_locked = g_list_remove (resp->surfaces_locked, surface);
818       resp->surfaces_avail = g_list_prepend (resp->surfaces_avail, surface);
819       ret = TRUE;
820     }
821   }
822   g_mutex_unlock (&priv->mutex);
823
824   return ret;
825 }
826
827 /*
828  * There are 3 lists here in GstMsdkContext as the following:
829  * 1. surfaces_avail : surfaces which are free and unused anywhere
830  * 2. surfaces_used : surfaces coupled with a gst buffer and being used now.
831  * 3. surfaces_locked : surfaces still locked even after the gst buffer is released.
832  *
833  * Note that they need to be protected by mutex to be thread-safe.
834  */
835
836 mfxFrameSurface1 *
837 gst_msdk_context_get_surface_available (GstMsdkContext * context,
838     mfxFrameAllocResponse * resp)
839 {
840   GList *l;
841   mfxFrameSurface1 *surface = NULL;
842   GstMsdkAllocResponse *msdk_resp =
843       gst_msdk_context_get_cached_alloc_responses (context, resp);
844   gint retry = 0;
845   GstMsdkContextPrivate *priv = context->priv;
846
847 retry:
848   g_mutex_lock (&priv->mutex);
849   for (l = msdk_resp->surfaces_avail; l;) {
850     surface = l->data;
851     l = l->next;
852     if (!surface->Data.Locked) {
853       msdk_resp->surfaces_avail =
854           g_list_remove (msdk_resp->surfaces_avail, surface);
855       msdk_resp->surfaces_used =
856           g_list_prepend (msdk_resp->surfaces_used, surface);
857       break;
858     }
859   }
860   g_mutex_unlock (&priv->mutex);
861
862   /*
863    * If a msdk context is shared by multiple msdk elements,
864    * upstream msdk element sometimes needs to wait for a gst buffer
865    * to be released in downstream.
866    *
867    * Poll the pool for a maximum of 20 millisecond.
868    *
869    * FIXME: Is there any better way to handle this case?
870    */
871   if (!surface && retry < 20) {
872     /* If there's no surface available, find unlocked surfaces in the locked list,
873      * take it back to the available list and then search again.
874      */
875     check_surfaces_available (context, msdk_resp);
876     retry++;
877     g_usleep (1000);
878     goto retry;
879   }
880
881   return surface;
882 }
883
884 void
885 gst_msdk_context_put_surface_locked (GstMsdkContext * context,
886     mfxFrameAllocResponse * resp, mfxFrameSurface1 * surface)
887 {
888   GstMsdkContextPrivate *priv = context->priv;
889   GstMsdkAllocResponse *msdk_resp =
890       gst_msdk_context_get_cached_alloc_responses (context, resp);
891
892   g_mutex_lock (&priv->mutex);
893   if (!g_list_find (msdk_resp->surfaces_locked, surface)) {
894     msdk_resp->surfaces_used =
895         g_list_remove (msdk_resp->surfaces_used, surface);
896     msdk_resp->surfaces_locked =
897         g_list_prepend (msdk_resp->surfaces_locked, surface);
898   }
899   g_mutex_unlock (&priv->mutex);
900 }
901
902 void
903 gst_msdk_context_put_surface_available (GstMsdkContext * context,
904     mfxFrameAllocResponse * resp, mfxFrameSurface1 * surface)
905 {
906   GstMsdkContextPrivate *priv = context->priv;
907   GstMsdkAllocResponse *msdk_resp =
908       gst_msdk_context_get_cached_alloc_responses (context, resp);
909
910   g_mutex_lock (&priv->mutex);
911   if (!g_list_find (msdk_resp->surfaces_avail, surface)) {
912     msdk_resp->surfaces_used =
913         g_list_remove (msdk_resp->surfaces_used, surface);
914     msdk_resp->surfaces_avail =
915         g_list_prepend (msdk_resp->surfaces_avail, surface);
916   }
917   g_mutex_unlock (&priv->mutex);
918 }
919
920 GstMsdkContextJobType
921 gst_msdk_context_get_job_type (GstMsdkContext * context)
922 {
923   return context->priv->job_type;
924 }
925
926 void
927 gst_msdk_context_add_job_type (GstMsdkContext * context,
928     GstMsdkContextJobType job_type)
929 {
930   context->priv->job_type |= job_type;
931 }
932
933 gint
934 gst_msdk_context_get_shared_async_depth (GstMsdkContext * context)
935 {
936   return context->priv->shared_async_depth;
937 }
938
939 void
940 gst_msdk_context_add_shared_async_depth (GstMsdkContext * context,
941     gint async_depth)
942 {
943   context->priv->shared_async_depth += async_depth;
944 }
945
946 void
947 gst_msdk_context_set_frame_allocator (GstMsdkContext * context,
948     mfxFrameAllocator * allocator)
949 {
950   GstMsdkContextPrivate *priv = context->priv;
951
952   g_mutex_lock (&priv->mutex);
953
954   if (!priv->has_frame_allocator) {
955     mfxStatus status;
956
957     status = MFXVideoCORE_SetFrameAllocator (priv->session.session, allocator);
958
959     if (status != MFX_ERR_NONE)
960       GST_ERROR ("Failed to set frame allocator");
961     else
962       priv->has_frame_allocator = 1;
963   }
964
965   g_mutex_unlock (&priv->mutex);
966 }