2025-11-28 00:35:46 +09:00

395 lines
10 KiB
C++

//------------------------------------------------------------------------------
// File: Allocator.cpp
//
// Desc: DirectShow sample code - implementation of the CAllocator class
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
#include "stdafx.h"
#include "util.h"
#include "Allocator.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CAllocator::CAllocator(HRESULT& hr, HWND wnd, IDirect3D9* d3d, IDirect3DDevice9* d3dd)
: m_refCount(1)
, m_D3D(d3d)
, m_D3DDev(d3dd)
, m_window( wnd )
{
CAutoLock Lock(&m_ObjectLock);
hr = E_FAIL;
if( IsWindow( wnd ) == FALSE )
{
hr = E_INVALIDARG;
return;
}
if( m_D3D == NULL )
{
ASSERT( d3dd == NULL );
m_D3D.Attach( Direct3DCreate9(D3D_SDK_VERSION) );
if (m_D3D == NULL) {
hr = E_FAIL;
return;
}
}
if( m_D3DDev == NULL )
{
hr = CreateDevice();
}
}
CAllocator::~CAllocator()
{
DeleteSurfaces();
}
HRESULT CAllocator::CreateDevice()
{
HRESULT hr;
m_D3DDev = NULL;
D3DDISPLAYMODE dm;
hr = m_D3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &dm);
D3DPRESENT_PARAMETERS pp;
ZeroMemory(&pp, sizeof(pp));
pp.Windowed = TRUE;
pp.hDeviceWindow = m_window;
pp.SwapEffect = D3DSWAPEFFECT_COPY;
pp.BackBufferFormat = dm.Format;
FAIL_RET( m_D3D->CreateDevice( D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
m_window,
D3DCREATE_SOFTWARE_VERTEXPROCESSING |
D3DCREATE_MULTITHREADED,
&pp,
&m_D3DDev) );
m_renderTarget = NULL;
return m_D3DDev->GetRenderTarget( 0, & m_renderTarget );
}
void CAllocator::DeleteSurfaces()
{
CAutoLock Lock(&m_ObjectLock);
// clear out the private texture
m_privateTexture = NULL;
for( size_t i = 0; i < m_surfaces.size(); ++i )
{
m_surfaces[i] = NULL;
}
}
//IVMRSurfaceAllocator9
HRESULT CAllocator::InitializeDevice(
/* [in] */ DWORD_PTR dwUserID,
/* [in] */ VMR9AllocationInfo *lpAllocInfo,
/* [out][in] */ DWORD *lpNumBuffers)
{
D3DCAPS9 d3dcaps;
DWORD dwWidth = 1;
DWORD dwHeight = 1;
float fTU = 1.f;
float fTV = 1.f;
if( lpNumBuffers == NULL )
{
return E_POINTER;
}
if( m_lpIVMRSurfAllocNotify == NULL )
{
return E_FAIL;
}
HRESULT hr = S_OK;
m_D3DDev->GetDeviceCaps( &d3dcaps );
if( d3dcaps.TextureCaps & D3DPTEXTURECAPS_POW2 )
{
while( dwWidth < lpAllocInfo->dwWidth )
dwWidth = dwWidth << 1;
while( dwHeight < lpAllocInfo->dwHeight )
dwHeight = dwHeight << 1;
fTU = (float)(lpAllocInfo->dwWidth) / (float)(dwWidth);
fTV = (float)(lpAllocInfo->dwHeight) / (float)(dwHeight);
m_scene.SetSrcRect( fTU, fTV );
lpAllocInfo->dwWidth = dwWidth;
lpAllocInfo->dwHeight = dwHeight;
}
// NOTE:
// we need to make sure that we create textures because
// surfaces can not be textured onto a primitive.
lpAllocInfo->dwFlags |= VMR9AllocFlag_TextureSurface;
DeleteSurfaces();
m_surfaces.resize(*lpNumBuffers);
hr = m_lpIVMRSurfAllocNotify->AllocateSurfaceHelper(lpAllocInfo, lpNumBuffers, & m_surfaces.at(0) );
// If we couldn't create a texture surface and
// the format is not an alpha format,
// then we probably cannot create a texture.
// So what we need to do is create a private texture
// and copy the decoded images onto it.
if(FAILED(hr) && !(lpAllocInfo->dwFlags & VMR9AllocFlag_3DRenderTarget))
{
DeleteSurfaces();
// is surface YUV ?
if (lpAllocInfo->Format > '0000')
{
D3DDISPLAYMODE dm;
FAIL_RET( m_D3DDev->GetDisplayMode(NULL, & dm ) );
// create the private texture
FAIL_RET( m_D3DDev->CreateTexture(lpAllocInfo->dwWidth, lpAllocInfo->dwHeight,
1,
D3DUSAGE_RENDERTARGET,
dm.Format,
D3DPOOL_DEFAULT /* default pool - usually video memory */,
& m_privateTexture, NULL ) );
}
lpAllocInfo->dwFlags &= ~VMR9AllocFlag_TextureSurface;
lpAllocInfo->dwFlags |= VMR9AllocFlag_OffscreenSurface;
FAIL_RET( m_lpIVMRSurfAllocNotify->AllocateSurfaceHelper(lpAllocInfo, lpNumBuffers, & m_surfaces.at(0) ) );
}
return m_scene.Init(m_D3DDev);
}
HRESULT CAllocator::TerminateDevice(
/* [in] */ DWORD_PTR dwID)
{
DeleteSurfaces();
return S_OK;
}
HRESULT CAllocator::GetSurface(
/* [in] */ DWORD_PTR dwUserID,
/* [in] */ DWORD SurfaceIndex,
/* [in] */ DWORD SurfaceFlags,
/* [out] */ IDirect3DSurface9 **lplpSurface)
{
if( lplpSurface == NULL )
{
return E_POINTER;
}
if (SurfaceIndex >= m_surfaces.size() )
{
return E_FAIL;
}
CAutoLock Lock(&m_ObjectLock);
*lplpSurface = m_surfaces[SurfaceIndex];
(*lplpSurface)->AddRef();
return S_OK;
// return m_surfaces[SurfaceIndex].CopyTo(lplpSurface) ;
}
HRESULT CAllocator::AdviseNotify(
/* [in] */ IVMRSurfaceAllocatorNotify9 *lpIVMRSurfAllocNotify)
{
CAutoLock Lock(&m_ObjectLock);
HRESULT hr;
m_lpIVMRSurfAllocNotify = lpIVMRSurfAllocNotify;
HMONITOR hMonitor = m_D3D->GetAdapterMonitor( D3DADAPTER_DEFAULT );
FAIL_RET( m_lpIVMRSurfAllocNotify->SetD3DDevice( m_D3DDev, hMonitor ) );
return hr;
}
HRESULT CAllocator::StartPresenting(
/* [in] */ DWORD_PTR dwUserID)
{
CAutoLock Lock(&m_ObjectLock);
ASSERT( m_D3DDev );
if( m_D3DDev == NULL )
{
return E_FAIL;
}
return S_OK;
}
HRESULT CAllocator::StopPresenting(
/* [in] */ DWORD_PTR dwUserID)
{
return S_OK;
}
HRESULT CAllocator::PresentImage(
/* [in] */ DWORD_PTR dwUserID,
/* [in] */ VMR9PresentationInfo *lpPresInfo)
{
HRESULT hr;
CAutoLock Lock(&m_ObjectLock);
// if we are in the middle of the display change
if( NeedToHandleDisplayChange() )
{
// NOTE: this piece of code is left as a user exercise.
// The D3DDevice here needs to be switched
// to the device that is using another adapter
}
hr = PresentHelper( lpPresInfo );
// IMPORTANT: device can be lost when user changes the resolution
// or when (s)he presses Ctrl + Alt + Delete.
// We need to restore our video memory after that
if( hr == D3DERR_DEVICELOST)
{
if (m_D3DDev->TestCooperativeLevel() == D3DERR_DEVICENOTRESET)
{
DeleteSurfaces();
FAIL_RET( CreateDevice() );
HMONITOR hMonitor = m_D3D->GetAdapterMonitor( D3DADAPTER_DEFAULT );
FAIL_RET( m_lpIVMRSurfAllocNotify->ChangeD3DDevice( m_D3DDev, hMonitor ) );
}
hr = S_OK;
}
return hr;
}
HRESULT CAllocator::PresentHelper(VMR9PresentationInfo *lpPresInfo)
{
// parameter validation
if( lpPresInfo == NULL )
{
return E_POINTER;
}
else if( lpPresInfo->lpSurf == NULL )
{
return E_POINTER;
}
CAutoLock Lock(&m_ObjectLock);
HRESULT hr;
m_D3DDev->SetRenderTarget( 0, m_renderTarget );
// if we created a private texture
// blt the decoded image onto the texture.
if( m_privateTexture != NULL )
{
SmartPtr<IDirect3DSurface9> surface;
FAIL_RET( m_privateTexture->GetSurfaceLevel( 0 , & surface ) );
// copy the full surface onto the texture's surface
FAIL_RET( m_D3DDev->StretchRect( lpPresInfo->lpSurf, NULL,
surface, NULL,
D3DTEXF_NONE ) );
FAIL_RET( m_scene.DrawScene(m_D3DDev, m_privateTexture ) );
}
else // this is the case where we have got the textures allocated by VMR
// all we need to do is to get them from the surface
{
SmartPtr<IDirect3DTexture9> texture;
FAIL_RET( lpPresInfo->lpSurf->GetContainer( __uuidof(IDirect3DTexture9), (LPVOID*) & texture ) );
FAIL_RET( m_scene.DrawScene(m_D3DDev, texture ) );
}
FAIL_RET( m_D3DDev->Present( NULL, NULL, NULL, NULL ) );
return hr;
}
bool CAllocator::NeedToHandleDisplayChange()
{
if( ! m_lpIVMRSurfAllocNotify )
{
return false;
}
D3DDEVICE_CREATION_PARAMETERS Parameters;
if( FAILED( m_D3DDev->GetCreationParameters(&Parameters) ) )
{
ASSERT( false );
return false;
}
HMONITOR currentMonitor = m_D3D->GetAdapterMonitor( Parameters.AdapterOrdinal );
HMONITOR hMonitor = m_D3D->GetAdapterMonitor( D3DADAPTER_DEFAULT );
return hMonitor != currentMonitor;
}
// IUnknown
HRESULT CAllocator::QueryInterface(
REFIID riid,
void** ppvObject)
{
HRESULT hr = E_NOINTERFACE;
if( ppvObject == NULL ) {
hr = E_POINTER;
}
else if( riid == IID_IVMRSurfaceAllocator9 ) {
*ppvObject = static_cast<IVMRSurfaceAllocator9*>( this );
AddRef();
hr = S_OK;
}
else if( riid == IID_IVMRImagePresenter9 ) {
*ppvObject = static_cast<IVMRImagePresenter9*>( this );
AddRef();
hr = S_OK;
}
else if( riid == IID_IUnknown ) {
*ppvObject =
static_cast<IUnknown*>(
static_cast<IVMRSurfaceAllocator9*>( this ) );
AddRef();
hr = S_OK;
}
return hr;
}
ULONG CAllocator::AddRef()
{
return InterlockedIncrement(& m_refCount);
}
ULONG CAllocator::Release()
{
ULONG ret = InterlockedDecrement(& m_refCount);
if( ret == 0 )
{
delete this;
}
return ret;
}