이번에 사용하는 방법은 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);
좋은 글을 보다 질문이 있어서 몇자 적어봅니다.
답글삭제주인장님은 yuv->rgb로 변환하여 속도를 개선하였는데, 혹시 YUV420->YUV422변환할때 이런 방식으로 사용해도 속도를 개선할 수 있나요?
이 포스팅은 yuv420->rgb32 변환이 목적이 아니라 directdraw를 이용한 영상출력이 목적입니다. 영상출력이 아닌 변환이 목적이라면 ffmpeg 스케일러만 사용하면 됩니다. 만약 directdraw로 변환하려면 이전 포스팅참조하셔서 yuv420 offscreen 평면 -> yuv422 offscreen 평면 으로 blt 하면됩니다. 참고로 위 두 방법의 성능은 동일합니다.
삭제주인장님의 유용한 포스팅 감사합니다.
답글삭제rgb32->yuv420으로 ffmpeg의 스케일러를 사용하고 있는데, 성능이 너무 느리네요.
이런경우 속도를 개선할수 있는 방법이 있을까요?
ffmpeg 빌드할때 어셈블리가 disable 됐거나 mmx/sse가 disable 된 경우가 아니라면 ffmpeg 스케일러는 최대성능을 냅니다. 성능을 더 올리려면 다른 스케일러를 찾거나 스케일러를 직접 구현해야합니다.
삭제이렇게 빨리 답변을 해주시다니 감사합니다. 컴파일 옵션을 봐야겠네요.
답글삭제좋은하루되세요. ^^
감사합니다...
답글삭제