2308 lines
65 KiB
C++
2308 lines
65 KiB
C++
#include "Presenter.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CPresenter class. - Presents samples using DX11.
|
|
//
|
|
// Notes:
|
|
// - Most public methods calls CheckShutdown. This method fails if the presenter was shut down.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//-------------------------------------------------------------------
|
|
// CPresenter constructor.
|
|
//-------------------------------------------------------------------
|
|
|
|
DX11VideoRenderer::CPresenter::CPresenter(void) :
|
|
m_nRefCount(1),
|
|
m_critSec(), // default ctor
|
|
m_IsShutdown(FALSE),
|
|
m_pDXGIFactory2(NULL),
|
|
m_pD3D11Device(NULL),
|
|
m_pD3DImmediateContext(NULL),
|
|
m_pDXGIManager(NULL),
|
|
m_pDXGIOutput1(NULL),
|
|
m_pSampleAllocatorEx(NULL),
|
|
m_pDCompDevice(NULL),
|
|
m_pHwndTarget(NULL),
|
|
m_pRootVisual(NULL),
|
|
m_bSoftwareDXVADeviceInUse(FALSE),
|
|
m_hwndVideo(NULL),
|
|
m_pMonitors(NULL),
|
|
m_lpCurrMon(NULL),
|
|
m_DeviceResetToken(0),
|
|
m_DXSWSwitch(0),
|
|
m_useXVP(1),
|
|
m_useDCompVisual(0),
|
|
m_useDebugLayer(D3D11_CREATE_DEVICE_VIDEO_SUPPORT),
|
|
m_pDX11VideoDevice(NULL),
|
|
m_pVideoProcessorEnum(NULL),
|
|
m_pVideoProcessor(NULL),
|
|
m_pSwapChain1(NULL),
|
|
m_bDeviceChanged(FALSE),
|
|
m_bResize(TRUE),
|
|
m_b3DVideo(FALSE),
|
|
m_bStereoEnabled(FALSE),
|
|
m_vp3DOutput(MFVideo3DSampleFormat_BaseView),
|
|
m_bFullScreenState(FALSE),
|
|
m_bCanProcessNextSample(TRUE),
|
|
m_displayRect(), // default ctor
|
|
m_imageWidthInPixels(0),
|
|
m_imageHeightInPixels(0),
|
|
m_uiRealDisplayWidth(0),
|
|
m_uiRealDisplayHeight(0),
|
|
m_rcSrcApp(), // default ctor
|
|
m_rcDstApp(), // default ctor
|
|
m_pXVP(NULL),
|
|
m_pXVPControl(NULL)
|
|
{
|
|
ZeroMemory(&m_rcSrcApp, sizeof(m_rcSrcApp));
|
|
ZeroMemory(&m_rcDstApp, sizeof(m_rcDstApp));
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// CPresenter destructor.
|
|
//-------------------------------------------------------------------
|
|
|
|
DX11VideoRenderer::CPresenter::~CPresenter(void)
|
|
{
|
|
SafeDelete(m_pMonitors);
|
|
}
|
|
|
|
// IUnknown
|
|
ULONG DX11VideoRenderer::CPresenter::AddRef(void)
|
|
{
|
|
return InterlockedIncrement(&m_nRefCount);
|
|
}
|
|
|
|
// IUnknown
|
|
HRESULT DX11VideoRenderer::CPresenter::QueryInterface(REFIID iid, __RPC__deref_out _Result_nullonfailure_ void** ppv)
|
|
{
|
|
if (!ppv)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
if (iid == IID_IUnknown)
|
|
{
|
|
*ppv = static_cast<IUnknown*>(static_cast<IMFVideoDisplayControl*>(this));
|
|
}
|
|
else if (iid == __uuidof(IMFVideoDisplayControl))
|
|
{
|
|
*ppv = static_cast<IMFVideoDisplayControl*>(this);
|
|
}
|
|
else if (iid == __uuidof(IMFGetService))
|
|
{
|
|
*ppv = static_cast<IMFGetService*>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
// IUnknown
|
|
ULONG DX11VideoRenderer::CPresenter::Release(void)
|
|
{
|
|
ULONG uCount = InterlockedDecrement(&m_nRefCount);
|
|
if (uCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
// For thread safety, return a temporary variable.
|
|
return uCount;
|
|
}
|
|
|
|
// IMFVideoDisplayControl
|
|
HRESULT DX11VideoRenderer::CPresenter::GetFullscreen(__RPC__out BOOL* pfFullscreen)
|
|
{
|
|
CAutoLock lock(&m_critSec);
|
|
|
|
HRESULT hr = CheckShutdown();
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (pfFullscreen == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
*pfFullscreen = m_bFullScreenState;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// IMFVideoDisplayControl
|
|
HRESULT DX11VideoRenderer::CPresenter::SetFullscreen(BOOL fFullscreen)
|
|
{
|
|
CAutoLock lock(&m_critSec);
|
|
|
|
HRESULT hr = CheckShutdown();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_bFullScreenState = fFullscreen;
|
|
|
|
SafeRelease(m_pDX11VideoDevice);
|
|
SafeRelease(m_pVideoProcessorEnum);
|
|
SafeRelease(m_pVideoProcessor);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// IMFVideoDisplayControl
|
|
HRESULT DX11VideoRenderer::CPresenter::SetVideoWindow(__RPC__in HWND hwndVideo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CAutoLock lock(&m_critSec);
|
|
|
|
do
|
|
{
|
|
hr = CheckShutdown();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!IsWindow(hwndVideo))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
m_pMonitors = new CMonitorArray();
|
|
if (!m_pMonitors)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
hr = SetVideoMonitor(hwndVideo);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
CheckDecodeSwitchRegKey();
|
|
|
|
m_hwndVideo = hwndVideo;
|
|
|
|
hr = CreateDXGIManagerAndDevice();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_useXVP)
|
|
{
|
|
hr = CreateXVP();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while(FALSE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Name: GetService
|
|
// Description: IMFGetService
|
|
//-------------------------------------------------------------------------
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::GetService(__RPC__in REFGUID guidService, __RPC__in REFIID riid, __RPC__deref_out_opt LPVOID* ppvObject)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (guidService == MR_VIDEO_ACCELERATION_SERVICE)
|
|
{
|
|
if (riid == __uuidof(IMFDXGIDeviceManager))
|
|
{
|
|
if (NULL != m_pDXGIManager)
|
|
{
|
|
*ppvObject = (void*) static_cast<IUnknown*>(m_pDXGIManager);
|
|
((IUnknown*) *ppvObject)->AddRef();
|
|
}
|
|
else
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
}
|
|
else if (riid == __uuidof(IMFVideoSampleAllocatorEx))
|
|
{
|
|
if (NULL == m_pSampleAllocatorEx)
|
|
{
|
|
hr = MFCreateVideoSampleAllocatorEx(IID_IMFVideoSampleAllocatorEx, (LPVOID*)&m_pSampleAllocatorEx);
|
|
if (SUCCEEDED(hr) && NULL != m_pDXGIManager)
|
|
{
|
|
hr = m_pSampleAllocatorEx->SetDirectXManager(m_pDXGIManager);
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pSampleAllocatorEx->QueryInterface(riid, ppvObject);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
}
|
|
else if (guidService == MR_VIDEO_RENDER_SERVICE)
|
|
{
|
|
hr = QueryInterface(riid, ppvObject);
|
|
}
|
|
else
|
|
{
|
|
hr = MF_E_UNSUPPORTED_SERVICE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL DX11VideoRenderer::CPresenter::CanProcessNextSample(void)
|
|
{
|
|
return m_bCanProcessNextSample;
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::Flush(void)
|
|
{
|
|
CAutoLock lock(&m_critSec);
|
|
|
|
HRESULT hr = CheckShutdown();
|
|
|
|
if (SUCCEEDED(hr) && m_useXVP)
|
|
{
|
|
hr = m_pXVP->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
|
|
}
|
|
|
|
m_bCanProcessNextSample = TRUE;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::GetMonitorRefreshRate(DWORD* pdwRefreshRate)
|
|
{
|
|
if (pdwRefreshRate == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (m_lpCurrMon == NULL)
|
|
{
|
|
return MF_E_INVALIDREQUEST;
|
|
}
|
|
|
|
*pdwRefreshRate = m_lpCurrMon->dwRefreshRate;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::IsMediaTypeSupported(IMFMediaType* pMediaType, DXGI_FORMAT dxgiFormat)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT32 uiNumerator = 30000, uiDenominator = 1001;
|
|
UINT32 uimageWidthInPixels, uimageHeightInPixels = 0;
|
|
|
|
do
|
|
{
|
|
hr = CheckShutdown();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (pMediaType == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
break;
|
|
}
|
|
|
|
if (!m_pDX11VideoDevice)
|
|
{
|
|
hr = m_pD3D11Device->QueryInterface(__uuidof(ID3D11VideoDevice), (void**)&m_pDX11VideoDevice);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &uimageWidthInPixels, &uimageHeightInPixels);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, &uiNumerator, &uiDenominator);
|
|
|
|
//Check if the format is supported
|
|
|
|
D3D11_VIDEO_PROCESSOR_CONTENT_DESC ContentDesc;
|
|
ZeroMemory( &ContentDesc, sizeof( ContentDesc ) );
|
|
ContentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
|
|
ContentDesc.InputWidth = (DWORD)uimageWidthInPixels;
|
|
ContentDesc.InputHeight = (DWORD)uimageHeightInPixels;
|
|
ContentDesc.OutputWidth = (DWORD)uimageWidthInPixels;
|
|
ContentDesc.OutputHeight = (DWORD)uimageHeightInPixels;
|
|
ContentDesc.InputFrameRate.Numerator = uiNumerator;
|
|
ContentDesc.InputFrameRate.Denominator = uiDenominator;
|
|
ContentDesc.OutputFrameRate.Numerator = uiNumerator;
|
|
ContentDesc.OutputFrameRate.Denominator = uiDenominator;
|
|
ContentDesc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
|
|
|
|
SafeRelease(m_pVideoProcessorEnum);
|
|
hr = m_pDX11VideoDevice->CreateVideoProcessorEnumerator(&ContentDesc, &m_pVideoProcessorEnum);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
UINT uiFlags;
|
|
hr = m_pVideoProcessorEnum->CheckVideoProcessorFormat(dxgiFormat, &uiFlags);
|
|
if (FAILED(hr) || 0 == (uiFlags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT))
|
|
{
|
|
hr = MF_E_UNSUPPORTED_D3D_TYPE;
|
|
break;
|
|
}
|
|
|
|
if (m_useXVP)
|
|
{
|
|
hr = m_pXVP->SetInputType(0, pMediaType, MFT_SET_TYPE_TEST_ONLY);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: PresentFrame
|
|
//
|
|
// Synopsis: Present the current outstanding frame in the DX queue
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::PresentFrame(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CAutoLock lock(&m_critSec);
|
|
|
|
do
|
|
{
|
|
hr = CheckShutdown();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (NULL == m_pSwapChain1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
RECT rcDest;
|
|
ZeroMemory(&rcDest, sizeof(rcDest));
|
|
if (CheckEmptyRect(&rcDest))
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
hr = m_pSwapChain1->Present( 0, 0 );
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
m_bCanProcessNextSample = TRUE;
|
|
}
|
|
while (FALSE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: ProcessFrame
|
|
// Description: Present one media sample.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::ProcessFrame(IMFMediaType* pCurrentType, IMFSample* pSample, UINT32* punInterlaceMode, BOOL* pbDeviceChanged, BOOL* pbProcessAgain, IMFSample** ppOutputSample)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BYTE* pData = NULL;
|
|
DWORD dwSampleSize = 0;
|
|
IMFMediaBuffer* pBuffer = NULL;
|
|
IMFMediaBuffer* pEVBuffer = NULL;
|
|
DWORD cBuffers = 0;
|
|
ID3D11Texture2D* pTexture2D = NULL;
|
|
IMFDXGIBuffer* pDXGIBuffer = NULL;
|
|
ID3D11Texture2D* pEVTexture2D = NULL;
|
|
IMFDXGIBuffer* pEVDXGIBuffer = NULL;
|
|
ID3D11Device* pDeviceInput = NULL;
|
|
UINT dwViewIndex = 0;
|
|
UINT dwEVViewIndex = 0;
|
|
|
|
CAutoLock lock(&m_critSec);
|
|
|
|
do
|
|
{
|
|
hr = CheckShutdown();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (punInterlaceMode == NULL || pCurrentType == NULL || pSample == NULL || pbDeviceChanged == NULL || pbProcessAgain == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
break;
|
|
}
|
|
|
|
*pbProcessAgain = FALSE;
|
|
*pbDeviceChanged = FALSE;
|
|
|
|
hr = pSample->GetBufferCount( &cBuffers );
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (1 == cBuffers)
|
|
{
|
|
hr = pSample->GetBufferByIndex(0, &pBuffer);
|
|
}
|
|
else if (2 == cBuffers && m_b3DVideo && 0 != m_vp3DOutput)
|
|
{
|
|
hr = pSample->GetBufferByIndex(0, &pBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pSample->GetBufferByIndex(1, &pEVBuffer);
|
|
}
|
|
else
|
|
{
|
|
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = CheckDeviceState(pbDeviceChanged);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
RECT rcDest;
|
|
ZeroMemory(&rcDest, sizeof(rcDest));
|
|
if (CheckEmptyRect(&rcDest))
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
MFVideoInterlaceMode unInterlaceMode = (MFVideoInterlaceMode) MFGetAttributeUINT32( pCurrentType, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive );
|
|
|
|
//
|
|
// Check the per-sample attributes
|
|
//
|
|
if (MFVideoInterlace_MixedInterlaceOrProgressive == unInterlaceMode)
|
|
{
|
|
BOOL fInterlaced = MFGetAttributeUINT32( pSample, MFSampleExtension_Interlaced, FALSE );
|
|
if ( !fInterlaced )
|
|
{
|
|
// Progressive sample
|
|
*punInterlaceMode = MFVideoInterlace_Progressive;
|
|
}
|
|
else
|
|
{
|
|
BOOL fBottomFirst = MFGetAttributeUINT32( pSample, MFSampleExtension_BottomFieldFirst, FALSE );
|
|
if ( fBottomFirst )
|
|
{
|
|
*punInterlaceMode = MFVideoInterlace_FieldInterleavedLowerFirst;
|
|
}
|
|
else
|
|
{
|
|
*punInterlaceMode = MFVideoInterlace_FieldInterleavedUpperFirst;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = pBuffer->QueryInterface(__uuidof(IMFDXGIBuffer), (LPVOID*)&pDXGIBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pDXGIBuffer->GetResource(__uuidof(ID3D11Texture2D), (LPVOID*)&pTexture2D);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pDXGIBuffer->GetSubresourceIndex(&dwViewIndex);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_b3DVideo && 0 != m_vp3DOutput)
|
|
{
|
|
if (pEVBuffer && MFVideo3DSampleFormat_MultiView == m_vp3DOutput)
|
|
{
|
|
hr = pEVBuffer->QueryInterface(__uuidof(IMFDXGIBuffer), (LPVOID*)&pEVDXGIBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pEVDXGIBuffer->GetResource(__uuidof(ID3D11Texture2D), (LPVOID*)&pEVTexture2D);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pEVDXGIBuffer->GetSubresourceIndex(&dwEVViewIndex);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pTexture2D->GetDevice(&pDeviceInput);
|
|
if ((NULL == pDeviceInput) || (pDeviceInput != m_pD3D11Device))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_useXVP)
|
|
{
|
|
BOOL bInputFrameUsed = FALSE;
|
|
|
|
hr = ProcessFrameUsingXVP( pCurrentType, pSample, pTexture2D, rcDest, ppOutputSample, &bInputFrameUsed );
|
|
|
|
if (SUCCEEDED(hr) && !bInputFrameUsed)
|
|
{
|
|
*pbProcessAgain = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = ProcessFrameUsingD3D11( pTexture2D, pEVTexture2D, dwViewIndex, dwEVViewIndex, rcDest, *punInterlaceMode, ppOutputSample );
|
|
|
|
LONGLONG hnsDuration = 0;
|
|
LONGLONG hnsTime = 0;
|
|
DWORD dwSampleFlags = 0;
|
|
|
|
if (ppOutputSample != NULL && *ppOutputSample != NULL)
|
|
{
|
|
if (SUCCEEDED(pSample->GetSampleDuration(&hnsDuration)))
|
|
{
|
|
(*ppOutputSample)->SetSampleDuration(hnsDuration);
|
|
}
|
|
|
|
if (SUCCEEDED(pSample->GetSampleTime(&hnsTime)))
|
|
{
|
|
(*ppOutputSample)->SetSampleTime(hnsTime);
|
|
}
|
|
|
|
if (SUCCEEDED(pSample->GetSampleFlags(&dwSampleFlags)))
|
|
{
|
|
(*ppOutputSample)->SetSampleFlags(dwSampleFlags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
SafeRelease(pTexture2D);
|
|
SafeRelease(pDXGIBuffer);
|
|
SafeRelease(pEVTexture2D);
|
|
SafeRelease(pEVDXGIBuffer);
|
|
SafeRelease(pDeviceInput);
|
|
SafeRelease(pBuffer);
|
|
SafeRelease(pEVBuffer);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::SetCurrentMediaType(IMFMediaType* pMediaType)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMFAttributes* pAttributes = NULL;
|
|
|
|
CAutoLock lock(&m_critSec);
|
|
|
|
do
|
|
{
|
|
hr = CheckShutdown();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pMediaType->QueryInterface(IID_IMFAttributes, reinterpret_cast<void**>(&pAttributes));
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
HRESULT hr1 = pAttributes->GetUINT32(MF_MT_VIDEO_3D, (UINT32*)&m_b3DVideo);
|
|
if (SUCCEEDED(hr1))
|
|
{
|
|
hr = pAttributes->GetUINT32(MF_MT_VIDEO_3D_FORMAT, (UINT32*)&m_vp3DOutput);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Now Determine Correct Display Resolution
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
UINT32 parX = 0, parY = 0;
|
|
int PARWidth = 0, PARHeight = 0;
|
|
MFVideoArea videoArea = {0};
|
|
ZeroMemory(&m_displayRect, sizeof(RECT));
|
|
|
|
if (FAILED(MFGetAttributeSize(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, &parX, &parY)))
|
|
{
|
|
parX = 1;
|
|
parY = 1;
|
|
}
|
|
|
|
hr = GetVideoDisplayArea(pMediaType, &videoArea);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
m_displayRect = MFVideoAreaToRect(videoArea);
|
|
|
|
PixelAspectToPictureAspect(
|
|
videoArea.Area.cx,
|
|
videoArea.Area.cy,
|
|
parX,
|
|
parY,
|
|
&PARWidth,
|
|
&PARHeight);
|
|
|
|
SIZE szVideo = videoArea.Area;
|
|
SIZE szPARVideo = {PARWidth, PARHeight};
|
|
AspectRatioCorrectSize(&szVideo, szPARVideo, videoArea.Area, FALSE);
|
|
m_uiRealDisplayWidth = szVideo.cx;
|
|
m_uiRealDisplayHeight = szVideo.cy;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && m_useXVP)
|
|
{
|
|
// set the input type on the XVP
|
|
hr = m_pXVP->SetInputType(0, pMediaType, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
SafeRelease(pAttributes);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: Shutdown
|
|
// Description: Releases resources held by the presenter.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::Shutdown(void)
|
|
{
|
|
CAutoLock lock(&m_critSec);
|
|
|
|
HRESULT hr = MF_E_SHUTDOWN;
|
|
|
|
m_IsShutdown = TRUE;
|
|
|
|
SafeRelease(m_pDXGIManager);
|
|
SafeRelease(m_pDXGIFactory2);
|
|
SafeRelease(m_pD3D11Device);
|
|
SafeRelease(m_pD3DImmediateContext);
|
|
SafeRelease(m_pDXGIOutput1);
|
|
SafeRelease(m_pSampleAllocatorEx);
|
|
SafeRelease(m_pDCompDevice);
|
|
SafeRelease(m_pHwndTarget);
|
|
SafeRelease(m_pRootVisual);
|
|
SafeRelease(m_pXVPControl);
|
|
SafeRelease(m_pXVP);
|
|
SafeRelease(m_pDX11VideoDevice);
|
|
SafeRelease(m_pVideoProcessor);
|
|
SafeRelease(m_pVideoProcessorEnum);
|
|
SafeRelease(m_pSwapChain1);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/// Private methods
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: AspectRatioCorrectSize
|
|
//
|
|
// Synopsis: Corrects the supplied size structure so that it becomes the same shape
|
|
// as the specified aspect ratio, the correction is always applied in the
|
|
// horizontal axis
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DX11VideoRenderer::CPresenter::AspectRatioCorrectSize(
|
|
LPSIZE lpSizeImage, // size to be aspect ratio corrected
|
|
const SIZE& sizeAr, // aspect ratio of image
|
|
const SIZE& sizeOrig, // original image size
|
|
BOOL ScaleXorY // axis to correct in
|
|
)
|
|
{
|
|
int cxAR = sizeAr.cx;
|
|
int cyAR = sizeAr.cy;
|
|
int cxOr = sizeOrig.cx;
|
|
int cyOr = sizeOrig.cy;
|
|
int sx = lpSizeImage->cx;
|
|
int sy = lpSizeImage->cy;
|
|
|
|
// MulDiv rounds correctly.
|
|
lpSizeImage->cx = MulDiv((sx * cyOr), cxAR, (cyAR * cxOr));
|
|
|
|
if (ScaleXorY && lpSizeImage->cx < cxOr)
|
|
{
|
|
lpSizeImage->cx = cxOr;
|
|
lpSizeImage->cy = MulDiv((sy * cxOr), cyAR, (cxAR * cyOr));
|
|
}
|
|
}
|
|
|
|
void DX11VideoRenderer::CPresenter::CheckDecodeSwitchRegKey(void)
|
|
{
|
|
const TCHAR* lpcszDXSW = TEXT("DXSWSwitch");
|
|
const TCHAR* lpcszInVP = TEXT("XVP");
|
|
const TCHAR* lpcszDComp = TEXT("DComp");
|
|
const TCHAR* lpcszDebugLayer = TEXT("Dbglayer");
|
|
const TCHAR* lpcszREGKEY = TEXT("SOFTWARE\\Microsoft\\Scrunch\\CodecPack\\MSDVD");
|
|
HKEY hk = NULL;
|
|
DWORD dwData;
|
|
DWORD cbData = sizeof(DWORD);
|
|
DWORD cbType;
|
|
|
|
if(0 == RegOpenKeyEx(HKEY_CURRENT_USER, lpcszREGKEY, 0, KEY_READ, &hk))
|
|
{
|
|
if (0 == RegQueryValueEx(hk, lpcszDXSW, 0, &cbType, (LPBYTE)&dwData, &cbData))
|
|
{
|
|
m_DXSWSwitch = dwData;
|
|
}
|
|
|
|
dwData = 0;
|
|
cbData = sizeof(DWORD);
|
|
if (0 == RegQueryValueEx(hk, lpcszInVP, 0, &cbType, (LPBYTE)&dwData, &cbData))
|
|
{
|
|
m_useXVP = dwData;
|
|
}
|
|
|
|
dwData = 0;
|
|
cbData = sizeof(DWORD);
|
|
if (0 == RegQueryValueEx(hk, lpcszDComp, 0, &cbType, (LPBYTE)&dwData, &cbData))
|
|
{
|
|
m_useDCompVisual = dwData;
|
|
}
|
|
|
|
dwData = 0;
|
|
cbData = sizeof(DWORD);
|
|
if (0 == RegQueryValueEx(hk, lpcszDebugLayer, 0, &cbType, (LPBYTE)&dwData, &cbData))
|
|
{
|
|
m_useDebugLayer = dwData;
|
|
}
|
|
}
|
|
|
|
if(NULL != hk)
|
|
{
|
|
RegCloseKey(hk);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::CheckDeviceState(BOOL* pbDeviceChanged)
|
|
{
|
|
if (pbDeviceChanged == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
static int deviceStateChecks = 0;
|
|
static D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_HARDWARE;
|
|
|
|
HRESULT hr = SetVideoMonitor(m_hwndVideo);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (m_pD3D11Device != NULL)
|
|
{
|
|
// Lost/hung device. Destroy the device and create a new one.
|
|
if (S_FALSE == hr || (m_DXSWSwitch > 0 && deviceStateChecks == m_DXSWSwitch))
|
|
{
|
|
if (m_DXSWSwitch > 0 && deviceStateChecks == m_DXSWSwitch)
|
|
{
|
|
(driverType == D3D_DRIVER_TYPE_HARDWARE) ? driverType = D3D_DRIVER_TYPE_WARP : driverType = D3D_DRIVER_TYPE_HARDWARE;
|
|
}
|
|
|
|
hr = CreateDXGIManagerAndDevice(driverType);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
*pbDeviceChanged = TRUE;
|
|
|
|
SafeRelease(m_pDX11VideoDevice);
|
|
SafeRelease(m_pVideoProcessorEnum);
|
|
SafeRelease(m_pVideoProcessor);
|
|
SafeRelease(m_pSwapChain1);
|
|
|
|
deviceStateChecks = 0;
|
|
}
|
|
deviceStateChecks++;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL DX11VideoRenderer::CPresenter::CheckEmptyRect(RECT* pDst)
|
|
{
|
|
GetClientRect(m_hwndVideo, pDst);
|
|
|
|
return IsRectEmpty(pDst);
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::CheckShutdown(void) const
|
|
{
|
|
if (m_IsShutdown)
|
|
{
|
|
return MF_E_SHUTDOWN;
|
|
}
|
|
else
|
|
{
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::CreateDCompDeviceAndVisual(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IDXGIDevice* pDXGIDevice = NULL;
|
|
|
|
do
|
|
{
|
|
hr = m_pD3D11Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&pDXGIDevice));
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = DCompositionCreateDevice(pDXGIDevice, __uuidof(IDCompositionDevice), reinterpret_cast<void**>(&m_pDCompDevice));
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = m_pDCompDevice->CreateTargetForHwnd(m_hwndVideo, TRUE, &m_pHwndTarget);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = m_pDCompDevice->CreateVisual(reinterpret_cast<IDCompositionVisual**>(&m_pRootVisual));
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = m_pHwndTarget->SetRoot(m_pRootVisual);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
while(FALSE);
|
|
|
|
SafeRelease(pDXGIDevice);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: CreateDXGIManagerAndDevice
|
|
// Description: Creates D3D11 device and manager.
|
|
//
|
|
// Note: This method is called once when SetVideoWindow is called using
|
|
// IDX11VideoRenderer.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::CreateDXGIManagerAndDevice(D3D_DRIVER_TYPE DriverType)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
IDXGIAdapter* pTempAdapter = NULL;
|
|
ID3D10Multithread* pMultiThread = NULL;
|
|
IDXGIDevice1* pDXGIDev = NULL;
|
|
IDXGIAdapter1* pAdapter = NULL;
|
|
IDXGIOutput* pDXGIOutput = NULL;
|
|
|
|
D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 };
|
|
D3D_FEATURE_LEVEL featureLevel;
|
|
UINT resetToken;
|
|
|
|
do
|
|
{
|
|
SafeRelease(m_pD3D11Device);
|
|
if (D3D_DRIVER_TYPE_WARP == DriverType)
|
|
{
|
|
ID3D11Device* pD3D11Device = NULL;
|
|
|
|
hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, m_useDebugLayer, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &pD3D11Device, &featureLevel, NULL);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pD3D11Device = new CPrivate_ID3D11Device(pD3D11Device);
|
|
if (NULL == m_pD3D11Device)
|
|
{
|
|
E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
// SafeRelease(pD3D11Device);
|
|
}
|
|
else
|
|
{
|
|
for (DWORD dwCount = 0; dwCount < ARRAYSIZE(featureLevels); dwCount++)
|
|
{
|
|
hr = D3D11CreateDevice(NULL, DriverType, NULL, m_useDebugLayer, &featureLevels[dwCount], 1, D3D11_SDK_VERSION, &m_pD3D11Device, &featureLevel, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ID3D11VideoDevice* pDX11VideoDevice = NULL;
|
|
hr = m_pD3D11Device->QueryInterface(__uuidof(ID3D11VideoDevice), (void**)&pDX11VideoDevice);
|
|
SafeRelease(pDX11VideoDevice);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
break;
|
|
}
|
|
SafeRelease(m_pD3D11Device);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (NULL == m_pDXGIManager)
|
|
{
|
|
hr = MFCreateDXGIDeviceManager(&resetToken, &m_pDXGIManager);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
m_DeviceResetToken = resetToken;
|
|
}
|
|
|
|
hr = m_pDXGIManager->ResetDevice(m_pD3D11Device, m_DeviceResetToken);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
SafeRelease(m_pD3DImmediateContext);
|
|
m_pD3D11Device->GetImmediateContext(&m_pD3DImmediateContext);
|
|
|
|
// Need to explitly set the multithreaded mode for this device
|
|
hr = m_pD3DImmediateContext->QueryInterface(__uuidof(ID3D10Multithread), (void**)&pMultiThread);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
pMultiThread->SetMultithreadProtected(TRUE);
|
|
|
|
hr = m_pD3D11Device->QueryInterface( __uuidof( IDXGIDevice1 ), ( LPVOID* )&pDXGIDev );
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pDXGIDev->GetAdapter(&pTempAdapter);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pTempAdapter->QueryInterface( __uuidof( IDXGIAdapter1 ), (LPVOID*) &pAdapter ) ;
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
SafeRelease(m_pDXGIFactory2);
|
|
hr = pAdapter->GetParent( __uuidof( IDXGIFactory2 ), (LPVOID*) &m_pDXGIFactory2 );
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pAdapter->EnumOutputs(0, &pDXGIOutput);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
SafeRelease(m_pDXGIOutput1);
|
|
hr = pDXGIOutput->QueryInterface( __uuidof(IDXGIOutput1), (LPVOID*) &m_pDXGIOutput1 );
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_useDCompVisual)
|
|
{
|
|
hr = CreateDCompDeviceAndVisual();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while(FALSE);
|
|
|
|
SafeRelease(pTempAdapter);
|
|
SafeRelease(pMultiThread);
|
|
SafeRelease(pDXGIDev);
|
|
SafeRelease(pAdapter);
|
|
SafeRelease(pDXGIOutput);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: CreateXVP
|
|
// Description: Creates a new instance of the XVP MFT.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::CreateXVP(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMFAttributes* pAttributes = NULL;
|
|
|
|
do
|
|
{
|
|
hr = CoCreateInstance(CLSID_VideoProcessorMFT, nullptr, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&m_pXVP);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = m_pXVP->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, ULONG_PTR(m_pDXGIManager));
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Tell the XVP that we are the swapchain allocator
|
|
hr = m_pXVP->GetAttributes(&pAttributes);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pAttributes->SetUINT32(MF_XVP_PLAYBACK_MODE, TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = m_pXVP->QueryInterface(IID_PPV_ARGS(&m_pXVPControl));
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
SafeRelease(pAttributes);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: FindBOBProcessorIndex
|
|
//
|
|
// Synopsis: Find the BOB video processor. BOB does not require any
|
|
// reference frames and can be used with both Progressive
|
|
// and interlaced video
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::FindBOBProcessorIndex(DWORD* pIndex)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
D3D11_VIDEO_PROCESSOR_CAPS caps = {};
|
|
D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS convCaps = {};
|
|
|
|
*pIndex = 0;
|
|
hr = m_pVideoProcessorEnum->GetVideoProcessorCaps(&caps);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
for (DWORD i = 0; i < caps.RateConversionCapsCount; i++)
|
|
{
|
|
hr = m_pVideoProcessorEnum->GetVideoProcessorRateConversionCaps(i, &convCaps);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// Check the caps to see which deinterlacer is supported
|
|
if ((convCaps.ProcessorCaps & D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB) != 0)
|
|
{
|
|
*pIndex = i;
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: GetVideoDisplayArea
|
|
// Description: get the display area from the media type.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::GetVideoDisplayArea(IMFMediaType* pType, MFVideoArea* pArea)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bPanScan = FALSE;
|
|
UINT32 uimageWidthInPixels = 0, uimageHeightInPixels = 0;
|
|
|
|
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &uimageWidthInPixels, &uimageHeightInPixels);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (uimageWidthInPixels != m_imageWidthInPixels || uimageHeightInPixels != m_imageHeightInPixels)
|
|
{
|
|
SafeRelease(m_pVideoProcessorEnum);
|
|
SafeRelease(m_pVideoProcessor);
|
|
SafeRelease(m_pSwapChain1);
|
|
}
|
|
|
|
m_imageWidthInPixels = uimageWidthInPixels;
|
|
m_imageHeightInPixels = uimageHeightInPixels;
|
|
|
|
bPanScan = MFGetAttributeUINT32(pType, MF_MT_PAN_SCAN_ENABLED, FALSE);
|
|
|
|
// In pan/scan mode, try to get the pan/scan region.
|
|
if (bPanScan)
|
|
{
|
|
hr = pType->GetBlob(
|
|
MF_MT_PAN_SCAN_APERTURE,
|
|
(UINT8*)pArea,
|
|
sizeof(MFVideoArea),
|
|
NULL
|
|
);
|
|
}
|
|
|
|
// If not in pan/scan mode, or the pan/scan region is not set,
|
|
// get the minimimum display aperture.
|
|
|
|
if (!bPanScan || hr == MF_E_ATTRIBUTENOTFOUND)
|
|
{
|
|
hr = pType->GetBlob(
|
|
MF_MT_MINIMUM_DISPLAY_APERTURE,
|
|
(UINT8*)pArea,
|
|
sizeof(MFVideoArea),
|
|
NULL
|
|
);
|
|
|
|
if (hr == MF_E_ATTRIBUTENOTFOUND)
|
|
{
|
|
// Minimum display aperture is not set.
|
|
|
|
// For backward compatibility with some components,
|
|
// check for a geometric aperture.
|
|
|
|
hr = pType->GetBlob(
|
|
MF_MT_GEOMETRIC_APERTURE,
|
|
(UINT8*)pArea,
|
|
sizeof(MFVideoArea),
|
|
NULL
|
|
);
|
|
}
|
|
|
|
// Default: Use the entire video area.
|
|
|
|
if (hr == MF_E_ATTRIBUTENOTFOUND)
|
|
{
|
|
*pArea = MakeArea(0.0, 0.0, m_imageWidthInPixels, m_imageHeightInPixels);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: LetterBoxDstRectPixelAspectToPictureAspect
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Takes a src rectangle and constructs the largest possible destination
|
|
// rectangle within the specifed destination rectangle such that
|
|
// the video maintains its current shape.
|
|
//
|
|
// This function assumes that pels are the same shape within both the src
|
|
// and dst rectangles.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DX11VideoRenderer::CPresenter::LetterBoxDstRect(
|
|
LPRECT lprcLBDst, // output letterboxed rectangle
|
|
const RECT& rcSrc, // input source rectangle
|
|
const RECT& rcDst // input destination rectangle
|
|
)
|
|
{
|
|
// figure out src/dest scale ratios
|
|
int iSrcWidth = rcSrc.right - rcSrc.left;
|
|
int iSrcHeight = rcSrc.bottom - rcSrc.top;
|
|
|
|
int iDstWidth = rcDst.right - rcDst.left;
|
|
int iDstHeight = rcDst.bottom - rcDst.top;
|
|
|
|
int iDstLBWidth = 0;
|
|
int iDstLBHeight = 0;
|
|
|
|
//
|
|
// work out if we are Column or Row letter boxing
|
|
//
|
|
|
|
if (MulDiv(iSrcWidth, iDstHeight, iSrcHeight) <= iDstWidth)
|
|
{
|
|
//
|
|
// column letter boxing - we add border color bars to the
|
|
// left and right of the video image to fill the destination
|
|
// rectangle.
|
|
//
|
|
iDstLBWidth = MulDiv(iDstHeight, iSrcWidth, iSrcHeight);
|
|
iDstLBHeight = iDstHeight;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// row letter boxing - we add border color bars to the top
|
|
// and bottom of the video image to fill the destination
|
|
// rectangle
|
|
//
|
|
iDstLBWidth = iDstWidth;
|
|
iDstLBHeight = MulDiv(iDstWidth, iSrcHeight, iSrcWidth);
|
|
}
|
|
|
|
//
|
|
// now create a centered LB rectangle within the current destination rect
|
|
//
|
|
lprcLBDst->left = rcDst.left + ((iDstWidth - iDstLBWidth) / 2);
|
|
lprcLBDst->right = lprcLBDst->left + iDstLBWidth;
|
|
|
|
lprcLBDst->top = rcDst.top + ((iDstHeight - iDstLBHeight) / 2);
|
|
lprcLBDst->bottom = lprcLBDst->top + iDstLBHeight;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: PixelAspectToPictureAspect
|
|
//
|
|
// Synopsis: Converts a pixel aspect ratio to a picture aspect ratio
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DX11VideoRenderer::CPresenter::PixelAspectToPictureAspect(
|
|
int Width,
|
|
int Height,
|
|
int PixelAspectX,
|
|
int PixelAspectY,
|
|
int* pPictureAspectX,
|
|
int* pPictureAspectY
|
|
)
|
|
{
|
|
//
|
|
// sanity check - if any inputs are 0, return 0
|
|
//
|
|
if (PixelAspectX == 0 || PixelAspectY == 0 || Width == 0 || Height == 0)
|
|
{
|
|
*pPictureAspectX = 0;
|
|
*pPictureAspectY = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// start by reducing both ratios to lowest terms
|
|
//
|
|
ReduceToLowestTerms(Width, Height, &Width, &Height);
|
|
ReduceToLowestTerms(PixelAspectX, PixelAspectY, &PixelAspectX, &PixelAspectY);
|
|
|
|
//
|
|
// Make sure that none of the values are larger than 2^16, so we don't
|
|
// overflow on the last operation. This reduces the accuracy somewhat,
|
|
// but it's a "hail mary" for incredibly strange aspect ratios that don't
|
|
// exist in practical usage.
|
|
//
|
|
while (Width > 0xFFFF || Height > 0xFFFF)
|
|
{
|
|
Width >>= 1;
|
|
Height >>= 1;
|
|
}
|
|
|
|
while (PixelAspectX > 0xFFFF || PixelAspectY > 0xFFFF)
|
|
{
|
|
PixelAspectX >>= 1;
|
|
PixelAspectY >>= 1;
|
|
}
|
|
|
|
ReduceToLowestTerms(
|
|
PixelAspectX * Width,
|
|
PixelAspectY * Height,
|
|
pPictureAspectX,
|
|
pPictureAspectY
|
|
);
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::ProcessFrameUsingD3D11( ID3D11Texture2D* pLeftTexture2D, ID3D11Texture2D* pRightTexture2D, UINT dwLeftViewIndex, UINT dwRightViewIndex, RECT rcDest, UINT32 unInterlaceMode, IMFSample** ppVideoOutFrame )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ID3D11VideoContext* pVideoContext = NULL;
|
|
ID3D11VideoProcessorInputView* pLeftInputView = NULL;
|
|
ID3D11VideoProcessorInputView* pRightInputView = NULL;
|
|
ID3D11VideoProcessorOutputView* pOutputView = NULL;
|
|
ID3D11Texture2D* pDXGIBackBuffer = NULL;
|
|
ID3D11RenderTargetView* pRTView = NULL;
|
|
IMFSample* pRTSample = NULL;
|
|
IMFMediaBuffer* pBuffer = NULL;
|
|
D3D11_VIDEO_PROCESSOR_CAPS vpCaps = {0};
|
|
LARGE_INTEGER lpcStart,lpcEnd;
|
|
|
|
do
|
|
{
|
|
if (!m_pDX11VideoDevice)
|
|
{
|
|
hr = m_pD3D11Device->QueryInterface(__uuidof(ID3D11VideoDevice), (void**)&m_pDX11VideoDevice);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = m_pD3DImmediateContext->QueryInterface(__uuidof( ID3D11VideoContext ), (void**)&pVideoContext);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// remember the original rectangles
|
|
RECT TRectOld = m_rcDstApp;
|
|
RECT SRectOld = m_rcSrcApp;
|
|
UpdateRectangles(&TRectOld, &SRectOld);
|
|
|
|
//Update destination rect with current client rect
|
|
m_rcDstApp = rcDest;
|
|
|
|
D3D11_TEXTURE2D_DESC surfaceDesc;
|
|
pLeftTexture2D->GetDesc(&surfaceDesc);
|
|
|
|
if (!m_pVideoProcessorEnum || !m_pVideoProcessor || m_imageWidthInPixels != surfaceDesc.Width || m_imageHeightInPixels != surfaceDesc.Height)
|
|
{
|
|
SafeRelease(m_pVideoProcessorEnum);
|
|
SafeRelease(m_pVideoProcessor);
|
|
|
|
m_imageWidthInPixels = surfaceDesc.Width;
|
|
m_imageHeightInPixels = surfaceDesc.Height;
|
|
|
|
D3D11_VIDEO_PROCESSOR_CONTENT_DESC ContentDesc;
|
|
ZeroMemory( &ContentDesc, sizeof( ContentDesc ) );
|
|
ContentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
|
|
ContentDesc.InputWidth = surfaceDesc.Width;
|
|
ContentDesc.InputHeight = surfaceDesc.Height;
|
|
ContentDesc.OutputWidth = surfaceDesc.Width;
|
|
ContentDesc.OutputHeight = surfaceDesc.Height;
|
|
ContentDesc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
|
|
|
|
hr = m_pDX11VideoDevice->CreateVideoProcessorEnumerator(&ContentDesc, &m_pVideoProcessorEnum);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
UINT uiFlags;
|
|
DXGI_FORMAT VP_Output_Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
|
|
hr = m_pVideoProcessorEnum->CheckVideoProcessorFormat(VP_Output_Format, &uiFlags);
|
|
if (FAILED(hr) || 0 == (uiFlags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT))
|
|
{
|
|
hr = MF_E_UNSUPPORTED_D3D_TYPE;
|
|
break;
|
|
}
|
|
|
|
m_rcSrcApp.left = 0;
|
|
m_rcSrcApp.top = 0;
|
|
m_rcSrcApp.right = m_uiRealDisplayWidth;
|
|
m_rcSrcApp.bottom = m_uiRealDisplayHeight;
|
|
|
|
DWORD index;
|
|
hr = FindBOBProcessorIndex(&index);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = m_pDX11VideoDevice->CreateVideoProcessor(m_pVideoProcessorEnum, index, &m_pVideoProcessor);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_b3DVideo)
|
|
{
|
|
hr = m_pVideoProcessorEnum->GetVideoProcessorCaps(&vpCaps);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (vpCaps.FeatureCaps & D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_STEREO)
|
|
{
|
|
m_bStereoEnabled = TRUE;
|
|
}
|
|
|
|
DXGI_MODE_DESC1 modeFilter = { 0 };
|
|
modeFilter.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
modeFilter.Width = surfaceDesc.Width;
|
|
modeFilter.Height = surfaceDesc.Height;
|
|
modeFilter.Stereo = m_bStereoEnabled;
|
|
|
|
DXGI_MODE_DESC1 matchedMode;
|
|
if (m_bFullScreenState)
|
|
{
|
|
hr = m_pDXGIOutput1->FindClosestMatchingMode1(&modeFilter, &matchedMode, m_pD3D11Device);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now create the input and output media types - these need to reflect
|
|
// the src and destination rectangles that we have been given.
|
|
RECT TRect = m_rcDstApp;
|
|
RECT SRect = m_rcSrcApp;
|
|
UpdateRectangles(&TRect, &SRect);
|
|
|
|
const BOOL fDestRectChanged = !EqualRect(&TRect, &TRectOld);
|
|
|
|
if (!m_pSwapChain1 || fDestRectChanged)
|
|
{
|
|
hr = UpdateDXGISwapChain();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_bCanProcessNextSample = FALSE;
|
|
|
|
// Get Backbuffer
|
|
hr = m_pSwapChain1->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pDXGIBackBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// create the output media sample
|
|
hr = MFCreateSample(&pRTSample);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), pDXGIBackBuffer, 0, FALSE, &pBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pRTSample->AddBuffer(pBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_b3DVideo && 0 != m_vp3DOutput)
|
|
{
|
|
SafeRelease(pBuffer);
|
|
|
|
hr = MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), pDXGIBackBuffer, 1, FALSE, &pBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pRTSample->AddBuffer(pBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
QueryPerformanceCounter(&lpcStart);
|
|
|
|
QueryPerformanceCounter(&lpcEnd);
|
|
|
|
//
|
|
// Create Output View of Output Surfaces.
|
|
//
|
|
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc;
|
|
ZeroMemory( &OutputViewDesc, sizeof( OutputViewDesc ) );
|
|
if (m_b3DVideo && m_bStereoEnabled)
|
|
{
|
|
OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2DARRAY;
|
|
}
|
|
else
|
|
{
|
|
OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
|
|
}
|
|
OutputViewDesc.Texture2D.MipSlice = 0;
|
|
OutputViewDesc.Texture2DArray.MipSlice = 0;
|
|
OutputViewDesc.Texture2DArray.FirstArraySlice = 0;
|
|
if (m_b3DVideo && 0 != m_vp3DOutput)
|
|
{
|
|
OutputViewDesc.Texture2DArray.ArraySize = 2; // STEREO
|
|
}
|
|
|
|
QueryPerformanceCounter(&lpcStart);
|
|
|
|
hr = m_pDX11VideoDevice->CreateVideoProcessorOutputView(pDXGIBackBuffer, m_pVideoProcessorEnum, &OutputViewDesc, &pOutputView);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputLeftViewDesc;
|
|
ZeroMemory( &InputLeftViewDesc, sizeof( InputLeftViewDesc ) );
|
|
InputLeftViewDesc.FourCC = 0;
|
|
InputLeftViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
|
|
InputLeftViewDesc.Texture2D.MipSlice = 0;
|
|
InputLeftViewDesc.Texture2D.ArraySlice = dwLeftViewIndex;
|
|
|
|
hr = m_pDX11VideoDevice->CreateVideoProcessorInputView(pLeftTexture2D, m_pVideoProcessorEnum, &InputLeftViewDesc, &pLeftInputView);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_b3DVideo && MFVideo3DSampleFormat_MultiView == m_vp3DOutput && pRightTexture2D)
|
|
{
|
|
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC InputRightViewDesc;
|
|
ZeroMemory( &InputRightViewDesc, sizeof( InputRightViewDesc ) );
|
|
InputRightViewDesc.FourCC = 0;
|
|
InputRightViewDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
|
|
InputRightViewDesc.Texture2D.MipSlice = 0;
|
|
InputRightViewDesc.Texture2D.ArraySlice = dwRightViewIndex;
|
|
|
|
hr = m_pDX11VideoDevice->CreateVideoProcessorInputView(pRightTexture2D, m_pVideoProcessorEnum, &InputRightViewDesc, &pRightInputView);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
QueryPerformanceCounter(&lpcEnd);
|
|
|
|
QueryPerformanceCounter(&lpcStart);
|
|
|
|
SetVideoContextParameters(pVideoContext, &SRect, &TRect, unInterlaceMode);
|
|
|
|
// Enable/Disable Stereo
|
|
if (m_b3DVideo)
|
|
{
|
|
pVideoContext->VideoProcessorSetOutputStereoMode(m_pVideoProcessor, m_bStereoEnabled);
|
|
|
|
D3D11_VIDEO_PROCESSOR_STEREO_FORMAT vpStereoFormat = D3D11_VIDEO_PROCESSOR_STEREO_FORMAT_SEPARATE;
|
|
if (MFVideo3DSampleFormat_Packed_LeftRight == m_vp3DOutput)
|
|
{
|
|
vpStereoFormat = D3D11_VIDEO_PROCESSOR_STEREO_FORMAT_HORIZONTAL;
|
|
}
|
|
else if (MFVideo3DSampleFormat_Packed_TopBottom == m_vp3DOutput)
|
|
{
|
|
vpStereoFormat = D3D11_VIDEO_PROCESSOR_STEREO_FORMAT_VERTICAL;
|
|
}
|
|
|
|
pVideoContext->VideoProcessorSetStreamStereoFormat(m_pVideoProcessor,
|
|
0, m_bStereoEnabled, vpStereoFormat, TRUE, TRUE, D3D11_VIDEO_PROCESSOR_STEREO_FLIP_NONE, 0);
|
|
}
|
|
|
|
QueryPerformanceCounter(&lpcEnd);
|
|
|
|
QueryPerformanceCounter(&lpcStart);
|
|
|
|
D3D11_VIDEO_PROCESSOR_STREAM StreamData;
|
|
ZeroMemory( &StreamData, sizeof( StreamData ) );
|
|
StreamData.Enable = TRUE;
|
|
StreamData.OutputIndex = 0;
|
|
StreamData.InputFrameOrField = 0;
|
|
StreamData.PastFrames = 0;
|
|
StreamData.FutureFrames = 0;
|
|
StreamData.ppPastSurfaces = NULL;
|
|
StreamData.ppFutureSurfaces = NULL;
|
|
StreamData.pInputSurface = pLeftInputView;
|
|
StreamData.ppPastSurfacesRight = NULL;
|
|
StreamData.ppFutureSurfacesRight = NULL;
|
|
|
|
if (m_b3DVideo && MFVideo3DSampleFormat_MultiView == m_vp3DOutput && pRightTexture2D)
|
|
{
|
|
StreamData.pInputSurfaceRight = pRightInputView;
|
|
}
|
|
|
|
hr = pVideoContext->VideoProcessorBlt(m_pVideoProcessor, pOutputView, 0, 1, &StreamData );
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
QueryPerformanceCounter(&lpcEnd);
|
|
|
|
if (ppVideoOutFrame != NULL)
|
|
{
|
|
*ppVideoOutFrame = pRTSample;
|
|
(*ppVideoOutFrame)->AddRef();
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
SafeRelease(pBuffer);
|
|
SafeRelease(pRTSample);
|
|
SafeRelease(pDXGIBackBuffer);
|
|
SafeRelease(pOutputView);
|
|
SafeRelease(pLeftInputView);
|
|
SafeRelease(pRightInputView);
|
|
SafeRelease(pVideoContext);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::ProcessFrameUsingXVP( IMFMediaType* pCurrentType, IMFSample* pVideoFrame, ID3D11Texture2D* pTexture2D, RECT rcDest, IMFSample** ppVideoOutFrame, BOOL* pbInputFrameUsed )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ID3D11VideoContext* pVideoContext = NULL;
|
|
ID3D11Texture2D* pDXGIBackBuffer = NULL;
|
|
IMFSample* pRTSample = NULL;
|
|
IMFMediaBuffer* pBuffer = NULL;
|
|
IMFAttributes* pAttributes = NULL;
|
|
D3D11_VIDEO_PROCESSOR_CAPS vpCaps = {0};
|
|
|
|
do
|
|
{
|
|
if (!m_pDX11VideoDevice)
|
|
{
|
|
hr = m_pD3D11Device->QueryInterface(__uuidof(ID3D11VideoDevice), (void**)&m_pDX11VideoDevice);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = m_pD3DImmediateContext->QueryInterface(__uuidof( ID3D11VideoContext ), (void**)&pVideoContext);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// remember the original rectangles
|
|
RECT TRectOld = m_rcDstApp;
|
|
RECT SRectOld = m_rcSrcApp;
|
|
UpdateRectangles(&TRectOld, &SRectOld);
|
|
|
|
//Update destination rect with current client rect
|
|
m_rcDstApp = rcDest;
|
|
|
|
D3D11_TEXTURE2D_DESC surfaceDesc;
|
|
pTexture2D->GetDesc(&surfaceDesc);
|
|
|
|
BOOL fTypeChanged = FALSE;
|
|
if (!m_pVideoProcessorEnum || !m_pSwapChain1 || m_imageWidthInPixels != surfaceDesc.Width || m_imageHeightInPixels != surfaceDesc.Height)
|
|
{
|
|
SafeRelease(m_pVideoProcessorEnum);
|
|
SafeRelease(m_pSwapChain1);
|
|
|
|
m_imageWidthInPixels = surfaceDesc.Width;
|
|
m_imageHeightInPixels = surfaceDesc.Height;
|
|
fTypeChanged = TRUE;
|
|
|
|
D3D11_VIDEO_PROCESSOR_CONTENT_DESC ContentDesc;
|
|
ZeroMemory( &ContentDesc, sizeof( ContentDesc ) );
|
|
ContentDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
|
|
ContentDesc.InputWidth = surfaceDesc.Width;
|
|
ContentDesc.InputHeight = surfaceDesc.Height;
|
|
ContentDesc.OutputWidth = surfaceDesc.Width;
|
|
ContentDesc.OutputHeight = surfaceDesc.Height;
|
|
ContentDesc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
|
|
|
|
hr = m_pDX11VideoDevice->CreateVideoProcessorEnumerator(&ContentDesc, &m_pVideoProcessorEnum);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
m_rcSrcApp.left = 0;
|
|
m_rcSrcApp.top = 0;
|
|
m_rcSrcApp.right = m_uiRealDisplayWidth;
|
|
m_rcSrcApp.bottom = m_uiRealDisplayHeight;
|
|
|
|
if (m_b3DVideo)
|
|
{
|
|
hr = m_pVideoProcessorEnum->GetVideoProcessorCaps(&vpCaps);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (vpCaps.FeatureCaps & D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_STEREO)
|
|
{
|
|
m_bStereoEnabled = TRUE;
|
|
}
|
|
|
|
DXGI_MODE_DESC1 modeFilter = { 0 };
|
|
modeFilter.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
modeFilter.Width = surfaceDesc.Width;
|
|
modeFilter.Height = surfaceDesc.Height;
|
|
modeFilter.Stereo = m_bStereoEnabled;
|
|
|
|
DXGI_MODE_DESC1 matchedMode;
|
|
if (m_bFullScreenState)
|
|
{
|
|
hr = m_pDXGIOutput1->FindClosestMatchingMode1(&modeFilter, &matchedMode, m_pD3D11Device);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = m_pXVP->GetAttributes(&pAttributes);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pAttributes->SetUINT32(MF_ENABLE_3DVIDEO_OUTPUT, (0 != m_vp3DOutput) ? MF3DVideoOutputType_Stereo : MF3DVideoOutputType_BaseView);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// now create the input and output media types - these need to reflect
|
|
// the src and destination rectangles that we have been given.
|
|
RECT TRect = m_rcDstApp;
|
|
RECT SRect = m_rcSrcApp;
|
|
UpdateRectangles(&TRect, &SRect);
|
|
|
|
const BOOL fDestRectChanged = !EqualRect(&TRect, &TRectOld);
|
|
const BOOL fSrcRectChanged = !EqualRect(&SRect, &SRectOld);
|
|
|
|
if (!m_pSwapChain1 || fDestRectChanged)
|
|
{
|
|
hr = UpdateDXGISwapChain();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fTypeChanged || fSrcRectChanged || fDestRectChanged)
|
|
{
|
|
// stop streaming to avoid multiple start\stop calls internally in XVP
|
|
hr = m_pXVP->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (fTypeChanged)
|
|
{
|
|
hr = SetXVPOutputMediaType(pCurrentType, DXGI_FORMAT_B8G8R8A8_UNORM);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fDestRectChanged)
|
|
{
|
|
hr = m_pXVPControl->SetDestinationRectangle(&m_rcDstApp);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fSrcRectChanged)
|
|
{
|
|
hr = m_pXVPControl->SetSourceRectangle(&SRect);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = m_pXVP->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_bCanProcessNextSample = FALSE;
|
|
|
|
// Get Backbuffer
|
|
hr = m_pSwapChain1->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pDXGIBackBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// create the output media sample
|
|
hr = MFCreateSample(&pRTSample);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), pDXGIBackBuffer, 0, FALSE, &pBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pRTSample->AddBuffer(pBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_b3DVideo && 0 != m_vp3DOutput)
|
|
{
|
|
SafeRelease(pBuffer);
|
|
|
|
hr = MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), pDXGIBackBuffer, 1, FALSE, &pBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = pRTSample->AddBuffer(pBuffer);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
DWORD dwStatus = 0;
|
|
MFT_OUTPUT_DATA_BUFFER outputDataBuffer = {};
|
|
outputDataBuffer.pSample = pRTSample;
|
|
hr = m_pXVP->ProcessOutput(0, 1, &outputDataBuffer, &dwStatus);
|
|
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
|
|
{
|
|
//call process input on the MFT to deliver the YUV video sample
|
|
// and the call process output to extract of newly processed frame
|
|
hr = m_pXVP->ProcessInput(0, pVideoFrame, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
*pbInputFrameUsed = TRUE;
|
|
|
|
hr = m_pXVP->ProcessOutput(0, 1, &outputDataBuffer, &dwStatus);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pbInputFrameUsed = FALSE;
|
|
}
|
|
|
|
if (ppVideoOutFrame != NULL)
|
|
{
|
|
*ppVideoOutFrame = pRTSample;
|
|
(*ppVideoOutFrame)->AddRef();
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
SafeRelease(pAttributes);
|
|
SafeRelease(pBuffer);
|
|
SafeRelease(pRTSample);
|
|
SafeRelease(pDXGIBackBuffer);
|
|
SafeRelease(pVideoContext);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: ReduceToLowestTerms
|
|
//
|
|
// Synopsis: reduces a numerator and denominator pair to their lowest terms
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DX11VideoRenderer::CPresenter::ReduceToLowestTerms(
|
|
int NumeratorIn,
|
|
int DenominatorIn,
|
|
int* pNumeratorOut,
|
|
int* pDenominatorOut
|
|
)
|
|
{
|
|
int GCD = gcd(NumeratorIn, DenominatorIn);
|
|
|
|
*pNumeratorOut = NumeratorIn / GCD;
|
|
*pDenominatorOut = DenominatorIn / GCD;
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::SetMonitor(UINT adapterID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwMatchID = 0;
|
|
|
|
CAutoLock lock(&m_critSec);
|
|
|
|
do
|
|
{
|
|
hr = m_pMonitors->MatchGUID(adapterID, &dwMatchID);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
m_lpCurrMon = &(*m_pMonitors)[dwMatchID];
|
|
m_ConnectionGUID = adapterID;
|
|
}
|
|
while (FALSE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: SetVideoContextParameters
|
|
//
|
|
// Synopsis: Updates the various parameters used for VpBlt call
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DX11VideoRenderer::CPresenter::SetVideoContextParameters(ID3D11VideoContext* pVideoContext, const RECT* pSRect, const RECT* pTRect, UINT32 unInterlaceMode)
|
|
{
|
|
D3D11_VIDEO_FRAME_FORMAT FrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
|
|
if ( MFVideoInterlace_FieldInterleavedUpperFirst == unInterlaceMode || MFVideoInterlace_FieldSingleUpper == unInterlaceMode || MFVideoInterlace_MixedInterlaceOrProgressive == unInterlaceMode )
|
|
{
|
|
FrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
|
|
}
|
|
else if ( MFVideoInterlace_FieldInterleavedLowerFirst == unInterlaceMode || MFVideoInterlace_FieldSingleLower == unInterlaceMode )
|
|
{
|
|
FrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
|
|
}
|
|
|
|
// input format
|
|
pVideoContext->VideoProcessorSetStreamFrameFormat(m_pVideoProcessor, 0, FrameFormat);
|
|
|
|
// Output rate (repeat frames)
|
|
pVideoContext->VideoProcessorSetStreamOutputRate(m_pVideoProcessor, 0, D3D11_VIDEO_PROCESSOR_OUTPUT_RATE_NORMAL, TRUE, NULL);
|
|
|
|
// Source rect
|
|
pVideoContext->VideoProcessorSetStreamSourceRect(m_pVideoProcessor, 0, TRUE, pSRect);
|
|
|
|
// Stream dest rect
|
|
pVideoContext->VideoProcessorSetStreamDestRect(m_pVideoProcessor, 0, TRUE, pTRect);
|
|
|
|
pVideoContext->VideoProcessorSetOutputTargetRect(m_pVideoProcessor, TRUE, &m_rcDstApp);
|
|
|
|
// Stream color space
|
|
D3D11_VIDEO_PROCESSOR_COLOR_SPACE colorSpace = {};
|
|
colorSpace.YCbCr_xvYCC = 1;
|
|
pVideoContext->VideoProcessorSetStreamColorSpace(m_pVideoProcessor, 0, &colorSpace);
|
|
|
|
// Output color space
|
|
pVideoContext->VideoProcessorSetOutputColorSpace(m_pVideoProcessor, &colorSpace);
|
|
|
|
// Output background color (black)
|
|
D3D11_VIDEO_COLOR backgroundColor = {};
|
|
backgroundColor.RGBA.A = 1.0F;
|
|
backgroundColor.RGBA.R = 1.0F * static_cast<float>(GetRValue(0)) / 255.0F;
|
|
backgroundColor.RGBA.G = 1.0F * static_cast<float>(GetGValue(0)) / 255.0F;
|
|
backgroundColor.RGBA.B = 1.0F * static_cast<float>(GetBValue(0)) / 255.0F;
|
|
|
|
pVideoContext->VideoProcessorSetOutputBackgroundColor(m_pVideoProcessor, FALSE, &backgroundColor);
|
|
}
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::SetVideoMonitor(HWND hwndVideo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CAMDDrawMonitorInfo* pMonInfo = NULL;
|
|
HMONITOR hMon = NULL;
|
|
|
|
if (!m_pMonitors)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
hMon = MonitorFromWindow(hwndVideo, MONITOR_DEFAULTTONULL);
|
|
|
|
do
|
|
{
|
|
if (NULL != hMon)
|
|
{
|
|
m_pMonitors->TerminateDisplaySystem();
|
|
m_lpCurrMon = NULL;
|
|
|
|
hr = m_pMonitors->InitializeDisplaySystem(hwndVideo);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
pMonInfo = m_pMonitors->FindMonitor(hMon);
|
|
if (NULL != pMonInfo && pMonInfo->uDevID != m_ConnectionGUID)
|
|
{
|
|
hr = SetMonitor(pMonInfo->uDevID);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_POINTER;
|
|
break;
|
|
}
|
|
}
|
|
while(FALSE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Name: SetXVPOutputMediaType
|
|
// Description: Tells the XVP about the size of the destination surface
|
|
// and where within the surface we should be writing.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT DX11VideoRenderer::CPresenter::SetXVPOutputMediaType(IMFMediaType* pType, DXGI_FORMAT vpOutputFormat)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMFVideoMediaType* pMTOutput = NULL;
|
|
MFVIDEOFORMAT mfvf = {};
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFInitVideoFormat_RGB(
|
|
&mfvf,
|
|
m_rcDstApp.right,
|
|
m_rcDstApp.bottom,
|
|
MFMapDXGIFormatToDX9Format(vpOutputFormat));
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFCreateVideoMediaType(&mfvf, &pMTOutput);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pXVP->SetOutputType(0, pMTOutput, 0);
|
|
}
|
|
|
|
SafeRelease(pMTOutput);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: UpdateDXGISwapChain
|
|
//
|
|
// Synopsis: Creates SwapChain for HWND or DComp or Resizes buffers in case of resolution change
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
_Post_satisfies_(this->m_pSwapChain1 != NULL)
|
|
HRESULT DX11VideoRenderer::CPresenter::UpdateDXGISwapChain(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Get the DXGISwapChain1
|
|
DXGI_SWAP_CHAIN_DESC1 scd;
|
|
ZeroMemory(&scd, sizeof(scd));
|
|
scd.SampleDesc.Count = 1;
|
|
scd.SampleDesc.Quality = 0;
|
|
scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
|
|
scd.Scaling = DXGI_SCALING_STRETCH;
|
|
scd.Width = m_rcDstApp.right;
|
|
scd.Height = m_rcDstApp.bottom;
|
|
scd.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
scd.Stereo = m_bStereoEnabled;
|
|
scd.BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
scd.Flags = m_bStereoEnabled ? DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH : 0; //opt in to do direct flip;
|
|
scd.BufferCount = 4;
|
|
|
|
do
|
|
{
|
|
if (m_pSwapChain1)
|
|
{
|
|
// Resize our back buffers for the desired format.
|
|
hr = m_pSwapChain1->ResizeBuffers
|
|
(
|
|
4,
|
|
m_rcDstApp.right,
|
|
m_rcDstApp.bottom,
|
|
scd.Format,
|
|
scd.Flags
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
if (!m_useDCompVisual)
|
|
{
|
|
hr = m_pDXGIFactory2->CreateSwapChainForHwnd(m_pD3D11Device, m_hwndVideo, &scd, NULL, NULL, &m_pSwapChain1);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (m_bFullScreenState)
|
|
{
|
|
hr = m_pSwapChain1->SetFullscreenState(TRUE, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = m_pSwapChain1->SetFullscreenState(FALSE, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Create a swap chain for composition
|
|
hr = m_pDXGIFactory2->CreateSwapChainForComposition(m_pD3D11Device, &scd, NULL, &m_pSwapChain1);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = m_pRootVisual->SetContent(m_pSwapChain1);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
hr = m_pDCompDevice->Commit();
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: UpodateRectangles
|
|
//
|
|
// Synopsis: Figures out the real source and destination rectangles
|
|
// to use when drawing the video frame into the clients
|
|
// destination location. Takes into account pixel aspect
|
|
// ration correction, letterboxing and source rectangles.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void DX11VideoRenderer::CPresenter::UpdateRectangles(RECT* pDst, RECT* pSrc)
|
|
{
|
|
// take the given src rect and reverse map it into the native video
|
|
// image rectange. For example, consider a video with a buffer size of
|
|
// 720x480 and an active area of 704x480 - 8,0 with a picture aspect
|
|
// ratio of 4:3. The user sees the native video size as 640x480.
|
|
//
|
|
// If the user gave us a src rectangle of (180, 135, 540, 405)
|
|
// then this gets reversed mapped to
|
|
//
|
|
// 8 + (180 * 704 / 640) = 206
|
|
// 0 + (135 * 480 / 480) = 135
|
|
// 8 + (540 * 704 / 640) = 602
|
|
// 0 + (405 * 480 / 480) = 405
|
|
|
|
RECT Src = *pSrc;
|
|
|
|
pSrc->left = m_displayRect.left + MulDiv(pSrc->left, (m_displayRect.right - m_displayRect.left), m_uiRealDisplayWidth);
|
|
pSrc->right = m_displayRect.left + MulDiv(pSrc->right, (m_displayRect.right - m_displayRect.left), m_uiRealDisplayWidth);
|
|
|
|
pSrc->top = m_displayRect.top + MulDiv(pSrc->top, (m_displayRect.bottom - m_displayRect.top), m_uiRealDisplayHeight);
|
|
pSrc->bottom = m_displayRect.top + MulDiv(pSrc->bottom, (m_displayRect.bottom - m_displayRect.top), m_uiRealDisplayHeight);
|
|
|
|
LetterBoxDstRect(pDst, Src, m_rcDstApp);
|
|
}
|