fb32027d831e813166e72481d8637d4c0278d7c8
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / qsv / gstqsvallocator.cpp
1 /* GStreamer
2  * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gstqsvallocator.h"
25
26 GST_DEBUG_CATEGORY_EXTERN (gst_qsv_allocator_debug);
27 #define GST_CAT_DEFAULT gst_qsv_allocator_debug
28
29 /* Both d3d11 and va use (GST_MAP_FLAG_LAST << 1) value
30  * for GPU access */
31 #define GST_MAP_QSV (GST_MAP_FLAG_LAST << 1)
32
33 struct _GstQsvFrame
34 {
35   GstMiniObject parent;
36
37   GstQsvAllocator *allocator;
38
39   GMutex lock;
40
41   guint map_count;
42   GstBuffer *buffer;
43   GstVideoInfo info;
44   GstVideoFrame frame;
45   GstQsvMemoryType mem_type;
46   GstMapFlags map_flags;
47 };
48
49 GST_DEFINE_MINI_OBJECT_TYPE (GstQsvFrame, gst_qsv_frame);
50
51 static void
52 _gst_qsv_frame_free (GstQsvFrame * frame)
53 {
54   g_mutex_clear (&frame->lock);
55   gst_clear_buffer (&frame->buffer);
56   gst_clear_object (&frame->allocator);
57   g_free (frame);
58 }
59
60 static GstQsvFrame *
61 gst_qsv_frame_new (void)
62 {
63   GstQsvFrame *self;
64
65   self = g_new0 (GstQsvFrame, 1);
66   g_mutex_init (&self->lock);
67
68   gst_mini_object_init (GST_MINI_OBJECT_CAST (self), 0,
69       GST_TYPE_QSV_FRAME, nullptr, nullptr,
70       (GstMiniObjectFreeFunction) _gst_qsv_frame_free);
71
72   return self;
73 }
74
75 GstBuffer *
76 gst_qsv_frame_peek_buffer (GstQsvFrame * frame)
77 {
78   g_return_val_if_fail (GST_IS_QSV_FRAME (frame), nullptr);
79
80   return frame->buffer;
81 }
82
83 gboolean
84 gst_qsv_frame_set_buffer (GstQsvFrame * frame, GstBuffer * buffer)
85 {
86   g_return_val_if_fail (GST_IS_QSV_FRAME (frame), FALSE);
87
88   g_mutex_lock (&frame->lock);
89   if (frame->buffer == buffer) {
90     g_mutex_unlock (&frame->lock);
91     return TRUE;
92   }
93
94   if (frame->map_count > 0) {
95     GST_ERROR ("frame is locked");
96     g_mutex_unlock (&frame->lock);
97
98     return FALSE;
99   }
100
101   gst_clear_buffer (&frame->buffer);
102   frame->buffer = buffer;
103   g_mutex_unlock (&frame->lock);
104
105   return TRUE;
106 }
107
108 struct _GstQsvAllocatorPrivate
109 {
110   GstAtomicQueue *queue;
111
112   mfxFrameAllocator allocator;
113   mfxFrameAllocResponse response;
114   guint16 extra_alloc_size;
115   gboolean dummy_alloc;
116 };
117
118 #define gst_qsv_allocator_parent_class parent_class
119 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstQsvAllocator,
120     gst_qsv_allocator, GST_TYPE_OBJECT);
121
122 static void gst_qsv_allocator_finalize (GObject * object);
123 static mfxStatus gst_qsv_allocator_alloc (mfxHDL pthis,
124     mfxFrameAllocRequest * request, mfxFrameAllocResponse * response);
125 static mfxStatus gst_qsv_allocator_lock (mfxHDL pthis, mfxMemId mid,
126     mfxFrameData * ptr);
127 static mfxStatus gst_qsv_allocator_unlock (mfxHDL pthis, mfxMemId mid,
128     mfxFrameData * ptr);
129 static mfxStatus gst_qsv_allocator_get_hdl (mfxHDL pthis, mfxMemId mid,
130     mfxHDL * handle);
131 static mfxStatus gst_qsv_allocator_free (mfxHDL pthis,
132     mfxFrameAllocResponse * response);
133 static GstBuffer *gst_qsv_allocator_download_default (GstQsvAllocator * self,
134     const GstVideoInfo * info, gboolean force_copy, GstQsvFrame * frame,
135     GstBufferPool * pool);
136
137 static void
138 gst_qsv_allocator_class_init (GstQsvAllocatorClass * klass)
139 {
140   GObjectClass *object_class = G_OBJECT_CLASS (klass);
141
142   object_class->finalize = gst_qsv_allocator_finalize;
143
144   klass->download = GST_DEBUG_FUNCPTR (gst_qsv_allocator_download_default);
145 }
146
147 static void
148 gst_qsv_allocator_init (GstQsvAllocator * self)
149 {
150   GstQsvAllocatorPrivate *priv;
151
152   priv = self->priv = (GstQsvAllocatorPrivate *)
153       gst_qsv_allocator_get_instance_private (self);
154
155   priv->queue = gst_atomic_queue_new (16);
156
157   priv->allocator.pthis = self;
158   priv->allocator.Alloc = gst_qsv_allocator_alloc;
159   priv->allocator.Lock = gst_qsv_allocator_lock;
160   priv->allocator.Unlock = gst_qsv_allocator_unlock;
161   priv->allocator.GetHDL = gst_qsv_allocator_get_hdl;
162   priv->allocator.Free = gst_qsv_allocator_free;
163 }
164
165 static void
166 gst_qsv_allocator_finalize (GObject * object)
167 {
168   GstQsvAllocator *self = GST_QSV_ALLOCATOR (object);
169   GstQsvAllocatorPrivate *priv = self->priv;
170   GstQsvFrame *frame;
171
172   GST_DEBUG_OBJECT (object, "finalize");
173
174   while ((frame = (GstQsvFrame *) gst_atomic_queue_pop (priv->queue)))
175     gst_qsv_frame_unref (frame);
176
177   gst_atomic_queue_unref (priv->queue);
178   gst_qsv_allocator_free ((mfxHDL) self, &priv->response);
179
180   G_OBJECT_CLASS (parent_class)->finalize (object);
181 }
182
183 static mfxStatus
184 gst_qsv_allocator_alloc_default (GstQsvAllocator * self, gboolean dummy_alloc,
185     mfxFrameAllocRequest * request, mfxFrameAllocResponse * response)
186 {
187   GstQsvFrame **mids = nullptr;
188   GstVideoInfo info;
189   GstVideoAlignment align;
190   GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
191   GstBufferPool *pool;
192   GstCaps *caps;
193   GstStructure *config;
194
195   /* Something unexpected and went wrong */
196   if ((request->Type & MFX_MEMTYPE_SYSTEM_MEMORY) == 0) {
197     GST_ERROR_OBJECT (self,
198         "MFX is requesting system memory, type 0x%x", request->Type);
199     return MFX_ERR_UNSUPPORTED;
200   }
201
202   switch (request->Info.FourCC) {
203     case MFX_FOURCC_NV12:
204       format = GST_VIDEO_FORMAT_NV12;
205       break;
206     case MFX_FOURCC_P010:
207       format = GST_VIDEO_FORMAT_P010_10LE;
208       break;
209     case MFX_FOURCC_AYUV:
210       format = GST_VIDEO_FORMAT_VUYA;
211       break;
212     case MFX_FOURCC_Y410:
213       format = GST_VIDEO_FORMAT_Y410;
214       break;
215     default:
216       /* TODO: add more formats */
217       break;
218   }
219
220   if (format == GST_VIDEO_FORMAT_UNKNOWN) {
221     GST_ERROR_OBJECT (self, "Unknown MFX format fourcc %" GST_FOURCC_FORMAT,
222         GST_FOURCC_ARGS (request->Info.FourCC));
223
224     return MFX_ERR_UNSUPPORTED;
225   }
226
227   mids = g_new0 (GstQsvFrame *, request->NumFrameSuggested);
228   response->NumFrameActual = request->NumFrameSuggested;
229
230   gst_video_info_set_format (&info,
231       format, request->Info.CropW, request->Info.CropH);
232
233   if (dummy_alloc) {
234     for (guint i = 0; i < request->NumFrameSuggested; i++) {
235       mids[i] = gst_qsv_allocator_acquire_frame (self,
236           GST_QSV_SYSTEM_MEMORY, &info, nullptr, nullptr);
237     }
238
239     response->mids = (mfxMemId *) mids;
240
241     return MFX_ERR_NONE;
242   }
243
244   caps = gst_video_info_to_caps (&info);
245   if (!caps) {
246     GST_ERROR_OBJECT (self, "Failed to convert video-info to caps");
247     return MFX_ERR_UNSUPPORTED;
248   }
249
250   gst_video_alignment_reset (&align);
251   align.padding_right = request->Info.Width - request->Info.CropW;
252   align.padding_bottom = request->Info.Height - request->Info.CropH;
253
254   pool = gst_video_buffer_pool_new ();
255   config = gst_buffer_pool_get_config (pool);
256   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
257   gst_buffer_pool_config_add_option (config,
258       GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
259   gst_buffer_pool_config_set_video_alignment (config, &align);
260   gst_buffer_pool_config_set_params (config, caps, GST_VIDEO_INFO_SIZE (&info),
261       0, 0);
262   gst_caps_unref (caps);
263   gst_buffer_pool_set_config (pool, config);
264   gst_buffer_pool_set_active (pool, TRUE);
265
266   for (guint i = 0; i < request->NumFrameSuggested; i++) {
267     GstBuffer *buffer;
268
269     if (gst_buffer_pool_acquire_buffer (pool, &buffer, nullptr) != GST_FLOW_OK) {
270       GST_ERROR_OBJECT (self, "Failed to allocate texture buffer");
271       gst_buffer_pool_set_active (pool, FALSE);
272       gst_object_unref (pool);
273       goto error;
274     }
275
276     mids[i] = gst_qsv_allocator_acquire_frame (self,
277         GST_QSV_SYSTEM_MEMORY, &info, buffer, nullptr);
278   }
279
280   gst_buffer_pool_set_active (pool, FALSE);
281   gst_object_unref (pool);
282
283   response->mids = (mfxMemId *) mids;
284
285   return MFX_ERR_NONE;
286
287 error:
288   if (mids) {
289     for (guint i = 0; i < response->NumFrameActual; i++)
290       gst_clear_qsv_frame (&mids[i]);
291
292     g_free (mids);
293   }
294
295   response->NumFrameActual = 0;
296
297   return MFX_ERR_MEMORY_ALLOC;
298 }
299
300 static gboolean
301 gst_qsv_allocator_copy_cached_response (GstQsvAllocator * self,
302     mfxFrameAllocResponse * dst, mfxFrameAllocResponse * src)
303 {
304   GstQsvFrame **mids;
305
306   if (src->NumFrameActual == 0)
307     return FALSE;
308
309   mids = g_new0 (GstQsvFrame *, src->NumFrameActual);
310
311   for (guint i = 0; i < src->NumFrameActual; i++) {
312     GstQsvFrame *frame = (GstQsvFrame *) src->mids[i];
313
314     mids[i] = gst_qsv_frame_ref (frame);
315   }
316
317   dst->NumFrameActual = src->NumFrameActual;
318   dst->mids = (mfxMemId *) mids;
319
320   return TRUE;
321 }
322
323 static mfxStatus
324 gst_qsv_allocator_alloc (mfxHDL pthis,
325     mfxFrameAllocRequest * request, mfxFrameAllocResponse * response)
326 {
327   GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
328   GstQsvAllocatorPrivate *priv = self->priv;
329   GstQsvAllocatorClass *klass;
330   mfxStatus status;
331   mfxFrameAllocRequest req = *request;
332   gboolean dummy_alloc = priv->dummy_alloc;
333
334   GST_INFO_OBJECT (self, "Alloc, Request Type: 0x%x, %dx%d (%dx%d)",
335       req.Type, req.Info.Width, req.Info.Height,
336       req.Info.CropW, req.Info.CropH);
337
338   /* Apply extra_alloc_size only for GST internal use case */
339   if ((request->Type & MFX_MEMTYPE_EXTERNAL_FRAME) != 0)
340     req.NumFrameSuggested += priv->extra_alloc_size;
341
342   if (req.Info.CropW == 0 || req.Info.CropH == 0) {
343     req.Info.CropW = req.Info.Width;
344     req.Info.CropH = req.Info.Height;
345   }
346
347   if (request->Info.FourCC == MFX_FOURCC_P8 ||
348       (request->Type & MFX_MEMTYPE_EXTERNAL_FRAME) == 0) {
349     dummy_alloc = FALSE;
350   }
351
352   GST_INFO_OBJECT (self, "Dummy alloc %d", dummy_alloc);
353
354   if ((request->Type & MFX_MEMTYPE_SYSTEM_MEMORY) != 0) {
355     status = gst_qsv_allocator_alloc_default (self,
356         dummy_alloc, &req, response);
357   } else {
358     klass = GST_QSV_ALLOCATOR_GET_CLASS (self);
359     g_assert (klass->alloc);
360
361     status = klass->alloc (self, dummy_alloc, &req, response);
362   }
363
364   if (status != MFX_ERR_NONE)
365     return status;
366
367   /* Cache this respons so that this can be accessible from GST side */
368   if (dummy_alloc) {
369     gst_qsv_allocator_free ((mfxHDL) self, &priv->response);
370     gst_qsv_allocator_copy_cached_response (self, &priv->response, response);
371   }
372
373   return MFX_ERR_NONE;
374 }
375
376 static mfxStatus
377 gst_qsv_allocator_lock (mfxHDL pthis, mfxMemId mid, mfxFrameData * ptr)
378 {
379   GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
380   GstQsvFrame *frame = (GstQsvFrame *) mid;
381   guint stride;
382
383   GST_TRACE_OBJECT (self, "Lock mfxMemId %p", mid);
384
385   g_mutex_lock (&frame->lock);
386   if (!frame->buffer) {
387     GST_ERROR_OBJECT (self, "MemId %p doesn't hold buffer", mid);
388     g_mutex_unlock (&frame->lock);
389     return MFX_ERR_LOCK_MEMORY;
390   }
391
392   if (frame->map_count == 0) {
393     gst_video_frame_map (&frame->frame, &frame->info, frame->buffer,
394         frame->map_flags);
395   }
396
397   frame->map_count++;
398   stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame->frame, 0);
399
400   /* FIXME: check and handle other formats */
401   switch (GST_VIDEO_INFO_FORMAT (&frame->info)) {
402     case GST_VIDEO_FORMAT_NV12:
403     case GST_VIDEO_FORMAT_P010_10LE:
404       ptr->Pitch = (mfxU16) stride;
405       ptr->Y = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
406       ptr->UV = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 1);
407       break;
408     case GST_VIDEO_FORMAT_VUYA:
409       ptr->PitchHigh = (mfxU16) (stride / (1 << 16));
410       ptr->PitchLow = (mfxU16) (stride % (1 << 16));
411       ptr->V = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
412       ptr->U = ptr->V + 1;
413       ptr->Y = ptr->V + 2;
414       ptr->A = ptr->V + 3;
415       break;
416     case GST_VIDEO_FORMAT_Y410:
417       ptr->PitchHigh = (mfxU16) (stride / (1 << 16));
418       ptr->PitchLow = (mfxU16) (stride % (1 << 16));
419       ptr->Y410 = (mfxY410 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
420       break;
421     default:
422       break;
423   }
424
425   g_mutex_unlock (&frame->lock);
426
427   return MFX_ERR_NONE;
428 }
429
430 static mfxStatus
431 gst_qsv_allocator_unlock (mfxHDL pthis, mfxMemId mid, mfxFrameData * ptr)
432 {
433   GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
434   GstQsvFrame *frame = (GstQsvFrame *) mid;
435
436   GST_TRACE_OBJECT (self, "Unlock mfxMemId %p", mid);
437
438   g_mutex_lock (&frame->lock);
439
440   if (frame->map_count > 0) {
441     frame->map_count--;
442
443     if (frame->map_count == 0)
444       gst_video_frame_unmap (&frame->frame);
445   } else {
446     GST_WARNING_OBJECT (self, "Unlock request for non-locked memory");
447   }
448
449   g_mutex_unlock (&frame->lock);
450
451   return MFX_ERR_NONE;
452 }
453
454 static mfxStatus
455 gst_qsv_allocator_get_hdl (mfxHDL pthis, mfxMemId mid, mfxHDL * handle)
456 {
457   GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
458   GstQsvFrame *frame = GST_QSV_FRAME_CAST (mid);
459   GstMapInfo map_info;
460
461   if (!GST_QSV_MEM_TYPE_IS_VIDEO (frame->mem_type)) {
462     GST_ERROR_OBJECT (self, "Unexpected call");
463     return MFX_ERR_UNSUPPORTED;
464   }
465
466   g_mutex_lock (&frame->lock);
467   if (!frame->buffer) {
468     GST_ERROR_OBJECT (self, "MemId %p doesn't hold buffer", mid);
469     g_mutex_unlock (&frame->lock);
470
471     return MFX_ERR_UNSUPPORTED;
472   }
473
474   g_assert ((frame->map_flags & GST_MAP_QSV) != 0);
475   if (!gst_buffer_map (frame->buffer, &map_info, frame->map_flags)) {
476     GST_ERROR_OBJECT (self, "Failed to map buffer");
477     g_mutex_unlock (&frame->lock);
478
479     return MFX_ERR_UNSUPPORTED;
480   }
481
482   GST_TRACE_OBJECT (self, "Get handle for mfxMemId %p", mid);
483
484 #ifdef G_OS_WIN32
485   mfxHDLPair *pair = (mfxHDLPair *) handle;
486   pair->first = (mfxHDL) map_info.data;
487
488   /* GstD3D11 will fill user_data[0] with subresource index */
489   pair->second = (mfxHDL) map_info.user_data[0];
490 #else
491   *handle = (mfxHDL) map_info.data;
492 #endif
493
494   /* XXX: Ideally we should unmap only when this surface is unlocked... */
495   gst_buffer_unmap (frame->buffer, &map_info);
496   g_mutex_unlock (&frame->lock);
497
498   return MFX_ERR_NONE;
499 }
500
501 static mfxStatus
502 gst_qsv_allocator_free (mfxHDL pthis, mfxFrameAllocResponse * response)
503 {
504   GstQsvFrame **frames = (GstQsvFrame **) response->mids;
505
506   for (guint i = 0; i < response->NumFrameActual; i++)
507     gst_clear_qsv_frame (&frames[i]);
508
509   g_clear_pointer (&response->mids, g_free);
510   response->NumFrameActual = 0;
511
512   return MFX_ERR_NONE;
513 }
514
515 static void
516 gst_qsv_frame_release (GstQsvFrame * frame)
517 {
518   GstQsvAllocator *allocator = frame->allocator;
519
520   g_mutex_lock (&frame->lock);
521   if (frame->map_count > 0) {
522     GST_WARNING_OBJECT (allocator, "Releasing mapped frame %p", frame);
523     gst_video_frame_unmap (&frame->frame);
524   }
525   frame->map_count = 0;
526   gst_clear_buffer (&frame->buffer);
527   g_mutex_unlock (&frame->lock);
528
529   GST_MINI_OBJECT_CAST (frame)->dispose = nullptr;
530   frame->allocator = nullptr;
531
532   GST_TRACE_OBJECT (allocator, "Moving frame %p back to pool", frame);
533
534   gst_atomic_queue_push (allocator->priv->queue, frame);
535   gst_object_unref (allocator);
536 }
537
538 static gboolean
539 gst_qsv_frame_dispose (GstQsvFrame * frame)
540 {
541   g_assert (frame->allocator);
542
543   gst_qsv_frame_ref (frame);
544   gst_qsv_frame_release (frame);
545
546   return FALSE;
547 }
548
549 static GstBuffer *
550 gst_qsv_allocator_upload_default (GstQsvAllocator * allocator,
551     const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool)
552 {
553   GstBuffer *dst_buf;
554   GstFlowReturn flow_ret;
555   GstVideoFrame src_frame, dst_frame;
556
557   flow_ret = gst_buffer_pool_acquire_buffer (pool, &dst_buf, nullptr);
558   if (flow_ret != GST_FLOW_OK) {
559     GST_WARNING ("Failed to acquire buffer from pool, return %s",
560         gst_flow_get_name (flow_ret));
561     return nullptr;
562   }
563
564   gst_video_frame_map (&src_frame, info, buffer, GST_MAP_READ);
565   gst_video_frame_map (&dst_frame, info, dst_buf, GST_MAP_WRITE);
566
567   if (GST_VIDEO_FRAME_WIDTH (&src_frame) == GST_VIDEO_FRAME_WIDTH (&dst_frame)
568       && GST_VIDEO_FRAME_HEIGHT (&src_frame) ==
569       GST_VIDEO_FRAME_HEIGHT (&dst_frame)) {
570     gst_video_frame_unmap (&src_frame);
571     gst_video_frame_unmap (&dst_frame);
572
573     gst_buffer_unref (dst_buf);
574     return gst_buffer_ref (buffer);
575   }
576
577   for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES (&src_frame); i++) {
578     guint src_width_in_bytes, src_height;
579     guint dst_width_in_bytes, dst_height;
580     guint width_in_bytes, height;
581     guint src_stride, dst_stride;
582     guint8 *src_data, *dst_data;
583
584     src_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&src_frame, i) *
585         GST_VIDEO_FRAME_COMP_PSTRIDE (&src_frame, i);
586     src_height = GST_VIDEO_FRAME_COMP_HEIGHT (&src_frame, i);
587     src_stride = GST_VIDEO_FRAME_COMP_STRIDE (&src_frame, i);
588
589     dst_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&dst_frame, i) *
590         GST_VIDEO_FRAME_COMP_PSTRIDE (&dst_frame, i);
591     dst_height = GST_VIDEO_FRAME_COMP_HEIGHT (&dst_frame, i);
592     dst_stride = GST_VIDEO_FRAME_COMP_STRIDE (&dst_frame, i);
593
594     width_in_bytes = MIN (src_width_in_bytes, dst_width_in_bytes);
595     height = MIN (src_height, dst_height);
596
597     src_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&src_frame, i);
598     dst_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&dst_frame, i);
599
600     for (guint j = 0; j < height; j++) {
601       memcpy (dst_data, src_data, width_in_bytes);
602       dst_data += dst_stride;
603       src_data += src_stride;
604     }
605   }
606
607   gst_video_frame_unmap (&dst_frame);
608   gst_video_frame_unmap (&src_frame);
609
610   return dst_buf;
611 }
612
613 /**
614  * gst_qsv_allocator_acquire_frame:
615  * @allocator: a #GstQsvAllocator
616  * @mem_type: a memory type
617  * @info: a #GstVideoInfo
618  * @buffer: (nullable) (transfer full): a #GstBuffer
619  * @pool: (nullable): a #GstBufferPool
620  *
621  * Uploads @buffer to video memory if required, and wraps GstBuffer using
622  * #GstQsvFrame object so that QSV API can access native memory handle
623  * via mfxFrameAllocator interface.
624  *
625  * Returns: a #GstQsvFrame object
626  */
627 GstQsvFrame *
628 gst_qsv_allocator_acquire_frame (GstQsvAllocator * allocator,
629     GstQsvMemoryType mem_type, const GstVideoInfo * info, GstBuffer * buffer,
630     GstBufferPool * pool)
631 {
632   GstQsvAllocatorPrivate *priv;
633   GstQsvFrame *frame;
634   guint32 map_flags = 0;
635
636   g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
637
638   if (GST_QSV_MEM_TYPE_IS_SYSTEM (mem_type) &&
639       GST_QSV_MEM_TYPE_IS_VIDEO (mem_type)) {
640     GST_ERROR_OBJECT (allocator, "Invalid memory type");
641     return nullptr;
642   }
643
644   if (GST_QSV_MEM_TYPE_IS_VIDEO (mem_type)) {
645     map_flags = GST_MAP_QSV;
646
647     if ((mem_type & GST_QSV_ENCODER_IN_MEMORY) != 0) {
648       map_flags |= GST_MAP_READ;
649     } else if ((mem_type & GST_QSV_DECODER_OUT_MEMORY) != 0) {
650       map_flags |= GST_MAP_WRITE;
651     } else {
652       GST_ERROR_OBJECT (allocator,
653           "Unknown read/write access for video memory");
654       return nullptr;
655     }
656   } else if ((mem_type & GST_QSV_ENCODER_IN_MEMORY) != 0) {
657     map_flags = GST_MAP_READ;
658   } else {
659     map_flags = GST_MAP_READWRITE;
660   }
661
662   priv = allocator->priv;
663   frame = (GstQsvFrame *) gst_atomic_queue_pop (priv->queue);
664
665   if (!frame)
666     frame = gst_qsv_frame_new ();
667
668   frame->mem_type = mem_type;
669   frame->map_flags = (GstMapFlags) map_flags;
670   frame->info = *info;
671
672   if (!pool) {
673     frame->buffer = buffer;
674   } else if (buffer) {
675     GstBuffer *upload_buf;
676
677     frame->allocator = (GstQsvAllocator *) gst_object_ref (allocator);
678     GST_MINI_OBJECT_CAST (frame)->dispose =
679         (GstMiniObjectDisposeFunction) gst_qsv_frame_dispose;
680
681     if (GST_QSV_MEM_TYPE_IS_SYSTEM (mem_type)) {
682       upload_buf = gst_qsv_allocator_upload_default (allocator, info, buffer,
683           pool);
684     } else {
685       GstQsvAllocatorClass *klass;
686
687       klass = GST_QSV_ALLOCATOR_GET_CLASS (allocator);
688       g_assert (klass->upload);
689
690       upload_buf = klass->upload (allocator, info, buffer, pool);
691     }
692
693     gst_buffer_unref (buffer);
694
695     if (!upload_buf) {
696       GST_WARNING_OBJECT (allocator, "Failed to upload buffer");
697       gst_qsv_frame_unref (frame);
698
699       return nullptr;
700     }
701
702     frame->buffer = upload_buf;
703   }
704
705   return frame;
706 }
707
708 static GstBuffer *
709 gst_qsv_allocator_download_default (GstQsvAllocator * self,
710     const GstVideoInfo * info, gboolean force_copy, GstQsvFrame * frame,
711     GstBufferPool * pool)
712 {
713   GstBuffer *buffer = nullptr;
714   GstFlowReturn ret;
715   GstVideoFrame dst_frame;
716   mfxStatus status;
717   mfxFrameData dummy;
718   gboolean copy_ret;
719
720   GST_TRACE_OBJECT (self, "Download");
721
722   if (!force_copy)
723     return gst_buffer_ref (frame->buffer);
724
725   ret = gst_buffer_pool_acquire_buffer (pool, &buffer, nullptr);
726   if (ret != GST_FLOW_OK) {
727     GST_WARNING_OBJECT (self, "Failed to acquire buffer");
728     return nullptr;
729   }
730
731   /* Use gst_qsv_allocator_lock() instead of gst_video_frame_map() to avoid
732    * redundant map if it's already locked by driver, already locked by driver
733    * sounds unsafe situaltion though */
734   status = gst_qsv_allocator_lock ((mfxHDL) self, (mfxMemId) frame, &dummy);
735   if (status != MFX_ERR_NONE) {
736     gst_buffer_unref (buffer);
737     GST_ERROR_OBJECT (self, "Failed to lock frame");
738     return nullptr;
739   }
740
741   if (!gst_video_frame_map (&dst_frame, &frame->info, buffer, GST_MAP_WRITE)) {
742     gst_qsv_allocator_unlock ((mfxHDL) self, (mfxMemId) frame, &dummy);
743     gst_buffer_unref (buffer);
744     GST_ERROR_OBJECT (self, "Failed to map output buffer");
745     return nullptr;
746   }
747
748   copy_ret = gst_video_frame_copy (&dst_frame, &frame->frame);
749   gst_qsv_allocator_unlock ((mfxHDL) self, (mfxMemId) frame, &dummy);
750   gst_video_frame_unmap (&dst_frame);
751
752   if (!copy_ret) {
753     GST_ERROR_OBJECT (self, "Failed to copy frame");
754     gst_buffer_unref (buffer);
755     return nullptr;
756   }
757
758   return buffer;
759 }
760
761 GstBuffer *
762 gst_qsv_allocator_download_frame (GstQsvAllocator * allocator,
763     gboolean force_copy, GstQsvFrame * frame, GstBufferPool * pool)
764 {
765   GstQsvAllocatorClass *klass;
766
767   g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
768   g_return_val_if_fail (GST_IS_QSV_FRAME (frame), nullptr);
769   g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), nullptr);
770
771   if (GST_QSV_MEM_TYPE_IS_SYSTEM (frame->mem_type)) {
772     return gst_qsv_allocator_download_default (allocator, &frame->info,
773         force_copy, frame, pool);
774   }
775
776   klass = GST_QSV_ALLOCATOR_GET_CLASS (allocator);
777   g_assert (klass->download);
778
779   return klass->download (allocator, &frame->info, force_copy, frame, pool);
780 }
781
782 mfxFrameAllocator *
783 gst_qsv_allocator_get_allocator_handle (GstQsvAllocator * allocator)
784 {
785   g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
786
787   return &allocator->priv->allocator;
788 }
789
790 gboolean
791 gst_qsv_allocator_get_cached_response (GstQsvAllocator * allocator,
792     mfxFrameAllocResponse * response)
793 {
794   g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), FALSE);
795
796   return gst_qsv_allocator_copy_cached_response (allocator,
797       response, &allocator->priv->response);
798 }
799
800 void
801 gst_qsv_allocator_set_options (GstQsvAllocator * allocator,
802     guint16 extra_alloc_size, gboolean dummy_alloc)
803 {
804   g_return_if_fail (GST_IS_QSV_ALLOCATOR (allocator));
805
806   allocator->priv->extra_alloc_size = extra_alloc_size;
807   allocator->priv->dummy_alloc = dummy_alloc;
808 }