mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 21:51:09 +00:00
84db4b68fe
Initial support for d3d11 texture so that encoder can copy upstream d3d11 texture into encoder's own texture pool without downloading memory. This implementation requires MFTEnum2() API for creating MFT (Media Foundation Transform) object for specific GPU but the API is Windows 10 desktop only. So UWP is not target of this change. See also https://docs.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mftenum2 Note that, for MF plugin to be able to support old OS versions without breakage, this commit will load MFTEnum2() symbol by using g_module_open() Summary of required system environment: - Needs Windows 10 (probably at least RS 1 update) - GPU should support ExtendedNV12SharedTextureSupported feature - Desktop application only (UWP is not supported yet) Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1903>
1407 lines
34 KiB
C++
1407 lines
34 KiB
C++
/* GStreamer
|
|
* Copyright (C) 2020 Seungha Yang <seungha.yang@navercorp.com>
|
|
* Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstmfconfig.h"
|
|
|
|
#include <gst/gst.h>
|
|
#include <gmodule.h>
|
|
#include "gstmftransform.h"
|
|
#include "gstmfutils.h"
|
|
#include <string.h>
|
|
#include <wrl.h>
|
|
|
|
using namespace Microsoft::WRL;
|
|
|
|
G_BEGIN_DECLS
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_mf_transform_debug);
|
|
#define GST_CAT_DEFAULT gst_mf_transform_debug
|
|
|
|
G_END_DECLS
|
|
|
|
static GModule *mf_plat_module = NULL;
|
|
typedef HRESULT (__stdcall *pMFTEnum2) (GUID guidCategory,
|
|
UINT32 Flags,
|
|
const MFT_REGISTER_TYPE_INFO * pInputType,
|
|
const MFT_REGISTER_TYPE_INFO * pOutputType,
|
|
IMFAttributes * pAttributes,
|
|
IMFActivate *** pppMFTActivate,
|
|
UINT32 * pnumMFTActivate);
|
|
static pMFTEnum2 GstMFTEnum2Func = NULL;
|
|
|
|
gboolean
|
|
gst_mf_transform_load_library (void)
|
|
{
|
|
#if GST_MF_HAVE_D3D11
|
|
static volatile gsize _init = 0;
|
|
if (g_once_init_enter (&_init)) {
|
|
mf_plat_module = g_module_open ("mfplat.dll", G_MODULE_BIND_LAZY);
|
|
|
|
if (mf_plat_module) {
|
|
if (!g_module_symbol (mf_plat_module, "MFTEnum2",
|
|
(gpointer *) & GstMFTEnum2Func)) {
|
|
GST_WARNING ("Cannot load MFTEnum2 symbol");
|
|
g_module_close (mf_plat_module);
|
|
mf_plat_module = NULL;
|
|
GstMFTEnum2Func = NULL;
|
|
} else {
|
|
GST_INFO ("MFTEnum2 symbol is available");
|
|
}
|
|
}
|
|
|
|
g_once_init_leave (&_init, 1);
|
|
}
|
|
#endif
|
|
|
|
return ! !GstMFTEnum2Func;
|
|
}
|
|
|
|
typedef HRESULT (*GstMFTransformAsyncCallbackOnEvent) (MediaEventType event,
|
|
GstObject * client);
|
|
|
|
class GstMFTransformAsyncCallback : public IMFAsyncCallback
|
|
{
|
|
public:
|
|
static HRESULT
|
|
CreateInstance (IMFTransform * mft,
|
|
GstMFTransformAsyncCallbackOnEvent event_cb, GstObject * client,
|
|
GstMFTransformAsyncCallback ** callback)
|
|
{
|
|
HRESULT hr;
|
|
GstMFTransformAsyncCallback *self;
|
|
|
|
if (!mft || !callback)
|
|
return E_INVALIDARG;
|
|
|
|
self = new GstMFTransformAsyncCallback ();
|
|
|
|
if (!self)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = self->Initialize (mft, event_cb, client);
|
|
|
|
if (!gst_mf_result (hr)) {
|
|
self->Release ();
|
|
return hr;
|
|
}
|
|
|
|
*callback = self;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
BeginGetEvent (void)
|
|
{
|
|
if (!gen_)
|
|
return E_FAIL;
|
|
|
|
/* we are running already */
|
|
if (running_)
|
|
return S_OK;
|
|
|
|
running_ = true;
|
|
|
|
return gen_->BeginGetEvent (this, nullptr);
|
|
}
|
|
|
|
HRESULT
|
|
Stop (void)
|
|
{
|
|
running_ = false;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/* IUnknown */
|
|
STDMETHODIMP
|
|
QueryInterface (REFIID riid, void ** object)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP_ (ULONG)
|
|
AddRef (void)
|
|
{
|
|
GST_TRACE ("%p, %d", this, ref_count_);
|
|
return InterlockedIncrement (&ref_count_);
|
|
}
|
|
|
|
STDMETHODIMP_ (ULONG)
|
|
Release (void)
|
|
{
|
|
ULONG ref_count;
|
|
|
|
GST_TRACE ("%p, %d", this, ref_count_);
|
|
ref_count = InterlockedDecrement (&ref_count_);
|
|
|
|
if (ref_count == 0) {
|
|
GST_TRACE ("Delete instance %p", this);
|
|
delete this;
|
|
}
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
/* IMFAsyncCallback */
|
|
STDMETHODIMP
|
|
GetParameters (DWORD * flags, DWORD * queue)
|
|
{
|
|
/* this callback could be blocked */
|
|
*flags = MFASYNC_BLOCKING_CALLBACK;
|
|
*queue = MFASYNC_CALLBACK_QUEUE_MULTITHREADED;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
Invoke (IMFAsyncResult * async_result)
|
|
{
|
|
ComPtr<IMFMediaEvent> event;
|
|
HRESULT hr;
|
|
bool do_next = true;
|
|
|
|
hr = gen_->EndGetEvent (async_result, &event);
|
|
|
|
if (!gst_mf_result (hr))
|
|
return hr;
|
|
|
|
if (event) {
|
|
MediaEventType type;
|
|
GstObject *client = nullptr;
|
|
hr = event->GetType(&type);
|
|
if (!gst_mf_result (hr))
|
|
return hr;
|
|
|
|
if (!event_cb_)
|
|
return S_OK;
|
|
|
|
client = (GstObject *) g_weak_ref_get (&client_);
|
|
if (!client)
|
|
return S_OK;
|
|
|
|
hr = event_cb_ (type, client);
|
|
gst_object_unref (client);
|
|
if (!gst_mf_result (hr))
|
|
return hr;
|
|
|
|
/* On Drain event, this callback object will stop calling BeginGetEvent()
|
|
* since there might be no more following events. Client should call
|
|
* our BeginGetEvent() method to run again */
|
|
if (type == METransformDrainComplete)
|
|
do_next = false;
|
|
}
|
|
|
|
if (do_next)
|
|
gen_->BeginGetEvent(this, nullptr);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
GstMFTransformAsyncCallback ()
|
|
: ref_count_ (1)
|
|
, running_ (false)
|
|
{
|
|
g_weak_ref_init (&client_, NULL);
|
|
}
|
|
|
|
~GstMFTransformAsyncCallback ()
|
|
{
|
|
g_weak_ref_clear (&client_);
|
|
}
|
|
|
|
HRESULT
|
|
Initialize (IMFTransform * mft, GstMFTransformAsyncCallbackOnEvent event_cb,
|
|
GstObject * client)
|
|
{
|
|
HRESULT hr = mft->QueryInterface(IID_PPV_ARGS(&gen_));
|
|
|
|
if (!gst_mf_result (hr))
|
|
return hr;
|
|
|
|
event_cb_ = event_cb;
|
|
g_weak_ref_set (&client_, client);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
private:
|
|
volatile ULONG ref_count_;
|
|
ComPtr<IMFMediaEventGenerator> gen_;
|
|
GstMFTransformAsyncCallbackOnEvent event_cb_;
|
|
GWeakRef client_;
|
|
|
|
bool running_;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_DEVICE_NAME,
|
|
PROP_HARDWARE,
|
|
PROP_ENUM_PARAMS,
|
|
PROP_D3D11_AWARE,
|
|
};
|
|
|
|
struct _GstMFTransform
|
|
{
|
|
GstObject object;
|
|
gboolean initialized;
|
|
|
|
GstMFTransformEnumParams enum_params;
|
|
|
|
gchar *device_name;
|
|
gboolean hardware;
|
|
gboolean d3d11_aware;
|
|
|
|
IMFActivate *activate;
|
|
IMFTransform *transform;
|
|
ICodecAPI * codec_api;
|
|
GstMFTransformAsyncCallback *callback_object;
|
|
|
|
GQueue *output_queue;
|
|
|
|
DWORD input_id;
|
|
DWORD output_id;
|
|
|
|
gboolean running;
|
|
|
|
gint pending_need_input;
|
|
|
|
GThread *thread;
|
|
GMutex lock;
|
|
GCond cond;
|
|
GMutex event_lock;
|
|
GCond event_cond;
|
|
GMainContext *context;
|
|
GMainLoop *loop;
|
|
gboolean draining;
|
|
gboolean flushing;
|
|
|
|
GstMFTransformNewSampleCallback callback;
|
|
gpointer user_data;
|
|
};
|
|
|
|
#define gst_mf_transform_parent_class parent_class
|
|
G_DEFINE_TYPE (GstMFTransform, gst_mf_transform, GST_TYPE_OBJECT);
|
|
|
|
static void gst_mf_transform_constructed (GObject * object);
|
|
static void gst_mf_transform_finalize (GObject * object);
|
|
static void gst_mf_transform_get_property (GObject * object,
|
|
guint prop_id, GValue * value, GParamSpec * pspec);
|
|
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 HRESULT gst_mf_transform_on_event (MediaEventType event,
|
|
GstMFTransform * self);
|
|
|
|
static void
|
|
gst_mf_transform_class_init (GstMFTransformClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->constructed = gst_mf_transform_constructed;
|
|
gobject_class->finalize = gst_mf_transform_finalize;
|
|
gobject_class->get_property = gst_mf_transform_get_property;
|
|
gobject_class->set_property = gst_mf_transform_set_property;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
|
|
g_param_spec_string ("device-name", "device-name",
|
|
"Device name", NULL,
|
|
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
|
|
g_object_class_install_property (gobject_class, PROP_HARDWARE,
|
|
g_param_spec_boolean ("hardware", "Hardware",
|
|
"Whether hardware device or not", FALSE,
|
|
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
|
|
g_object_class_install_property (gobject_class, PROP_ENUM_PARAMS,
|
|
g_param_spec_pointer ("enum-params", "Enum Params",
|
|
"GstMFTransformEnumParams for MFTEnumEx",
|
|
(GParamFlags) (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS)));
|
|
g_object_class_install_property (gobject_class, PROP_D3D11_AWARE,
|
|
g_param_spec_boolean ("d3d11-aware", "D3D11 Aware",
|
|
"Whether Direct3D11 supports Direct3D11", FALSE,
|
|
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
|
|
}
|
|
|
|
static void
|
|
gst_mf_transform_init (GstMFTransform * self)
|
|
{
|
|
self->output_queue = g_queue_new ();
|
|
|
|
g_mutex_init (&self->lock);
|
|
g_mutex_init (&self->event_lock);
|
|
g_cond_init (&self->cond);
|
|
g_cond_init (&self->event_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
|
|
gst_mf_transform_clear_enum_params (GstMFTransformEnumParams *params)
|
|
{
|
|
g_free (params->input_typeinfo);
|
|
params->input_typeinfo = NULL;
|
|
|
|
g_free (params->output_typeinfo);
|
|
params->output_typeinfo = NULL;
|
|
}
|
|
|
|
static void
|
|
release_mf_sample (IMFSample * sample)
|
|
{
|
|
if (sample)
|
|
sample->Release ();
|
|
}
|
|
|
|
static void
|
|
gst_mf_transform_finalize (GObject * object)
|
|
{
|
|
GstMFTransform *self = GST_MF_TRANSFORM (object);
|
|
|
|
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);
|
|
gst_mf_transform_clear_enum_params (&self->enum_params);
|
|
g_free (self->device_name);
|
|
g_mutex_clear (&self->lock);
|
|
g_mutex_clear (&self->event_lock);
|
|
g_cond_clear (&self->cond);
|
|
g_cond_clear (&self->event_cond);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gst_mf_transform_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstMFTransform *self = GST_MF_TRANSFORM (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DEVICE_NAME:
|
|
g_value_set_string (value, self->device_name);
|
|
break;
|
|
case PROP_HARDWARE:
|
|
g_value_set_boolean (value, self->hardware);
|
|
break;
|
|
case PROP_D3D11_AWARE:
|
|
g_value_set_boolean (value, self->d3d11_aware);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mf_transform_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstMFTransform *self = GST_MF_TRANSFORM (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_ENUM_PARAMS:
|
|
{
|
|
GstMFTransformEnumParams *params;
|
|
params = (GstMFTransformEnumParams *) g_value_get_pointer (value);
|
|
|
|
gst_mf_transform_clear_enum_params (&self->enum_params);
|
|
self->enum_params.category = params->category;
|
|
self->enum_params.enum_flags = params->enum_flags;
|
|
self->enum_params.device_index = params->device_index;
|
|
self->enum_params.adapter_luid = params->adapter_luid;
|
|
if (params->input_typeinfo) {
|
|
self->enum_params.input_typeinfo = g_new0 (MFT_REGISTER_TYPE_INFO, 1);
|
|
memcpy (self->enum_params.input_typeinfo, params->input_typeinfo,
|
|
sizeof (MFT_REGISTER_TYPE_INFO));
|
|
}
|
|
|
|
if (params->output_typeinfo) {
|
|
self->enum_params.output_typeinfo = g_new0 (MFT_REGISTER_TYPE_INFO, 1);
|
|
memcpy (self->enum_params.output_typeinfo, params->output_typeinfo,
|
|
sizeof (MFT_REGISTER_TYPE_INFO));
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
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);
|
|
|
|
/* NOTE: MFTEnum2 is desktop only and requires Windows 10 */
|
|
#if GST_MF_HAVE_D3D11
|
|
if (GstMFTEnum2Func && self->enum_params.adapter_luid &&
|
|
(self->enum_params.enum_flags & MFT_ENUM_FLAG_HARDWARE) != 0) {
|
|
ComPtr<IMFAttributes> attr;
|
|
LUID luid;
|
|
|
|
hr = MFCreateAttributes (&attr, 1);
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't create IMFAttributes");
|
|
goto run_loop;
|
|
}
|
|
|
|
GST_INFO_OBJECT (self,
|
|
"Enumerating MFT for adapter-luid %" G_GINT64_FORMAT,
|
|
self->enum_params.adapter_luid);
|
|
|
|
luid.LowPart = (DWORD) (self->enum_params.adapter_luid & 0xffffffff);
|
|
luid.HighPart = (LONG) (self->enum_params.adapter_luid >> 32);
|
|
|
|
hr = attr->SetBlob (GST_GUID_MFT_ENUM_ADAPTER_LUID, (BYTE *) &luid,
|
|
sizeof (LUID));
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't set MFT_ENUM_ADAPTER_LUID");
|
|
goto run_loop;
|
|
}
|
|
|
|
hr = GstMFTEnum2Func (self->enum_params.category,
|
|
self->enum_params.enum_flags, self->enum_params.input_typeinfo,
|
|
self->enum_params.output_typeinfo, attr.Get (), &devices, &num_devices);
|
|
} else
|
|
#endif
|
|
{
|
|
hr = MFTEnumEx (self->enum_params.category, self->enum_params.enum_flags,
|
|
self->enum_params.input_typeinfo, self->enum_params.output_typeinfo,
|
|
&devices, &num_devices);
|
|
}
|
|
|
|
if (!gst_mf_result (hr)) {
|
|
GST_WARNING_OBJECT (self, "MFTEnumEx failure");
|
|
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++)
|
|
devices[i]->Release ();
|
|
|
|
CoTaskMemFree (devices);
|
|
goto run_loop;
|
|
}
|
|
|
|
self->activate = devices[self->enum_params.device_index];
|
|
self->activate->AddRef ();
|
|
|
|
for (i = 0; i < num_devices; i++)
|
|
devices[i]->Release ();
|
|
|
|
hr = self->activate->GetAllocatedString (MFT_FRIENDLY_NAME_Attribute,
|
|
&name, NULL);
|
|
|
|
if (gst_mf_result (hr)) {
|
|
self->device_name = g_utf16_to_utf8 ((const gunichar2 *) name,
|
|
-1, NULL, NULL, NULL);
|
|
|
|
GST_INFO_OBJECT (self, "Open device %s", self->device_name);
|
|
CoTaskMemFree (name);
|
|
}
|
|
|
|
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 GstFlowReturn
|
|
gst_mf_transform_process_output (GstMFTransform * self)
|
|
{
|
|
DWORD status;
|
|
HRESULT hr;
|
|
IMFTransform *transform = self->transform;
|
|
DWORD stream_id = self->output_id;
|
|
MFT_OUTPUT_STREAM_INFO out_stream_info = { 0 };
|
|
MFT_OUTPUT_DATA_BUFFER out_data = { 0 };
|
|
GstFlowReturn ret = GST_FLOW_OK;
|
|
|
|
GST_TRACE_OBJECT (self, "Process output");
|
|
|
|
hr = transform->GetOutputStreamInfo (stream_id, &out_stream_info);
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get output stream info");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
if ((out_stream_info.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES |
|
|
MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0) {
|
|
ComPtr<IMFMediaBuffer> buffer;
|
|
ComPtr<IMFSample> new_sample;
|
|
|
|
hr = MFCreateMemoryBuffer (out_stream_info.cbSize,
|
|
buffer.GetAddressOf ());
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't create memory buffer");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
hr = MFCreateSample (new_sample.GetAddressOf ());
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't create sample");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
hr = new_sample->AddBuffer (buffer.Get ());
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't add buffer to sample");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
out_data.pSample = new_sample.Detach ();
|
|
}
|
|
|
|
out_data.dwStreamID = stream_id;
|
|
|
|
hr = transform->ProcessOutput (0, 1, &out_data, &status);
|
|
|
|
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
|
GST_LOG_OBJECT (self, "Need more input data");
|
|
ret = GST_MF_TRANSFORM_FLOW_NEED_DATA;
|
|
} else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
|
|
ComPtr<IMFMediaType> output_type;
|
|
|
|
GST_DEBUG_OBJECT (self, "Stream change, set output type again");
|
|
|
|
hr = transform->GetOutputAvailableType (stream_id,
|
|
0, output_type.GetAddressOf ());
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get available output type");
|
|
ret = GST_FLOW_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
hr = transform->SetOutputType (stream_id, output_type.Get (), 0);
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't set output type");
|
|
ret = GST_FLOW_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
ret = GST_MF_TRANSFORM_FLOW_NEED_DATA;
|
|
} else if (!gst_mf_result (hr)) {
|
|
if (self->flushing) {
|
|
GST_DEBUG_OBJECT (self, "Ignore error on flushing");
|
|
ret = GST_FLOW_FLUSHING;
|
|
} else {
|
|
GST_ERROR_OBJECT (self, "ProcessOutput error, hr 0x%x", hr);
|
|
ret = GST_FLOW_ERROR;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (ret != GST_FLOW_OK) {
|
|
if (out_data.pSample)
|
|
out_data.pSample->Release();
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (!out_data.pSample) {
|
|
GST_WARNING_OBJECT (self, "No output sample");
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
if (self->callback) {
|
|
self->callback (self, out_data.pSample, self->user_data);
|
|
out_data.pSample->Release ();
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
g_queue_push_tail (self->output_queue, out_data.pSample);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
/* Must be called with event_lock */
|
|
static gboolean
|
|
gst_mf_transform_process_input_sync (GstMFTransform * self,
|
|
IMFSample * sample)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = self->transform->ProcessInput (self->output_id, sample, 0);
|
|
|
|
if (self->hardware)
|
|
self->pending_need_input--;
|
|
|
|
return gst_mf_result (hr);
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_process_input (GstMFTransform * object,
|
|
IMFSample * sample)
|
|
{
|
|
HRESULT hr;
|
|
gboolean ret = FALSE;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
g_return_val_if_fail (sample != NULL, FALSE);
|
|
|
|
GST_TRACE_OBJECT (object, "Process input");
|
|
|
|
if (!object->transform)
|
|
return FALSE;
|
|
|
|
g_mutex_lock (&object->event_lock);
|
|
if (!object->running) {
|
|
object->pending_need_input = 0;
|
|
|
|
hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_START_OF_STREAM,
|
|
0);
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (object, "Cannot post start-of-stream message");
|
|
goto done;
|
|
}
|
|
|
|
hr = object->transform->ProcessMessage (MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
|
|
0);
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (object, "Cannot post begin-stream message");
|
|
goto done;
|
|
}
|
|
|
|
if (object->callback_object) {
|
|
hr = object->callback_object->BeginGetEvent ();
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (object, "BeginGetEvent failed");
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (object, "MFT is running now");
|
|
|
|
object->running = TRUE;
|
|
object->flushing = FALSE;
|
|
}
|
|
|
|
/* Wait METransformNeedInput event. While waiting METransformNeedInput
|
|
* event, we can still output data if MFT notifyes METransformHaveOutput
|
|
* event. */
|
|
if (object->hardware) {
|
|
while (object->pending_need_input == 0 && !object->flushing)
|
|
g_cond_wait (&object->event_cond, &object->event_lock);
|
|
}
|
|
|
|
if (object->flushing) {
|
|
GST_DEBUG_OBJECT (object, "We are flushing");
|
|
ret = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
ret = gst_mf_transform_process_input_sync (object, sample);
|
|
|
|
done:
|
|
g_mutex_unlock (&object->event_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
GstFlowReturn
|
|
gst_mf_transform_get_output (GstMFTransform * object,
|
|
IMFSample ** sample)
|
|
{
|
|
GstFlowReturn ret;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), GST_FLOW_ERROR);
|
|
g_return_val_if_fail (sample != NULL, GST_FLOW_ERROR);
|
|
/* Hardware MFT must not call this method, instead client must install
|
|
* new sample callback so that outputting data from Media Foundation's
|
|
* worker thread */
|
|
g_return_val_if_fail (!object->hardware, GST_FLOW_ERROR);
|
|
|
|
if (!object->transform)
|
|
return GST_FLOW_ERROR;
|
|
|
|
ret = gst_mf_transform_process_output (object);
|
|
|
|
if (ret != GST_MF_TRANSFORM_FLOW_NEED_DATA && ret != GST_FLOW_OK)
|
|
return ret;
|
|
|
|
if (g_queue_is_empty (object->output_queue))
|
|
return GST_MF_TRANSFORM_FLOW_NEED_DATA;
|
|
|
|
*sample = (IMFSample *) g_queue_pop_head (object->output_queue);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_flush (GstMFTransform * object)
|
|
{
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
|
|
g_mutex_lock (&object->event_lock);
|
|
object->flushing = TRUE;
|
|
g_cond_broadcast (&object->event_cond);
|
|
g_mutex_unlock (&object->event_lock);
|
|
|
|
if (object->transform) {
|
|
/* In case of async MFT, there would be no more event after FLUSH,
|
|
* then callback object shouldn't wait another event.
|
|
* Call Stop() so that our callback object can stop calling BeginGetEvent()
|
|
* from it's Invoke() method */
|
|
if (object->callback_object)
|
|
object->callback_object->Stop ();
|
|
|
|
if (object->running) {
|
|
object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_FLUSH, 0);
|
|
}
|
|
|
|
object->pending_need_input = 0;
|
|
}
|
|
|
|
object->running = FALSE;
|
|
|
|
while (!g_queue_is_empty (object->output_queue)) {
|
|
IMFSample *sample = (IMFSample *) g_queue_pop_head (object->output_queue);
|
|
sample->Release ();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_drain (GstMFTransform * object)
|
|
{
|
|
GstFlowReturn ret;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
|
|
if (!object->transform || !object->running)
|
|
return TRUE;
|
|
|
|
object->running = FALSE;
|
|
object->draining = TRUE;
|
|
|
|
GST_DEBUG_OBJECT (object, "Start drain");
|
|
|
|
object->transform->ProcessMessage (MFT_MESSAGE_COMMAND_DRAIN, 0);
|
|
|
|
if (object->hardware) {
|
|
g_mutex_lock (&object->event_lock);
|
|
while (object->draining)
|
|
g_cond_wait (&object->event_cond, &object->event_lock);
|
|
g_mutex_unlock (&object->event_lock);
|
|
} else {
|
|
do {
|
|
ret = gst_mf_transform_process_output (object);
|
|
} while (ret == GST_FLOW_OK);
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (object, "End drain");
|
|
|
|
object->draining = FALSE;
|
|
object->pending_need_input = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
GstMFTransform *object;
|
|
gboolean invoked;
|
|
gboolean ret;
|
|
} GstMFTransformOpenData;
|
|
|
|
static gboolean
|
|
gst_mf_transform_open_internal (GstMFTransformOpenData * data)
|
|
{
|
|
GstMFTransform *object = data->object;
|
|
HRESULT hr;
|
|
|
|
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");
|
|
goto done;
|
|
}
|
|
|
|
if (object->hardware) {
|
|
ComPtr<IMFAttributes> attr;
|
|
UINT32 supports_d3d11 = 0;
|
|
|
|
hr = object->transform->GetAttributes (attr.GetAddressOf ());
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (object, "Couldn't get attribute object");
|
|
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 done;
|
|
}
|
|
|
|
hr = attr->GetUINT32 (GST_GUID_MF_SA_D3D11_AWARE, &supports_d3d11);
|
|
if (gst_mf_result (hr) && supports_d3d11 != 0) {
|
|
GST_DEBUG_OBJECT (object, "MFT supports direct3d11");
|
|
object->d3d11_aware = TRUE;
|
|
}
|
|
|
|
/* Create our IMFAsyncCallback object so that listen METransformNeedInput
|
|
* and METransformHaveOutput events. The event callback will be called from
|
|
* Media Foundation's worker queue thread */
|
|
hr = GstMFTransformAsyncCallback::CreateInstance (object->transform,
|
|
(GstMFTransformAsyncCallbackOnEvent) gst_mf_transform_on_event,
|
|
GST_OBJECT_CAST (object), &object->callback_object);
|
|
|
|
if (!object->callback_object) {
|
|
GST_ERROR_OBJECT (object, "IMFMediaEventGenerator unavailable");
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
hr = object->transform->GetStreamIDs (1, &object->input_id, 1,
|
|
&object->output_id);
|
|
if (hr == E_NOTIMPL) {
|
|
object->input_id = 0;
|
|
object->output_id = 0;
|
|
}
|
|
|
|
hr = object->transform->QueryInterface (IID_ICodecAPI,
|
|
(void **) &object->codec_api);
|
|
if (!gst_mf_result (hr)) {
|
|
GST_WARNING_OBJECT (object, "ICodecAPI is unavailable");
|
|
}
|
|
|
|
data->ret = TRUE;
|
|
|
|
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;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_set_device_manager (GstMFTransform * object,
|
|
IMFDXGIDeviceManager * manager)
|
|
{
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
|
|
if (!object->transform) {
|
|
GST_ERROR_OBJECT (object, "IMFTransform is not configured yet");
|
|
return FALSE;
|
|
}
|
|
|
|
hr = object->transform->ProcessMessage (MFT_MESSAGE_SET_D3D_MANAGER,
|
|
(ULONG_PTR) manager);
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (object, "Couldn't set device manager");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gst_mf_transform_set_new_sample_callback (GstMFTransform * object,
|
|
GstMFTransformNewSampleCallback callback, gpointer user_data)
|
|
{
|
|
g_return_if_fail (GST_IS_MF_TRANSFORM (object));
|
|
|
|
object->callback = callback;
|
|
object->user_data = user_data;
|
|
}
|
|
|
|
static gboolean
|
|
gst_mf_transform_close (GstMFTransform * object)
|
|
{
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
|
|
gst_mf_transform_flush (object);
|
|
|
|
if (object->callback_object) {
|
|
object->callback_object->Release ();
|
|
object->callback_object = nullptr;
|
|
}
|
|
|
|
if (object->codec_api) {
|
|
object->codec_api->Release ();
|
|
object->codec_api = NULL;
|
|
}
|
|
|
|
if (object->transform) {
|
|
object->transform->Release ();
|
|
object->transform = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gchar *
|
|
gst_mf_transform_event_type_to_string (MediaEventType event)
|
|
{
|
|
switch (event) {
|
|
case METransformNeedInput:
|
|
return "METransformNeedInput";
|
|
case METransformHaveOutput:
|
|
return "METransformHaveOutput";
|
|
case METransformDrainComplete:
|
|
return "METransformDrainComplete";
|
|
case METransformMarker:
|
|
return "METransformMarker";
|
|
case METransformInputStreamStateChanged:
|
|
return "METransformInputStreamStateChanged";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
static HRESULT
|
|
gst_mf_transform_on_event (MediaEventType event,
|
|
GstMFTransform * self)
|
|
{
|
|
GST_TRACE_OBJECT (self, "Have event %s (%d)",
|
|
gst_mf_transform_event_type_to_string (event), (gint) event);
|
|
|
|
switch (event) {
|
|
case METransformNeedInput:
|
|
g_mutex_lock (&self->event_lock);
|
|
self->pending_need_input++;
|
|
g_cond_broadcast (&self->event_cond);
|
|
g_mutex_unlock (&self->event_lock);
|
|
break;
|
|
case METransformHaveOutput:
|
|
gst_mf_transform_process_output (self);
|
|
break;
|
|
case METransformDrainComplete:
|
|
g_mutex_lock (&self->event_lock);
|
|
self->draining = FALSE;
|
|
g_cond_broadcast (&self->event_cond);
|
|
g_mutex_unlock (&self->event_lock);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
IMFActivate *
|
|
gst_mf_transform_get_activate_handle (GstMFTransform * object)
|
|
{
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
|
|
|
|
return object->activate;
|
|
}
|
|
|
|
IMFTransform *
|
|
gst_mf_transform_get_transform_handle (GstMFTransform * object)
|
|
{
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
|
|
|
|
if (!object->transform) {
|
|
GST_WARNING_OBJECT (object,
|
|
"IMFTransform is not configured, open MFT first");
|
|
return NULL;
|
|
}
|
|
|
|
return object->transform;
|
|
}
|
|
|
|
ICodecAPI *
|
|
gst_mf_transform_get_codec_api_handle (GstMFTransform * object)
|
|
{
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), NULL);
|
|
|
|
if (!object->codec_api) {
|
|
GST_WARNING_OBJECT (object,
|
|
"ICodecAPI is not configured, open MFT first");
|
|
return NULL;
|
|
}
|
|
|
|
return object->codec_api;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_get_input_available_types (GstMFTransform * object,
|
|
GList ** input_types)
|
|
{
|
|
IMFTransform *transform;
|
|
HRESULT hr;
|
|
DWORD index = 0;
|
|
GList *list = NULL;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
g_return_val_if_fail (input_types != NULL, FALSE);
|
|
|
|
transform = object->transform;
|
|
|
|
if (!transform) {
|
|
GST_ERROR_OBJECT (object, "Should open first");
|
|
return FALSE;
|
|
}
|
|
|
|
do {
|
|
IMFMediaType *type = NULL;
|
|
|
|
hr = transform->GetInputAvailableType (object->input_id, index, &type);
|
|
if (SUCCEEDED (hr))
|
|
list = g_list_append (list, type);
|
|
|
|
index++;
|
|
} while (SUCCEEDED (hr));
|
|
|
|
*input_types = list;
|
|
|
|
return !!list;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_get_output_available_types (GstMFTransform * object,
|
|
GList ** output_types)
|
|
{
|
|
IMFTransform *transform;
|
|
HRESULT hr;
|
|
DWORD index = 0;
|
|
GList *list = NULL;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
g_return_val_if_fail (output_types != NULL, FALSE);
|
|
|
|
transform = object->transform;
|
|
|
|
if (!transform) {
|
|
GST_ERROR_OBJECT (object, "Should open first");
|
|
return FALSE;
|
|
}
|
|
|
|
do {
|
|
IMFMediaType *type;
|
|
|
|
hr = transform->GetOutputAvailableType (object->input_id, index, &type);
|
|
if (SUCCEEDED (hr))
|
|
list = g_list_append (list, type);
|
|
|
|
index++;
|
|
} while (SUCCEEDED (hr));
|
|
|
|
*output_types = list;
|
|
|
|
return !!list;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_set_input_type (GstMFTransform * object,
|
|
IMFMediaType * input_type)
|
|
{
|
|
IMFTransform *transform;
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
|
|
transform = object->transform;
|
|
|
|
if (!transform) {
|
|
GST_ERROR_OBJECT (object, "Should open first");
|
|
return FALSE;
|
|
}
|
|
|
|
hr = transform->SetInputType (object->input_id, input_type, 0);
|
|
if (!gst_mf_result (hr))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_set_output_type (GstMFTransform * object,
|
|
IMFMediaType * output_type)
|
|
{
|
|
IMFTransform *transform;
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
|
|
transform = object->transform;
|
|
|
|
if (!transform) {
|
|
GST_ERROR_OBJECT (object, "Should open first");
|
|
return FALSE;
|
|
}
|
|
|
|
hr = transform->SetOutputType (object->output_id, output_type, 0);
|
|
if (!gst_mf_result (hr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_get_input_current_type (GstMFTransform * object,
|
|
IMFMediaType ** input_type)
|
|
{
|
|
IMFTransform *transform;
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
g_return_val_if_fail (input_type != NULL, FALSE);
|
|
|
|
transform = object->transform;
|
|
|
|
if (!transform) {
|
|
GST_ERROR_OBJECT (object, "Should open first");
|
|
return FALSE;
|
|
}
|
|
|
|
hr = transform->GetInputCurrentType (object->input_id, input_type);
|
|
if (!gst_mf_result (hr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_get_output_current_type (GstMFTransform * object,
|
|
IMFMediaType ** output_type)
|
|
{
|
|
IMFTransform *transform;
|
|
HRESULT hr;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
g_return_val_if_fail (output_type != NULL, FALSE);
|
|
|
|
transform = object->transform;
|
|
|
|
if (!transform) {
|
|
GST_ERROR_OBJECT (object, "Should open first");
|
|
return FALSE;
|
|
}
|
|
|
|
hr = transform->GetOutputCurrentType (object->output_id, output_type);
|
|
if (!gst_mf_result (hr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GstMFTransform *
|
|
gst_mf_transform_new (GstMFTransformEnumParams * params)
|
|
{
|
|
GstMFTransform *self;
|
|
|
|
g_return_val_if_fail (params != NULL, NULL);
|
|
|
|
self = (GstMFTransform *) g_object_new (GST_TYPE_MF_TRANSFORM_OBJECT,
|
|
"enum-params", params, NULL);
|
|
|
|
if (!self->initialized) {
|
|
gst_object_unref (self);
|
|
return NULL;
|
|
}
|
|
|
|
gst_object_ref_sink (self);
|
|
|
|
return self;
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_set_codec_api_uint32 (GstMFTransform * object,
|
|
const GUID * api, guint32 value)
|
|
{
|
|
HRESULT hr;
|
|
VARIANT var;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
g_return_val_if_fail (api != NULL, FALSE);
|
|
|
|
if (!object->codec_api) {
|
|
GST_WARNING_OBJECT (object, "codec api unavailable");
|
|
return FALSE;
|
|
}
|
|
|
|
VariantInit (&var);
|
|
var.vt = VT_UI4;
|
|
var.ulVal = value;
|
|
|
|
hr = object->codec_api->SetValue (api, &var);
|
|
VariantClear (&var);
|
|
|
|
return gst_mf_result (hr);
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_set_codec_api_uint64 (GstMFTransform * object,
|
|
const GUID * api, guint64 value)
|
|
{
|
|
HRESULT hr;
|
|
VARIANT var;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
g_return_val_if_fail (api != NULL, FALSE);
|
|
|
|
if (!object->codec_api) {
|
|
GST_WARNING_OBJECT (object, "codec api unavailable");
|
|
return FALSE;
|
|
}
|
|
|
|
VariantInit (&var);
|
|
var.vt = VT_UI8;
|
|
var.ullVal = value;
|
|
|
|
hr = object->codec_api->SetValue (api, &var);
|
|
VariantClear (&var);
|
|
|
|
return gst_mf_result (hr);
|
|
}
|
|
|
|
gboolean
|
|
gst_mf_transform_set_codec_api_boolean (GstMFTransform * object,
|
|
const GUID * api, gboolean value)
|
|
{
|
|
HRESULT hr;
|
|
VARIANT var;
|
|
|
|
g_return_val_if_fail (GST_IS_MF_TRANSFORM (object), FALSE);
|
|
g_return_val_if_fail (api != NULL, FALSE);
|
|
|
|
if (!object->codec_api) {
|
|
GST_WARNING_OBJECT (object, "codec api unavailable");
|
|
return FALSE;
|
|
}
|
|
|
|
VariantInit (&var);
|
|
var.vt = VT_BOOL;
|
|
var.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
|
|
|
|
hr = object->codec_api->SetValue (api, &var);
|
|
VariantClear (&var);
|
|
|
|
return gst_mf_result (hr);
|
|
}
|
|
|