1a395fcb3a527f295b9ed0cf43a5fd351aef3e14
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / qsv / gstqsvencoder.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 "gstqsvencoder.h"
25 #include <mfxvideo++.h>
26 #include <string.h>
27 #include <string>
28
29 #ifdef G_OS_WIN32
30 #include <gst/d3d11/gstd3d11.h>
31 #include "gstqsvallocator_d3d11.h"
32
33 #include <wrl.h>
34
35 /* *INDENT-OFF* */
36 using namespace Microsoft::WRL;
37 /* *INDENT-ON* */
38 #else
39 #include <gst/va/gstvadisplay_drm.h>
40 #include "gstqsvallocator_va.h"
41 #endif /* G_OS_WIN32 */
42
43 GST_DEBUG_CATEGORY_EXTERN (gst_qsv_encoder_debug);
44 #define GST_CAT_DEFAULT gst_qsv_encoder_debug
45
46 GType
47 gst_qsv_coding_option_get_type (void)
48 {
49   static GType coding_opt_type = 0;
50   static const GEnumValue coding_opts[] = {
51     {MFX_CODINGOPTION_UNKNOWN, "Unknown", "unknown"},
52     {MFX_CODINGOPTION_ON, "On", "on"},
53     {MFX_CODINGOPTION_OFF, "Off", "off"},
54     {0, nullptr, nullptr}
55   };
56
57   if (g_once_init_enter (&coding_opt_type)) {
58     GType type = g_enum_register_static ("GstQsvCodingOption",
59         coding_opts);
60     g_once_init_leave (&coding_opt_type, type);
61   }
62
63   return coding_opt_type;
64 }
65
66 enum
67 {
68   PROP_0,
69   PROP_TARGET_USAGE,
70   PROP_LOW_LATENCY,
71 };
72
73 #define DEFAULT_TARGET_USAGE MFX_TARGETUSAGE_BALANCED
74 #define DEFAULT_LOW_LATENCY  FALSE
75
76 typedef struct _GstQsvEncoderSurface
77 {
78   mfxFrameSurface1 surface;
79   mfxEncodeCtrl encode_control;
80
81   /* array of mfxPayload (e.g., SEI data) associated with this surface */
82   GPtrArray *payload;
83
84   /* holds ownership */
85   GstQsvFrame *qsv_frame;
86 } GstQsvEncoderSurface;
87
88 typedef struct _GstQsvEncoderTask
89 {
90   mfxSyncPoint sync_point;
91   mfxBitstream bitstream;
92 } GstQsvEncoderTask;
93
94 struct _GstQsvEncoderPrivate
95 {
96   GstObject *device;
97
98   GstVideoCodecState *input_state;
99   GstQsvAllocator *allocator;
100
101   /* API specific alignment requirement (multiple of 16 or 32) */
102   GstVideoInfo aligned_info;
103
104   mfxSession session;
105   mfxVideoParam video_param;
106
107   /* List of mfxExtBuffer configured by subclass, subclass will hold
108    * allocated memory for each mfxExtBuffer */
109   GPtrArray *extra_params;
110
111   MFXVideoENCODE *encoder;
112   GstQsvMemoryType mem_type;
113
114   /* Internal buffer pool used to allocate fallback buffer when input buffer
115    * is not compatible with expected format/type/resolution etc */
116   GstBufferPool *internal_pool;
117
118   /* Array of GstQsvEncoderSurface, holding ownership */
119   GArray *surface_pool;
120   guint next_surface_index;
121
122   /* Array of GstQsvEncoderTask, holding ownership */
123   GArray *task_pool;
124
125   GQueue free_tasks;
126   GQueue pending_tasks;
127
128   /* Properties */
129   guint target_usage;
130   gboolean low_latency;
131 };
132
133 #define gst_qsv_encoder_parent_class parent_class
134 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstQsvEncoder, gst_qsv_encoder,
135     GST_TYPE_VIDEO_ENCODER);
136
137 static void gst_qsv_encoder_dispose (GObject * object);
138 static void gst_qsv_encoder_finalize (GObject * object);
139 static void gst_qsv_encoder_set_property (GObject * object, guint prop_id,
140     const GValue * value, GParamSpec * pspec);
141 static void gst_qsv_encoder_get_property (GObject * object, guint prop_id,
142     GValue * value, GParamSpec * pspec);
143
144 static void gst_qsv_encoder_set_context (GstElement * element,
145     GstContext * context);
146
147 static gboolean gst_qsv_encoder_open (GstVideoEncoder * encoder);
148 static gboolean gst_qsv_encoder_stop (GstVideoEncoder * encoder);
149 static gboolean gst_qsv_encoder_close (GstVideoEncoder * encoder);
150 static gboolean gst_qsv_encoder_set_format (GstVideoEncoder * encoder,
151     GstVideoCodecState * state);
152 static GstFlowReturn gst_qsv_encoder_handle_frame (GstVideoEncoder * encoder,
153     GstVideoCodecFrame * frame);
154 static GstFlowReturn gst_qsv_encoder_finish (GstVideoEncoder * encoder);
155 static gboolean gst_qsv_encoder_flush (GstVideoEncoder * encoder);
156 static gboolean gst_qsv_encoder_sink_query (GstVideoEncoder * encoder,
157     GstQuery * query);
158 static gboolean gst_qsv_encoder_src_query (GstVideoEncoder * encoder,
159     GstQuery * query);
160 static gboolean gst_qsv_encoder_propose_allocation (GstVideoEncoder * encoder,
161     GstQuery * query);
162
163 static void gst_qsv_encoder_surface_clear (GstQsvEncoderSurface * task);
164 static void gst_qsv_encoder_task_clear (GstQsvEncoderTask * task);
165
166 static void
167 gst_qsv_encoder_class_init (GstQsvEncoderClass * klass)
168 {
169   GObjectClass *object_class = G_OBJECT_CLASS (klass);
170   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
171   GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
172
173   object_class->dispose = gst_qsv_encoder_dispose;
174   object_class->finalize = gst_qsv_encoder_finalize;
175   object_class->set_property = gst_qsv_encoder_set_property;
176   object_class->get_property = gst_qsv_encoder_get_property;
177
178   g_object_class_install_property (object_class, PROP_TARGET_USAGE,
179       g_param_spec_uint ("target-usage", "Target Usage",
180           "1: Best quality, 4: Balanced, 7: Best speed",
181           1, 7, DEFAULT_TARGET_USAGE,
182           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
183
184   g_object_class_install_property (object_class, PROP_LOW_LATENCY,
185       g_param_spec_boolean ("low-latency", "Low Latency",
186           "Enables low-latency encoding", DEFAULT_LOW_LATENCY,
187           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
188
189   element_class->set_context = GST_DEBUG_FUNCPTR (gst_qsv_encoder_set_context);
190
191   videoenc_class->open = GST_DEBUG_FUNCPTR (gst_qsv_encoder_open);
192   videoenc_class->stop = GST_DEBUG_FUNCPTR (gst_qsv_encoder_stop);
193   videoenc_class->close = GST_DEBUG_FUNCPTR (gst_qsv_encoder_close);
194   videoenc_class->set_format = GST_DEBUG_FUNCPTR (gst_qsv_encoder_set_format);
195   videoenc_class->handle_frame =
196       GST_DEBUG_FUNCPTR (gst_qsv_encoder_handle_frame);
197   videoenc_class->finish = GST_DEBUG_FUNCPTR (gst_qsv_encoder_finish);
198   videoenc_class->flush = GST_DEBUG_FUNCPTR (gst_qsv_encoder_flush);
199   videoenc_class->sink_query = GST_DEBUG_FUNCPTR (gst_qsv_encoder_sink_query);
200   videoenc_class->src_query = GST_DEBUG_FUNCPTR (gst_qsv_encoder_src_query);
201   videoenc_class->propose_allocation =
202       GST_DEBUG_FUNCPTR (gst_qsv_encoder_propose_allocation);
203 }
204
205 static void
206 gst_qsv_encoder_init (GstQsvEncoder * self)
207 {
208   GstQsvEncoderPrivate *priv;
209
210   priv = self->priv =
211       (GstQsvEncoderPrivate *) gst_qsv_encoder_get_instance_private (self);
212
213   priv->extra_params = g_ptr_array_sized_new (8);
214
215   priv->surface_pool = g_array_new (FALSE, TRUE, sizeof (GstQsvEncoderSurface));
216   g_array_set_clear_func (priv->surface_pool,
217       (GDestroyNotify) gst_qsv_encoder_surface_clear);
218
219   priv->task_pool = g_array_new (FALSE, TRUE, sizeof (GstQsvEncoderTask));
220   g_array_set_clear_func (priv->task_pool,
221       (GDestroyNotify) gst_qsv_encoder_task_clear);
222
223   g_queue_init (&priv->free_tasks);
224   g_queue_init (&priv->pending_tasks);
225
226   priv->target_usage = DEFAULT_TARGET_USAGE;
227   priv->low_latency = DEFAULT_LOW_LATENCY;
228 }
229
230 static void
231 gst_qsv_encoder_dispose (GObject * object)
232 {
233   GstQsvEncoder *self = GST_QSV_ENCODER (object);
234   GstQsvEncoderPrivate *priv = self->priv;
235
236   gst_clear_object (&priv->device);
237
238   G_OBJECT_CLASS (parent_class)->dispose (object);
239 }
240
241 static void
242 gst_qsv_encoder_finalize (GObject * object)
243 {
244   GstQsvEncoder *self = GST_QSV_ENCODER (object);
245   GstQsvEncoderPrivate *priv = self->priv;
246
247   g_ptr_array_unref (priv->extra_params);
248   g_array_unref (priv->task_pool);
249   g_array_unref (priv->surface_pool);
250
251   G_OBJECT_CLASS (parent_class)->finalize (object);
252 }
253
254 static void
255 gst_qsv_encoder_set_property (GObject * object, guint prop_id,
256     const GValue * value, GParamSpec * pspec)
257 {
258   GstQsvEncoder *self = GST_QSV_ENCODER (object);
259   GstQsvEncoderPrivate *priv = self->priv;
260
261   switch (prop_id) {
262     case PROP_TARGET_USAGE:
263       priv->target_usage = g_value_get_uint (value);
264       break;
265     case PROP_LOW_LATENCY:
266       priv->low_latency = g_value_get_boolean (value);
267       break;
268     default:
269       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
270       break;
271   }
272 }
273
274 static void
275 gst_qsv_encoder_get_property (GObject * object, guint prop_id, GValue * value,
276     GParamSpec * pspec)
277 {
278   GstQsvEncoder *self = GST_QSV_ENCODER (object);
279   GstQsvEncoderPrivate *priv = self->priv;
280
281   switch (prop_id) {
282     case PROP_TARGET_USAGE:
283       g_value_set_uint (value, priv->target_usage);
284       break;
285     case PROP_LOW_LATENCY:
286       g_value_set_boolean (value, priv->low_latency);
287       break;
288     default:
289       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
290       break;
291   }
292 }
293
294 static void
295 gst_qsv_encoder_set_context (GstElement * element, GstContext * context)
296 {
297 #ifdef G_OS_WIN32
298   GstQsvEncoder *self = GST_QSV_ENCODER (element);
299   GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (element);
300   GstQsvEncoderPrivate *priv = self->priv;
301
302   gst_d3d11_handle_set_context_for_adapter_luid (element,
303       context, klass->adapter_luid, (GstD3D11Device **) & priv->device);
304 #endif
305
306   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
307 }
308
309 #ifdef G_OS_WIN32
310 static gboolean
311 gst_qsv_encoder_open_platform_device (GstQsvEncoder * self)
312 {
313   GstQsvEncoderPrivate *priv = self->priv;
314   GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
315   ComPtr < ID3D10Multithread > multi_thread;
316   HRESULT hr;
317   ID3D11Device *device_handle;
318   mfxStatus status;
319   GstD3D11Device *device;
320
321   if (!gst_d3d11_ensure_element_data_for_adapter_luid (GST_ELEMENT (self),
322           klass->adapter_luid, (GstD3D11Device **) & priv->device)) {
323     GST_ERROR_OBJECT (self, "d3d11 device is unavailable");
324     return FALSE;
325   }
326
327   device = GST_D3D11_DEVICE_CAST (priv->device);
328   priv->allocator = gst_qsv_d3d11_allocator_new (device);
329
330   /* For D3D11 device handle to be used by QSV, multithread protection layer
331    * must be enabled before the MFXVideoCORE_SetHandle() call.
332    *
333    * TODO: Need to check performance impact by this mutithread protection layer,
334    * since it may have a negative impact on overall pipeline performance.
335    * If so, we should create encoding session dedicated d3d11 device and
336    * make use of shared resource */
337   device_handle = gst_d3d11_device_get_device_handle (device);
338   hr = device_handle->QueryInterface (IID_PPV_ARGS (&multi_thread));
339   if (!gst_d3d11_result (hr, device)) {
340     GST_ERROR_OBJECT (self, "ID3D10Multithread interface is unavailable");
341     return FALSE;
342   }
343
344   multi_thread->SetMultithreadProtected (TRUE);
345   status = MFXVideoCORE_SetHandle (priv->session, MFX_HANDLE_D3D11_DEVICE,
346       device_handle);
347   if (status != MFX_ERR_NONE) {
348     GST_ERROR_OBJECT (self, "Failed to set d3d11 device handle");
349     return FALSE;
350   }
351
352   /* NOTE: We never use this mfxFrameAllocator to allocate memory from our side,
353    * but required for QSV because:
354    * 1) QSV may request memory allocation for encoder's internal usage,
355    *   MFX_FOURCC_P8 for example
356    * 2) Our mfxFrameAllocator provides bridge layer for
357    *   gst_video_frame_{map,unmap} and mfxFrameAllocator::{Lock,Unlock},
358    *   including mfxFrameAllocator::GetHDL.
359    * 3) GstQsvAllocator provides GstQsvFrame pool, and therefore allocated
360    *   GstQsvFrame struct can be re-used without per-frame malloc/free
361    */
362   status = MFXVideoCORE_SetFrameAllocator (priv->session,
363       gst_qsv_allocator_get_allocator_handle (priv->allocator));
364   if (status != MFX_ERR_NONE) {
365     GST_ERROR_OBJECT (self, "Failed to set frame allocator %d", status);
366     return FALSE;
367   }
368
369   return TRUE;
370 }
371 #else
372 static gboolean
373 gst_qsv_encoder_open_platform_device (GstQsvEncoder * self)
374 {
375   GstQsvEncoderPrivate *priv = self->priv;
376   GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
377   mfxStatus status;
378   GstVaDisplay *display;
379
380   /* GstVADisplay context sharing is not public yet (VA plugin internal) */
381   if (!priv->device) {
382     display = gst_va_display_drm_new_from_path (klass->display_path);
383     if (!display) {
384       GST_ERROR_OBJECT (self, "VA display is unavailable");
385       return FALSE;
386     }
387
388     priv->device = GST_OBJECT (display);
389   } else {
390     display = GST_VA_DISPLAY (priv->device);
391   }
392
393   priv->allocator = gst_qsv_va_allocator_new (display);
394
395   status = MFXVideoCORE_SetHandle (priv->session, MFX_HANDLE_VA_DISPLAY,
396       gst_va_display_get_va_dpy (display));
397   if (status != MFX_ERR_NONE) {
398     GST_ERROR_OBJECT (self, "Failed to set VA display handle");
399     return FALSE;
400   }
401
402   status = MFXVideoCORE_SetFrameAllocator (priv->session,
403       gst_qsv_allocator_get_allocator_handle (priv->allocator));
404   if (status != MFX_ERR_NONE) {
405     GST_ERROR_OBJECT (self, "Failed to set frame allocator %d", status);
406     return FALSE;
407   }
408
409   return TRUE;
410 }
411 #endif
412
413 static gboolean
414 gst_qsv_encoder_open (GstVideoEncoder * encoder)
415 {
416   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
417   GstQsvEncoderPrivate *priv = self->priv;
418   GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
419   mfxStatus status;
420
421   status = MFXCreateSession (gst_qsv_get_loader (), klass->impl_index,
422       &priv->session);
423   if (status != MFX_ERR_NONE) {
424     GST_ERROR_OBJECT (self, "Failed to create session");
425     return FALSE;
426   }
427
428   if (!gst_qsv_encoder_open_platform_device (self)) {
429     g_clear_pointer (&priv->session, MFXClose);
430     gst_clear_object (&priv->allocator);
431     gst_clear_object (&priv->device);
432
433     return FALSE;
434   }
435
436   return TRUE;
437 }
438
439 static gboolean
440 gst_qsv_encoder_reset (GstQsvEncoder * self)
441 {
442   GstQsvEncoderPrivate *priv = self->priv;
443
444   if (priv->encoder) {
445     delete priv->encoder;
446     priv->encoder = nullptr;
447   }
448
449   if (priv->internal_pool) {
450     gst_buffer_pool_set_active (priv->internal_pool, FALSE);
451     gst_clear_object (&priv->internal_pool);
452   }
453
454   g_array_set_size (priv->surface_pool, 0);
455   g_array_set_size (priv->task_pool, 0);
456   g_queue_clear (&priv->free_tasks);
457   g_queue_clear (&priv->pending_tasks);
458   g_clear_pointer (&priv->input_state, gst_video_codec_state_unref);
459
460   return TRUE;
461 }
462
463 static gboolean
464 gst_qsv_encoder_stop (GstVideoEncoder * encoder)
465 {
466   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
467
468   return gst_qsv_encoder_reset (self);
469 }
470
471 static gboolean
472 gst_qsv_encoder_close (GstVideoEncoder * encoder)
473 {
474   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
475   GstQsvEncoderPrivate *priv = self->priv;
476
477   g_clear_pointer (&priv->session, MFXClose);
478   gst_clear_object (&priv->allocator);
479   gst_clear_object (&priv->device);
480
481   return TRUE;
482 }
483
484 static void
485 gst_qsv_encoder_payload_clear (mfxPayload * payload)
486 {
487   if (!payload)
488     return;
489
490   g_free (payload->Data);
491   g_free (payload);
492 }
493
494 static void
495 gst_qsv_encoder_surface_reset (GstQsvEncoderSurface * surface)
496 {
497   if (!surface)
498     return;
499
500   gst_clear_qsv_frame (&surface->qsv_frame);
501   g_ptr_array_set_size (surface->payload, 0);
502   memset (&surface->encode_control, 0, sizeof (mfxEncodeCtrl));
503 }
504
505 static void
506 gst_qsv_encoder_surface_clear (GstQsvEncoderSurface * surface)
507 {
508   if (!surface)
509     return;
510
511   gst_qsv_encoder_surface_reset (surface);
512   g_clear_pointer (&surface->payload, g_ptr_array_unref);
513   memset (&surface->surface, 0, sizeof (mfxFrameSurface1));
514 }
515
516 static void
517 gst_qsv_encoder_task_reset (GstQsvEncoder * self, GstQsvEncoderTask * task)
518 {
519   GstQsvEncoderPrivate *priv = self->priv;
520
521   if (!task)
522     return;
523
524   task->sync_point = nullptr;
525   task->bitstream.DataLength = 0;
526   g_queue_push_head (&priv->free_tasks, task);
527 }
528
529 static void
530 gst_qsv_encoder_task_clear (GstQsvEncoderTask * task)
531 {
532   if (!task)
533     return;
534
535   g_clear_pointer (&task->bitstream.Data, g_free);
536   memset (&task->bitstream, 0, sizeof (mfxBitstream));
537 }
538
539 static GstQsvEncoderSurface *
540 gst_qsv_encoder_get_next_surface (GstQsvEncoder * self)
541 {
542   GstQsvEncoderPrivate *priv = self->priv;
543   GstQsvEncoderSurface *surface = nullptr;
544
545   for (guint i = priv->next_surface_index; i < priv->surface_pool->len; i++) {
546     GstQsvEncoderSurface *iter =
547         &g_array_index (priv->surface_pool, GstQsvEncoderSurface, i);
548
549     /* This means surface is still being used by QSV */
550     if (iter->surface.Data.Locked > 0)
551       continue;
552
553     surface = iter;
554     priv->next_surface_index = i;
555     goto out;
556   }
557
558   for (guint i = 0; i < priv->next_surface_index; i++) {
559     GstQsvEncoderSurface *iter =
560         &g_array_index (priv->surface_pool, GstQsvEncoderSurface, i);
561
562     /* This means surface is still being used by QSV */
563     if (iter->surface.Data.Locked > 0)
564       continue;
565
566     surface = iter;
567     priv->next_surface_index = i;
568     goto out;
569   }
570
571   /* Magic number to avoid too large pool size */
572   if (priv->surface_pool->len > 64) {
573     GST_ERROR_OBJECT (self,
574         "No availble surface but pool size is too large already");
575     return nullptr;
576   }
577
578   /* Something went wrong, increase surface pool size */
579   GST_INFO_OBJECT (self, "No useable surfaces, increasing pool size to %d",
580       priv->surface_pool->len + 1);
581
582   g_array_set_size (priv->surface_pool, priv->surface_pool->len + 1);
583   surface = &g_array_index (priv->surface_pool, GstQsvEncoderSurface,
584       priv->surface_pool->len - 1);
585
586   memset (surface, 0, sizeof (GstQsvEncoderSurface));
587   surface->surface.Info =
588       g_array_index (priv->surface_pool, GstQsvEncoderSurface, 0).surface.Info;
589   surface->payload = g_ptr_array_new_with_free_func ((GDestroyNotify)
590       gst_qsv_encoder_payload_clear);
591
592 out:
593   priv->next_surface_index++;
594   priv->next_surface_index %= priv->surface_pool->len;
595
596   gst_qsv_encoder_surface_reset (surface);
597   return surface;
598 }
599
600 static mfxStatus
601 gst_qsv_encoder_encode_frame (GstQsvEncoder * self,
602     GstQsvEncoderSurface * surface, GstQsvEncoderTask * task, mfxU64 timestamp)
603 {
604   mfxFrameSurface1 *s;
605   GstQsvEncoderPrivate *priv = self->priv;
606   mfxStatus status;
607   guint retry_count = 0;
608   /* magic number */
609   const guint retry_threshold = 100;
610   mfxEncodeCtrl *encode_ctrl;
611
612   if (surface) {
613     s = &surface->surface;
614     s->Data.MemId = (mfxMemId) surface->qsv_frame;
615     s->Data.TimeStamp = timestamp;
616     encode_ctrl = &surface->encode_control;
617   } else {
618     /* draining */
619     s = nullptr;
620     encode_ctrl = nullptr;
621   }
622
623   do {
624     status = priv->encoder->EncodeFrameAsync (encode_ctrl,
625         s, &task->bitstream, &task->sync_point);
626
627     /* XXX: probably we should try to drain pending tasks if any in this case
628      * as documented? */
629     if (status == MFX_WRN_DEVICE_BUSY && retry_count < retry_threshold) {
630       GST_INFO_OBJECT (self, "GPU is busy, retry count (%d/%d)",
631           retry_count, retry_threshold);
632       retry_count++;
633
634       /* Magic number 10ms */
635       g_usleep (10000);
636       continue;
637     }
638
639     break;
640   } while (TRUE);
641
642   return status;
643 }
644
645 static GstVideoCodecFrame *
646 gst_qsv_encoder_find_output_frame (GstQsvEncoder * self, GstClockTime pts)
647 {
648   GList *frames, *iter;
649   GstVideoCodecFrame *ret = nullptr;
650   GstVideoCodecFrame *closest = nullptr;
651   guint64 min_pts_abs_diff = 0;
652
653   /* give up, just returns the oldest frame */
654   if (!GST_CLOCK_TIME_IS_VALID (pts))
655     return gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (self));
656
657   frames = gst_video_encoder_get_frames (GST_VIDEO_ENCODER (self));
658
659   for (iter = frames; iter; iter = g_list_next (iter)) {
660     GstVideoCodecFrame *frame = (GstVideoCodecFrame *) iter->data;
661     guint64 abs_diff;
662
663     if (!GST_CLOCK_TIME_IS_VALID (frame->pts))
664       continue;
665
666     if (pts == frame->pts) {
667       ret = frame;
668       break;
669     }
670
671     if (pts >= frame->pts)
672       abs_diff = pts - frame->pts;
673     else
674       abs_diff = frame->pts - pts;
675
676     if (!closest || abs_diff < min_pts_abs_diff) {
677       closest = frame;
678       min_pts_abs_diff = abs_diff;
679     }
680   }
681
682   if (!ret && closest)
683     ret = closest;
684
685   if (ret) {
686     gst_video_codec_frame_ref (ret);
687   } else {
688     ret = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (self));
689   }
690
691   if (frames)
692     g_list_free_full (frames, (GDestroyNotify) gst_video_codec_frame_unref);
693
694   return ret;
695 }
696
697 static GstFlowReturn
698 gst_qsv_encoder_finish_frame (GstQsvEncoder * self, GstQsvEncoderTask * task,
699     gboolean discard)
700 {
701   GstQsvEncoderPrivate *priv = self->priv;
702   GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
703   mfxStatus status;
704   mfxBitstream *bs;
705   GstVideoCodecFrame *frame;
706   GstClockTime pts = GST_CLOCK_TIME_NONE;
707   GstClockTime dts = GST_CLOCK_TIME_NONE;
708   GstBuffer *buffer;
709   gboolean keyframe = FALSE;
710   guint retry_count = 0;
711   /* magic number */
712   const guint retry_threshold = 100;
713
714   status = MFX_ERR_NONE;
715   do {
716     /* magic number 100 ms */
717     status = MFXVideoCORE_SyncOperation (priv->session, task->sync_point, 100);
718
719     /* Retry up to 10 sec (100 ms x 100 times), that should be enough time for
720      * encoding a frame using hardware */
721     if (status == MFX_WRN_IN_EXECUTION && retry_count < retry_threshold) {
722       GST_DEBUG_OBJECT (self,
723           "Operation is still in execution, retry count (%d/%d)",
724           retry_count, retry_threshold);
725       retry_count++;
726       continue;
727     }
728
729     break;
730   } while (TRUE);
731
732   if (discard) {
733     gst_qsv_encoder_task_reset (self, task);
734     return GST_FLOW_OK;
735   }
736
737   if (status != MFX_ERR_NONE && status != MFX_ERR_NONE_PARTIAL_OUTPUT) {
738     gst_qsv_encoder_task_reset (self, task);
739
740     if (status == MFX_ERR_ABORTED) {
741       GST_INFO_OBJECT (self, "Operation was aborted");
742       return GST_FLOW_FLUSHING;
743     }
744
745     GST_WARNING_OBJECT (self, "SyncOperation returned %d (%s)",
746         QSV_STATUS_ARGS (status));
747
748     return GST_FLOW_ERROR;
749   }
750
751   bs = &task->bitstream;
752   pts = gst_qsv_timestamp_to_gst (bs->TimeStamp);
753   dts = gst_qsv_timestamp_to_gst ((mfxU64) bs->DecodeTimeStamp);
754
755   if ((bs->FrameType & MFX_FRAMETYPE_IDR) != 0)
756     keyframe = TRUE;
757
758   if (klass->create_output_buffer) {
759     buffer = klass->create_output_buffer (self, bs);
760   } else {
761     buffer = gst_buffer_new_memdup (bs->Data + bs->DataOffset, bs->DataLength);
762   }
763   gst_qsv_encoder_task_reset (self, task);
764
765   if (!buffer) {
766     GST_ERROR_OBJECT (self, "No output buffer");
767     return GST_FLOW_ERROR;
768   }
769
770   frame = gst_qsv_encoder_find_output_frame (self, pts);
771   if (frame) {
772     frame->pts = pts;
773     frame->dts = dts;
774     frame->output_buffer = buffer;
775
776     if (keyframe)
777       GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
778
779     return gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (self), frame);
780   }
781
782   /* Empty available frame, something went wrong but we can just push this
783    * buffer */
784   GST_WARNING_OBJECT (self, "Failed to find corresponding frame");
785   GST_BUFFER_PTS (buffer) = pts;
786   GST_BUFFER_DTS (buffer) = dts;
787
788   if (!keyframe)
789     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
790
791   return gst_pad_push (GST_VIDEO_ENCODER_SRC_PAD (self), buffer);
792 }
793
794 static GstFlowReturn
795 gst_qsv_encoder_drain (GstQsvEncoder * self, gboolean discard)
796 {
797   GstQsvEncoderPrivate *priv = self->priv;
798   mfxStatus status = MFX_ERR_NONE;
799   GstFlowReturn ret = GST_FLOW_OK;
800   GstQsvEncoderTask *task;
801
802   if (!priv->session || !priv->encoder)
803     return GST_FLOW_OK;
804
805   GST_DEBUG_OBJECT (self, "Drain");
806
807   /* Drain pending tasks first if any */
808   while (g_queue_get_length (&priv->pending_tasks) > 0) {
809     task = (GstQsvEncoderTask *) g_queue_pop_tail (&priv->pending_tasks);
810     ret = gst_qsv_encoder_finish_frame (self, task, discard);
811   }
812
813   while (status == MFX_ERR_NONE) {
814     task = (GstQsvEncoderTask *) g_queue_pop_tail (&priv->free_tasks);
815     status = gst_qsv_encoder_encode_frame (self,
816         nullptr, task, MFX_TIMESTAMP_UNKNOWN);
817
818     /* once it's fully drained, then driver will return more data */
819     if (status == MFX_ERR_NONE && task->sync_point) {
820       ret = gst_qsv_encoder_finish_frame (self, task, discard);
821       continue;
822     }
823
824     if (status != MFX_ERR_MORE_DATA)
825       GST_WARNING_OBJECT (self, "Unexpected status return %d (%s)",
826           QSV_STATUS_ARGS (status));
827
828     g_queue_push_head (&priv->free_tasks, task);
829   }
830
831   /* Release GstQsvFrame objects */
832   for (guint i = 0; i < priv->surface_pool->len; i++) {
833     GstQsvEncoderSurface *iter =
834         &g_array_index (priv->surface_pool, GstQsvEncoderSurface, i);
835
836     if (iter->surface.Data.Locked > 0) {
837       GST_WARNING_OBJECT (self,
838           "Encoder was drained but QSV is holding surface %d", i);
839       continue;
840     }
841
842     gst_qsv_encoder_surface_reset (iter);
843   }
844
845   return ret;
846 }
847
848 #ifdef G_OS_WIN32
849 static gboolean
850 gst_qsv_encoder_prepare_d3d11_pool (GstQsvEncoder * self,
851     GstCaps * caps, GstVideoInfo * aligned_info)
852 {
853   GstQsvEncoderPrivate *priv = self->priv;
854   GstStructure *config;
855   GstD3D11AllocationParams *params;
856   GstD3D11Device *device = GST_D3D11_DEVICE_CAST (priv->device);
857
858   GST_DEBUG_OBJECT (self, "Use d3d11 memory pool");
859
860   priv->internal_pool = gst_d3d11_buffer_pool_new (device);
861   config = gst_buffer_pool_get_config (priv->internal_pool);
862   params = gst_d3d11_allocation_params_new (device, aligned_info,
863       (GstD3D11AllocationFlags) 0, 0);
864
865   gst_buffer_pool_config_set_d3d11_allocation_params (config, params);
866   gst_d3d11_allocation_params_free (params);
867   gst_buffer_pool_config_set_params (config, caps,
868       GST_VIDEO_INFO_SIZE (aligned_info), 0, 0);
869   gst_buffer_pool_set_config (priv->internal_pool, config);
870   gst_buffer_pool_set_active (priv->internal_pool, TRUE);
871
872   return TRUE;
873 }
874 #endif
875
876 static gboolean
877 gst_qsv_encoder_prepare_system_pool (GstQsvEncoder * self,
878     GstCaps * caps, GstVideoInfo * aligned_info)
879 {
880   GstQsvEncoderPrivate *priv = self->priv;
881   GstStructure *config;
882
883   GST_DEBUG_OBJECT (self, "Use system memory pool");
884
885   priv->internal_pool = gst_video_buffer_pool_new ();
886   config = gst_buffer_pool_get_config (priv->internal_pool);
887   caps = gst_video_info_to_caps (aligned_info);
888   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
889   gst_buffer_pool_config_set_params (config,
890       caps, GST_VIDEO_INFO_SIZE (aligned_info), 0, 0);
891
892   gst_buffer_pool_set_config (priv->internal_pool, config);
893   gst_buffer_pool_set_active (priv->internal_pool, TRUE);
894
895   return TRUE;
896 }
897
898 /* Prepare internal pool, which is used to allocate fallback buffer
899  * when upstream buffer is not directly accessible by QSV */
900 static gboolean
901 gst_qsv_encoder_prepare_pool (GstQsvEncoder * self, GstCaps * caps,
902     GstVideoInfo * aligned_info, mfxU16 * io_pattern)
903 {
904   GstQsvEncoderPrivate *priv = self->priv;
905   gboolean ret = FALSE;
906   GstCaps *aligned_caps;
907
908   if (priv->internal_pool) {
909     gst_buffer_pool_set_active (priv->internal_pool, FALSE);
910     gst_clear_object (&priv->internal_pool);
911   }
912
913   aligned_caps = gst_video_info_to_caps (aligned_info);
914
915   /* TODO: Add Linux video memory (VA/DMABuf) support */
916 #ifdef G_OS_WIN32
917   priv->mem_type = GST_QSV_VIDEO_MEMORY;
918   *io_pattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
919
920   ret = gst_qsv_encoder_prepare_d3d11_pool (self, aligned_caps, aligned_info);
921 #endif
922
923   if (!ret) {
924     priv->mem_type = GST_QSV_SYSTEM_MEMORY;
925     *io_pattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
926
927     ret = gst_qsv_encoder_prepare_system_pool (self,
928         aligned_caps, aligned_info);
929   }
930   gst_caps_unref (aligned_caps);
931
932   return ret;
933 }
934
935 static gboolean
936 gst_qsv_encoder_set_format (GstVideoEncoder * encoder,
937     GstVideoCodecState * state)
938 {
939   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
940   GstQsvEncoderPrivate *priv = self->priv;
941   GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
942   GstVideoInfo *info;
943   mfxVideoParam param;
944   mfxFrameInfo *frame_info;
945   mfxFrameAllocRequest alloc_request;
946   mfxStatus status;
947   MFXVideoENCODE *encoder_handle = nullptr;
948   guint bitstream_size;
949   gboolean ret;
950   guint64 min_delay_frames, max_delay_frames;
951   GstClockTime min_latency, max_latency;
952
953   gst_qsv_encoder_drain (self, FALSE);
954   gst_qsv_encoder_reset (self);
955
956   priv->input_state = gst_video_codec_state_ref (state);
957
958   info = &priv->input_state->info;
959
960   encoder_handle = new MFXVideoENCODE (priv->session);
961
962   memset (&param, 0, sizeof (mfxVideoParam));
963
964   g_ptr_array_set_size (priv->extra_params, 0);
965   g_assert (klass->set_format);
966   if (!klass->set_format (self, priv->input_state, &param, priv->extra_params)) {
967     GST_ERROR_OBJECT (self, "Subclass failed to set format");
968     goto error;
969   }
970
971   /* LowPower mode supports smaller set of features, don't enable it for now */
972   param.mfx.LowPower = MFX_CODINGOPTION_OFF;
973   if (priv->low_latency)
974     param.AsyncDepth = 1;
975   else
976     param.AsyncDepth = 4;
977
978   param.mfx.TargetUsage = priv->target_usage;
979
980   frame_info = &param.mfx.FrameInfo;
981
982   gst_video_info_set_interlaced_format (&priv->aligned_info,
983       GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_INTERLACE_MODE (info),
984       frame_info->Width, frame_info->Height);
985
986   if (!gst_qsv_encoder_prepare_pool (self, state->caps, &priv->aligned_info,
987           &param.IOPattern)) {
988     GST_ERROR_OBJECT (self, "Failed to prepare pool");
989     goto error;
990   }
991
992 #define CHECK_STATUS(s,func) G_STMT_START { \
993   if (s < MFX_ERR_NONE) { \
994     GST_ERROR_OBJECT (self, G_STRINGIFY (func) " failed %d (%s)", \
995         QSV_STATUS_ARGS (s)); \
996     goto error; \
997   } else if (status != MFX_ERR_NONE) { \
998     GST_WARNING_OBJECT (self, G_STRINGIFY (func) " returned warning %d (%s)", \
999         QSV_STATUS_ARGS (s)); \
1000   } \
1001 } G_STMT_END
1002
1003   status = encoder_handle->Query (&param, &param);
1004   /* If device is unhappy with LowPower = OFF, try again with unknown */
1005   if (status < MFX_ERR_NONE) {
1006     GST_INFO_OBJECT (self, "LowPower - OFF returned %d (%s)",
1007         QSV_STATUS_ARGS (status));
1008     param.mfx.LowPower = MFX_CODINGOPTION_UNKNOWN;
1009   }
1010
1011   status = encoder_handle->Query (&param, &param);
1012   CHECK_STATUS (status, MFXVideoENCODE::Query);
1013
1014   status = encoder_handle->QueryIOSurf (&param, &alloc_request);
1015   CHECK_STATUS (status, MFXVideoENCODE::QueryIOSurf);
1016
1017   status = encoder_handle->Init (&param);
1018   CHECK_STATUS (status, MFXVideoENCODE::Init);
1019
1020   status = encoder_handle->GetVideoParam (&param);
1021   CHECK_STATUS (status, MFXVideoENCODE::GetVideoParam);
1022
1023 #undef CHECK_STATUS
1024
1025   GST_DEBUG_OBJECT (self, "NumFrameSuggested: %d, AsyncDepth %d",
1026       alloc_request.NumFrameSuggested, param.AsyncDepth);
1027
1028   g_assert (klass->set_output_state);
1029   ret = klass->set_output_state (self, priv->input_state, priv->session);
1030   if (!ret) {
1031     GST_ERROR_OBJECT (self, "Subclass failed to set output state");
1032     goto error;
1033   }
1034
1035   /* Prepare surface pool with size NumFrameSuggested, then if it's not
1036    * sufficient while encoding, we can increse the pool size dynamically
1037    * if needed */
1038   g_array_set_size (priv->surface_pool, alloc_request.NumFrameSuggested);
1039   for (guint i = 0; i < priv->surface_pool->len; i++) {
1040     GstQsvEncoderSurface *surface = &g_array_index (priv->surface_pool,
1041         GstQsvEncoderSurface, i);
1042
1043     surface->surface.Info = param.mfx.FrameInfo;
1044     surface->payload = g_ptr_array_new_with_free_func ((GDestroyNotify)
1045         gst_qsv_encoder_payload_clear);
1046   }
1047   priv->next_surface_index = 0;
1048
1049   g_array_set_size (priv->task_pool, param.AsyncDepth);
1050   bitstream_size =
1051       (guint) param.mfx.BufferSizeInKB * param.mfx.BRCParamMultiplier * 1024;
1052
1053   for (guint i = 0; i < priv->task_pool->len; i++) {
1054     GstQsvEncoderTask *task = &g_array_index (priv->task_pool,
1055         GstQsvEncoderTask, i);
1056
1057     task->bitstream.Data = (mfxU8 *) g_malloc (bitstream_size);
1058     task->bitstream.MaxLength = bitstream_size;
1059
1060     g_queue_push_head (&priv->free_tasks, task);
1061   }
1062
1063   min_delay_frames = priv->task_pool->len;
1064   max_delay_frames = priv->surface_pool->len + min_delay_frames;
1065
1066   min_latency = gst_util_uint64_scale (min_delay_frames * GST_SECOND,
1067       param.mfx.FrameInfo.FrameRateExtD, param.mfx.FrameInfo.FrameRateExtN);
1068   max_latency = gst_util_uint64_scale (max_delay_frames * GST_SECOND,
1069       param.mfx.FrameInfo.FrameRateExtD, param.mfx.FrameInfo.FrameRateExtN);
1070   gst_video_encoder_set_latency (encoder, min_latency, max_latency);
1071
1072   priv->video_param = param;
1073   priv->encoder = encoder_handle;
1074
1075   return TRUE;
1076
1077 error:
1078   if (encoder_handle)
1079     delete encoder_handle;
1080
1081   gst_qsv_encoder_reset (self);
1082
1083   return FALSE;
1084 }
1085
1086 static mfxU16
1087 gst_qsv_encoder_get_pic_struct (GstQsvEncoder * self,
1088     GstVideoCodecFrame * frame)
1089 {
1090   GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
1091   GstQsvEncoderPrivate *priv = self->priv;
1092   GstVideoInfo *info = &priv->input_state->info;
1093
1094   if (klass->codec_id != MFX_CODEC_AVC)
1095     return MFX_PICSTRUCT_PROGRESSIVE;
1096
1097   if (!GST_VIDEO_INFO_IS_INTERLACED (info))
1098     return MFX_PICSTRUCT_PROGRESSIVE;
1099
1100   if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED) {
1101     if (!GST_BUFFER_FLAG_IS_SET (frame->input_buffer,
1102             GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
1103       return MFX_PICSTRUCT_PROGRESSIVE;
1104     }
1105
1106     if (GST_BUFFER_FLAG_IS_SET (frame->input_buffer, GST_VIDEO_BUFFER_FLAG_TFF))
1107       return MFX_PICSTRUCT_FIELD_TFF;
1108
1109     return MFX_PICSTRUCT_FIELD_BFF;
1110   }
1111
1112   switch (GST_VIDEO_INFO_FIELD_ORDER (info)) {
1113     case GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST:
1114       return MFX_PICSTRUCT_FIELD_TFF;
1115       break;
1116     case GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST:
1117       return MFX_PICSTRUCT_FIELD_BFF;
1118       break;
1119     default:
1120       break;
1121   }
1122
1123   if (GST_BUFFER_FLAG_IS_SET (frame->input_buffer, GST_VIDEO_BUFFER_FLAG_TFF))
1124     return MFX_PICSTRUCT_FIELD_TFF;
1125
1126   return MFX_PICSTRUCT_FIELD_BFF;
1127 }
1128
1129 static GstFlowReturn
1130 gst_qsv_encoder_handle_frame (GstVideoEncoder * encoder,
1131     GstVideoCodecFrame * frame)
1132 {
1133   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1134   GstQsvEncoderPrivate *priv = self->priv;
1135   GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
1136   GstFlowReturn ret = GST_FLOW_ERROR;
1137   GstQsvEncoderSurface *surface;
1138   GstQsvEncoderTask *task;
1139   mfxU64 timestamp;
1140   mfxStatus status;
1141
1142   if (klass->check_reconfigure) {
1143     GstQsvEncoderReconfigure reconfigure;
1144
1145     reconfigure = klass->check_reconfigure (self, &priv->video_param);
1146     switch (reconfigure) {
1147       case GST_QSV_ENCODER_RECONFIGURE_BITRATE:
1148         /* TODO: In case of bitrate change, we can query whether we need to
1149          * start from a new sequence or soft-reset is possible
1150          * via MFXVideoENCODE_Query() with mfxExtEncoderResetOption struct,
1151          * and then if soft-reset is allowed, we can avoid inefficient full-reset
1152          * (including IDR insertion) by using MFXVideoENCODE_Reset() */
1153         /* fallthrough */
1154       case GST_QSV_ENCODER_RECONFIGURE_FULL:
1155       {
1156         GstVideoCodecState *state =
1157             gst_video_codec_state_ref (priv->input_state);
1158         gboolean rst;
1159
1160         GST_INFO_OBJECT (self, "Configure encoder again");
1161         rst = gst_qsv_encoder_set_format (encoder, state);
1162         gst_video_codec_state_unref (state);
1163
1164         if (!rst)
1165           return GST_FLOW_NOT_NEGOTIATED;
1166         break;
1167       }
1168       default:
1169         break;
1170     }
1171   }
1172
1173   if (!priv->encoder) {
1174     GST_ERROR_OBJECT (self, "Encoder object was not configured");
1175     return GST_FLOW_NOT_NEGOTIATED;
1176   }
1177
1178   surface = gst_qsv_encoder_get_next_surface (self);
1179   if (!surface) {
1180     GST_ERROR_OBJECT (self, "No available surface");
1181     goto out;
1182   }
1183
1184   task = (GstQsvEncoderTask *) g_queue_pop_tail (&priv->free_tasks);
1185   g_assert (task);
1186
1187   surface->qsv_frame =
1188       gst_qsv_allocator_acquire_frame (priv->allocator, priv->mem_type,
1189       &priv->input_state->info, frame->input_buffer, priv->internal_pool);
1190   if (!surface->qsv_frame) {
1191     GST_ERROR_OBJECT (self, "Failed to wrap buffer with qsv frame");
1192     gst_qsv_encoder_task_reset (self, task);
1193     goto out;
1194   }
1195
1196   surface->surface.Info.PicStruct =
1197       gst_qsv_encoder_get_pic_struct (self, frame);
1198
1199   if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
1200     surface->encode_control.FrameType =
1201         MFX_FRAMETYPE_IDR | MFX_FRAMETYPE_I | MFX_FRAMETYPE_REF;
1202   } else {
1203     surface->encode_control.FrameType = MFX_FRAMETYPE_UNKNOWN;
1204   }
1205
1206   if (klass->attach_payload) {
1207     klass->attach_payload (self, frame, surface->payload);
1208     if (surface->payload->len > 0) {
1209       surface->encode_control.NumPayload = surface->payload->len;
1210       surface->encode_control.Payload = (mfxPayload **) surface->payload->pdata;
1211     }
1212   }
1213
1214   timestamp = gst_qsv_timestamp_from_gst (frame->pts);
1215   status = gst_qsv_encoder_encode_frame (self, surface, task, timestamp);
1216   if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
1217     GST_ERROR_OBJECT (self, "Failed to encode frame, ret %d (%s)",
1218         QSV_STATUS_ARGS (status));
1219     gst_qsv_encoder_task_reset (self, task);
1220     goto out;
1221   }
1222
1223   if (status == MFX_ERR_NONE && task->sync_point) {
1224     g_queue_push_head (&priv->pending_tasks, task);
1225   } else {
1226     gst_qsv_encoder_task_reset (self, task);
1227   }
1228
1229   ret = GST_FLOW_OK;
1230   /* Do not sync immediately, but record tasks which have output buffer here
1231    * to improve throughput.
1232    * In this way, hardware may be able to run encoding job from its background
1233    * threads (if any). We will do sync only when there's no more free task item
1234    */
1235   while (g_queue_get_length (&priv->pending_tasks) >= priv->task_pool->len) {
1236     GstQsvEncoderTask *task =
1237         (GstQsvEncoderTask *) g_queue_pop_tail (&priv->pending_tasks);
1238     ret = gst_qsv_encoder_finish_frame (self, task, FALSE);
1239   }
1240
1241 out:
1242   gst_video_codec_frame_unref (frame);
1243
1244   return ret;
1245 }
1246
1247 static GstFlowReturn
1248 gst_qsv_encoder_finish (GstVideoEncoder * encoder)
1249 {
1250   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1251
1252   return gst_qsv_encoder_drain (self, FALSE);
1253 }
1254
1255 static gboolean
1256 gst_qsv_encoder_flush (GstVideoEncoder * encoder)
1257 {
1258   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1259
1260   gst_qsv_encoder_drain (self, TRUE);
1261
1262   return TRUE;
1263 }
1264
1265 static gboolean
1266 gst_qsv_encoder_handle_context_query (GstQsvEncoder * self, GstQuery * query)
1267 {
1268 #ifdef G_OS_WIN32
1269   GstQsvEncoderPrivate *priv = self->priv;
1270
1271   return gst_d3d11_handle_context_query (GST_ELEMENT (self), query,
1272       (GstD3D11Device *) priv->device);
1273 #endif
1274
1275   return FALSE;
1276 }
1277
1278 static gboolean
1279 gst_qsv_encoder_sink_query (GstVideoEncoder * encoder, GstQuery * query)
1280 {
1281   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1282
1283   switch (GST_QUERY_TYPE (query)) {
1284     case GST_QUERY_CONTEXT:
1285       if (gst_qsv_encoder_handle_context_query (self, query))
1286         return TRUE;
1287       break;
1288     default:
1289       break;
1290   }
1291
1292   return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (encoder, query);
1293 }
1294
1295 static gboolean
1296 gst_qsv_encoder_src_query (GstVideoEncoder * encoder, GstQuery * query)
1297 {
1298   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1299
1300   switch (GST_QUERY_TYPE (query)) {
1301     case GST_QUERY_CONTEXT:
1302       if (gst_qsv_encoder_handle_context_query (self, query))
1303         return TRUE;
1304       break;
1305     default:
1306       break;
1307   }
1308
1309   return GST_VIDEO_ENCODER_CLASS (parent_class)->src_query (encoder, query);
1310 }
1311
1312 #ifdef G_OS_WIN32
1313 static gboolean
1314 gst_qsv_encoder_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
1315 {
1316   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1317   GstQsvEncoderPrivate *priv = self->priv;
1318   GstD3D11Device *device = GST_D3D11_DEVICE (priv->device);
1319   GstVideoInfo info;
1320   GstBufferPool *pool;
1321   GstCaps *caps;
1322   guint size;
1323   GstStructure *config;
1324   GstCapsFeatures *features;
1325   gboolean is_d3d11 = FALSE;
1326
1327   gst_query_parse_allocation (query, &caps, nullptr);
1328   if (!caps) {
1329     GST_WARNING_OBJECT (self, "null caps in query");
1330     return FALSE;
1331   }
1332
1333   if (!gst_video_info_from_caps (&info, caps)) {
1334     GST_WARNING_OBJECT (self, "Failed to convert caps into info");
1335     return FALSE;
1336   }
1337
1338   features = gst_caps_get_features (caps, 0);
1339   if (features && gst_caps_features_contains (features,
1340           GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
1341     GST_DEBUG_OBJECT (self, "upstream support d3d11 memory");
1342     pool = gst_d3d11_buffer_pool_new (device);
1343     is_d3d11 = TRUE;
1344   } else {
1345     pool = gst_d3d11_staging_buffer_pool_new (device);
1346   }
1347
1348   config = gst_buffer_pool_get_config (pool);
1349   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1350
1351   if (is_d3d11) {
1352     GstD3D11AllocationParams *d3d11_params;
1353     GstVideoAlignment align;
1354
1355     /* d3d11 buffer pool doesn't support generic video alignment
1356      * because memory layout of CPU accessible staging texture is uncontrollable.
1357      * Do D3D11 specific handling */
1358     gst_video_alignment_reset (&align);
1359
1360     align.padding_right = GST_VIDEO_INFO_WIDTH (&priv->aligned_info) -
1361         GST_VIDEO_INFO_WIDTH (&info);
1362     align.padding_bottom = GST_VIDEO_INFO_HEIGHT (&priv->aligned_info) -
1363         GST_VIDEO_INFO_HEIGHT (&info);
1364
1365     d3d11_params = gst_d3d11_allocation_params_new (device, &info,
1366         (GstD3D11AllocationFlags) 0, 0);
1367
1368     gst_d3d11_allocation_params_alignment (d3d11_params, &align);
1369     gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
1370     gst_d3d11_allocation_params_free (d3d11_params);
1371   }
1372
1373   size = GST_VIDEO_INFO_SIZE (&info);
1374   gst_buffer_pool_config_set_params (config,
1375       caps, size, priv->surface_pool->len, 0);
1376
1377   if (!gst_buffer_pool_set_config (pool, config)) {
1378     GST_WARNING_OBJECT (self, "Failed to set pool config");
1379     gst_object_unref (pool);
1380     return FALSE;
1381   }
1382
1383   /* d3d11 buffer pool will update actual CPU accessible buffer size based on
1384    * allocated staging texture per gst_buffer_pool_set_config() call,
1385    * need query again to get the size */
1386   config = gst_buffer_pool_get_config (pool);
1387   gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr, nullptr);
1388   gst_structure_free (config);
1389
1390   gst_query_add_allocation_pool (query, pool, size, priv->surface_pool->len, 0);
1391   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
1392   gst_object_unref (pool);
1393
1394   return TRUE;
1395 }
1396 #else
1397 /* TODO: Add support VA/DMABuf */
1398 static gboolean
1399 gst_qsv_encoder_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
1400 {
1401   GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1402   GstQsvEncoderPrivate *priv = self->priv;
1403   GstVideoInfo info;
1404   GstBufferPool *pool;
1405   GstCaps *caps;
1406   guint size;
1407   GstStructure *config;
1408   GstVideoAlignment align;
1409
1410   gst_query_parse_allocation (query, &caps, nullptr);
1411   if (!caps) {
1412     GST_WARNING_OBJECT (self, "null caps in query");
1413     return FALSE;
1414   }
1415
1416   if (!gst_video_info_from_caps (&info, caps)) {
1417     GST_WARNING_OBJECT (self, "Failed to convert caps into info");
1418     return FALSE;
1419   }
1420
1421   pool = gst_video_buffer_pool_new ();
1422
1423   gst_video_alignment_reset (&align);
1424   align.padding_right = GST_VIDEO_INFO_WIDTH (&priv->aligned_info) -
1425       GST_VIDEO_INFO_WIDTH (&info);
1426   align.padding_bottom = GST_VIDEO_INFO_HEIGHT (&priv->aligned_info) -
1427       GST_VIDEO_INFO_HEIGHT (&info);
1428
1429   config = gst_buffer_pool_get_config (pool);
1430   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1431   gst_buffer_pool_config_add_option (config,
1432       GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
1433   gst_video_info_align (&info, &align);
1434   gst_buffer_pool_config_set_video_alignment (config, &align);
1435
1436   size = GST_VIDEO_INFO_SIZE (&info);
1437   gst_buffer_pool_config_set_params (config,
1438       caps, size, priv->surface_pool->len, 0);
1439
1440   if (!gst_buffer_pool_set_config (pool, config)) {
1441     GST_WARNING_OBJECT (self, "Failed to set pool config");
1442     gst_object_unref (pool);
1443     return FALSE;
1444   }
1445
1446   gst_query_add_allocation_pool (query, pool, size, priv->surface_pool->len, 0);
1447   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
1448   gst_object_unref (pool);
1449
1450   return TRUE;
1451 }
1452 #endif