34e510ce2635df5629db378138b66e90269de53a
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / mediafoundation / gstmfvideoencoder.cpp
1 /* GStreamer
2  * Copyright (C) 2020 Seungha Yang <seungha.yang@navercorp.com>
3  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <gst/gst.h>
26 #include "gstmfvideoencoder.h"
27 #include "gstmfvideobuffer.h"
28 #include "gstmfplatloader.h"
29 #include <wrl.h>
30 #include <string.h>
31 #include <cmath>
32
33 #if GST_MF_HAVE_D3D11
34 #include <d3d10.h>
35 #endif
36
37 /* *INDENT-OFF* */
38 using namespace Microsoft::WRL;
39 /* *INDENT-ON* */
40
41 GST_DEBUG_CATEGORY_EXTERN (gst_mf_video_encoder_debug);
42 #define GST_CAT_DEFAULT gst_mf_video_encoder_debug
43
44 /**
45  * GstMFVideoEncoder:
46  *
47  * Base class for MediaFoundation video encoders
48  *
49  * Since: 1.22
50  */
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);
54
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,
68     GstQuery * query);
69 static gboolean gst_mf_video_encoder_sink_query (GstVideoEncoder * enc,
70     GstQuery * query);
71 static gboolean gst_mf_video_encoder_src_query (GstVideoEncoder * enc,
72     GstQuery * query);
73
74 static HRESULT gst_mf_video_on_new_sample (GstMFTransform * object,
75     IMFSample * sample, GstMFVideoEncoder * self);
76
77 static void
78 gst_mf_video_encoder_class_init (GstMFVideoEncoderClass * klass)
79 {
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);
83
84   gobject_class->dispose = gst_mf_video_encoder_dispose;
85
86   element_class->set_context =
87       GST_DEBUG_FUNCPTR (gst_mf_video_encoder_set_context);
88
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);
104
105   gst_type_mark_as_plugin_api (GST_TYPE_MF_VIDEO_ENCODER,
106       (GstPluginAPIFlags) 0);
107 }
108
109 static void
110 gst_mf_video_encoder_init (GstMFVideoEncoder * self)
111 {
112 }
113
114 static void
115 gst_mf_video_encoder_dispose (GObject * object)
116 {
117 #if GST_MF_HAVE_D3D11
118   GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (object);
119
120   gst_clear_object (&self->d3d11_device);
121   gst_clear_object (&self->other_d3d11_device);
122 #endif
123
124   G_OBJECT_CLASS (parent_class)->dispose (object);
125 }
126
127 static void
128 gst_mf_video_encoder_set_context (GstElement * element, GstContext * context)
129 {
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;
134
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);
138   }
139 #endif
140
141   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
142 }
143
144 static gboolean
145 gst_mf_video_encoder_open (GstVideoEncoder * enc)
146 {
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;
152
153 #if GST_MF_HAVE_D3D11
154   if (device_caps->d3d11_aware) {
155     HRESULT hr;
156     ID3D11Device *device_handle;
157     ComPtr < ID3D10Multithread > multi_thread;
158     GstD3D11Device *device;
159
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");
163       return FALSE;
164     }
165
166     /* Create our own device with D3D11_CREATE_DEVICE_VIDEO_SUPPORT */
167     self->d3d11_device =
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);
173       return FALSE;
174     }
175
176     device = self->d3d11_device;
177
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);
184       return FALSE;
185     }
186
187     device_handle = gst_d3d11_device_get_device_handle (device);
188     /* Enable multi thread protection as this device will be shared with
189      * MFT */
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);
196     }
197
198     multi_thread->SetMultithreadProtected (TRUE);
199
200     hr = self->device_manager->ResetDevice ((IUnknown *) device_handle,
201         self->reset_token);
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);
206       return FALSE;
207     }
208   }
209 #endif
210
211   output_type.guidMajorType = MFMediaType_Video;
212   output_type.guidSubtype = klass->codec_id;
213
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;
218
219   if (device_caps->d3d11_aware)
220     enum_params.adapter_luid = device_caps->adapter_luid;
221
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);
226
227   self->transform = gst_mf_transform_new (&enum_params);
228   if (!self->transform) {
229     GST_ERROR_OBJECT (self, "Cannot create MFT object");
230     return FALSE;
231   }
232
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);
242   } else {
243     self->async_mft = FALSE;
244   }
245
246   return TRUE;
247 }
248
249 static gboolean
250 gst_mf_video_encoder_close (GstVideoEncoder * enc)
251 {
252   GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
253
254   gst_clear_object (&self->transform);
255
256   if (self->input_state) {
257     gst_video_codec_state_unref (self->input_state);
258     self->input_state = nullptr;
259   }
260 #if GST_MF_HAVE_D3D11
261   if (self->device_manager) {
262     self->device_manager->Release ();
263     self->device_manager = nullptr;
264   }
265
266   if (self->mf_allocator) {
267     self->mf_allocator->UninitializeSampleAllocator ();
268     self->mf_allocator->Release ();
269     self->mf_allocator = nullptr;
270   }
271
272   gst_clear_object (&self->other_d3d11_device);
273   gst_clear_object (&self->d3d11_device);
274   gst_clear_d3d11_fence (&self->fence);
275 #endif
276
277   return TRUE;
278 }
279
280 static gboolean
281 gst_mf_video_encoder_start (GstVideoEncoder * enc)
282 {
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);
286
287   return TRUE;
288 }
289
290 static gboolean
291 gst_mf_video_encoder_init_mft (GstMFVideoEncoder * self)
292 {
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;
299   GList *iter;
300   HRESULT hr;
301   gint fps_n, fps_d;
302
303   GST_DEBUG_OBJECT (self, "Set format");
304
305   gst_mf_video_encoder_finish (GST_VIDEO_ENCODER (self));
306
307   self->mf_pts_offset = 0;
308   self->has_reorder_frame = FALSE;
309   self->last_ret = GST_FLOW_OK;
310
311   if (!gst_mf_transform_open (self->transform)) {
312     GST_ERROR_OBJECT (self, "Failed to open MFT");
313     return FALSE;
314   }
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");
320       return FALSE;
321     } else {
322       GST_DEBUG_OBJECT (self, "set device manager done");
323     }
324   }
325 #endif
326
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;
331   } else {
332     self->need_align = FALSE;
333   }
334
335   hr = MFCreateMediaType (&out_type);
336   if (!gst_mf_result (hr))
337     return FALSE;
338
339   hr = out_type->SetGUID (MF_MT_MAJOR_TYPE, MFMediaType_Video);
340   if (!gst_mf_result (hr))
341     return FALSE;
342
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");
346       return FALSE;
347     }
348   }
349
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 */
356     fps_n = 25;
357     fps_d = 1;
358   }
359
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);
364     return FALSE;
365   }
366
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);
373     return FALSE;
374   }
375
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));
381     return FALSE;
382   }
383
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);
388     return FALSE;
389   }
390
391   if (!gst_mf_transform_set_output_type (self->transform, out_type.Get ())) {
392     GST_ERROR_OBJECT (self, "Couldn't set output type");
393     return FALSE;
394   }
395
396   if (!gst_mf_transform_get_input_available_types (self->transform,
397           &input_types)) {
398     GST_ERROR_OBJECT (self, "Couldn't get available input types");
399     return FALSE;
400   }
401
402   for (iter = input_types; iter; iter = g_list_next (iter)) {
403     GstVideoFormat format;
404     GUID subtype;
405     IMFMediaType *type = (IMFMediaType *) iter->data;
406
407     hr = type->GetGUID (MF_MT_SUBTYPE, &subtype);
408     if (!gst_mf_result (hr))
409       continue;
410
411     format = gst_mf_video_subtype_to_video_format (&subtype);
412     if (format != GST_VIDEO_INFO_FORMAT (info))
413       continue;
414
415     in_type = type;
416   }
417
418   g_list_free_full (input_types, (GDestroyNotify) gst_mf_media_type_release);
419
420   if (!in_type) {
421     GST_ERROR_OBJECT (self,
422         "Couldn't convert input caps %" GST_PTR_FORMAT " to media type", caps);
423     return FALSE;
424   }
425
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));
431     return FALSE;
432   }
433
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);
438     return FALSE;
439   }
440
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));
446     return FALSE;
447   }
448
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);
452     return FALSE;
453   }
454
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");
459     return FALSE;
460   }
461
462   if (!gst_mf_transform_set_input_type (self->transform, in_type.Get ())) {
463     GST_ERROR_OBJECT (self, "Couldn't set input media type");
464     return FALSE;
465   }
466
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");
470     return FALSE;
471   }
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;
477   }
478
479   /* Check whether upstream is d3d11 element */
480   GstCapsFeatures *features;
481   ComPtr < IMFVideoSampleAllocatorEx > allocator;
482
483   features = gst_caps_get_features (caps, 0);
484
485   if (features &&
486       gst_caps_features_contains (features,
487           GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
488     GST_DEBUG_OBJECT (self, "found D3D11 memory feature");
489
490     hr = GstMFCreateVideoSampleAllocatorEx (IID_PPV_ARGS (&allocator));
491     if (!gst_mf_result (hr))
492       GST_WARNING_OBJECT (self,
493           "IMFVideoSampleAllocatorEx interface is unavailable");
494   }
495
496   if (allocator) {
497     do {
498       ComPtr < IMFAttributes > attr;
499
500       hr = MFCreateAttributes (&attr, 4);
501       if (!gst_mf_result (hr))
502         break;
503
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))
508         break;
509
510       hr = attr->SetUINT32 (GST_GUID_MF_SA_D3D11_USAGE, D3D11_USAGE_DEFAULT);
511       if (!gst_mf_result (hr))
512         break;
513
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))
517         break;
518
519       hr = attr->SetUINT32 (GST_GUID_MF_SA_D3D11_BINDFLAGS,
520           D3D11_BIND_VIDEO_ENCODER);
521       if (!gst_mf_result (hr))
522         break;
523
524       hr = allocator->SetDirectXManager (self->device_manager);
525       if (!gst_mf_result (hr))
526         break;
527
528       hr = allocator->InitializeSampleAllocatorEx (
529           /* min samples, since we are running on async mode,
530            * at least 2 samples would be required */
531           2,
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 ()
535           );
536
537       if (!gst_mf_result (hr))
538         break;
539
540       GST_DEBUG_OBJECT (self, "IMFVideoSampleAllocatorEx is initialized");
541
542       self->mf_allocator = allocator.Detach ();
543     } while (0);
544   }
545 #endif
546
547   return TRUE;
548 }
549
550 static gboolean
551 gst_mf_video_encoder_set_format (GstVideoEncoder * enc,
552     GstVideoCodecState * state)
553 {
554   GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
555   GST_DEBUG_OBJECT (self, "Set format");
556
557   if (self->input_state)
558     gst_video_codec_state_unref (self->input_state);
559   self->input_state = gst_video_codec_state_ref (state);
560
561   return gst_mf_video_encoder_init_mft (self);
562 }
563
564 static void
565 gst_mf_video_buffer_free (GstVideoFrame * frame)
566 {
567   if (!frame)
568     return;
569
570   gst_video_frame_unmap (frame);
571   g_free (frame);
572 }
573
574 static gboolean
575 gst_mf_video_encoder_frame_needs_copy (GstVideoFrame * vframe)
576 {
577   /* Single plane data can be used without copy */
578   if (GST_VIDEO_FRAME_N_PLANES (vframe) == 1)
579     return FALSE;
580
581   switch (GST_VIDEO_FRAME_FORMAT (vframe)) {
582     case GST_VIDEO_FORMAT_I420:
583     {
584       guint8 *data, *other_data;
585       guint size;
586
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)) {
593         return TRUE;
594       }
595
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))
600         return TRUE;
601
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)
605         return TRUE;
606
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))
611         return TRUE;
612
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)
616         return TRUE;
617
618       return FALSE;
619     }
620     case GST_VIDEO_FORMAT_NV12:
621     case GST_VIDEO_FORMAT_P010_10LE:
622     case GST_VIDEO_FORMAT_P016_LE:
623     {
624       guint8 *data, *other_data;
625       guint size;
626
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)) {
631         return TRUE;
632       }
633
634       size = GST_VIDEO_FRAME_PLANE_STRIDE (vframe, 0) *
635           GST_VIDEO_FRAME_HEIGHT (vframe);
636
637       /* Unexpected padding */
638       if (size + GST_VIDEO_FRAME_PLANE_OFFSET (vframe, 0) !=
639           GST_VIDEO_FRAME_PLANE_OFFSET (vframe, 1))
640         return TRUE;
641
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)
645         return TRUE;
646
647       return FALSE;
648     }
649     default:
650       g_assert_not_reached ();
651       return TRUE;
652   }
653
654   return TRUE;
655 }
656
657 typedef struct
658 {
659   LONGLONG mf_pts;
660 } GstMFVideoEncoderFrameData;
661
662 static gboolean
663 gst_mf_video_encoder_process_input (GstMFVideoEncoder * self,
664     GstVideoCodecFrame * frame, IMFSample * sample)
665 {
666   GstMFVideoEncoderClass *klass = GST_MF_VIDEO_ENCODER_GET_CLASS (self);
667   HRESULT hr;
668   gboolean unset_force_keyframe = FALSE;
669   GstMFVideoEncoderFrameData *frame_data = nullptr;
670   gboolean res;
671
672   frame_data = g_new0 (GstMFVideoEncoderFrameData, 1);
673   frame_data->mf_pts = frame->pts / 100;
674
675   gst_video_codec_frame_set_user_data (frame,
676       frame_data, (GDestroyNotify) g_free);
677
678   hr = sample->SetSampleTime (frame_data->mf_pts);
679   if (!gst_mf_result (hr))
680     return FALSE;
681
682   hr = sample->
683       SetSampleDuration (GST_CLOCK_TIME_IS_VALID (frame->duration) ? frame->
684       duration / 100 : 0);
685   if (!gst_mf_result (hr))
686     return FALSE;
687
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);
693     } else {
694       GST_WARNING_OBJECT (self, "encoder does not support force keyframe");
695     }
696   }
697
698   /* Unlock temporary so that we can output frame from Media Foundation's
699    * worker thread.
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 */
704   if (self->async_mft)
705     GST_VIDEO_ENCODER_STREAM_UNLOCK (self);
706   res = gst_mf_transform_process_input (self->transform, sample);
707   if (self->async_mft)
708     GST_VIDEO_ENCODER_STREAM_LOCK (self);
709
710   if (unset_force_keyframe) {
711     gst_mf_transform_set_codec_api_uint32 (self->transform,
712         &CODECAPI_AVEncVideoForceKeyFrame, FALSE);
713   }
714
715   if (!res) {
716     GST_ERROR_OBJECT (self, "Failed to process input");
717     return FALSE;
718   }
719
720   return TRUE;
721 }
722
723 static GstVideoCodecFrame *
724 gst_mf_video_encoder_find_output_frame (GstMFVideoEncoder * self,
725     LONGLONG mf_pts)
726 {
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;
731
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);
736     LONGLONG abs_diff;
737
738     if (!data)
739       continue;
740
741     if (mf_pts == data->mf_pts) {
742       ret = frame;
743       break;
744     }
745
746     abs_diff = std::abs (mf_pts - data->mf_pts);
747
748     if (!closest || abs_diff < min_pts_abs_diff) {
749       closest = frame;
750       min_pts_abs_diff = abs_diff;
751     }
752   }
753
754   if (!ret && closest)
755     ret = closest;
756
757   if (ret) {
758     gst_video_codec_frame_ref (ret);
759   } else {
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));
766   }
767
768   if (walk)
769     g_list_free_full (walk, (GDestroyNotify) gst_video_codec_frame_unref);
770
771   return ret;
772 }
773
774 static HRESULT
775 gst_mf_video_encoder_finish_sample (GstMFVideoEncoder * self,
776     IMFSample * sample)
777 {
778   HRESULT hr = S_OK;
779   BYTE *data;
780   ComPtr < IMFMediaBuffer > media_buffer;
781   GstBuffer *buffer;
782   GstFlowReturn res = GST_FLOW_ERROR;
783   GstVideoCodecFrame *frame;
784   LONGLONG sample_timestamp;
785   LONGLONG sample_duration;
786   LONGLONG target_mf_pts;
787   UINT64 mf_dts;
788   UINT32 keyframe = FALSE;
789   DWORD buffer_len;
790   GstClockTime pts, dts, duration;
791
792   hr = sample->GetBufferByIndex (0, &media_buffer);
793   if (!gst_mf_result (hr))
794     goto done;
795
796   hr = media_buffer->Lock (&data, nullptr, &buffer_len);
797   if (!gst_mf_result (hr))
798     goto done;
799
800   buffer = gst_buffer_new_allocate (nullptr, buffer_len, nullptr);
801   gst_buffer_fill (buffer, 0, data, buffer_len);
802   media_buffer->Unlock ();
803
804   sample->GetSampleTime (&sample_timestamp);
805   target_mf_pts = sample_timestamp;
806   sample->GetSampleDuration (&sample_duration);
807   sample->GetUINT32 (MFSampleExtension_CleanPoint, &keyframe);
808
809   hr = sample->GetUINT64 (MFSampleExtension_DecodeTimestamp, &mf_dts);
810   if (FAILED (hr)) {
811     mf_dts = sample_timestamp;
812     hr = S_OK;
813   }
814
815   pts = sample_timestamp * 100;
816   dts = mf_dts * 100;
817   duration = sample_duration * 100;
818
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));
822
823   /* NOTE: When B-frame is enabled, MFT shows following pattern
824    * (input timestamp starts from 1000:00:00.000000000, and 30fps)
825    *
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
830    *
831    * - Sounds MFT doesn't support negative timestamp, so PTS of each frame seems
832    *   to be shifthed
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
836    *
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
846    */
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);
859       } else {
860         GST_WARNING_OBJECT (self, "Cannot calculate PTS offset");
861       }
862
863       self->mf_pts_offset = mf_pts_offset;
864     }
865
866     if (self->mf_pts_offset > 0) {
867       target_mf_pts -= self->mf_pts_offset;
868
869       pts -= (self->mf_pts_offset * 100);
870       /* +1 to compensate timescale difference */
871       dts -= ((self->mf_pts_offset + 1) * 100);
872     }
873   }
874
875   frame = gst_mf_video_encoder_find_output_frame (self, target_mf_pts);
876
877   if (frame) {
878     if (keyframe) {
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);
882     }
883
884     frame->output_buffer = buffer;
885
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) {
889       frame->dts = dts;
890     } else {
891       frame->dts = frame->pts;
892     }
893
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));
900
901       /* XXX: just set clock-time-none? */
902       frame->dts = frame->pts;
903     }
904
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));
908
909     res = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (self), frame);
910   } else {
911     GST_BUFFER_PTS (buffer) = pts;
912     GST_BUFFER_DTS (buffer) = dts;
913     GST_BUFFER_DURATION (buffer) = duration;
914
915     if (keyframe) {
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);
919     } else {
920       GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
921     }
922
923     GST_LOG_OBJECT (self, "Buffer pts %" GST_TIME_FORMAT ", Buffer DTS %"
924         GST_TIME_FORMAT, GST_TIME_ARGS (pts), GST_TIME_ARGS (dts));
925
926     res = gst_pad_push (GST_VIDEO_ENCODER_SRC_PAD (self), buffer);
927   }
928
929 done:
930   self->last_ret = res;
931
932   return hr;
933 }
934
935 static GstFlowReturn
936 gst_mf_video_encoder_process_output (GstMFVideoEncoder * self)
937 {
938   ComPtr < IMFSample > sample;
939   GstFlowReturn res = GST_FLOW_ERROR;
940
941   res = gst_mf_transform_get_output (self->transform, &sample);
942
943   if (res != GST_FLOW_OK)
944     return res;
945
946   gst_mf_video_encoder_finish_sample (self, sample.Get ());
947
948   return self->last_ret;
949 }
950
951 static gboolean
952 gst_mf_video_encoder_create_input_sample (GstMFVideoEncoder * self,
953     GstVideoCodecFrame * frame, IMFSample ** sample)
954 {
955   HRESULT hr;
956   ComPtr < IMFSample > new_sample;
957   ComPtr < IMFMediaBuffer > media_buffer;
958   ComPtr < IGstMFVideoBuffer > video_buffer;
959   GstVideoInfo *info = &self->input_state->info;
960   gint i, j;
961   GstVideoFrame *vframe = nullptr;
962   BYTE *data = nullptr;
963   gboolean need_copy = self->need_align;
964
965   vframe = g_new0 (GstVideoFrame, 1);
966
967   if (!gst_video_frame_map (vframe, info, frame->input_buffer, GST_MAP_READ)) {
968     GST_ERROR_OBJECT (self, "Couldn't map input frame");
969     g_free (vframe);
970     return FALSE;
971   }
972
973   hr = MFCreateSample (&new_sample);
974   if (!gst_mf_result (hr))
975     goto error;
976
977   /* Check if we can forward this memory to Media Foundation without copy */
978   if (!need_copy)
979     need_copy = gst_mf_video_encoder_frame_needs_copy (vframe);
980
981   if (need_copy) {
982     GST_TRACE_OBJECT (self, "Copy input buffer into Media Foundation memory");
983     hr = MFCreateMemoryBuffer (GST_VIDEO_INFO_SIZE (info), &media_buffer);
984   } else {
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);
989   }
990
991   if (!gst_mf_result (hr))
992     goto error;
993
994   if (!need_copy) {
995     hr = media_buffer.As (&video_buffer);
996     if (!gst_mf_result (hr))
997       goto error;
998   } else {
999     hr = media_buffer->Lock (&data, nullptr, nullptr);
1000     if (!gst_mf_result (hr))
1001       goto error;
1002
1003     for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
1004       guint8 *src, *dst;
1005       gint src_stride, dst_stride;
1006       gint width;
1007
1008       src = (guint8 *) GST_VIDEO_FRAME_PLANE_DATA (vframe, i);
1009       dst = data + GST_VIDEO_INFO_PLANE_OFFSET (info, i);
1010
1011       src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (vframe, i);
1012       dst_stride = GST_VIDEO_INFO_PLANE_STRIDE (info, i);
1013
1014       width = GST_VIDEO_INFO_COMP_WIDTH (info, i)
1015           * GST_VIDEO_INFO_COMP_PSTRIDE (info, i);
1016
1017       for (j = 0; j < GST_VIDEO_INFO_COMP_HEIGHT (info, i); j++) {
1018         memcpy (dst, src, width);
1019         src += src_stride;
1020         dst += dst_stride;
1021       }
1022     }
1023
1024     media_buffer->Unlock ();
1025   }
1026
1027   hr = media_buffer->SetCurrentLength (GST_VIDEO_INFO_SIZE (info));
1028   if (!gst_mf_result (hr))
1029     goto error;
1030
1031   hr = new_sample->AddBuffer (media_buffer.Get ());
1032   if (!gst_mf_result (hr))
1033     goto error;
1034
1035   if (!need_copy) {
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))
1041       goto error;
1042   } else {
1043     gst_video_frame_unmap (vframe);
1044     g_free (vframe);
1045     vframe = nullptr;
1046   }
1047
1048   *sample = new_sample.Detach ();
1049
1050   return TRUE;
1051
1052 error:
1053   if (vframe) {
1054     gst_video_frame_unmap (vframe);
1055     g_free (vframe);
1056   }
1057
1058   return FALSE;
1059 }
1060
1061 #if GST_MF_HAVE_D3D11
1062 static gboolean
1063 gst_mf_video_encoder_create_input_sample_d3d11 (GstMFVideoEncoder * self,
1064     GstVideoCodecFrame * frame, IMFSample ** sample)
1065 {
1066   GstMFVideoEncoderClass *klass = GST_MF_VIDEO_ENCODER_GET_CLASS (self);
1067   GstMFVideoEncoderDeviceCaps *device_caps = &klass->device_caps;
1068   HRESULT hr;
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;
1076   GstMemory *mem;
1077   GstD3D11Memory *dmem;
1078   ID3D11Texture2D *texture;
1079   ID3D11Device *device_handle;
1080   ID3D11DeviceContext *context_handle;
1081   GstMapInfo info;
1082   D3D11_BOX src_box = { 0, };
1083   D3D11_TEXTURE2D_DESC dst_desc, src_desc;
1084   guint subidx;
1085   gint64 adapter_luid;
1086
1087   if (!self->mf_allocator) {
1088     GST_WARNING_OBJECT (self, "IMFVideoSampleAllocatorEx was configured");
1089     return FALSE;
1090   }
1091
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");
1095     return FALSE;
1096   }
1097
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");
1102     return FALSE;
1103   }
1104
1105   device_handle = gst_d3d11_device_get_device_handle (dmem->device);
1106   context_handle = gst_d3d11_device_get_device_context_handle (dmem->device);
1107
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");
1113     return FALSE;
1114   }
1115
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");
1119     return FALSE;
1120   }
1121
1122   hr = mf_buffer.As (&dxgi_buffer);
1123   if (!gst_mf_result (hr)) {
1124     GST_WARNING_OBJECT (self, "Couldn't get IMFDXGIBuffer from IMFMediaBuffer");
1125     return FALSE;
1126   }
1127
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");
1132     return FALSE;
1133   }
1134
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");
1139     return FALSE;
1140   }
1141
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");
1145     return FALSE;
1146   }
1147
1148   /* Allocation succeeded. Now open shared texture to access it from
1149    * other device */
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");
1154     return FALSE;
1155   }
1156
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");
1162     return FALSE;
1163   }
1164
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);
1169
1170   /* src/dst texture size might be different if padding was used.
1171    * select smaller size */
1172   src_box.left = 0;
1173   src_box.top = 0;
1174   src_box.front = 0;
1175   src_box.back = 1;
1176   src_box.right = MIN (src_desc.Width, dst_desc.Width);
1177   src_box.bottom = MIN (src_desc.Height, dst_desc.Height);
1178
1179   gst_d3d11_device_lock (dmem->device);
1180   if (self->fence && self->fence->device != dmem->device)
1181     gst_clear_d3d11_fence (&self->fence);
1182
1183   if (!self->fence)
1184     self->fence = gst_d3d11_device_create_fence (dmem->device);
1185
1186   if (!self->fence) {
1187     GST_ERROR_OBJECT (self, "Couldn't create fence object");
1188     gst_d3d11_device_unlock (dmem->device);
1189     gst_memory_unmap (mem, &info);
1190     return FALSE;
1191   }
1192
1193   context_handle->CopySubresourceRegion (shared_texture.Get (), 0, 0, 0, 0,
1194       texture, subidx, &src_box);
1195
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);
1202
1203     return FALSE;
1204   }
1205
1206   gst_d3d11_device_unlock (dmem->device);
1207   gst_memory_unmap (mem, &info);
1208
1209   *sample = new_sample.Detach ();
1210
1211   return TRUE;
1212 }
1213 #endif
1214
1215 static GstFlowReturn
1216 gst_mf_video_encoder_handle_frame (GstVideoEncoder * enc,
1217     GstVideoCodecFrame * frame)
1218 {
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);
1223
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;
1227     goto done;
1228   }
1229
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;
1234   }
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");
1239     sample = nullptr;
1240   }
1241 #endif
1242
1243   if (!sample
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;
1247     goto done;
1248   }
1249
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;
1253     goto done;
1254   }
1255
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) {
1260     do {
1261       ret = gst_mf_video_encoder_process_output (self);
1262     } while (ret == GST_FLOW_OK);
1263   }
1264
1265   if (ret == GST_MF_TRANSFORM_FLOW_NEED_DATA)
1266     ret = GST_FLOW_OK;
1267
1268 done:
1269   gst_video_codec_frame_unref (frame);
1270
1271   return ret;
1272 }
1273
1274 static GstFlowReturn
1275 gst_mf_video_encoder_finish (GstVideoEncoder * enc)
1276 {
1277   GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1278   GstFlowReturn ret = GST_FLOW_OK;
1279
1280   if (!self->transform)
1281     return GST_FLOW_OK;
1282
1283   /* Unlock temporary so that we can output frame from Media Foundation's
1284    * worker thread */
1285   if (self->async_mft)
1286     GST_VIDEO_ENCODER_STREAM_UNLOCK (enc);
1287
1288   gst_mf_transform_drain (self->transform);
1289
1290   if (self->async_mft)
1291     GST_VIDEO_ENCODER_STREAM_LOCK (enc);
1292
1293   if (!self->async_mft) {
1294     do {
1295       ret = gst_mf_video_encoder_process_output (self);
1296     } while (ret == GST_FLOW_OK);
1297   }
1298
1299   if (ret == GST_MF_TRANSFORM_FLOW_NEED_DATA)
1300     ret = GST_FLOW_OK;
1301
1302   return ret;
1303 }
1304
1305 static gboolean
1306 gst_mf_video_encoder_flush (GstVideoEncoder * enc)
1307 {
1308   GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1309
1310   if (!self->transform)
1311     goto out;
1312
1313   /* Unlock while flushing, while flushing, new sample callback might happen */
1314   if (self->async_mft)
1315     GST_VIDEO_ENCODER_STREAM_UNLOCK (enc);
1316
1317   gst_mf_transform_flush (self->transform);
1318
1319   if (self->async_mft)
1320     GST_VIDEO_ENCODER_STREAM_LOCK (enc);
1321
1322 out:
1323   self->last_ret = GST_FLOW_OK;
1324
1325   return TRUE;
1326 }
1327
1328 static gboolean
1329 gst_mf_video_encoder_propose_allocation (GstVideoEncoder * enc,
1330     GstQuery * query)
1331 {
1332 #if GST_MF_HAVE_D3D11
1333   GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1334   GstVideoInfo info;
1335   GstBufferPool *pool = nullptr;
1336   GstCaps *caps;
1337   guint size;
1338   GstD3D11Device *device = self->other_d3d11_device;
1339
1340   gst_query_parse_allocation (query, &caps, nullptr);
1341
1342   if (caps == nullptr)
1343     return FALSE;
1344
1345   if (!gst_video_info_from_caps (&info, caps))
1346     return FALSE;
1347
1348   if (gst_query_get_n_allocation_pools (query) == 0) {
1349     GstCapsFeatures *features;
1350     GstStructure *config;
1351     gboolean is_d3d11 = FALSE;
1352
1353     features = gst_caps_get_features (caps, 0);
1354
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);
1359       is_d3d11 = TRUE;
1360     } else {
1361       pool = gst_video_buffer_pool_new ();
1362     }
1363
1364     config = gst_buffer_pool_get_config (pool);
1365
1366     gst_buffer_pool_config_add_option (config,
1367         GST_BUFFER_POOL_OPTION_VIDEO_META);
1368
1369     /* d3d11 pool does not support video alignment */
1370     if (!is_d3d11) {
1371       gst_buffer_pool_config_add_option (config,
1372           GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
1373     }
1374
1375     size = GST_VIDEO_INFO_SIZE (&info);
1376     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1377
1378     if (!gst_buffer_pool_set_config (pool, config))
1379       goto config_failed;
1380
1381     /* d3d11 buffer pool will update buffer size based on allocated texture,
1382      * get size from config again */
1383     if (is_d3d11) {
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);
1388     }
1389
1390     gst_query_add_allocation_pool (query, pool, size, 0, 0);
1391     gst_object_unref (pool);
1392   }
1393
1394   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
1395
1396   return TRUE;
1397
1398   /* ERRORS */
1399 config_failed:
1400   {
1401     GST_ERROR_OBJECT (self, "failed to set config");
1402     gst_object_unref (pool);
1403     return FALSE;
1404   }
1405
1406 #else
1407   return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (enc,
1408       query);
1409 #endif
1410 }
1411
1412 static gboolean
1413 gst_mf_video_encoder_sink_query (GstVideoEncoder * enc, GstQuery * query)
1414 {
1415 #if GST_MF_HAVE_D3D11
1416   GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1417
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)) {
1422         return TRUE;
1423       }
1424       break;
1425     default:
1426       break;
1427   }
1428 #endif
1429
1430   return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (enc, query);
1431 }
1432
1433 static gboolean
1434 gst_mf_video_encoder_src_query (GstVideoEncoder * enc, GstQuery * query)
1435 {
1436 #if GST_MF_HAVE_D3D11
1437   GstMFVideoEncoder *self = GST_MF_VIDEO_ENCODER (enc);
1438
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)) {
1443         return TRUE;
1444       }
1445       break;
1446     default:
1447       break;
1448   }
1449 #endif
1450
1451   return GST_VIDEO_ENCODER_CLASS (parent_class)->src_query (enc, query);
1452 }
1453
1454 static HRESULT
1455 gst_mf_video_on_new_sample (GstMFTransform * object,
1456     IMFSample * sample, GstMFVideoEncoder * self)
1457 {
1458   GST_LOG_OBJECT (self, "New Sample callback");
1459
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);
1465
1466   return S_OK;
1467 }
1468
1469 typedef struct
1470 {
1471   guint profile;
1472   const gchar *profile_str;
1473 } GstMFVideoEncoderProfileMap;
1474
1475 static void
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)
1479 {
1480   HRESULT hr;
1481   MFT_REGISTER_TYPE_INFO *infos;
1482   UINT32 info_size;
1483   gint i;
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;
1494 #endif
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"},
1504     {0, nullptr},
1505   };
1506   GstMFVideoEncoderProfileMap hevc_profile_map[] = {
1507     {eAVEncH265VProfile_Main_420_8, "main"},
1508     {eAVEncH265VProfile_Main_420_10, "main-10"},
1509     {0, nullptr},
1510   };
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;
1518
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))
1522     return;
1523
1524   activate = gst_mf_transform_get_activate_handle (transform);
1525   if (!activate) {
1526     GST_WARNING_OBJECT (transform, "No IMFActivate interface available");
1527     return;
1528   }
1529
1530   encoder = gst_mf_transform_get_transform_handle (transform);
1531   if (!encoder) {
1532     GST_WARNING_OBJECT (transform, "No IMFTransform interface available");
1533     return;
1534   }
1535
1536   codec_api = gst_mf_transform_get_codec_api_handle (transform);
1537   if (!codec_api) {
1538     GST_WARNING_OBJECT (transform, "No ICodecAPI interface available");
1539     return;
1540   }
1541
1542   g_object_get (transform, "device-name", &device_name, nullptr);
1543   if (!device_name) {
1544     GST_WARNING_OBJECT (transform, "Unknown device name");
1545     return;
1546   }
1547   g_free (device_name);
1548
1549   hr = activate->GetAllocatedBlob (MFT_INPUT_TYPES_Attributes,
1550       (UINT8 **) & infos, &info_size);
1551   if (!gst_mf_result (hr))
1552     return;
1553
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;
1558
1559     format = gst_mf_video_subtype_to_video_format (&infos[i].guidSubtype);
1560     if (format == GST_VIDEO_FORMAT_UNKNOWN)
1561       continue;
1562
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));
1567       continue;
1568     }
1569
1570     if (!supported_formats) {
1571       supported_formats = g_new0 (GValue, 1);
1572       g_value_init (supported_formats, GST_TYPE_LIST);
1573     }
1574
1575     switch (format) {
1576         /* media foundation has duplicated formats IYUV and I420 */
1577       case GST_VIDEO_FORMAT_I420:
1578         if (have_I420)
1579           continue;
1580
1581         have_I420 = TRUE;
1582         break;
1583       case GST_VIDEO_FORMAT_NV12:
1584         have_NV12 = TRUE;
1585         break;
1586       case GST_VIDEO_FORMAT_P010_10LE:
1587         have_P010 = TRUE;
1588         break;
1589       default:
1590         break;
1591     }
1592
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);
1596   }
1597   CoTaskMemFree (infos);
1598
1599   if (!supported_formats) {
1600     GST_WARNING_OBJECT (transform, "Couldn't figure out supported format");
1601     return;
1602   }
1603
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;
1612   } else {
1613     g_assert_not_reached ();
1614     return;
1615   }
1616
1617   if (profile_to_check) {
1618     hr = MFCreateMediaType (&out_type);
1619     if (!gst_mf_result (hr))
1620       return;
1621
1622     hr = out_type->SetGUID (MF_MT_MAJOR_TYPE, MFMediaType_Video);
1623     if (!gst_mf_result (hr))
1624       return;
1625
1626     hr = out_type->SetGUID (MF_MT_SUBTYPE, subtype);
1627     if (!gst_mf_result (hr))
1628       return;
1629
1630     hr = out_type->SetUINT32 (MF_MT_AVG_BITRATE, 2048000);
1631     if (!gst_mf_result (hr))
1632       return;
1633
1634     hr = MFSetAttributeRatio (out_type.Get (), MF_MT_FRAME_RATE, 30, 1);
1635     if (!gst_mf_result (hr))
1636       return;
1637
1638     hr = out_type->SetUINT32 (MF_MT_INTERLACE_MODE,
1639         MFVideoInterlace_Progressive);
1640     if (!gst_mf_result (hr))
1641       return;
1642
1643     hr = MFSetAttributeSize (out_type.Get (), MF_MT_FRAME_SIZE, 1920, 1080);
1644     if (!gst_mf_result (hr))
1645       return;
1646
1647     i = 0;
1648     do {
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;
1652
1653       i++;
1654
1655       if (mf_profile == 0)
1656         break;
1657
1658       g_assert (profile_str != nullptr);
1659
1660       hr = out_type->SetUINT32 (MF_MT_MPEG2_PROFILE, mf_profile);
1661       if (!gst_mf_result (hr))
1662         return;
1663
1664       if (!gst_mf_transform_set_output_type (transform, out_type.Get ()))
1665         continue;
1666
1667       if (!profiles) {
1668         profiles = g_new0 (GValue, 1);
1669         g_value_init (profiles, GST_TYPE_LIST);
1670       }
1671
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);
1677       }
1678
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);
1682     } while (1);
1683
1684     if (!profiles) {
1685       GST_WARNING_OBJECT (transform, "Couldn't query supported profile");
1686       return;
1687     }
1688   }
1689
1690   src_caps = gst_caps_from_string (codec_caps_str);
1691   if (profiles) {
1692     gst_caps_set_value (src_caps, "profile", profiles);
1693     g_value_unset (profiles);
1694     g_free (profiles);
1695   }
1696
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... */
1700
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);
1704
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);
1709
1710   g_value_unset (&res_val);
1711
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);
1717   }
1718
1719   if (d3d11_device && (have_NV12 || have_P010) && d3d11_aware) {
1720     gint64 adapter_luid = 0;
1721     GValue d3d11_formats = G_VALUE_INIT;
1722
1723     g_object_get (d3d11_device, "adapter-luid", &adapter_luid, nullptr);
1724
1725     d3d11_caps = gst_caps_copy (sink_caps);
1726
1727     g_value_init (&d3d11_formats, GST_TYPE_LIST);
1728     if (have_NV12) {
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);
1733     }
1734
1735     if (have_P010) {
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);
1740     }
1741
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;
1748   }
1749 #endif
1750
1751   gst_caps_set_value (sink_caps, "format", supported_formats);
1752   g_value_unset (supported_formats);
1753   g_free (supported_formats);
1754
1755   if (d3d11_caps)
1756     gst_caps_append (sink_caps, d3d11_caps);
1757
1758   *sink_template = sink_caps;
1759   *src_template = src_caps;
1760
1761 #define CHECK_DEVICE_CAPS(codec_obj,api,val) \
1762   if (SUCCEEDED((codec_obj)->IsSupported(&(api)))) {\
1763     (device_caps)->val = TRUE; \
1764   }
1765
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) {
1791     VARIANT min;
1792     VARIANT max;
1793     VARIANT step;
1794
1795     hr = codec_api->GetParameterRange (&CODECAPI_AVEncVideoMaxNumRefFrame,
1796         &min, &max, &step);
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);
1803     } else {
1804       device_caps->max_num_ref = FALSE;
1805     }
1806   }
1807 #undef CHECK_DEVICE_CAPS
1808
1809   return;
1810 }
1811
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)
1816 {
1817   GstMFTransformEnumParams enum_params = { 0, };
1818   MFT_REGISTER_TYPE_INFO output_type;
1819   GstMFTransform *transform;
1820   gint64 adapter_luid = 0;
1821
1822   *sink_template = nullptr;
1823   *src_template = nullptr;
1824   memset (device_caps, 0, sizeof (GstMFVideoEncoderDeviceCaps));
1825
1826   if (!IsEqualGUID (MFVideoFormat_H264, *subtype) &&
1827       !IsEqualGUID (MFVideoFormat_HEVC, *subtype) &&
1828       !IsEqualGUID (MFVideoFormat_VP90, *subtype)) {
1829     GST_ERROR ("Unknown subtype GUID");
1830
1831     return nullptr;
1832   }
1833
1834   if (d3d11_device) {
1835     g_object_get (d3d11_device, "adapter-luid", &adapter_luid, nullptr);
1836     if (!adapter_luid) {
1837       GST_ERROR ("Couldn't get adapter LUID");
1838       return nullptr;
1839     }
1840   }
1841
1842   output_type.guidMajorType = MFMediaType_Video;
1843   output_type.guidSubtype = *subtype;
1844
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;
1850
1851   transform = gst_mf_transform_new (&enum_params);
1852   if (!transform)
1853     return nullptr;
1854
1855   gst_mf_video_encoder_enum_internal (transform, output_type.guidSubtype,
1856       d3d11_device, device_caps, sink_template, src_template);
1857
1858   return transform;
1859 }
1860
1861 static void
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)
1867 {
1868   GType type;
1869   GTypeInfo local_type_info;
1870   gchar *type_name;
1871   gchar *feature_name;
1872   gint i;
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;
1878
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";
1888   } else {
1889     g_assert_not_reached ();
1890     return;
1891   }
1892
1893   /* Must be checked already */
1894   g_object_get (transform, "device-name", &device_name, nullptr);
1895   g_assert (device_name != nullptr);
1896
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;
1904
1905   local_type_info = *type_info;
1906   local_type_info.class_data = cdata;
1907
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);
1912
1913   type_name = g_strdup_printf ("GstMF%sEnc", type_name_prefix);
1914   feature_name = g_strdup_printf ("mf%senc", feature_name_prefix);
1915
1916   i = 1;
1917   while (g_type_from_name (type_name) != 0) {
1918     g_free (type_name);
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);
1922     is_default = FALSE;
1923     i++;
1924   }
1925
1926   cdata->is_default = is_default;
1927
1928   type =
1929       g_type_register_static (GST_TYPE_MF_VIDEO_ENCODER, type_name,
1930       &local_type_info, (GTypeFlags) 0);
1931
1932   /* make lower rank than default device */
1933   if (rank > 0 && !is_default)
1934     rank--;
1935
1936   if (!is_default || !device_caps->d3d11_aware)
1937     gst_element_type_set_skip_documentation (type);
1938
1939   if (!gst_element_register (plugin, feature_name, rank, type))
1940     GST_WARNING ("Failed to register plugin '%s'", type_name);
1941
1942   g_free (type_name);
1943   g_free (feature_name);
1944 }
1945
1946 void
1947 gst_mf_video_encoder_register (GstPlugin * plugin, guint rank, GUID * subtype,
1948     GTypeInfo * type_info, GList * d3d11_device)
1949 {
1950   GstMFTransform *transform = nullptr;
1951   GstCaps *sink_template = nullptr;
1952   GstCaps *src_template = nullptr;
1953   guint enum_flags;
1954   GstMFVideoEncoderDeviceCaps device_caps;
1955   guint i;
1956
1957   /* register hardware encoders first */
1958   enum_flags = (MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_ASYNCMFT |
1959       MFT_ENUM_FLAG_SORTANDFILTER_APPROVED_ONLY);
1960
1961   if (d3d11_device) {
1962     GList *iter;
1963     for (iter = d3d11_device; iter; iter = g_list_next (iter)) {
1964       GstObject *device = (GstObject *) iter->data;
1965
1966       transform =
1967           gst_mf_video_encoder_enum (enum_flags, subtype, 0, &device_caps,
1968           device, &sink_template, &src_template);
1969
1970       if (!transform)
1971         continue;
1972
1973       /* Failed to open MFT */
1974       if (!sink_template) {
1975         gst_clear_object (&transform);
1976         continue;
1977       }
1978
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);
1984     }
1985   } else {
1986     /* AMD seems to be able to support up to 12 GPUs */
1987     for (i = 0; i < 12; i++) {
1988       transform =
1989           gst_mf_video_encoder_enum (enum_flags, subtype, i, &device_caps,
1990           nullptr, &sink_template, &src_template);
1991
1992       /* No more MFT to enumerate */
1993       if (!transform)
1994         break;
1995
1996       /* Failed to open MFT */
1997       if (!sink_template) {
1998         gst_clear_object (&transform);
1999         continue;
2000       }
2001
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);
2007     }
2008   }
2009
2010   /* register software encoders */
2011   enum_flags = (MFT_ENUM_FLAG_SYNCMFT |
2012       MFT_ENUM_FLAG_SORTANDFILTER_APPROVED_ONLY);
2013
2014   transform = gst_mf_video_encoder_enum (enum_flags, subtype, 0, &device_caps,
2015       nullptr, &sink_template, &src_template);
2016
2017   if (!transform)
2018     goto done;
2019
2020   if (!sink_template)
2021     goto done;
2022
2023   gst_mf_video_encoder_register_internal (plugin, rank, subtype, type_info,
2024       &device_caps, enum_flags, 0, transform, sink_template, src_template);
2025
2026 done:
2027   gst_clear_object (&transform);
2028   gst_clear_caps (&sink_template);
2029   gst_clear_caps (&src_template);
2030 }