2 * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
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.
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.
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.
24 #include "gstqsvencoder.h"
25 #include <mfxvideo++.h>
30 #include "gstqsvallocator_d3d11.h"
35 using namespace Microsoft::WRL;
38 #include "gstqsvallocator_va.h"
39 #endif /* G_OS_WIN32 */
41 GST_DEBUG_CATEGORY_STATIC (gst_qsv_encoder_debug);
42 #define GST_CAT_DEFAULT gst_qsv_encoder_debug
45 gst_qsv_coding_option_get_type (void)
47 static GType coding_opt_type = 0;
48 static const GEnumValue coding_opts[] = {
49 {MFX_CODINGOPTION_UNKNOWN, "Unknown", "unknown"},
50 {MFX_CODINGOPTION_ON, "On", "on"},
51 {MFX_CODINGOPTION_OFF, "Off", "off"},
55 if (g_once_init_enter (&coding_opt_type)) {
56 GType type = g_enum_register_static ("GstQsvCodingOption",
58 g_once_init_leave (&coding_opt_type, type);
61 return coding_opt_type;
73 #define DEFAULT_TARGET_USAGE MFX_TARGETUSAGE_BALANCED
74 #define DEFAULT_LOW_LATENCY FALSE
76 typedef struct _GstQsvEncoderSurface
78 mfxFrameSurface1 surface;
79 mfxEncodeCtrl encode_control;
81 /* array of mfxPayload (e.g., SEI data) associated with this surface */
85 GstQsvFrame *qsv_frame;
86 } GstQsvEncoderSurface;
88 typedef struct _GstQsvEncoderTask
90 mfxSyncPoint sync_point;
91 mfxBitstream bitstream;
94 struct _GstQsvEncoderPrivate
98 GstVideoCodecState *input_state;
99 GstQsvAllocator *allocator;
101 /* API specific alignment requirement (multiple of 16 or 32) */
102 GstVideoInfo aligned_info;
105 mfxVideoParam video_param;
107 /* List of mfxExtBuffer configured by subclass, subclass will hold
108 * allocated memory for each mfxExtBuffer */
109 GPtrArray *extra_params;
111 MFXVideoENCODE *encoder;
112 GstQsvMemoryType mem_type;
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;
118 /* Array of GstQsvEncoderSurface, holding ownership */
119 GArray *surface_pool;
120 guint next_surface_index;
122 /* Array of GstQsvEncoderTask, holding ownership */
126 GQueue pending_tasks;
130 gboolean low_latency;
133 #define gst_qsv_encoder_parent_class parent_class
134 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstQsvEncoder, gst_qsv_encoder,
135 GST_TYPE_VIDEO_ENCODER, G_ADD_PRIVATE (GstQsvEncoder);
136 GST_DEBUG_CATEGORY_INIT (gst_qsv_encoder_debug,
137 "qsvencoder", 0, "qsvencoder"));
139 static void gst_qsv_encoder_dispose (GObject * object);
140 static void gst_qsv_encoder_finalize (GObject * object);
141 static void gst_qsv_encoder_set_property (GObject * object, guint prop_id,
142 const GValue * value, GParamSpec * pspec);
143 static void gst_qsv_encoder_get_property (GObject * object, guint prop_id,
144 GValue * value, GParamSpec * pspec);
146 static void gst_qsv_encoder_set_context (GstElement * element,
147 GstContext * context);
149 static gboolean gst_qsv_encoder_open (GstVideoEncoder * encoder);
150 static gboolean gst_qsv_encoder_stop (GstVideoEncoder * encoder);
151 static gboolean gst_qsv_encoder_close (GstVideoEncoder * encoder);
152 static gboolean gst_qsv_encoder_set_format (GstVideoEncoder * encoder,
153 GstVideoCodecState * state);
154 static GstFlowReturn gst_qsv_encoder_handle_frame (GstVideoEncoder * encoder,
155 GstVideoCodecFrame * frame);
156 static GstFlowReturn gst_qsv_encoder_finish (GstVideoEncoder * encoder);
157 static gboolean gst_qsv_encoder_flush (GstVideoEncoder * encoder);
158 static gboolean gst_qsv_encoder_sink_query (GstVideoEncoder * encoder,
160 static gboolean gst_qsv_encoder_src_query (GstVideoEncoder * encoder,
162 static gboolean gst_qsv_encoder_propose_allocation (GstVideoEncoder * encoder,
165 static void gst_qsv_encoder_surface_clear (GstQsvEncoderSurface * task);
166 static void gst_qsv_encoder_task_clear (GstQsvEncoderTask * task);
169 gst_qsv_encoder_class_init (GstQsvEncoderClass * klass)
171 GObjectClass *object_class = G_OBJECT_CLASS (klass);
172 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
173 GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
175 object_class->dispose = gst_qsv_encoder_dispose;
176 object_class->finalize = gst_qsv_encoder_finalize;
177 object_class->set_property = gst_qsv_encoder_set_property;
178 object_class->get_property = gst_qsv_encoder_get_property;
181 g_object_class_install_property (object_class, PROP_ADAPTER_LUID,
182 g_param_spec_int64 ("adapter-luid", "Adapter LUID",
183 "DXGI Adapter LUID (Locally Unique Identifier) of created device",
184 G_MININT64, G_MAXINT64, 0,
185 (GParamFlags) (GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_READABLE |
186 G_PARAM_STATIC_STRINGS)));
188 g_object_class_install_property (object_class, PROP_DEVICE_PATH,
189 g_param_spec_string ("device-path", "Device Path",
190 "DRM device path", NULL,
191 (GParamFlags) (GST_PARAM_CONDITIONALLY_AVAILABLE |
192 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
195 g_object_class_install_property (object_class, PROP_TARGET_USAGE,
196 g_param_spec_uint ("target-usage", "Target Usage",
197 "1: Best quality, 4: Balanced, 7: Best speed",
198 1, 7, DEFAULT_TARGET_USAGE,
199 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
201 g_object_class_install_property (object_class, PROP_LOW_LATENCY,
202 g_param_spec_boolean ("low-latency", "Low Latency",
203 "Enables low-latency encoding", DEFAULT_LOW_LATENCY,
204 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
206 element_class->set_context = GST_DEBUG_FUNCPTR (gst_qsv_encoder_set_context);
208 videoenc_class->open = GST_DEBUG_FUNCPTR (gst_qsv_encoder_open);
209 videoenc_class->stop = GST_DEBUG_FUNCPTR (gst_qsv_encoder_stop);
210 videoenc_class->close = GST_DEBUG_FUNCPTR (gst_qsv_encoder_close);
211 videoenc_class->set_format = GST_DEBUG_FUNCPTR (gst_qsv_encoder_set_format);
212 videoenc_class->handle_frame =
213 GST_DEBUG_FUNCPTR (gst_qsv_encoder_handle_frame);
214 videoenc_class->finish = GST_DEBUG_FUNCPTR (gst_qsv_encoder_finish);
215 videoenc_class->flush = GST_DEBUG_FUNCPTR (gst_qsv_encoder_flush);
216 videoenc_class->sink_query = GST_DEBUG_FUNCPTR (gst_qsv_encoder_sink_query);
217 videoenc_class->src_query = GST_DEBUG_FUNCPTR (gst_qsv_encoder_src_query);
218 videoenc_class->propose_allocation =
219 GST_DEBUG_FUNCPTR (gst_qsv_encoder_propose_allocation);
223 gst_qsv_encoder_init (GstQsvEncoder * self)
225 GstQsvEncoderPrivate *priv;
228 (GstQsvEncoderPrivate *) gst_qsv_encoder_get_instance_private (self);
230 priv->extra_params = g_ptr_array_sized_new (8);
232 priv->surface_pool = g_array_new (FALSE, TRUE, sizeof (GstQsvEncoderSurface));
233 g_array_set_clear_func (priv->surface_pool,
234 (GDestroyNotify) gst_qsv_encoder_surface_clear);
236 priv->task_pool = g_array_new (FALSE, TRUE, sizeof (GstQsvEncoderTask));
237 g_array_set_clear_func (priv->task_pool,
238 (GDestroyNotify) gst_qsv_encoder_task_clear);
240 g_queue_init (&priv->free_tasks);
241 g_queue_init (&priv->pending_tasks);
243 priv->target_usage = DEFAULT_TARGET_USAGE;
244 priv->low_latency = DEFAULT_LOW_LATENCY;
248 gst_qsv_encoder_dispose (GObject * object)
250 GstQsvEncoder *self = GST_QSV_ENCODER (object);
251 GstQsvEncoderPrivate *priv = self->priv;
253 gst_clear_object (&priv->device);
255 G_OBJECT_CLASS (parent_class)->dispose (object);
259 gst_qsv_encoder_finalize (GObject * object)
261 GstQsvEncoder *self = GST_QSV_ENCODER (object);
262 GstQsvEncoderPrivate *priv = self->priv;
264 g_ptr_array_unref (priv->extra_params);
265 g_array_unref (priv->task_pool);
266 g_array_unref (priv->surface_pool);
268 G_OBJECT_CLASS (parent_class)->finalize (object);
272 gst_qsv_encoder_set_property (GObject * object, guint prop_id,
273 const GValue * value, GParamSpec * pspec)
275 GstQsvEncoder *self = GST_QSV_ENCODER (object);
276 GstQsvEncoderPrivate *priv = self->priv;
279 case PROP_TARGET_USAGE:
280 priv->target_usage = g_value_get_uint (value);
282 case PROP_LOW_LATENCY:
283 priv->low_latency = g_value_get_boolean (value);
286 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292 gst_qsv_encoder_get_property (GObject * object, guint prop_id, GValue * value,
295 GstQsvEncoder *self = GST_QSV_ENCODER (object);
296 GstQsvEncoderPrivate *priv = self->priv;
297 GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
300 case PROP_ADAPTER_LUID:
301 g_value_set_int64 (value, klass->adapter_luid);
303 case PROP_DEVICE_PATH:
304 g_value_set_string (value, klass->display_path);
306 case PROP_TARGET_USAGE:
307 g_value_set_uint (value, priv->target_usage);
309 case PROP_LOW_LATENCY:
310 g_value_set_boolean (value, priv->low_latency);
313 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
319 gst_qsv_encoder_set_context (GstElement * element, GstContext * context)
321 GstQsvEncoder *self = GST_QSV_ENCODER (element);
322 GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (element);
323 GstQsvEncoderPrivate *priv = self->priv;
326 gst_d3d11_handle_set_context_for_adapter_luid (element,
327 context, klass->adapter_luid, (GstD3D11Device **) & priv->device);
329 gst_va_handle_set_context (element, context, klass->display_path,
330 (GstVaDisplay **) & priv->device);
333 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
338 gst_qsv_encoder_open_platform_device (GstQsvEncoder * self)
340 GstQsvEncoderPrivate *priv = self->priv;
341 GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
342 ComPtr < ID3D10Multithread > multi_thread;
344 ID3D11Device *device_handle;
346 GstD3D11Device *device;
348 if (!gst_d3d11_ensure_element_data_for_adapter_luid (GST_ELEMENT (self),
349 klass->adapter_luid, (GstD3D11Device **) & priv->device)) {
350 GST_ERROR_OBJECT (self, "d3d11 device is unavailable");
354 device = GST_D3D11_DEVICE_CAST (priv->device);
355 priv->allocator = gst_qsv_d3d11_allocator_new (device);
357 /* For D3D11 device handle to be used by QSV, multithread protection layer
358 * must be enabled before the MFXVideoCORE_SetHandle() call.
360 * TODO: Need to check performance impact by this mutithread protection layer,
361 * since it may have a negative impact on overall pipeline performance.
362 * If so, we should create encoding session dedicated d3d11 device and
363 * make use of shared resource */
364 device_handle = gst_d3d11_device_get_device_handle (device);
365 hr = device_handle->QueryInterface (IID_PPV_ARGS (&multi_thread));
366 if (!gst_d3d11_result (hr, device)) {
367 GST_ERROR_OBJECT (self, "ID3D10Multithread interface is unavailable");
371 multi_thread->SetMultithreadProtected (TRUE);
372 status = MFXVideoCORE_SetHandle (priv->session, MFX_HANDLE_D3D11_DEVICE,
374 if (status != MFX_ERR_NONE) {
375 GST_ERROR_OBJECT (self, "Failed to set d3d11 device handle");
379 /* NOTE: We never use this mfxFrameAllocator to allocate memory from our side,
380 * but required for QSV because:
381 * 1) QSV may request memory allocation for encoder's internal usage,
382 * MFX_FOURCC_P8 for example
383 * 2) Our mfxFrameAllocator provides bridge layer for
384 * gst_video_frame_{map,unmap} and mfxFrameAllocator::{Lock,Unlock},
385 * including mfxFrameAllocator::GetHDL.
386 * 3) GstQsvAllocator provides GstQsvFrame pool, and therefore allocated
387 * GstQsvFrame struct can be re-used without per-frame malloc/free
389 status = MFXVideoCORE_SetFrameAllocator (priv->session,
390 gst_qsv_allocator_get_allocator_handle (priv->allocator));
391 if (status != MFX_ERR_NONE) {
392 GST_ERROR_OBJECT (self, "Failed to set frame allocator %d", status);
400 gst_qsv_encoder_open_platform_device (GstQsvEncoder * self)
402 GstQsvEncoderPrivate *priv = self->priv;
403 GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
405 GstVaDisplay *display;
407 if (!gst_va_ensure_element_data (GST_ELEMENT (self), klass->display_path,
408 (GstVaDisplay **) & priv->device)) {
409 GST_ERROR_OBJECT (self, "VA display is unavailable");
413 display = GST_VA_DISPLAY (priv->device);
415 priv->allocator = gst_qsv_va_allocator_new (display);
417 status = MFXVideoCORE_SetHandle (priv->session, MFX_HANDLE_VA_DISPLAY,
418 gst_va_display_get_va_dpy (display));
419 if (status != MFX_ERR_NONE) {
420 GST_ERROR_OBJECT (self, "Failed to set VA display handle");
424 status = MFXVideoCORE_SetFrameAllocator (priv->session,
425 gst_qsv_allocator_get_allocator_handle (priv->allocator));
426 if (status != MFX_ERR_NONE) {
427 GST_ERROR_OBJECT (self, "Failed to set frame allocator %d", status);
436 gst_qsv_encoder_open (GstVideoEncoder * encoder)
438 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
439 GstQsvEncoderPrivate *priv = self->priv;
440 GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
443 status = MFXCreateSession (gst_qsv_get_loader (), klass->impl_index,
445 if (status != MFX_ERR_NONE) {
446 GST_ERROR_OBJECT (self, "Failed to create session");
450 if (!gst_qsv_encoder_open_platform_device (self)) {
451 g_clear_pointer (&priv->session, MFXClose);
452 gst_clear_object (&priv->allocator);
453 gst_clear_object (&priv->device);
462 gst_qsv_encoder_reset (GstQsvEncoder * self)
464 GstQsvEncoderPrivate *priv = self->priv;
467 delete priv->encoder;
468 priv->encoder = nullptr;
471 if (priv->internal_pool) {
472 gst_buffer_pool_set_active (priv->internal_pool, FALSE);
473 gst_clear_object (&priv->internal_pool);
476 g_array_set_size (priv->surface_pool, 0);
477 g_array_set_size (priv->task_pool, 0);
478 g_queue_clear (&priv->free_tasks);
479 g_queue_clear (&priv->pending_tasks);
485 gst_qsv_encoder_stop (GstVideoEncoder * encoder)
487 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
488 GstQsvEncoderPrivate *priv = self->priv;
490 gst_qsv_encoder_reset (self);
491 g_clear_pointer (&priv->input_state, gst_video_codec_state_unref);
497 gst_qsv_encoder_close (GstVideoEncoder * encoder)
499 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
500 GstQsvEncoderPrivate *priv = self->priv;
502 g_clear_pointer (&priv->session, MFXClose);
503 gst_clear_object (&priv->allocator);
504 gst_clear_object (&priv->device);
510 gst_qsv_encoder_payload_clear (mfxPayload * payload)
515 g_free (payload->Data);
520 gst_qsv_encoder_surface_reset (GstQsvEncoderSurface * surface)
525 gst_clear_qsv_frame (&surface->qsv_frame);
526 g_ptr_array_set_size (surface->payload, 0);
527 memset (&surface->encode_control, 0, sizeof (mfxEncodeCtrl));
531 gst_qsv_encoder_surface_clear (GstQsvEncoderSurface * surface)
536 gst_qsv_encoder_surface_reset (surface);
537 g_clear_pointer (&surface->payload, g_ptr_array_unref);
538 memset (&surface->surface, 0, sizeof (mfxFrameSurface1));
542 gst_qsv_encoder_task_reset (GstQsvEncoder * self, GstQsvEncoderTask * task)
544 GstQsvEncoderPrivate *priv = self->priv;
549 task->sync_point = nullptr;
550 task->bitstream.DataLength = 0;
551 g_queue_push_head (&priv->free_tasks, task);
555 gst_qsv_encoder_task_clear (GstQsvEncoderTask * task)
560 g_clear_pointer (&task->bitstream.Data, g_free);
561 memset (&task->bitstream, 0, sizeof (mfxBitstream));
564 static GstQsvEncoderSurface *
565 gst_qsv_encoder_get_next_surface (GstQsvEncoder * self)
567 GstQsvEncoderPrivate *priv = self->priv;
568 GstQsvEncoderSurface *surface = nullptr;
570 for (guint i = priv->next_surface_index; i < priv->surface_pool->len; i++) {
571 GstQsvEncoderSurface *iter =
572 &g_array_index (priv->surface_pool, GstQsvEncoderSurface, i);
574 /* This means surface is still being used by QSV */
575 if (iter->surface.Data.Locked > 0)
579 priv->next_surface_index = i;
583 for (guint i = 0; i < priv->next_surface_index; i++) {
584 GstQsvEncoderSurface *iter =
585 &g_array_index (priv->surface_pool, GstQsvEncoderSurface, i);
587 /* This means surface is still being used by QSV */
588 if (iter->surface.Data.Locked > 0)
592 priv->next_surface_index = i;
596 /* Magic number to avoid too large pool size */
597 if (priv->surface_pool->len > 64) {
598 GST_ERROR_OBJECT (self,
599 "No availble surface but pool size is too large already");
603 /* Something went wrong, increase surface pool size */
604 GST_INFO_OBJECT (self, "No useable surfaces, increasing pool size to %d",
605 priv->surface_pool->len + 1);
607 g_array_set_size (priv->surface_pool, priv->surface_pool->len + 1);
608 surface = &g_array_index (priv->surface_pool, GstQsvEncoderSurface,
609 priv->surface_pool->len - 1);
611 memset (surface, 0, sizeof (GstQsvEncoderSurface));
612 surface->surface.Info =
613 g_array_index (priv->surface_pool, GstQsvEncoderSurface, 0).surface.Info;
614 surface->payload = g_ptr_array_new_with_free_func ((GDestroyNotify)
615 gst_qsv_encoder_payload_clear);
618 priv->next_surface_index++;
619 priv->next_surface_index %= priv->surface_pool->len;
621 gst_qsv_encoder_surface_reset (surface);
626 gst_qsv_encoder_encode_frame (GstQsvEncoder * self,
627 GstQsvEncoderSurface * surface, GstQsvEncoderTask * task, mfxU64 timestamp)
630 GstQsvEncoderPrivate *priv = self->priv;
632 guint retry_count = 0;
634 const guint retry_threshold = 100;
635 mfxEncodeCtrl *encode_ctrl;
638 s = &surface->surface;
639 s->Data.MemId = (mfxMemId) surface->qsv_frame;
640 s->Data.TimeStamp = timestamp;
641 encode_ctrl = &surface->encode_control;
645 encode_ctrl = nullptr;
649 status = priv->encoder->EncodeFrameAsync (encode_ctrl,
650 s, &task->bitstream, &task->sync_point);
652 /* XXX: probably we should try to drain pending tasks if any in this case
654 if (status == MFX_WRN_DEVICE_BUSY && retry_count < retry_threshold) {
655 GST_INFO_OBJECT (self, "GPU is busy, retry count (%d/%d)",
656 retry_count, retry_threshold);
659 /* Magic number 10ms */
670 static GstVideoCodecFrame *
671 gst_qsv_encoder_find_output_frame (GstQsvEncoder * self, GstClockTime pts)
673 GList *frames, *iter;
674 GstVideoCodecFrame *ret = nullptr;
675 GstVideoCodecFrame *closest = nullptr;
676 guint64 min_pts_abs_diff = 0;
678 /* give up, just returns the oldest frame */
679 if (!GST_CLOCK_TIME_IS_VALID (pts))
680 return gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (self));
682 frames = gst_video_encoder_get_frames (GST_VIDEO_ENCODER (self));
684 for (iter = frames; iter; iter = g_list_next (iter)) {
685 GstVideoCodecFrame *frame = (GstVideoCodecFrame *) iter->data;
688 if (!GST_CLOCK_TIME_IS_VALID (frame->pts))
691 if (pts == frame->pts) {
696 if (pts >= frame->pts)
697 abs_diff = pts - frame->pts;
699 abs_diff = frame->pts - pts;
701 if (!closest || abs_diff < min_pts_abs_diff) {
703 min_pts_abs_diff = abs_diff;
711 gst_video_codec_frame_ref (ret);
713 ret = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (self));
717 g_list_free_full (frames, (GDestroyNotify) gst_video_codec_frame_unref);
723 gst_qsv_encoder_finish_frame (GstQsvEncoder * self, GstQsvEncoderTask * task,
726 GstQsvEncoderPrivate *priv = self->priv;
727 GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
730 GstVideoCodecFrame *frame;
731 GstClockTime qsv_pts = GST_CLOCK_TIME_NONE;
732 GstClockTime qsv_dts = GST_CLOCK_TIME_NONE;
734 gboolean keyframe = FALSE;
735 guint retry_count = 0;
737 const guint retry_threshold = 100;
739 status = MFX_ERR_NONE;
741 /* magic number 100 ms */
742 status = MFXVideoCORE_SyncOperation (priv->session, task->sync_point, 100);
744 /* Retry up to 10 sec (100 ms x 100 times), that should be enough time for
745 * encoding a frame using hardware */
746 if (status == MFX_WRN_IN_EXECUTION && retry_count < retry_threshold) {
747 GST_DEBUG_OBJECT (self,
748 "Operation is still in execution, retry count (%d/%d)",
749 retry_count, retry_threshold);
758 gst_qsv_encoder_task_reset (self, task);
762 if (status != MFX_ERR_NONE && status != MFX_ERR_NONE_PARTIAL_OUTPUT) {
763 gst_qsv_encoder_task_reset (self, task);
765 if (status == MFX_ERR_ABORTED) {
766 GST_INFO_OBJECT (self, "Operation was aborted");
767 return GST_FLOW_FLUSHING;
770 GST_WARNING_OBJECT (self, "SyncOperation returned %d (%s)",
771 QSV_STATUS_ARGS (status));
773 return GST_FLOW_ERROR;
776 bs = &task->bitstream;
777 qsv_pts = gst_qsv_timestamp_to_gst (bs->TimeStamp);
779 /* SDK runtime seems to report zero DTS for all fraems in case of VP9.
780 * It sounds SDK bug, but we can workaround it safely because VP9 B-frame is
781 * not supported in this implementation.
783 * Also we perfer our nanoseconds timestamp instead of QSV's timescale.
784 * So let' ignore QSV's timescale for non-{h264,h265} cases.
786 * TODO: We may need to use DTS for MPEG2 (not implemented yet)
788 if (klass->codec_id == MFX_CODEC_AVC || klass->codec_id == MFX_CODEC_HEVC)
789 qsv_dts = gst_qsv_timestamp_to_gst ((mfxU64) bs->DecodeTimeStamp);
791 if ((bs->FrameType & MFX_FRAMETYPE_IDR) != 0)
794 if (klass->create_output_buffer) {
795 buffer = klass->create_output_buffer (self, bs);
797 buffer = gst_buffer_new_memdup (bs->Data + bs->DataOffset, bs->DataLength);
799 gst_qsv_encoder_task_reset (self, task);
802 GST_ERROR_OBJECT (self, "No output buffer");
803 return GST_FLOW_ERROR;
806 frame = gst_qsv_encoder_find_output_frame (self, qsv_pts);
808 if (GST_CLOCK_TIME_IS_VALID (qsv_dts)) {
809 frame->pts = qsv_pts;
810 frame->dts = qsv_dts;
812 frame->dts = frame->pts;
815 frame->output_buffer = buffer;
818 GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
820 return gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (self), frame);
823 /* Empty available frame, something went wrong but we can just push this
825 GST_WARNING_OBJECT (self, "Failed to find corresponding frame");
826 GST_BUFFER_PTS (buffer) = qsv_pts;
827 if (GST_CLOCK_TIME_IS_VALID (qsv_dts))
828 GST_BUFFER_DTS (buffer) = qsv_dts;
830 GST_BUFFER_DTS (buffer) = qsv_pts;
833 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
835 return gst_pad_push (GST_VIDEO_ENCODER_SRC_PAD (self), buffer);
839 gst_qsv_encoder_drain (GstQsvEncoder * self, gboolean discard)
841 GstQsvEncoderPrivate *priv = self->priv;
842 mfxStatus status = MFX_ERR_NONE;
843 GstFlowReturn ret = GST_FLOW_OK;
844 GstQsvEncoderTask *task;
846 if (!priv->session || !priv->encoder)
849 GST_DEBUG_OBJECT (self, "Drain");
851 /* Drain pending tasks first if any */
852 while (g_queue_get_length (&priv->pending_tasks) > 0) {
853 task = (GstQsvEncoderTask *) g_queue_pop_tail (&priv->pending_tasks);
854 ret = gst_qsv_encoder_finish_frame (self, task, discard);
857 while (status == MFX_ERR_NONE) {
858 task = (GstQsvEncoderTask *) g_queue_pop_tail (&priv->free_tasks);
859 status = gst_qsv_encoder_encode_frame (self,
860 nullptr, task, MFX_TIMESTAMP_UNKNOWN);
862 /* once it's fully drained, then driver will return more data */
863 if (status == MFX_ERR_NONE && task->sync_point) {
864 ret = gst_qsv_encoder_finish_frame (self, task, discard);
868 if (status != MFX_ERR_MORE_DATA)
869 GST_WARNING_OBJECT (self, "Unexpected status return %d (%s)",
870 QSV_STATUS_ARGS (status));
872 g_queue_push_head (&priv->free_tasks, task);
875 /* Release GstQsvFrame objects */
876 for (guint i = 0; i < priv->surface_pool->len; i++) {
877 GstQsvEncoderSurface *iter =
878 &g_array_index (priv->surface_pool, GstQsvEncoderSurface, i);
880 if (iter->surface.Data.Locked > 0) {
881 GST_WARNING_OBJECT (self,
882 "Encoder was drained but QSV is holding surface %d", i);
886 gst_qsv_encoder_surface_reset (iter);
894 gst_qsv_encoder_prepare_d3d11_pool (GstQsvEncoder * self,
895 GstCaps * caps, GstVideoInfo * aligned_info)
897 GstQsvEncoderPrivate *priv = self->priv;
898 GstStructure *config;
899 GstD3D11AllocationParams *params;
900 GstD3D11Device *device = GST_D3D11_DEVICE_CAST (priv->device);
901 guint bind_flags = 0;
902 GstD3D11Format device_format;
904 gst_d3d11_device_get_format (device, GST_VIDEO_INFO_FORMAT (aligned_info),
906 if ((device_format.format_support[0] & D3D11_FORMAT_SUPPORT_RENDER_TARGET) ==
907 D3D11_FORMAT_SUPPORT_RENDER_TARGET) {
908 /* XXX: workaround for greenish artifacts
909 * https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1238
910 * bind to render target so that d3d11 memory allocator can clear texture
911 * with black color */
912 bind_flags = D3D11_BIND_RENDER_TARGET;
915 priv->internal_pool = gst_d3d11_buffer_pool_new (device);
916 config = gst_buffer_pool_get_config (priv->internal_pool);
917 params = gst_d3d11_allocation_params_new (device, aligned_info,
918 GST_D3D11_ALLOCATION_FLAG_DEFAULT, bind_flags,
919 D3D11_RESOURCE_MISC_SHARED);
921 gst_buffer_pool_config_set_d3d11_allocation_params (config, params);
922 gst_d3d11_allocation_params_free (params);
923 gst_buffer_pool_config_set_params (config, caps,
924 GST_VIDEO_INFO_SIZE (aligned_info), 0, 0);
925 gst_buffer_pool_set_config (priv->internal_pool, config);
926 gst_buffer_pool_set_active (priv->internal_pool, TRUE);
932 gst_qsv_encoder_prepare_va_pool (GstQsvEncoder * self,
933 GstCaps * caps, GstVideoInfo * aligned_info)
935 GstQsvEncoderPrivate *priv = self->priv;
936 GstAllocator *allocator;
937 GstStructure *config;
939 GstAllocationParams params;
940 GstVaDisplay *display = GST_VA_DISPLAY (priv->device);
942 formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
943 g_array_append_val (formats, GST_VIDEO_INFO_FORMAT (aligned_info));
945 allocator = gst_va_allocator_new (display, formats);
947 GST_ERROR_OBJECT (self, "Failed to create allocator");
951 gst_allocation_params_init (¶ms);
953 priv->internal_pool = gst_va_pool_new_with_config (caps,
954 GST_VIDEO_INFO_SIZE (aligned_info), 0, 0,
955 VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC, GST_VA_FEATURE_AUTO,
957 gst_object_unref (allocator);
960 if (!priv->internal_pool) {
961 GST_ERROR_OBJECT (self, "Failed to create va pool");
965 config = gst_buffer_pool_get_config (priv->internal_pool);
966 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
967 gst_buffer_pool_config_set_params (config, caps,
968 GST_VIDEO_INFO_SIZE (aligned_info), 0, 0);
969 gst_buffer_pool_set_config (priv->internal_pool, config);
970 gst_buffer_pool_set_active (priv->internal_pool, TRUE);
976 /* Prepare internal pool, which is used to allocate fallback buffer
977 * when upstream buffer is not directly accessible by QSV */
979 gst_qsv_encoder_prepare_pool (GstQsvEncoder * self, GstCaps * caps,
980 GstVideoInfo * aligned_info)
982 GstQsvEncoderPrivate *priv = self->priv;
983 gboolean ret = FALSE;
984 GstCaps *aligned_caps;
986 if (priv->internal_pool) {
987 gst_buffer_pool_set_active (priv->internal_pool, FALSE);
988 gst_clear_object (&priv->internal_pool);
991 aligned_caps = gst_video_info_to_caps (aligned_info);
994 ret = gst_qsv_encoder_prepare_d3d11_pool (self, aligned_caps, aligned_info);
996 ret = gst_qsv_encoder_prepare_va_pool (self, aligned_caps, aligned_info);
999 gst_caps_unref (aligned_caps);
1005 gst_qsv_encoder_init_encode_session (GstQsvEncoder * self)
1007 GstQsvEncoderPrivate *priv = self->priv;
1008 GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
1009 GstVideoInfo *info = &priv->input_state->info;
1010 GstCaps *caps = priv->input_state->caps;
1011 mfxVideoParam param;
1012 mfxFrameInfo *frame_info;
1013 mfxFrameAllocRequest alloc_request;
1015 MFXVideoENCODE *encoder_handle = nullptr;
1016 guint bitstream_size;
1018 guint64 min_delay_frames, max_delay_frames;
1019 GstClockTime min_latency, max_latency;
1021 gst_qsv_encoder_drain (self, FALSE);
1022 gst_qsv_encoder_reset (self);
1024 encoder_handle = new MFXVideoENCODE (priv->session);
1026 memset (¶m, 0, sizeof (mfxVideoParam));
1028 g_ptr_array_set_size (priv->extra_params, 0);
1029 g_assert (klass->set_format);
1030 if (!klass->set_format (self, priv->input_state, ¶m, priv->extra_params)) {
1031 GST_ERROR_OBJECT (self, "Subclass failed to set format");
1035 /* LowPower mode supports smaller set of features, don't enable it for now */
1036 param.mfx.LowPower = MFX_CODINGOPTION_OFF;
1037 if (priv->low_latency)
1038 param.AsyncDepth = 1;
1040 param.AsyncDepth = 4;
1042 param.mfx.TargetUsage = priv->target_usage;
1044 frame_info = ¶m.mfx.FrameInfo;
1046 gst_video_info_set_interlaced_format (&priv->aligned_info,
1047 GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_INTERLACE_MODE (info),
1048 frame_info->Width, frame_info->Height);
1050 /* Always video memory, even when upstream is non-hardware element */
1051 priv->mem_type = GST_QSV_VIDEO_MEMORY | GST_QSV_ENCODER_IN_MEMORY;
1052 param.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
1053 if (!gst_qsv_encoder_prepare_pool (self, caps, &priv->aligned_info)) {
1054 GST_ERROR_OBJECT (self, "Failed to prepare pool");
1058 status = encoder_handle->Query (¶m, ¶m);
1059 /* If device is unhappy with LowPower = OFF, try again with unknown */
1060 if (status < MFX_ERR_NONE) {
1061 GST_INFO_OBJECT (self, "LowPower - OFF returned %d (%s)",
1062 QSV_STATUS_ARGS (status));
1063 param.mfx.LowPower = MFX_CODINGOPTION_UNKNOWN;
1064 status = encoder_handle->Query (¶m, ¶m);
1066 QSV_CHECK_STATUS (self, status, MFXVideoENCODE::Query);
1068 status = encoder_handle->QueryIOSurf (¶m, &alloc_request);
1069 QSV_CHECK_STATUS (self, status, MFXVideoENCODE::QueryIOSurf);
1071 status = encoder_handle->Init (¶m);
1072 QSV_CHECK_STATUS (self, status, MFXVideoENCODE::Init);
1074 status = encoder_handle->GetVideoParam (¶m);
1075 QSV_CHECK_STATUS (self, status, MFXVideoENCODE::GetVideoParam);
1077 GST_DEBUG_OBJECT (self, "NumFrameSuggested: %d, AsyncDepth %d",
1078 alloc_request.NumFrameSuggested, param.AsyncDepth);
1080 g_assert (klass->set_output_state);
1081 ret = klass->set_output_state (self, priv->input_state, priv->session);
1083 GST_ERROR_OBJECT (self, "Subclass failed to set output state");
1087 /* Prepare surface pool with size NumFrameSuggested, then if it's not
1088 * sufficient while encoding, we can increse the pool size dynamically
1090 g_array_set_size (priv->surface_pool, alloc_request.NumFrameSuggested);
1091 for (guint i = 0; i < priv->surface_pool->len; i++) {
1092 GstQsvEncoderSurface *surface = &g_array_index (priv->surface_pool,
1093 GstQsvEncoderSurface, i);
1095 surface->surface.Info = param.mfx.FrameInfo;
1096 surface->payload = g_ptr_array_new_with_free_func ((GDestroyNotify)
1097 gst_qsv_encoder_payload_clear);
1099 priv->next_surface_index = 0;
1101 g_array_set_size (priv->task_pool, param.AsyncDepth);
1102 if (klass->codec_id == MFX_CODEC_JPEG) {
1103 gdouble factor = 4.0;
1105 /* jpeg zero returns buffer size */
1106 switch (GST_VIDEO_INFO_FORMAT (info)) {
1107 case GST_VIDEO_FORMAT_NV12:
1110 case GST_VIDEO_FORMAT_YUY2:
1116 bitstream_size = (guint)
1117 (factor * GST_VIDEO_INFO_WIDTH (info) * GST_VIDEO_INFO_HEIGHT (info));
1120 (guint) param.mfx.BufferSizeInKB * param.mfx.BRCParamMultiplier * 1024;
1123 for (guint i = 0; i < priv->task_pool->len; i++) {
1124 GstQsvEncoderTask *task = &g_array_index (priv->task_pool,
1125 GstQsvEncoderTask, i);
1127 task->bitstream.Data = (mfxU8 *) g_malloc (bitstream_size);
1128 task->bitstream.MaxLength = bitstream_size;
1130 g_queue_push_head (&priv->free_tasks, task);
1133 min_delay_frames = priv->task_pool->len;
1134 /* takes the number of bframes into account */
1135 if (param.mfx.GopRefDist > 1)
1136 min_delay_frames += (param.mfx.GopRefDist - 1);
1137 max_delay_frames = priv->surface_pool->len + priv->task_pool->len;
1139 min_latency = gst_util_uint64_scale (min_delay_frames * GST_SECOND,
1140 param.mfx.FrameInfo.FrameRateExtD, param.mfx.FrameInfo.FrameRateExtN);
1141 max_latency = gst_util_uint64_scale (max_delay_frames * GST_SECOND,
1142 param.mfx.FrameInfo.FrameRateExtD, param.mfx.FrameInfo.FrameRateExtN);
1143 gst_video_encoder_set_latency (GST_VIDEO_ENCODER (self),
1144 min_latency, max_latency);
1146 priv->video_param = param;
1147 priv->encoder = encoder_handle;
1153 delete encoder_handle;
1155 gst_qsv_encoder_reset (self);
1161 gst_qsv_encoder_reset_encode_session (GstQsvEncoder * self)
1163 GstQsvEncoderPrivate *priv = self->priv;
1164 GPtrArray *extra_params = priv->extra_params;
1166 mfxExtEncoderResetOption reset_opt;
1168 if (!priv->encoder) {
1169 GST_WARNING_OBJECT (self, "Encoder was not configured");
1170 return gst_qsv_encoder_init_encode_session (self);
1173 reset_opt.Header.BufferId = MFX_EXTBUFF_ENCODER_RESET_OPTION;
1174 reset_opt.Header.BufferSz = sizeof (mfxExtEncoderResetOption);
1175 reset_opt.StartNewSequence = MFX_CODINGOPTION_OFF;
1177 gst_qsv_encoder_drain (self, FALSE);
1179 g_ptr_array_add (extra_params, &reset_opt);
1180 priv->video_param.ExtParam = (mfxExtBuffer **) extra_params->pdata;
1181 priv->video_param.NumExtParam = extra_params->len;
1183 status = priv->encoder->Reset (&priv->video_param);
1184 g_ptr_array_remove_index (extra_params, extra_params->len - 1);
1185 priv->video_param.NumExtParam = extra_params->len;
1187 if (status != MFX_ERR_NONE) {
1188 GST_WARNING_OBJECT (self, "MFXVideoENCODE_Reset returned %d (%s)",
1189 QSV_STATUS_ARGS (status));
1190 return gst_qsv_encoder_init_encode_session (self);
1193 GST_DEBUG_OBJECT (self, "Encode session reset done");
1199 gst_qsv_encoder_set_format (GstVideoEncoder * encoder,
1200 GstVideoCodecState * state)
1202 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1203 GstQsvEncoderPrivate *priv = self->priv;
1205 g_clear_pointer (&priv->input_state, gst_video_codec_state_unref);
1206 priv->input_state = gst_video_codec_state_ref (state);
1208 return gst_qsv_encoder_init_encode_session (self);
1212 gst_qsv_encoder_get_pic_struct (GstQsvEncoder * self,
1213 GstVideoCodecFrame * frame)
1215 GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
1216 GstQsvEncoderPrivate *priv = self->priv;
1217 GstVideoInfo *info = &priv->input_state->info;
1219 if (klass->codec_id != MFX_CODEC_AVC)
1220 return MFX_PICSTRUCT_PROGRESSIVE;
1222 if (!GST_VIDEO_INFO_IS_INTERLACED (info))
1223 return MFX_PICSTRUCT_PROGRESSIVE;
1225 if (GST_VIDEO_INFO_INTERLACE_MODE (info) == GST_VIDEO_INTERLACE_MODE_MIXED) {
1226 if (!GST_BUFFER_FLAG_IS_SET (frame->input_buffer,
1227 GST_VIDEO_BUFFER_FLAG_INTERLACED)) {
1228 return MFX_PICSTRUCT_PROGRESSIVE;
1231 if (GST_BUFFER_FLAG_IS_SET (frame->input_buffer, GST_VIDEO_BUFFER_FLAG_TFF))
1232 return MFX_PICSTRUCT_FIELD_TFF;
1234 return MFX_PICSTRUCT_FIELD_BFF;
1237 switch (GST_VIDEO_INFO_FIELD_ORDER (info)) {
1238 case GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST:
1239 return MFX_PICSTRUCT_FIELD_TFF;
1241 case GST_VIDEO_FIELD_ORDER_BOTTOM_FIELD_FIRST:
1242 return MFX_PICSTRUCT_FIELD_BFF;
1248 if (GST_BUFFER_FLAG_IS_SET (frame->input_buffer, GST_VIDEO_BUFFER_FLAG_TFF))
1249 return MFX_PICSTRUCT_FIELD_TFF;
1251 return MFX_PICSTRUCT_FIELD_BFF;
1254 static GstFlowReturn
1255 gst_qsv_encoder_handle_frame (GstVideoEncoder * encoder,
1256 GstVideoCodecFrame * frame)
1258 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1259 GstQsvEncoderPrivate *priv = self->priv;
1260 GstQsvEncoderClass *klass = GST_QSV_ENCODER_GET_CLASS (self);
1261 GstFlowReturn ret = GST_FLOW_ERROR;
1262 GstQsvEncoderSurface *surface;
1263 GstQsvEncoderTask *task;
1267 if (klass->check_reconfigure && priv->encoder) {
1268 GstQsvEncoderReconfigure reconfigure;
1270 reconfigure = klass->check_reconfigure (self, priv->session,
1271 &priv->video_param, priv->extra_params);
1273 switch (reconfigure) {
1274 case GST_QSV_ENCODER_RECONFIGURE_BITRATE:
1275 if (!gst_qsv_encoder_reset_encode_session (self)) {
1276 GST_ERROR_OBJECT (self, "Failed to reset session");
1277 gst_video_encoder_finish_frame (encoder, frame);
1279 return GST_FLOW_ERROR;
1282 case GST_QSV_ENCODER_RECONFIGURE_FULL:
1283 if (!gst_qsv_encoder_init_encode_session (self)) {
1284 GST_ERROR_OBJECT (self, "Failed to init session");
1285 gst_video_encoder_finish_frame (encoder, frame);
1287 return GST_FLOW_ERROR;
1295 if (!priv->encoder) {
1296 GST_ERROR_OBJECT (self, "Encoder object was not configured");
1297 gst_video_encoder_finish_frame (encoder, frame);
1299 return GST_FLOW_NOT_NEGOTIATED;
1302 surface = gst_qsv_encoder_get_next_surface (self);
1304 GST_ERROR_OBJECT (self, "No available surface");
1308 task = (GstQsvEncoderTask *) g_queue_pop_tail (&priv->free_tasks);
1311 surface->qsv_frame =
1312 gst_qsv_allocator_acquire_frame (priv->allocator, priv->mem_type,
1313 &priv->input_state->info, gst_buffer_ref (frame->input_buffer),
1314 priv->internal_pool);
1316 if (!surface->qsv_frame) {
1317 GST_ERROR_OBJECT (self, "Failed to wrap buffer with qsv frame");
1318 gst_qsv_encoder_task_reset (self, task);
1322 surface->surface.Info.PicStruct =
1323 gst_qsv_encoder_get_pic_struct (self, frame);
1325 if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
1326 surface->encode_control.FrameType =
1327 MFX_FRAMETYPE_IDR | MFX_FRAMETYPE_I | MFX_FRAMETYPE_REF;
1329 surface->encode_control.FrameType = MFX_FRAMETYPE_UNKNOWN;
1332 if (klass->attach_payload) {
1333 klass->attach_payload (self, frame, surface->payload);
1334 if (surface->payload->len > 0) {
1335 surface->encode_control.NumPayload = surface->payload->len;
1336 surface->encode_control.Payload = (mfxPayload **) surface->payload->pdata;
1340 timestamp = gst_qsv_timestamp_from_gst (frame->pts);
1341 status = gst_qsv_encoder_encode_frame (self, surface, task, timestamp);
1342 if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
1343 GST_ERROR_OBJECT (self, "Failed to encode frame, ret %d (%s)",
1344 QSV_STATUS_ARGS (status));
1345 gst_qsv_encoder_task_reset (self, task);
1349 if (status == MFX_ERR_NONE && task->sync_point) {
1350 g_queue_push_head (&priv->pending_tasks, task);
1352 gst_qsv_encoder_task_reset (self, task);
1356 /* Do not sync immediately, but record tasks which have output buffer here
1357 * to improve throughput.
1358 * In this way, hardware may be able to run encoding job from its background
1359 * threads (if any). We will do sync only when there's no more free task item
1361 while (g_queue_get_length (&priv->pending_tasks) >= priv->task_pool->len) {
1362 GstQsvEncoderTask *task =
1363 (GstQsvEncoderTask *) g_queue_pop_tail (&priv->pending_tasks);
1364 ret = gst_qsv_encoder_finish_frame (self, task, FALSE);
1368 gst_video_codec_frame_unref (frame);
1373 static GstFlowReturn
1374 gst_qsv_encoder_finish (GstVideoEncoder * encoder)
1376 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1378 return gst_qsv_encoder_drain (self, FALSE);
1382 gst_qsv_encoder_flush (GstVideoEncoder * encoder)
1384 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1386 gst_qsv_encoder_drain (self, TRUE);
1392 gst_qsv_encoder_handle_context_query (GstQsvEncoder * self, GstQuery * query)
1394 GstQsvEncoderPrivate *priv = self->priv;
1397 return gst_d3d11_handle_context_query (GST_ELEMENT (self), query,
1398 (GstD3D11Device *) priv->device);
1400 return gst_va_handle_context_query (GST_ELEMENT (self), query,
1401 (GstVaDisplay *) priv->device);
1406 gst_qsv_encoder_sink_query (GstVideoEncoder * encoder, GstQuery * query)
1408 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1410 switch (GST_QUERY_TYPE (query)) {
1411 case GST_QUERY_CONTEXT:
1412 if (gst_qsv_encoder_handle_context_query (self, query))
1419 return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (encoder, query);
1423 gst_qsv_encoder_src_query (GstVideoEncoder * encoder, GstQuery * query)
1425 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1427 switch (GST_QUERY_TYPE (query)) {
1428 case GST_QUERY_CONTEXT:
1429 if (gst_qsv_encoder_handle_context_query (self, query))
1436 return GST_VIDEO_ENCODER_CLASS (parent_class)->src_query (encoder, query);
1441 gst_qsv_encoder_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
1443 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1444 GstQsvEncoderPrivate *priv = self->priv;
1445 GstD3D11Device *device = GST_D3D11_DEVICE (priv->device);
1447 GstBufferPool *pool;
1450 GstStructure *config;
1451 GstCapsFeatures *features;
1452 gboolean is_d3d11 = FALSE;
1454 gst_query_parse_allocation (query, &caps, nullptr);
1456 GST_WARNING_OBJECT (self, "null caps in query");
1460 if (!gst_video_info_from_caps (&info, caps)) {
1461 GST_WARNING_OBJECT (self, "Failed to convert caps into info");
1465 features = gst_caps_get_features (caps, 0);
1466 if (features && gst_caps_features_contains (features,
1467 GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
1468 GST_DEBUG_OBJECT (self, "upstream support d3d11 memory");
1469 pool = gst_d3d11_buffer_pool_new (device);
1472 pool = gst_video_buffer_pool_new ();
1475 config = gst_buffer_pool_get_config (pool);
1476 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1479 GstD3D11AllocationParams *d3d11_params;
1480 GstVideoAlignment align;
1482 /* d3d11 buffer pool doesn't support generic video alignment
1483 * because memory layout of CPU accessible staging texture is uncontrollable.
1484 * Do D3D11 specific handling */
1485 gst_video_alignment_reset (&align);
1487 align.padding_right = GST_VIDEO_INFO_WIDTH (&priv->aligned_info) -
1488 GST_VIDEO_INFO_WIDTH (&info);
1489 align.padding_bottom = GST_VIDEO_INFO_HEIGHT (&priv->aligned_info) -
1490 GST_VIDEO_INFO_HEIGHT (&info);
1492 d3d11_params = gst_d3d11_allocation_params_new (device, &info,
1493 GST_D3D11_ALLOCATION_FLAG_DEFAULT, 0, 0);
1495 gst_d3d11_allocation_params_alignment (d3d11_params, &align);
1496 gst_buffer_pool_config_set_d3d11_allocation_params (config, d3d11_params);
1497 gst_d3d11_allocation_params_free (d3d11_params);
1499 gst_buffer_pool_config_add_option (config,
1500 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
1503 size = GST_VIDEO_INFO_SIZE (&info);
1504 gst_buffer_pool_config_set_params (config,
1505 caps, size, priv->surface_pool->len, 0);
1507 if (!gst_buffer_pool_set_config (pool, config)) {
1508 GST_WARNING_OBJECT (self, "Failed to set pool config");
1509 gst_object_unref (pool);
1513 /* d3d11 buffer pool will update actual CPU accessible buffer size based on
1514 * allocated staging texture per gst_buffer_pool_set_config() call,
1515 * need query again to get the size */
1516 config = gst_buffer_pool_get_config (pool);
1517 gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr, nullptr);
1518 gst_structure_free (config);
1520 gst_query_add_allocation_pool (query, pool, size, priv->surface_pool->len, 0);
1521 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
1522 gst_object_unref (pool);
1528 gst_qsv_encoder_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
1530 GstQsvEncoder *self = GST_QSV_ENCODER (encoder);
1531 GstQsvEncoderPrivate *priv = self->priv;
1533 GstAllocator *allocator = nullptr;
1534 GstBufferPool *pool;
1537 GstStructure *config;
1538 GstVideoAlignment align;
1539 GstAllocationParams params;
1542 gst_query_parse_allocation (query, &caps, nullptr);
1544 GST_WARNING_OBJECT (self, "null caps in query");
1548 if (!gst_video_info_from_caps (&info, caps)) {
1549 GST_WARNING_OBJECT (self, "Failed to convert caps into info");
1553 gst_allocation_params_init (¶ms);
1555 formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
1556 g_array_append_val (formats, GST_VIDEO_INFO_FORMAT (&info));
1558 allocator = gst_va_allocator_new (GST_VA_DISPLAY (priv->device), formats);
1560 GST_ERROR_OBJECT (self, "Failed to create allocator");
1564 pool = gst_va_pool_new_with_config (caps,
1565 GST_VIDEO_INFO_SIZE (&info), priv->surface_pool->len, 0,
1566 VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC, GST_VA_FEATURE_AUTO,
1567 allocator, ¶ms);
1570 GST_ERROR_OBJECT (self, "Failed to create va pool");
1571 gst_object_unref (allocator);
1576 gst_video_alignment_reset (&align);
1577 align.padding_right = GST_VIDEO_INFO_WIDTH (&priv->aligned_info) -
1578 GST_VIDEO_INFO_WIDTH (&info);
1579 align.padding_bottom = GST_VIDEO_INFO_HEIGHT (&priv->aligned_info) -
1580 GST_VIDEO_INFO_HEIGHT (&info);
1582 config = gst_buffer_pool_get_config (pool);
1583 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1584 gst_buffer_pool_config_add_option (config,
1585 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
1586 gst_buffer_pool_config_set_video_alignment (config, &align);
1588 gst_buffer_pool_config_set_params (config,
1589 caps, GST_VIDEO_INFO_SIZE (&info), priv->surface_pool->len, 0);
1591 if (!gst_buffer_pool_set_config (pool, config)) {
1592 GST_ERROR_OBJECT (self, "Failed to set pool config");
1593 gst_clear_object (&allocator);
1594 gst_object_unref (pool);
1599 gst_query_add_allocation_param (query, allocator, ¶ms);
1601 config = gst_buffer_pool_get_config (pool);
1602 gst_buffer_pool_config_get_params (config, nullptr, &size, nullptr, nullptr);
1603 gst_structure_free (config);
1605 gst_query_add_allocation_pool (query, pool, size, priv->surface_pool->len, 0);
1606 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
1608 gst_clear_object (&allocator);
1609 gst_object_unref (pool);