mfreadwrite/reader: Create and append a converter transform.

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.
This commit is contained in:
Rémi Bernon 2024-02-28 16:17:47 +01:00 committed by Alexandre Julliard
parent d6c9ac94d2
commit ea4b9bafb2
2 changed files with 114 additions and 46 deletions

View file

@ -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;
@ -1767,6 +1769,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);
@ -1859,7 +1866,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;
@ -1877,21 +1900,23 @@ 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);
count = 0;
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)))
{
if (!count)
return MF_E_TOPO_CODEC_NOT_FOUND;
@ -1905,9 +1930,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_media_type_from_upstream(output_type, media_type)))
hr = IMFTransform_SetOutputType(transform, 0, output_type, 0);
IMFMediaType_Release(media_type);
if (SUCCEEDED(hr = update_media_type_from_upstream(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_media_type_from_upstream(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))
{
@ -1929,20 +1964,46 @@ 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 list *ptr = list_head(&entry->entry);
struct transform_entry *service = ptr ? LIST_ENTRY(ptr, struct transform_entry, entry) : entry;
IMFMediaTypeHandler *type_handler;
list_add_tail(&stream->transforms, &entry->entry);
if (enable_advanced)
{
/* when advanced video processing is enabled, converters are exposed as stream transform service */
stream->transform_service = service->transform;
IMFTransform_AddRef(stream->transform_service);
}
else
{
/* when advanced video processing is disabled, only decoders are exposed as stream transform service */
if (IsEqualGUID(&entry->category, &MFT_CATEGORY_AUDIO_DECODER)
|| IsEqualGUID(&entry->category, &MFT_CATEGORY_VIDEO_DECODER))
{
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)))
{
@ -1951,7 +2012,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(service->transform, 0, &output_type)))
WARN("Failed to get decoder output media type, hr %#lx\n", hr);
else
{
@ -2265,18 +2326,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;
}

View file

@ -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);