This MR also adds support for the I420 10LE format.
Windows doesn't have support for the I420 10LE format. Instead, a decoding MFT will offer P010 as an output format.
Therefore, we translate GST_VIDEO_FORMAT_I420_10LE to WG_VIDEO_FORMAT_P010_10LE so that Wine also offers P010 as an output.
This fixes playback of videos in VRChat that use I420 10LE as the raw video color format.
-- v3: winedmo: Add support for the P010 format. winegstreamer: Add support for the P010 format. mfplat: Add support for the P010 format. mfplat/tests: Add image size tests for P010.
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mfplat/tests/mfplat.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 6fe3f40cd93..386b2d22144 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -5703,6 +5703,13 @@ image_size_tests[] = { &MFVideoFormat_IYUV, 4, 2, 12, 0, 384, 12, 128 }, { &MFVideoFormat_IYUV, 4, 3, 18, 0, 576, 18, 128 }, { &MFVideoFormat_IYUV, 320, 240, 115200, 0, 138240, 115200, 384 }, + + { &MFVideoFormat_P010, 1, 2, 12, 6, 192, 6, 64 }, + { &MFVideoFormat_P010, 2, 2, 12, 0, 192, 12, 64 }, + { &MFVideoFormat_P010, 2, 4, 24, 0, 384, 24, 64 }, + { &MFVideoFormat_P010, 3, 2, 24, 18, 192, 18, 64 }, + { &MFVideoFormat_P010, 4, 2, 24, 0, 192, 24, 64 }, + { &MFVideoFormat_P010, 320, 240, 230400, 0, 230400, 230400, 640 }, };
static void test_MFCalculateImageSize(void) @@ -5726,6 +5733,7 @@ static void test_MFCalculateImageSize(void) IsEqualGUID(ptr->subtype, &MFVideoFormat_ABGR32);
hr = MFCalculateImageSize(ptr->subtype, ptr->width, ptr->height, &size); + todo_wine_if(i >= 140) ok(hr == S_OK || broken(is_broken && hr == E_INVALIDARG), "%u: failed to calculate image size, hr %#lx.\n", i, hr); if (hr == S_OK) { @@ -5761,6 +5769,7 @@ static void test_MFGetPlaneSize(void)
hr = pMFGetPlaneSize(ptr->subtype->Data1, ptr->width, ptr->height, &size); ok(hr == S_OK, "%u: failed to get plane size, hr %#lx.\n", i, hr); + todo_wine_if(i >= 140) ok(size == plane_size, "%u: unexpected plane size %lu, expected %u. Size %u x %u, format %s.\n", i, size, plane_size, ptr->width, ptr->height, wine_dbgstr_an((char*)&ptr->subtype->Data1, 4)); } @@ -7529,8 +7538,16 @@ static void test_MFCreate2DMediaBuffer(void) winetest_push_context("%u, %u x %u, format %s", i, ptr->width, ptr->height, wine_dbgstr_guid(ptr->subtype));
hr = pMFCreate2DMediaBuffer(ptr->width, ptr->height, ptr->subtype->Data1, FALSE, &buffer); + todo_wine_if(i >= 140) ok(hr == S_OK, "Failed to create a buffer, hr %#lx.\n", hr);
+ if (hr != S_OK) + { + skip("P010 is not implemented in wine.\n"); + winetest_pop_context(); + continue; + } + hr = IMFMediaBuffer_GetMaxLength(buffer, &length); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(length == ptr->max_length, "Unexpected maximum length %lu.\n", length); @@ -7794,8 +7811,15 @@ static void test_MFCreate2DMediaBuffer(void) continue;
hr = pMFCreate2DMediaBuffer(ptr->width, ptr->height, ptr->subtype->Data1, FALSE, &buffer); + todo_wine_if(i >= 140) ok(hr == S_OK, "Failed to create a buffer, hr %#lx.\n", hr);
+ if (hr != S_OK) + { + skip("P010 is not implemented in wine.\n"); + continue; + } + hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer, (void **)&_2dbuffer); ok(hr == S_OK, "Failed to get interface, hr %#lx.\n", hr);
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/mfplat/buffer.c | 5 +++++ dlls/mfplat/mediatype.c | 7 +++++++ dlls/mfplat/tests/mfplat.c | 17 ----------------- 3 files changed, 12 insertions(+), 17 deletions(-)
diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 65fab78d427..6d59245b40c 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -1405,6 +1405,7 @@ static p_copy_image_func get_2d_buffer_copy_func(DWORD fourcc) return copy_image_imc2;
case MAKEFOURCC('N','V','1','2'): + case MAKEFOURCC('P','0','1','0'): return copy_image_nv12;
default: @@ -1454,6 +1455,9 @@ static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bo case MAKEFOURCC('N','V','1','2'): plane_size = stride * height * 3 / 2; break; + case MAKEFOURCC('P','0','1','0'): + plane_size = width * height * 3; + break; default: plane_size = stride * height; } @@ -1492,6 +1496,7 @@ static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bo case MAKEFOURCC('N','V','1','1'): case MAKEFOURCC('I','4','2','0'): case MAKEFOURCC('I','Y','U','V'): + case MAKEFOURCC('P','0','1','0'): max_length = pitch * height * 3 / 2; break; default: diff --git a/dlls/mfplat/mediatype.c b/dlls/mfplat/mediatype.c index 755ef7e2216..10a92ad7464 100644 --- a/dlls/mfplat/mediatype.c +++ b/dlls/mfplat/mediatype.c @@ -2728,6 +2728,7 @@ static struct uncompressed_video_format video_formats[] = { &MFVideoFormat_IYUV, 12, 0, 0, 1, -1 }, { &MFVideoFormat_NV11, 12, 0, 0, 1, -1 }, { &MFVideoFormat_NV12, 12, 0, 0, 1, -1 }, + { &MFVideoFormat_P010, 24, 0, 0, 1, -1 }, { &MFVideoFormat_D16, 16, 3, 0, 0, -1 }, { &MFVideoFormat_L16, 16, 3, 0, 0, -1 }, { &MFVideoFormat_UYVY, 16, 0, 0, 1, -1 }, @@ -2762,6 +2763,7 @@ static struct uncompressed_video_format *mf_get_video_format(const GUID *subtype static unsigned int mf_get_stride_for_format(const struct uncompressed_video_format *format, unsigned int width) { if (format->bpp < 8) return (width * format->bpp) / 8; + else if (IsEqualGUID(format->subtype, &MFVideoFormat_P010)) return width * 2; return (width * (format->bpp / 8) + format->alignment) & ~format->alignment; }
@@ -2831,6 +2833,10 @@ HRESULT WINAPI MFCalculateImageSize(REFGUID subtype, UINT32 width, UINT32 height /* 2 x 2 block, interleaving UV for half the height */ *size = ((width + 1) & ~1) * height * 3 / 2; break; + case MAKEFOURCC('P','0','1','0'): + /* width is rounded up to a multiple of two */ + *size = ((width + 1) & ~1) * height * 3; + break; case MAKEFOURCC('N','V','1','1'): *size = ((width + 3) & ~3) * height * 3 / 2; break; @@ -2875,6 +2881,7 @@ HRESULT WINAPI MFGetPlaneSize(DWORD fourcc, DWORD width, DWORD height, DWORD *si case MAKEFOURCC('I','4','2','0'): case MAKEFOURCC('I','Y','U','V'): case MAKEFOURCC('N','V','1','1'): + case MAKEFOURCC('P','0','1','0'): *size = stride * height * 3 / 2; break; default: diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 386b2d22144..1cdbb6f48ff 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -5733,7 +5733,6 @@ static void test_MFCalculateImageSize(void) IsEqualGUID(ptr->subtype, &MFVideoFormat_ABGR32);
hr = MFCalculateImageSize(ptr->subtype, ptr->width, ptr->height, &size); - todo_wine_if(i >= 140) ok(hr == S_OK || broken(is_broken && hr == E_INVALIDARG), "%u: failed to calculate image size, hr %#lx.\n", i, hr); if (hr == S_OK) { @@ -5769,7 +5768,6 @@ static void test_MFGetPlaneSize(void)
hr = pMFGetPlaneSize(ptr->subtype->Data1, ptr->width, ptr->height, &size); ok(hr == S_OK, "%u: failed to get plane size, hr %#lx.\n", i, hr); - todo_wine_if(i >= 140) ok(size == plane_size, "%u: unexpected plane size %lu, expected %u. Size %u x %u, format %s.\n", i, size, plane_size, ptr->width, ptr->height, wine_dbgstr_an((char*)&ptr->subtype->Data1, 4)); } @@ -7538,16 +7536,8 @@ static void test_MFCreate2DMediaBuffer(void) winetest_push_context("%u, %u x %u, format %s", i, ptr->width, ptr->height, wine_dbgstr_guid(ptr->subtype));
hr = pMFCreate2DMediaBuffer(ptr->width, ptr->height, ptr->subtype->Data1, FALSE, &buffer); - todo_wine_if(i >= 140) ok(hr == S_OK, "Failed to create a buffer, hr %#lx.\n", hr);
- if (hr != S_OK) - { - skip("P010 is not implemented in wine.\n"); - winetest_pop_context(); - continue; - } - hr = IMFMediaBuffer_GetMaxLength(buffer, &length); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(length == ptr->max_length, "Unexpected maximum length %lu.\n", length); @@ -7811,15 +7801,8 @@ static void test_MFCreate2DMediaBuffer(void) continue;
hr = pMFCreate2DMediaBuffer(ptr->width, ptr->height, ptr->subtype->Data1, FALSE, &buffer); - todo_wine_if(i >= 140) ok(hr == S_OK, "Failed to create a buffer, hr %#lx.\n", hr);
- if (hr != S_OK) - { - skip("P010 is not implemented in wine.\n"); - continue; - } - hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer, (void **)&_2dbuffer); ok(hr == S_OK, "Failed to get interface, hr %#lx.\n", hr);
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/winegstreamer/mfplat.c | 1 + dlls/winegstreamer/quartz_parser.c | 1 + dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/video_decoder.c | 1 + dlls/winegstreamer/wg_format.c | 3 +++ dlls/winegstreamer/wg_media_type.c | 1 + 6 files changed, 8 insertions(+)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index fd2c6995d8c..4088a935625 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -183,6 +183,7 @@ video_formats[] = {&MFVideoFormat_I420, WG_VIDEO_FORMAT_I420}, {&MFVideoFormat_IYUV, WG_VIDEO_FORMAT_I420}, {&MFVideoFormat_NV12, WG_VIDEO_FORMAT_NV12}, + {&MFVideoFormat_P010, WG_VIDEO_FORMAT_P010_10LE}, {&MFVideoFormat_UYVY, WG_VIDEO_FORMAT_UYVY}, {&MFVideoFormat_YUY2, WG_VIDEO_FORMAT_YUY2}, {&MFVideoFormat_YV12, WG_VIDEO_FORMAT_YV12}, diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index 19b104d55df..67aaaa3e134 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -488,6 +488,7 @@ static const GUID *wg_video_format_get_mediasubtype(enum wg_video_format format) case WG_VIDEO_FORMAT_AYUV: return &MEDIASUBTYPE_AYUV; case WG_VIDEO_FORMAT_I420: return &MEDIASUBTYPE_I420; case WG_VIDEO_FORMAT_NV12: return &MEDIASUBTYPE_NV12; + case WG_VIDEO_FORMAT_P010_10LE: return &MEDIASUBTYPE_P010; case WG_VIDEO_FORMAT_UYVY: return &MEDIASUBTYPE_UYVY; case WG_VIDEO_FORMAT_YUY2: return &MEDIASUBTYPE_YUY2; case WG_VIDEO_FORMAT_YV12: return &MEDIASUBTYPE_YV12; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 83e38849fa9..179f15f78f7 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -104,6 +104,7 @@ enum wg_video_format WG_VIDEO_FORMAT_AYUV, WG_VIDEO_FORMAT_I420, WG_VIDEO_FORMAT_NV12, + WG_VIDEO_FORMAT_P010_10LE, WG_VIDEO_FORMAT_UYVY, WG_VIDEO_FORMAT_YUY2, WG_VIDEO_FORMAT_YV12, diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index a65f1781f96..bee035f1848 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -53,6 +53,7 @@ static const struct subtype_info subtype_info_list[] = { &MFVideoFormat_UYVY, 16, MAKEFOURCC('U', 'Y', 'V', 'Y') }, { &MFVideoFormat_YVYU, 16, MAKEFOURCC('Y', 'V', 'Y', 'U') }, { &MFVideoFormat_NV11, 12, MAKEFOURCC('N', 'V', '1', '1') }, + { &MFVideoFormat_P010, 24, MAKEFOURCC('P', '0', '1', '0') }, { &MFVideoFormat_RGB8, 8, BI_RGB }, { &MFVideoFormat_RGB555, 16, BI_RGB }, { &MFVideoFormat_RGB565, 16, BI_BITFIELDS }, diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 12fee8c4923..027184e0846 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -153,6 +153,8 @@ static enum wg_video_format wg_video_format_from_gst(GstVideoFormat format) return WG_VIDEO_FORMAT_I420; case GST_VIDEO_FORMAT_NV12: return WG_VIDEO_FORMAT_NV12; + case GST_VIDEO_FORMAT_P010_10LE: + return WG_VIDEO_FORMAT_P010_10LE; case GST_VIDEO_FORMAT_UYVY: return WG_VIDEO_FORMAT_UYVY; case GST_VIDEO_FORMAT_YUY2: @@ -771,6 +773,7 @@ static GstVideoFormat wg_video_format_to_gst(enum wg_video_format format) case WG_VIDEO_FORMAT_YUY2: return GST_VIDEO_FORMAT_YUY2; case WG_VIDEO_FORMAT_YV12: return GST_VIDEO_FORMAT_YV12; case WG_VIDEO_FORMAT_YVYU: return GST_VIDEO_FORMAT_YVYU; + case WG_VIDEO_FORMAT_P010_10LE: return GST_VIDEO_FORMAT_P010_10LE; default: return GST_VIDEO_FORMAT_UNKNOWN; } } diff --git a/dlls/winegstreamer/wg_media_type.c b/dlls/winegstreamer/wg_media_type.c index 9e829f69680..44b39743bc3 100644 --- a/dlls/winegstreamer/wg_media_type.c +++ b/dlls/winegstreamer/wg_media_type.c @@ -653,6 +653,7 @@ static GUID subtype_from_gst_video_format(GstVideoFormat video_format) case GST_VIDEO_FORMAT_AYUV: return MFVideoFormat_AYUV; case GST_VIDEO_FORMAT_I420: return MFVideoFormat_I420; case GST_VIDEO_FORMAT_NV12: return MFVideoFormat_NV12; + case GST_VIDEO_FORMAT_P010_10LE: return MFVideoFormat_P010; case GST_VIDEO_FORMAT_UYVY: return MFVideoFormat_UYVY; case GST_VIDEO_FORMAT_YUY2: return MFVideoFormat_YUY2; case GST_VIDEO_FORMAT_YV12: return MFVideoFormat_YV12;
From: Brendan McGrath bmcgrath@codeweavers.com
--- dlls/winedmo/unix_media_type.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/winedmo/unix_media_type.c b/dlls/winedmo/unix_media_type.c index 28cc27edb03..fac3a82e8c9 100644 --- a/dlls/winedmo/unix_media_type.c +++ b/dlls/winedmo/unix_media_type.c @@ -212,6 +212,7 @@ static GUID subtype_from_pixel_format( enum AVPixelFormat fmt ) case AV_PIX_FMT_BGR24: return MFVideoFormat_RGB24; case AV_PIX_FMT_NV12: return MFVideoFormat_NV12; case AV_PIX_FMT_NV21: return MFVideoFormat_NV21; + case AV_PIX_FMT_P010: return MFVideoFormat_P010; case AV_PIX_FMT_RGB565: return MFVideoFormat_RGB565; case AV_PIX_FMT_RGB555: return MFVideoFormat_RGB555; case AV_PIX_FMT_RGB8: return MFVideoFormat_RGB8;
IMO you could drop the one I420_10LE to P010 mapping hunk for now
OK - I've dropped that one.
will be useful to implement that AV1 decoder
It occurred to me that I had to manually install the [AV1 codec](https://apps.microsoft.com/detail/9mvzqvxjbq9v?hl=en-US&gl=US) on Windows. That being the case, would we implement that decoder in Wine?
This merge request was approved by Elizabeth Figura.
On Mon Jun 2 22:16:15 2025 +0000, Brendan McGrath wrote:
IMO you could drop the one I420_10LE to P010 mapping hunk for now
OK - I've dropped that one.
will be useful to implement that AV1 decoder
It occurred to me that I had to manually install the [AV1 codec](https://apps.microsoft.com/detail/9mvzqvxjbq9v?hl=en-US&gl=US) on Windows. That being the case, would we implement that decoder in Wine?
I think it's still worth implementing if it used by some applications, as it's not possible to use Windows Store apps/extensions in Wine?
This merge request was approved by Rémi Bernon.