[v3 PATCH 1/2] mfreadwrite: Configure stream decoder on SetCurrentMediaType() if necessary.

Nikolay Sivov nsivov at codeweavers.com
Fri Mar 20 07:16:56 CDT 2020


From: Derek Lesho <dlesho at codeweavers.com>

Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---

- split into several helpers to improve readability;
- removed PLAYING state check, since we don't support flushing anyway;
- fixed some misplaced media type calls;
- removed incorrect IsMediaTypeSupported() usage, last argument is not guaranteed to be set.
  Default implementation does not set it;

Derek, please take a look if my changes break anything.

 dlls/mfreadwrite/main.c | 203 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 196 insertions(+), 7 deletions(-)

diff --git a/dlls/mfreadwrite/main.c b/dlls/mfreadwrite/main.c
index 56ec036089..d3c10a4d11 100644
--- a/dlls/mfreadwrite/main.c
+++ b/dlls/mfreadwrite/main.c
@@ -94,6 +94,7 @@ struct media_stream
 {
     IMFMediaStream *stream;
     IMFMediaType *current;
+    IMFTransform *decoder;
     DWORD id;
     CRITICAL_SECTION cs;
     CONDITION_VARIABLE sample_event;
@@ -576,6 +577,8 @@ static ULONG WINAPI src_reader_Release(IMFSourceReader *iface)
                 IMFMediaStream_Release(stream->stream);
             if (stream->current)
                 IMFMediaType_Release(stream->current);
+            if (stream->decoder)
+                IMFTransform_Release(stream->decoder);
             DeleteCriticalSection(&stream->cs);
 
             LIST_FOR_EACH_ENTRY_SAFE(ptr, next, &stream->samples, struct sample, entry)
@@ -669,18 +672,15 @@ static HRESULT WINAPI src_reader_SetStreamSelection(IMFSourceReader *iface, DWOR
     return S_OK;
 }
 
-static HRESULT WINAPI src_reader_GetNativeMediaType(IMFSourceReader *iface, DWORD index, DWORD type_index,
-            IMFMediaType **type)
+static HRESULT source_reader_get_native_media_type(struct source_reader *reader, DWORD index, DWORD type_index,
+        IMFMediaType **type)
 {
-    struct source_reader *reader = impl_from_IMFSourceReader(iface);
     IMFMediaTypeHandler *handler;
     IMFStreamDescriptor *sd;
     IMFMediaType *src_type;
     BOOL selected;
     HRESULT hr;
 
-    TRACE("%p, %#x, %#x, %p.\n", iface, index, type_index, type);
-
     switch (index)
     {
         case MF_SOURCE_READER_FIRST_VIDEO_STREAM:
@@ -717,6 +717,16 @@ static HRESULT WINAPI src_reader_GetNativeMediaType(IMFSourceReader *iface, DWOR
     return hr;
 }
 
+static HRESULT WINAPI src_reader_GetNativeMediaType(IMFSourceReader *iface, DWORD index, DWORD type_index,
+            IMFMediaType **type)
+{
+    struct source_reader *reader = impl_from_IMFSourceReader(iface);
+
+    TRACE("%p, %#x, %#x, %p.\n", iface, index, type_index, type);
+
+    return source_reader_get_native_media_type(reader, index, type_index, type);
+}
+
 static HRESULT WINAPI src_reader_GetCurrentMediaType(IMFSourceReader *iface, DWORD index, IMFMediaType **type)
 {
     struct source_reader *reader = impl_from_IMFSourceReader(iface);
@@ -751,6 +761,184 @@ static HRESULT WINAPI src_reader_GetCurrentMediaType(IMFSourceReader *iface, DWO
     return hr;
 }
 
+static HRESULT source_reader_get_source_type_handler(struct source_reader *reader, DWORD index,
+        IMFMediaTypeHandler **handler)
+{
+    IMFStreamDescriptor *sd;
+    BOOL selected;
+    HRESULT hr;
+
+    if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(reader->descriptor, index, &selected, &sd)))
+        return hr;
+
+    hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, handler);
+    IMFStreamDescriptor_Release(sd);
+
+    return hr;
+}
+
+static HRESULT source_reader_set_compatible_media_type(struct source_reader *reader, DWORD index, IMFMediaType *type)
+{
+    IMFMediaTypeHandler *type_handler;
+    IMFMediaType *native_type;
+    BOOL type_set = FALSE;
+    unsigned int i = 0;
+    DWORD flags;
+    HRESULT hr;
+
+    if (FAILED(hr = IMFMediaType_IsEqual(type, reader->streams[index].current, &flags)))
+        return hr;
+
+    if (!(flags & MF_MEDIATYPE_EQUAL_MAJOR_TYPES))
+        return MF_E_INVALIDMEDIATYPE;
+
+    /* No need for a decoder or type change. */
+    if (flags & MF_MEDIATYPE_EQUAL_FORMAT_TYPES)
+        return S_OK;
+
+    if (FAILED(hr = source_reader_get_source_type_handler(reader, index, &type_handler)))
+        return hr;
+
+    while (!type_set && IMFMediaTypeHandler_GetMediaTypeByIndex(type_handler, i++, &native_type) == S_OK)
+    {
+        static const DWORD compare_flags = MF_MEDIATYPE_EQUAL_MAJOR_TYPES | MF_MEDIATYPE_EQUAL_FORMAT_TYPES;
+
+        if (SUCCEEDED(IMFMediaType_IsEqual(native_type, type, &flags)) && (flags & compare_flags) == compare_flags)
+        {
+            if ((type_set = SUCCEEDED(IMFMediaTypeHandler_SetCurrentMediaType(type_handler, native_type))))
+                IMFMediaType_CopyAllItems(native_type, (IMFAttributes *)reader->streams[index].current);
+        }
+
+        IMFMediaType_Release(native_type);
+    }
+
+    IMFMediaTypeHandler_Release(type_handler);
+
+    return type_set ? S_OK : S_FALSE;
+}
+
+static HRESULT source_reader_configure_decoder(struct source_reader *reader, DWORD index, const CLSID *clsid,
+        IMFMediaType *input_type, IMFMediaType *output_type)
+{
+    IMFMediaTypeHandler *type_handler;
+    IMFTransform *transform = NULL;
+    IMFMediaType *type = NULL;
+    DWORD flags;
+    HRESULT hr;
+    int i = 0;
+
+    if (FAILED(hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform)))
+    {
+        WARN("Failed to create transform object, hr %#x.\n", hr);
+        return hr;
+    }
+
+    if (FAILED(hr = IMFTransform_SetInputType(transform, 0, input_type, 0)))
+    {
+        WARN("Failed to set decoder input type, hr %#x.\n", hr);
+        IMFTransform_Release(transform);
+        return hr;
+    }
+
+    /* Find the relevant output type. */
+    while (IMFTransform_GetOutputAvailableType(transform, 0, i++, &type) == S_OK)
+    {
+        flags = 0;
+
+        if (SUCCEEDED(IMFMediaType_IsEqual(type, output_type, &flags)))
+        {
+            if (flags & MF_MEDIATYPE_EQUAL_FORMAT_TYPES)
+            {
+                if (SUCCEEDED(IMFTransform_SetOutputType(transform, 0, type, 0)))
+                {
+                    if (SUCCEEDED(source_reader_get_source_type_handler(reader, index, &type_handler)))
+                    {
+                        IMFMediaTypeHandler_SetCurrentMediaType(type_handler, input_type);
+                        IMFMediaTypeHandler_Release(type_handler);
+                    }
+
+                    if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *)reader->streams[index].current)))
+                        WARN("Failed to copy attributes, hr %#x.\n", hr);
+                    IMFMediaType_Release(type);
+
+                    if (reader->streams[index].decoder)
+                        IMFTransform_Release(reader->streams[index].decoder);
+
+                    reader->streams[index].decoder = transform;
+
+                    return S_OK;
+                }
+            }
+        }
+
+        IMFMediaType_Release(type);
+    }
+
+    WARN("Failed to find suitable decoder output type.\n");
+
+    IMFTransform_Release(transform);
+
+    return MF_E_TOPO_CODEC_NOT_FOUND;
+}
+
+static HRESULT source_reader_create_decoder_for_stream(struct source_reader *reader, DWORD index, IMFMediaType *output_type)
+{
+    MFT_REGISTER_TYPE_INFO in_type, out_type;
+    CLSID *clsids, mft_clsid, category;
+    unsigned int i = 0, count;
+    IMFMediaType *input_type;
+    HRESULT hr;
+
+    /* TODO: should we check if the source type is compressed? */
+
+    if (FAILED(hr = IMFMediaType_GetMajorType(output_type, &out_type.guidMajorType)))
+        return hr;
+
+    if (IsEqualGUID(&out_type.guidMajorType, &MFMediaType_Video))
+    {
+        category = MFT_CATEGORY_VIDEO_DECODER;
+    }
+    else if (IsEqualGUID(&out_type.guidMajorType, &MFMediaType_Audio))
+    {
+        category = MFT_CATEGORY_AUDIO_DECODER;
+    }
+    else
+    {
+        WARN("Unhandled major type %s.\n", debugstr_guid(&out_type.guidMajorType));
+        return MF_E_TOPO_CODEC_NOT_FOUND;
+    }
+
+    if (FAILED(hr = IMFMediaType_GetGUID(output_type, &MF_MT_SUBTYPE, &out_type.guidSubtype)))
+        return hr;
+
+    in_type.guidMajorType = out_type.guidMajorType;
+
+    while (source_reader_get_native_media_type(reader, index, i++, &input_type) == S_OK)
+    {
+        if (SUCCEEDED(IMFMediaType_GetGUID(input_type, &MF_MT_SUBTYPE, &in_type.guidSubtype)))
+        {
+            count = 0;
+            if (SUCCEEDED(hr = MFTEnum(category, 0, &in_type, &out_type, NULL, &clsids, &count)) && count)
+            {
+                mft_clsid = clsids[0];
+                CoTaskMemFree(clsids);
+
+                /* TODO: Should we iterate over all of them? */
+                if (SUCCEEDED(source_reader_configure_decoder(reader, index, &mft_clsid, input_type, output_type)))
+                {
+                    IMFMediaType_Release(input_type);
+                    return S_OK;
+                }
+
+            }
+        }
+
+        IMFMediaType_Release(input_type);
+    }
+
+    return MF_E_TOPO_CODEC_NOT_FOUND;
+}
+
 static HRESULT WINAPI src_reader_SetCurrentMediaType(IMFSourceReader *iface, DWORD index, DWORD *reserved,
         IMFMediaType *type)
 {
@@ -774,11 +962,12 @@ static HRESULT WINAPI src_reader_SetCurrentMediaType(IMFSourceReader *iface, DWO
     if (index >= reader->stream_count)
         return MF_E_INVALIDSTREAMNUMBER;
 
-    /* FIXME: validate passed type and current presentation state. */
+    /* FIXME: setting the output type while streaming should trigger a flush */
 
     EnterCriticalSection(&reader->cs);
 
-    hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *)reader->streams[index].current);
+    if ((hr = source_reader_set_compatible_media_type(reader, index, type)) == S_FALSE)
+        hr = source_reader_create_decoder_for_stream(reader, index, type);
 
     LeaveCriticalSection(&reader->cs);
 
-- 
2.25.1




More information about the wine-devel mailing list