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"
34 using namespace Microsoft::WRL;
38 GST_DEBUG_CATEGORY_EXTERN (gst_mf_transform_debug);
39 #define GST_CAT_DEFAULT gst_mf_transform_debug
43 static GModule *mf_plat_module = NULL;
44 typedef HRESULT (__stdcall *pMFTEnum2) (GUID guidCategory,
46 const MFT_REGISTER_TYPE_INFO * pInputType,
47 const MFT_REGISTER_TYPE_INFO * pOutputType,
48 IMFAttributes * pAttributes,
49 IMFActivate *** pppMFTActivate,
50 UINT32 * pnumMFTActivate);
51 static pMFTEnum2 GstMFTEnum2Func = NULL;
54 gst_mf_transform_load_library (void)
57 static volatile gsize _init = 0;
58 if (g_once_init_enter (&_init)) {
59 mf_plat_module = g_module_open ("mfplat.dll", G_MODULE_BIND_LAZY);
62 if (!g_module_symbol (mf_plat_module, "MFTEnum2",
63 (gpointer *) & GstMFTEnum2Func)) {
64 GST_WARNING ("Cannot load MFTEnum2 symbol");
65 g_module_close (mf_plat_module);
66 mf_plat_module = NULL;
67 GstMFTEnum2Func = NULL;
69 GST_INFO ("MFTEnum2 symbol is available");
73 g_once_init_leave (&_init, 1);
77 return ! !GstMFTEnum2Func;
80 typedef HRESULT (*GstMFTransformAsyncCallbackOnEvent) (MediaEventType event,
83 class GstMFTransformAsyncCallback : public IMFAsyncCallback
87 CreateInstance (IMFTransform * mft,
88 GstMFTransformAsyncCallbackOnEvent event_cb, GstObject * client,
89 GstMFTransformAsyncCallback ** callback)
92 GstMFTransformAsyncCallback *self;
94 if (!mft || !callback)
97 self = new GstMFTransformAsyncCallback ();
100 return E_OUTOFMEMORY;
102 hr = self->Initialize (mft, event_cb, client);
104 if (!gst_mf_result (hr)) {
120 /* we are running already */
126 return gen_->BeginGetEvent (this, nullptr);
139 QueryInterface (REFIID riid, void ** object)
144 STDMETHODIMP_ (ULONG)
147 GST_TRACE ("%p, %d", this, ref_count_);
148 return InterlockedIncrement (&ref_count_);
151 STDMETHODIMP_ (ULONG)
156 GST_TRACE ("%p, %d", this, ref_count_);
157 ref_count = InterlockedDecrement (&ref_count_);
159 if (ref_count == 0) {
160 GST_TRACE ("Delete instance %p", this);
167 /* IMFAsyncCallback */
169 GetParameters (DWORD * flags, DWORD * queue)
171 /* this callback could be blocked */
172 *flags = MFASYNC_BLOCKING_CALLBACK;
173 *queue = MFASYNC_CALLBACK_QUEUE_MULTITHREADED;
178 Invoke (IMFAsyncResult * async_result)
180 ComPtr<IMFMediaEvent> event;
184 hr = gen_->EndGetEvent (async_result, &event);
186 if (!gst_mf_result (hr))
191 GstObject *client = nullptr;
192 hr = event->GetType(&type);
193 if (!gst_mf_result (hr))
199 client = (GstObject *) g_weak_ref_get (&client_);
203 hr = event_cb_ (type, client);
204 gst_object_unref (client);
205 if (!gst_mf_result (hr))
208 /* On Drain event, this callback object will stop calling BeginGetEvent()
209 * since there might be no more following events. Client should call
210 * our BeginGetEvent() method to run again */
211 if (type == METransformDrainComplete)
216 gen_->BeginGetEvent(this, nullptr);
222 GstMFTransformAsyncCallback ()
226 g_weak_ref_init (&client_, NULL);
229 ~GstMFTransformAsyncCallback ()
231 g_weak_ref_clear (&client_);
235 Initialize (IMFTransform * mft, GstMFTransformAsyncCallbackOnEvent event_cb,
238 HRESULT hr = mft->QueryInterface(IID_PPV_ARGS(&gen_));
240 if (!gst_mf_result (hr))
243 event_cb_ = event_cb;
244 g_weak_ref_set (&client_, client);
250 volatile ULONG ref_count_;
251 ComPtr<IMFMediaEventGenerator> gen_;
252 GstMFTransformAsyncCallbackOnEvent event_cb_;
267 struct _GstMFTransform
270 gboolean initialized;
272 GstMFTransformEnumParams enum_params;
276 gboolean d3d11_aware;
278 IMFActivate *activate;
279 IMFTransform *transform;
280 ICodecAPI * codec_api;
281 GstMFTransformAsyncCallback *callback_object;
283 GQueue *output_queue;
290 gint pending_need_input;
297 GMainContext *context;
302 GstMFTransformNewSampleCallback callback;
306 #define gst_mf_transform_parent_class parent_class
307 G_DEFINE_TYPE (GstMFTransform, gst_mf_transform, GST_TYPE_OBJECT);
309 static void gst_mf_transform_constructed (GObject * object);
310 static void gst_mf_transform_finalize (GObject * object);
311 static void gst_mf_transform_get_property (GObject * object,
312 guint prop_id, GValue * value, GParamSpec * pspec);
313 static void gst_mf_transform_set_property (GObject * object,
314 guint prop_id, const GValue * value, GParamSpec * pspec);
316 static gpointer gst_mf_transform_thread_func (GstMFTransform * self);
317 static gboolean gst_mf_transform_close (GstMFTransform * self);
318 static HRESULT gst_mf_transform_on_event (MediaEventType event,
319 GstMFTransform * self);
322 gst_mf_transform_class_init (GstMFTransformClass * klass)
324 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
326 gobject_class->constructed = gst_mf_transform_constructed;
327 gobject_class->finalize = gst_mf_transform_finalize;
328 gobject_class->get_property = gst_mf_transform_get_property;
329 gobject_class->set_property = gst_mf_transform_set_property;
331 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
332 g_param_spec_string ("device-name", "device-name",
334 (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
335 g_object_class_install_property (gobject_class, PROP_HARDWARE,
336 g_param_spec_boolean ("hardware", "Hardware",
337 "Whether hardware device or not", FALSE,
338 (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
339 g_object_class_install_property (gobject_class, PROP_ENUM_PARAMS,
340 g_param_spec_pointer ("enum-params", "Enum Params",
341 "GstMFTransformEnumParams for MFTEnumEx",
342 (GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
343 G_PARAM_STATIC_STRINGS)));
344 g_object_class_install_property (gobject_class, PROP_D3D11_AWARE,
345 g_param_spec_boolean ("d3d11-aware", "D3D11 Aware",
346 "Whether Direct3D11 supports Direct3D11", FALSE,
347 (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
351 gst_mf_transform_init (GstMFTransform * self)
353 self->output_queue = g_queue_new ();
355 g_mutex_init (&self->lock);
356 g_mutex_init (&self->event_lock);
357 g_cond_init (&self->cond);
358 g_cond_init (&self->event_cond);
360 self->context = g_main_context_new ();
361 self->loop = g_main_loop_new (self->context, FALSE);
365 gst_mf_transform_constructed (GObject * object)
367 GstMFTransform *self = GST_MF_TRANSFORM (object);
369 /* Create thread so that ensure COM thread can be MTA thread */
370 g_mutex_lock (&self->lock);
371 self->thread = g_thread_new ("GstMFTransform",
372 (GThreadFunc) gst_mf_transform_thread_func, self);
373 while (!g_main_loop_is_running (self->loop))
374 g_cond_wait (&self->cond, &self->lock);
375 g_mutex_unlock (&self->lock);
377 G_OBJECT_CLASS (parent_class)->constructed (object);
381 gst_mf_transform_clear_enum_params (GstMFTransformEnumParams *params)
383 g_free (params->input_typeinfo);
384 params->input_typeinfo = NULL;
386 g_free (params->output_typeinfo);
387 params->output_typeinfo = NULL;
391 release_mf_sample (IMFSample * sample)
398 gst_mf_transform_finalize (GObject * object)
400 GstMFTransform *self = GST_MF_TRANSFORM (object);
402 g_main_loop_quit (self->loop);
403 g_thread_join (self->thread);
404 g_main_loop_unref (self->loop);
405 g_main_context_unref (self->context);
407 g_queue_free_full (self->output_queue, (GDestroyNotify) release_mf_sample);
408 gst_mf_transform_clear_enum_params (&self->enum_params);
409 g_free (self->device_name);
410 g_mutex_clear (&self->lock);
411 g_mutex_clear (&self->event_lock);
412 g_cond_clear (&self->cond);
413 g_cond_clear (&self->event_cond);
415 G_OBJECT_CLASS (parent_class)->finalize (object);
419 gst_mf_transform_get_property (GObject * object, guint prop_id,
420 GValue * value, GParamSpec * pspec)
422 GstMFTransform *self = GST_MF_TRANSFORM (object);
425 case PROP_DEVICE_NAME:
426 g_value_set_string (value, self->device_name);
429 g_value_set_boolean (value, self->hardware);
431 case PROP_D3D11_AWARE:
432 g_value_set_boolean (value, self->d3d11_aware);
435 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
441 gst_mf_transform_set_property (GObject * object, guint prop_id,
442 const GValue * value, GParamSpec * pspec)
444 GstMFTransform *self = GST_MF_TRANSFORM (object);
447 case PROP_ENUM_PARAMS:
449 GstMFTransformEnumParams *params;
450 params = (GstMFTransformEnumParams *) g_value_get_pointer (value);
452 gst_mf_transform_clear_enum_params (&self->enum_params);
453 self->enum_params.category = params->category;
454 self->enum_params.enum_flags = params->enum_flags;
455 self->enum_params.device_index = params->device_index;
456 self->enum_params.adapter_luid = params->adapter_luid;
457 if (params->input_typeinfo) {
458 self->enum_params.input_typeinfo = g_new0 (MFT_REGISTER_TYPE_INFO, 1);
459 memcpy (self->enum_params.input_typeinfo, params->input_typeinfo,
460 sizeof (MFT_REGISTER_TYPE_INFO));
463 if (params->output_typeinfo) {
464 self->enum_params.output_typeinfo = g_new0 (MFT_REGISTER_TYPE_INFO, 1);
465 memcpy (self->enum_params.output_typeinfo, params->output_typeinfo,
466 sizeof (MFT_REGISTER_TYPE_INFO));
471 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
477 gst_mf_transform_main_loop_running_cb (GstMFTransform * self)
479 GST_TRACE_OBJECT (self, "Main loop running now");
481 g_mutex_lock (&self->lock);
482 g_cond_signal (&self->cond);
483 g_mutex_unlock (&self->lock);
485 return G_SOURCE_REMOVE;
489 gst_mf_transform_thread_func (GstMFTransform * self)
492 IMFActivate **devices = NULL;
493 UINT32 num_devices, i;
497 CoInitializeEx (NULL, COINIT_MULTITHREADED);
499 g_main_context_push_thread_default (self->context);
501 source = g_idle_source_new ();
502 g_source_set_callback (source,
503 (GSourceFunc) gst_mf_transform_main_loop_running_cb, self, NULL);
504 g_source_attach (source, self->context);
505 g_source_unref (source);
507 /* NOTE: MFTEnum2 is desktop only and requires Windows 10 */
508 #if GST_MF_HAVE_D3D11
509 if (GstMFTEnum2Func && self->enum_params.adapter_luid &&
510 (self->enum_params.enum_flags & MFT_ENUM_FLAG_HARDWARE) != 0) {
511 ComPtr<IMFAttributes> attr;
514 hr = MFCreateAttributes (&attr, 1);
515 if (!gst_mf_result (hr)) {
516 GST_ERROR_OBJECT (self, "Couldn't create IMFAttributes");
520 GST_INFO_OBJECT (self,
521 "Enumerating MFT for adapter-luid %" G_GINT64_FORMAT,
522 self->enum_params.adapter_luid);
524 luid.LowPart = (DWORD) (self->enum_params.adapter_luid & 0xffffffff);
525 luid.HighPart = (LONG) (self->enum_params.adapter_luid >> 32);
527 hr = attr->SetBlob (GST_GUID_MFT_ENUM_ADAPTER_LUID, (BYTE *) &luid,
529 if (!gst_mf_result (hr)) {
530 GST_ERROR_OBJECT (self, "Couldn't set MFT_ENUM_ADAPTER_LUID");
534 hr = GstMFTEnum2Func (self->enum_params.category,
535 self->enum_params.enum_flags, self->enum_params.input_typeinfo,
536 self->enum_params.output_typeinfo, attr.Get (), &devices, &num_devices);
540 hr = MFTEnumEx (self->enum_params.category, self->enum_params.enum_flags,
541 self->enum_params.input_typeinfo, self->enum_params.output_typeinfo,
542 &devices, &num_devices);
545 if (!gst_mf_result (hr)) {
546 GST_WARNING_OBJECT (self, "MFTEnumEx failure");
550 if (num_devices == 0 || self->enum_params.device_index >= num_devices) {
551 GST_WARNING_OBJECT (self, "No available device at index %d",
552 self->enum_params.device_index);
553 for (i = 0; i < num_devices; i++)
554 devices[i]->Release ();
556 CoTaskMemFree (devices);
560 self->activate = devices[self->enum_params.device_index];
561 self->activate->AddRef ();
563 for (i = 0; i < num_devices; i++)
564 devices[i]->Release ();
566 hr = self->activate->GetAllocatedString (MFT_FRIENDLY_NAME_Attribute,
569 if (gst_mf_result (hr)) {
570 self->device_name = g_utf16_to_utf8 ((const gunichar2 *) name,
571 -1, NULL, NULL, NULL);
573 GST_INFO_OBJECT (self, "Open device %s", self->device_name);
574 CoTaskMemFree (name);
577 CoTaskMemFree (devices);
579 self->hardware = ! !(self->enum_params.enum_flags & MFT_ENUM_FLAG_HARDWARE);
580 self->initialized = TRUE;
583 GST_TRACE_OBJECT (self, "Starting main loop");
584 g_main_loop_run (self->loop);
585 GST_TRACE_OBJECT (self, "Stopped main loop");
587 g_main_context_pop_thread_default (self->context);
589 /* cleanup internal COM object here */
590 gst_mf_transform_close (self);
592 if (self->activate) {
593 self->activate->Release ();
594 self->activate = NULL;
603 gst_mf_transform_process_output (GstMFTransform * self)
607 IMFTransform *transform = self->transform;
608 DWORD stream_id = self->output_id;
609 MFT_OUTPUT_STREAM_INFO out_stream_info = { 0 };
610 MFT_OUTPUT_DATA_BUFFER out_data = { 0 };
611 GstFlowReturn ret = GST_FLOW_OK;
613 GST_TRACE_OBJECT (self, "Process output");
615 hr = transform->GetOutputStreamInfo (stream_id, &out_stream_info);
616 if (!gst_mf_result (hr)) {
617 GST_ERROR_OBJECT (self, "Couldn't get output stream info");
618 return GST_FLOW_ERROR;
621 if ((out_stream_info.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES |
622 MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0) {
623 ComPtr<IMFMediaBuffer> buffer;
624 ComPtr<IMFSample> new_sample;
626 hr = MFCreateMemoryBuffer (out_stream_info.cbSize,
627 buffer.GetAddressOf ());
628 if (!gst_mf_result (hr)) {
629 GST_ERROR_OBJECT (self, "Couldn't create memory buffer");
630 return GST_FLOW_ERROR;
633 hr = MFCreateSample (new_sample.GetAddressOf ());
634 if (!gst_mf_result (hr)) {
635 GST_ERROR_OBJECT (self, "Couldn't create sample");
636 return GST_FLOW_ERROR;
639 hr = new_sample->AddBuffer (buffer.Get ());
640 if (!gst_mf_result (hr)) {
641 GST_ERROR_OBJECT (self, "Couldn't add buffer to sample");
642 return GST_FLOW_ERROR;
645 out_data.pSample = new_sample.Detach ();
648 out_data.dwStreamID = stream_id;
650 hr = transform->ProcessOutput (0, 1, &out_data, &status);
652 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
653 GST_LOG_OBJECT (self, "Need more input data");
654 ret = GST_MF_TRANSFORM_FLOW_NEED_DATA;
655 } else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
656 ComPtr<IMFMediaType> output_type;
658 GST_DEBUG_OBJECT (self, "Stream change, set output type again");
660 hr = transform->GetOutputAvailableType (stream_id,
661 0, output_type.GetAddressOf ());
662 if (!gst_mf_result (hr)) {
663 GST_ERROR_OBJECT (self, "Couldn't get available output type");
664 ret = GST_FLOW_ERROR;
668 hr = transform->SetOutputType (stream_id, output_type.Get (), 0);
669 if (!gst_mf_result (hr)) {
670 GST_ERROR_OBJECT (self, "Couldn't set output type");
671 ret = GST_FLOW_ERROR;
675 ret = GST_MF_TRANSFORM_FLOW_NEED_DATA;
676 } else if (!gst_mf_result (hr)) {
677 if (self->flushing) {
678 GST_DEBUG_OBJECT (self, "Ignore error on flushing");
679 ret = GST_FLOW_FLUSHING;
681 GST_ERROR_OBJECT (self, "ProcessOutput error, hr 0x%x", hr);
682 ret = GST_FLOW_ERROR;
687 if (ret != GST_FLOW_OK) {
688 if (out_data.pSample)
689 out_data.pSample->Release();
694 if (!out_data.pSample) {
695 GST_WARNING_OBJECT (self, "No output sample");
699 if (self->callback) {
700 self->callback (self, out_data.pSample, self->user_data);
701 out_data.pSample->Release ();
705 g_queue_push_tail (self->output_queue, out_data.pSample);
710 /* Must be called with event_lock */
712 gst_mf_transform_process_input_sync (GstMFTransform * self,
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,
730 gboolean ret = FALSE;
732 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
733 g_return_val_if_fail (sample != NULL, FALSE);
735 GST_TRACE_OBJECT (object, "Process input");
737 if (!object->transform)
740 g_mutex_lock (&object->event_lock);
741 if (!object->running) {
742 object->pending_need_input = 0;
744 hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_START_OF_STREAM,
746 if (!gst_mf_result (hr)) {
747 GST_ERROR_OBJECT (object, "Cannot post start-of-stream message");
751 hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
753 if (!gst_mf_result (hr)) {
754 GST_ERROR_OBJECT (object, "Cannot post begin-stream message");
758 if (object->callback_object) {
759 hr = object->callback_object->BeginGetEvent ();
760 if (!gst_mf_result (hr)) {
761 GST_ERROR_OBJECT (object, "BeginGetEvent failed");
766 GST_DEBUG_OBJECT (object, "MFT is running now");
768 object->running = TRUE;
769 object->flushing = FALSE;
772 /* Wait METransformNeedInput event. While waiting METransformNeedInput
773 * event, we can still output data if MFT notifyes METransformHaveOutput
775 if (object->hardware) {
776 while (object->pending_need_input == 0 && !object->flushing)
777 g_cond_wait (&object->event_cond, &object->event_lock);
780 if (object->flushing) {
781 GST_DEBUG_OBJECT (object, "We are flushing");
786 ret = gst_mf_transform_process_input_sync (object, sample);
789 g_mutex_unlock (&object->event_lock);
795 gst_mf_transform_get_output (GstMFTransform * object,
800 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), GST_FLOW_ERROR);
801 g_return_val_if_fail (sample != NULL, GST_FLOW_ERROR);
802 /* Hardware MFT must not call this method, instead client must install
803 * new sample callback so that outputting data from Media Foundation's
805 g_return_val_if_fail (!object->hardware, GST_FLOW_ERROR);
807 if (!object->transform)
808 return GST_FLOW_ERROR;
810 ret = gst_mf_transform_process_output (object);
812 if (ret != GST_MF_TRANSFORM_FLOW_NEED_DATA && ret != GST_FLOW_OK)
815 if (g_queue_is_empty (object->output_queue))
816 return GST_MF_TRANSFORM_FLOW_NEED_DATA;
818 *sample = (IMFSample *) g_queue_pop_head (object->output_queue);
824 gst_mf_transform_flush (GstMFTransform * object)
826 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
828 g_mutex_lock (&object->event_lock);
829 object->flushing = TRUE;
830 g_cond_broadcast (&object->event_cond);
831 g_mutex_unlock (&object->event_lock);
833 if (object->transform) {
834 /* In case of async MFT, there would be no more event after FLUSH,
835 * then callback object shouldn't wait another event.
836 * Call Stop() so that our callback object can stop calling BeginGetEvent()
837 * from it's Invoke() method */
838 if (object->callback_object)
839 object->callback_object->Stop ();
841 if (object->running) {
842 object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_FLUSH, 0);
845 object->pending_need_input = 0;
848 object->running = FALSE;
850 while (!g_queue_is_empty (object->output_queue)) {
851 IMFSample *sample = (IMFSample *) g_queue_pop_head (object->output_queue);
859 gst_mf_transform_drain (GstMFTransform * object)
863 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
865 if (!object->transform || !object->running)
868 object->running = FALSE;
869 object->draining = TRUE;
871 GST_DEBUG_OBJECT (object, "Start drain");
873 object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_DRAIN, 0);
875 if (object->hardware) {
876 g_mutex_lock (&object->event_lock);
877 while (object->draining)
878 g_cond_wait (&object->event_cond, &object->event_lock);
879 g_mutex_unlock (&object->event_lock);
882 ret = gst_mf_transform_process_output (object);
883 } while (ret == GST_FLOW_OK);
886 GST_DEBUG_OBJECT (object, "End drain");
888 object->draining = FALSE;
889 object->pending_need_input = 0;
896 GstMFTransform *object;
899 } GstMFTransformOpenData;
902 gst_mf_transform_open_internal (GstMFTransformOpenData * data)
904 GstMFTransform *object = data->object;
909 gst_mf_transform_close (object);
910 hr = object->activate->ActivateObject (IID_IMFTransform,
911 (void **) &object->transform);
913 if (!gst_mf_result (hr)) {
914 GST_WARNING_OBJECT (object, "Couldn't open MFT");
918 if (object->hardware) {
919 ComPtr<IMFAttributes> attr;
920 UINT32 supports_d3d11 = 0;
922 hr = object->transform->GetAttributes (attr.GetAddressOf ());
923 if (!gst_mf_result (hr)) {
924 GST_ERROR_OBJECT (object, "Couldn't get attribute object");
928 hr = attr->SetUINT32 (MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
929 if (!gst_mf_result (hr)) {
930 GST_ERROR_OBJECT (object, "MF_TRANSFORM_ASYNC_UNLOCK error");
934 hr = attr->GetUINT32 (GST_GUID_MF_SA_D3D11_AWARE, &supports_d3d11);
935 if (gst_mf_result (hr) && supports_d3d11 != 0) {
936 GST_DEBUG_OBJECT (object, "MFT supports direct3d11");
937 object->d3d11_aware = TRUE;
940 /* Create our IMFAsyncCallback object so that listen METransformNeedInput
941 * and METransformHaveOutput events. The event callback will be called from
942 * Media Foundation's worker queue thread */
943 hr = GstMFTransformAsyncCallback::CreateInstance (object->transform,
944 (GstMFTransformAsyncCallbackOnEvent) gst_mf_transform_on_event,
945 GST_OBJECT_CAST (object), &object->callback_object);
947 if (!object->callback_object) {
948 GST_ERROR_OBJECT (object, "IMFMediaEventGenerator unavailable");
953 hr = object->transform->GetStreamIDs (1, &object->input_id, 1,
955 if (hr == E_NOTIMPL) {
956 object->input_id = 0;
957 object->output_id = 0;
960 hr = object->transform->QueryInterface (IID_ICodecAPI,
961 (void **) &object->codec_api);
962 if (!gst_mf_result (hr)) {
963 GST_WARNING_OBJECT (object, "ICodecAPI is unavailable");
970 gst_mf_transform_close (object);
972 g_mutex_lock (&object->lock);
973 data->invoked = TRUE;
974 g_cond_broadcast (&object->cond);
975 g_mutex_unlock (&object->lock);
977 return G_SOURCE_REMOVE;
981 gst_mf_transform_open (GstMFTransform * object)
983 GstMFTransformOpenData data;
985 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
986 g_return_val_if_fail (object->activate != NULL, FALSE);
988 data.object = object;
989 data.invoked = FALSE;
992 g_main_context_invoke (object->context,
993 (GSourceFunc) gst_mf_transform_open_internal, &data);
995 g_mutex_lock (&object->lock);
996 while (!data.invoked)
997 g_cond_wait (&object->cond, &object->lock);
998 g_mutex_unlock (&object->lock);
1004 gst_mf_transform_set_device_manager (GstMFTransform * object,
1005 IMFDXGIDeviceManager * manager)
1009 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1011 if (!object->transform) {
1012 GST_ERROR_OBJECT (object, "IMFTransform is not configured yet");
1016 hr = object->transform->ProcessMessage (MFT_MESSAGE_SET_D3D_MANAGER,
1017 (ULONG_PTR) manager);
1018 if (!gst_mf_result (hr)) {
1019 GST_ERROR_OBJECT (object, "Couldn't set device manager");
1027 gst_mf_transform_set_new_sample_callback (GstMFTransform * object,
1028 GstMFTransformNewSampleCallback callback, gpointer user_data)
1030 g_return_if_fail (GST_IS_MF_TRANSFORM (object));
1032 object->callback = callback;
1033 object->user_data = user_data;
1037 gst_mf_transform_close (GstMFTransform * object)
1039 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1041 gst_mf_transform_flush (object);
1043 /* Otherwise IMFTransform will be alive even after we release the IMFTransform
1045 if (object->activate)
1046 object->activate->ShutdownObject ();
1048 if (object->callback_object) {
1049 object->callback_object->Release ();
1050 object->callback_object = nullptr;
1053 if (object->codec_api) {
1054 object->codec_api->Release ();
1055 object->codec_api = NULL;
1058 if (object->transform) {
1059 object->transform->Release ();
1060 object->transform = NULL;
1067 gst_mf_transform_event_type_to_string (MediaEventType event)
1070 case METransformNeedInput:
1071 return "METransformNeedInput";
1072 case METransformHaveOutput:
1073 return "METransformHaveOutput";
1074 case METransformDrainComplete:
1075 return "METransformDrainComplete";
1076 case METransformMarker:
1077 return "METransformMarker";
1078 case METransformInputStreamStateChanged:
1079 return "METransformInputStreamStateChanged";
1088 gst_mf_transform_on_event (MediaEventType event,
1089 GstMFTransform * self)
1091 GST_TRACE_OBJECT (self, "Have event %s (%d)",
1092 gst_mf_transform_event_type_to_string (event), (gint) event);
1095 case METransformNeedInput:
1096 g_mutex_lock (&self->event_lock);
1097 self->pending_need_input++;
1098 g_cond_broadcast (&self->event_cond);
1099 g_mutex_unlock (&self->event_lock);
1101 case METransformHaveOutput:
1102 gst_mf_transform_process_output (self);
1104 case METransformDrainComplete:
1105 g_mutex_lock (&self->event_lock);
1106 self->draining = FALSE;
1107 g_cond_broadcast (&self->event_cond);
1108 g_mutex_unlock (&self->event_lock);
1118 gst_mf_transform_get_activate_handle (GstMFTransform * object)
1120 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
1122 return object->activate;
1126 gst_mf_transform_get_transform_handle (GstMFTransform * object)
1128 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
1130 if (!object->transform) {
1131 GST_WARNING_OBJECT (object,
1132 "IMFTransform is not configured, open MFT first");
1136 return object->transform;
1140 gst_mf_transform_get_codec_api_handle (GstMFTransform * object)
1142 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
1144 if (!object->codec_api) {
1145 GST_WARNING_OBJECT (object,
1146 "ICodecAPI is not configured, open MFT first");
1150 return object->codec_api;
1154 gst_mf_transform_get_input_available_types (GstMFTransform * object,
1155 GList ** input_types)
1157 IMFTransform *transform;
1162 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1163 g_return_val_if_fail (input_types != NULL, FALSE);
1165 transform = object->transform;
1168 GST_ERROR_OBJECT (object, "Should open first");
1173 IMFMediaType *type = NULL;
1175 hr = transform->GetInputAvailableType (object->input_id, index, &type);
1177 list = g_list_append (list, type);
1180 } while (SUCCEEDED (hr));
1182 *input_types = list;
1188 gst_mf_transform_get_output_available_types (GstMFTransform * object,
1189 GList ** output_types)
1191 IMFTransform *transform;
1196 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1197 g_return_val_if_fail (output_types != NULL, FALSE);
1199 transform = object->transform;
1202 GST_ERROR_OBJECT (object, "Should open first");
1209 hr = transform->GetOutputAvailableType (object->input_id, index, &type);
1211 list = g_list_append (list, type);
1214 } while (SUCCEEDED (hr));
1216 *output_types = list;
1222 gst_mf_transform_set_input_type (GstMFTransform * object,
1223 IMFMediaType * input_type)
1225 IMFTransform *transform;
1228 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1230 transform = object->transform;
1233 GST_ERROR_OBJECT (object, "Should open first");
1237 hr = transform->SetInputType (object->input_id, input_type, 0);
1238 if (!gst_mf_result (hr))
1245 gst_mf_transform_set_output_type (GstMFTransform * object,
1246 IMFMediaType * output_type)
1248 IMFTransform *transform;
1251 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1253 transform = object->transform;
1256 GST_ERROR_OBJECT (object, "Should open first");
1260 hr = transform->SetOutputType (object->output_id, output_type, 0);
1261 if (!gst_mf_result (hr)) {
1269 gst_mf_transform_get_input_current_type (GstMFTransform * object,
1270 IMFMediaType ** input_type)
1272 IMFTransform *transform;
1275 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1276 g_return_val_if_fail (input_type != NULL, FALSE);
1278 transform = object->transform;
1281 GST_ERROR_OBJECT (object, "Should open first");
1285 hr = transform->GetInputCurrentType (object->input_id, input_type);
1286 if (!gst_mf_result (hr)) {
1294 gst_mf_transform_get_output_current_type (GstMFTransform * object,
1295 IMFMediaType ** output_type)
1297 IMFTransform *transform;
1300 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1301 g_return_val_if_fail (output_type != NULL, FALSE);
1303 transform = object->transform;
1306 GST_ERROR_OBJECT (object, "Should open first");
1310 hr = transform->GetOutputCurrentType (object->output_id, output_type);
1311 if (!gst_mf_result (hr)) {
1319 gst_mf_transform_new (GstMFTransformEnumParams * params)
1321 GstMFTransform *self;
1323 g_return_val_if_fail (params != NULL, NULL);
1325 self = (GstMFTransform *) g_object_new (GST_TYPE_MF_TRANSFORM_OBJECT,
1326 "enum-params", params, NULL);
1328 if (!self->initialized) {
1329 gst_object_unref (self);
1333 gst_object_ref_sink (self);
1339 gst_mf_transform_set_codec_api_uint32 (GstMFTransform * object,
1340 const GUID * api, guint32 value)
1345 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1346 g_return_val_if_fail (api != NULL, FALSE);
1348 if (!object->codec_api) {
1349 GST_WARNING_OBJECT (object, "codec api unavailable");
1357 hr = object->codec_api->SetValue (api, &var);
1358 VariantClear (&var);
1360 return gst_mf_result (hr);
1364 gst_mf_transform_set_codec_api_uint64 (GstMFTransform * object,
1365 const GUID * api, guint64 value)
1370 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1371 g_return_val_if_fail (api != NULL, FALSE);
1373 if (!object->codec_api) {
1374 GST_WARNING_OBJECT (object, "codec api unavailable");
1382 hr = object->codec_api->SetValue (api, &var);
1383 VariantClear (&var);
1385 return gst_mf_result (hr);
1389 gst_mf_transform_set_codec_api_boolean (GstMFTransform * object,
1390 const GUID * api, gboolean value)
1395 g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
1396 g_return_val_if_fail (api != NULL, FALSE);
1398 if (!object->codec_api) {
1399 GST_WARNING_OBJECT (object, "codec api unavailable");
1405 var.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
1407 hr = object->codec_api->SetValue (api, &var);
1408 VariantClear (&var);
1410 return gst_mf_result (hr);