VMR9 On DirectShow (4)

壊れたエアコンを取り替えて新しいのを買おうかと思っていたのですが、取り外しの工事費だけで1万円するんだな~とびっくりしました。

取り外しの方法は検索をかけてみれば出てきたのでがんばって解体しようかと思います。さすがにもったいなさすぎですので。

さて、放置されていた話題のVMR9です。今回はInitializeDeviceとTerminateDeviceをやってみます。

特にInitializeDeviceがとっても訳がわからないのでいろいろと説明をつけてやってみます。

というわけでさっさとコードを

一つ追加される解放関数のDeleteSurfaceをつけて紹介です。

IDirect3DTexture9 far *m_alpDirect3DTexture[2];
vector<IDirect3DSurface9 *> m_veclpDirect3DSurface;
unsigned long m_ulWidth,m_ulHeight,m_ulTexWidth,m_ulTexHeight;
D3DFORMAT m_fmtTexture;
STDMETHODIMP CVMRSurfaceAllocator::InitializeDevice(DWORD_PTR dwUserID,VMR9AllocationInfo *lpAllocInfo,DWORD *lpNumBuffers)
{
	IDirect3DTexture9 *lpTexture;
	D3DCAPS9 d3dcaps; D3DDISPLAYMODE d3ddm;
	HRESULT hr; unsigned long ulWidth,ulHeight,ul; unsigned int i;
	//(a)
	CheckPointer(lpAllocInfo,E_POINTER);
	CheckPointer(lpNumBuffers,E_POINTER);
	CheckPointer(m_lpSurfaceAllocNotify,E_FAIL);
	//(b)
	m_ulWidth = ulWidth = lpAllocInfo->dwWidth;
	m_ulHeight = ulHeight = lpAllocInfo->dwHeight;
	m_fmtTexture = lpAllocInfo->Format;
	if(m_fmtTexture == D3DFMT_UNKNOWN){
		hr = m_lpDirect3DDevice->GetDisplayMode(NULL,&d3ddm);
		if(hr != S_OK) return hr;
		lpAllocInfo->Format = m_fmtTexture = d3ddm.Format;
	}
	//(c)
	hr = m_lpDirect3DDevice->GetDeviceCaps(&d3dcaps);
	if(d3dcaps.TextureCaps & D3DPTEXTURECAPS_POW2){
		for(ulWidth = 1;ulWidth < lpAllocInfo->dwWidth;ulWidth <<= 1);
		for(ulHeight = 1;ulHeight < lpAllocInfo->dwHeight;ulHeight <<= 1);
		lpAllocInfo->dwWidth = ulWidth;
		lpAllocInfo->dwHeight = ulHeight;
	}
	//(d)
	lpAllocInfo->dwFlags |= VMR9AllocFlag_TextureSurface;
	DeleteSurface(); m_veclpDirect3DSurface.resize(*lpNumBuffers);
	hr = m_lpSurfaceAllocNotify->AllocateSurfaceHelper(lpAllocInfo,lpNumBuffers,&(m_veclpDirect3DSurface[0]));
	if(hr != S_OK && !(lpAllocInfo->dwFlags & VMR9AllocFlag_3DRenderTarget)){
		DeleteSurface();
		//(e)
		if(lpAllocInfo->Format > '0000'){
			hr = m_lpDirect3DDevice->GetDisplayMode(NULL,&d3ddm);
			if(hr != S_OK) return hr;
			for(i = 0;i < 2;i++){
				hr = m_lpDirect3DDevice->CreateTexture(lpAllocInfo->dwWidth,lpAllocInfo->dwHeight,1,D3DUSAGE_RENDERTARGET,d3ddm.Format,D3DPOOL_DEFAULT,&lpTexture,NULL);
				if(hr != S_OK){ return hr; } m_alpDirect3DTexture[i] = lpTexture;
			}
			m_fmtTexture = d3ddm.Format;
		}
		//(f)
		lpAllocInfo->dwFlags &= ~VMR9AllocFlag_TextureSurface;
		lpAllocInfo->dwFlags |= VMR9AllocFlag_OffscreenSurface;
		hr = m_lpSurfaceAllocNotify->AllocateSurfaceHelper(lpAllocInfo,lpNumBuffers,&(m_veclpDirect3DSurface[0]));
	}
	
	//(g)
	m_ulTexWidth = lpAllocInfo->dwWidth; m_ulTexHeight = lpAllocInfo->dwHeight;
	return hr;
}
STDMETHODIMP CVMRSurfaceAllocator::TerminateDevice(DWORD_PTR dwUserID)
{
	DeleteSurface();
	return S_OK;
}
BOOL CVMRSurfaceAllocator::DeleteSurface(void)
{
	size_t i; vector<IDirect3DSurface9 far *>::iterator it,itend;
	for(i = 0;i < sizeof(m_alpDirect3DTexture) / sizeof(IDirect3DTexture9 *);i++){
		if(m_alpDirect3DTexture&#91;i&#93; != NULL){
			m_alpDirect3DTexture&#91;i&#93;->Release();
			m_alpDirect3DTexture[i] = NULL;
		}
	}
	for(it = m_veclpDirect3DSurface.begin(),itend = m_veclpDirect3DSurface.end();it != itend;++it){
		if(*it != NULL){
			(*it)->Release();
			(*it) = NULL;
		}
	}
	return TRUE;
}

TerminateDeviceはDeleteSurfaceを呼び出しているだけで、DeleteSurfaceはInitializeDevice内で作成させるサーフェイスおよびテクスチャを解放するための処理が入っています。

この辺はCOMの流れなので流してみてください。それではInitializeDeviceを見ていきましょう。

InitializeDeviceの引数と引数チェック(a)

引数は3つあります。

dwUserID VMR9のデバイスを識別するためのID(入力が1つしか無ければ無視してもよい)
lpAllocInfo VMR9がデバイスを初期化するときに指定する情報。この関数から戻ると実際に確保されたフォーマット情報がはいっている
lpNumBuffers VMR9が初期化時に初期化してほしいサーフェイスの枚数。この関数から戻ると実際に確保された枚数が入っている

ポインタ引数はNULLだと困るので引数チェックを行います。さらにIVMRSurfaceAllocatorNotify9も無いと処理が続行できないのでNULLチェックを行います。

描画対象となるビデオのフォーマットを取得(b)

幅、高さ、フォーマットを取得します。フォーマットが指定されていないときは現在の画面フォーマットを取得してそれを使います。

作成するテクスチャの条件に従って作成サイズを変更(c)

一部のグラフィックボードでは、テクスチャのサイズには一定の条件がつくことがあります。特に

  1. テクスチャは正方形でなければならない
  2. テクスチャの幅および高さは2の乗数でなければならない
  3. テクスチャは一定の幅および高さを超えてはならない

があり、それをチェックする必要があります。今回はその中でも2.の「2の乗数」を調べています。簡易なのでとりあずこれだけです。

正しく作るのであれば全部調べる必要があります。

指定した作成条件でテクスチャを作成してみる(d)

まずはVMR9の描画対象となるサーフェイスをテクスチャとして作成してみます。テクスチャ作成フラグを与えてヘルパに作成を依頼します。

このとき、対象のテクスチャが何枚必要かはVMR9側から指定してくるので必要な分を作成するための可変長配列としてvectorを利用しています。

ここにエラー処理があることからわかるとおり、この処理は失敗することがあります。

ビデオフォーマットが直接レンダリング不可の場合、それを変換するための最終描画テクスチャを作成しておく(e)

このテクスチャの作成は、ビデオフォーマットがDirect3Dによって直接レンダリングできない場合(YUVフォーマットのままなど)に最終描画用として

描画テクスチャを作っておくという作業です。このテクスチャを描画結果として使用するので、リソースの衝突が起きないようにダブルバッファリングを行います。

3枚以上はあまり確保する意味はないですね。1枚だとテクスチャ使用中にVMR9のイメージ描画要求がかかったときに処理がやりづらいです。

テクスチャでは無理だったのでサーフェイスとして確保する(f)

オフスクリーンサーフェイスを指定して再度作成を依頼します。この処理が失敗するときはリソース不足やフォーマットの誤りなどでこの場で処理するのはほぼ不可能です。

なので、この場合はhrをそのまま返して終わりです。なお、レンダリングターゲットを指定しての作成要求であったときはこのやり方が使えないのでこの処理は意味がありません。

作成が完了したので、処理結果を保存して処理コードを返す(g)

テクスチャのサイズとビデオの描画サイズが違うことがある(というか調整した)のでそれを保存して完了です。

ヘルパがエラーコードを返したときはそのコードがVMR9側に戻ることになります。

という処理で完了です。vectorの型はCComPtrなどを使えばDeleteSurfaceの処理がもうちょっとわかりやすくなるのですが、一応基本形ということで許してください。

お次はGetSurfaceとPresentImageですが・・・後最低でも2回あるわけですね・・・。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

この記事のトラックバック用URL