2012년 4월 27일 금요일

ffmpeg 스케일러와 DirectDraw 를 이용한 YUV -> RGB 고속변환 출력 - YUV -> RGB Converting by ffmpeg and DirectDraw

YUV420->RGB32 변환출력의 경우 보통 이전에 포스팅된 YUV->RGB 변환방식을 사용하면된다. YUV420 버퍼를 YUV 평면에 그데로 복사하고 primary 평면에 blt 를 하면 yuv->rgb 변환과 스케일링을 동시에 고속으로 수행할 수 있다.

이번에 사용하는 방법은 YUV420 버퍼를 일반 RGB32 offscreen 평면에 스케일링한 후에  primary 평면에 blt 하는 방법이다.
이 방식은 기존 YUV->RGB 변환 방식과 성능이 동일하게 나오지만 비디오카드의 하드웨어 가속기를 사용하지않기 때문에 다른 프로세스가 하드웨어 가속기를 점유하고 있을경우에도 문제없이 사용할수 있다.

ffmpeg 스케일러 사용법과 DirectDraw 사용방법은 이전 포스팅 참조

< DirectDraw 평면 생성 >
...

ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize         = sizeof( ddsd );
ddsd.dwFlags        = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

ddsd.dwWidth        = targetWidth;
ddsd.dwHeight       = targetHeight;

if ((hr=pDD->CreateSurface(&ddsd, &lpBackBuffer, NULL)) != DD_OK)
{
printf("failed to create back buffer surface (hr:0x%x)\n", hr);
return -1;
}

// 스케일러 생성
sws = sws_getContext(srcWidth, srcHeight, PIX_FMT_YUV420P, targetWidth, targetHeight, PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL);
...

< primary 평면에 출력 >

int VideoDDraw::draw(AVFrame *pPicture, RECT *rectTarget)
...
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
if ((hr=lpBackBuffer->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)) == DD_OK)
{
        // offscreen 평면에 복사 (스케일링과 동시에 복사)
LPBYTE lpSurf = (LPBYTE)ddsd.lpSurface;
LPBYTE ptr = lpSurf;
unsigned char *src_buf[4];
src_buf[0] = pPicture->data[0];
src_buf[1] = pPicture->data[1];
src_buf[2] = pPicture->data[2];
src_buf[3] = NULL;

int src_stride[4];
src_stride[0] = pPicture->linesize[0];
src_stride[1] = pPicture->linesize[1];
src_stride[2] = pPicture->linesize[2];
src_stride[3] = 0;

unsigned char *dst_buf[4];
dst_buf[0] = ptr;
dst_buf[1] = NULL;
dst_buf[2] = NULL;
dst_buf[3] = NULL;
int dst_stride[4];
dst_stride[0] = ddsd.lPitch;
dst_stride[1] = ddsd.lPitch;
dst_stride[2] = ddsd.lPitch;
dst_stride[3] = ddsd.lPitch;

        // 스케일링(변환된 데이터가 들어감)
sws_scale(sws, src_buf, src_stride, 0, h, dst_buf, dst_stride);

lpBackBuffer->Unlock(NULL);
}
...
// primary 평면에 blt
hr = lpPrimary->Blt(rectTarget, lpBackBuffer, &rectSrc, DDBLT_ASYNC, NULL);