qsv: Update SDK version to v2022.2.4
[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     guint map_flags = (guint) frame->map_flags;
394     map_flags &= ~((guint) GST_MAP_QSV);
395
396     gst_video_frame_map (&frame->frame, &frame->info, frame->buffer,
397         (GstMapFlags) map_flags);
398   }
399
400   frame->map_count++;
401   stride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame->frame, 0);
402
403   /* FIXME: check and handle other formats */
404   switch (GST_VIDEO_INFO_FORMAT (&frame->info)) {
405     case GST_VIDEO_FORMAT_NV12:
406     case GST_VIDEO_FORMAT_P010_10LE:
407       ptr->Pitch = (mfxU16) stride;
408       ptr->Y = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
409       ptr->UV = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 1);
410       break;
411     case GST_VIDEO_FORMAT_VUYA:
412       ptr->PitchHigh = (mfxU16) (stride / (1 << 16));
413       ptr->PitchLow = (mfxU16) (stride % (1 << 16));
414       ptr->V = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
415       ptr->U = ptr->V + 1;
416       ptr->Y = ptr->V + 2;
417       ptr->A = ptr->V + 3;
418       break;
419     case GST_VIDEO_FORMAT_Y410:
420       ptr->PitchHigh = (mfxU16) (stride / (1 << 16));
421       ptr->PitchLow = (mfxU16) (stride % (1 << 16));
422       ptr->Y410 = (mfxY410 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
423       break;
424     case GST_VIDEO_FORMAT_BGRA:
425       ptr->Pitch = (mfxU16) stride;
426       ptr->B = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
427       ptr->G = ptr->B + 1;
428       ptr->R = ptr->B + 2;
429       ptr->A = ptr->B + 3;
430       break;
431     case GST_VIDEO_FORMAT_RGBA:
432       ptr->Pitch = (mfxU16) stride;
433       ptr->R = (mfxU8 *) GST_VIDEO_FRAME_PLANE_DATA (&frame->frame, 0);
434       ptr->G = ptr->R + 1;
435       ptr->B = ptr->R + 2;
436       ptr->A = ptr->R + 3;
437       break;
438     default:
439       break;
440   }
441
442   g_mutex_unlock (&frame->lock);
443
444   return MFX_ERR_NONE;
445 }
446
447 static mfxStatus
448 gst_qsv_allocator_unlock (mfxHDL pthis, mfxMemId mid, mfxFrameData * ptr)
449 {
450   GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
451   GstQsvFrame *frame = (GstQsvFrame *) mid;
452
453   GST_TRACE_OBJECT (self, "Unlock mfxMemId %p", mid);
454
455   g_mutex_lock (&frame->lock);
456
457   if (frame->map_count > 0) {
458     frame->map_count--;
459
460     if (frame->map_count == 0)
461       gst_video_frame_unmap (&frame->frame);
462   } else {
463     GST_WARNING_OBJECT (self, "Unlock request for non-locked memory");
464   }
465
466   g_mutex_unlock (&frame->lock);
467
468   /* SDK will not re-lock unless we clear data pointer here. It happens
469    * on Linux with BGRA JPEG encoding */
470   ptr->R = nullptr;
471   ptr->G = nullptr;
472   ptr->B = nullptr;
473   ptr->A = nullptr;
474
475   return MFX_ERR_NONE;
476 }
477
478 static mfxStatus
479 gst_qsv_allocator_get_hdl (mfxHDL pthis, mfxMemId mid, mfxHDL * handle)
480 {
481   GstQsvAllocator *self = GST_QSV_ALLOCATOR (pthis);
482   GstQsvFrame *frame = GST_QSV_FRAME_CAST (mid);
483   GstMapInfo map_info;
484
485   if (!GST_QSV_MEM_TYPE_IS_VIDEO (frame->mem_type)) {
486     GST_ERROR_OBJECT (self, "Unexpected call");
487     return MFX_ERR_UNSUPPORTED;
488   }
489
490   g_mutex_lock (&frame->lock);
491   if (!frame->buffer) {
492     GST_ERROR_OBJECT (self, "MemId %p doesn't hold buffer", mid);
493     g_mutex_unlock (&frame->lock);
494
495     return MFX_ERR_UNSUPPORTED;
496   }
497
498   g_assert ((frame->map_flags & GST_MAP_QSV) != 0);
499   if (!gst_buffer_map (frame->buffer, &map_info, frame->map_flags)) {
500     GST_ERROR_OBJECT (self, "Failed to map buffer");
501     g_mutex_unlock (&frame->lock);
502
503     return MFX_ERR_UNSUPPORTED;
504   }
505
506   GST_TRACE_OBJECT (self, "Get handle for mfxMemId %p", mid);
507
508 #ifdef G_OS_WIN32
509   mfxHDLPair *pair = (mfxHDLPair *) handle;
510   pair->first = (mfxHDL) map_info.data;
511
512   /* GstD3D11 will fill user_data[0] with subresource index */
513   pair->second = (mfxHDL) map_info.user_data[0];
514 #else
515   *handle = (mfxHDL) map_info.data;
516 #endif
517
518   /* XXX: Ideally we should unmap only when this surface is unlocked... */
519   gst_buffer_unmap (frame->buffer, &map_info);
520   g_mutex_unlock (&frame->lock);
521
522   return MFX_ERR_NONE;
523 }
524
525 static mfxStatus
526 gst_qsv_allocator_free (mfxHDL pthis, mfxFrameAllocResponse * response)
527 {
528   GstQsvFrame **frames = (GstQsvFrame **) response->mids;
529
530   for (guint i = 0; i < response->NumFrameActual; i++)
531     gst_clear_qsv_frame (&frames[i]);
532
533   g_clear_pointer (&response->mids, g_free);
534   response->NumFrameActual = 0;
535
536   return MFX_ERR_NONE;
537 }
538
539 static void
540 gst_qsv_frame_release (GstQsvFrame * frame)
541 {
542   GstQsvAllocator *allocator = frame->allocator;
543
544   g_mutex_lock (&frame->lock);
545   if (frame->map_count > 0) {
546     GST_WARNING_OBJECT (allocator, "Releasing mapped frame %p", frame);
547     gst_video_frame_unmap (&frame->frame);
548   }
549   frame->map_count = 0;
550   gst_clear_buffer (&frame->buffer);
551   g_mutex_unlock (&frame->lock);
552
553   GST_MINI_OBJECT_CAST (frame)->dispose = nullptr;
554   frame->allocator = nullptr;
555
556   GST_TRACE_OBJECT (allocator, "Moving frame %p back to pool", frame);
557
558   gst_atomic_queue_push (allocator->priv->queue, frame);
559   gst_object_unref (allocator);
560 }
561
562 static gboolean
563 gst_qsv_frame_dispose (GstQsvFrame * frame)
564 {
565   g_assert (frame->allocator);
566
567   gst_qsv_frame_ref (frame);
568   gst_qsv_frame_release (frame);
569
570   return FALSE;
571 }
572
573 static GstBuffer *
574 gst_qsv_allocator_upload_default (GstQsvAllocator * allocator,
575     const GstVideoInfo * info, GstBuffer * buffer, GstBufferPool * pool)
576 {
577   GstBuffer *dst_buf;
578   GstFlowReturn flow_ret;
579   GstVideoFrame src_frame, dst_frame;
580
581   flow_ret = gst_buffer_pool_acquire_buffer (pool, &dst_buf, nullptr);
582   if (flow_ret != GST_FLOW_OK) {
583     GST_WARNING ("Failed to acquire buffer from pool, return %s",
584         gst_flow_get_name (flow_ret));
585     return nullptr;
586   }
587
588   gst_video_frame_map (&src_frame, info, buffer, GST_MAP_READ);
589   gst_video_frame_map (&dst_frame, info, dst_buf, GST_MAP_WRITE);
590
591   if (GST_VIDEO_FRAME_WIDTH (&src_frame) == GST_VIDEO_FRAME_WIDTH (&dst_frame)
592       && GST_VIDEO_FRAME_HEIGHT (&src_frame) ==
593       GST_VIDEO_FRAME_HEIGHT (&dst_frame)) {
594     gst_video_frame_unmap (&src_frame);
595     gst_video_frame_unmap (&dst_frame);
596
597     gst_buffer_unref (dst_buf);
598     return gst_buffer_ref (buffer);
599   }
600
601   for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES (&src_frame); i++) {
602     guint src_width_in_bytes, src_height;
603     guint dst_width_in_bytes, dst_height;
604     guint width_in_bytes, height;
605     guint src_stride, dst_stride;
606     guint8 *src_data, *dst_data;
607
608     src_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&src_frame, i) *
609         GST_VIDEO_FRAME_COMP_PSTRIDE (&src_frame, i);
610     src_height = GST_VIDEO_FRAME_COMP_HEIGHT (&src_frame, i);
611     src_stride = GST_VIDEO_FRAME_COMP_STRIDE (&src_frame, i);
612
613     dst_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&dst_frame, i) *
614         GST_VIDEO_FRAME_COMP_PSTRIDE (&dst_frame, i);
615     dst_height = GST_VIDEO_FRAME_COMP_HEIGHT (&dst_frame, i);
616     dst_stride = GST_VIDEO_FRAME_COMP_STRIDE (&dst_frame, i);
617
618     width_in_bytes = MIN (src_width_in_bytes, dst_width_in_bytes);
619     height = MIN (src_height, dst_height);
620
621     src_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&src_frame, i);
622     dst_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (&dst_frame, i);
623
624     for (guint j = 0; j < height; j++) {
625       memcpy (dst_data, src_data, width_in_bytes);
626       dst_data += dst_stride;
627       src_data += src_stride;
628     }
629   }
630
631   gst_video_frame_unmap (&dst_frame);
632   gst_video_frame_unmap (&src_frame);
633
634   return dst_buf;
635 }
636
637 /**
638  * gst_qsv_allocator_acquire_frame:
639  * @allocator: a #GstQsvAllocator
640  * @mem_type: a memory type
641  * @info: a #GstVideoInfo
642  * @buffer: (nullable) (transfer full): a #GstBuffer
643  * @pool: (nullable): a #GstBufferPool
644  *
645  * Uploads @buffer to video memory if required, and wraps GstBuffer using
646  * #GstQsvFrame object so that QSV API can access native memory handle
647  * via mfxFrameAllocator interface.
648  *
649  * Returns: a #GstQsvFrame object
650  */
651 GstQsvFrame *
652 gst_qsv_allocator_acquire_frame (GstQsvAllocator * allocator,
653     GstQsvMemoryType mem_type, const GstVideoInfo * info, GstBuffer * buffer,
654     GstBufferPool * pool)
655 {
656   GstQsvAllocatorPrivate *priv;
657   GstQsvFrame *frame;
658   guint32 map_flags = 0;
659
660   g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
661
662   if (GST_QSV_MEM_TYPE_IS_SYSTEM (mem_type) &&
663       GST_QSV_MEM_TYPE_IS_VIDEO (mem_type)) {
664     GST_ERROR_OBJECT (allocator, "Invalid memory type");
665     return nullptr;
666   }
667
668   if (GST_QSV_MEM_TYPE_IS_VIDEO (mem_type)) {
669     map_flags = GST_MAP_QSV;
670
671     if ((mem_type & GST_QSV_ENCODER_IN_MEMORY) != 0) {
672       map_flags |= GST_MAP_READ;
673     } else if ((mem_type & GST_QSV_DECODER_OUT_MEMORY) != 0) {
674       map_flags |= GST_MAP_WRITE;
675     } else {
676       GST_ERROR_OBJECT (allocator,
677           "Unknown read/write access for video memory");
678       return nullptr;
679     }
680   } else if ((mem_type & GST_QSV_ENCODER_IN_MEMORY) != 0) {
681     map_flags = GST_MAP_READ;
682   } else {
683     map_flags = GST_MAP_READWRITE;
684   }
685
686   priv = allocator->priv;
687   frame = (GstQsvFrame *) gst_atomic_queue_pop (priv->queue);
688
689   if (!frame)
690     frame = gst_qsv_frame_new ();
691
692   frame->mem_type = mem_type;
693   frame->map_flags = (GstMapFlags) map_flags;
694   frame->info = *info;
695
696   if (!pool) {
697     frame->buffer = buffer;
698   } else if (buffer) {
699     GstBuffer *upload_buf;
700
701     frame->allocator = (GstQsvAllocator *) gst_object_ref (allocator);
702     GST_MINI_OBJECT_CAST (frame)->dispose =
703         (GstMiniObjectDisposeFunction) gst_qsv_frame_dispose;
704
705     if (GST_QSV_MEM_TYPE_IS_SYSTEM (mem_type)) {
706       upload_buf = gst_qsv_allocator_upload_default (allocator, info, buffer,
707           pool);
708     } else {
709       GstQsvAllocatorClass *klass;
710
711       klass = GST_QSV_ALLOCATOR_GET_CLASS (allocator);
712       g_assert (klass->upload);
713
714       upload_buf = klass->upload (allocator, info, buffer, pool);
715     }
716
717     gst_buffer_unref (buffer);
718
719     if (!upload_buf) {
720       GST_WARNING_OBJECT (allocator, "Failed to upload buffer");
721       gst_qsv_frame_unref (frame);
722
723       return nullptr;
724     }
725
726     frame->buffer = upload_buf;
727   }
728
729   return frame;
730 }
731
732 static GstBuffer *
733 gst_qsv_allocator_download_default (GstQsvAllocator * self,
734     const GstVideoInfo * info, gboolean force_copy, GstQsvFrame * frame,
735     GstBufferPool * pool)
736 {
737   GstBuffer *buffer = nullptr;
738   GstFlowReturn ret;
739   GstVideoFrame dst_frame;
740   mfxStatus status;
741   mfxFrameData dummy;
742   gboolean copy_ret;
743
744   GST_TRACE_OBJECT (self, "Download");
745
746   if (!force_copy)
747     return gst_buffer_ref (frame->buffer);
748
749   ret = gst_buffer_pool_acquire_buffer (pool, &buffer, nullptr);
750   if (ret != GST_FLOW_OK) {
751     GST_WARNING_OBJECT (self, "Failed to acquire buffer");
752     return nullptr;
753   }
754
755   /* Use gst_qsv_allocator_lock() instead of gst_video_frame_map() to avoid
756    * redundant map if it's already locked by driver, already locked by driver
757    * sounds unsafe situaltion though */
758   status = gst_qsv_allocator_lock ((mfxHDL) self, (mfxMemId) frame, &dummy);
759   if (status != MFX_ERR_NONE) {
760     gst_buffer_unref (buffer);
761     GST_ERROR_OBJECT (self, "Failed to lock frame");
762     return nullptr;
763   }
764
765   if (!gst_video_frame_map (&dst_frame, &frame->info, buffer, GST_MAP_WRITE)) {
766     gst_qsv_allocator_unlock ((mfxHDL) self, (mfxMemId) frame, &dummy);
767     gst_buffer_unref (buffer);
768     GST_ERROR_OBJECT (self, "Failed to map output buffer");
769     return nullptr;
770   }
771
772   copy_ret = gst_video_frame_copy (&dst_frame, &frame->frame);
773   gst_qsv_allocator_unlock ((mfxHDL) self, (mfxMemId) frame, &dummy);
774   gst_video_frame_unmap (&dst_frame);
775
776   if (!copy_ret) {
777     GST_ERROR_OBJECT (self, "Failed to copy frame");
778     gst_buffer_unref (buffer);
779     return nullptr;
780   }
781
782   return buffer;
783 }
784
785 GstBuffer *
786 gst_qsv_allocator_download_frame (GstQsvAllocator * allocator,
787     gboolean force_copy, GstQsvFrame * frame, GstBufferPool * pool)
788 {
789   GstQsvAllocatorClass *klass;
790
791   g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
792   g_return_val_if_fail (GST_IS_QSV_FRAME (frame), nullptr);
793   g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), nullptr);
794
795   if (GST_QSV_MEM_TYPE_IS_SYSTEM (frame->mem_type)) {
796     return gst_qsv_allocator_download_default (allocator, &frame->info,
797         force_copy, frame, pool);
798   }
799
800   klass = GST_QSV_ALLOCATOR_GET_CLASS (allocator);
801   g_assert (klass->download);
802
803   return klass->download (allocator, &frame->info, force_copy, frame, pool);
804 }
805
806 mfxFrameAllocator *
807 gst_qsv_allocator_get_allocator_handle (GstQsvAllocator * allocator)
808 {
809   g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), nullptr);
810
811   return &allocator->priv->allocator;
812 }
813
814 gboolean
815 gst_qsv_allocator_get_cached_response (GstQsvAllocator * allocator,
816     mfxFrameAllocResponse * response)
817 {
818   g_return_val_if_fail (GST_IS_QSV_ALLOCATOR (allocator), FALSE);
819
820   return gst_qsv_allocator_copy_cached_response (allocator,
821       response, &allocator->priv->response);
822 }
823
824 void
825 gst_qsv_allocator_set_options (GstQsvAllocator * allocator,
826     guint16 extra_alloc_size, gboolean dummy_alloc)
827 {
828   g_return_if_fail (GST_IS_QSV_ALLOCATOR (allocator));
829
830   allocator->priv->extra_alloc_size = extra_alloc_size;
831   allocator->priv->dummy_alloc = dummy_alloc;
832 }