2 * Copyright (C) 2020 Seungha Yang <seungha.yang@navercorp.com>
3 * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
26 #include "gstmfvideoencoder.h"
27 #include "gstmfvideobuffer.h"
28 #include "gstmfplatloader.h"
38 using namespace Microsoft::WRL;
41 GST_DEBUG_CATEGORY_EXTERN (gst_mf_video_encoder_debug);
42 #define GST_CAT_DEFAULT gst_mf_video_encoder_debug
47 * Base class for MediaFoundation video encoders
51 #define gst_mf_video_encoder_parent_class parent_class
52 G_DEFINE_ABSTRACT_TYPE (GstMFVideoEncoder, gst_mf_video_encoder,
53 GST_TYPE_VIDEO_ENCODER);
55 static void gst_mf_video_encoder_dispose (GObject * object);
56 static void gst_mf_video_encoder_set_context (GstElement * element,
57 GstContext * context);
58 static gboolean gst_mf_video_encoder_open (GstVideoEncoder * enc);
59 static gboolean gst_mf_video_encoder_close (GstVideoEncoder * enc);
60 static gboolean gst_mf_video_encoder_start (GstVideoEncoder * enc);
61 static gboolean gst_mf_video_encoder_set_format (GstVideoEncoder * enc,
62 GstVideoCodecState * state);
63 static GstFlowReturn gst_mf_video_encoder_handle_frame (GstVideoEncoder * enc,
64 GstVideoCodecFrame * frame);
65 static GstFlowReturn gst_mf_video_encoder_finish (GstVideoEncoder * enc);
66 static gboolean gst_mf_video_encoder_flush (GstVideoEncoder * enc);
67 static gboolean gst_mf_video_encoder_propose_allocation (GstVideoEncoder * enc,
69 static gboolean gst_mf_video_encoder_sink_query (GstVideoEncoder * enc,
71 static gboolean gst_mf_video_encoder_src_query (GstVideoEncoder * enc,
74 static HRESULT gst_mf_video_on_new_sample (GstMFTransform * object,
75 IMFSample * sample, GstMFVideoEncoder * self);
78 gst_mf_video_encoder_class_init (GstMFVideoEncoderClass * klass)
80 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
81 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
82 GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
84 gobject_class->dispose = gst_mf_video_encoder_dispose;
86 element_class->set_context =
87 GST_DEBUG_FUNCPTR (gst_mf_video_encoder_set_context);
89 videoenc_class->open = GST_DEBUG_FUNCPTR (gst_mf_video_encoder_open);
90 videoenc_class->close = GST_DEBUG_FUNCPTR (gst_mf_video_encoder_close);
91 videoenc_class->start = GST_DEBUG_FUNCPTR (gst_mf_video_encoder_start);
92 videoenc_class->set_format =
93 GST_DEBUG_FUNCPTR (gst_mf_video_encoder_set_format);
94 videoenc_class->handle_frame =
95 GST_DEBUG_FUNCPTR (gst_mf_video_encoder_handle_frame);
96 videoenc_class->finish = GST_DEBUG_FUNCPTR (gst_mf_video_encoder_finish);
97 videoenc_class->flush = GST_DEBUG_FUNCPTR (gst_mf_video_encoder_flush);
98 videoenc_class->propose_allocation =
99 GST_DEBUG_FUNCPTR (gst_mf_video_encoder_propose_allocation);
100 videoenc_class->sink_query =
101 GST_DEBUG_FUNCPTR (gst_mf_video_encoder_sink_query);
102 videoenc_class->src_query =
103 GST_DEBUG_FUNCPTR (gst_mf_video_encoder_src_query);
105 gst_type_mark_as_plugin_api (GST_TYPE_MF_VIDEO_ENCODER,
106 (GstPluginAPIFlags) 0);
110 gst_mf_video_encoder_init (GstMFVideoEncoder * self)
115 gst_mf_video_encoder_dispose (GObject * object)
117 #if GST_MF_HAVE_D3D11
118 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (object);
120 gst_clear_object (&self->d3d11_device);
121 gst_clear_object (&self->other_d3d11_device);
124 G_OBJECT_CLASS (parent_class)->dispose (object);
128 gst_mf_video_encoder_set_context (GstElement * element, GstContext * context)
130 #if GST_MF_HAVE_D3D11
131 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (element);
132 GstMFVideoEncoderClass *klass = GST_MF_VIDEO_ENCODER_GET_CLASS (self);
133 GstMFVideoEncoderDeviceCaps *device_caps = &klass->device_caps;
135 if (device_caps->d3d11_aware) {
136 gst_d3d11_handle_set_context_for_adapter_luid (element, context,
137 device_caps->adapter_luid, &self->other_d3d11_device);
141 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
145 gst_mf_video_encoder_open (GstVideoEncoder * enc)
147 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
148 GstMFVideoEncoderClass *klass = GST_MF_VIDEO_ENCODER_GET_CLASS (enc);
149 GstMFVideoEncoderDeviceCaps *device_caps = &klass->device_caps;
150 GstMFTransformEnumParams enum_params = { 0, };
151 MFT_REGISTER_TYPE_INFO output_type;
153 #if GST_MF_HAVE_D3D11
154 if (device_caps->d3d11_aware) {
156 ID3D11Device *device_handle;
157 ComPtr < ID3D10Multithread > multi_thread;
158 GstD3D11Device *device;
160 if (!gst_d3d11_ensure_element_data_for_adapter_luid (GST_ELEMENT (self),
161 device_caps->adapter_luid, &self->other_d3d11_device)) {
162 GST_ERROR_OBJECT (self, "Other d3d11 device is unavailable");
166 /* Create our own device with D3D11_CREATE_DEVICE_VIDEO_SUPPORT */
168 gst_d3d11_device_new_for_adapter_luid (device_caps->adapter_luid,
169 D3D11_CREATE_DEVICE_VIDEO_SUPPORT);
170 if (!self->d3d11_device) {
171 GST_ERROR_OBJECT (self, "Couldn't create internal d3d11 device");
172 gst_clear_object (&self->other_d3d11_device);
176 device = self->d3d11_device;
178 hr = GstMFCreateDXGIDeviceManager (&self->reset_token,
179 &self->device_manager);
180 if (!gst_mf_result (hr)) {
181 GST_ERROR_OBJECT (self, "Couldn't create DXGI device manager");
182 gst_clear_object (&self->other_d3d11_device);
183 gst_clear_object (&self->d3d11_device);
187 device_handle = gst_d3d11_device_get_device_handle (device);
188 /* Enable multi thread protection as this device will be shared with
190 hr = device_handle->QueryInterface (IID_PPV_ARGS (&multi_thread));
191 if (!gst_d3d11_result (hr, device)) {
192 GST_WARNING_OBJECT (self,
193 "device doesn't suport ID3D10Multithread interface");
194 gst_clear_object (&self->other_d3d11_device);
195 gst_clear_object (&self->d3d11_device);
198 multi_thread->SetMultithreadProtected (TRUE);
200 hr = self->device_manager->ResetDevice ((IUnknown *) device_handle,
202 if (!gst_mf_result (hr)) {
203 GST_ERROR_OBJECT (self, "Couldn't reset device with given d3d11 device");
204 gst_clear_object (&self->other_d3d11_device);
205 gst_clear_object (&self->d3d11_device);
211 output_type.guidMajorType = MFMediaType_Video;
212 output_type.guidSubtype = klass->codec_id;
214 enum_params.category = MFT_CATEGORY_VIDEO_ENCODER;
215 enum_params.enum_flags = klass->enum_flags;
216 enum_params.output_typeinfo = &output_type;
217 enum_params.device_index = klass->device_index;
219 if (device_caps->d3d11_aware)
220 enum_params.adapter_luid = device_caps->adapter_luid;
222 GST_DEBUG_OBJECT (self,
223 "Create MFT with enum flags: 0x%x, device index: %d, d3d11 aware: %d, "
224 "adapter-luid %" G_GINT64_FORMAT, klass->enum_flags, klass->device_index,
225 device_caps->d3d11_aware, device_caps->adapter_luid);
227 self->transform = gst_mf_transform_new (&enum_params);
228 if (!self->transform) {
229 GST_ERROR_OBJECT (self, "Cannot create MFT object");
233 /* In case of hardware MFT, it will be running on async mode.
234 * And new output sample callback will be called from Media Foundation's
235 * internal worker queue thread */
236 if (self->transform &&
237 (enum_params.enum_flags & MFT_ENUM_FLAG_HARDWARE) ==
238 MFT_ENUM_FLAG_HARDWARE) {
239 self->async_mft = TRUE;
240 gst_mf_transform_set_new_sample_callback (self->transform,
241 (GstMFTransformNewSampleCallback) gst_mf_video_on_new_sample, self);
243 self->async_mft = FALSE;
250 gst_mf_video_encoder_close (GstVideoEncoder * enc)
252 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
254 gst_clear_object (&self->transform);
256 if (self->input_state) {
257 gst_video_codec_state_unref (self->input_state);
258 self->input_state = nullptr;
260 #if GST_MF_HAVE_D3D11
261 if (self->device_manager) {
262 self->device_manager->Release ();
263 self->device_manager = nullptr;
266 if (self->mf_allocator) {
267 self->mf_allocator->UninitializeSampleAllocator ();
268 self->mf_allocator->Release ();
269 self->mf_allocator = nullptr;
272 gst_clear_object (&self->other_d3d11_device);
273 gst_clear_object (&self->d3d11_device);
274 gst_clear_d3d11_fence (&self->fence);
281 gst_mf_video_encoder_start (GstVideoEncoder * enc)
283 /* Media Foundation Transform will shift PTS in case that B-frame is enabled.
284 * We need to adjust DTS correspondingly */
285 gst_video_encoder_set_min_pts (enc, GST_SECOND * 60 * 60 * 1000);
291 gst_mf_video_encoder_init_mft (GstMFVideoEncoder * self)
293 GstMFVideoEncoderClass *klass = GST_MF_VIDEO_ENCODER_GET_CLASS (self);
294 GstVideoInfo *info = &self->input_state->info;
295 GstCaps *caps = self->input_state->caps;
296 ComPtr < IMFMediaType > in_type;
297 ComPtr < IMFMediaType > out_type;
298 GList *input_types = nullptr;
303 GST_DEBUG_OBJECT (self, "Set format");
305 gst_mf_video_encoder_finish (GST_VIDEO_ENCODER (self));
307 self->mf_pts_offset = 0;
308 self->has_reorder_frame = FALSE;
309 self->last_ret = GST_FLOW_OK;
311 if (!gst_mf_transform_open (self->transform)) {
312 GST_ERROR_OBJECT (self, "Failed to open MFT");
315 #if GST_MF_HAVE_D3D11
316 if (self->device_manager) {
317 if (!gst_mf_transform_set_device_manager (self->transform,
318 self->device_manager)) {
319 GST_ERROR_OBJECT (self, "Couldn't set device manager");
322 GST_DEBUG_OBJECT (self, "set device manager done");
327 /* TODO: We support I420/NV12/P010 only for now.
328 * Consider other subsampling once we add it */
329 if ((info->width % 2) != 0 || (info->height % 2) != 0) {
330 self->need_align = TRUE;
332 self->need_align = FALSE;
335 hr = MFCreateMediaType (&out_type);
336 if (!gst_mf_result (hr))
339 hr = out_type->SetGUID (MF_MT_MAJOR_TYPE, MFMediaType_Video);
340 if (!gst_mf_result (hr))
343 if (klass->set_option) {
344 if (!klass->set_option (self, self->input_state, out_type.Get ())) {
345 GST_ERROR_OBJECT (self, "subclass failed to set option");
350 fps_n = GST_VIDEO_INFO_FPS_N (info);
351 fps_d = GST_VIDEO_INFO_FPS_D (info);
352 if (fps_n <= 0 || fps_d <= 0) {
353 /* XXX: not sure why. NVIDIA MFT accepts 0/1 framerate, but Intel or
354 * Microsoft's software MFT doesn't accept 0/1 framerate.
355 * Need to set something meaningful value here therefore */
360 hr = MFSetAttributeRatio (out_type.Get (), MF_MT_FRAME_RATE, fps_n, fps_d);
361 if (!gst_mf_result (hr)) {
362 GST_ERROR_OBJECT (self,
363 "Couldn't set framerate %d/%d, hr: 0x%x", (guint) hr);
367 hr = MFSetAttributeSize (out_type.Get (), MF_MT_FRAME_SIZE,
368 GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
369 if (!gst_mf_result (hr)) {
370 GST_ERROR_OBJECT (self,
371 "Couldn't set resolution %dx%d, hr: 0x%x", GST_VIDEO_INFO_WIDTH (info),
372 GST_VIDEO_INFO_HEIGHT (info), (guint) hr);
376 hr = MFSetAttributeRatio (out_type.Get (), MF_MT_PIXEL_ASPECT_RATIO,
377 GST_VIDEO_INFO_PAR_N (info), GST_VIDEO_INFO_PAR_D (info));
378 if (!gst_mf_result (hr)) {
379 GST_ERROR_OBJECT (self, "Couldn't set par %d/%d",
380 GST_VIDEO_INFO_PAR_N (info), GST_VIDEO_INFO_PAR_D (info));
384 hr = out_type->SetUINT32 (MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
385 if (!gst_mf_result (hr)) {
386 GST_ERROR_OBJECT (self,
387 "Couldn't set interlace mode, hr: 0x%x", (guint) hr);
391 if (!gst_mf_transform_set_output_type (self->transform, out_type.Get ())) {
392 GST_ERROR_OBJECT (self, "Couldn't set output type");
396 if (!gst_mf_transform_get_input_available_types (self->transform,
398 GST_ERROR_OBJECT (self, "Couldn't get available input types");
402 for (iter = input_types; iter; iter = g_list_next (iter)) {
403 GstVideoFormat format;
405 IMFMediaType *type = (IMFMediaType *) iter->data;
407 hr = type->GetGUID (MF_MT_SUBTYPE, &subtype);
408 if (!gst_mf_result (hr))
411 format = gst_mf_video_subtype_to_video_format (&subtype);
412 if (format != GST_VIDEO_INFO_FORMAT (info))
418 g_list_free_full (input_types, (GDestroyNotify) gst_mf_media_type_release);
421 GST_ERROR_OBJECT (self,
422 "Couldn't convert input caps %" GST_PTR_FORMAT " to media type", caps);
426 hr = MFSetAttributeSize (in_type.Get (), MF_MT_FRAME_SIZE,
427 GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
428 if (!gst_mf_result (hr)) {
429 GST_ERROR_OBJECT (self, "Couldn't set frame size %dx%d",
430 GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
434 hr = in_type->SetUINT32 (MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
435 if (!gst_mf_result (hr)) {
436 GST_ERROR_OBJECT (self,
437 "Couldn't set interlace mode, hr: 0x%x", (guint) hr);
441 hr = MFSetAttributeRatio (in_type.Get (), MF_MT_PIXEL_ASPECT_RATIO,
442 GST_VIDEO_INFO_PAR_N (info), GST_VIDEO_INFO_PAR_D (info));
443 if (!gst_mf_result (hr)) {
444 GST_ERROR_OBJECT (self, "Couldn't set par %d/%d",
445 GST_VIDEO_INFO_PAR_N (info), GST_VIDEO_INFO_PAR_D (info));
449 hr = MFSetAttributeRatio (in_type.Get (), MF_MT_FRAME_RATE, fps_n, fps_d);
450 if (!gst_mf_result (hr)) {
451 GST_ERROR_OBJECT (self, "Couldn't set framerate ratio %d/%d", fps_n, fps_d);
455 hr = in_type->SetUINT32 (MF_MT_DEFAULT_STRIDE,
456 GST_VIDEO_INFO_PLANE_STRIDE (info, 0));
457 if (!gst_mf_result (hr)) {
458 GST_ERROR_OBJECT (self, "Couldn't set default stride");
462 if (!gst_mf_transform_set_input_type (self->transform, in_type.Get ())) {
463 GST_ERROR_OBJECT (self, "Couldn't set input media type");
467 g_assert (klass->set_src_caps != nullptr);
468 if (!klass->set_src_caps (self, self->input_state, out_type.Get ())) {
469 GST_ERROR_OBJECT (self, "subclass couldn't set src caps");
472 #if GST_MF_HAVE_D3D11
473 if (self->mf_allocator) {
474 self->mf_allocator->UninitializeSampleAllocator ();
475 self->mf_allocator->Release ();
476 self->mf_allocator = nullptr;
479 /* Check whether upstream is d3d11 element */
480 GstCapsFeatures *features;
481 ComPtr < IMFVideoSampleAllocatorEx > allocator;
483 features = gst_caps_get_features (caps, 0);
486 gst_caps_features_contains (features,
487 GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
488 GST_DEBUG_OBJECT (self, "found D3D11 memory feature");
490 hr = GstMFCreateVideoSampleAllocatorEx (IID_PPV_ARGS (&allocator));
491 if (!gst_mf_result (hr))
492 GST_WARNING_OBJECT (self,
493 "IMFVideoSampleAllocatorEx interface is unavailable");
498 ComPtr < IMFAttributes > attr;
500 hr = MFCreateAttributes (&attr, 4);
501 if (!gst_mf_result (hr))
504 /* Only one buffer per sample
505 * (multiple sample is usually for multi-view things) */
506 hr = attr->SetUINT32 (GST_GUID_MF_SA_BUFFERS_PER_SAMPLE, 1);
507 if (!gst_mf_result (hr))
510 hr = attr->SetUINT32 (GST_GUID_MF_SA_D3D11_USAGE, D3D11_USAGE_DEFAULT);
511 if (!gst_mf_result (hr))
514 /* TODO: Check if we need to use keyed-mutex */
515 hr = attr->SetUINT32 (GST_GUID_MF_SA_D3D11_SHARED_WITHOUT_MUTEX, TRUE);
516 if (!gst_mf_result (hr))
519 hr = attr->SetUINT32 (GST_GUID_MF_SA_D3D11_BINDFLAGS,
520 D3D11_BIND_VIDEO_ENCODER);
521 if (!gst_mf_result (hr))
524 hr = allocator->SetDirectXManager (self->device_manager);
525 if (!gst_mf_result (hr))
528 hr = allocator->InitializeSampleAllocatorEx (
529 /* min samples, since we are running on async mode,
530 * at least 2 samples would be required */
532 /* max samples, why 16 + 2? it's just magic number
533 * (H264 max dpb size 16 + our min sample size 2) */
534 16 + 2, attr.Get (), in_type.Get ()
537 if (!gst_mf_result (hr))
540 GST_DEBUG_OBJECT (self, "IMFVideoSampleAllocatorEx is initialized");
542 self->mf_allocator = allocator.Detach ();
551 gst_mf_video_encoder_set_format (GstVideoEncoder * enc,
552 GstVideoCodecState * state)
554 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
555 GST_DEBUG_OBJECT (self, "Set format");
557 if (self->input_state)
558 gst_video_codec_state_unref (self->input_state);
559 self->input_state = gst_video_codec_state_ref (state);
561 return gst_mf_video_encoder_init_mft (self);
565 gst_mf_video_buffer_free (GstVideoFrame * frame)
570 gst_video_frame_unmap (frame);
575 gst_mf_video_encoder_frame_needs_copy (GstVideoFrame * vframe)
577 /* Single plane data can be used without copy */
578 if (GST_VIDEO_FRAME_N_PLANES (vframe) == 1)
581 switch (GST_VIDEO_FRAME_FORMAT (vframe)) {
582 case GST_VIDEO_FORMAT_I420:
584 guint8 *data, *other_data;
587 /* Unexpected stride size, Media Foundation doesn't provide API for
588 * per plane stride information */
589 if (GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 0) !=
590 2 * GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 1) ||
591 GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 1) !=
592 GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 2)) {
596 size = GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 0) *
597 GST_VIDEO_FRAME_HEIGHT (vframe);
598 if (size + GST_VIDEO_FRAME_PLANE_OFFSET (vframe, 0) !=
599 GST_VIDEO_FRAME_PLANE_OFFSET (vframe, 1))
602 data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (vframe, 0);
603 other_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (vframe, 1);
604 if (data + size != other_data)
607 size = GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 1) *
608 GST_VIDEO_FRAME_COMP_HEIGHT (vframe, 1);
609 if (size + GST_VIDEO_FRAME_PLANE_OFFSET (vframe, 1) !=
610 GST_VIDEO_FRAME_PLANE_OFFSET (vframe, 2))
613 data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (vframe, 1);
614 other_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (vframe, 2);
615 if (data + size != other_data)
620 case GST_VIDEO_FORMAT_NV12:
621 case GST_VIDEO_FORMAT_P010_10LE:
622 case GST_VIDEO_FORMAT_P016_LE:
624 guint8 *data, *other_data;
627 /* Unexpected stride size, Media Foundation doesn't provide API for
628 * per plane stride information */
629 if (GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 0) !=
630 GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 1)) {
634 size = GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 0) *
635 GST_VIDEO_FRAME_HEIGHT (vframe);
637 /* Unexpected padding */
638 if (size + GST_VIDEO_FRAME_PLANE_OFFSET (vframe, 0) !=
639 GST_VIDEO_FRAME_PLANE_OFFSET (vframe, 1))
642 data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (vframe, 0);
643 other_data = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (vframe, 1);
644 if (data + size != other_data)
650 g_assert_not_reached ();
660 } GstMFVideoEncoderFrameData;
663 gst_mf_video_encoder_process_input (GstMFVideoEncoder * self,
664 GstVideoCodecFrame * frame, IMFSample * sample)
666 GstMFVideoEncoderClass *klass = GST_MF_VIDEO_ENCODER_GET_CLASS (self);
668 gboolean unset_force_keyframe = FALSE;
669 GstMFVideoEncoderFrameData *frame_data = nullptr;
672 frame_data = g_new0 (GstMFVideoEncoderFrameData, 1);
673 frame_data->mf_pts = frame->pts / 100;
675 gst_video_codec_frame_set_user_data (frame,
676 frame_data, (GDestroyNotify) g_free);
678 hr = sample->SetSampleTime (frame_data->mf_pts);
679 if (!gst_mf_result (hr))
683 SetSampleDuration (GST_CLOCK_TIME_IS_VALID (frame->duration) ? frame->
685 if (!gst_mf_result (hr))
688 if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) {
689 if (klass->device_caps.force_keyframe) {
690 unset_force_keyframe =
691 gst_mf_transform_set_codec_api_uint32 (self->transform,
692 &CODECAPI_AVEncVideoForceKeyFrame, TRUE);
694 GST_WARNING_OBJECT (self, "encoder does not support force keyframe");
698 /* Unlock temporary so that we can output frame from Media Foundation's
700 * While we are processing input, MFT might notify
701 * METransformHaveOutput event from Media Foundation's internal worker queue
702 * thread. Then we will output encoded data from the thread synchroniously,
703 * not from streaming (this) thread */
705 GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
706 res = gst_mf_transform_process_input (self->transform, sample);
708 GST_VIDEO_ENCODER_STREAM_LOCK (self);
710 if (unset_force_keyframe) {
711 gst_mf_transform_set_codec_api_uint32 (self->transform,
712 &CODECAPI_AVEncVideoForceKeyFrame, FALSE);
716 GST_ERROR_OBJECT (self, "Failed to process input");
723 static GstVideoCodecFrame *
724 gst_mf_video_encoder_find_output_frame (GstMFVideoEncoder * self,
727 GList *l, *walk = gst_video_encoder_get_frames (GST_VIDEO_ENCODER (self));
728 GstVideoCodecFrame *ret = nullptr;
729 GstVideoCodecFrame *closest = nullptr;
730 LONGLONG min_pts_abs_diff = 0;
732 for (l = walk; l; l = l->next) {
733 GstVideoCodecFrame *frame = (GstVideoCodecFrame *) l->data;
734 GstMFVideoEncoderFrameData *data = (GstMFVideoEncoderFrameData *)
735 gst_video_codec_frame_get_user_data (frame);
741 if (mf_pts == data->mf_pts) {
746 abs_diff = std::abs (mf_pts - data->mf_pts);
748 if (!closest || abs_diff < min_pts_abs_diff) {
750 min_pts_abs_diff = abs_diff;
758 gst_video_codec_frame_ref (ret);
760 /* XXX: Shouldn't happen, but possible if no GstVideoCodecFrame holds
761 * user data for some reasons */
762 GST_WARNING_OBJECT (self,
763 "Failed to find closest GstVideoCodecFrame with MF pts %"
764 G_GINT64_FORMAT, mf_pts);
765 ret = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (self));
769 g_list_free_full (walk, (GDestroyNotify) gst_video_codec_frame_unref);
775 gst_mf_video_encoder_finish_sample (GstMFVideoEncoder * self,
780 ComPtr < IMFMediaBuffer > media_buffer;
782 GstFlowReturn res = GST_FLOW_ERROR;
783 GstVideoCodecFrame *frame;
784 LONGLONG sample_timestamp;
785 LONGLONG sample_duration;
786 LONGLONG target_mf_pts;
788 UINT32 keyframe = FALSE;
790 GstClockTime pts, dts, duration;
792 hr = sample->GetBufferByIndex (0, &media_buffer);
793 if (!gst_mf_result (hr))
796 hr = media_buffer->Lock (&data, nullptr, &buffer_len);
797 if (!gst_mf_result (hr))
800 buffer = gst_buffer_new_allocate (nullptr, buffer_len, nullptr);
801 gst_buffer_fill (buffer, 0, data, buffer_len);
802 media_buffer->Unlock ();
804 sample->GetSampleTime (&sample_timestamp);
805 target_mf_pts = sample_timestamp;
806 sample->GetSampleDuration (&sample_duration);
807 sample->GetUINT32 (MFSampleExtension_CleanPoint, &keyframe);
809 hr = sample->GetUINT64 (MFSampleExtension_DecodeTimestamp, &mf_dts);
811 mf_dts = sample_timestamp;
815 pts = sample_timestamp * 100;
817 duration = sample_duration * 100;
819 GST_LOG_OBJECT (self, "Finish sample, MF pts %" GST_TIME_FORMAT " MF dts %"
820 GST_TIME_FORMAT ", MF duration %" GST_TIME_FORMAT,
821 GST_TIME_ARGS (pts), GST_TIME_ARGS (dts), GST_TIME_ARGS (duration));
823 /* NOTE: When B-frame is enabled, MFT shows following pattern
824 * (input timestamp starts from 1000:00:00.000000000, and 30fps)
826 * Frame-1: MF pts 0:00.033333300 MF dts 0:00.000000000
827 * Frame-2: MF pts 0:00.133333300 MF dts 0:00.033333300
828 * Frame-3: MF pts 0:00.066666600 MF dts 0:00.066666600
829 * Frame-4: MF pts 0:00.099999900 MF dts 0:00.100000000
831 * - Sounds MFT doesn't support negative timestamp, so PTS of each frame seems
833 * - DTS is likely based on timestamp we've set to input sample,
834 * but some frames has (especially Frame-4 case) unexpected PTS and
835 * even PTS < DTS. That would be the result of PTS shifting
837 * To handle this case,
838 * - Calculate timestamp offset "Frame-1 PTS" - "Frame-1 DTS" (== duration),
839 * and compensate PTS/DTS of each frame
840 * - Needs additional offset for DTS to compenstate GST/MF timescale difference
841 * (MF uses 100ns timescale). So DTS offset should be "PTS offset + 100ns"
842 * - Find corresponding GstVideoCodecFrame by using compensated PTS.
843 * Note that MFT doesn't support user-data for tracing input/output sample
844 * pair. So, timestamp based lookup is the only way to map MF sample
845 * and our GstVideoCodecFrame
847 if (self->has_reorder_frame) {
848 /* This would be the first frame */
849 if (self->mf_pts_offset == 0) {
850 LONGLONG mf_pts_offset = -1;
851 if (sample_timestamp > mf_dts) {
852 mf_pts_offset = sample_timestamp - mf_dts;
853 GST_DEBUG_OBJECT (self, "Calculates PTS offset using \"PTS - DTS\": %"
854 G_GINT64_FORMAT, mf_pts_offset);
855 } else if (sample_duration > 0) {
856 mf_pts_offset = sample_duration;
857 GST_DEBUG_OBJECT (self, "Calculates PTS offset using duration: %"
858 G_GINT64_FORMAT, mf_pts_offset);
860 GST_WARNING_OBJECT (self, "Cannot calculate PTS offset");
863 self->mf_pts_offset = mf_pts_offset;
866 if (self->mf_pts_offset > 0) {
867 target_mf_pts -= self->mf_pts_offset;
869 pts -= (self->mf_pts_offset * 100);
870 /* +1 to compensate timescale difference */
871 dts -= ((self->mf_pts_offset + 1) * 100);
875 frame = gst_mf_video_encoder_find_output_frame (self, target_mf_pts);
879 GST_DEBUG_OBJECT (self, "Keyframe pts %" GST_TIME_FORMAT,
880 GST_TIME_ARGS (frame->pts));
881 GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
884 frame->output_buffer = buffer;
886 /* Update DTS only if B-frame was enabled, but use input frame pts as-is.
887 * Otherwise we will lost at most 100ns precision */
888 if (self->has_reorder_frame) {
891 frame->dts = frame->pts;
894 /* make sure PTS > DTS */
895 if (GST_CLOCK_TIME_IS_VALID (frame->pts) &&
896 GST_CLOCK_TIME_IS_VALID (frame->dts) && frame->pts < frame->dts) {
897 GST_WARNING_OBJECT (self, "Calculated DTS %" GST_TIME_FORMAT
898 " is larger than PTS %" GST_TIME_FORMAT, GST_TIME_ARGS (frame->pts),
899 GST_TIME_ARGS (frame->dts));
901 /* XXX: just set clock-time-none? */
902 frame->dts = frame->pts;
905 GST_LOG_OBJECT (self, "Frame pts %" GST_TIME_FORMAT ", Frame DTS %"
906 GST_TIME_FORMAT, GST_TIME_ARGS (frame->pts),
907 GST_TIME_ARGS (frame->dts));
909 res = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (self), frame);
911 GST_BUFFER_PTS (buffer) = pts;
912 GST_BUFFER_DTS (buffer) = dts;
913 GST_BUFFER_DURATION (buffer) = duration;
916 GST_DEBUG_OBJECT (self, "Keyframe pts %" GST_TIME_FORMAT,
917 GST_BUFFER_PTS (buffer));
918 GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
920 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
923 GST_LOG_OBJECT (self, "Buffer pts %" GST_TIME_FORMAT ", Buffer DTS %"
924 GST_TIME_FORMAT, GST_TIME_ARGS (pts), GST_TIME_ARGS (dts));
926 res = gst_pad_push (GST_VIDEO_ENCODER_SRC_PAD (self), buffer);
930 self->last_ret = res;
936 gst_mf_video_encoder_process_output (GstMFVideoEncoder * self)
938 ComPtr < IMFSample > sample;
939 GstFlowReturn res = GST_FLOW_ERROR;
941 res = gst_mf_transform_get_output (self->transform, &sample);
943 if (res != GST_FLOW_OK)
946 gst_mf_video_encoder_finish_sample (self, sample.Get ());
948 return self->last_ret;
952 gst_mf_video_encoder_create_input_sample (GstMFVideoEncoder * self,
953 GstVideoCodecFrame * frame, IMFSample ** sample)
956 ComPtr < IMFSample > new_sample;
957 ComPtr < IMFMediaBuffer > media_buffer;
958 ComPtr < IGstMFVideoBuffer > video_buffer;
959 GstVideoInfo *info = &self->input_state->info;
961 GstVideoFrame *vframe = nullptr;
962 BYTE *data = nullptr;
963 gboolean need_copy = self->need_align;
965 vframe = g_new0 (GstVideoFrame, 1);
967 if (!gst_video_frame_map (vframe, info, frame->input_buffer, GST_MAP_READ)) {
968 GST_ERROR_OBJECT (self, "Couldn't map input frame");
973 hr = MFCreateSample (&new_sample);
974 if (!gst_mf_result (hr))
977 /* Check if we can forward this memory to Media Foundation without copy */
979 need_copy = gst_mf_video_encoder_frame_needs_copy (vframe);
982 GST_TRACE_OBJECT (self, "Copy input buffer into Media Foundation memory");
983 hr = MFCreateMemoryBuffer (GST_VIDEO_INFO_SIZE (info), &media_buffer);
985 GST_TRACE_OBJECT (self, "Can use input buffer without copy");
986 hr = IGstMFVideoBuffer::CreateInstanceWrapped (&vframe->info,
987 (BYTE *) GST_VIDEO_FRAME_PLANE_DATA (vframe, 0),
988 GST_VIDEO_INFO_SIZE (&vframe->info), &media_buffer);
991 if (!gst_mf_result (hr))
995 hr = media_buffer.As (&video_buffer);
996 if (!gst_mf_result (hr))
999 hr = media_buffer->Lock (&data, nullptr, nullptr);
1000 if (!gst_mf_result (hr))
1003 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
1005 gint src_stride, dst_stride;
1008 src = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (vframe, i);
1009 dst = data + GST_VIDEO_INFO_PLANE_OFFSET (info, i);
1011 src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (vframe, i);
1012 dst_stride = GST_VIDEO_INFO_PLANE_STRIDE (info, i);
1014 width = GST_VIDEO_INFO_COMP_WIDTH (info, i)
1015 * GST_VIDEO_INFO_COMP_PSTRIDE (info, i);
1017 for (j = 0; j < GST_VIDEO_INFO_COMP_HEIGHT (info, i); j++) {
1018 memcpy (dst, src, width);
1024 media_buffer->Unlock ();
1027 hr = media_buffer->SetCurrentLength (GST_VIDEO_INFO_SIZE (info));
1028 if (!gst_mf_result (hr))
1031 hr = new_sample->AddBuffer (media_buffer.Get ());
1032 if (!gst_mf_result (hr))
1036 /* IGstMFVideoBuffer will hold GstVideoFrame (+ GstBuffer), then it will be
1037 * cleared when it's no more referenced by Media Foundation internals */
1038 hr = video_buffer->SetUserData ((gpointer) vframe,
1039 (GDestroyNotify) gst_mf_video_buffer_free);
1040 if (!gst_mf_result (hr))
1043 gst_video_frame_unmap (vframe);
1048 *sample = new_sample.Detach ();
1054 gst_video_frame_unmap (vframe);
1061 #if GST_MF_HAVE_D3D11
1063 gst_mf_video_encoder_create_input_sample_d3d11 (GstMFVideoEncoder * self,
1064 GstVideoCodecFrame * frame, IMFSample ** sample)
1066 GstMFVideoEncoderClass *klass = GST_MF_VIDEO_ENCODER_GET_CLASS (self);
1067 GstMFVideoEncoderDeviceCaps *device_caps = &klass->device_caps;
1069 ComPtr < IMFSample > new_sample;
1070 ComPtr < IMFMediaBuffer > mf_buffer;
1071 ComPtr < IMFDXGIBuffer > dxgi_buffer;
1072 ComPtr < ID3D11Texture2D > mf_texture;
1073 ComPtr < IDXGIResource > dxgi_resource;
1074 ComPtr < ID3D11Texture2D > shared_texture;
1075 HANDLE shared_handle;
1077 GstD3D11Memory *dmem;
1078 ID3D11Texture2D *texture;
1079 ID3D11Device *device_handle;
1080 ID3D11DeviceContext *context_handle;
1082 D3D11_BOX src_box = { 0, };
1083 D3D11_TEXTURE2D_DESC dst_desc, src_desc;
1085 gint64 adapter_luid;
1087 if (!self->mf_allocator) {
1088 GST_WARNING_OBJECT (self, "IMFVideoSampleAllocatorEx was configured");
1092 mem = gst_buffer_peek_memory (frame->input_buffer, 0);
1093 if (!gst_is_d3d11_memory (mem)) {
1094 GST_WARNING_OBJECT (self, "Non-d3d11 memory");
1098 dmem = GST_D3D11_MEMORY_CAST (mem);
1099 g_object_get (dmem->device, "adapter-luid", &adapter_luid, nullptr);
1100 if (adapter_luid != device_caps->adapter_luid) {
1101 GST_LOG_OBJECT (self, "Buffer from different GPU");
1105 device_handle = gst_d3d11_device_get_device_handle (dmem->device);
1106 context_handle = gst_d3d11_device_get_device_context_handle (dmem->device);
1108 /* 1) Allocate new encoding surface */
1109 hr = self->mf_allocator->AllocateSample (&new_sample);
1110 if (!gst_mf_result (hr)) {
1111 GST_WARNING_OBJECT (self,
1112 "Couldn't allocate new sample via IMFVideoSampleAllocatorEx");
1116 hr = new_sample->GetBufferByIndex (0, &mf_buffer);
1117 if (!gst_mf_result (hr)) {
1118 GST_WARNING_OBJECT (self, "Couldn't get IMFMediaBuffer from sample");
1122 hr = mf_buffer.As (&dxgi_buffer);
1123 if (!gst_mf_result (hr)) {
1124 GST_WARNING_OBJECT (self, "Couldn't get IMFDXGIBuffer from IMFMediaBuffer");
1128 hr = dxgi_buffer->GetResource (IID_PPV_ARGS (&mf_texture));
1129 if (!gst_mf_result (hr)) {
1130 GST_WARNING_OBJECT (self,
1131 "Couldn't get ID3D11Texture2D from IMFDXGIBuffer");
1135 hr = mf_texture.As (&dxgi_resource);
1136 if (!gst_mf_result (hr)) {
1137 GST_WARNING_OBJECT (self,
1138 "Couldn't get IDXGIResource from ID3D11Texture2D");
1142 hr = dxgi_resource->GetSharedHandle (&shared_handle);
1143 if (!gst_mf_result (hr)) {
1144 GST_WARNING_OBJECT (self, "Couldn't get shared handle from IDXGIResource");
1148 /* Allocation succeeded. Now open shared texture to access it from
1150 hr = device_handle->OpenSharedResource (shared_handle,
1151 IID_PPV_ARGS (&shared_texture));
1152 if (!gst_mf_result (hr)) {
1153 GST_WARNING_OBJECT (self, "Couldn't open shared resource");
1157 /* 2) Copy upstream texture to mf's texture */
1158 /* Map memory so that ensure pending upload from staging texture */
1159 if (!gst_memory_map (mem, &info,
1160 (GstMapFlags) (GST_MAP_READ | GST_MAP_D3D11))) {
1161 GST_ERROR_OBJECT (self, "Couldn't map d3d11 memory");
1165 texture = (ID3D11Texture2D *) info.data;
1166 texture->GetDesc (&src_desc);
1167 shared_texture->GetDesc (&dst_desc);
1168 subidx = gst_d3d11_memory_get_subresource_index (dmem);
1170 /* src/dst texture size might be different if padding was used.
1171 * select smaller size */
1176 src_box.right = MIN (src_desc.Width, dst_desc.Width);
1177 src_box.bottom = MIN (src_desc.Height, dst_desc.Height);
1179 gst_d3d11_device_lock (dmem->device);
1180 if (self->fence && self->fence->device != dmem->device)
1181 gst_clear_d3d11_fence (&self->fence);
1184 self->fence = gst_d3d11_device_create_fence (dmem->device);
1187 GST_ERROR_OBJECT (self, "Couldn't create fence object");
1188 gst_d3d11_device_unlock (dmem->device);
1189 gst_memory_unmap (mem, &info);
1193 context_handle->CopySubresourceRegion (shared_texture.Get (), 0, 0, 0, 0,
1194 texture, subidx, &src_box);
1196 if (!gst_d3d11_fence_signal (self->fence) ||
1197 !gst_d3d11_fence_wait (self->fence)) {
1198 GST_ERROR_OBJECT (self, "Couldn't sync GPU operation");
1199 gst_clear_d3d11_fence (&self->fence);
1200 gst_d3d11_device_unlock (dmem->device);
1201 gst_memory_unmap (mem, &info);
1206 gst_d3d11_device_unlock (dmem->device);
1207 gst_memory_unmap (mem, &info);
1209 *sample = new_sample.Detach ();
1215 static GstFlowReturn
1216 gst_mf_video_encoder_handle_frame (GstVideoEncoder * enc,
1217 GstVideoCodecFrame * frame)
1219 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1220 GstFlowReturn ret = GST_FLOW_OK;
1221 ComPtr < IMFSample > sample;
1222 GstMFVideoEncoderClass *klass = GST_MF_VIDEO_ENCODER_GET_CLASS (self);
1224 if (self->last_ret != GST_FLOW_OK) {
1225 GST_DEBUG_OBJECT (self, "Last return was %s", gst_flow_get_name (ret));
1226 ret = self->last_ret;
1230 if (klass->check_reconfigure (self) && !gst_mf_video_encoder_init_mft (self)) {
1231 GST_ELEMENT_ERROR (self, STREAM, ENCODE, (nullptr),
1232 ("Failed to reconfigure encoder"));
1233 return GST_FLOW_ERROR;
1235 #if GST_MF_HAVE_D3D11
1236 if (self->mf_allocator &&
1237 !gst_mf_video_encoder_create_input_sample_d3d11 (self, frame, &sample)) {
1238 GST_LOG_OBJECT (self, "Failed to create IMFSample for D3D11");
1244 && !gst_mf_video_encoder_create_input_sample (self, frame, &sample)) {
1245 GST_ERROR_OBJECT (self, "Failed to create IMFSample");
1246 ret = GST_FLOW_ERROR;
1250 if (!gst_mf_video_encoder_process_input (self, frame, sample.Get ())) {
1251 GST_ERROR_OBJECT (self, "Failed to process input");
1252 ret = GST_FLOW_ERROR;
1256 /* Don't call process_output for async (hardware) MFT. We will output
1257 * encoded data from gst_mf_video_on_new_sample() callback which is called
1258 * from Media Foundation's internal worker queue thread */
1259 if (!self->async_mft) {
1261 ret = gst_mf_video_encoder_process_output (self);
1262 } while (ret == GST_FLOW_OK);
1265 if (ret == GST_MF_TRANSFORM_FLOW_NEED_DATA)
1269 gst_video_codec_frame_unref (frame);
1274 static GstFlowReturn
1275 gst_mf_video_encoder_finish (GstVideoEncoder * enc)
1277 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1278 GstFlowReturn ret = GST_FLOW_OK;
1280 if (!self->transform)
1283 /* Unlock temporary so that we can output frame from Media Foundation's
1285 if (self->async_mft)
1286 GST_VIDEO_ENCODER_STREAM_UNLOCK (enc);
1288 gst_mf_transform_drain (self->transform);
1290 if (self->async_mft)
1291 GST_VIDEO_ENCODER_STREAM_LOCK (enc);
1293 if (!self->async_mft) {
1295 ret = gst_mf_video_encoder_process_output (self);
1296 } while (ret == GST_FLOW_OK);
1299 if (ret == GST_MF_TRANSFORM_FLOW_NEED_DATA)
1306 gst_mf_video_encoder_flush (GstVideoEncoder * enc)
1308 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1310 if (!self->transform)
1313 /* Unlock while flushing, while flushing, new sample callback might happen */
1314 if (self->async_mft)
1315 GST_VIDEO_ENCODER_STREAM_UNLOCK (enc);
1317 gst_mf_transform_flush (self->transform);
1319 if (self->async_mft)
1320 GST_VIDEO_ENCODER_STREAM_LOCK (enc);
1323 self->last_ret = GST_FLOW_OK;
1329 gst_mf_video_encoder_propose_allocation (GstVideoEncoder * enc,
1332 #if GST_MF_HAVE_D3D11
1333 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1335 GstBufferPool *pool = nullptr;
1338 GstD3D11Device *device = self->other_d3d11_device;
1340 gst_query_parse_allocation (query, &caps, nullptr);
1342 if (caps == nullptr)
1345 if (!gst_video_info_from_caps (&info, caps))
1348 if (gst_query_get_n_allocation_pools (query) == 0) {
1349 GstCapsFeatures *features;
1350 GstStructure *config;
1351 gboolean is_d3d11 = FALSE;
1353 features = gst_caps_get_features (caps, 0);
1355 if (features && gst_caps_features_contains (features,
1356 GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
1357 GST_DEBUG_OBJECT (self, "Allocation caps supports d3d11 memory");
1358 pool = gst_d3d11_buffer_pool_new (device);
1361 pool = gst_video_buffer_pool_new ();
1364 config = gst_buffer_pool_get_config (pool);
1366 gst_buffer_pool_config_add_option (config,
1367 GST_BUFFER_POOL_OPTION_VIDEO_META);
1369 /* d3d11 pool does not support video alignment */
1371 gst_buffer_pool_config_add_option (config,
1372 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
1375 size = GST_VIDEO_INFO_SIZE (&info);
1376 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1378 if (!gst_buffer_pool_set_config (pool, config))
1381 /* d3d11 buffer pool will update buffer size based on allocated texture,
1382 * get size from config again */
1384 config = gst_buffer_pool_get_config (pool);
1385 gst_buffer_pool_config_get_params (config,
1386 nullptr, &size, nullptr, nullptr);
1387 gst_structure_free (config);
1390 gst_query_add_allocation_pool (query, pool, size, 0, 0);
1391 gst_object_unref (pool);
1394 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
1401 GST_ERROR_OBJECT (self, "failed to set config");
1402 gst_object_unref (pool);
1407 return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (enc,
1413 gst_mf_video_encoder_sink_query (GstVideoEncoder * enc, GstQuery * query)
1415 #if GST_MF_HAVE_D3D11
1416 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1418 switch (GST_QUERY_TYPE (query)) {
1419 case GST_QUERY_CONTEXT:
1420 if (gst_d3d11_handle_context_query (GST_ELEMENT (self),
1421 query, self->other_d3d11_device)) {
1430 return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (enc, query);
1434 gst_mf_video_encoder_src_query (GstVideoEncoder * enc, GstQuery * query)
1436 #if GST_MF_HAVE_D3D11
1437 GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1439 switch (GST_QUERY_TYPE (query)) {
1440 case GST_QUERY_CONTEXT:
1441 if (gst_d3d11_handle_context_query (GST_ELEMENT (self),
1442 query, self->other_d3d11_device)) {
1451 return GST_VIDEO_ENCODER_CLASS (parent_class)->src_query (enc, query);
1455 gst_mf_video_on_new_sample (GstMFTransform * object,
1456 IMFSample * sample, GstMFVideoEncoder * self)
1458 GST_LOG_OBJECT (self, "New Sample callback");
1460 /* NOTE: this callback will be called from Media Foundation's internal
1461 * worker queue thread */
1462 GST_VIDEO_ENCODER_STREAM_LOCK (self);
1463 gst_mf_video_encoder_finish_sample (self, sample);
1464 GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
1472 const gchar *profile_str;
1473 } GstMFVideoEncoderProfileMap;
1476 gst_mf_video_encoder_enum_internal (GstMFTransform * transform, GUID & subtype,
1477 GstObject * d3d11_device, GstMFVideoEncoderDeviceCaps * device_caps,
1478 GstCaps ** sink_template, GstCaps ** src_template)
1481 MFT_REGISTER_TYPE_INFO *infos;
1484 GstCaps *src_caps = nullptr;
1485 GstCaps *sink_caps = nullptr;
1486 GstCaps *d3d11_caps = nullptr;
1487 GValue *supported_formats = nullptr;
1488 GValue *profiles = nullptr;
1489 gboolean have_I420 = FALSE;
1490 gboolean have_NV12 = FALSE;
1491 gboolean have_P010 = FALSE;
1492 #if GST_MF_HAVE_D3D11
1493 gboolean d3d11_aware = FALSE;
1495 gchar *device_name = nullptr;
1496 IMFActivate *activate;
1497 IMFTransform *encoder;
1498 ICodecAPI *codec_api;
1499 ComPtr < IMFMediaType > out_type;
1500 GstMFVideoEncoderProfileMap h264_profile_map[] = {
1501 {eAVEncH264VProfile_High, "high"},
1502 {eAVEncH264VProfile_Main, "main"},
1503 {eAVEncH264VProfile_Base, "baseline"},
1506 GstMFVideoEncoderProfileMap hevc_profile_map[] = {
1507 {eAVEncH265VProfile_Main_420_8, "main"},
1508 {eAVEncH265VProfile_Main_420_10, "main-10"},
1511 GstMFVideoEncoderProfileMap *profile_to_check = nullptr;
1512 static const gchar *h264_caps_str =
1513 "video/x-h264, stream-format=(string) byte-stream, alignment=(string) au";
1514 static const gchar *hevc_caps_str =
1515 "video/x-h265, stream-format=(string) byte-stream, alignment=(string) au";
1516 static const gchar *vp9_caps_str = "video/x-vp9";
1517 const gchar *codec_caps_str = nullptr;
1519 /* NOTE: depending on environment,
1520 * some enumerated h/w MFT might not be usable (e.g., multiple GPU case) */
1521 if (!gst_mf_transform_open (transform))
1524 activate = gst_mf_transform_get_activate_handle (transform);
1526 GST_WARNING_OBJECT (transform, "No IMFActivate interface available");
1530 encoder = gst_mf_transform_get_transform_handle (transform);
1532 GST_WARNING_OBJECT (transform, "No IMFTransform interface available");
1536 codec_api = gst_mf_transform_get_codec_api_handle (transform);
1538 GST_WARNING_OBJECT (transform, "No ICodecAPI interface available");
1542 g_object_get (transform, "device-name", &device_name, nullptr);
1544 GST_WARNING_OBJECT (transform, "Unknown device name");
1547 g_free (device_name);
1549 hr = activate->GetAllocatedBlob (MFT_INPUT_TYPES_Attributes,
1550 (UINT8 **) & infos, &info_size);
1551 if (!gst_mf_result (hr))
1554 for (i = 0; i < info_size / sizeof (MFT_REGISTER_TYPE_INFO); i++) {
1555 GstVideoFormat format;
1556 const GstVideoFormatInfo *format_info;
1557 GValue val = G_VALUE_INIT;
1559 format = gst_mf_video_subtype_to_video_format (&infos[i].guidSubtype);
1560 if (format == GST_VIDEO_FORMAT_UNKNOWN)
1563 format_info = gst_video_format_get_info (format);
1564 if (GST_VIDEO_FORMAT_INFO_IS_RGB (format_info)) {
1565 GST_DEBUG_OBJECT (transform, "Skip %s format",
1566 GST_VIDEO_FORMAT_INFO_NAME (format_info));
1570 if (!supported_formats) {
1571 supported_formats = g_new0 (GValue, 1);
1572 g_value_init (supported_formats, GST_TYPE_LIST);
1576 /* media foundation has duplicated formats IYUV and I420 */
1577 case GST_VIDEO_FORMAT_I420:
1583 case GST_VIDEO_FORMAT_NV12:
1586 case GST_VIDEO_FORMAT_P010_10LE:
1593 g_value_init (&val, G_TYPE_STRING);
1594 g_value_set_static_string (&val, gst_video_format_to_string (format));
1595 gst_value_list_append_and_take_value (supported_formats, &val);
1597 CoTaskMemFree (infos);
1599 if (!supported_formats) {
1600 GST_WARNING_OBJECT (transform, "Couldn't figure out supported format");
1604 if (IsEqualGUID (MFVideoFormat_H264, subtype)) {
1605 profile_to_check = h264_profile_map;
1606 codec_caps_str = h264_caps_str;
1607 } else if (IsEqualGUID (MFVideoFormat_HEVC, subtype)) {
1608 profile_to_check = hevc_profile_map;
1609 codec_caps_str = hevc_caps_str;
1610 } else if (IsEqualGUID (MFVideoFormat_VP90, subtype)) {
1611 codec_caps_str = vp9_caps_str;
1613 g_assert_not_reached ();
1617 if (profile_to_check) {
1618 hr = MFCreateMediaType (&out_type);
1619 if (!gst_mf_result (hr))
1622 hr = out_type->SetGUID (MF_MT_MAJOR_TYPE, MFMediaType_Video);
1623 if (!gst_mf_result (hr))
1626 hr = out_type->SetGUID (MF_MT_SUBTYPE, subtype);
1627 if (!gst_mf_result (hr))
1630 hr = out_type->SetUINT32 (MF_MT_AVG_BITRATE, 2048000);
1631 if (!gst_mf_result (hr))
1634 hr = MFSetAttributeRatio (out_type.Get (), MF_MT_FRAME_RATE, 30, 1);
1635 if (!gst_mf_result (hr))
1638 hr = out_type->SetUINT32 (MF_MT_INTERLACE_MODE,
1639 MFVideoInterlace_Progressive);
1640 if (!gst_mf_result (hr))
1643 hr = MFSetAttributeSize (out_type.Get (), MF_MT_FRAME_SIZE, 1920, 1080);
1644 if (!gst_mf_result (hr))
1649 GValue profile_val = G_VALUE_INIT;
1650 guint mf_profile = profile_to_check[i].profile;
1651 const gchar *profile_str = profile_to_check[i].profile_str;
1655 if (mf_profile == 0)
1658 g_assert (profile_str != nullptr);
1660 hr = out_type->SetUINT32 (MF_MT_MPEG2_PROFILE, mf_profile);
1661 if (!gst_mf_result (hr))
1664 if (!gst_mf_transform_set_output_type (transform, out_type.Get ()))
1668 profiles = g_new0 (GValue, 1);
1669 g_value_init (profiles, GST_TYPE_LIST);
1672 /* Add "constrained-baseline" in addition to "baseline" */
1673 if (profile_str == "baseline") {
1674 g_value_init (&profile_val, G_TYPE_STRING);
1675 g_value_set_static_string (&profile_val, "constrained-baseline");
1676 gst_value_list_append_and_take_value (profiles, &profile_val);
1679 g_value_init (&profile_val, G_TYPE_STRING);
1680 g_value_set_static_string (&profile_val, profile_str);
1681 gst_value_list_append_and_take_value (profiles, &profile_val);
1685 GST_WARNING_OBJECT (transform, "Couldn't query supported profile");
1690 src_caps = gst_caps_from_string (codec_caps_str);
1692 gst_caps_set_value (src_caps, "profile", profiles);
1693 g_value_unset (profiles);
1697 sink_caps = gst_caps_new_empty_simple ("video/x-raw");
1698 /* FIXME: don't hardcode max resolution, but MF doesn't provide
1699 * API for querying supported max resolution... */
1701 GValue res_val = G_VALUE_INIT;
1702 g_value_init (&res_val, GST_TYPE_INT_RANGE);
1703 gst_value_set_int_range_step (&res_val, 64, 8192, 2);
1705 gst_caps_set_value (sink_caps, "width", &res_val);
1706 gst_caps_set_value (sink_caps, "heigh", &res_val);
1707 gst_caps_set_value (src_caps, "width", &res_val);
1708 gst_caps_set_value (src_caps, "heigh", &res_val);
1710 g_value_unset (&res_val);
1712 #if GST_MF_HAVE_D3D11
1713 /* Check whether this MFT can support D3D11 */
1714 if (d3d11_device && (have_NV12 || have_P010)) {
1715 g_object_get (transform, "d3d11-aware", &d3d11_aware, nullptr);
1716 GST_DEBUG_OBJECT (transform, "d3d11 aware %d", d3d11_aware);
1719 if (d3d11_device && (have_NV12 || have_P010) && d3d11_aware) {
1720 gint64 adapter_luid = 0;
1721 GValue d3d11_formats = G_VALUE_INIT;
1723 g_object_get (d3d11_device, "adapter-luid", &adapter_luid, nullptr);
1725 d3d11_caps = gst_caps_copy (sink_caps);
1727 g_value_init (&d3d11_formats, GST_TYPE_LIST);
1729 GValue val = G_VALUE_INIT;
1730 g_value_init (&val, G_TYPE_STRING);
1731 g_value_set_static_string (&val, "NV12");
1732 gst_value_list_append_and_take_value (&d3d11_formats, &val);
1736 GValue val = G_VALUE_INIT;
1737 g_value_init (&val, G_TYPE_STRING);
1738 g_value_set_static_string (&val, "P010_10LE");
1739 gst_value_list_append_and_take_value (&d3d11_formats, &val);
1742 gst_caps_set_value (d3d11_caps, "format", &d3d11_formats);
1743 g_value_unset (&d3d11_formats);
1744 gst_caps_set_features_simple (d3d11_caps,
1745 gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY));
1746 device_caps->d3d11_aware = TRUE;
1747 device_caps->adapter_luid = adapter_luid;
1751 gst_caps_set_value (sink_caps, "format", supported_formats);
1752 g_value_unset (supported_formats);
1753 g_free (supported_formats);
1756 gst_caps_append (sink_caps, d3d11_caps);
1758 *sink_template = sink_caps;
1759 *src_template = src_caps;
1761 #define CHECK_DEVICE_CAPS(codec_obj,api,val) \
1762 if (SUCCEEDED((codec_obj)->IsSupported(&(api)))) {\
1763 (device_caps)->val = TRUE; \
1766 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncCommonRateControlMode, rc_mode);
1767 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncCommonQuality, quality);
1768 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncAdaptiveMode, adaptive_mode);
1769 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncCommonBufferSize, buffer_size);
1770 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncCommonMeanBitRate, mean_bitrate);
1771 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncCommonMaxBitRate, max_bitrate);
1772 CHECK_DEVICE_CAPS (codec_api,
1773 CODECAPI_AVEncCommonQualityVsSpeed, quality_vs_speed);
1774 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncH264CABACEnable, cabac);
1775 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncH264SPSID, sps_id);
1776 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncH264PPSID, pps_id);
1777 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncMPVDefaultBPictureCount, bframes);
1778 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncMPVGOPSize, gop_size);
1779 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncNumWorkerThreads, threads);
1780 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncVideoContentType, content_type);
1781 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncVideoEncodeQP, qp);
1782 CHECK_DEVICE_CAPS (codec_api,
1783 CODECAPI_AVEncVideoForceKeyFrame, force_keyframe);
1784 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVLowLatencyMode, low_latency);
1785 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncVideoMinQP, min_qp);
1786 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncVideoMaxQP, max_qp);
1787 CHECK_DEVICE_CAPS (codec_api,
1788 CODECAPI_AVEncVideoEncodeFrameTypeQP, frame_type_qp);
1789 CHECK_DEVICE_CAPS (codec_api, CODECAPI_AVEncVideoMaxNumRefFrame, max_num_ref);
1790 if (device_caps->max_num_ref) {
1795 hr = codec_api->GetParameterRange (&CODECAPI_AVEncVideoMaxNumRefFrame,
1797 if (SUCCEEDED (hr)) {
1798 device_caps->max_num_ref_high = max.uiVal;
1799 device_caps->max_num_ref_low = min.uiVal;
1800 VariantClear (&min);
1801 VariantClear (&max);
1802 VariantClear (&step);
1804 device_caps->max_num_ref = FALSE;
1807 #undef CHECK_DEVICE_CAPS
1812 static GstMFTransform *
1813 gst_mf_video_encoder_enum (guint enum_flags, GUID * subtype, guint device_index,
1814 GstMFVideoEncoderDeviceCaps * device_caps, GstObject * d3d11_device,
1815 GstCaps ** sink_template, GstCaps ** src_template)
1817 GstMFTransformEnumParams enum_params = { 0, };
1818 MFT_REGISTER_TYPE_INFO output_type;
1819 GstMFTransform *transform;
1820 gint64 adapter_luid = 0;
1822 *sink_template = nullptr;
1823 *src_template = nullptr;
1824 memset (device_caps, 0, sizeof (GstMFVideoEncoderDeviceCaps));
1826 if (!IsEqualGUID (MFVideoFormat_H264, *subtype) &&
1827 !IsEqualGUID (MFVideoFormat_HEVC, *subtype) &&
1828 !IsEqualGUID (MFVideoFormat_VP90, *subtype)) {
1829 GST_ERROR ("Unknown subtype GUID");
1835 g_object_get (d3d11_device, "adapter-luid", &adapter_luid, nullptr);
1836 if (!adapter_luid) {
1837 GST_ERROR ("Couldn't get adapter LUID");
1842 output_type.guidMajorType = MFMediaType_Video;
1843 output_type.guidSubtype = *subtype;
1845 enum_params.category = MFT_CATEGORY_VIDEO_ENCODER;
1846 enum_params.output_typeinfo = &output_type;
1847 enum_params.device_index = device_index;
1848 enum_params.enum_flags = enum_flags;
1849 enum_params.adapter_luid = adapter_luid;
1851 transform = gst_mf_transform_new (&enum_params);
1855 gst_mf_video_encoder_enum_internal (transform, output_type.guidSubtype,
1856 d3d11_device, device_caps, sink_template, src_template);
1862 gst_mf_video_encoder_register_internal (GstPlugin * plugin, guint rank,
1863 GUID * subtype, GTypeInfo * type_info,
1864 const GstMFVideoEncoderDeviceCaps * device_caps,
1865 guint32 enum_flags, guint device_index, GstMFTransform * transform,
1866 GstCaps * sink_caps, GstCaps * src_caps)
1869 GTypeInfo local_type_info;
1871 gchar *feature_name;
1873 GstMFVideoEncoderClassData *cdata;
1874 gboolean is_default = TRUE;
1875 gchar *device_name = nullptr;
1876 const gchar *type_name_prefix = nullptr;
1877 const gchar *feature_name_prefix = nullptr;
1879 if (IsEqualGUID (MFVideoFormat_H264, *subtype)) {
1880 type_name_prefix = "H264";
1881 feature_name_prefix = "h264";
1882 } else if (IsEqualGUID (MFVideoFormat_HEVC, *subtype)) {
1883 type_name_prefix = "H265";
1884 feature_name_prefix = "h265";
1885 } else if (IsEqualGUID (MFVideoFormat_VP90, *subtype)) {
1886 type_name_prefix = "VP9";
1887 feature_name_prefix = "vp9";
1889 g_assert_not_reached ();
1893 /* Must be checked already */
1894 g_object_get (transform, "device-name", &device_name, nullptr);
1895 g_assert (device_name != nullptr);
1897 cdata = g_new0 (GstMFVideoEncoderClassData, 1);
1898 cdata->sink_caps = gst_caps_copy (sink_caps);
1899 cdata->src_caps = gst_caps_copy (src_caps);
1900 cdata->device_name = device_name;
1901 cdata->device_caps = *device_caps;
1902 cdata->enum_flags = enum_flags;
1903 cdata->device_index = device_index;
1905 local_type_info = *type_info;
1906 local_type_info.class_data = cdata;
1908 GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps,
1909 GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
1910 GST_MINI_OBJECT_FLAG_SET (cdata->src_caps,
1911 GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
1913 type_name = g_strdup_printf ("GstMF%sEnc", type_name_prefix);
1914 feature_name = g_strdup_printf ("mf%senc", feature_name_prefix);
1917 while (g_type_from_name (type_name) != 0) {
1919 g_free (feature_name);
1920 type_name = g_strdup_printf ("GstMF%sDevice%dEnc", type_name_prefix, i);
1921 feature_name = g_strdup_printf ("mf%sdevice%denc", feature_name_prefix, i);
1926 cdata->is_default = is_default;
1929 g_type_register_static (GST_TYPE_MF_VIDEO_ENCODER, type_name,
1930 &local_type_info, (GTypeFlags) 0);
1932 /* make lower rank than default device */
1933 if (rank > 0 && !is_default)
1936 if (!is_default || !device_caps->d3d11_aware)
1937 gst_element_type_set_skip_documentation (type);
1939 if (!gst_element_register (plugin, feature_name, rank, type))
1940 GST_WARNING ("Failed to register plugin '%s'", type_name);
1943 g_free (feature_name);
1947 gst_mf_video_encoder_register (GstPlugin * plugin, guint rank, GUID * subtype,
1948 GTypeInfo * type_info, GList * d3d11_device)
1950 GstMFTransform *transform = nullptr;
1951 GstCaps *sink_template = nullptr;
1952 GstCaps *src_template = nullptr;
1954 GstMFVideoEncoderDeviceCaps device_caps;
1957 /* register hardware encoders first */
1958 enum_flags = (MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_ASYNCMFT |
1959 MFT_ENUM_FLAG_SORTANDFILTER_APPROVED_ONLY);
1963 for (iter = d3d11_device; iter; iter = g_list_next (iter)) {
1964 GstObject *device = (GstObject *) iter->data;
1967 gst_mf_video_encoder_enum (enum_flags, subtype, 0, &device_caps,
1968 device, &sink_template, &src_template);
1973 /* Failed to open MFT */
1974 if (!sink_template) {
1975 gst_clear_object (&transform);
1979 gst_mf_video_encoder_register_internal (plugin, rank, subtype, type_info,
1980 &device_caps, enum_flags, 0, transform, sink_template, src_template);
1981 gst_clear_object (&transform);
1982 gst_clear_caps (&sink_template);
1983 gst_clear_caps (&src_template);
1986 /* AMD seems to be able to support up to 12 GPUs */
1987 for (i = 0; i < 12; i++) {
1989 gst_mf_video_encoder_enum (enum_flags, subtype, i, &device_caps,
1990 nullptr, &sink_template, &src_template);
1992 /* No more MFT to enumerate */
1996 /* Failed to open MFT */
1997 if (!sink_template) {
1998 gst_clear_object (&transform);
2002 gst_mf_video_encoder_register_internal (plugin, rank, subtype, type_info,
2003 &device_caps, enum_flags, i, transform, sink_template, src_template);
2004 gst_clear_object (&transform);
2005 gst_clear_caps (&sink_template);
2006 gst_clear_caps (&src_template);
2010 /* register software encoders */
2011 enum_flags = (MFT_ENUM_FLAG_SYNCMFT |
2012 MFT_ENUM_FLAG_SORTANDFILTER_APPROVED_ONLY);
2014 transform = gst_mf_video_encoder_enum (enum_flags, subtype, 0, &device_caps,
2015 nullptr, &sink_template, &src_template);
2023 gst_mf_video_encoder_register_internal (plugin, rank, subtype, type_info,
2024 &device_caps, enum_flags, 0, transform, sink_template, src_template);
2027 gst_clear_object (&transform);
2028 gst_clear_caps (&sink_template);
2029 gst_clear_caps (&src_template);