我试图了解如何处理YV12格式的渲染。例如,我采取了一个简单的示例。看到此图:
网络摄像头创建RGBx24或MJPEG尺寸为640x480的帧。之后,LAV解码器将帧转换为YV12,并将其发送到DS渲染器(EVR或VMR9)。
解码器在1024上更改帧宽度(步幅)640。因此,帧的输出大小将为1.5 * 1024 * 640 = 737280。 YV12的正常大小为1.5 * 640 * 480 = 460800。我知道步幅可以大于实际帧的宽度(https://docs.microsoft.com/en-us/windows/desktop/medfound/image-stride)。我的第一个问题-渲染器为什么选择了另一个值(1024)?我可以通过编程方式获得它吗?
当我用用于转换RGB24 / YV12(https://gist.github.com/thedeemon/8052fb98f8ba154510d7)的滤镜替换LAV解码器时,尽管所有参数都与第一张图相同,但渲染器向我显示了偏移的图像:
为什么?我注意到VIDEOINFOHEADER2设置了隔行标记dwInterlaceFlags。因此,我的下一个问题是:为了渲染器的正常工作,是否必须在我的滤镜中添加隔行扫描?
答案 0 :(得分:3)
我的第一个问题-渲染器为什么选择该值(1024)而不是另一个?我可以通过编程方式获得它吗?
视频渲染器正在使用Direct3D纹理作为图像的载体。当纹理映射到系统内存中以启用CPU的写访问权限时,由于视频硬件的实现细节,可以应用这种扩展的跨度。您可以通过Handling Format Changes from the Video Renderer中所述的动态媒体类型协商来获得值1024。
如果您希望转换过滤器能够直接连接到视频渲染器,则必须处理此类更新。
否则,您通常不希望获得此扩展的跨步值,因为通过媒体类型更新获得的是要使用的那个,并且您必须接受它。
当我用用于转换RGB24 / YV12的滤镜替换LAV解码器时,尽管所有参数都与第一张图相同,但渲染器向我显示了偏移图像。 为什么?
您的过滤器无法正确处理大步更新。
...我注意到VIDEOINFOHEADER2具有设置的隔行标记dwInterlaceFlags。因此,我的下一个问题是:为了渲染器的正常工作,是否必须在我的滤镜中添加隔行扫描?
您在这里没有隔行扫描视频。该问题与隔行视频无关。
答案 1 :(得分:0)
我的解决方案:
我必须正确地将YV12帧的三个表面复制到视频缓冲区中:Y = 4x4,U = 1x2,V = 1x2。这是帧尺寸为640x480的代码:
CVideoGrabberFilter::Transform(IMediaSample *pIn, IMediaSample *pOut)
{
BYTE* pSrcBuf = 0;
pIn->GetPointer(&pSrcBuf);
BYTE* pDstBuf = 0;
pOut->GetPointer(&pDstBuf);
SIZE size;
size.cx = 640;
size.cy = 480;
int nLen = pOut->GetActualDataLength();
BYTE* pDstTmp = new BYTE[nLen];
YV12ConverterFromRGB24(pSrcBufEnd, pDstTmp, size.cx, size.cy);
BYTE* pDst = pDstTmp;
int stride = 1024; //the real video stride for 640x480. For other resolutions you need to use pOut->GetMediaType() for the stride defining.
//Y
for (int y = 0; y < size.cy; ++y)
{
memcpy(pDstBuf, pDst, size.cx);
pDst += size.cx;
pDstBuf += stride;
}
stride /= 2;
size.cy /= 2;
size.cx /= 2;
//U and V
for (int y = 0; y < size.cy; y++ )
{
memcpy(pDstBuf, pDst, size.cx );
pDst += size.cx;
pDstBuf += stride;
memcpy(pDstBuf, pDst, size.cx);
pDst += size.cx;
pDstBuf += stride;
}
delete[] pDstTmp;
}