--- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_cbs.c | 65 + dlls/winegstreamer/gst_cbs.h | 18 + dlls/winegstreamer/gst_private.h | 7 + dlls/winegstreamer/main.c | 3 +- dlls/winegstreamer/mf_decode.c | 1210 ++++++++++++++++++ dlls/winegstreamer/mfplat.c | 143 ++- dlls/winegstreamer/winegstreamer_classes.idl | 12 + include/mfidl.idl | 4 +- 9 files changed, 1460 insertions(+), 3 deletions(-) create mode 100644 dlls/winegstreamer/mf_decode.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 61789884b8..9ca63e926c 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ main.c \ media_source.c \ mediatype.c \ + mf_decode.c \ mfplat.c \ pin.c \ qualitycontrol.c \ diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index 9c27c3490d..4ba58e87b0 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -51,6 +51,8 @@ static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user) perform_cb_gstdemux(cbdata); else if (cbdata->type < MEDIA_SOURCE_MAX) perform_cb_media_source(cbdata); + else if (cbdata->type < MF_DECODE_MAX) + perform_cb_mf_decode(cbdata);
pthread_mutex_lock(&cbdata->lock); cbdata->finished = 1; @@ -429,4 +431,67 @@ GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpoin call_cb(&cbdata);
return cbdata.u.watch_bus_data.ret; +} + +gboolean activate_push_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + struct cb_data cbdata = { ACTIVATE_PUSH_MODE }; + + cbdata.u.activate_mode_data.pad = pad; + cbdata.u.activate_mode_data.parent = parent; + cbdata.u.activate_mode_data.mode = mode; + cbdata.u.activate_mode_data.activate = activate; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +gboolean query_input_src_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct cb_data cbdata = { QUERY_INPUT_SRC }; + + cbdata.u.query_function_data.pad = pad; + cbdata.u.query_function_data.parent = parent; + cbdata.u.query_function_data.query = query; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +GstBusSyncReply watch_decoder_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) +{ + struct cb_data cbdata = { WATCH_DECODER_BUS }; + + cbdata.u.watch_bus_data.bus = bus; + cbdata.u.watch_bus_data.msg = message; + cbdata.u.watch_bus_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.watch_bus_data.ret; +} + +void decoder_pad_added_wrapper(GstElement *element, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { DECODER_PAD_ADDED }; + + cbdata.u.pad_added_data.element = element; + cbdata.u.pad_added_data.pad = pad; + cbdata.u.pad_added_data.user = user; + + call_cb(&cbdata); +} + +GstFlowReturn decoder_new_sample_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = {DECODER_NEW_SAMPLE}; + + cbdata.u.new_sample_data.appsink = appsink; + cbdata.u.new_sample_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.new_sample_data.ret; } \ No newline at end of file diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 2c83f294f4..e1e018fa46 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -55,6 +55,12 @@ enum CB_TYPE { STREAM_EOS, WATCH_SOURCE_BUS, MEDIA_SOURCE_MAX, + ACTIVATE_PUSH_MODE, + QUERY_INPUT_SRC, + DECODER_NEW_SAMPLE, + WATCH_DECODER_BUS, + DECODER_PAD_ADDED, + MF_DECODE_MAX, };
struct cb_data { @@ -148,6 +154,12 @@ struct cb_data { GstElement *appsink; gpointer user; } eos_data; + struct chain_data { + GstPad *pad; + GstObject *parent; + GstBuffer *buffer; + GstFlowReturn ret; + } chain_data; } u;
int finished; @@ -159,6 +171,7 @@ struct cb_data { void mark_wine_thread(void) DECLSPEC_HIDDEN; void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN; void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN; +void perform_cb_mf_decode(struct cb_data *data) DECLSPEC_HIDDEN;
GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN; void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; @@ -185,5 +198,10 @@ void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HID GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; void stream_eos_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN; +gboolean activate_push_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN; +gboolean query_input_src_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +GstBusSyncReply watch_decoder_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN; +GstFlowReturn decoder_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; +void decoder_pad_added_wrapper(GstElement *element, GstPad *Pad, gpointer user) DECLSPEC_HIDDEN;
#endif diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 489b543738..4e9cc53a18 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -53,6 +53,7 @@ BOOL init_gstreamer(void) DECLSPEC_HIDDEN;
void start_dispatch_thread(void) DECLSPEC_HIDDEN;
+extern HRESULT mfplat_DllRegisterServer(void) DECLSPEC_HIDDEN; extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
IMFMediaType* media_type_from_caps(GstCaps *caps); @@ -60,6 +61,12 @@ GstCaps *caps_from_media_type(IMFMediaType *type); IMFSample* mf_sample_from_gst_buffer(GstBuffer *in); GstBuffer* gst_buffer_from_mf_sample(IMFSample *in);
+enum decoder_type +{ + DECODER_TYPE_H264, + DECODER_TYPE_AAC, +}; +HRESULT generic_decoder_construct(REFIID riid, void **obj, enum decoder_type); enum source_type { SOURCE_TYPE_MPEG_4, diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 2872710b3e..12ca11fa6c 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -365,7 +365,8 @@ HRESULT WINAPI DllRegisterServer(void) IFilterMapper2_RegisterFilter(mapper, &CLSID_WAVEParser, wave_parserW, NULL, NULL, NULL, ®_wave_parser);
IFilterMapper2_Release(mapper); - return S_OK; + + return mfplat_DllRegisterServer(); }
HRESULT WINAPI DllUnregisterServer(void) diff --git a/dlls/winegstreamer/mf_decode.c b/dlls/winegstreamer/mf_decode.c new file mode 100644 index 0000000000..9c36f94fdc --- /dev/null +++ b/dlls/winegstreamer/mf_decode.c @@ -0,0 +1,1210 @@ +#include "config.h" + +#include <gst/gst.h> + +#include "gst_private.h" +#include "gst_cbs.h" + +#include <stdarg.h> + +#define COBJMACROS +#define NONAMELESSUNION + +#include "mfapi.h" +#include "mferror.h" +#include "mfobjects.h" +#include "mftransform.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +const GUID *h264_input_types[] = {&MFVideoFormat_H264}; +const GUID *h264_output_types[] = {&MFVideoFormat_I420, &MFVideoFormat_IYUV, &MFVideoFormat_NV12, &MFVideoFormat_YUY2, &MFVideoFormat_YV12}; + +const GUID *aac_input_types[] = {&MFAudioFormat_AAC}; +const GUID *aac_output_types[] = {&MFAudioFormat_Float}; + +static struct decoder_desc +{ + const GUID *major_type; + const GUID **input_types; + unsigned int input_types_count; + const GUID **output_types; + unsigned int output_types_count; +} decoder_descs[] = +{ + { /* DECODER_TYPE_H264 */ + &MFMediaType_Video, + h264_input_types, + ARRAY_SIZE(h264_input_types), + h264_output_types, + ARRAY_SIZE(h264_output_types), + }, + { /* DECODER_TYPE_AAC */ + &MFMediaType_Audio, + aac_input_types, + ARRAY_SIZE(aac_input_types), + aac_output_types, + ARRAY_SIZE(aac_output_types), + } +}; + +struct mf_decoder +{ + IMFTransform IMFTransform_iface; + IMFAsyncCallback process_message_callback; + LONG refcount; + enum decoder_type type; + BOOL video; + IMFMediaType *input_type, *output_type; + BOOL valid_state; + GstBus *bus; + GstElement *container; + GstElement *parser, *decoder, *converter, *appsink; + GstPad *input_src, *their_sink; + unsigned int output_counter; + BOOL draining, eos; + CRITICAL_SECTION state_cs; + CONDITION_VARIABLE state_cv; +}; + +static struct mf_decoder *impl_mf_decoder_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct mf_decoder, IMFTransform_iface); +} + +static struct mf_decoder *impl_from_message_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct mf_decoder, process_message_callback); +} + +static HRESULT WINAPI mf_decoder_QueryInterface (IMFTransform *iface, REFIID riid, void **out) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFTransform) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + IMFTransform_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI mf_decoder_AddRef(IMFTransform *iface) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&This->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static void mf_decoder_destroy(struct mf_decoder *decoder); +static ULONG WINAPI mf_decoder_Release(IMFTransform *iface) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&This->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + mf_decoder_destroy(This); + } + + return refcount; +} + +static HRESULT WINAPI mf_decoder_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum, + DWORD *output_minimum, DWORD *output_maximum) +{ + TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum); + + *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + TRACE("%p %p %p.\n", iface, inputs, outputs); + + *inputs = *outputs = 1; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + TRACE("%p %u %p %u %p.\n", iface, input_size, inputs, output_size, outputs); + + return E_NOTIMPL; +} + + +static HRESULT WINAPI mf_decoder_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + + TRACE("%p %u %p\n", This, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + /* If we create a wrapped GstBuffer, remove MFT_INPUT_STREAM_DOES_NOT_ADDREF */ + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_DOES_NOT_ADDREF; + info->cbMaxLookahead = 0; + info->cbAlignment = 0; + /* this is incorrect */ + info->hnsMaxLatency = 0; + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + MFT_OUTPUT_STREAM_INFO stream_info = {}; + + TRACE("%p %u %p\n", This, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + stream_info.dwFlags = MFT_OUTPUT_STREAM_PROVIDES_SAMPLES; + stream_info.cbSize = 0; + stream_info.cbAlignment = 0; + + *info = stream_info; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + FIXME("%p, %p. stub!\n", iface, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetInputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + FIXME("%p, %u. stub!\n", iface, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + FIXME("%p, %u, %p. stub!\n", iface, streams, ids); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + IMFMediaType *input_type; + HRESULT hr; + + TRACE("%p, %u, %u, %p\n", This, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (index >= decoder_descs[This->type].input_types_count) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&input_type))) + return hr; + + if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_MAJOR_TYPE, decoder_descs[This->type].major_type))) + { + IMFMediaType_Release(input_type); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_SUBTYPE, decoder_descs[This->type].input_types[index]))) + { + IMFMediaType_Release(input_type); + return hr; + } + + *type = input_type; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + IMFMediaType *output_type; + HRESULT hr; + + TRACE("%p, %u, %u, %p\n", This, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!(This->input_type)) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (index >= decoder_descs[This->type].output_types_count) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&output_type))) + return hr; + + /* TODO: This may need to be more fine tuned */ + if (FAILED(hr = IMFMediaType_CopyAllItems(This->input_type, (IMFAttributes*) output_type))) + { + IMFMediaType_Release(output_type); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_MAJOR_TYPE, decoder_descs[This->type].major_type))) + { + IMFMediaType_Release(output_type); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_SUBTYPE, decoder_descs[This->type].output_types[index]))) + { + IMFMediaType_Release(output_type); + return hr; + } + + *type = output_type; + + return S_OK; +} + +static gboolean activate_push_mode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + TRACE("%s mft input pad in %s mode.\n", + activate ? "Activating" : "Deactivating", gst_pad_mode_get_name(mode)); + + switch (mode) { + case GST_PAD_MODE_PUSH: + return TRUE; + default: + return FALSE; + } +} + +static gboolean query_input_src(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct mf_decoder *This = gst_pad_get_element_private(pad); + + TRACE("GStreamer queries MFT Input Pad %p for %s\n", This, GST_QUERY_TYPE_NAME(query)); + + switch (GST_QUERY_TYPE(query)) + { + case GST_QUERY_CAPS: + { + gst_query_set_caps_result(query, caps_from_media_type(This->input_type)); + return TRUE; + } + case GST_QUERY_SCHEDULING: + { + gst_query_add_scheduling_mode(query, GST_PAD_MODE_PUSH); + return TRUE; + } + case GST_QUERY_SEEKING: + { + GstFormat format; + gboolean seekable; + gint64 segment_start, segment_end; + + gst_query_parse_seeking(query, &format, &seekable, &segment_start, &segment_end); + gst_query_set_seeking(query, format, 0, segment_start, segment_end); + return TRUE; + } + case GST_QUERY_DURATION: + { + return FALSE; + } + case GST_QUERY_LATENCY: + { + return FALSE; + } + default: + { + ERR("Unhandled query type %s on MFT Input Pad %p\n", GST_QUERY_TYPE_NAME(query), This); + return gst_pad_query_default (pad, parent, query); + } + } +} + +static GstFlowReturn decoder_new_sample(GstElement *appsink, gpointer user) +{ + struct mf_decoder *This = (struct mf_decoder *) user; + + This->output_counter++; + + return GST_FLOW_OK; +} + +static BOOL find_decoder_from_caps(GstCaps *input_caps, GstElement **decoder, GstElement **parser) +{ + GList *parser_list_one, *parser_list_two; + GList *walk; + BOOL ret = TRUE; + + TRACE("input caps: %s\n", gst_caps_to_string(input_caps)); + + parser_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER, 1); + parser_list_two = gst_element_factory_list_filter(parser_list_one, input_caps, GST_PAD_SINK, 0); + gst_plugin_feature_list_free(parser_list_one); + parser_list_one = parser_list_two; + if (!(g_list_length(parser_list_one))) + { + GList *decoder_list_one, *decoder_list_two; + decoder_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER, 1); + decoder_list_two = gst_element_factory_list_filter(decoder_list_one, input_caps, GST_PAD_SINK, 0); + gst_plugin_feature_list_free(decoder_list_one); + decoder_list_one = decoder_list_two; + if (!(g_list_length(decoder_list_one)) || + !(*decoder = gst_element_factory_create(g_list_first(decoder_list_one)->data, NULL))) + { + gst_plugin_feature_list_free(decoder_list_one); + ERR("Failed to create decoder\n"); + ret = FALSE; + goto done; + } + TRACE("Found decoder %s\n", GST_ELEMENT_NAME(g_list_first(decoder_list_one)->data)); + } + else + { + for (walk = (GList *) parser_list_one; walk; walk = g_list_next(walk)) + { + GstElementFactory *parser_factory = walk->data; + const GList *templates, *walk_templ; + + templates = gst_element_factory_get_static_pad_templates(parser_factory); + + for (walk_templ = (GList *)templates; walk_templ; walk_templ = g_list_next(walk_templ)) + { + GList *decoder_list_one, *decoder_list_two; + GstStaticPadTemplate *templ = walk_templ->data; + GstCaps *templ_caps; + + if (templ->direction != GST_PAD_SRC) + continue; + + templ_caps = gst_static_pad_template_get_caps(templ); + + TRACE("Matching parser src caps %s to decoder.\n", gst_caps_to_string(templ_caps)); + + decoder_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER, 1); + decoder_list_two = gst_element_factory_list_filter(decoder_list_one, templ_caps, GST_PAD_SINK, 0); + gst_plugin_feature_list_free(decoder_list_one); + decoder_list_one = decoder_list_two; + gst_caps_unref(templ_caps); + + if (!(g_list_length(decoder_list_one))) + continue; + + if (!(*parser = gst_element_factory_create(parser_factory, NULL))) + { + gst_plugin_feature_list_free(decoder_list_one); + ERR("Failed to create parser\n"); + ret = FALSE; + goto done; + } + + if (!(*decoder = gst_element_factory_create(g_list_first(decoder_list_one)->data, NULL))) + { + gst_plugin_feature_list_free(decoder_list_one); + ERR("Failed to create decoder\n"); + ret = FALSE; + goto done; + } + + TRACE("Found decoder %s parser %s\n", + GST_ELEMENT_NAME(g_list_first(decoder_list_one)->data), GST_ELEMENT_NAME(parser_factory)); + gst_plugin_feature_list_free(decoder_list_one); + + goto done; + } + } + } + done: + gst_plugin_feature_list_free(parser_list_one); + return ret; +} + +static void decoder_update_pipeline(struct mf_decoder *This) +{ + GstSegment *segment; + GstCaps *input_caps = NULL; + + This->valid_state = FALSE; + + /* tear down current pipeline */ + gst_element_set_state(This->container, GST_STATE_READY); + if (gst_element_get_state(This->container, NULL, NULL, -1) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to stop container\n"); + } + + g_object_set(This->appsink, "caps", gst_caps_new_empty(), NULL); + + if (This->input_src) + { + gst_pad_unlink(This->input_src, This->their_sink); + gst_object_unref(G_OBJECT(This->input_src)); + This->input_src = NULL; + } + + if (This->their_sink) + { + gst_object_unref(G_OBJECT(This->their_sink)); + This->their_sink = NULL; + } + + if (This->parser) + { + gst_element_unlink(This->parser, This->decoder); + gst_bin_remove(GST_BIN(This->container), This->parser); + This->parser = NULL; + } + if (This->decoder) + { + gst_element_unlink(This->decoder, This->converter); + gst_bin_remove(GST_BIN(This->container), This->decoder); + This->decoder = NULL; + } + + /* we can only have a valid state if an input and output type is present */ + if (!This->input_type || !This->output_type) + return; + + /* We do leave a lot of unfreed objects here when we failure, + but it will be cleaned up on the next call */ + + input_caps = caps_from_media_type(This->input_type); + + if (!(This->input_src = gst_pad_new_from_template(gst_pad_template_new( + "mf_src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + input_caps + ), "input_src"))) + { + ERR("Failed to create input source\n"); + goto done; + } + + gst_pad_set_activatemode_function(This->input_src, activate_push_mode_wrapper); + gst_pad_set_query_function(This->input_src, query_input_src_wrapper); + gst_pad_set_element_private(This->input_src, This); + + if (!(find_decoder_from_caps(input_caps, &This->decoder, &This->parser))) + { + goto done; + } + + gst_bin_add(GST_BIN(This->container), This->decoder); + if (This->parser) + { + gst_bin_add(GST_BIN(This->container), This->parser); + } + + if (!(This->their_sink = gst_element_get_static_pad(This->parser ? This->parser : This->decoder, "sink"))) + { + goto done; + } + + g_object_set(This->appsink, "caps", caps_from_media_type(This->output_type), NULL); + + if (gst_pad_link(This->input_src, This->their_sink) != GST_PAD_LINK_OK) + { + ERR("Failed to link input source to decoder sink\n"); + return; + } + + if (This->parser && !(gst_element_link(This->parser, This->decoder))) + { + ERR("Failed to link parser to decoder\n"); + goto done; + } + + if (!(gst_element_link(This->decoder, This->converter))) + { + ERR("Failed to link decoder to converter\n"); + goto done; + } + + gst_element_set_state(This->container, GST_STATE_PLAYING); + + gst_pad_set_active(This->input_src, 1); + gst_pad_push_event(This->input_src, gst_event_new_stream_start("decoder-stream")); + gst_pad_push_event(This->input_src, gst_event_new_caps(caps_from_media_type(This->input_type))); + segment = gst_segment_new(); + gst_segment_init(segment, GST_FORMAT_DEFAULT); + gst_pad_push_event(This->input_src, gst_event_new_segment(segment)); + + This->valid_state = TRUE; + done: + if (input_caps) + gst_caps_unref(input_caps); + return; +} + +static HRESULT WINAPI mf_decoder_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + HRESULT hr = S_OK; + + TRACE("%p, %u, %p, %#x\n", This, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (type) + { + GUID major_type, subtype; + BOOL found = FALSE; + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return hr; + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return hr; + + for (unsigned int i = 0; i < decoder_descs[This->type].input_types_count; i++) + { + UINT64 unused; + + if (IsEqualGUID(&major_type, decoder_descs[This->type].major_type) && + IsEqualGUID(&subtype, decoder_descs[This->type].input_types[i])) + { + if (This->video) + { + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused))) + return hr; + + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &unused))) + return hr; + } + + found = TRUE; + break; + } + } + + if (!found) + return MF_E_INVALIDTYPE; + } + + if (flags & MFT_SET_TYPE_TEST_ONLY) + { + return S_OK; + } + + EnterCriticalSection(&This->state_cs); + + if (type) + { + if (!This->input_type) + if (FAILED(hr = MFCreateMediaType(&This->input_type))) + goto done; + + if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) This->input_type))) + goto done; + } + else if (This->input_type) + { + IMFMediaType_Release(This->input_type); + This->input_type = NULL; + } + + decoder_update_pipeline(This); + + done: + LeaveCriticalSection(&This->state_cs); + WakeAllConditionVariable(&This->state_cv); + return hr; +} + +static HRESULT WINAPI mf_decoder_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + HRESULT hr = S_OK; + + TRACE("%p, %u, %p, %#x\n", This, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (type) + { + /* validate the type */ + + for (unsigned int i = 0; i < decoder_descs[This->type].output_types_count; i++) + { + GUID major_type, subtype; + UINT64 unused; + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return hr; + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return hr; + + if (IsEqualGUID(&major_type, decoder_descs[This->type].major_type) && + IsEqualGUID(&subtype, decoder_descs[This->type].output_types[i])) + { + if (This->video) + { + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &unused))) + return hr; + + if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &unused))) + return hr; + } + + break; + } + } + } + + if (flags & MFT_SET_TYPE_TEST_ONLY) + { + return S_OK; + } + + EnterCriticalSection(&This->state_cs); + if (type) + { + if (!This->output_type) + if (FAILED(hr = MFCreateMediaType(&This->output_type))) + goto done; + + if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) This->output_type))) + goto done; + } + else if (This->output_type) + { + IMFMediaType_Release(This->output_type); + This->output_type = NULL; + } + + decoder_update_pipeline(This); + + done: + LeaveCriticalSection(&This->state_cs); + WakeAllConditionVariable(&This->state_cv); + return hr; +} + +static HRESULT WINAPI mf_decoder_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + + TRACE("%p, %u, %p\n", This, id, flags); + + *flags = This->output_counter ? MFT_INPUT_STATUS_ACCEPT_DATA : 0; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + + TRACE("%p, %p.\n", This, flags); + + *flags = This->output_counter ? MFT_OUTPUT_STATUS_SAMPLE_READY : 0; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + FIXME("%p, %s, %s. stub!\n", iface, wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + FIXME("%p, %u, %p. stub!\n", iface, id, event); + + return E_NOTIMPL; +} + +static HRESULT WINAPI decoder_process_message_callback_QueryInterface(IMFAsyncCallback *iface, + REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI decoder_process_message_callback_AddRef(IMFAsyncCallback *iface) +{ + struct mf_decoder *decoder = impl_from_message_callback_IMFAsyncCallback(iface); + return IMFTransform_AddRef(&decoder->IMFTransform_iface); +} + +static ULONG WINAPI decoder_process_message_callback_Release(IMFAsyncCallback *iface) +{ + struct mf_decoder *decoder = impl_from_message_callback_IMFAsyncCallback(iface); + return IMFTransform_Release(&decoder->IMFTransform_iface); +} + +static HRESULT WINAPI decoder_process_message_callback_GetParameters(IMFAsyncCallback *iface, + DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +const GUID WINE_MFT_MESSAGE_TYPE = {0xd09998bf, 0x102f, 0x4efa, {0x8f,0x84,0x06,0x1f,0xa4,0x10,0xf2,0x64}}; + +static HRESULT WINAPI decoder_process_message_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct mf_decoder *This = impl_from_message_callback_IMFAsyncCallback(iface); + IUnknown *state; + IMFAttributes *async_param; + MFT_MESSAGE_TYPE message_type; + HRESULT hr; + + state = IMFAsyncResult_GetStateNoAddRef(result); + if (!state) + return E_FAIL; + if (FAILED(hr = IUnknown_QueryInterface(state, &IID_IMFAttributes, (void **)&async_param))) + return hr; + if (FAILED(hr = IMFAttributes_GetUINT32(async_param, &WINE_MFT_MESSAGE_TYPE, &message_type))) + { + IMFAttributes_Release(async_param); + return hr; + } + IMFAttributes_Release(async_param); + + switch (message_type) + { + case MFT_MESSAGE_COMMAND_DRAIN: + { + EnterCriticalSection(&This->state_cs); + This->draining = TRUE; + LeaveCriticalSection(&This->state_cs); + WakeAllConditionVariable(&This->state_cv); + gst_pad_push_event(This->input_src, gst_event_new_eos()); + + EnterCriticalSection(&This->state_cs); + while(This->eos == FALSE) + { + SleepConditionVariableCS(&This->state_cv, &This->state_cs, INFINITE); + } + gst_pad_push_event(This->input_src, gst_event_new_flush_stop(0)); + This->draining = FALSE; + LeaveCriticalSection(&This->state_cs); + WakeAllConditionVariable(&This->state_cv); + return S_OK; + } + default: + return E_FAIL; + } +} + +static const IMFAsyncCallbackVtbl process_message_callback_vtbl = +{ + decoder_process_message_callback_QueryInterface, + decoder_process_message_callback_AddRef, + decoder_process_message_callback_Release, + decoder_process_message_callback_GetParameters, + decoder_process_message_callback_Invoke, +}; + +static HRESULT WINAPI mf_decoder_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + IMFAttributes *async_param; + HRESULT hr; + + TRACE("%p, %u %lu.\n", This, message, param); + + if (FAILED(hr = MFCreateAttributes(&async_param, 1))) + return hr; + EnterCriticalSection(&This->state_cs); + + switch (message) + { + case MFT_MESSAGE_COMMAND_DRAIN: + { + if (This->draining) + { + hr = S_OK; + break; + } + + IMFAttributes_SetUINT32(async_param, &WINE_MFT_MESSAGE_TYPE, message); + + MFPutWorkItem(MF_MULTITHREADED_WORKQUEUE, &This->process_message_callback, (IUnknown *)async_param); + while (!This->draining) + SleepConditionVariableCS(&This->state_cv, &This->state_cs, INFINITE); + + hr = S_OK; + break; + } + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + { + hr = S_OK; + break; + } + default: + { + ERR("Unhandled message type %u.\n", message); + hr = E_FAIL; + break; + } + } + + LeaveCriticalSection(&This->state_cs); + IMFAttributes_Release(async_param); + return hr; +} + +static HRESULT WINAPI mf_decoder_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + GstBuffer *gst_buffer; + GstFlowReturn ret; + HRESULT hr = S_OK; + + TRACE("%p, %u, %p, %#x\n", This, id, sample, flags); + + if (flags) + WARN("Unsupported flags %#x\n", flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!This->valid_state) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + EnterCriticalSection(&This->state_cs); + + if (This->output_counter || This->draining) + { + hr = MF_E_NOTACCEPTING; + goto done; + } + + if (!(gst_buffer = gst_buffer_from_mf_sample(sample))) + { + hr = E_FAIL; + goto done; + } + + ret = gst_pad_push(This->input_src, gst_buffer); + if (ret != GST_FLOW_OK) + { + ERR("Couldn't process input ret = %d\n", ret); + hr = E_FAIL; + goto done; + } + + done: + LeaveCriticalSection(&This->state_cs); + return hr; +} + +static HRESULT WINAPI mf_decoder_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct mf_decoder *This = impl_mf_decoder_from_IMFTransform(iface); + GstSample *buffer; + MFT_OUTPUT_DATA_BUFFER *relevant_buffer = NULL; + + TRACE("%p, %#x, %u, %p, %p,\n", iface, flags, count, samples, status); + + if (flags) + { + WARN("Unsupported flags %#x\n", flags); + } + + if (!This->valid_state) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + for (unsigned int i = 0; i < count; i++) + { + MFT_OUTPUT_DATA_BUFFER *out_buffer = &samples[i]; + + if (out_buffer->dwStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (relevant_buffer) + return MF_E_INVALIDSTREAMNUMBER; + + relevant_buffer = out_buffer; + } + + if (!relevant_buffer) + return S_OK; + + EnterCriticalSection(&This->state_cs); + if (This->draining) + { + if (This->eos) + buffer = NULL; + else + g_signal_emit_by_name(This->appsink, "pull-sample", &buffer); + } + else + g_signal_emit_by_name(This->appsink, "try-pull-sample", 0, &buffer); + LeaveCriticalSection(&This->state_cs); + + if (!buffer) + return MF_E_TRANSFORM_NEED_MORE_INPUT; + This->output_counter--; + + relevant_buffer->pSample = mf_sample_from_gst_buffer(gst_sample_get_buffer(buffer)); + gst_sample_unref(buffer); + relevant_buffer->dwStatus = S_OK; + relevant_buffer->pEvents = NULL; + *status = 0; + return S_OK; +} + +static const IMFTransformVtbl mf_decoder_vtbl = +{ + mf_decoder_QueryInterface, + mf_decoder_AddRef, + mf_decoder_Release, + mf_decoder_GetStreamLimits, + mf_decoder_GetStreamCount, + mf_decoder_GetStreamIDs, + mf_decoder_GetInputStreamInfo, + mf_decoder_GetOutputStreamInfo, + mf_decoder_GetAttributes, + mf_decoder_GetInputStreamAttributes, + mf_decoder_GetOutputStreamAttributes, + mf_decoder_DeleteInputStream, + mf_decoder_AddInputStreams, + mf_decoder_GetInputAvailableType, + mf_decoder_GetOutputAvailableType, + mf_decoder_SetInputType, + mf_decoder_SetOutputType, + mf_decoder_GetInputCurrentType, + mf_decoder_GetOutputCurrentType, + mf_decoder_GetInputStatus, + mf_decoder_GetOutputStatus, + mf_decoder_SetOutputBounds, + mf_decoder_ProcessEvent, + mf_decoder_ProcessMessage, + mf_decoder_ProcessInput, + mf_decoder_ProcessOutput, +}; + +GstBusSyncReply watch_decoder_bus(GstBus *bus, GstMessage *message, gpointer user_data) +{ + struct mf_decoder *This = user_data; + GError *err = NULL; + gchar *dbg_info = NULL; + + TRACE("decoder %p message type %s\n", This, GST_MESSAGE_TYPE_NAME(message)); + + switch (message->type) + { + case GST_MESSAGE_ERROR: + gst_message_parse_error(message, &err, &dbg_info); + ERR("%s: %s\n", GST_OBJECT_NAME(message->src), err->message); + ERR("%s\n", dbg_info); + g_error_free(err); + g_free(dbg_info); + break; + case GST_MESSAGE_WARNING: + gst_message_parse_warning(message, &err, &dbg_info); + WARN("%s: %s\n", GST_OBJECT_NAME(message->src), err->message); + WARN("%s\n", dbg_info); + g_error_free(err); + g_free(dbg_info); + break; + case GST_MESSAGE_EOS: + This->eos = TRUE; + WakeAllConditionVariable(&This->state_cv); + break; + default: + break; + } + + return GST_BUS_DROP; +} + +static void mf_decoder_destroy(struct mf_decoder *This) +{ + if (This->input_type) + { + IMFMediaType_Release(This->input_type); + This->input_type = NULL; + } + + if (This->output_type) + { + IMFMediaType_Release(This->output_type); + This->output_type = NULL; + } + + decoder_update_pipeline(This); + + if (This->their_sink) + gst_object_unref(G_OBJECT(This->their_sink)); + + if (This->container) + gst_object_unref(G_OBJECT(This->container)); + + if (This->bus) + gst_object_unref(G_OBJECT(This->bus)); + + DeleteCriticalSection(&This->state_cs); + + heap_free(This); +} + +HRESULT generic_decoder_construct(REFIID riid, void **obj, enum decoder_type type) +{ + struct mf_decoder *This; + HRESULT hr = S_OK; + + TRACE("%s, %p %u.\n", debugstr_guid(riid), obj, type); + + if (!(This = heap_alloc_zero(sizeof(*This)))) + return E_OUTOFMEMORY; + This->type = type; + This->video = decoder_descs[type].major_type == &MFMediaType_Video; + + InitializeCriticalSection(&This->state_cs); + InitializeConditionVariable(&This->state_cv); + + This->container = gst_bin_new(NULL); + This->bus = gst_bus_new(); + gst_bus_set_sync_handler(This->bus, watch_decoder_bus_wrapper, This, NULL); + gst_element_set_bus(This->container, This->bus); + + if (!(This->converter = gst_element_factory_make(This->video ? "videoconvert" : "audioconvert", NULL))) + { + ERR("Failed to create videoconvert\n"); + hr = E_FAIL; + goto fail; + } + if (!(This->appsink = gst_element_factory_make("appsink", NULL))) + { + ERR("Failed to create appsink\n"); + hr = E_FAIL; + goto fail; + } + + gst_bin_add(GST_BIN(This->container), This->converter); + gst_bin_add(GST_BIN(This->container), This->appsink); + + if (!(gst_element_link(This->converter, This->appsink))) + { + ERR("Failed to link converter to appsink\n"); + hr = E_FAIL; + goto fail; + } + + g_object_set(This->appsink, "emit-signals", TRUE, NULL); + g_object_set(This->appsink, "sync", FALSE, NULL); + g_signal_connect(This->appsink, "new-sample", G_CALLBACK(decoder_new_sample_wrapper), This); + + This->process_message_callback.lpVtbl = &process_message_callback_vtbl; + + This->IMFTransform_iface.lpVtbl = &mf_decoder_vtbl; + This->refcount = 1; + + *obj = This; + return S_OK; + + fail: + ERR("Failed to create Decoder MFT type %u, hr = %#x\n", type, hr); + mf_decoder_destroy(This); + return hr; +} + +void perform_cb_mf_decode(struct cb_data *cbdata) +{ + switch (cbdata->type) + { + case ACTIVATE_PUSH_MODE: + { + struct activate_mode_data *data = &cbdata->u.activate_mode_data; + cbdata->u.activate_mode_data.ret = activate_push_mode(data->pad, data->parent, data->mode, data->activate); + break; + } + case QUERY_INPUT_SRC: + { + struct query_function_data *data = &cbdata->u.query_function_data; + cbdata->u.query_function_data.ret = query_input_src(data->pad, data->parent, data->query); + break; + } + case DECODER_NEW_SAMPLE: + { + struct new_sample_data *data = &cbdata->u.new_sample_data; + cbdata->u.new_sample_data.ret = decoder_new_sample(data->appsink, data->user); + break; + } + case WATCH_DECODER_BUS: + { + struct watch_bus_data *data = &cbdata->u.watch_bus_data; + cbdata->u.watch_bus_data.ret = watch_decoder_bus(data->bus, data->msg, data->user); + break; + } + default: + { + ERR("Wrong callback forwarder called\n"); + return; + } + } +} \ No newline at end of file diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 5fbb331c89..b7bab5e572 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -404,6 +404,16 @@ failed: return hr; }
+static HRESULT h264_decoder_create(REFIID riid, void **ret) +{ + return generic_decoder_construct(riid, ret, DECODER_TYPE_H264); +} + +static HRESULT aac_decoder_create(REFIID riid, void **ret) +{ + return generic_decoder_construct(riid, ret, DECODER_TYPE_AAC); +} + static HRESULT mp4_stream_handler_create(REFIID riid, void **ret) { return container_stream_handler_construct(riid, ret, SOURCE_TYPE_MPEG_4); @@ -417,6 +427,8 @@ static const struct class_object class_objects[] = { { &CLSID_VideoProcessorMFT, &video_processor_create }, + { &CLSID_CMSH264DecoderMFT, &h264_decoder_create }, + { &CLSID_CMSAACDecMFT, &aac_decoder_create }, { &CLSID_MPEG4ByteStreamHandler, &mp4_stream_handler_create }, };
@@ -446,11 +458,140 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) return CLASS_E_CLASSNOTAVAILABLE; }
+struct register_type_info +{ + const GUID *major_type; + const GUID *sub_type; +}; + +static WCHAR h264decoderW[] = {'H','.','2','6','4',' ','D','e','c','o','d','e','r',0}; +const struct register_type_info h264_decoder_input_types[] = +{ + { + &MFMediaType_Video, + &MFVideoFormat_H264 + } +}; +const struct register_type_info h264_decoder_output_types[] = +{ + { + &MFMediaType_Video, + &MFVideoFormat_I420 + }, + { + &MFMediaType_Video, + &MFVideoFormat_IYUV + }, + { + &MFMediaType_Video, + &MFVideoFormat_NV12 + }, + { + &MFMediaType_Video, + &MFVideoFormat_YUY2, + }, + { + &MFMediaType_Video, + &MFVideoFormat_YV12, + } +}; + +static WCHAR aacdecoderW[] = {'A','A','C',' ','D','e','c','o','d','e','r',0}; +const struct register_type_info aac_decoder_input_types[] = +{ + { + &MFMediaType_Audio, + &MFAudioFormat_AAC + } +}; + +const struct register_type_info aac_decoder_output_types[] = +{ + { + &MFMediaType_Audio, + &MFAudioFormat_Float + } +}; + +static const struct mft +{ + const GUID *clsid; + const GUID *category; + LPWSTR name; + const UINT32 flags; + const UINT32 input_types_count; + const struct register_type_info *input_types; + const UINT32 output_types_count; + const struct register_type_info *output_types; + IMFAttributes *attributes; +} +mfts[] = +{ + { + &CLSID_CMSH264DecoderMFT, + &MFT_CATEGORY_VIDEO_DECODER, + h264decoderW, + MFT_ENUM_FLAG_SYNCMFT, + ARRAY_SIZE(h264_decoder_input_types), + h264_decoder_input_types, + ARRAY_SIZE(h264_decoder_output_types), + h264_decoder_output_types, + NULL + }, + { + &CLSID_CMSAACDecMFT, + &MFT_CATEGORY_AUDIO_DECODER, + aacdecoderW, + MFT_ENUM_FLAG_SYNCMFT, + ARRAY_SIZE(aac_decoder_input_types), + aac_decoder_input_types, + ARRAY_SIZE(aac_decoder_output_types), + aac_decoder_output_types, + NULL + } +}; + +HRESULT mfplat_DllRegisterServer(void) +{ + HRESULT hr; + + for (unsigned int i = 0; i < ARRAY_SIZE(mfts); i++) + { + const struct mft *cur = &mfts[i]; + + MFT_REGISTER_TYPE_INFO *input_types, *output_types; + input_types = heap_alloc(cur->input_types_count * sizeof(input_types[0])); + output_types = heap_alloc(cur->output_types_count * sizeof(output_types[0])); + for (unsigned int i = 0; i < cur->input_types_count; i++) + { + input_types[i].guidMajorType = *(cur->input_types[i].major_type); + input_types[i].guidSubtype = *(cur->input_types[i].sub_type); + } + for (unsigned int i = 0; i < cur->output_types_count; i++) + { + output_types[i].guidMajorType = *(cur->output_types[i].major_type); + output_types[i].guidSubtype = *(cur->output_types[i].sub_type); + } + + hr = MFTRegister(*(cur->clsid), *(cur->category), cur->name, cur->flags, cur->input_types_count, + input_types, cur->output_types_count, output_types, cur->attributes); + + heap_free(input_types); + heap_free(output_types); + + if (FAILED(hr)) + { + FIXME("Failed to register MFT, hr %#x\n", hr); + return hr; + } + } + return S_OK; +} + struct aac_user_data { WORD payload_type; WORD profile_level_indication; - WORD struct_type; WORD reserved; /*BYTE audio_specific_config;*/ }; diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index 997a28b052..ee8090cf4a 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -61,3 +61,15 @@ coclass VideoProcessorMFT {} uuid(271c3902-6095-4c45-a22f-20091816ee9e) ] coclass MPEG4ByteStreamHandler {} + +[ + threading(both), + uuid(62ce7e72-4c71-4d20-b15d-452831a87d9d) +] +coclass CMSH264DecoderMFT { } + +[ + threading(both), + uuid(32d186a7-218f-4c75-8876-dd77273a8999) +] +coclass CMSAACDecMFT { } diff --git a/include/mfidl.idl b/include/mfidl.idl index 15a68a4253..d07852e444 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -1066,4 +1066,6 @@ cpp_quote("EXTERN_GUID(MF_SESSION_APPROX_EVENT_OCCURRENCE_TIME, 0x190e852f, 0x62 cpp_quote("EXTERN_GUID(MF_PMP_SERVER_CONTEXT, 0x2f00c910, 0xd2cf, 0x4278, 0x8b, 0x6a, 0xd0, 0x77, 0xfa, 0xc3, 0xa2, 0x5f);")
cpp_quote("EXTERN_GUID(CLSID_VideoProcessorMFT, 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82);") -cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);") \ No newline at end of file +cpp_quote("EXTERN_GUID(CLSID_MPEG4ByteStreamHandler, 0x271c3902, 0x6095, 0x4c45, 0xa2, 0x2f, 0x20, 0x09, 0x18, 0x16, 0xee, 0x9e);") +cpp_quote("EXTERN_GUID(CLSID_CMSH264DecoderMFT, 0x62ce7e72, 0x4c71, 0x4d20, 0xb1, 0x5d, 0x45, 0x28, 0x31, 0xa8, 0x7d, 0x9d);") +cpp_quote("EXTERN_GUID(CLSID_CMSAACDecMFT, 0x32d186a7, 0x218f, 0x4c75, 0x88, 0x76, 0xdd, 0x77, 0x27, 0x3a, 0x89, 0x99);")