You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EVR与Sink Writer对屏幕捕获图像垂直翻转的不一致问题

问题原因

EVR 和 Sink Writer 对 RGB32 格式视频的像素行扫描方向默认处理逻辑不一致:

  • EVR 渲染时直接遵循缓冲区像素行的存储顺序(自上而下,屏幕顶部对应缓冲区第一行),和屏幕捕获的原始数据逻辑匹配,所以未反转时显示正常。
  • Sink Writer 编码视频时默认遵循传统视频文件的存储规范(自下而上,屏幕顶部对应缓冲区最后一行),会自动将自上而下的缓冲区数据翻转,导致录制视频垂直颠倒。

手动反转图像后,缓冲区数据变为自下而上,Sink Writer 处理逻辑匹配但 EVR 渲染时就会显示翻转。

解决方案

不需要手动修改图像像素数据,通过明确指定媒体类型的扫描方向属性,让两个组件按统一逻辑处理即可,有两种可行方案:

方案一:给两个接收器分别设置适配的媒体类型

  1. 完善基础媒体类型定义,添加MF_MT_DEFAULT_STRIDE属性明确扫描方向:
check = MFExtern.MFCreateMediaType(out var mediaType);
check = mediaType.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video);
check = mediaType.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MFMediaType.RGB32);
check = mediaType.SetUINT32(MFAttributesClsid.MF_MT_INTERLACE_MODE, (uint)MFVideoInterlaceMode.Progressive);
check = mediaType.SetSize(MFAttributesClsid.MF_MT_FRAME_SIZE, (uint)mode.Width, (uint)mode.Height);
check = mediaType.SetRatio(MFAttributesClsid.MF_MT_FRAME_RATE, FrameRateNumerator, FrameRateDenominator);
check = mediaType.SetRatio(MFAttributesClsid.MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
check = mediaType.SetBoolean(MFAttributesClsid.MF_MT_ALL_SAMPLES_INDEPENDENT, true);
// RGB32每个像素占4字节,正步长表示自上而下的扫描顺序
int stride = mode.Width * 4;
check = mediaType.SetINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, stride);
  1. 为 Sink Writer 单独复制并修改媒体类型:
// 复制基础媒体类型,用于Sink Writer
check = MFExtern.MFCreateMediaType(out var sinkWriterMediaType);
check = mediaType.CopyAllItems(sinkWriterMediaType);
// 设置负步长,告诉Sink Writer缓冲区是自上而下的,需要转换为视频规范的自下而上顺序
check = sinkWriterMediaType.SetINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, -stride);
// 使用这个修改后的媒体类型初始化Sink Writer

此时 EVR 用正步长媒体类型正常渲染,Sink Writer 用负步长媒体类型自动适配视频存储规范,录制结果正常。

方案二:统一缓冲区数据顺序

如果不想维护两份媒体类型,可以在将 DirectX9 缓冲区数据复制到IMFSample时,主动翻转像素行顺序,再配合统一的媒体类型设置:

// 假设dxBuffer是DirectX9捕获的缓冲区,sampleBuffer是IMFSample的缓冲区指针
int rowSize = mode.Width * 4;
byte[] dxData = new byte[mode.Height * rowSize];
// 从DX9缓冲区读取原始数据
dxBuffer.Read(dxData, 0, dxData.Length);

// 翻转像素行顺序
byte[] flippedData = new byte[dxData.Length];
for (int y = 0; y < mode.Height; y++)
{
    int srcRowIndex = (mode.Height - 1 - y) * rowSize;
    int destRowIndex = y * rowSize;
    Buffer.BlockCopy(dxData, srcRowIndex, flippedData, destRowIndex, rowSize);
}

// 将翻转后的数据写入IMFSample缓冲区
sampleBuffer.Write(flippedData, 0, flippedData.Length);

// 媒体类型设置负步长,匹配翻转后的数据顺序
check = mediaType.SetINT32(MFAttributesClsid.MF_MT_DEFAULT_STRIDE, -rowSize);

这样处理后,EVR 和 Sink Writer 都会基于统一的数据和媒体类型,正确完成渲染与录制。

内容的提问来源于stack exchange,提问作者Javid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 12:05:33