mediafoundation: Remove COM thread constraints from GstMFTransform object

Move CoInitializeEx/CoUninitialize pair into our dedicated thread so that
we can ensure COM thread is MTA. This will remove thread constraints
around plugin init.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1269>
This commit is contained in:
Seungha Yang 2020-05-14 20:17:33 +09:00 committed by GStreamer Merge Bot
parent b3cbdb1d26
commit c29c71ae9d
5 changed files with 135 additions and 47 deletions

View file

@ -1353,8 +1353,6 @@ gst_mf_h264_enc_plugin_init (GstPlugin * plugin, guint rank)
gint i;
gboolean do_next;
CoInitializeEx (NULL, COINIT_MULTITHREADED);
GST_DEBUG_CATEGORY_INIT (gst_mf_h264_enc_debug, "mfh264enc", 0, "mfh264enc");
output_type.guidMajorType = MFMediaType_Video;
@ -1398,6 +1396,4 @@ gst_mf_h264_enc_plugin_init (GstPlugin * plugin, guint rank)
gst_clear_object (&transform);
}
} while (do_next);
CoUninitialize ();
}

View file

@ -1117,8 +1117,6 @@ gst_mf_h265_enc_plugin_init (GstPlugin * plugin, guint rank)
gint i;
gboolean do_next;
CoInitializeEx (NULL, COINIT_MULTITHREADED);
GST_DEBUG_CATEGORY_INIT (gst_mf_h265_enc_debug, "mfh265enc", 0, "mfh265enc");
output_type.guidMajorType = MFMediaType_Video;
@ -1162,6 +1160,4 @@ gst_mf_h265_enc_plugin_init (GstPlugin * plugin, guint rank)
gst_clear_object (&transform);
}
} while (do_next);
CoUninitialize ();
}

View file

@ -67,6 +67,12 @@ struct _GstMFTransform
gint pending_need_input;
gint pending_have_output;
GThread *thread;
GMutex lock;
GCond cond;
GMainContext *context;
GMainLoop *loop;
};
#define gst_mf_transform_parent_class parent_class
@ -79,6 +85,9 @@ static void gst_mf_transform_get_property (GObject * object,
static void gst_mf_transform_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static gpointer gst_mf_transform_thread_func (GstMFTransform * self);
static gboolean gst_mf_transform_close (GstMFTransform * self);
static void
gst_mf_transform_class_init (GstMFTransformClass * klass)
{
@ -109,7 +118,27 @@ gst_mf_transform_init (GstMFTransform * self)
{
self->output_queue = g_queue_new ();
CoInitializeEx (NULL, COINIT_MULTITHREADED);
g_mutex_init (&self->lock);
g_cond_init (&self->cond);
self->context = g_main_context_new ();
self->loop = g_main_loop_new (self->context, FALSE);
}
static void
gst_mf_transform_constructed (GObject * object)
{
GstMFTransform *self = GST_MF_TRANSFORM (object);
/* Create thread so that ensure COM thread can be MTA thread */
g_mutex_lock (&self->lock);
self->thread = g_thread_new ("GstMFTransform",
(GThreadFunc) gst_mf_transform_thread_func, self);
while (!g_main_loop_is_running (self->loop))
g_cond_wait (&self->cond, &self->lock);
g_mutex_unlock (&self->lock);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
@ -134,17 +163,16 @@ gst_mf_transform_finalize (GObject * object)
{
GstMFTransform *self = GST_MF_TRANSFORM (object);
gst_mf_transform_close (self);
if (self->activate)
self->activate->Release ();
gst_mf_transform_clear_enum_params (&self->enum_params);
g_free (self->device_name);
g_main_loop_quit (self->loop);
g_thread_join (self->thread);
g_main_loop_unref (self->loop);
g_main_context_unref (self->context);
g_queue_free_full (self->output_queue, (GDestroyNotify) release_mf_sample);
CoUninitialize ();
gst_mf_transform_clear_enum_params (&self->enum_params);
g_free (self->device_name);
g_mutex_clear (&self->lock);
g_cond_clear (&self->cond);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -203,14 +231,36 @@ gst_mf_transform_set_property (GObject * object, guint prop_id,
}
}
static void
gst_mf_transform_constructed (GObject * object)
static gboolean
gst_mf_transform_main_loop_running_cb (GstMFTransform * self)
{
GST_TRACE_OBJECT (self, "Main loop running now");
g_mutex_lock (&self->lock);
g_cond_signal (&self->cond);
g_mutex_unlock (&self->lock);
return G_SOURCE_REMOVE;
}
static gpointer
gst_mf_transform_thread_func (GstMFTransform * self)
{
GstMFTransform *self = GST_MF_TRANSFORM (object);
HRESULT hr;
IMFActivate **devices = NULL;
UINT32 num_devices, i;
LPWSTR name = NULL;
GSource *source;
CoInitializeEx (NULL, COINIT_MULTITHREADED);
g_main_context_push_thread_default (self->context);
source = g_idle_source_new ();
g_source_set_callback (source,
(GSourceFunc) gst_mf_transform_main_loop_running_cb, self, NULL);
g_source_attach (source, self->context);
g_source_unref (source);
hr = MFTEnumEx (self->enum_params.category, self->enum_params.enum_flags,
self->enum_params.input_typeinfo, self->enum_params.output_typeinfo,
@ -218,18 +268,17 @@ gst_mf_transform_constructed (GObject * object)
if (!gst_mf_result (hr)) {
GST_WARNING_OBJECT (self, "MFTEnumEx failure");
return;
goto run_loop;
}
if (num_devices == 0 || self->enum_params.device_index >= num_devices) {
GST_WARNING_OBJECT (self, "No available device at index %d",
self->enum_params.device_index);
for (i = 0; i < num_devices; i++) {
for (i = 0; i < num_devices; i++)
devices[i]->Release ();
}
CoTaskMemFree (devices);
return;
goto run_loop;
}
self->activate = devices[self->enum_params.device_index];
@ -245,16 +294,33 @@ gst_mf_transform_constructed (GObject * object)
self->device_name = g_utf16_to_utf8 ((const gunichar2 *) name,
-1, NULL, NULL, NULL);
CoTaskMemFree (name);
GST_INFO_OBJECT (self, "Open device %s", self->device_name);
CoTaskMemFree (name);
}
done:
CoTaskMemFree (devices);
self->hardware = ! !(self->enum_params.enum_flags & MFT_ENUM_FLAG_HARDWARE);
self->initialized = TRUE;
run_loop:
GST_TRACE_OBJECT (self, "Starting main loop");
g_main_loop_run (self->loop);
GST_TRACE_OBJECT (self, "Stopped main loop");
g_main_context_pop_thread_default (self->context);
/* cleanup internal COM object here */
gst_mf_transform_close (self);
if (self->activate) {
self->activate->Release ();
self->activate = NULL;
}
CoUninitialize ();
return NULL;
}
static HRESULT
@ -596,21 +662,28 @@ gst_mf_transform_drain (GstMFTransform * object)
return TRUE;
}
gboolean
gst_mf_transform_open (GstMFTransform * object)
typedef struct
{
GstMFTransform *object;
gboolean invoked;
gboolean ret;
} GstMFTransformOpenData;
static gboolean
gst_mf_transform_open_internal (GstMFTransformOpenData * data)
{
GstMFTransform *object = data->object;
HRESULT hr;
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
data->ret = FALSE;
gst_mf_transform_close (object);
hr = object->activate->ActivateObject (IID_IMFTransform,
(void **) &object->transform);
if (!gst_mf_result (hr)) {
GST_WARNING_OBJECT (object, "Couldn't open MFT");
return FALSE;
goto done;
}
if (object->hardware) {
@ -619,20 +692,20 @@ gst_mf_transform_open (GstMFTransform * object)
hr = object->transform->GetAttributes (attr.GetAddressOf ());
if (!gst_mf_result (hr)) {
GST_ERROR_OBJECT (object, "Couldn't get attribute object");
goto error;
goto done;
}
hr = attr->SetUINT32 (MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
if (!gst_mf_result (hr)) {
GST_ERROR_OBJECT (object, "MF_TRANSFORM_ASYNC_UNLOCK error");
goto error;
goto done;
}
hr = object->transform->QueryInterface (IID_IMFMediaEventGenerator,
(void **) &object->event_gen);
if (!gst_mf_result (hr)) {
GST_ERROR_OBJECT (object, "IMFMediaEventGenerator unavailable");
goto error;
goto done;
}
}
@ -649,14 +722,44 @@ gst_mf_transform_open (GstMFTransform * object)
GST_WARNING_OBJECT (object, "ICodecAPI is unavailable");
}
return TRUE;
data->ret = TRUE;
error:
gst_mf_transform_close (object);
return FALSE;
done:
if (!data->ret)
gst_mf_transform_close (object);
g_mutex_lock (&object->lock);
data->invoked = TRUE;
g_cond_broadcast (&object->cond);
g_mutex_unlock (&object->lock);
return G_SOURCE_REMOVE;
}
gboolean
gst_mf_transform_open (GstMFTransform * object)
{
GstMFTransformOpenData data;
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
g_return_val_if_fail (object->activate != NULL, FALSE);
data.object = object;
data.invoked = FALSE;
data.ret = FALSE;
g_main_context_invoke (object->context,
(GSourceFunc) gst_mf_transform_open_internal, &data);
g_mutex_lock (&object->lock);
while (!data.invoked)
g_cond_wait (&object->cond, &object->lock);
g_mutex_unlock (&object->lock);
return data.ret;
}
static gboolean
gst_mf_transform_close (GstMFTransform * object)
{
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);

View file

@ -48,8 +48,6 @@ GstMFTransform * gst_mf_transform_new (GstMFTransformEnumParams * pa
gboolean gst_mf_transform_open (GstMFTransform * object);
gboolean gst_mf_transform_close (GstMFTransform * object);
IMFActivate * gst_mf_transform_get_activate_handle (GstMFTransform * object);
IMFTransform * gst_mf_transform_get_transform_handle (GstMFTransform * object);

View file

@ -41,11 +41,6 @@ GST_DEBUG_CATEGORY (gst_mf_transform_debug);
#define GST_CAT_DEFAULT gst_mf_debug
/* NOTE: If you want to use this plugin in UWP app, don't try to load/initialize
* this plugin on UI thread, since the UI thread would be STA Thread
* but this plugin will be initialized with COINIT_MULTITHREADED parameter.
* This rule can be applied over all GStreamer plugins which are involved with
* COM libraries */
static gboolean
plugin_init (GstPlugin * plugin)
{