922 lines
22 KiB
C++
922 lines
22 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// device.cpp: Manages the Direct3D device
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "MFCaptureD3D.h"
|
|
#include "BufferLock.h"
|
|
|
|
const DWORD NUM_BACK_BUFFERS = 2;
|
|
|
|
void TransformImage_RGB24(
|
|
BYTE* pDest,
|
|
LONG lDestStride,
|
|
const BYTE* pSrc,
|
|
LONG lSrcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
);
|
|
|
|
void TransformImage_RGB32(
|
|
BYTE* pDest,
|
|
LONG lDestStride,
|
|
const BYTE* pSrc,
|
|
LONG lSrcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
);
|
|
|
|
void TransformImage_YUY2(
|
|
BYTE* pDest,
|
|
LONG lDestStride,
|
|
const BYTE* pSrc,
|
|
LONG lSrcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
);
|
|
|
|
void TransformImage_NV12(
|
|
BYTE* pDst,
|
|
LONG dstStride,
|
|
const BYTE* pSrc,
|
|
LONG srcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
);
|
|
|
|
|
|
RECT LetterBoxRect(const RECT& rcSrc, const RECT& rcDst);
|
|
RECT CorrectAspectRatio(const RECT& src, const MFRatio& srcPAR);
|
|
HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride);
|
|
|
|
|
|
inline LONG Width(const RECT& r)
|
|
{
|
|
return r.right - r.left;
|
|
}
|
|
|
|
inline LONG Height(const RECT& r)
|
|
{
|
|
return r.bottom - r.top;
|
|
}
|
|
|
|
|
|
// Static table of output formats and conversion functions.
|
|
struct ConversionFunction
|
|
{
|
|
GUID subtype;
|
|
IMAGE_TRANSFORM_FN xform;
|
|
};
|
|
|
|
|
|
ConversionFunction g_FormatConversions[] =
|
|
{
|
|
{ MFVideoFormat_RGB32, TransformImage_RGB32 },
|
|
{ MFVideoFormat_RGB24, TransformImage_RGB24 },
|
|
{ MFVideoFormat_YUY2, TransformImage_YUY2 },
|
|
{ MFVideoFormat_NV12, TransformImage_NV12 }
|
|
};
|
|
|
|
const DWORD g_cFormats = ARRAYSIZE(g_FormatConversions);
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Constructor
|
|
//-------------------------------------------------------------------
|
|
|
|
DrawDevice::DrawDevice() :
|
|
m_hwnd(NULL),
|
|
m_pD3D(NULL),
|
|
m_pDevice(NULL),
|
|
m_pSwapChain(NULL),
|
|
m_format(D3DFMT_UNKNOWN),
|
|
m_width(0),
|
|
m_height(0),
|
|
m_lDefaultStride(0),
|
|
m_interlace(MFVideoInterlace_Unknown),
|
|
m_convertFn(NULL)
|
|
{
|
|
m_PixelAR.Denominator = m_PixelAR.Numerator = 1;
|
|
|
|
ZeroMemory(&m_d3dpp, sizeof(m_d3dpp));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Destructor
|
|
//-------------------------------------------------------------------
|
|
|
|
DrawDevice::~DrawDevice()
|
|
{
|
|
DestroyDevice();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// GetFormat
|
|
//
|
|
// Get a supported output format by index.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DrawDevice::GetFormat(DWORD index, GUID *pSubtype) const
|
|
{
|
|
if (index < g_cFormats)
|
|
{
|
|
*pSubtype = g_FormatConversions[index].subtype;
|
|
return S_OK;
|
|
}
|
|
return MF_E_NO_MORE_TYPES;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// IsFormatSupported
|
|
//
|
|
// Query if a format is supported.
|
|
//-------------------------------------------------------------------
|
|
|
|
BOOL DrawDevice::IsFormatSupported(REFGUID subtype) const
|
|
{
|
|
for (DWORD i = 0; i < g_cFormats; i++)
|
|
{
|
|
if (subtype == g_FormatConversions[i].subtype)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// CreateDevice
|
|
//
|
|
// Create the Direct3D device.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DrawDevice::CreateDevice(HWND hwnd)
|
|
{
|
|
if (m_pDevice)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// Create the Direct3D object.
|
|
if (m_pD3D == NULL)
|
|
{
|
|
m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
|
|
|
|
if (m_pD3D == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
D3DPRESENT_PARAMETERS pp = { 0 };
|
|
D3DDISPLAYMODE mode = { 0 };
|
|
|
|
hr = m_pD3D->GetAdapterDisplayMode(
|
|
D3DADAPTER_DEFAULT,
|
|
&mode
|
|
);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
hr = m_pD3D->CheckDeviceType(
|
|
D3DADAPTER_DEFAULT,
|
|
D3DDEVTYPE_HAL,
|
|
mode.Format,
|
|
D3DFMT_X8R8G8B8,
|
|
TRUE // windowed
|
|
);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
pp.BackBufferFormat = D3DFMT_X8R8G8B8;
|
|
pp.SwapEffect = D3DSWAPEFFECT_COPY;
|
|
pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
pp.Windowed = TRUE;
|
|
pp.hDeviceWindow = hwnd;
|
|
|
|
hr = m_pD3D->CreateDevice(
|
|
D3DADAPTER_DEFAULT,
|
|
D3DDEVTYPE_HAL,
|
|
hwnd,
|
|
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE,
|
|
&pp,
|
|
&m_pDevice
|
|
);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
m_hwnd = hwnd;
|
|
m_d3dpp = pp;
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// SetConversionFunction
|
|
//
|
|
// Set the conversion function for the specified video format.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DrawDevice::SetConversionFunction(REFGUID subtype)
|
|
{
|
|
m_convertFn = NULL;
|
|
|
|
for (DWORD i = 0; i < g_cFormats; i++)
|
|
{
|
|
if (g_FormatConversions[i].subtype == subtype)
|
|
{
|
|
m_convertFn = g_FormatConversions[i].xform;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return MF_E_INVALIDMEDIATYPE;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// SetVideoType
|
|
//
|
|
// Set the video format.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DrawDevice::SetVideoType(IMFMediaType *pType)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
GUID subtype = { 0 };
|
|
MFRatio PAR = { 0 };
|
|
|
|
// Find the video subtype.
|
|
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
// Choose a conversion function.
|
|
// (This also validates the format type.)
|
|
|
|
hr = SetConversionFunction(subtype);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
//
|
|
// Get some video attributes.
|
|
//
|
|
|
|
// Get the frame size.
|
|
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &m_width, &m_height);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
|
|
// Get the interlace mode. Default: assume progressive.
|
|
m_interlace = (MFVideoInterlaceMode)MFGetAttributeUINT32(
|
|
pType,
|
|
MF_MT_INTERLACE_MODE,
|
|
MFVideoInterlace_Progressive
|
|
);
|
|
|
|
// Get the image stride.
|
|
hr = GetDefaultStride(pType, &m_lDefaultStride);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
// Get the pixel aspect ratio. Default: Assume square pixels (1:1)
|
|
hr = MFGetAttributeRatio(
|
|
pType,
|
|
MF_MT_PIXEL_ASPECT_RATIO,
|
|
(UINT32*)&PAR.Numerator,
|
|
(UINT32*)&PAR.Denominator
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_PixelAR = PAR;
|
|
}
|
|
else
|
|
{
|
|
m_PixelAR.Numerator = m_PixelAR.Denominator = 1;
|
|
}
|
|
|
|
m_format = (D3DFORMAT)subtype.Data1;
|
|
|
|
// Create Direct3D swap chains.
|
|
|
|
hr = CreateSwapChains();
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
|
|
// Update the destination rectangle for the correct
|
|
// aspect ratio.
|
|
|
|
UpdateDestinationRect();
|
|
|
|
done:
|
|
if (FAILED(hr))
|
|
{
|
|
m_format = D3DFMT_UNKNOWN;
|
|
m_convertFn = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// UpdateDestinationRect
|
|
//
|
|
// Update the destination rectangle for the current window size.
|
|
// The destination rectangle is letterboxed to preserve the
|
|
// aspect ratio of the video image.
|
|
//-------------------------------------------------------------------
|
|
|
|
void DrawDevice::UpdateDestinationRect()
|
|
{
|
|
RECT rcClient;
|
|
RECT rcSrc = { 0, 0, m_width, m_height };
|
|
|
|
GetClientRect(m_hwnd, &rcClient);
|
|
|
|
rcSrc = CorrectAspectRatio(rcSrc, m_PixelAR);
|
|
|
|
m_rcDest = LetterBoxRect(rcSrc, rcClient);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// CreateSwapChains
|
|
//
|
|
// Create Direct3D swap chains.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DrawDevice::CreateSwapChains()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
D3DPRESENT_PARAMETERS pp = { 0 };
|
|
|
|
SafeRelease(&m_pSwapChain);
|
|
|
|
pp.BackBufferWidth = m_width;
|
|
pp.BackBufferHeight = m_height;
|
|
pp.Windowed = TRUE;
|
|
pp.SwapEffect = D3DSWAPEFFECT_FLIP;
|
|
pp.hDeviceWindow = m_hwnd;
|
|
pp.BackBufferFormat = D3DFMT_X8R8G8B8;
|
|
pp.Flags =
|
|
D3DPRESENTFLAG_VIDEO | D3DPRESENTFLAG_DEVICECLIP |
|
|
D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
|
|
pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
pp.BackBufferCount = NUM_BACK_BUFFERS;
|
|
|
|
hr = m_pDevice->CreateAdditionalSwapChain(&pp, &m_pSwapChain);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// DrawFrame
|
|
//
|
|
// Draw the video frame.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DrawDevice::DrawFrame(IMFMediaBuffer *pBuffer)
|
|
{
|
|
if (m_convertFn == NULL)
|
|
{
|
|
return MF_E_INVALIDREQUEST;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
BYTE *pbScanline0 = NULL;
|
|
LONG lStride = 0;
|
|
D3DLOCKED_RECT lr;
|
|
|
|
IDirect3DSurface9 *pSurf = NULL;
|
|
IDirect3DSurface9 *pBB = NULL;
|
|
|
|
if (m_pDevice == NULL || m_pSwapChain == NULL)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
VideoBufferLock buffer(pBuffer); // Helper object to lock the video buffer.
|
|
|
|
hr = TestCooperativeLevel();
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
// Lock the video buffer. This method returns a pointer to the first scan
|
|
// line in the image, and the stride in bytes.
|
|
|
|
hr = buffer.LockBuffer(m_lDefaultStride, m_height, &pbScanline0, &lStride);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
// Get the swap-chain surface.
|
|
hr = m_pSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pSurf);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
// Lock the swap-chain surface.
|
|
hr = pSurf->LockRect(&lr, NULL, D3DLOCK_NOSYSLOCK );
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
|
|
// Convert the frame. This also copies it to the Direct3D surface.
|
|
|
|
m_convertFn(
|
|
(BYTE*)lr.pBits,
|
|
lr.Pitch,
|
|
pbScanline0,
|
|
lStride,
|
|
m_width,
|
|
m_height
|
|
);
|
|
|
|
hr = pSurf->UnlockRect();
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
|
|
// Color fill the back buffer.
|
|
hr = m_pDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBB);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
hr = m_pDevice->ColorFill(pBB, NULL, D3DCOLOR_XRGB(0, 0, 0x80));
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
|
|
// Blit the frame.
|
|
|
|
hr = m_pDevice->StretchRect(pSurf, NULL, pBB, &m_rcDest, D3DTEXF_LINEAR);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
|
|
// Present the frame.
|
|
|
|
hr = m_pDevice->Present(NULL, NULL, NULL, NULL);
|
|
|
|
|
|
done:
|
|
SafeRelease(&pBB);
|
|
SafeRelease(&pSurf);
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// TestCooperativeLevel
|
|
//
|
|
// Test the cooperative-level status of the Direct3D device.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DrawDevice::TestCooperativeLevel()
|
|
{
|
|
if (m_pDevice == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Check the current status of D3D9 device.
|
|
hr = m_pDevice->TestCooperativeLevel();
|
|
|
|
switch (hr)
|
|
{
|
|
case D3D_OK:
|
|
break;
|
|
|
|
case D3DERR_DEVICELOST:
|
|
hr = S_OK;
|
|
|
|
case D3DERR_DEVICENOTRESET:
|
|
hr = ResetDevice();
|
|
break;
|
|
|
|
default:
|
|
// Some other failure.
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// ResetDevice
|
|
//
|
|
// Resets the Direct3D device.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DrawDevice::ResetDevice()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pDevice)
|
|
{
|
|
D3DPRESENT_PARAMETERS d3dpp = m_d3dpp;
|
|
|
|
hr = m_pDevice->Reset(&d3dpp);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DestroyDevice();
|
|
}
|
|
}
|
|
|
|
if (m_pDevice == NULL)
|
|
{
|
|
hr = CreateDevice(m_hwnd);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
}
|
|
|
|
if ((m_pSwapChain == NULL) && (m_format != D3DFMT_UNKNOWN))
|
|
{
|
|
hr = CreateSwapChains();
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
UpdateDestinationRect();
|
|
}
|
|
|
|
done:
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// DestroyDevice
|
|
//
|
|
// Release all Direct3D resources.
|
|
//-------------------------------------------------------------------
|
|
|
|
void DrawDevice::DestroyDevice()
|
|
{
|
|
SafeRelease(&m_pSwapChain);
|
|
SafeRelease(&m_pDevice);
|
|
SafeRelease(&m_pD3D);
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
//
|
|
// Conversion functions
|
|
//
|
|
//-------------------------------------------------------------------
|
|
|
|
__forceinline BYTE Clip(int clr)
|
|
{
|
|
return (BYTE)(clr < 0 ? 0 : ( clr > 255 ? 255 : clr ));
|
|
}
|
|
|
|
__forceinline RGBQUAD ConvertYCrCbToRGB(
|
|
int y,
|
|
int cr,
|
|
int cb
|
|
)
|
|
{
|
|
RGBQUAD rgbq;
|
|
|
|
int c = y - 16;
|
|
int d = cb - 128;
|
|
int e = cr - 128;
|
|
|
|
rgbq.rgbRed = Clip(( 298 * c + 409 * e + 128) >> 8);
|
|
rgbq.rgbGreen = Clip(( 298 * c - 100 * d - 208 * e + 128) >> 8);
|
|
rgbq.rgbBlue = Clip(( 298 * c + 516 * d + 128) >> 8);
|
|
|
|
return rgbq;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// TransformImage_RGB24
|
|
//
|
|
// RGB-24 to RGB-32
|
|
//-------------------------------------------------------------------
|
|
|
|
void TransformImage_RGB24(
|
|
BYTE* pDest,
|
|
LONG lDestStride,
|
|
const BYTE* pSrc,
|
|
LONG lSrcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
)
|
|
{
|
|
for (DWORD y = 0; y < dwHeightInPixels; y++)
|
|
{
|
|
RGBTRIPLE *pSrcPel = (RGBTRIPLE*)pSrc;
|
|
DWORD *pDestPel = (DWORD*)pDest;
|
|
|
|
for (DWORD x = 0; x < dwWidthInPixels; x++)
|
|
{
|
|
pDestPel[x] = D3DCOLOR_XRGB(
|
|
pSrcPel[x].rgbtRed,
|
|
pSrcPel[x].rgbtGreen,
|
|
pSrcPel[x].rgbtBlue
|
|
);
|
|
}
|
|
|
|
pSrc += lSrcStride;
|
|
pDest += lDestStride;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// TransformImage_RGB32
|
|
//
|
|
// RGB-32 to RGB-32
|
|
//
|
|
// Note: This function is needed to copy the image from system
|
|
// memory to the Direct3D surface.
|
|
//-------------------------------------------------------------------
|
|
|
|
void TransformImage_RGB32(
|
|
BYTE* pDest,
|
|
LONG lDestStride,
|
|
const BYTE* pSrc,
|
|
LONG lSrcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
)
|
|
{
|
|
MFCopyImage(pDest, lDestStride, pSrc, lSrcStride, dwWidthInPixels * 4, dwHeightInPixels);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// TransformImage_YUY2
|
|
//
|
|
// YUY2 to RGB-32
|
|
//-------------------------------------------------------------------
|
|
|
|
void TransformImage_YUY2(
|
|
BYTE* pDest,
|
|
LONG lDestStride,
|
|
const BYTE* pSrc,
|
|
LONG lSrcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
)
|
|
{
|
|
for (DWORD y = 0; y < dwHeightInPixels; y++)
|
|
{
|
|
RGBQUAD *pDestPel = (RGBQUAD*)pDest;
|
|
WORD *pSrcPel = (WORD*)pSrc;
|
|
|
|
for (DWORD x = 0; x < dwWidthInPixels; x += 2)
|
|
{
|
|
// Byte order is U0 Y0 V0 Y1
|
|
|
|
int y0 = (int)LOBYTE(pSrcPel[x]);
|
|
int u0 = (int)HIBYTE(pSrcPel[x]);
|
|
int y1 = (int)LOBYTE(pSrcPel[x + 1]);
|
|
int v0 = (int)HIBYTE(pSrcPel[x + 1]);
|
|
|
|
pDestPel[x] = ConvertYCrCbToRGB(y0, v0, u0);
|
|
pDestPel[x + 1] = ConvertYCrCbToRGB(y1, v0, u0);
|
|
}
|
|
|
|
pSrc += lSrcStride;
|
|
pDest += lDestStride;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// TransformImage_NV12
|
|
//
|
|
// NV12 to RGB-32
|
|
//-------------------------------------------------------------------
|
|
|
|
void TransformImage_NV12(
|
|
BYTE* pDst,
|
|
LONG dstStride,
|
|
const BYTE* pSrc,
|
|
LONG srcStride,
|
|
DWORD dwWidthInPixels,
|
|
DWORD dwHeightInPixels
|
|
)
|
|
{
|
|
const BYTE* lpBitsY = pSrc;
|
|
const BYTE* lpBitsCb = lpBitsY + (dwHeightInPixels * srcStride);;
|
|
const BYTE* lpBitsCr = lpBitsCb + 1;
|
|
|
|
for (UINT y = 0; y < dwHeightInPixels; y += 2)
|
|
{
|
|
const BYTE* lpLineY1 = lpBitsY;
|
|
const BYTE* lpLineY2 = lpBitsY + srcStride;
|
|
const BYTE* lpLineCr = lpBitsCr;
|
|
const BYTE* lpLineCb = lpBitsCb;
|
|
|
|
LPBYTE lpDibLine1 = pDst;
|
|
LPBYTE lpDibLine2 = pDst + dstStride;
|
|
|
|
for (UINT x = 0; x < dwWidthInPixels; x += 2)
|
|
{
|
|
int y0 = (int)lpLineY1[0];
|
|
int y1 = (int)lpLineY1[1];
|
|
int y2 = (int)lpLineY2[0];
|
|
int y3 = (int)lpLineY2[1];
|
|
int cb = (int)lpLineCb[0];
|
|
int cr = (int)lpLineCr[0];
|
|
|
|
RGBQUAD r = ConvertYCrCbToRGB(y0, cr, cb);
|
|
lpDibLine1[0] = r.rgbBlue;
|
|
lpDibLine1[1] = r.rgbGreen;
|
|
lpDibLine1[2] = r.rgbRed;
|
|
lpDibLine1[3] = 0; // Alpha
|
|
|
|
r = ConvertYCrCbToRGB(y1, cr, cb);
|
|
lpDibLine1[4] = r.rgbBlue;
|
|
lpDibLine1[5] = r.rgbGreen;
|
|
lpDibLine1[6] = r.rgbRed;
|
|
lpDibLine1[7] = 0; // Alpha
|
|
|
|
r = ConvertYCrCbToRGB(y2, cr, cb);
|
|
lpDibLine2[0] = r.rgbBlue;
|
|
lpDibLine2[1] = r.rgbGreen;
|
|
lpDibLine2[2] = r.rgbRed;
|
|
lpDibLine2[3] = 0; // Alpha
|
|
|
|
r = ConvertYCrCbToRGB(y3, cr, cb);
|
|
lpDibLine2[4] = r.rgbBlue;
|
|
lpDibLine2[5] = r.rgbGreen;
|
|
lpDibLine2[6] = r.rgbRed;
|
|
lpDibLine2[7] = 0; // Alpha
|
|
|
|
lpLineY1 += 2;
|
|
lpLineY2 += 2;
|
|
lpLineCr += 2;
|
|
lpLineCb += 2;
|
|
|
|
lpDibLine1 += 8;
|
|
lpDibLine2 += 8;
|
|
}
|
|
|
|
pDst += (2 * dstStride);
|
|
lpBitsY += (2 * srcStride);
|
|
lpBitsCr += srcStride;
|
|
lpBitsCb += srcStride;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// LetterBoxDstRect
|
|
//
|
|
// Takes a src rectangle and constructs the largest possible
|
|
// destination rectangle within the specifed destination rectangle
|
|
// such thatthe video maintains its current shape.
|
|
//
|
|
// This function assumes that pels are the same shape within both the
|
|
// source and destination rectangles.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
|
|
RECT LetterBoxRect(const RECT& rcSrc, const RECT& rcDst)
|
|
{
|
|
// figure out src/dest scale ratios
|
|
int iSrcWidth = Width(rcSrc);
|
|
int iSrcHeight = Height(rcSrc);
|
|
|
|
int iDstWidth = Width(rcDst);
|
|
int iDstHeight = Height(rcDst);
|
|
|
|
int iDstLBWidth;
|
|
int iDstLBHeight;
|
|
|
|
if (MulDiv(iSrcWidth, iDstHeight, iSrcHeight) <= iDstWidth) {
|
|
|
|
// Column letter boxing ("pillar box")
|
|
|
|
iDstLBWidth = MulDiv(iDstHeight, iSrcWidth, iSrcHeight);
|
|
iDstLBHeight = iDstHeight;
|
|
}
|
|
else {
|
|
|
|
// Row letter boxing.
|
|
|
|
iDstLBWidth = iDstWidth;
|
|
iDstLBHeight = MulDiv(iDstWidth, iSrcHeight, iSrcWidth);
|
|
}
|
|
|
|
|
|
// Create a centered rectangle within the current destination rect
|
|
|
|
RECT rc;
|
|
|
|
LONG left = rcDst.left + ((iDstWidth - iDstLBWidth) / 2);
|
|
LONG top = rcDst.top + ((iDstHeight - iDstLBHeight) / 2);
|
|
|
|
SetRect(&rc, left, top, left + iDstLBWidth, top + iDstLBHeight);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CorrectAspectRatio
|
|
//
|
|
// Converts a rectangle from the source's pixel aspect ratio (PAR) to 1:1 PAR.
|
|
// Returns the corrected rectangle.
|
|
//
|
|
// For example, a 720 x 486 rect with a PAR of 9:10, when converted to 1x1 PAR,
|
|
// is stretched to 720 x 540.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
RECT CorrectAspectRatio(const RECT& src, const MFRatio& srcPAR)
|
|
{
|
|
// Start with a rectangle the same size as src, but offset to the origin (0,0).
|
|
RECT rc = {0, 0, src.right - src.left, src.bottom - src.top};
|
|
|
|
if ((srcPAR.Numerator != 1) || (srcPAR.Denominator != 1))
|
|
{
|
|
// Correct for the source's PAR.
|
|
|
|
if (srcPAR.Numerator > srcPAR.Denominator)
|
|
{
|
|
// The source has "wide" pixels, so stretch the width.
|
|
rc.right = MulDiv(rc.right, srcPAR.Numerator, srcPAR.Denominator);
|
|
}
|
|
else if (srcPAR.Numerator < srcPAR.Denominator)
|
|
{
|
|
// The source has "tall" pixels, so stretch the height.
|
|
rc.bottom = MulDiv(rc.bottom, srcPAR.Denominator, srcPAR.Numerator);
|
|
}
|
|
// else: PAR is 1:1, which is a no-op.
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// GetDefaultStride
|
|
//
|
|
// Gets the default stride for a video frame, assuming no extra padding bytes.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride)
|
|
{
|
|
LONG lStride = 0;
|
|
|
|
// Try to get the default stride from the media type.
|
|
HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride);
|
|
if (FAILED(hr))
|
|
{
|
|
// Attribute not set. Try to calculate the default stride.
|
|
GUID subtype = GUID_NULL;
|
|
|
|
UINT32 width = 0;
|
|
UINT32 height = 0;
|
|
|
|
// Get the subtype and the image size.
|
|
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride);
|
|
}
|
|
|
|
// Set the attribute for later reference.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
(void)pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride));
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*plStride = lStride;
|
|
}
|
|
return hr;
|
|
} |