From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfreadwrite/reader.c | 61 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 32 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index 6e895667ef6..1533693a32f 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -1825,56 +1825,53 @@ static HRESULT source_reader_configure_decoder(struct source_reader *reader, DWO return MF_E_TOPO_CODEC_NOT_FOUND; }
-static HRESULT source_reader_create_decoder_for_stream(struct source_reader *reader, DWORD index, IMFMediaType *output_type) +static HRESULT source_reader_create_transform(struct source_reader *reader, DWORD index, + IMFMediaType *input_type, IMFMediaType *output_type) { MFT_REGISTER_TYPE_INFO in_type, out_type; - CLSID *clsids, mft_clsid, category; - unsigned int i = 0, count; - IMFMediaType *input_type; + GUID *classes, category; HRESULT hr; + UINT count;
- /* TODO: should we check if the source type is compressed? */ - - if (FAILED(hr = IMFMediaType_GetMajorType(output_type, &out_type.guidMajorType))) + if (FAILED(hr = IMFMediaType_GetMajorType(input_type, &in_type.guidMajorType)) + || FAILED(hr = IMFMediaType_GetGUID(input_type, &MF_MT_SUBTYPE, &in_type.guidSubtype))) + return hr; + if (FAILED(hr = IMFMediaType_GetMajorType(output_type, &out_type.guidMajorType)) + || FAILED(hr = IMFMediaType_GetGUID(output_type, &MF_MT_SUBTYPE, &out_type.guidSubtype))) 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 (SUCCEEDED(hr = MFTEnum(category, 0, &in_type, &out_type, NULL, &classes, &count))) + { + /* TODO: Should we iterate over all of them? */ + if (count) + hr = source_reader_configure_decoder(reader, index, &classes[0], input_type, output_type); + else + hr = MF_E_TOPO_CODEC_NOT_FOUND; + CoTaskMemFree(classes); }
- if (FAILED(hr = IMFMediaType_GetGUID(output_type, &MF_MT_SUBTYPE, &out_type.guidSubtype))) - return hr; + return hr; +}
- in_type.guidMajorType = out_type.guidMajorType; +static HRESULT source_reader_create_decoder_for_stream(struct source_reader *reader, DWORD index, IMFMediaType *output_type) +{ + IMFMediaType *input_type; + unsigned int i = 0; + HRESULT hr;
- while (source_reader_get_native_media_type(reader, index, i++, &input_type) == S_OK) + while (SUCCEEDED(hr = source_reader_get_native_media_type(reader, index, i++, &input_type))) { - if (SUCCEEDED(IMFMediaType_GetGUID(input_type, &MF_MT_SUBTYPE, &in_type.guidSubtype))) + if (SUCCEEDED(hr = source_reader_create_transform(reader, index, input_type, output_type))) { - 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 S_OK; }
IMFMediaType_Release(input_type);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfreadwrite/reader.c | 146 ++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 75 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index 1533693a32f..e402d042972 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -678,6 +678,27 @@ static HRESULT source_reader_allocate_stream_sample(MFT_OUTPUT_STREAM_INFO *info return hr; }
+/* update the output type with additional attributes reported by the decoder such as frame size */ +static HRESULT update_output_type_from_decoder(IMFMediaType *output_type, IMFMediaType *decoded_type) +{ + PROPVARIANT value; + UINT32 count; + HRESULT hr; + GUID key; + + hr = IMFMediaType_GetCount(decoded_type, &count); + while (SUCCEEDED(hr) && count--) + { + PropVariantInit(&value); + hr = IMFMediaType_GetItemByIndex(decoded_type, count, &key, &value); + if (SUCCEEDED(hr) && FAILED(IMFMediaType_GetItem(output_type, &key, NULL))) + hr = IMFMediaType_SetItem(output_type, &key, &value); + PropVariantClear(&value); + } + + return hr; +} + static HRESULT source_reader_pull_transform_samples(struct source_reader *reader, struct media_stream *stream, struct transform_entry *entry); static HRESULT source_reader_push_transform_samples(struct source_reader *reader, struct media_stream *stream, @@ -1756,82 +1777,16 @@ static HRESULT source_reader_setup_sample_allocator(struct source_reader *reader return hr; }
-static HRESULT source_reader_configure_decoder(struct source_reader *reader, DWORD index, const CLSID *clsid, - IMFMediaType *input_type, IMFMediaType *output_type) -{ - IMFMediaTypeHandler *type_handler; - unsigned int block_alignment = 0; - IMFTransform *transform = NULL; - IMFMediaType *type = NULL; - GUID major = { 0 }; - 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 %#lx.\n", hr); - return hr; - } - - if (FAILED(hr = IMFTransform_SetInputType(transform, 0, input_type, 0))) - { - WARN("Failed to set decoder input type, hr %#lx.\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 %#lx.\n", hr); - if (SUCCEEDED(IMFMediaType_GetMajorType(type, &major)) && IsEqualGUID(&major, &MFMediaType_Audio)) - IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment); - IMFMediaType_Release(type); - - if (reader->streams[index].decoder.transform) - IMFTransform_Release(reader->streams[index].decoder.transform); - - reader->streams[index].decoder.transform = transform; - reader->streams[index].decoder.min_buffer_size = block_alignment; - - 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_transform(struct source_reader *reader, DWORD index, IMFMediaType *input_type, IMFMediaType *output_type) { + struct media_stream *stream = &reader->streams[index]; + struct transform_entry *entry = &stream->decoder; MFT_REGISTER_TYPE_INFO in_type, out_type; GUID *classes, category; + IMFTransform *transform; + UINT i, count; HRESULT hr; - UINT count;
if (FAILED(hr = IMFMediaType_GetMajorType(input_type, &in_type.guidMajorType)) || FAILED(hr = IMFMediaType_GetGUID(input_type, &MF_MT_SUBTYPE, &in_type.guidSubtype))) @@ -1847,13 +1802,35 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, DWOR else return MF_E_TOPO_CODEC_NOT_FOUND;
+ if (IsEqualGUID(&out_type.guidMajorType, &MFMediaType_Audio)) + IMFMediaType_GetUINT32(output_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, + &entry->min_buffer_size); + if (SUCCEEDED(hr = MFTEnum(category, 0, &in_type, &out_type, NULL, &classes, &count))) { - /* TODO: Should we iterate over all of them? */ - if (count) - hr = source_reader_configure_decoder(reader, index, &classes[0], input_type, output_type); - else - hr = MF_E_TOPO_CODEC_NOT_FOUND; + for (i = 0, hr = MF_E_TOPO_CODEC_NOT_FOUND; i < count; i++) + { + IMFMediaType *media_type; + + if (FAILED(hr = CoCreateInstance(&classes[i], NULL, CLSCTX_INPROC_SERVER, &IID_IMFTransform, (void **)&transform))) + break; + if (SUCCEEDED(hr = IMFTransform_SetInputType(transform, 0, input_type, 0)) + && SUCCEEDED(hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type))) + { + if (SUCCEEDED(hr = update_output_type_from_decoder(output_type, media_type))) + hr = IMFTransform_SetOutputType(transform, 0, output_type, 0); + IMFMediaType_Release(media_type); + + if (SUCCEEDED(hr)) + { + entry->transform = transform; + return S_OK; + } + } + + IMFTransform_Release(transform); + } + CoTaskMemFree(classes); }
@@ -1862,6 +1839,7 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, DWOR
static HRESULT source_reader_create_decoder_for_stream(struct source_reader *reader, DWORD index, IMFMediaType *output_type) { + struct media_stream *stream = &reader->streams[index]; IMFMediaType *input_type; unsigned int i = 0; HRESULT hr; @@ -1870,6 +1848,24 @@ static HRESULT source_reader_create_decoder_for_stream(struct source_reader *rea { if (SUCCEEDED(hr = source_reader_create_transform(reader, index, input_type, output_type))) { + IMFMediaTypeHandler *type_handler; + + if (SUCCEEDED(source_reader_get_source_type_handler(reader, index, &type_handler))) + { + if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, input_type))) + WARN("Failed to set current input media type, hr %#lx\n", hr); + IMFMediaTypeHandler_Release(type_handler); + } + + if (FAILED(hr = IMFTransform_GetOutputCurrentType(stream->decoder.transform, 0, &output_type))) + WARN("Failed to get decoder output media type, hr %#lx\n", hr); + else + { + if (FAILED(hr = IMFMediaType_CopyAllItems(output_type, (IMFAttributes *)stream->current))) + WARN("Failed to set current output media type, hr %#lx\n", hr); + IMFMediaType_Release(output_type); + } + IMFMediaType_Release(input_type); return S_OK; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfreadwrite/reader.c | 133 +++++++++++++++++++++++++++++--------- 1 file changed, 103 insertions(+), 30 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index e402d042972..3ed3dacae90 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -73,6 +73,7 @@ enum media_stream_flags
struct transform_entry { + struct list entry; IMFTransform *transform; unsigned int min_buffer_size; }; @@ -81,7 +82,7 @@ struct media_stream { IMFMediaStream *stream; IMFMediaType *current; - struct transform_entry decoder; + struct list transforms; IMFVideoSampleAllocatorEx *allocator; DWORD id; unsigned int index; @@ -202,6 +203,30 @@ static ULONG source_reader_addref(struct source_reader *reader) return InterlockedIncrement(&reader->refcount); }
+static void transform_entry_destroy(struct transform_entry *entry) +{ + IMFTransform_Release(entry->transform); + free(entry); +} + +static void media_stream_destroy(struct media_stream *stream) +{ + struct transform_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE(entry, next, &stream->transforms, struct transform_entry, entry) + { + list_remove(&entry->entry); + transform_entry_destroy(entry); + } + + if (stream->stream) + IMFMediaStream_Release(stream->stream); + if (stream->current) + IMFMediaType_Release(stream->current); + if (stream->allocator) + IMFVideoSampleAllocatorEx_Release(stream->allocator); +} + static ULONG source_reader_release(struct source_reader *reader) { ULONG refcount = InterlockedDecrement(&reader->refcount); @@ -222,15 +247,7 @@ static ULONG source_reader_release(struct source_reader *reader) for (i = 0; i < reader->stream_count; ++i) { struct media_stream *stream = &reader->streams[i]; - - if (stream->stream) - IMFMediaStream_Release(stream->stream); - if (stream->current) - IMFMediaType_Release(stream->current); - if (stream->decoder.transform) - IMFTransform_Release(stream->decoder.transform); - if (stream->allocator) - IMFVideoSampleAllocatorEx_Release(stream->allocator); + media_stream_destroy(stream); } source_reader_release_responses(reader, NULL); free(reader->streams); @@ -722,10 +739,15 @@ static HRESULT source_reader_push_transform_samples(struct source_reader *reader static HRESULT source_reader_pull_transform_samples(struct source_reader *reader, struct media_stream *stream, struct transform_entry *entry) { - MFT_OUTPUT_STREAM_INFO stream_info = { 0 }; + MFT_OUTPUT_STREAM_INFO stream_info = {0}; + struct transform_entry *next = NULL; + struct list *ptr; DWORD status; HRESULT hr;
+ if ((ptr = list_next(&stream->transforms, &entry->entry))) + next = LIST_ENTRY(ptr, struct transform_entry, entry); + if (FAILED(hr = IMFTransform_GetOutputStreamInfo(entry->transform, 0, &stream_info))) return hr; stream_info.cbSize = max(stream_info.cbSize, entry->min_buffer_size); @@ -738,8 +760,13 @@ static HRESULT source_reader_pull_transform_samples(struct source_reader *reader && FAILED(hr = source_reader_allocate_stream_sample(&stream_info, &out_buffer.pSample))) break;
- if (SUCCEEDED(hr = IMFTransform_ProcessOutput(stream->decoder.transform, 0, 1, &out_buffer, &status))) - hr = source_reader_queue_sample(reader, stream, out_buffer.pSample); + if (SUCCEEDED(hr = IMFTransform_ProcessOutput(entry->transform, 0, 1, &out_buffer, &status))) + { + if (next) + hr = source_reader_push_transform_samples(reader, stream, next, out_buffer.pSample); + else + hr = source_reader_queue_sample(reader, stream, out_buffer.pSample); + }
if (out_buffer.pSample) IMFSample_Release(out_buffer.pSample); @@ -753,38 +780,51 @@ static HRESULT source_reader_pull_transform_samples(struct source_reader *reader static HRESULT source_reader_drain_transform_samples(struct source_reader *reader, struct media_stream *stream, struct transform_entry *entry) { + struct transform_entry *next = NULL; + struct list *ptr; HRESULT hr;
+ if ((ptr = list_next(&stream->transforms, &entry->entry))) + next = LIST_ENTRY(ptr, struct transform_entry, entry); + if (FAILED(hr = IMFTransform_ProcessMessage(entry->transform, MFT_MESSAGE_COMMAND_DRAIN, 0))) WARN("Failed to drain transform %p, hr %#lx\n", entry->transform, hr); if (FAILED(hr = source_reader_pull_transform_samples(reader, stream, entry)) && hr != MF_E_TRANSFORM_NEED_MORE_INPUT) WARN("Failed to pull pending samples, hr %#lx.\n", hr);
- return S_OK; + return next ? source_reader_drain_transform_samples(reader, stream, next) : S_OK; }
static HRESULT source_reader_flush_transform_samples(struct source_reader *reader, struct media_stream *stream, struct transform_entry *entry) { + struct transform_entry *next = NULL; + struct list *ptr; HRESULT hr;
+ if ((ptr = list_next(&stream->transforms, &entry->entry))) + next = LIST_ENTRY(ptr, struct transform_entry, entry); + if (FAILED(hr = IMFTransform_ProcessMessage(entry->transform, MFT_MESSAGE_COMMAND_FLUSH, 0))) WARN("Failed to flush transform %p, hr %#lx\n", entry->transform, hr);
- return S_OK; + return next ? source_reader_flush_transform_samples(reader, stream, next) : S_OK; }
static HRESULT source_reader_process_sample(struct source_reader *reader, struct media_stream *stream, IMFSample *sample) { + struct transform_entry *entry; + struct list *ptr; HRESULT hr;
- if (!stream->decoder.transform) + if (!(ptr = list_head(&stream->transforms))) return source_reader_queue_sample(reader, stream, sample); + entry = LIST_ENTRY(ptr, struct transform_entry, entry);
/* It's assumed that decoder has 1 input and 1 output, both id's are 0. */ - if (SUCCEEDED(hr = source_reader_push_transform_samples(reader, stream, &stream->decoder, sample)) + if (SUCCEEDED(hr = source_reader_push_transform_samples(reader, stream, entry, sample)) || hr == MF_E_TRANSFORM_NEED_MORE_INPUT) hr = stream->requests ? source_reader_request_sample(reader, stream) : S_OK; else @@ -868,12 +908,16 @@ static HRESULT source_reader_media_stream_state_handler(struct source_reader *re switch (event_type) { case MEEndOfStream: + { + struct list *ptr; + stream->state = STREAM_STATE_EOS; stream->flags &= ~STREAM_FLAG_SAMPLE_REQUESTED;
- if (stream->decoder.transform) + if ((ptr = list_head(&stream->transforms))) { - if (FAILED(hr = source_reader_drain_transform_samples(reader, stream, &stream->decoder))) + struct transform_entry *entry = LIST_ENTRY(ptr, struct transform_entry, entry); + if (FAILED(hr = source_reader_drain_transform_samples(reader, stream, entry))) WARN("Failed to drain pending samples, hr %#lx.\n", hr); }
@@ -881,6 +925,7 @@ static HRESULT source_reader_media_stream_state_handler(struct source_reader *re source_reader_queue_response(reader, stream, S_OK, MF_SOURCE_READERF_ENDOFSTREAM, 0, NULL);
break; + } case MEStreamSeeked: case MEStreamStarted: stream->state = STREAM_STATE_READY; @@ -1233,13 +1278,15 @@ static void source_reader_release_responses(struct source_reader *reader, struct static void source_reader_flush_stream(struct source_reader *reader, DWORD stream_index) { struct media_stream *stream = &reader->streams[stream_index]; + struct list *ptr; HRESULT hr;
source_reader_release_responses(reader, stream);
- if (stream->decoder.transform) + if ((ptr = list_head(&stream->transforms))) { - if (FAILED(hr = source_reader_flush_transform_samples(reader, stream, &stream->decoder))) + struct transform_entry *entry = LIST_ENTRY(ptr, struct transform_entry, entry); + if (FAILED(hr = source_reader_flush_transform_samples(reader, stream, entry))) WARN("Failed to drain pending samples, hr %#lx.\n", hr); }
@@ -1674,6 +1721,8 @@ static HRESULT source_reader_get_source_type_handler(struct source_reader *reade
static HRESULT source_reader_set_compatible_media_type(struct source_reader *reader, DWORD index, IMFMediaType *type) { + struct media_stream *stream = &reader->streams[index]; + struct transform_entry *entry, *next; IMFMediaTypeHandler *type_handler; IMFMediaType *native_type; BOOL type_set = FALSE; @@ -1681,7 +1730,7 @@ static HRESULT source_reader_set_compatible_media_type(struct source_reader *rea DWORD flags; HRESULT hr;
- if (FAILED(hr = IMFMediaType_IsEqual(type, reader->streams[index].current, &flags))) + if (FAILED(hr = IMFMediaType_IsEqual(type, stream->current, &flags))) return hr;
if (!(flags & MF_MEDIATYPE_EQUAL_MAJOR_TYPES)) @@ -1691,6 +1740,12 @@ static HRESULT source_reader_set_compatible_media_type(struct source_reader *rea if (flags & MF_MEDIATYPE_EQUAL_FORMAT_DATA) return S_OK;
+ LIST_FOR_EACH_ENTRY_SAFE(entry, next, &stream->transforms, struct transform_entry, entry) + { + list_remove(&entry->entry); + transform_entry_destroy(entry); + } + if (FAILED(hr = source_reader_get_source_type_handler(reader, index, &type_handler))) return hr;
@@ -1701,7 +1756,7 @@ static HRESULT source_reader_set_compatible_media_type(struct source_reader *rea 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_CopyAllItems(native_type, (IMFAttributes *)stream->current); }
IMFMediaType_Release(native_type); @@ -1777,12 +1832,11 @@ static HRESULT source_reader_setup_sample_allocator(struct source_reader *reader return hr; }
-static HRESULT source_reader_create_transform(struct source_reader *reader, DWORD index, - IMFMediaType *input_type, IMFMediaType *output_type) +static HRESULT source_reader_create_transform(struct source_reader *reader, + IMFMediaType *input_type, IMFMediaType *output_type, struct transform_entry **out) { - struct media_stream *stream = &reader->streams[index]; - struct transform_entry *entry = &stream->decoder; MFT_REGISTER_TYPE_INFO in_type, out_type; + struct transform_entry *entry; GUID *classes, category; IMFTransform *transform; UINT i, count; @@ -1802,6 +1856,9 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, DWOR else return MF_E_TOPO_CODEC_NOT_FOUND;
+ if (!(entry = calloc(1, sizeof(*entry)))) + return E_OUTOFMEMORY; + if (IsEqualGUID(&out_type.guidMajorType, &MFMediaType_Audio)) IMFMediaType_GetUINT32(output_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &entry->min_buffer_size); @@ -1824,6 +1881,7 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, DWOR if (SUCCEEDED(hr)) { entry->transform = transform; + *out = entry; return S_OK; } } @@ -1834,6 +1892,7 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, DWOR CoTaskMemFree(classes); }
+ free(entry); return hr; }
@@ -1846,10 +1905,14 @@ static HRESULT source_reader_create_decoder_for_stream(struct source_reader *rea
while (SUCCEEDED(hr = source_reader_get_native_media_type(reader, index, i++, &input_type))) { - if (SUCCEEDED(hr = source_reader_create_transform(reader, index, input_type, output_type))) + struct transform_entry *entry; + + if (SUCCEEDED(hr = source_reader_create_transform(reader, input_type, output_type, &entry))) { IMFMediaTypeHandler *type_handler;
+ list_add_tail(&stream->transforms, &entry->entry); + if (SUCCEEDED(source_reader_get_source_type_handler(reader, index, &type_handler))) { if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, input_type))) @@ -1857,7 +1920,7 @@ static HRESULT source_reader_create_decoder_for_stream(struct source_reader *rea IMFMediaTypeHandler_Release(type_handler); }
- if (FAILED(hr = IMFTransform_GetOutputCurrentType(stream->decoder.transform, 0, &output_type))) + if (FAILED(hr = IMFTransform_GetOutputCurrentType(entry->transform, 0, &output_type))) WARN("Failed to get decoder output media type, hr %#lx\n", hr); else { @@ -2151,6 +2214,7 @@ static HRESULT WINAPI src_reader_GetServiceForStream(IMFSourceReaderEx *iface, D REFIID riid, void **object) { struct source_reader *reader = impl_from_IMFSourceReaderEx(iface); + struct media_stream *stream = &reader->streams[index]; IUnknown *obj = NULL; HRESULT hr = S_OK;
@@ -2173,7 +2237,14 @@ static HRESULT WINAPI src_reader_GetServiceForStream(IMFSourceReaderEx *iface, D hr = MF_E_INVALIDSTREAMNUMBER; else { - obj = (IUnknown *)reader->streams[index].decoder.transform; + struct list *ptr; + + if ((ptr = list_tail(&stream->transforms))) + { + struct transform_entry *entry = LIST_ENTRY(ptr, struct transform_entry, entry); + obj = (IUnknown *)entry->transform; + } + if (!obj) hr = E_NOINTERFACE; } break; @@ -2396,6 +2467,8 @@ static HRESULT create_source_reader_from_source(IMFMediaSource *source, IMFAttri IMFMediaType *src_type; BOOL selected;
+ list_init(&object->streams[i].transforms); + if (FAILED(hr = MFCreateMediaType(&object->streams[i].current))) break;
From: Rémi Bernon rbernon@codeweavers.com
This enables advanced color conversion in all cases, and thus allows NV12 -> RGB32 conversion even when MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING is not set. This should be harmless and makes the code simpler as we can simply append a VideoProcessor transform in all cases.
The tests todos is tweaked to reflect cases where a single processor is used, which outputs slightly different attributes to when it is connected to an upstream decoder. Ultimately we could try to match native here, but it shouldn't matter too much in the meantime. --- dlls/mfreadwrite/reader.c | 92 +++++++++++++++++++++++++-------- dlls/mfreadwrite/tests/mfplat.c | 65 ++++++++++++++--------- 2 files changed, 111 insertions(+), 46 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index 3ed3dacae90..c6d0e3af061 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -76,6 +76,7 @@ struct transform_entry struct list entry; IMFTransform *transform; unsigned int min_buffer_size; + GUID category; };
struct media_stream @@ -84,6 +85,7 @@ struct media_stream IMFMediaType *current; struct list transforms; IMFVideoSampleAllocatorEx *allocator; + IMFTransform *transform_service; DWORD id; unsigned int index; enum media_stream_state state; @@ -1740,6 +1742,11 @@ static HRESULT source_reader_set_compatible_media_type(struct source_reader *rea if (flags & MF_MEDIATYPE_EQUAL_FORMAT_DATA) return S_OK;
+ if (stream->transform_service) + { + IMFTransform_Release(stream->transform_service); + stream->transform_service = NULL; + } LIST_FOR_EACH_ENTRY_SAFE(entry, next, &stream->transforms, struct transform_entry, entry) { list_remove(&entry->entry); @@ -1832,7 +1839,23 @@ static HRESULT source_reader_setup_sample_allocator(struct source_reader *reader return hr; }
-static HRESULT source_reader_create_transform(struct source_reader *reader, +static BOOL source_reader_allow_video_processor(struct source_reader *reader, BOOL *advanced) +{ + UINT32 value; + + *advanced = FALSE; + if (!reader->attributes) + return FALSE; + + if (SUCCEEDED(IMFAttributes_GetUINT32(reader->attributes, &MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, &value))) + *advanced = value; + if (SUCCEEDED(IMFAttributes_GetUINT32(reader->attributes, &MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, &value))) + return value || *advanced; + + return *advanced; +} + +static HRESULT source_reader_create_transform(struct source_reader *reader, BOOL decoder, BOOL allow_processor, IMFMediaType *input_type, IMFMediaType *output_type, struct transform_entry **out) { MFT_REGISTER_TYPE_INFO in_type, out_type; @@ -1850,20 +1873,22 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, return hr;
if (IsEqualGUID(&out_type.guidMajorType, &MFMediaType_Video)) - category = MFT_CATEGORY_VIDEO_DECODER; + category = decoder ? MFT_CATEGORY_VIDEO_DECODER : MFT_CATEGORY_VIDEO_PROCESSOR; else if (IsEqualGUID(&out_type.guidMajorType, &MFMediaType_Audio)) - category = MFT_CATEGORY_AUDIO_DECODER; + category = decoder ? MFT_CATEGORY_AUDIO_DECODER : MFT_CATEGORY_AUDIO_EFFECT; else return MF_E_TOPO_CODEC_NOT_FOUND;
if (!(entry = calloc(1, sizeof(*entry)))) return E_OUTOFMEMORY; + list_init(&entry->entry); + entry->category = category;
if (IsEqualGUID(&out_type.guidMajorType, &MFMediaType_Audio)) IMFMediaType_GetUINT32(output_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &entry->min_buffer_size);
- if (SUCCEEDED(hr = MFTEnum(category, 0, &in_type, &out_type, NULL, &classes, &count))) + if (SUCCEEDED(hr = MFTEnum(category, 0, &in_type, allow_processor ? NULL : &out_type, NULL, &classes, &count))) { for (i = 0, hr = MF_E_TOPO_CODEC_NOT_FOUND; i < count; i++) { @@ -1874,9 +1899,19 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, if (SUCCEEDED(hr = IMFTransform_SetInputType(transform, 0, input_type, 0)) && SUCCEEDED(hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type))) { - if (SUCCEEDED(hr = update_output_type_from_decoder(output_type, media_type))) - hr = IMFTransform_SetOutputType(transform, 0, output_type, 0); - IMFMediaType_Release(media_type); + if (SUCCEEDED(hr = update_output_type_from_decoder(output_type, media_type)) + && FAILED(hr = IMFTransform_SetOutputType(transform, 0, output_type, 0)) && allow_processor + && SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type))) + { + struct transform_entry *converter; + + if (SUCCEEDED(hr = IMFTransform_SetOutputType(transform, 0, media_type, 0)) + && SUCCEEDED(hr = update_output_type_from_decoder(output_type, media_type)) + && SUCCEEDED(hr = source_reader_create_transform(reader, FALSE, FALSE, media_type, output_type, &converter))) + list_add_tail(&entry->entry, &converter->entry); + + IMFMediaType_Release(media_type); + }
if (SUCCEEDED(hr)) { @@ -1898,20 +1933,43 @@ static HRESULT source_reader_create_transform(struct source_reader *reader,
static HRESULT source_reader_create_decoder_for_stream(struct source_reader *reader, DWORD index, IMFMediaType *output_type) { + BOOL enable_advanced, allow_processor; struct media_stream *stream = &reader->streams[index]; IMFMediaType *input_type; unsigned int i = 0; HRESULT hr;
+ allow_processor = source_reader_allow_video_processor(reader, &enable_advanced); + while (SUCCEEDED(hr = source_reader_get_native_media_type(reader, index, i++, &input_type))) { struct transform_entry *entry;
- if (SUCCEEDED(hr = source_reader_create_transform(reader, input_type, output_type, &entry))) + /* first, try to append a single processor, then try again with a decoder and a processor */ + if ((allow_processor && SUCCEEDED(hr = source_reader_create_transform(reader, FALSE, FALSE, input_type, output_type, &entry))) + || SUCCEEDED(hr = source_reader_create_transform(reader, TRUE, allow_processor, input_type, output_type, &entry))) { + struct transform_entry *next = entry; IMFMediaTypeHandler *type_handler; + struct list *ptr;
- list_add_tail(&stream->transforms, &entry->entry); + if ((ptr = list_head(&entry->entry)) && enable_advanced) + { + next = LIST_ENTRY(ptr, struct transform_entry, entry); + stream->transform_service = next->transform; + IMFTransform_AddRef(stream->transform_service); + } + else if (IsEqualGUID(&entry->category, &MFT_CATEGORY_AUDIO_DECODER) + || IsEqualGUID(&entry->category, &MFT_CATEGORY_VIDEO_DECODER) + || enable_advanced) + { + stream->transform_service = entry->transform; + IMFTransform_AddRef(stream->transform_service); + } + + /* move any additional transforms that have been created */ + list_move_head(&stream->transforms, &entry->entry); + list_add_head(&stream->transforms, &entry->entry);
if (SUCCEEDED(source_reader_get_source_type_handler(reader, index, &type_handler))) { @@ -1920,7 +1978,7 @@ static HRESULT source_reader_create_decoder_for_stream(struct source_reader *rea IMFMediaTypeHandler_Release(type_handler); }
- if (FAILED(hr = IMFTransform_GetOutputCurrentType(entry->transform, 0, &output_type))) + if (FAILED(hr = IMFTransform_GetOutputCurrentType(next->transform, 0, &output_type))) WARN("Failed to get decoder output media type, hr %#lx\n", hr); else { @@ -2235,18 +2293,8 @@ static HRESULT WINAPI src_reader_GetServiceForStream(IMFSourceReaderEx *iface, D
if (index >= reader->stream_count) hr = MF_E_INVALIDSTREAMNUMBER; - else - { - struct list *ptr; - - if ((ptr = list_tail(&stream->transforms))) - { - struct transform_entry *entry = LIST_ENTRY(ptr, struct transform_entry, entry); - obj = (IUnknown *)entry->transform; - } - - if (!obj) hr = E_NOINTERFACE; - } + else if (!(obj = (IUnknown *)stream->transform_service)) + hr = E_NOINTERFACE; break; }
diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index 08e19589ad1..91f9641ccfc 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -909,7 +909,7 @@ static void test_source_reader(const char *filename, bool video) hr = IMFMediaType_SetGUID(mediatype, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReader_SetCurrentMediaType(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, mediatype); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFMediaType_Release(mediatype);
if (hr == S_OK) @@ -922,7 +922,7 @@ static void test_source_reader(const char *filename, bool video) ok(IsEqualGUID(&subtype, &MFVideoFormat_RGB32), "Got subtype %s.\n", debugstr_guid(&subtype));
hr = IMFMediaType_GetUINT32(mediatype, &MF_MT_DEFAULT_STRIDE, &stride); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); todo_wine ok(stride == 160 * 4, "Got stride %u.\n", stride);
IMFMediaType_Release(mediatype); @@ -1687,7 +1687,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad static const struct attribute_desc nv12_expect_advanced_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12, .todo_value = TRUE), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), @@ -1721,7 +1721,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad static const struct attribute_desc yuy2_expect_advanced_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2, .todo_value = TRUE), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2), ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), @@ -1738,7 +1738,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad static const struct attribute_desc rgb32_expect_desc[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .todo_value = TRUE), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 384, .todo = TRUE), @@ -1746,15 +1746,24 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad ATTR_UINT32(MF_MT_SAMPLE_SIZE, 36864, .todo = TRUE), {0}, }; - static const struct attribute_desc rgb32_expect_advanced_desc[] = + static const struct attribute_desc rgb32_expect_advanced_desc_todo1[] = { ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), - ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32, .todo_value = TRUE), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1, .todo = TRUE), ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), ATTR_UINT32(MF_MT_INTERLACE_MODE, 2, .todo = TRUE), }; + static const struct attribute_desc rgb32_expect_advanced_desc_todo2[] = + { + ATTR_GUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), + ATTR_GUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), + ATTR_RATIO(MF_MT_FRAME_SIZE, 96, 96), + ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), + ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), + ATTR_UINT32(MF_MT_INTERLACE_MODE, 2, .todo_value = TRUE), + }; IMFStreamDescriptor *video_stream; IMFSourceReaderEx *reader_ex; IMFAttributes *attributes; @@ -1840,16 +1849,21 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad init_media_type(media_type, nv12_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); if (enable_advanced) - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); else + { + todo_wine_if(enable_processing) /* Wine enables advanced video processing in all cases */ ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + } IMFMediaType_Release(media_type);
hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (enable_advanced) check_media_type(media_type, nv12_expect_advanced_desc, -1); - else + else if (!enable_processing) + check_media_type(media_type, rgb32_stream_type_desc, -1); + else if (!winetest_platform_is_wine) /* Wine enables advanced video processing in all cases */ check_media_type(media_type, rgb32_stream_type_desc, -1); IMFMediaType_Release(media_type);
@@ -1858,7 +1872,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad if (!enable_advanced) ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); else - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (hr == S_OK) { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); @@ -1912,7 +1926,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad init_media_type(media_type, rgb32_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); if (enable_processing || enable_advanced) - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); else ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); IMFMediaType_Release(media_type); @@ -1920,7 +1934,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (enable_advanced) - check_media_type(media_type, rgb32_expect_advanced_desc, -1); + check_media_type(media_type, rgb32_expect_advanced_desc_todo1, -1); else if (enable_processing) check_media_type(media_type, rgb32_expect_desc, -1); else @@ -1932,7 +1946,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad if (!enable_advanced) ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); else - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (hr == S_OK) { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); @@ -1954,19 +1968,22 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad init_media_type(media_type, yuy2_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); if (enable_advanced) - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); else + { + todo_wine_if(enable_processing) /* Wine enables advanced video processing in all cases */ ok(hr == MF_E_TOPO_CODEC_NOT_FOUND, "Unexpected hr %#lx.\n", hr); + } IMFMediaType_Release(media_type);
hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (enable_advanced) check_media_type(media_type, yuy2_expect_advanced_desc, -1); - else if (enable_processing) - check_media_type(media_type, rgb32_expect_desc, -1); - else + else if (!enable_processing) check_media_type(media_type, nv12_stream_type_desc, -1); + else if (!winetest_platform_is_wine) /* Wine enables advanced video processing in all cases */ + check_media_type(media_type, rgb32_expect_desc, -1); IMFMediaType_Release(media_type);
/* convert transform is only exposed with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ @@ -1974,7 +1991,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad if (!enable_advanced) ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); else - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (hr == S_OK) { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); @@ -2058,7 +2075,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad init_media_type(media_type, rgb32_stream_type_desc, 2); /* doesn't need the frame size */ hr = IMFSourceReader_SetCurrentMediaType(reader, 0, NULL, media_type); if (enable_processing || enable_advanced) - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); else todo_wine ok(hr == MF_E_INVALIDMEDIATYPE, "Unexpected hr %#lx.\n", hr); IMFMediaType_Release(media_type); @@ -2066,11 +2083,11 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad hr = IMFSourceReader_GetCurrentMediaType(reader, 0, &media_type); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (enable_advanced) - check_media_type(media_type, rgb32_expect_advanced_desc, -1); - else if (enable_processing) - check_media_type(media_type, rgb32_expect_desc, -1); - else + check_media_type(media_type, rgb32_expect_advanced_desc_todo2, -1); + else if (!enable_processing) check_media_type(media_type, h264_stream_type_desc, -1); + else if (!winetest_platform_is_wine) /* Wine enables advanced video processing in all cases */ + check_media_type(media_type, rgb32_expect_desc, -1); IMFMediaType_Release(media_type);
/* the exposed transform is the H264 decoder or the converter with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ @@ -2078,7 +2095,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad if (!enable_processing && !enable_advanced) ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr); else - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (hr == S_OK) { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfreadwrite/reader.c | 43 +++++++++++++++++++++++++++++++-- dlls/mfreadwrite/tests/mfplat.c | 22 +++++++++-------- 2 files changed, 53 insertions(+), 12 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index c6d0e3af061..8af78e89d48 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -2402,9 +2402,48 @@ static HRESULT WINAPI src_reader_RemoveAllTransformsForStream(IMFSourceReaderEx static HRESULT WINAPI src_reader_GetTransformForStream(IMFSourceReaderEx *iface, DWORD stream_index, DWORD transform_index, GUID *category, IMFTransform **transform) { - FIXME("%p, %#lx, %#lx, %p, %p.\n", iface, stream_index, transform_index, category, transform); + struct source_reader *reader = impl_from_IMFSourceReaderEx(iface); + struct media_stream *stream; + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %#lx, %#lx, %p, %p.\n", iface, stream_index, transform_index, category, transform); + + EnterCriticalSection(&reader->cs); + + if (stream_index == MF_SOURCE_READER_FIRST_VIDEO_STREAM) + stream_index = reader->first_video_stream_index; + else if (stream_index == MF_SOURCE_READER_FIRST_AUDIO_STREAM) + stream_index = reader->first_audio_stream_index; + + if (stream_index >= reader->stream_count) + hr = MF_E_INVALIDSTREAMNUMBER; + else + { + struct list *ptr; + + stream = &reader->streams[stream_index]; + ptr = list_head(&stream->transforms); + while (ptr && transform_index) + { + ptr = list_next(&stream->transforms, ptr); + transform_index--; + } + + if (!ptr) + hr = MF_E_INVALIDINDEX; + else + { + struct transform_entry *entry = LIST_ENTRY(ptr, struct transform_entry, entry); + *category = entry->category; + *transform = entry->transform; + IMFTransform_AddRef(*transform); + hr = S_OK; + } + } + + LeaveCriticalSection(&reader->cs); + + return hr; }
static const IMFSourceReaderExVtbl srcreader_vtbl = diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index 91f9641ccfc..f45c32b6b3e 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -2011,9 +2011,10 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 0, &category, &transform); if (!enable_advanced) - todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + todo_wine_if(enable_processing) /* Wine enables advanced video processing in all cases */ + ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); else - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (hr == S_OK) { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); @@ -2030,7 +2031,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad }
hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 1, &category, &transform); - todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); IMFSourceReaderEx_Release(reader_ex);
IMFSourceReader_Release(reader); @@ -2123,9 +2124,9 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 0, &category, &transform); if (!enable_processing && !enable_advanced) - todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); else - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (hr == S_OK) { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); @@ -2144,9 +2145,10 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad /* the video processor can be accessed at index 1 with MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING */ hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 1, &category, &transform); if (!enable_advanced) - todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + todo_wine_if(enable_processing) /* Wine enables advanced video processing in all cases */ + ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); else - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (hr == S_OK) { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); @@ -2163,7 +2165,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad }
hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 2, &category, &transform); - todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); IMFSourceReaderEx_Release(reader_ex);
/* H264 -> NV12 conversion */ @@ -2212,7 +2214,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad hr = IMFSourceReader_QueryInterface(reader, &IID_IMFSourceReaderEx, (void **)&reader_ex); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 0, &category, &transform); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); if (hr == S_OK) { hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type); @@ -2242,7 +2244,7 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad }
hr = IMFSourceReaderEx_GetTransformForStream(reader_ex, 0, 1, &category, &transform); - todo_wine ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#lx.\n", hr); IMFSourceReaderEx_Release(reader_ex);
skip_tests:
Test failures are unrelated (mmdev:render and oleaut32:varformat).
Anything here?
Nikolay Sivov (@nsivov) commented about dlls/mfreadwrite/reader.c:
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 (SUCCEEDED(hr = MFTEnum(category, 0, &in_type, &out_type, NULL, &classes, &count)))
- {
The `count` variable used to be zero-initialized. Could you restore that? Similarly CoTaskMemFree() was only used when count is non-zero. I don't know if we have that covered by MFTEnum() tests, but it's important to use in a compatible way, to be able to test just our mfreadwrite.dll separately, on Windows.
Nikolay Sivov (@nsivov) commented about dlls/mfreadwrite/reader.c:
- UINT32 count;
- HRESULT hr;
- GUID key;
- hr = IMFMediaType_GetCount(decoded_type, &count);
- while (SUCCEEDED(hr) && count--)
- {
PropVariantInit(&value);
hr = IMFMediaType_GetItemByIndex(decoded_type, count, &key, &value);
if (SUCCEEDED(hr) && FAILED(IMFMediaType_GetItem(output_type, &key, NULL)))
hr = IMFMediaType_SetItem(output_type, &key, &value);
PropVariantClear(&value);
- }
- return hr;
+}
Is that exactly what we need, to preserve existing attributes? If not, another option is to copy everything based on Compare() result.
Nikolay Sivov (@nsivov) commented about dlls/mfreadwrite/reader.c:
if (SUCCEEDED(source_reader_get_source_type_handler(reader, index, &type_handler)))
{
if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, input_type)))
WARN("Failed to set current input media type, hr %#lx\n", hr);
IMFMediaTypeHandler_Release(type_handler);
}
if (FAILED(hr = IMFTransform_GetOutputCurrentType(stream->decoder.transform, 0, &output_type)))
WARN("Failed to get decoder output media type, hr %#lx\n", hr);
else
{
if (FAILED(hr = IMFMediaType_CopyAllItems(output_type, (IMFAttributes *)stream->current)))
WARN("Failed to set current output media type, hr %#lx\n", hr);
IMFMediaType_Release(output_type);
}
It's a duplication, not setting output type. I think for this call you don't really need a warning, it would be purely memory management failure, if it fails.
Nikolay Sivov (@nsivov) commented about dlls/mfreadwrite/reader.c:
hr = MF_E_INVALIDSTREAMNUMBER; else {
obj = (IUnknown *)reader->streams[index].decoder.transform;
struct list *ptr;
if ((ptr = list_tail(&stream->transforms)))
{
struct transform_entry *entry = LIST_ENTRY(ptr, struct transform_entry, entry);
obj = (IUnknown *)entry->transform;
}
if (!obj) hr = E_NOINTERFACE; } break;
GetServiceForStream() is supposed to use decoder transform, according to its description, and not the last one added. Is that not the case? To access the whole chain they added GetTransformForStream().
Nikolay Sivov (@nsivov) commented about dlls/mfreadwrite/reader.c:
-static HRESULT source_reader_create_transform(struct source_reader *reader, +static BOOL source_reader_allow_video_processor(struct source_reader *reader, BOOL *advanced) +{
- UINT32 value;
- *advanced = FALSE;
- if (!reader->attributes)
return FALSE;
- if (SUCCEEDED(IMFAttributes_GetUINT32(reader->attributes, &MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, &value)))
*advanced = value;
- if (SUCCEEDED(IMFAttributes_GetUINT32(reader->attributes, &MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, &value)))
return value || *advanced;
- return *advanced;
+}
I think on Windows it's to distinguish between "simpler" color conversion and VideoProcessorMFT. I don't know if we care too much, but it's obviously exposed to applications.
On Tue Feb 27 13:53:03 2024 +0000, Nikolay Sivov wrote:
I think on Windows it's to distinguish between "simpler" color conversion and VideoProcessorMFT. I don't know if we care too much, but it's obviously exposed to applications.
Yes, in this MR I'm handling the two cases a bit differently (wrt GetServiceForStream mostly, hiding the processor transform like it should, when advanced mode isn't enabled), but for simplicity a video processor is used in both cases. This ends up, like the comments in the tests describe, with accepting additional conversions from what we should.
As it would otherwise just fail instead, I suppose it's alright. There's one visible difference though, with IMFSourceReaderEx_GetTransformForStream, which allows to access every transform in the pipeline, and which doesn't hide the video processor transform in the non-advanced case. It's a very tiny corner cases, I hope it'll be alright (and we can probably tweak it later if needed).
On Tue Feb 27 13:53:01 2024 +0000, Nikolay Sivov wrote:
GetServiceForStream() is supposed to use decoder transform, according to its description, and not the last one added. Is that not the case? To access the whole chain they added GetTransformForStream().
The tests show that when both a decoder and video processor are added, the video processor is returned (in the advanced mode) from GetServiceForStream. This is what I implemented.
In this case, applications need to use GetTransformForStream to get the decoder instead.
On Tue Feb 27 13:52:59 2024 +0000, Nikolay Sivov wrote:
Is that exactly what we need, to preserve existing attributes? If not, another option is to copy everything based on Compare() result.
The output type might still contain different attributes, such as different subtype. It's not completely clear how native handles this and there's plenty of possibilities.
The main reason I'm doing this is because the frame size isn't required, and might be provided by the decoder instead. I could do that completion for hand-picked attributes if that sounds better.
This is probably not completely correct, as it propagates every attribute downstream when I believe some get swallowed, but it was also easier. Note that there's the same kind of thing being done in the topology loader now, for mostly the same reasons.