mediafoundation: Fix various string constness handling
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / mediafoundation / gstmftransform.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 "gstmfconfig.h"
26
27 #include <gst/gst.h>
28 #include <gmodule.h>
29 #include "gstmftransform.h"
30 #include "gstmfutils.h"
31 #include <string.h>
32 #include <wrl.h>
33
34 /* *INDENT-OFF* */
35 using namespace Microsoft::WRL;
36
37 G_BEGIN_DECLS
38
39 GST_DEBUG_CATEGORY_EXTERN (gst_mf_transform_debug);
40 #define GST_CAT_DEFAULT gst_mf_transform_debug
41
42 G_END_DECLS
43
44 static GModule *mf_plat_module = NULL;
45 typedef HRESULT (__stdcall *pMFTEnum2) (GUID guidCategory,
46                                         UINT32 Flags,
47                                         const MFT_REGISTER_TYPE_INFO * pInputType,
48                                         const MFT_REGISTER_TYPE_INFO * pOutputType,
49                                         IMFAttributes * pAttributes,
50                                         IMFActivate *** pppMFTActivate,
51                                         UINT32 * pnumMFTActivate);
52 static pMFTEnum2 GstMFTEnum2Func = NULL;
53
54 gboolean
55 gst_mf_transform_load_library (void)
56 {
57 #if GST_MF_HAVE_D3D11
58   static gsize _init = 0;
59   if (g_once_init_enter (&_init)) {
60     mf_plat_module = g_module_open ("mfplat.dll", G_MODULE_BIND_LAZY);
61
62     if (mf_plat_module) {
63       if (!g_module_symbol (mf_plat_module, "MFTEnum2",
64               (gpointer *) & GstMFTEnum2Func)) {
65         GST_WARNING ("Cannot load MFTEnum2 symbol");
66         g_module_close (mf_plat_module);
67         mf_plat_module = NULL;
68         GstMFTEnum2Func = NULL;
69       } else {
70         GST_INFO ("MFTEnum2 symbol is available");
71       }
72     }
73
74     g_once_init_leave (&_init, 1);
75   }
76 #endif
77
78   return ! !GstMFTEnum2Func;
79 }
80
81 typedef HRESULT (*GstMFTransformAsyncCallbackOnEvent) (MediaEventType event,
82     GstObject * client);
83
84 class GstMFTransformAsyncCallback : public IMFAsyncCallback
85 {
86 public:
87   static HRESULT
88   CreateInstance (IMFTransform * mft,
89       GstMFTransformAsyncCallbackOnEvent event_cb, GstObject * client,
90       GstMFTransformAsyncCallback ** callback)
91   {
92     HRESULT hr;
93     GstMFTransformAsyncCallback *self;
94
95     if (!mft || !callback)
96       return E_INVALIDARG;
97
98     self = new GstMFTransformAsyncCallback ();
99
100     if (!self)
101       return E_OUTOFMEMORY;
102
103     hr = self->Initialize (mft, event_cb, client);
104
105     if (!gst_mf_result (hr)) {
106       self->Release ();
107       return hr;
108     }
109
110     *callback = self;
111
112     return S_OK;
113   }
114
115   HRESULT
116   BeginGetEvent (void)
117   {
118     if (!gen_)
119       return E_FAIL;
120
121     /* we are running already */
122     if (running_)
123       return S_OK;
124
125     running_ = true;
126
127     return gen_->BeginGetEvent (this, nullptr);
128   }
129
130   HRESULT
131   Stop (void)
132   {
133     running_ = false;
134
135     return S_OK;
136   }
137
138   /* IUnknown */
139   STDMETHODIMP
140   QueryInterface (REFIID riid, void ** object)
141   {
142     return E_NOTIMPL;
143   }
144
145   STDMETHODIMP_ (ULONG)
146   AddRef (void)
147   {
148     GST_TRACE ("%p, %d", this, ref_count_);
149     return InterlockedIncrement (&ref_count_);
150   }
151
152   STDMETHODIMP_ (ULONG)
153   Release (void)
154   {
155     ULONG ref_count;
156
157     GST_TRACE ("%p, %d", this, ref_count_);
158     ref_count = InterlockedDecrement (&ref_count_);
159
160     if (ref_count == 0) {
161       GST_TRACE ("Delete instance %p", this);
162       delete this;
163     }
164
165     return ref_count;
166   }
167
168   /* IMFAsyncCallback */
169   STDMETHODIMP
170   GetParameters (DWORD * flags, DWORD * queue)
171   {
172     /* this callback could be blocked */
173     *flags = MFASYNC_BLOCKING_CALLBACK;
174     *queue = MFASYNC_CALLBACK_QUEUE_MULTITHREADED;
175     return S_OK;
176   }
177
178   STDMETHODIMP
179   Invoke (IMFAsyncResult * async_result)
180   {
181     ComPtr<IMFMediaEvent> event;
182     HRESULT hr;
183     bool do_next = true;
184
185     hr = gen_->EndGetEvent (async_result, &event);
186
187     if (!gst_mf_result (hr))
188       return hr;
189
190     if (event) {
191       MediaEventType type;
192       GstObject *client = nullptr;
193       hr = event->GetType(&type);
194       if (!gst_mf_result (hr))
195         return hr;
196
197       if (!event_cb_)
198         return S_OK;
199
200       client = (GstObject *) g_weak_ref_get (&client_);
201       if (!client)
202         return S_OK;
203
204       hr = event_cb_ (type, client);
205       gst_object_unref (client);
206       if (!gst_mf_result (hr))
207         return hr;
208
209       /* On Drain event, this callback object will stop calling BeginGetEvent()
210        * since there might be no more following events. Client should call
211        * our BeginGetEvent() method to run again */
212       if (type == METransformDrainComplete)
213         do_next = false;
214     }
215
216     if (do_next)
217       gen_->BeginGetEvent(this, nullptr);
218
219     return S_OK;
220   }
221
222 private:
223   GstMFTransformAsyncCallback ()
224     : ref_count_ (1)
225     , running_ (false)
226   {
227     g_weak_ref_init (&client_, NULL);
228   }
229
230   ~GstMFTransformAsyncCallback ()
231   {
232     g_weak_ref_clear (&client_);
233   }
234
235   HRESULT
236   Initialize (IMFTransform * mft, GstMFTransformAsyncCallbackOnEvent event_cb,
237       GstObject * client)
238   {
239     HRESULT hr = mft->QueryInterface(IID_PPV_ARGS(&gen_));
240
241     if (!gst_mf_result (hr))
242       return hr;
243
244     event_cb_ = event_cb;
245     g_weak_ref_set (&client_, client);
246
247     return S_OK;
248   }
249
250 private:
251   ULONG ref_count_;
252   ComPtr<IMFMediaEventGenerator> gen_;
253   GstMFTransformAsyncCallbackOnEvent event_cb_;
254   GWeakRef client_;
255
256   bool running_;
257 };
258 /* *INDENT-ON* */
259
260 enum
261 {
262   PROP_0,
263   PROP_DEVICE_NAME,
264   PROP_HARDWARE,
265   PROP_ENUM_PARAMS,
266   PROP_D3D11_AWARE,
267 };
268
269 struct _GstMFTransform
270 {
271   GstObject object;
272   gboolean initialized;
273
274   GstMFTransformEnumParams enum_params;
275
276   gchar *device_name;
277   gboolean hardware;
278   gboolean d3d11_aware;
279
280   IMFActivate *activate;
281   IMFTransform *transform;
282   ICodecAPI *codec_api;
283   GstMFTransformAsyncCallback *callback_object;
284
285   GQueue *output_queue;
286
287   DWORD input_id;
288   DWORD output_id;
289
290   gboolean running;
291
292   gint pending_need_input;
293
294   GThread *thread;
295   GMutex lock;
296   GCond cond;
297   GMutex event_lock;
298   GCond event_cond;
299   GMainContext *context;
300   GMainLoop *loop;
301   gboolean draining;
302   gboolean flushing;
303
304   GstMFTransformNewSampleCallback callback;
305   gpointer user_data;
306 };
307
308 #define gst_mf_transform_parent_class parent_class
309 G_DEFINE_TYPE (GstMFTransform, gst_mf_transform, GST_TYPE_OBJECT);
310
311 static void gst_mf_transform_constructed (GObject * object);
312 static void gst_mf_transform_finalize (GObject * object);
313 static void gst_mf_transform_get_property (GObject * object,
314     guint prop_id, GValue * value, GParamSpec * pspec);
315 static void gst_mf_transform_set_property (GObject * object,
316     guint prop_id, const GValue * value, GParamSpec * pspec);
317
318 static gpointer gst_mf_transform_thread_func (GstMFTransform * self);
319 static gboolean gst_mf_transform_close (GstMFTransform * self);
320 static HRESULT gst_mf_transform_on_event (MediaEventType event,
321     GstMFTransform * self);
322
323 static void
324 gst_mf_transform_class_init (GstMFTransformClass * klass)
325 {
326   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
327
328   gobject_class->constructed = gst_mf_transform_constructed;
329   gobject_class->finalize = gst_mf_transform_finalize;
330   gobject_class->get_property = gst_mf_transform_get_property;
331   gobject_class->set_property = gst_mf_transform_set_property;
332
333   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
334       g_param_spec_string ("device-name", "device-name",
335           "Device name", NULL,
336           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
337   g_object_class_install_property (gobject_class, PROP_HARDWARE,
338       g_param_spec_boolean ("hardware", "Hardware",
339           "Whether hardware device or not", FALSE,
340           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
341   g_object_class_install_property (gobject_class, PROP_ENUM_PARAMS,
342       g_param_spec_pointer ("enum-params", "Enum Params",
343           "GstMFTransformEnumParams for MFTEnumEx",
344           (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
345               G_PARAM_STATIC_STRINGS)));
346   g_object_class_install_property (gobject_class, PROP_D3D11_AWARE,
347       g_param_spec_boolean ("d3d11-aware", "D3D11 Aware",
348           "Whether Direct3D11 supports Direct3D11", FALSE,
349           (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
350 }
351
352 static void
353 gst_mf_transform_init (GstMFTransform * self)
354 {
355   self->output_queue = g_queue_new ();
356
357   g_mutex_init (&self->lock);
358   g_mutex_init (&self->event_lock);
359   g_cond_init (&self->cond);
360   g_cond_init (&self->event_cond);
361
362   self->context = g_main_context_new ();
363   self->loop = g_main_loop_new (self->context, FALSE);
364 }
365
366 static void
367 gst_mf_transform_constructed (GObject * object)
368 {
369   GstMFTransform *self = GST_MF_TRANSFORM (object);
370
371   /* Create thread so that ensure COM thread can be MTA thread */
372   g_mutex_lock (&self->lock);
373   self->thread = g_thread_new ("GstMFTransform",
374       (GThreadFunc) gst_mf_transform_thread_func, self);
375   while (!g_main_loop_is_running (self->loop))
376     g_cond_wait (&self->cond, &self->lock);
377   g_mutex_unlock (&self->lock);
378
379   G_OBJECT_CLASS (parent_class)->constructed (object);
380 }
381
382 static void
383 gst_mf_transform_clear_enum_params (GstMFTransformEnumParams * params)
384 {
385   g_free (params->input_typeinfo);
386   params->input_typeinfo = NULL;
387
388   g_free (params->output_typeinfo);
389   params->output_typeinfo = NULL;
390 }
391
392 static void
393 release_mf_sample (IMFSample * sample)
394 {
395   if (sample)
396     sample->Release ();
397 }
398
399 static void
400 gst_mf_transform_finalize (GObject * object)
401 {
402   GstMFTransform *self = GST_MF_TRANSFORM (object);
403
404   g_main_loop_quit (self->loop);
405   g_thread_join (self->thread);
406   g_main_loop_unref (self->loop);
407   g_main_context_unref (self->context);
408
409   g_queue_free_full (self->output_queue, (GDestroyNotify) release_mf_sample);
410   gst_mf_transform_clear_enum_params (&self->enum_params);
411   g_free (self->device_name);
412   g_mutex_clear (&self->lock);
413   g_mutex_clear (&self->event_lock);
414   g_cond_clear (&self->cond);
415   g_cond_clear (&self->event_cond);
416
417   G_OBJECT_CLASS (parent_class)->finalize (object);
418 }
419
420 static void
421 gst_mf_transform_get_property (GObject * object, guint prop_id,
422     GValue * value, GParamSpec * pspec)
423 {
424   GstMFTransform *self = GST_MF_TRANSFORM (object);
425
426   switch (prop_id) {
427     case PROP_DEVICE_NAME:
428       g_value_set_string (value, self->device_name);
429       break;
430     case PROP_HARDWARE:
431       g_value_set_boolean (value, self->hardware);
432       break;
433     case PROP_D3D11_AWARE:
434       g_value_set_boolean (value, self->d3d11_aware);
435       break;
436     default:
437       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
438       break;
439   }
440 }
441
442 static void
443 gst_mf_transform_set_property (GObject * object, guint prop_id,
444     const GValue * value, GParamSpec * pspec)
445 {
446   GstMFTransform *self = GST_MF_TRANSFORM (object);
447
448   switch (prop_id) {
449     case PROP_ENUM_PARAMS:
450     {
451       GstMFTransformEnumParams *params;
452       params = (GstMFTransformEnumParams *) g_value_get_pointer (value);
453
454       gst_mf_transform_clear_enum_params (&self->enum_params);
455       self->enum_params.category = params->category;
456       self->enum_params.enum_flags = params->enum_flags;
457       self->enum_params.device_index = params->device_index;
458       self->enum_params.adapter_luid = params->adapter_luid;
459       if (params->input_typeinfo) {
460         self->enum_params.input_typeinfo = g_new0 (MFT_REGISTER_TYPE_INFO, 1);
461         memcpy (self->enum_params.input_typeinfo, params->input_typeinfo,
462             sizeof (MFT_REGISTER_TYPE_INFO));
463       }
464
465       if (params->output_typeinfo) {
466         self->enum_params.output_typeinfo = g_new0 (MFT_REGISTER_TYPE_INFO, 1);
467         memcpy (self->enum_params.output_typeinfo, params->output_typeinfo,
468             sizeof (MFT_REGISTER_TYPE_INFO));
469       }
470       break;
471     }
472     default:
473       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
474       break;
475   }
476 }
477
478 static gboolean
479 gst_mf_transform_main_loop_running_cb (GstMFTransform * self)
480 {
481   GST_TRACE_OBJECT (self, "Main loop running now");
482
483   g_mutex_lock (&self->lock);
484   g_cond_signal (&self->cond);
485   g_mutex_unlock (&self->lock);
486
487   return G_SOURCE_REMOVE;
488 }
489
490 static gpointer
491 gst_mf_transform_thread_func (GstMFTransform * self)
492 {
493   HRESULT hr = S_OK;
494   IMFActivate **devices = NULL;
495   UINT32 num_devices, i;
496   LPWSTR name = NULL;
497   GSource *source;
498
499   CoInitializeEx (NULL, COINIT_MULTITHREADED);
500
501   g_main_context_push_thread_default (self->context);
502
503   source = g_idle_source_new ();
504   g_source_set_callback (source,
505       (GSourceFunc) gst_mf_transform_main_loop_running_cb, self, NULL);
506   g_source_attach (source, self->context);
507   g_source_unref (source);
508
509   /* NOTE: MFTEnum2 is desktop only and requires Windows 10 */
510 #if GST_MF_HAVE_D3D11
511   if (GstMFTEnum2Func && self->enum_params.adapter_luid &&
512       (self->enum_params.enum_flags & MFT_ENUM_FLAG_HARDWARE) != 0) {
513     ComPtr < IMFAttributes > attr;
514     LUID luid;
515
516     hr = MFCreateAttributes (&attr, 1);
517     if (!gst_mf_result (hr)) {
518       GST_ERROR_OBJECT (self, "Couldn't create IMFAttributes");
519       goto run_loop;
520     }
521
522     GST_INFO_OBJECT (self,
523         "Enumerating MFT for adapter-luid %" G_GINT64_FORMAT,
524         self->enum_params.adapter_luid);
525
526     luid.LowPart = (DWORD) (self->enum_params.adapter_luid & 0xffffffff);
527     luid.HighPart = (LONG) (self->enum_params.adapter_luid >> 32);
528
529     hr = attr->SetBlob (GST_GUID_MFT_ENUM_ADAPTER_LUID, (BYTE *) & luid,
530         sizeof (LUID));
531     if (!gst_mf_result (hr)) {
532       GST_ERROR_OBJECT (self, "Couldn't set MFT_ENUM_ADAPTER_LUID");
533       goto run_loop;
534     }
535
536     hr = GstMFTEnum2Func (self->enum_params.category,
537         self->enum_params.enum_flags, self->enum_params.input_typeinfo,
538         self->enum_params.output_typeinfo, attr.Get (), &devices, &num_devices);
539   } else
540 #endif
541   {
542     hr = MFTEnumEx (self->enum_params.category, self->enum_params.enum_flags,
543         self->enum_params.input_typeinfo, self->enum_params.output_typeinfo,
544         &devices, &num_devices);
545   }
546
547   if (!gst_mf_result (hr)) {
548     GST_WARNING_OBJECT (self, "MFTEnumEx failure");
549     goto run_loop;
550   }
551
552   if (num_devices == 0 || self->enum_params.device_index >= num_devices) {
553     GST_WARNING_OBJECT (self, "No available device at index %d",
554         self->enum_params.device_index);
555     for (i = 0; i < num_devices; i++)
556       devices[i]->Release ();
557
558     CoTaskMemFree (devices);
559     goto run_loop;
560   }
561
562   self->activate = devices[self->enum_params.device_index];
563   self->activate->AddRef ();
564
565   for (i = 0; i < num_devices; i++)
566     devices[i]->Release ();
567
568   hr = self->activate->GetAllocatedString (MFT_FRIENDLY_NAME_Attribute,
569       &name, NULL);
570
571   if (gst_mf_result (hr)) {
572     self->device_name = g_utf16_to_utf8 ((const gunichar2 *) name,
573         -1, NULL, NULL, NULL);
574
575     GST_INFO_OBJECT (self, "Open device %s", self->device_name);
576     CoTaskMemFree (name);
577   }
578
579   CoTaskMemFree (devices);
580
581   self->hardware = !!(self->enum_params.enum_flags & MFT_ENUM_FLAG_HARDWARE);
582   self->initialized = TRUE;
583
584 run_loop:
585   GST_TRACE_OBJECT (self, "Starting main loop");
586   g_main_loop_run (self->loop);
587   GST_TRACE_OBJECT (self, "Stopped main loop");
588
589   g_main_context_pop_thread_default (self->context);
590
591   /* cleanup internal COM object here */
592   gst_mf_transform_close (self);
593
594   if (self->activate) {
595     self->activate->Release ();
596     self->activate = NULL;
597   }
598
599   CoUninitialize ();
600
601   return NULL;
602 }
603
604 static GstFlowReturn
605 gst_mf_transform_process_output (GstMFTransform * self)
606 {
607   DWORD status;
608   HRESULT hr;
609   IMFTransform *transform = self->transform;
610   DWORD stream_id = self->output_id;
611   MFT_OUTPUT_STREAM_INFO out_stream_info = { 0 };
612   MFT_OUTPUT_DATA_BUFFER out_data = { 0 };
613   GstFlowReturn ret = GST_FLOW_OK;
614
615   GST_TRACE_OBJECT (self, "Process output");
616
617   hr = transform->GetOutputStreamInfo (stream_id, &out_stream_info);
618   if (!gst_mf_result (hr)) {
619     GST_ERROR_OBJECT (self, "Couldn't get output stream info");
620     return GST_FLOW_ERROR;
621   }
622
623   if ((out_stream_info.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES |
624               MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0) {
625     ComPtr < IMFMediaBuffer > buffer;
626     ComPtr < IMFSample > new_sample;
627
628     hr = MFCreateMemoryBuffer (out_stream_info.cbSize, buffer.GetAddressOf ());
629     if (!gst_mf_result (hr)) {
630       GST_ERROR_OBJECT (self, "Couldn't create memory buffer");
631       return GST_FLOW_ERROR;
632     }
633
634     hr = MFCreateSample (new_sample.GetAddressOf ());
635     if (!gst_mf_result (hr)) {
636       GST_ERROR_OBJECT (self, "Couldn't create sample");
637       return GST_FLOW_ERROR;
638     }
639
640     hr = new_sample->AddBuffer (buffer.Get ());
641     if (!gst_mf_result (hr)) {
642       GST_ERROR_OBJECT (self, "Couldn't add buffer to sample");
643       return GST_FLOW_ERROR;
644     }
645
646     out_data.pSample = new_sample.Detach ();
647   }
648
649   out_data.dwStreamID = stream_id;
650
651   hr = transform->ProcessOutput (0, 1, &out_data, &status);
652
653   if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
654     GST_LOG_OBJECT (self, "Need more input data");
655     ret = GST_MF_TRANSFORM_FLOW_NEED_DATA;
656   } else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
657     ComPtr < IMFMediaType > output_type;
658
659     GST_DEBUG_OBJECT (self, "Stream change, set output type again");
660
661     hr = transform->GetOutputAvailableType (stream_id,
662         0, output_type.GetAddressOf ());
663     if (!gst_mf_result (hr)) {
664       GST_ERROR_OBJECT (self, "Couldn't get available output type");
665       ret = GST_FLOW_ERROR;
666       goto done;
667     }
668
669     hr = transform->SetOutputType (stream_id, output_type.Get (), 0);
670     if (!gst_mf_result (hr)) {
671       GST_ERROR_OBJECT (self, "Couldn't set output type");
672       ret = GST_FLOW_ERROR;
673       goto done;
674     }
675
676     ret = GST_MF_TRANSFORM_FLOW_NEED_DATA;
677   } else if (!gst_mf_result (hr)) {
678     if (self->flushing) {
679       GST_DEBUG_OBJECT (self, "Ignore error on flushing");
680       ret = GST_FLOW_FLUSHING;
681     } else {
682       GST_ERROR_OBJECT (self, "ProcessOutput error, hr 0x%x", hr);
683       ret = GST_FLOW_ERROR;
684     }
685   }
686
687 done:
688   if (ret != GST_FLOW_OK) {
689     if (out_data.pSample)
690       out_data.pSample->Release ();
691
692     return ret;
693   }
694
695   if (!out_data.pSample) {
696     GST_WARNING_OBJECT (self, "No output sample");
697     return GST_FLOW_OK;
698   }
699
700   if (self->callback) {
701     self->callback (self, out_data.pSample, self->user_data);
702     out_data.pSample->Release ();
703     return GST_FLOW_OK;
704   }
705
706   g_queue_push_tail (self->output_queue, out_data.pSample);
707
708   return GST_FLOW_OK;
709 }
710
711 /* Must be called with event_lock */
712 static gboolean
713 gst_mf_transform_process_input_sync (GstMFTransform * self, IMFSample * sample)
714 {
715   HRESULT hr;
716
717   hr = self->transform->ProcessInput (self->output_id, sample, 0);
718
719   if (self->hardware)
720     self->pending_need_input--;
721
722   return gst_mf_result (hr);
723 }
724
725 gboolean
726 gst_mf_transform_process_input (GstMFTransform * object, IMFSample * sample)
727 {
728   HRESULT hr;
729   gboolean ret = FALSE;
730
731   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
732   g_return_val_if_fail (sample != NULL, FALSE);
733
734   GST_TRACE_OBJECT (object, "Process input");
735
736   if (!object->transform)
737     return FALSE;
738
739   g_mutex_lock (&object->event_lock);
740   if (!object->running) {
741     object->pending_need_input = 0;
742
743     hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_START_OF_STREAM,
744         0);
745     if (!gst_mf_result (hr)) {
746       GST_ERROR_OBJECT (object, "Cannot post start-of-stream message");
747       goto done;
748     }
749
750     hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
751         0);
752     if (!gst_mf_result (hr)) {
753       GST_ERROR_OBJECT (object, "Cannot post begin-stream message");
754       goto done;
755     }
756
757     if (object->callback_object) {
758       hr = object->callback_object->BeginGetEvent ();
759       if (!gst_mf_result (hr)) {
760         GST_ERROR_OBJECT (object, "BeginGetEvent failed");
761         goto done;
762       }
763     }
764
765     GST_DEBUG_OBJECT (object, "MFT is running now");
766
767     object->running = TRUE;
768     object->flushing = FALSE;
769   }
770
771   /* Wait METransformNeedInput event. While waiting METransformNeedInput
772    * event, we can still output data if MFT notifyes METransformHaveOutput
773    * event. */
774   if (object->hardware) {
775     while (object->pending_need_input == 0 && !object->flushing)
776       g_cond_wait (&object->event_cond, &object->event_lock);
777   }
778
779   if (object->flushing) {
780     GST_DEBUG_OBJECT (object, "We are flushing");
781     ret = TRUE;
782     goto done;
783   }
784
785   ret = gst_mf_transform_process_input_sync (object, sample);
786
787 done:
788   g_mutex_unlock (&object->event_lock);
789
790   return ret;
791 }
792
793 GstFlowReturn
794 gst_mf_transform_get_output (GstMFTransform * object, IMFSample ** sample)
795 {
796   GstFlowReturn ret;
797
798   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), GST_FLOW_ERROR);
799   g_return_val_if_fail (sample != NULL, GST_FLOW_ERROR);
800   /* Hardware MFT must not call this method, instead client must install
801    * new sample callback so that outputting data from Media Foundation's
802    * worker thread */
803   g_return_val_if_fail (!object->hardware, GST_FLOW_ERROR);
804
805   if (!object->transform)
806     return GST_FLOW_ERROR;
807
808   ret = gst_mf_transform_process_output (object);
809
810   if (ret != GST_MF_TRANSFORM_FLOW_NEED_DATA && ret != GST_FLOW_OK)
811     return ret;
812
813   if (g_queue_is_empty (object->output_queue))
814     return GST_MF_TRANSFORM_FLOW_NEED_DATA;
815
816   *sample = (IMFSample *) g_queue_pop_head (object->output_queue);
817
818   return GST_FLOW_OK;
819 }
820
821 gboolean
822 gst_mf_transform_flush (GstMFTransform * object)
823 {
824   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
825
826   g_mutex_lock (&object->event_lock);
827   object->flushing = TRUE;
828   g_cond_broadcast (&object->event_cond);
829   g_mutex_unlock (&object->event_lock);
830
831   if (object->transform) {
832     /* In case of async MFT, there would be no more event after FLUSH,
833      * then callback object shouldn't wait another event.
834      * Call Stop() so that our callback object can stop calling BeginGetEvent()
835      * from it's Invoke() method */
836     if (object->callback_object)
837       object->callback_object->Stop ();
838
839     if (object->running) {
840       object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_FLUSH, 0);
841     }
842
843     object->pending_need_input = 0;
844   }
845
846   object->running = FALSE;
847
848   while (!g_queue_is_empty (object->output_queue)) {
849     IMFSample *sample = (IMFSample *) g_queue_pop_head (object->output_queue);
850     sample->Release ();
851   }
852
853   return TRUE;
854 }
855
856 gboolean
857 gst_mf_transform_drain (GstMFTransform * object)
858 {
859   GstFlowReturn ret;
860
861   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
862
863   if (!object->transform || !object->running)
864     return TRUE;
865
866   object->running = FALSE;
867   object->draining = TRUE;
868
869   GST_DEBUG_OBJECT (object, "Start drain");
870
871   object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_DRAIN, 0);
872
873   if (object->hardware) {
874     g_mutex_lock (&object->event_lock);
875     while (object->draining)
876       g_cond_wait (&object->event_cond, &object->event_lock);
877     g_mutex_unlock (&object->event_lock);
878   } else {
879     do {
880       ret = gst_mf_transform_process_output (object);
881     } while (ret == GST_FLOW_OK);
882   }
883
884   GST_DEBUG_OBJECT (object, "End drain");
885
886   object->draining = FALSE;
887   object->pending_need_input = 0;
888
889   return TRUE;
890 }
891
892 typedef struct
893 {
894   GstMFTransform *object;
895   gboolean invoked;
896   gboolean ret;
897 } GstMFTransformOpenData;
898
899 static gboolean
900 gst_mf_transform_open_internal (GstMFTransformOpenData * data)
901 {
902   GstMFTransform *object = data->object;
903   HRESULT hr;
904
905   data->ret = FALSE;
906
907   gst_mf_transform_close (object);
908   hr = object->activate->ActivateObject (IID_PPV_ARGS (&object->transform));
909
910   if (!gst_mf_result (hr)) {
911     GST_WARNING_OBJECT (object, "Couldn't open MFT");
912     goto done;
913   }
914
915   if (object->hardware) {
916     ComPtr < IMFAttributes > attr;
917     UINT32 supports_d3d11 = 0;
918
919     hr = object->transform->GetAttributes (attr.GetAddressOf ());
920     if (!gst_mf_result (hr)) {
921       GST_ERROR_OBJECT (object, "Couldn't get attribute object");
922       goto done;
923     }
924
925     hr = attr->SetUINT32 (MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
926     if (!gst_mf_result (hr)) {
927       GST_ERROR_OBJECT (object, "MF_TRANSFORM_ASYNC_UNLOCK error");
928       goto done;
929     }
930
931     hr = attr->GetUINT32 (GST_GUID_MF_SA_D3D11_AWARE, &supports_d3d11);
932     if (gst_mf_result (hr) && supports_d3d11 != 0) {
933       GST_DEBUG_OBJECT (object, "MFT supports direct3d11");
934       object->d3d11_aware = TRUE;
935     }
936
937     /* Create our IMFAsyncCallback object so that listen METransformNeedInput
938      * and METransformHaveOutput events. The event callback will be called from
939      * Media Foundation's worker queue thread */
940     hr = GstMFTransformAsyncCallback::CreateInstance (object->transform,
941         (GstMFTransformAsyncCallbackOnEvent) gst_mf_transform_on_event,
942         GST_OBJECT_CAST (object), &object->callback_object);
943
944     if (!object->callback_object) {
945       GST_ERROR_OBJECT (object, "IMFMediaEventGenerator unavailable");
946       goto done;
947     }
948   }
949
950   hr = object->transform->GetStreamIDs (1, &object->input_id, 1,
951       &object->output_id);
952   if (hr == E_NOTIMPL) {
953     object->input_id = 0;
954     object->output_id = 0;
955   }
956
957   hr = object->transform->QueryInterface (IID_PPV_ARGS (&object->codec_api));
958   if (!gst_mf_result (hr)) {
959     GST_WARNING_OBJECT (object, "ICodecAPI is unavailable");
960   }
961
962   data->ret = TRUE;
963
964 done:
965   if (!data->ret)
966     gst_mf_transform_close (object);
967
968   g_mutex_lock (&object->lock);
969   data->invoked = TRUE;
970   g_cond_broadcast (&object->cond);
971   g_mutex_unlock (&object->lock);
972
973   return G_SOURCE_REMOVE;
974 }
975
976 gboolean
977 gst_mf_transform_open (GstMFTransform * object)
978 {
979   GstMFTransformOpenData data;
980
981   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
982   g_return_val_if_fail (object->activate != NULL, FALSE);
983
984   data.object = object;
985   data.invoked = FALSE;
986   data.ret = FALSE;
987
988   g_main_context_invoke (object->context,
989       (GSourceFunc) gst_mf_transform_open_internal, &data);
990
991   g_mutex_lock (&object->lock);
992   while (!data.invoked)
993     g_cond_wait (&object->cond, &object->lock);
994   g_mutex_unlock (&object->lock);
995
996   return data.ret;
997 }
998
999 gboolean
1000 gst_mf_transform_set_device_manager (GstMFTransform * object,
1001     IMFDXGIDeviceManager * manager)
1002 {
1003   HRESULT hr;
1004
1005   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1006
1007   if (!object->transform) {
1008     GST_ERROR_OBJECT (object, "IMFTransform is not configured yet");
1009     return FALSE;
1010   }
1011
1012   hr = object->transform->ProcessMessage (MFT_MESSAGE_SET_D3D_MANAGER,
1013       (ULONG_PTR) manager);
1014   if (!gst_mf_result (hr)) {
1015     GST_ERROR_OBJECT (object, "Couldn't set device manager");
1016     return FALSE;
1017   }
1018
1019   return TRUE;
1020 }
1021
1022 void
1023 gst_mf_transform_set_new_sample_callback (GstMFTransform * object,
1024     GstMFTransformNewSampleCallback callback, gpointer user_data)
1025 {
1026   g_return_if_fail (GST_IS_MF_TRANSFORM (object));
1027
1028   object->callback = callback;
1029   object->user_data = user_data;
1030 }
1031
1032 static gboolean
1033 gst_mf_transform_close (GstMFTransform * object)
1034 {
1035   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1036
1037   gst_mf_transform_flush (object);
1038
1039   /* Otherwise IMFTransform will be alive even after we release the IMFTransform
1040    * below */
1041   if (object->activate)
1042     object->activate->ShutdownObject ();
1043
1044   if (object->callback_object) {
1045     object->callback_object->Release ();
1046     object->callback_object = nullptr;
1047   }
1048
1049   if (object->codec_api) {
1050     object->codec_api->Release ();
1051     object->codec_api = NULL;
1052   }
1053
1054   if (object->transform) {
1055     object->transform->Release ();
1056     object->transform = NULL;
1057   }
1058
1059   return TRUE;
1060 }
1061
1062 static const gchar *
1063 gst_mf_transform_event_type_to_string (MediaEventType event)
1064 {
1065   switch (event) {
1066     case METransformNeedInput:
1067       return "METransformNeedInput";
1068     case METransformHaveOutput:
1069       return "METransformHaveOutput";
1070     case METransformDrainComplete:
1071       return "METransformDrainComplete";
1072     case METransformMarker:
1073       return "METransformMarker";
1074     case METransformInputStreamStateChanged:
1075       return "METransformInputStreamStateChanged";
1076     default:
1077       break;
1078   }
1079
1080   return "Unknown";
1081 }
1082
1083 static HRESULT
1084 gst_mf_transform_on_event (MediaEventType event, GstMFTransform * self)
1085 {
1086   GST_TRACE_OBJECT (self, "Have event %s (%d)",
1087       gst_mf_transform_event_type_to_string (event), (gint) event);
1088
1089   switch (event) {
1090     case METransformNeedInput:
1091       g_mutex_lock (&self->event_lock);
1092       self->pending_need_input++;
1093       g_cond_broadcast (&self->event_cond);
1094       g_mutex_unlock (&self->event_lock);
1095       break;
1096     case METransformHaveOutput:
1097       gst_mf_transform_process_output (self);
1098       break;
1099     case METransformDrainComplete:
1100       g_mutex_lock (&self->event_lock);
1101       self->draining = FALSE;
1102       g_cond_broadcast (&self->event_cond);
1103       g_mutex_unlock (&self->event_lock);
1104       break;
1105     default:
1106       break;
1107   }
1108
1109   return S_OK;
1110 }
1111
1112 IMFActivate *
1113 gst_mf_transform_get_activate_handle (GstMFTransform * object)
1114 {
1115   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
1116
1117   return object->activate;
1118 }
1119
1120 IMFTransform *
1121 gst_mf_transform_get_transform_handle (GstMFTransform * object)
1122 {
1123   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
1124
1125   if (!object->transform) {
1126     GST_WARNING_OBJECT (object,
1127         "IMFTransform is not configured, open MFT first");
1128     return NULL;
1129   }
1130
1131   return object->transform;
1132 }
1133
1134 ICodecAPI *
1135 gst_mf_transform_get_codec_api_handle (GstMFTransform * object)
1136 {
1137   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
1138
1139   if (!object->codec_api) {
1140     GST_WARNING_OBJECT (object, "ICodecAPI is not configured, open MFT first");
1141     return NULL;
1142   }
1143
1144   return object->codec_api;
1145 }
1146
1147 gboolean
1148 gst_mf_transform_get_input_available_types (GstMFTransform * object,
1149     GList ** input_types)
1150 {
1151   IMFTransform *transform;
1152   HRESULT hr;
1153   DWORD index = 0;
1154   GList *list = NULL;
1155
1156   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1157   g_return_val_if_fail (input_types != NULL, FALSE);
1158
1159   transform = object->transform;
1160
1161   if (!transform) {
1162     GST_ERROR_OBJECT (object, "Should open first");
1163     return FALSE;
1164   }
1165
1166   do {
1167     IMFMediaType *type = NULL;
1168
1169     hr = transform->GetInputAvailableType (object->input_id, index, &type);
1170     if (SUCCEEDED (hr))
1171       list = g_list_append (list, type);
1172
1173     index++;
1174   } while (SUCCEEDED (hr));
1175
1176   *input_types = list;
1177
1178   return !!list;
1179 }
1180
1181 gboolean
1182 gst_mf_transform_get_output_available_types (GstMFTransform * object,
1183     GList ** output_types)
1184 {
1185   IMFTransform *transform;
1186   HRESULT hr;
1187   DWORD index = 0;
1188   GList *list = NULL;
1189
1190   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1191   g_return_val_if_fail (output_types != NULL, FALSE);
1192
1193   transform = object->transform;
1194
1195   if (!transform) {
1196     GST_ERROR_OBJECT (object, "Should open first");
1197     return FALSE;
1198   }
1199
1200   do {
1201     IMFMediaType *type;
1202
1203     hr = transform->GetOutputAvailableType (object->input_id, index, &type);
1204     if (SUCCEEDED (hr))
1205       list = g_list_append (list, type);
1206
1207     index++;
1208   } while (SUCCEEDED (hr));
1209
1210   *output_types = list;
1211
1212   return !!list;
1213 }
1214
1215 gboolean
1216 gst_mf_transform_set_input_type (GstMFTransform * object,
1217     IMFMediaType * input_type)
1218 {
1219   IMFTransform *transform;
1220   HRESULT hr;
1221
1222   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1223
1224   transform = object->transform;
1225
1226   if (!transform) {
1227     GST_ERROR_OBJECT (object, "Should open first");
1228     return FALSE;
1229   }
1230
1231   hr = transform->SetInputType (object->input_id, input_type, 0);
1232   if (!gst_mf_result (hr))
1233     return FALSE;
1234
1235   return TRUE;
1236 }
1237
1238 gboolean
1239 gst_mf_transform_set_output_type (GstMFTransform * object,
1240     IMFMediaType * output_type)
1241 {
1242   IMFTransform *transform;
1243   HRESULT hr;
1244
1245   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1246
1247   transform = object->transform;
1248
1249   if (!transform) {
1250     GST_ERROR_OBJECT (object, "Should open first");
1251     return FALSE;
1252   }
1253
1254   hr = transform->SetOutputType (object->output_id, output_type, 0);
1255   if (!gst_mf_result (hr)) {
1256     return FALSE;
1257   }
1258
1259   return TRUE;
1260 }
1261
1262 gboolean
1263 gst_mf_transform_get_input_current_type (GstMFTransform * object,
1264     IMFMediaType ** input_type)
1265 {
1266   IMFTransform *transform;
1267   HRESULT hr;
1268
1269   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1270   g_return_val_if_fail (input_type != NULL, FALSE);
1271
1272   transform = object->transform;
1273
1274   if (!transform) {
1275     GST_ERROR_OBJECT (object, "Should open first");
1276     return FALSE;
1277   }
1278
1279   hr = transform->GetInputCurrentType (object->input_id, input_type);
1280   if (!gst_mf_result (hr)) {
1281     return FALSE;
1282   }
1283
1284   return TRUE;
1285 }
1286
1287 gboolean
1288 gst_mf_transform_get_output_current_type (GstMFTransform * object,
1289     IMFMediaType ** output_type)
1290 {
1291   IMFTransform *transform;
1292   HRESULT hr;
1293
1294   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1295   g_return_val_if_fail (output_type != NULL, FALSE);
1296
1297   transform = object->transform;
1298
1299   if (!transform) {
1300     GST_ERROR_OBJECT (object, "Should open first");
1301     return FALSE;
1302   }
1303
1304   hr = transform->GetOutputCurrentType (object->output_id, output_type);
1305   if (!gst_mf_result (hr)) {
1306     return FALSE;
1307   }
1308
1309   return TRUE;
1310 }
1311
1312 GstMFTransform *
1313 gst_mf_transform_new (GstMFTransformEnumParams * params)
1314 {
1315   GstMFTransform *self;
1316
1317   g_return_val_if_fail (params != NULL, NULL);
1318
1319   self = (GstMFTransform *) g_object_new (GST_TYPE_MF_TRANSFORM_OBJECT,
1320       "enum-params", params, NULL);
1321
1322   if (!self->initialized) {
1323     gst_object_unref (self);
1324     return NULL;
1325   }
1326
1327   gst_object_ref_sink (self);
1328
1329   return self;
1330 }
1331
1332 gboolean
1333 gst_mf_transform_set_codec_api_uint32 (GstMFTransform * object,
1334     const GUID * api, guint32 value)
1335 {
1336   HRESULT hr;
1337   VARIANT var;
1338
1339   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1340   g_return_val_if_fail (api != NULL, FALSE);
1341
1342   if (!object->codec_api) {
1343     GST_WARNING_OBJECT (object, "codec api unavailable");
1344     return FALSE;
1345   }
1346
1347   VariantInit (&var);
1348   var.vt = VT_UI4;
1349   var.ulVal = value;
1350
1351   hr = object->codec_api->SetValue (api, &var);
1352   VariantClear (&var);
1353
1354   return gst_mf_result (hr);
1355 }
1356
1357 gboolean
1358 gst_mf_transform_set_codec_api_uint64 (GstMFTransform * object,
1359     const GUID * api, guint64 value)
1360 {
1361   HRESULT hr;
1362   VARIANT var;
1363
1364   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1365   g_return_val_if_fail (api != NULL, FALSE);
1366
1367   if (!object->codec_api) {
1368     GST_WARNING_OBJECT (object, "codec api unavailable");
1369     return FALSE;
1370   }
1371
1372   VariantInit (&var);
1373   var.vt = VT_UI8;
1374   var.ullVal = value;
1375
1376   hr = object->codec_api->SetValue (api, &var);
1377   VariantClear (&var);
1378
1379   return gst_mf_result (hr);
1380 }
1381
1382 gboolean
1383 gst_mf_transform_set_codec_api_boolean (GstMFTransform * object,
1384     const GUID * api, gboolean value)
1385 {
1386   HRESULT hr;
1387   VARIANT var;
1388
1389   g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1390   g_return_val_if_fail (api != NULL, FALSE);
1391
1392   if (!object->codec_api) {
1393     GST_WARNING_OBJECT (object, "codec api unavailable");
1394     return FALSE;
1395   }
1396
1397   VariantInit (&var);
1398   var.vt = VT_BOOL;
1399   var.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
1400
1401   hr = object->codec_api->SetValue (api, &var);
1402   VariantClear (&var);
1403
1404   return gst_mf_result (hr);
1405 }