2 * Copyright (C) 2020 Seungha Yang <seungha.yang@navercorp.com>
3 * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "gstmfconfig.h"
29 #include "gstmftransform.h"
30 #include "gstmfutils.h"
35 using namespace Microsoft::WRL;
39 GST_DEBUG_CATEGORY_EXTERN (gst_mf_transform_debug);
40 #define GST_CAT_DEFAULT gst_mf_transform_debug
44 static GModule *mf_plat_module = NULL;
45 typedef HRESULT (__stdcall *pMFTEnum2) (GUID guidCategory,
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;
55 gst_mf_transform_load_library (void)
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);
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;
70 GST_INFO ("MFTEnum2 symbol is available");
74 g_once_init_leave (&_init, 1);
78 return ! !GstMFTEnum2Func;
81 typedef HRESULT (*GstMFTransformAsyncCallbackOnEvent) (MediaEventType event,
84 class GstMFTransformAsyncCallback : public IMFAsyncCallback
88 CreateInstance (IMFTransform * mft,
89 GstMFTransformAsyncCallbackOnEvent event_cb, GstObject * client,
90 GstMFTransformAsyncCallback ** callback)
93 GstMFTransformAsyncCallback *self;
95 if (!mft || !callback)
98 self = new GstMFTransformAsyncCallback ();
101 return E_OUTOFMEMORY;
103 hr = self->Initialize (mft, event_cb, client);
105 if (!gst_mf_result (hr)) {
121 /* we are running already */
127 return gen_->BeginGetEvent (this, nullptr);
140 QueryInterface (REFIID riid, void ** object)
145 STDMETHODIMP_ (ULONG)
148 GST_TRACE ("%p, %d", this, ref_count_);
149 return InterlockedIncrement (&ref_count_);
152 STDMETHODIMP_ (ULONG)
157 GST_TRACE ("%p, %d", this, ref_count_);
158 ref_count = InterlockedDecrement (&ref_count_);
160 if (ref_count == 0) {
161 GST_TRACE ("Delete instance %p", this);
168 /* IMFAsyncCallback */
170 GetParameters (DWORD * flags, DWORD * queue)
172 /* this callback could be blocked */
173 *flags = MFASYNC_BLOCKING_CALLBACK;
174 *queue = MFASYNC_CALLBACK_QUEUE_MULTITHREADED;
179 Invoke (IMFAsyncResult * async_result)
181 ComPtr<IMFMediaEvent> event;
185 hr = gen_->EndGetEvent (async_result, &event);
187 if (!gst_mf_result (hr))
192 GstObject *client = nullptr;
193 hr = event->GetType(&type);
194 if (!gst_mf_result (hr))
200 client = (GstObject *) g_weak_ref_get (&client_);
204 hr = event_cb_ (type, client);
205 gst_object_unref (client);
206 if (!gst_mf_result (hr))
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)
217 gen_->BeginGetEvent(this, nullptr);
223 GstMFTransformAsyncCallback ()
227 g_weak_ref_init (&client_, NULL);
230 ~GstMFTransformAsyncCallback ()
232 g_weak_ref_clear (&client_);
236 Initialize (IMFTransform * mft, GstMFTransformAsyncCallbackOnEvent event_cb,
239 HRESULT hr = mft->QueryInterface(IID_PPV_ARGS(&gen_));
241 if (!gst_mf_result (hr))
244 event_cb_ = event_cb;
245 g_weak_ref_set (&client_, client);
252 ComPtr<IMFMediaEventGenerator> gen_;
253 GstMFTransformAsyncCallbackOnEvent event_cb_;
269 struct _GstMFTransform
272 gboolean initialized;
274 GstMFTransformEnumParams enum_params;
278 gboolean d3d11_aware;
280 IMFActivate *activate;
281 IMFTransform *transform;
282 ICodecAPI *codec_api;
283 GstMFTransformAsyncCallback *callback_object;
285 GQueue *output_queue;
292 gint pending_need_input;
299 GMainContext *context;
304 GstMFTransformNewSampleCallback callback;
308 #define gst_mf_transform_parent_class parent_class
309 G_DEFINE_TYPE (GstMFTransform, gst_mf_transform, GST_TYPE_OBJECT);
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);
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);
324 gst_mf_transform_class_init (GstMFTransformClass * klass)
326 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
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;
333 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
334 g_param_spec_string ("device-name", "device-name",
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)));
353 gst_mf_transform_init (GstMFTransform * self)
355 self->output_queue = g_queue_new ();
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);
362 self->context = g_main_context_new ();
363 self->loop = g_main_loop_new (self->context, FALSE);
367 gst_mf_transform_constructed (GObject * object)
369 GstMFTransform *self = GST_MF_TRANSFORM (object);
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);
379 G_OBJECT_CLASS (parent_class)->constructed (object);
383 gst_mf_transform_clear_enum_params (GstMFTransformEnumParams * params)
385 g_free (params->input_typeinfo);
386 params->input_typeinfo = NULL;
388 g_free (params->output_typeinfo);
389 params->output_typeinfo = NULL;
393 release_mf_sample (IMFSample * sample)
400 gst_mf_transform_finalize (GObject * object)
402 GstMFTransform *self = GST_MF_TRANSFORM (object);
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);
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);
417 G_OBJECT_CLASS (parent_class)->finalize (object);
421 gst_mf_transform_get_property (GObject * object, guint prop_id,
422 GValue * value, GParamSpec * pspec)
424 GstMFTransform *self = GST_MF_TRANSFORM (object);
427 case PROP_DEVICE_NAME:
428 g_value_set_string (value, self->device_name);
431 g_value_set_boolean (value, self->hardware);
433 case PROP_D3D11_AWARE:
434 g_value_set_boolean (value, self->d3d11_aware);
437 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
443 gst_mf_transform_set_property (GObject * object, guint prop_id,
444 const GValue * value, GParamSpec * pspec)
446 GstMFTransform *self = GST_MF_TRANSFORM (object);
449 case PROP_ENUM_PARAMS:
451 GstMFTransformEnumParams *params;
452 params = (GstMFTransformEnumParams *) g_value_get_pointer (value);
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));
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));
473 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
479 gst_mf_transform_main_loop_running_cb (GstMFTransform * self)
481 GST_TRACE_OBJECT (self, "Main loop running now");
483 g_mutex_lock (&self->lock);
484 g_cond_signal (&self->cond);
485 g_mutex_unlock (&self->lock);
487 return G_SOURCE_REMOVE;
491 gst_mf_transform_thread_func (GstMFTransform * self)
494 IMFActivate **devices = NULL;
495 UINT32 num_devices, i;
499 CoInitializeEx (NULL, COINIT_MULTITHREADED);
501 g_main_context_push_thread_default (self->context);
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);
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;
516 hr = MFCreateAttributes (&attr, 1);
517 if (!gst_mf_result (hr)) {
518 GST_ERROR_OBJECT (self, "Couldn't create IMFAttributes");
522 GST_INFO_OBJECT (self,
523 "Enumerating MFT for adapter-luid %" G_GINT64_FORMAT,
524 self->enum_params.adapter_luid);
526 luid.LowPart = (DWORD) (self->enum_params.adapter_luid & 0xffffffff);
527 luid.HighPart = (LONG) (self->enum_params.adapter_luid >> 32);
529 hr = attr->SetBlob (GST_GUID_MFT_ENUM_ADAPTER_LUID, (BYTE *) & luid,
531 if (!gst_mf_result (hr)) {
532 GST_ERROR_OBJECT (self, "Couldn't set MFT_ENUM_ADAPTER_LUID");
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);
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);
547 if (!gst_mf_result (hr)) {
548 GST_WARNING_OBJECT (self, "MFTEnumEx failure");
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 ();
558 CoTaskMemFree (devices);
562 self->activate = devices[self->enum_params.device_index];
563 self->activate->AddRef ();
565 for (i = 0; i < num_devices; i++)
566 devices[i]->Release ();
568 hr = self->activate->GetAllocatedString (MFT_FRIENDLY_NAME_Attribute,
571 if (gst_mf_result (hr)) {
572 self->device_name = g_utf16_to_utf8 ((const gunichar2 *) name,
573 -1, NULL, NULL, NULL);
575 GST_INFO_OBJECT (self, "Open device %s", self->device_name);
576 CoTaskMemFree (name);
579 CoTaskMemFree (devices);
581 self->hardware = !!(self->enum_params.enum_flags & MFT_ENUM_FLAG_HARDWARE);
582 self->initialized = TRUE;
585 GST_TRACE_OBJECT (self, "Starting main loop");
586 g_main_loop_run (self->loop);
587 GST_TRACE_OBJECT (self, "Stopped main loop");
589 g_main_context_pop_thread_default (self->context);
591 /* cleanup internal COM object here */
592 gst_mf_transform_close (self);
594 if (self->activate) {
595 self->activate->Release ();
596 self->activate = NULL;
605 gst_mf_transform_process_output (GstMFTransform * self)
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;
615 GST_TRACE_OBJECT (self, "Process output");
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;
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;
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;
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;
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;
646 out_data.pSample = new_sample.Detach ();
649 out_data.dwStreamID = stream_id;
651 hr = transform->ProcessOutput (0, 1, &out_data, &status);
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;
659 GST_DEBUG_OBJECT (self, "Stream change, set output type again");
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;
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;
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;
682 GST_ERROR_OBJECT (self, "ProcessOutput error, hr 0x%x", hr);
683 ret = GST_FLOW_ERROR;
688 if (ret != GST_FLOW_OK) {
689 if (out_data.pSample)
690 out_data.pSample->Release ();
695 if (!out_data.pSample) {
696 GST_WARNING_OBJECT (self, "No output sample");
700 if (self->callback) {
701 self->callback (self, out_data.pSample, self->user_data);
702 out_data.pSample->Release ();
706 g_queue_push_tail (self->output_queue, out_data.pSample);
711 /* Must be called with event_lock */
713 gst_mf_transform_process_input_sync (GstMFTransform * self, IMFSample * sample)
717 hr = self->transform->ProcessInput (self->output_id, sample, 0);
720 self->pending_need_input--;
722 return gst_mf_result (hr);
726 gst_mf_transform_process_input (GstMFTransform * object, IMFSample * sample)
729 gboolean ret = FALSE;
731 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
732 g_return_val_if_fail (sample != NULL, FALSE);
734 GST_TRACE_OBJECT (object, "Process input");
736 if (!object->transform)
739 g_mutex_lock (&object->event_lock);
740 if (!object->running) {
741 object->pending_need_input = 0;
743 hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_START_OF_STREAM,
745 if (!gst_mf_result (hr)) {
746 GST_ERROR_OBJECT (object, "Cannot post start-of-stream message");
750 hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
752 if (!gst_mf_result (hr)) {
753 GST_ERROR_OBJECT (object, "Cannot post begin-stream message");
757 if (object->callback_object) {
758 hr = object->callback_object->BeginGetEvent ();
759 if (!gst_mf_result (hr)) {
760 GST_ERROR_OBJECT (object, "BeginGetEvent failed");
765 GST_DEBUG_OBJECT (object, "MFT is running now");
767 object->running = TRUE;
768 object->flushing = FALSE;
771 /* Wait METransformNeedInput event. While waiting METransformNeedInput
772 * event, we can still output data if MFT notifyes METransformHaveOutput
774 if (object->hardware) {
775 while (object->pending_need_input == 0 && !object->flushing)
776 g_cond_wait (&object->event_cond, &object->event_lock);
779 if (object->flushing) {
780 GST_DEBUG_OBJECT (object, "We are flushing");
785 ret = gst_mf_transform_process_input_sync (object, sample);
788 g_mutex_unlock (&object->event_lock);
794 gst_mf_transform_get_output (GstMFTransform * object, IMFSample ** sample)
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
803 g_return_val_if_fail (!object->hardware, GST_FLOW_ERROR);
805 if (!object->transform)
806 return GST_FLOW_ERROR;
808 ret = gst_mf_transform_process_output (object);
810 if (ret != GST_MF_TRANSFORM_FLOW_NEED_DATA && ret != GST_FLOW_OK)
813 if (g_queue_is_empty (object->output_queue))
814 return GST_MF_TRANSFORM_FLOW_NEED_DATA;
816 *sample = (IMFSample *) g_queue_pop_head (object->output_queue);
822 gst_mf_transform_flush (GstMFTransform * object)
824 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
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);
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 ();
839 if (object->running) {
840 object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_FLUSH, 0);
843 object->pending_need_input = 0;
846 object->running = FALSE;
848 while (!g_queue_is_empty (object->output_queue)) {
849 IMFSample *sample = (IMFSample *) g_queue_pop_head (object->output_queue);
857 gst_mf_transform_drain (GstMFTransform * object)
861 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
863 if (!object->transform || !object->running)
866 object->running = FALSE;
867 object->draining = TRUE;
869 GST_DEBUG_OBJECT (object, "Start drain");
871 object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_DRAIN, 0);
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);
880 ret = gst_mf_transform_process_output (object);
881 } while (ret == GST_FLOW_OK);
884 GST_DEBUG_OBJECT (object, "End drain");
886 object->draining = FALSE;
887 object->pending_need_input = 0;
894 GstMFTransform *object;
897 } GstMFTransformOpenData;
900 gst_mf_transform_open_internal (GstMFTransformOpenData * data)
902 GstMFTransform *object = data->object;
907 gst_mf_transform_close (object);
908 hr = object->activate->ActivateObject (IID_PPV_ARGS (&object->transform));
910 if (!gst_mf_result (hr)) {
911 GST_WARNING_OBJECT (object, "Couldn't open MFT");
915 if (object->hardware) {
916 ComPtr < IMFAttributes > attr;
917 UINT32 supports_d3d11 = 0;
919 hr = object->transform->GetAttributes (attr.GetAddressOf ());
920 if (!gst_mf_result (hr)) {
921 GST_ERROR_OBJECT (object, "Couldn't get attribute object");
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");
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;
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);
944 if (!object->callback_object) {
945 GST_ERROR_OBJECT (object, "IMFMediaEventGenerator unavailable");
950 hr = object->transform->GetStreamIDs (1, &object->input_id, 1,
952 if (hr == E_NOTIMPL) {
953 object->input_id = 0;
954 object->output_id = 0;
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");
966 gst_mf_transform_close (object);
968 g_mutex_lock (&object->lock);
969 data->invoked = TRUE;
970 g_cond_broadcast (&object->cond);
971 g_mutex_unlock (&object->lock);
973 return G_SOURCE_REMOVE;
977 gst_mf_transform_open (GstMFTransform * object)
979 GstMFTransformOpenData data;
981 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
982 g_return_val_if_fail (object->activate != NULL, FALSE);
984 data.object = object;
985 data.invoked = FALSE;
988 g_main_context_invoke (object->context,
989 (GSourceFunc) gst_mf_transform_open_internal, &data);
991 g_mutex_lock (&object->lock);
992 while (!data.invoked)
993 g_cond_wait (&object->cond, &object->lock);
994 g_mutex_unlock (&object->lock);
1000 gst_mf_transform_set_device_manager (GstMFTransform * object,
1001 IMFDXGIDeviceManager * manager)
1005 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1007 if (!object->transform) {
1008 GST_ERROR_OBJECT (object, "IMFTransform is not configured yet");
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");
1023 gst_mf_transform_set_new_sample_callback (GstMFTransform * object,
1024 GstMFTransformNewSampleCallback callback, gpointer user_data)
1026 g_return_if_fail (GST_IS_MF_TRANSFORM (object));
1028 object->callback = callback;
1029 object->user_data = user_data;
1033 gst_mf_transform_close (GstMFTransform * object)
1035 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1037 gst_mf_transform_flush (object);
1039 /* Otherwise IMFTransform will be alive even after we release the IMFTransform
1041 if (object->activate)
1042 object->activate->ShutdownObject ();
1044 if (object->callback_object) {
1045 object->callback_object->Release ();
1046 object->callback_object = nullptr;
1049 if (object->codec_api) {
1050 object->codec_api->Release ();
1051 object->codec_api = NULL;
1054 if (object->transform) {
1055 object->transform->Release ();
1056 object->transform = NULL;
1063 gst_mf_transform_event_type_to_string (MediaEventType 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";
1084 gst_mf_transform_on_event (MediaEventType event, GstMFTransform * self)
1086 GST_TRACE_OBJECT (self, "Have event %s (%d)",
1087 gst_mf_transform_event_type_to_string (event), (gint) 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);
1096 case METransformHaveOutput:
1097 gst_mf_transform_process_output (self);
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);
1113 gst_mf_transform_get_activate_handle (GstMFTransform * object)
1115 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
1117 return object->activate;
1121 gst_mf_transform_get_transform_handle (GstMFTransform * object)
1123 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
1125 if (!object->transform) {
1126 GST_WARNING_OBJECT (object,
1127 "IMFTransform is not configured, open MFT first");
1131 return object->transform;
1135 gst_mf_transform_get_codec_api_handle (GstMFTransform * object)
1137 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
1139 if (!object->codec_api) {
1140 GST_WARNING_OBJECT (object, "ICodecAPI is not configured, open MFT first");
1144 return object->codec_api;
1148 gst_mf_transform_get_input_available_types (GstMFTransform * object,
1149 GList ** input_types)
1151 IMFTransform *transform;
1156 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1157 g_return_val_if_fail (input_types != NULL, FALSE);
1159 transform = object->transform;
1162 GST_ERROR_OBJECT (object, "Should open first");
1167 IMFMediaType *type = NULL;
1169 hr = transform->GetInputAvailableType (object->input_id, index, &type);
1171 list = g_list_append (list, type);
1174 } while (SUCCEEDED (hr));
1176 *input_types = list;
1182 gst_mf_transform_get_output_available_types (GstMFTransform * object,
1183 GList ** output_types)
1185 IMFTransform *transform;
1190 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1191 g_return_val_if_fail (output_types != NULL, FALSE);
1193 transform = object->transform;
1196 GST_ERROR_OBJECT (object, "Should open first");
1203 hr = transform->GetOutputAvailableType (object->input_id, index, &type);
1205 list = g_list_append (list, type);
1208 } while (SUCCEEDED (hr));
1210 *output_types = list;
1216 gst_mf_transform_set_input_type (GstMFTransform * object,
1217 IMFMediaType * input_type)
1219 IMFTransform *transform;
1222 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1224 transform = object->transform;
1227 GST_ERROR_OBJECT (object, "Should open first");
1231 hr = transform->SetInputType (object->input_id, input_type, 0);
1232 if (!gst_mf_result (hr))
1239 gst_mf_transform_set_output_type (GstMFTransform * object,
1240 IMFMediaType * output_type)
1242 IMFTransform *transform;
1245 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1247 transform = object->transform;
1250 GST_ERROR_OBJECT (object, "Should open first");
1254 hr = transform->SetOutputType (object->output_id, output_type, 0);
1255 if (!gst_mf_result (hr)) {
1263 gst_mf_transform_get_input_current_type (GstMFTransform * object,
1264 IMFMediaType ** input_type)
1266 IMFTransform *transform;
1269 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1270 g_return_val_if_fail (input_type != NULL, FALSE);
1272 transform = object->transform;
1275 GST_ERROR_OBJECT (object, "Should open first");
1279 hr = transform->GetInputCurrentType (object->input_id, input_type);
1280 if (!gst_mf_result (hr)) {
1288 gst_mf_transform_get_output_current_type (GstMFTransform * object,
1289 IMFMediaType ** output_type)
1291 IMFTransform *transform;
1294 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1295 g_return_val_if_fail (output_type != NULL, FALSE);
1297 transform = object->transform;
1300 GST_ERROR_OBJECT (object, "Should open first");
1304 hr = transform->GetOutputCurrentType (object->output_id, output_type);
1305 if (!gst_mf_result (hr)) {
1313 gst_mf_transform_new (GstMFTransformEnumParams * params)
1315 GstMFTransform *self;
1317 g_return_val_if_fail (params != NULL, NULL);
1319 self = (GstMFTransform *) g_object_new (GST_TYPE_MF_TRANSFORM_OBJECT,
1320 "enum-params", params, NULL);
1322 if (!self->initialized) {
1323 gst_object_unref (self);
1327 gst_object_ref_sink (self);
1333 gst_mf_transform_set_codec_api_uint32 (GstMFTransform * object,
1334 const GUID * api, guint32 value)
1339 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1340 g_return_val_if_fail (api != NULL, FALSE);
1342 if (!object->codec_api) {
1343 GST_WARNING_OBJECT (object, "codec api unavailable");
1351 hr = object->codec_api->SetValue (api, &var);
1352 VariantClear (&var);
1354 return gst_mf_result (hr);
1358 gst_mf_transform_set_codec_api_uint64 (GstMFTransform * object,
1359 const GUID * api, guint64 value)
1364 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1365 g_return_val_if_fail (api != NULL, FALSE);
1367 if (!object->codec_api) {
1368 GST_WARNING_OBJECT (object, "codec api unavailable");
1376 hr = object->codec_api->SetValue (api, &var);
1377 VariantClear (&var);
1379 return gst_mf_result (hr);
1383 gst_mf_transform_set_codec_api_boolean (GstMFTransform * object,
1384 const GUID * api, gboolean value)
1389 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1390 g_return_val_if_fail (api != NULL, FALSE);
1392 if (!object->codec_api) {
1393 GST_WARNING_OBJECT (object, "codec api unavailable");
1399 var.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
1401 hr = object->codec_api->SetValue (api, &var);
1402 VariantClear (&var);
1404 return gst_mf_result (hr);