From: Derek Lesho Subject: [PATCH v2 06/18] winegstreamer: Implement IMFMediaStream::GetStreamDescriptor. Message-Id: <20200401220539.522012-6-dlesho@codeweavers.com> Date: Wed, 1 Apr 2020 17:05:27 -0500 In-Reply-To: <20200401220539.522012-1-dlesho@codeweavers.com> References: <20200401220539.522012-1-dlesho@codeweavers.com> Signed-off-by: Derek Lesho --- dlls/winegstreamer/gst_private.h | 4 + dlls/winegstreamer/media_source.c | 54 ++++++++- dlls/winegstreamer/mfplat.c | 186 ++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 71ca429088..780cf1b02f 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -36,6 +36,7 @@ #include "winuser.h" #include "dshow.h" #include "strmif.h" +#include "mfobjects.h" #include "wine/heap.h" #include "wine/strmbase.h" @@ -54,6 +55,9 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN; extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN; +GstCaps *make_mf_compatible_caps(GstCaps *caps); +IMFMediaType *mf_media_type_from_caps(GstCaps *caps); + enum source_type { SOURCE_TYPE_MPEG_4, diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index ae9880847c..080d2c43ca 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -207,7 +207,10 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM if (This->state == STREAM_SHUTDOWN) return MF_E_SHUTDOWN; - return E_NOTIMPL; + IMFStreamDescriptor_AddRef(This->descriptor); + *descriptor = This->descriptor; + + return S_OK; } static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) @@ -269,6 +272,9 @@ static void media_stream_teardown(struct media_stream *This) static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) { HRESULT hr; + GstCaps *caps = NULL, *compatible_caps = NULL; + IMFMediaType *media_type; + IMFMediaTypeHandler *type_handler; struct media_stream *This = heap_alloc_zero(sizeof(*This)); TRACE("(%p %p)->(%p)\n", source, pad, out_stream); @@ -297,6 +303,44 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad g_object_set(This->appsink, "sync", FALSE, NULL); g_signal_connect(This->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), This); + if (FAILED(hr = MFCreateMediaType(&media_type))) + { + goto fail; + } + + caps = gst_pad_query_caps(pad, NULL); + + if (!(caps)) + { + goto fail; + } + + compatible_caps = make_mf_compatible_caps(caps); + media_type = mf_media_type_from_caps(compatible_caps); + + if (!media_type) + goto fail; + + MFCreateStreamDescriptor(stream_id, 1, &media_type, &This->descriptor); + + IMFStreamDescriptor_GetMediaTypeHandler(This->descriptor, &type_handler); + IMFMediaTypeHandler_SetCurrentMediaType(type_handler, media_type); + IMFMediaTypeHandler_Release(type_handler); + IMFMediaType_Release(media_type); + media_type = NULL; + + if (TRACE_ON(mfplat)) + { + gchar *caps_str = gst_caps_to_string(caps), *compatible_caps_str = gst_caps_to_string(compatible_caps); + TRACE("caps %s compatible caps %s\n", debugstr_a(caps_str), debugstr_a(compatible_caps_str)); + g_free(caps_str); + g_free(compatible_caps_str); + } + gst_caps_unref(caps); + caps = NULL; + gst_caps_unref(compatible_caps); + compatible_caps = NULL; + This->their_src = pad; This->my_sink = gst_element_get_static_pad(This->appsink, "sink"); gst_pad_set_element_private(pad, This); @@ -316,6 +360,14 @@ static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad fail: WARN("Failed to construct media stream, hr %#x.\n", hr); + /* Destroy temporary objects */ + if (caps) + gst_caps_unref(caps); + if (compatible_caps) + gst_caps_unref(compatible_caps); + if (media_type) + IMFMediaType_Release(media_type); + media_stream_teardown(This); heap_free(This); return hr; diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index 16e55247de..09266ffab4 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -16,6 +16,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "config.h" +#include + +#include "gst_private.h" + #include #include "gst_private.h" @@ -442,3 +447,184 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) return CLASS_E_CLASSNOTAVAILABLE; } + +const static struct +{ + const GUID *subtype; + GstVideoFormat format; +} +uncompressed_formats[] = +{ + {&MFVideoFormat_ARGB32, GST_VIDEO_FORMAT_BGRA}, + {&MFVideoFormat_RGB32, GST_VIDEO_FORMAT_BGRx}, + {&MFVideoFormat_RGB24, GST_VIDEO_FORMAT_BGR}, + {&MFVideoFormat_RGB565, GST_VIDEO_FORMAT_BGR16}, + {&MFVideoFormat_RGB555, GST_VIDEO_FORMAT_BGR15}, +}; + +/* caps will be modified to represent the exact type needed for the format */ +static IMFMediaType* transform_to_media_type(GstCaps *caps) +{ + IMFMediaType *media_type; + GstStructure *info; + const char *mime_type; + + if (TRACE_ON(mfplat)) + { + gchar *human_readable = gst_caps_to_string(caps); + TRACE("caps = %s\n", debugstr_a(human_readable)); + g_free(human_readable); + } + + if (FAILED(MFCreateMediaType(&media_type))) + { + return NULL; + } + + info = gst_caps_get_structure(caps, 0); + mime_type = gst_structure_get_name(info); + + if (!(strncmp(mime_type, "video", 5))) + { + GstVideoInfo video_info; + + if (!(gst_video_info_from_caps(&video_info, caps))) + { + return NULL; + } + + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + + IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)video_info.width << 32) | video_info.height); + + IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)video_info.fps_n << 32) | video_info.fps_d); + + if (!(strcmp(mime_type, "video/x-raw"))) + { + GUID fourcc_subtype = MFVideoFormat_Base; + unsigned int i; + + IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, FALSE); + + /* First try FOURCC */ + if ((fourcc_subtype.Data1 = gst_video_format_to_fourcc(video_info.finfo->format))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &fourcc_subtype); + } + else + { + for (i = 0; i < ARRAY_SIZE(uncompressed_formats); i++) + { + if (uncompressed_formats[i].format == video_info.finfo->format) + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, uncompressed_formats[i].subtype); + break; + } + } + if (i == ARRAY_SIZE(uncompressed_formats)) + FIXME("Unrecognized format.\n"); + } + } + else + FIXME("Unrecognized video format %s\n", mime_type); + } + else if (!(strncmp(mime_type, "audio", 5))) + { + IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + + if (!(strcmp(mime_type, "audio/x-raw"))) + { + const char *format; + if ((format = gst_structure_get_string(info, "format"))) + { + char type; + unsigned int bits_per_sample; + char endian[2]; + char new_format[6]; + if ((strlen(format) > 5) || (sscanf(format, "%c%u%2c", &type, &bits_per_sample, endian) < 2)) + { + FIXME("Unhandled audio format %s\n", format); + IMFMediaType_Release(media_type); + return NULL; + } + + if (type == 'F') + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_Float); + } + else if (type == 'U' || type == 'S') + { + IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); + if (bits_per_sample == 8) + type = 'U'; + else + type = 'S'; + } + else + { + FIXME("Unrecognized audio format: %s\n", format); + IMFMediaType_Release(media_type); + return NULL; + } + + IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, bits_per_sample); + + if (endian[0] == 'B') + endian[0] = 'L'; + + sprintf(new_format, "%c%u%.2s", type, bits_per_sample, bits_per_sample > 8 ? endian : 0); + gst_caps_set_simple(caps, "format", G_TYPE_STRING, new_format, NULL); + } + else + { + ERR("Failed to get audio format\n"); + } + } + else + FIXME("Unrecognized audio format %s\n", mime_type); + } + else + { + IMFMediaType_Release(media_type); + return NULL; + } + + return media_type; +} + +/* returns NULL if doesn't match exactly */ +IMFMediaType *mf_media_type_from_caps(GstCaps *caps) +{ + GstCaps *writeable_caps; + IMFMediaType *ret; + + writeable_caps = gst_caps_copy(caps); + ret = transform_to_media_type(writeable_caps); + + if (!(gst_caps_is_equal(caps, writeable_caps))) + { + IMFMediaType_Release(ret); + ret = NULL; + } + gst_caps_unref(writeable_caps); + return ret; +} + +GstCaps *make_mf_compatible_caps(GstCaps *caps) +{ + GstCaps *ret; + IMFMediaType *media_type; + + ret = gst_caps_copy(caps); + + if ((media_type = transform_to_media_type(ret))) + IMFMediaType_Release(media_type); + + if (!media_type) + { + gst_caps_unref(ret); + return NULL; + } + + return ret; +} \ No newline at end of file -- 2.26.0