2
votes

I am trying to understand how to work the rendering for YV12 format. For example, I took a simple sample. See this graph: enter image description here

The webcam creates frames by size 640x480 in RGB24 or MJPEG . After it the LAV decoder transforms the frames to YV12 and sends them to DS renderer (EVR or VMR9).

The decoder changes the frame width (stride) 640 on 1024. Hence, the output size of frame will be 1.5*1024*640=737280. The normal size for YV12 is 1.5*640*480=460800. I know the stride can be more than the width of real frame (https://docs.microsoft.com/en-us/windows/desktop/medfound/image-stride). My first question - why did the renderer select that value (1024) than another? Can I get it programmatically?

When I replace the LAV decoder with my filter for transformation RGB24/YV12 (https://gist.github.com/thedeemon/8052fb98f8ba154510d7), the renderer shows me a shifted image, though all parameters are the same, as for the first graph:

enter image description here

Why? I noted that VIDEOINFOHEADER2 had the set interlacing flag dwInterlaceFlags. Therefore my next question: Do I have to add interlacing into my filter for normal work of renderer?

2

2 Answers

3
votes

My first question - why did the renderer select that value (1024) than another? Can I get it programmatically?

Video renderer is using Direct3D texture as a carrier for the image. When texture is mapped into system memory to enable CPU's write access such extended stride could be applied because of specifics of implementation of video hardware. You get the value 1024 via dynamic media type negotiation as described in Handling Format Changes from the Video Renderer.

Your transformation filter has to handle such updates if you want it to be able to connect to video renderer directly.

You are generally not interested in getting this extended stride value otherwise because the one you get via media type update is the one to be used and you have to accept it.

When I replace the LAV decoder with my filter for transformation RGB24/YV12, the renderer shows me a shifted image, though all parameters are the same, as for the first graph... Why?

Your filter does not handle stride update right.

...I noted that VIDEOINFOHEADER2 had the set interlacing flag dwInterlaceFlags. Therefore my next question: Do I have to add interlacing into my filter for normal work of renderer?

You don't have interlaced video here. The problem is unrelated to interlaced video.

0
votes

My solution:

I must right copy a YV12 frame into a video buffer by its three surfaces: Y = 4x4, U = 1x2, V = 1x2. Here is a code for the frame size 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;
}