[PATCH v2 0/4] MR9633: mfreadwrite: Implement transforms creation for writer.
-- v2: mfreadwrite: Create converter if failed to use a single encoder. mfreadwrite: Implement transforms creation for writer. mfreadwrite: Rename update_media_type_from_upstream and make it non-static. mfreadwrite: Implement stream_create_transforms helper. https://gitlab.winehq.org/wine/wine/-/merge_requests/9633
From: Ziqing Hui <zhui@codeweavers.com> --- dlls/mfreadwrite/writer.c | 82 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/dlls/mfreadwrite/writer.c b/dlls/mfreadwrite/writer.c index e57bf4764ec..0e059288e2b 100644 --- a/dlls/mfreadwrite/writer.c +++ b/dlls/mfreadwrite/writer.c @@ -60,6 +60,7 @@ struct stream IMFStreamSink *stream_sink; IMFTransform *converter, *encoder; MF_SINK_WRITER_STATISTICS stats; + struct sink_writer *writer; struct list queue; /* struct pending_item. */ }; @@ -190,6 +191,81 @@ static HRESULT stream_get_type(struct stream *stream, IMFMediaType **out_type) return hr; } +static HRESULT stream_enumerate_transforms(struct stream *stream, IMFMediaType *input_type, IMFMediaType *output_type, + BOOL use_encoder, IMFAttributes *attributes, IMFActivate ***out_activates, UINT32 *out_count) +{ + UINT32 flags = MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER; + BOOL disable_converter = FALSE, use_hardware_transforms = FALSE; + MFT_REGISTER_TYPE_INFO input_type_info, output_type_info; + GUID category; + HRESULT hr; + + /* Check writer attributes. */ + if (attributes) + { + IMFAttributes_GetUINT32(attributes, + &MF_READWRITE_DISABLE_CONVERTERS, (UINT32 *)&disable_converter); + IMFAttributes_GetUINT32(attributes, + &MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32 *)&use_hardware_transforms); + if (use_hardware_transforms) + flags |= MFT_ENUM_FLAG_HARDWARE; + } + + /* Neither encoder nor converter is allowed, return failure. */ + if (!use_encoder && disable_converter) + return E_FAIL; + + /* Get type infos. */ + if (FAILED(hr = IMFMediaType_GetMajorType(input_type, &input_type_info.guidMajorType)) + || FAILED(hr = IMFMediaType_GetGUID(input_type, &MF_MT_SUBTYPE, &input_type_info.guidSubtype))) + return hr; + if (FAILED(hr = IMFMediaType_GetMajorType(output_type, &output_type_info.guidMajorType)) + || FAILED(hr = IMFMediaType_GetGUID(output_type, &MF_MT_SUBTYPE, &output_type_info.guidSubtype))) + return hr; + + /* Set category according to major type. */ + if (IsEqualGUID(&output_type_info.guidMajorType, &MFMediaType_Video)) + category = use_encoder ? MFT_CATEGORY_VIDEO_ENCODER : MFT_CATEGORY_VIDEO_PROCESSOR; + else if (IsEqualGUID(&output_type_info.guidMajorType, &MFMediaType_Audio)) + category = use_encoder ? MFT_CATEGORY_AUDIO_ENCODER : MFT_CATEGORY_AUDIO_EFFECT; + else + return MF_E_TOPO_CODEC_NOT_FOUND; + + /* Enumerate available transforms. */ + *out_count = 0; + if (FAILED(hr = MFTEnumEx(category, flags, + (disable_converter ? &input_type_info : NULL), &output_type_info, out_activates, out_count))) + return hr; + if (!*out_count) + return MF_E_TOPO_CODEC_NOT_FOUND; + + return hr; +} + +static HRESULT stream_create_transforms(struct stream *stream, + IMFMediaType *input_type, IMFMediaType *output_type, BOOL use_encoder, IMFAttributes *attributes) +{ + IMFActivate **activates; + UINT32 count = 0, i; + HRESULT hr; + + /* Enumerate available transforms. */ + if (FAILED(hr = stream_enumerate_transforms(stream, input_type, output_type, use_encoder, attributes, &activates, &count))) + return hr; + for (i = 0; i < count; i++) + { + /* TODO: Create transform from activates. */ + hr = E_NOTIMPL; + break; + } + + for (i = 0; i < count; ++i) + IMFActivate_Release(activates[i]); + CoTaskMemFree(activates); + + return hr; +} + static struct stream *sink_writer_get_stream(const struct sink_writer *writer, DWORD index) { if (index >= writer->streams.count) @@ -372,9 +448,9 @@ static HRESULT WINAPI sink_writer_SetInputMediaType(IMFSinkWriterEx *iface, DWOR /* Types are not compatible, create transforms. */ if (!(flags & MF_MEDIATYPE_EQUAL_FORMAT_DATA)) { - /* TODO: Try only using converter first, then try again with encoder. */ - FIXME("Not implemented.\n"); - hr = E_NOTIMPL; + /* Try only using converter first, then try again with encoder. */ + if (FAILED(hr = stream_create_transforms(stream, type, stream_type, FALSE, writer->attributes))) + hr = stream_create_transforms(stream, type, stream_type, TRUE, writer->attributes); } done: -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9633
From: Ziqing Hui <zhui@codeweavers.com> We will use this in writer.c, so make it non-static. For writer, the source type is from downstream, so rename this. --- dlls/mfreadwrite/mf_private.h | 1 + dlls/mfreadwrite/reader.c | 59 ++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/dlls/mfreadwrite/mf_private.h b/dlls/mfreadwrite/mf_private.h index d8b44712b4a..77b55813ac9 100644 --- a/dlls/mfreadwrite/mf_private.h +++ b/dlls/mfreadwrite/mf_private.h @@ -22,6 +22,7 @@ extern HRESULT create_sink_writer_from_url(const WCHAR *url, IMFByteStream *stre IMFAttributes *attributes, REFIID riid, void **out); extern HRESULT create_sink_writer_from_sink(IMFMediaSink *sink, IMFAttributes *attributes, REFIID riid, void **out); +extern HRESULT update_media_type(IMFMediaType *dst_type, IMFMediaType *src_type, BOOL advanced); static inline BOOL mf_array_reserve(void **elements, size_t *capacity, size_t count, size_t size) { diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index d7653c2dd14..68b90357928 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -714,41 +714,42 @@ static void media_type_try_copy_attr(IMFMediaType *dst, IMFMediaType *src, const PropVariantClear(&value); } -/* update a media type with additional attributes reported by upstream element */ -/* also present in mf/topology_loader.c pipeline */ -static HRESULT update_media_type_from_upstream(IMFMediaType *media_type, IMFMediaType *upstream_type, BOOL advanced) + +/* Update a media type with additional attributes reported by another media type, */ +/* also present as update_media_type_from_upstream in mf/topology_loader.c pipeline. */ +HRESULT update_media_type(IMFMediaType *dst_type, IMFMediaType *src_type, BOOL advanced) { HRESULT hr = S_OK; /* propagate common video attributes */ - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_FRAME_SIZE, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_FRAME_RATE, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_ROTATION, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_FIXED_SIZE_SAMPLES, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_PIXEL_ASPECT_RATIO, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, &hr); - - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_CHROMA_SITING, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_INTERLACE_MODE, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_TRANSFER_FUNCTION, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_PRIMARIES, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_YUV_MATRIX, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_LIGHTING, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_NOMINAL_RANGE, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_FRAME_SIZE, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_FRAME_RATE, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_VIDEO_ROTATION, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_FIXED_SIZE_SAMPLES, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_PIXEL_ASPECT_RATIO, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, &hr); + + media_type_try_copy_attr(dst_type, src_type, &MF_MT_VIDEO_CHROMA_SITING, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_INTERLACE_MODE, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_TRANSFER_FUNCTION, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_VIDEO_PRIMARIES, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_YUV_MATRIX, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_VIDEO_LIGHTING, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_VIDEO_NOMINAL_RANGE, &hr); if (!advanced) - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_DEFAULT_STRIDE, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_DEFAULT_STRIDE, &hr); /* propagate common audio attributes */ - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_NUM_CHANNELS, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_CHANNEL_MASK, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_SAMPLES_PER_BLOCK, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_VALID_BITS_PER_SAMPLE, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_AUDIO_NUM_CHANNELS, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_AUDIO_CHANNEL_MASK, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_AUDIO_SAMPLES_PER_BLOCK, &hr); + media_type_try_copy_attr(dst_type, src_type, &MF_MT_AUDIO_VALID_BITS_PER_SAMPLE, &hr); return hr; } @@ -2173,7 +2174,7 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, BOOL source_reader_allow_video_processor(reader, &enable_advanced); - if ((SUCCEEDED(hr = update_media_type_from_upstream(output_type, media_type, enable_advanced))) + if ((SUCCEEDED(hr = update_media_type(output_type, media_type, enable_advanced))) && FAILED(hr = IMFTransform_SetOutputType(transform, 0, output_type, 0)) && FAILED(hr = set_matching_transform_output_type(transform, output_type)) && allow_processor && SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type))) @@ -2181,7 +2182,7 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, BOOL struct transform_entry *converter; if (SUCCEEDED(hr = IMFTransform_SetOutputType(transform, 0, media_type, 0)) - && SUCCEEDED(hr = update_media_type_from_upstream(output_type, media_type, enable_advanced)) + && SUCCEEDED(hr = update_media_type(output_type, media_type, enable_advanced)) && (enable_advanced || SUCCEEDED(hr = set_default_video_attributes(reader, output_type))) && SUCCEEDED(hr = source_reader_create_transform(reader, FALSE, FALSE, media_type, output_type, &converter))) list_add_tail(&entry->entry, &converter->entry); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9633
From: Ziqing Hui <zhui@codeweavers.com> --- dlls/mfreadwrite/tests/mfplat.c | 3 --- dlls/mfreadwrite/writer.c | 44 ++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index a33ebd23194..7c256109ec0 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -1772,14 +1772,11 @@ static void test_sink_writer_get_object(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); init_media_type(input_type, video_input_type_nv12_desc, -1); hr = IMFSinkWriter_SetInputMediaType(writer, 0, input_type, NULL); - todo_wine ok(hr == S_OK, "SetInputMediaType returned %#lx.\n", hr); IMFMediaType_Release(input_type); hr = IMFSinkWriterEx_GetTransformForStream(writer_ex, 0, 0, &guid, &transform); - todo_wine ok(hr == S_OK, "GetTransformForStream returned %#lx.\n", hr); - todo_wine ok(IsEqualGUID(&guid, &MFT_CATEGORY_VIDEO_ENCODER), "Unexpected guid %s.\n", debugstr_guid(&guid)); if (hr == S_OK) IMFTransform_Release(transform); diff --git a/dlls/mfreadwrite/writer.c b/dlls/mfreadwrite/writer.c index 0e059288e2b..4172f77ce1c 100644 --- a/dlls/mfreadwrite/writer.c +++ b/dlls/mfreadwrite/writer.c @@ -242,10 +242,46 @@ static HRESULT stream_enumerate_transforms(struct stream *stream, IMFMediaType * return hr; } +static HRESULT stream_set_transform(struct stream *stream, IMFTransform *transform, + IMFMediaType *input_type, IMFMediaType *output_type, BOOL use_encoder) +{ + IMFMediaType *output_current_type; + HRESULT hr; + + /* Set the output type on transform. */ + if (FAILED(hr = IMFTransform_SetOutputType(transform, 0, output_type, 0)) + || FAILED(hr = IMFTransform_GetOutputCurrentType(transform, 0, &output_current_type))) + return hr; + + /* Set the input type on transform. */ + if (FAILED(hr = update_media_type(input_type, output_current_type, FALSE)) + || FAILED(hr = IMFTransform_SetInputType(transform, 0, input_type, 0))) + { + IMFMediaType_Release(output_current_type); + return hr; + } + + /* Set the transform to stream. */ + if (use_encoder) + { + TRACE("Created encoder %p.\n", transform); + stream->encoder = transform; + } + else + { + TRACE("Created converter %p.\n", transform); + stream->converter = transform; + } + + IMFMediaType_Release(output_current_type); + return hr; +} + static HRESULT stream_create_transforms(struct stream *stream, IMFMediaType *input_type, IMFMediaType *output_type, BOOL use_encoder, IMFAttributes *attributes) { IMFActivate **activates; + IMFTransform *transform; UINT32 count = 0, i; HRESULT hr; @@ -254,9 +290,11 @@ static HRESULT stream_create_transforms(struct stream *stream, return hr; for (i = 0; i < count; i++) { - /* TODO: Create transform from activates. */ - hr = E_NOTIMPL; - break; + if (FAILED(hr = IMFActivate_ActivateObject(activates[i], &IID_IMFTransform, (void **)&transform))) + continue; + if (SUCCEEDED(hr = stream_set_transform(stream, transform, input_type, output_type, use_encoder))) + break; + IMFTransform_Release(transform); } for (i = 0; i < count; ++i) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9633
From: Ziqing Hui <zhui@codeweavers.com> --- dlls/mfreadwrite/tests/mfplat.c | 8 -------- dlls/mfreadwrite/writer.c | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index 7c256109ec0..6743347993d 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -1735,29 +1735,23 @@ static void test_sink_writer_get_object(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); init_media_type(input_type, video_input_type_desc, -1); hr = IMFSinkWriter_SetInputMediaType(writer, 0, input_type, NULL); - todo_wine ok(hr == S_OK, "SetInputMediaType returned %#lx.\n", hr); IMFMediaType_Release(input_type); /* Get transform after SetInputMediaType. */ hr = IMFSinkWriter_GetServiceForStream(writer, 0, &GUID_NULL, &IID_IMFTransform, (void **)&transform); - todo_wine ok(hr == S_OK, "GetServiceForStream returned %#lx.\n", hr); if (hr == S_OK) IMFTransform_Release(transform); hr = IMFSinkWriterEx_GetTransformForStream(writer_ex, 0, 0, &guid, &transform); - todo_wine ok(hr == S_OK, "GetTransformForStream returned %#lx.\n", hr); - todo_wine ok(IsEqualGUID(&guid, &MFT_CATEGORY_VIDEO_PROCESSOR), "Unexpected guid %s.\n", debugstr_guid(&guid)); if (hr == S_OK) IMFTransform_Release(transform); hr = IMFSinkWriterEx_GetTransformForStream(writer_ex, 0, 1, &guid, &transform); - todo_wine ok(hr == S_OK, "GetTransformForStream returned %#lx.\n", hr); - todo_wine ok(IsEqualGUID(&guid, &MFT_CATEGORY_VIDEO_ENCODER), "Unexpected guid %s.\n", debugstr_guid(&guid)); if (hr == S_OK) IMFTransform_Release(transform); @@ -1878,7 +1872,6 @@ static void test_sink_writer_add_stream(void) ok(hr == MF_E_INVALIDSTREAMNUMBER, "SetInputMediaType returned %#lx.\n", hr); hr = IMFSinkWriter_SetInputMediaType(writer, 0, input_type, NULL); - todo_wine ok(hr == S_OK, "SetInputMediaType returned %#lx.\n", hr); IMFMediaType_Release(input_type); @@ -1938,7 +1931,6 @@ static void test_sink_writer_sample_process(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); init_media_type(input_type, video_input_type_desc, -1); hr = IMFSinkWriter_SetInputMediaType(writer, 0, input_type, NULL); - todo_wine ok(hr == S_OK, "SetInputMediaType returned %#lx.\n", hr); IMFMediaType_Release(input_type); diff --git a/dlls/mfreadwrite/writer.c b/dlls/mfreadwrite/writer.c index 4172f77ce1c..c0fd537f3e7 100644 --- a/dlls/mfreadwrite/writer.c +++ b/dlls/mfreadwrite/writer.c @@ -280,6 +280,7 @@ static HRESULT stream_set_transform(struct stream *stream, IMFTransform *transfo static HRESULT stream_create_transforms(struct stream *stream, IMFMediaType *input_type, IMFMediaType *output_type, BOOL use_encoder, IMFAttributes *attributes) { + IMFMediaType *encoder_input_type; IMFActivate **activates; IMFTransform *transform; UINT32 count = 0, i; @@ -294,6 +295,22 @@ static HRESULT stream_create_transforms(struct stream *stream, continue; if (SUCCEEDED(hr = stream_set_transform(stream, transform, input_type, output_type, use_encoder))) break; + + /* Failed to use a single encoder, try using an encoder and a converter. */ + if (use_encoder && SUCCEEDED(hr = IMFTransform_GetInputAvailableType(transform, 0, 0, &encoder_input_type))) + { + /* Create the converter with a recursive call. */ + hr = stream_create_transforms(stream, input_type, encoder_input_type, FALSE, attributes); + IMFMediaType_Release(encoder_input_type); + if (SUCCEEDED(hr)) + { + /* Converter is already set in the recursive call, set encoder here. */ + stream->encoder = transform; + TRACE("Created encoder %p.", transform); + break; + } + } + IMFTransform_Release(transform); } @@ -484,6 +501,7 @@ static HRESULT WINAPI sink_writer_SetInputMediaType(IMFSinkWriterEx *iface, DWOR } /* Types are not compatible, create transforms. */ + stream_release_transforms(stream); if (!(flags & MF_MEDIATYPE_EQUAL_FORMAT_DATA)) { /* Try only using converter first, then try again with encoder. */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9633
On Fri Dec 5 08:35:05 2025 +0000, Nikolay Sivov wrote:
We don't need a back-reference like that. User attributes are already referenced, you can use them directly. It is is changed in new version :p
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9633#note_125258
participants (2)
-
Ziqing Hui -
Ziqing Hui (@zhui)