Windows uses a smaller alignment than gstreamer for some formats, for example NV12. This means we cannot use MFCalculateImageSize() to get the output sample size. Commit 7b79e3a87b1e switched to calling it instead of GetOutputStreamInfo() to fix some game bugs.
From: Conor McCarthy cmccarthy@codeweavers.com
Windows uses a smaller alignment than gstreamer for some formats, for example NV12. This means we cannot use MFCalculateImageSize() to get the output sample size. Commit 7b79e3a87b1e switched to calling it instead of GetOutputStreamInfo() to fix some game bugs. --- dlls/winegstreamer/video_decoder.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index 4086b586d76..378e51cc8db 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -102,6 +102,7 @@ struct video_decoder wg_transform_t wg_transform; struct wg_transform_attrs wg_transform_attrs; struct wg_sample_queue *wg_sample_queue; + struct wg_format wg_output_format; /* for calculating sample size */
IMFVideoSampleAllocatorEx *allocator; BOOL allocator_initialized; @@ -754,6 +755,13 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF else hr = try_create_wg_transform(decoder, output_type);
+ mf_media_type_to_wg_format(output_type, &decoder->wg_output_format); + if (!decoder->wg_output_format.major_type) + { + FIXME("Failed to get output wg format.\n"); + hr = MF_E_INVALIDMEDIATYPE; + } + IMFMediaType_Release(output_type);
if (FAILED(hr)) @@ -943,8 +951,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, UINT32 sample_size; LONGLONG duration; IMFSample *sample; - UINT64 frame_size, frame_rate; - GUID subtype; + UINT64 frame_rate; DWORD size; HRESULT hr;
@@ -960,12 +967,10 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (!(sample = samples->pSample) && !(decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) return E_INVALIDARG;
- if (FAILED(hr = IMFMediaType_GetGUID(decoder->output_type, &MF_MT_SUBTYPE, &subtype))) - return hr; - if (FAILED(hr = IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_SIZE, &frame_size))) - return hr; - if (FAILED(hr = MFCalculateImageSize(&subtype, frame_size >> 32, (UINT32)frame_size, &sample_size))) - return hr; + /* GetOutputStreamInfo() is bugged for some games, and MFCalculateImageSize() + * for some formats uses a smaller stride alignment than gstreamer does */ + if (!(sample_size = wg_format_get_max_size(&decoder->wg_output_format))) + return E_INVALIDARG;
if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) {
This sound suspicious, we request specific alignment to GStreamer exactly because it otherwise doesn't align planes by default. Either we have the alignment wrong, or perhaps this is fixing the same issue as https://gitlab.winehq.org/wine/wine/-/merge_requests/7946?
Yes, unless there's evidence that native mfplat decoders are returning larger samples than the size they return in GetOutputSizeInfo(), this seems like the wrong way to solve things.
On Thu May 15 04:53:07 2025 +0000, Rémi Bernon wrote:
This sound suspicious, we request specific alignment to GStreamer exactly because it otherwise doesn't align planes by default. Either we have the alignment wrong, or perhaps this is fixing the same issue as https://gitlab.winehq.org/wine/wine/-/merge_requests/7946?
The video is 650x850, and while `MFCalculateImageSize()` returns `828750`, `gst_video_info_from_caps()` fills `src_video_info` in `wg_transform_read_data()` with a size of `831300`. The caps object has no paddings, but the gstreamer source has in `video-info.c` for NV12: ``` info->stride[0] = GST_ROUND_UP_4 (width); ```
On Thu May 15 04:56:25 2025 +0000, Elizabeth Figura wrote:
Yes, unless there's evidence that native mfplat decoders are returning larger samples than the size they return in GetOutputSizeInfo(), this seems like the wrong way to solve things.
The code was changed a while ago to call `MFCalculateImageSize()` instead of `GetOutputSizeInfo()` to fix several games, but I found no info on the exact issue. If `GetOutputSizeInfo()` never returns an exceedingly large size, we could just take the max of the two.
On Thu May 15 04:53:07 2025 +0000, Conor McCarthy wrote:
The video is 650x850, and while `MFCalculateImageSize()` returns `828750`, `gst_video_info_from_caps()` fills `src_video_info` in `wg_transform_read_data()` with a size of `831300`. The caps object has no paddings, but the gstreamer source has in `video-info.c` for NV12:
info->stride[0] = GST_ROUND_UP_4 (width);
Okay, it would be nice to add a test with these sizes to mfplat `image_size_tests` tests.
Then we should return the same kind of buffers as native, so it would be better to fix GStreamer alignment (by requesting a smaller alignment, if possible, similar to how we request a larger alignment with H264) rather than the other way around.
Does this work if you set `output_plane_align = 1` with non-H264 decoders?
A test in `mf/tests/transform.c` with such size would be nice too.
On Thu May 15 10:22:56 2025 +0000, Rémi Bernon wrote:
Okay, it would be nice to add a test with these sizes to mfplat `image_size_tests` tests. Then we should return the same kind of buffers as native, so it would be better to fix GStreamer alignment (by requesting a smaller alignment, if possible, similar to how we request a larger alignment with H264) rather than the other way around. Does this work if you set `output_plane_align = 1` with non-H264 decoders? A test in `mf/tests/transform.c` with such size would be nice too.
Setting `output_plane_align = 1` has no effect on the issue. Calling `gst_video_info_align()` doesn't change the stride; it only sets the padding. The code line I mentioned above is the minimum stride we will get from `gst_video_info_from_caps()`, and I see no way to change the caps to deal with the issue either.