#include "Stdafx.h"
#include "CustomEVRPresenter.h"
#include "共通\AutoLock.h"
#include "MediaTypeBuilders.h"
#include "CustomGUID.h"
#include "Direct3D9\CDirect3DNative.h"


//=========================================================
// !! Win32 と連携するので、例外を発生させてはならない。!!
//=========================================================

namespace FDK
{
// public:

	HRESULT CustomEVRPresenter::CreateInstance( IUnknown *pUnkOuter, REFIID iid, void **ppv )
	{
		// パラメータチェック。

		if( pUnkOuter != NULL )
			return CLASS_E_NOAGGREGATION;

		if( ppv == NULL )
			return E_POINTER;


		HRESULT hr = S_OK;
		CustomEVRPresenter *pObject = NULL;
			
		try
		{
			// カスタムプレゼンタのインスタンスを作成。

			if( (pObject = new CustomEVRPresenter( hr )) == NULL )
				return E_OUTOFMEMORY;

			tFAILEDなら帰還( hr );


			// 指定されたインターフェースをクエリして返す。

			tFAILEDなら帰還( hr = pObject->QueryInterface( iid, ppv ) );
		}
		finally
		{
			SAFE_COM_RELEASE( pObject );
		}

		return hr;
	}


	// IUnknown 実装

	STDMETHODIMP CustomEVRPresenter::QueryInterface( REFIID riid, void **ppv )
	{
		if( ppv == NULL )
			return E_POINTER;

		if( riid == __uuidof(IUnknown) )
			*ppv = static_cast<IUnknown*>( static_cast<IMFVideoPresenter*>(this) );

		else if( riid == __uuidof(IMFVideoDeviceID) )
			*ppv = static_cast<IMFVideoDeviceID*>(this);

		else if( riid == __uuidof(IMFVideoPresenter) )
			*ppv = static_cast<IMFVideoPresenter*>(this);

		else if( riid == __uuidof(IMFClockStateSink) )
			*ppv = static_cast<IMFClockStateSink*>(this);

		else if( riid == __uuidof(IMFRateSupport) )
			*ppv = static_cast<IMFRateSupport*>(this);

		else if( riid == __uuidof(IMFGetService) )
			*ppv = static_cast<IMFGetService*>(this);

		else if( riid == __uuidof(IMFTopologyServiceLookupClient) )
			*ppv = static_cast<IMFTopologyServiceLookupClient*>(this);

		else if( riid == __uuidof(IMFVideoDisplayControl) )
			*ppv = static_cast<IMFVideoDisplayControl*>(this);

		else if( riid == __uuidof(IMFActivate) )
			*ppv = static_cast<IMFActivate*>(this);

		else if( riid == __uuidof(IMFAttributes) )
			*ppv = static_cast<IMFAttributes*>(this);

		else
		{
			*ppv = NULL;
			return E_NOINTERFACE;
		}

		this->AddRef();

		return S_OK;
	}
	STDMETHODIMP_( ULONG ) CustomEVRPresenter::AddRef()
	{
		return ::InterlockedIncrement( &this->n参照カウンタ );
	}
	STDMETHODIMP_( ULONG ) CustomEVRPresenter::Release()
	{
		ULONG uCount = ::InterlockedDecrement( &this->n参照カウンタ );

		if( uCount == 0 )
			delete this;
        
		return uCount;
	}


	// IMFGetService 実装
	
	STDMETHODIMP CustomEVRPresenter::GetService( REFGUID guidService, REFIID riid, LPVOID *ppvObject )
	{
		if( guidService != MR_VIDEO_RENDER_SERVICE )	// MR_VIDEO_RENDER_SERVICE 以外のサービスIDはサポートしない。
			return MF_E_UNSUPPORTED_SERVICE;

		if( ppvObject == NULL )
			return E_POINTER;


		HRESULT hr = S_OK;

		
		// 最初に、プレゼンタエンジンの GetService() を試す。

		Debug::Assert( this->m_pPresenterEngine != NULL );
		hr = m_pPresenterEngine->GetService( guidService, riid, ppvObject );

		
		// 失敗したら QueryInterface() へ丸投げ。

		if( FAILED( hr ) )
			hr = this->QueryInterface( riid, ppvObject );	// エラーチェックなし

		return hr;
	}
	

	// IMFTopologyServiceLookupClient 実装
	
	STDMETHODIMP CustomEVRPresenter::InitServicePointers( IMFTopologyServiceLookup *pLookup )
	{
		// from MSDN:
		//
		// EVR がプレゼンタを初期化するとき、プレゼンタの IMFTopologyServiceLookupClient::InitServicePointers を呼び出す。
		// 引数には、EVR の IMFTopologyServiceLookup へのポインタが格納されている。
		//
		// プレゼンタは、IMFTopologyServiceLookup::LookupService を呼び出して、EVR やミキサーからインターフェースポインタを取得する。
		//
		// ・サービス GUID が MR_VIDEO_RENDER_SERVICE なら、EVR へ問い合わせる。
		// ・サービス GUID が MR_VIDEO_MIXER_SERVICE なら、ミキサーへ問い合わせる。
		// 
		// プレゼンタは、InitServicePointers の実装の中で、EVR から以下のインターフェースを取得すること。
		//
		//	IMediaEventSink			プレゼンタが EVR にメッセージを送る方法を提供する。このインターフェースは DirectShow SDK で
		//							定義されているため、メッセージは Media Foundation イベントではなく DirectShow イベントパターンに従う。
		//
		//	IMFClock				EVR のクロックを表す。プレゼンタはこのインターフェスを使って、プレゼンテーションのサンプルをスケジュールする。
		//							EVR はクロック無しでも実行することができるため、このインターフェースは利用可能ではないかもしれない。
		//							そうなら、LookupService からのエラーコードを無視すること。
		//							また、クロックは IMFTimer も実装する。Media Foundation パイプラインでは、クロックは IMFPresentationClock も実装する。
		//
		// ミキサーからは以下のインターフェースを取得すること。
		//
		//	IMFTransform			プレゼンタがミキサーとやりとりできるようにする。
		//
		//	IMFVideoDeviceID		プレゼンターがミキサーのデバイス GUID を検証することができる。

		if( pLookup == NULL )
			return E_POINTER;

		HRESULT hr = S_OK;
		DWORD dwObjectCount = 0;

		AutoLock lock( m_ObjectLock );


		// 再生または停止中に初期化することは許されない。

		if( this->b再生中または一時停止中である() )
			return MF_E_INVALIDREQUEST;


		// COM の解放

		SAFE_COM_RELEASE( m_pClock );
		SAFE_COM_RELEASE( m_pMixer );
		SAFE_COM_RELEASE( m_pMediaEventSink );

		#pragma region [ EVR から IMFClock を取得する(→m_pClock)。]
		//-----------------
		dwObjectCount = 1;

		hr = pLookup->LookupService(      
			MF_SERVICE_LOOKUP_GLOBAL,   // 未使用。
			0,                          // 予約。
			MR_VIDEO_RENDER_SERVICE,    // 検索対象のサービス。
			__uuidof(IMFClock),         // 検索対象のインターフェース。
			(void**)&m_pClock,			// インターフェースの格納場所（配列）。
			&dwObjectCount              // 格納場所の配列の要素数。
			);

		// EVRはクロックを持っていないかも知れないので、hr の成功・失敗は問わない。
			
		//-----------------
		#pragma endregion

		#pragma region [ Mixer(IMFTransform)を取得する(→m_pMixer)。]
		//-----------------
		dwObjectCount = 1; 

		hr = pLookup->LookupService(
				MF_SERVICE_LOOKUP_GLOBAL,
				0,
				MR_VIDEO_MIXER_SERVICE,
				__uuidof( IMFTransform ),
				(void**) &m_pMixer,
				&dwObjectCount );

		if( FAILED( hr ) )
			goto failed;

		//-----------------
		#pragma endregion

		#pragma region [ Mixer から IMFVideoDeviceID を取得し、チェックする。]
		//-----------------
		if( FAILED( hr = this->tミキサのDeviceIDを確認し転送元矩形を設定する( m_pMixer ) ) )	// 確認するだけで保持はしない。
			goto failed;
		//-----------------
		#pragma endregion

		#pragma region [ EVR から IMediaEventSink を取得する(→m_pMediaEventSink)。]
		//-----------------
		dwObjectCount = 1;

		hr = pLookup->LookupService(
				MF_SERVICE_LOOKUP_GLOBAL,
				0,
				MR_VIDEO_RENDER_SERVICE,
				__uuidof( IMediaEventSink ),
				(void**) &m_pMediaEventSink,
				&dwObjectCount );

		if( FAILED( hr ) )
			goto failed;
		//-----------------
		#pragma endregion

		// 初期化に成功したのでステータスを stopped にする。
		this->m_RenderState.Set( RENDER_STATE_停止中 );
		return S_OK;

	failed:
		SAFE_COM_RELEASE( m_pClock );
		SAFE_COM_RELEASE( m_pMixer );
		SAFE_COM_RELEASE( m_pMediaEventSink );
		return hr;
	}
	STDMETHODIMP CustomEVRPresenter::ReleaseServicePointers()
	{
		// from MSDN:
		//
		// LoopupService で得られたインターフェースポインタが無効になる時、EVR は IMFTopologyServiceLookupClient::ReleaseServicePointers を呼び出す。
		// このメソッドの内部では、すべてのポインタが解放され、プレゼンタの状態は shutdown にセットされる。

		HRESULT hr = S_OK;


		// ステータスを「シャットダウン済み」に変える。

		this->m_RenderState.Set( RENDER_STATE_シャットダウン済み );


		// スケジュールされているすべてのサンプルの解放。
		
		hr = this->Flush();		// 失敗は無視。

		
		// メディアタイプをクリアならびに関連するリソース（サーフェイスなど）の解放。

		hr = this->SetMediaType( NULL );	// 失敗は無視。


		// InitServicePointers() で取得した各種インターフェースを解放する。

		SAFE_COM_RELEASE( this->m_pClock );
		SAFE_COM_RELEASE( this->m_pMixer );
		SAFE_COM_RELEASE( this->m_pMediaEventSink );

		return S_OK;
	}

	
	// IMFVideoPresenter 実装

	STDMETHODIMP CustomEVRPresenter::ProcessMessage( MFVP_MESSAGE_TYPE eMessage, ULONG_PTR ulParam )
	{
		// from MSDN:
		//
		// ProcessMessage メソッドは、EVR にとってプレゼンタと通信する優先的メカニズムである。
		// 以下のメッセージが定義されている。各メッセージの実装の詳細については後述する。


		HRESULT hr = S_OK;
		AutoLock lock( m_ObjectLock );
	
		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );


		// メッセージにより処理分岐。

		switch( eMessage )
		{

		// EVR がレンダリングパイプライン中にあるデータをフラッシュした。
		// プレゼンタはスケジューリングしているすべてのビデオフレームを破棄すべきである。
		case MFVP_MESSAGE_FLUSH:

			return this->Flush();
				

		case MFVP_MESSAGE_INVALIDATEMEDIATYPE:
			// ミキサーの出力メディアタイプが無効である。
			// プレゼンタはミキサーと新しいメディアタイプをネゴシエーションすべきである。
			// プレゼンタは、適切なタイプが見つかるまでは、いくつかの MFVP_MESSAGE_INVALIDATEMEDIATYPE メッセージを受け取る場合がある。

			return this->RenegotiateMediaType();


		case MFVP_MESSAGE_PROCESSINPUTNOTIFY:
			// ミキサーが新しい入力サンプルを受け取るたびに、EVR は MFVP_MESSAGE_PROCESSINPUTNOTIFY メッセージをプレゼンタに送信する。   
			// このメッセージは、ミキサーが提供可能なビデオフレームを持っているだろうことを示している。
			// 応答として、プレゼンタはミキサー上の IMFTransform::ProcessOutput を呼び出す。
			// メソッドが成功した場合は、プレゼンタはサンプルをプレゼンテーション用にスケジュールする。

			return this->ProcessInputNotify( );


		case MFVP_MESSAGE_BEGINSTREAMING:
			// ストリーミングが開始された。
			// このメッセージに対して特別なアクションを起こす必要はないが、リソースを割り当てる場合に使用することができる。

			return this->BeginStreaming();


		case MFVP_MESSAGE_ENDSTREAMING:
			// ストリーミングが終了した。
			// MFVP_MESSAGE_BEGINSTREAMING メッセージを受け取った時に割り当てたすべてのリソースを解放すること。

			return this->EndStreaming( );


		case MFVP_MESSAGE_ENDOFSTREAM:
			// プレゼンテーションが終了した。

			this->m_bEndStreaming = TRUE; 
        	return this->CheckEndOfStream();	// 終了してたら EC_COMPLETE イベントを EVR に送る。


		case MFVP_MESSAGE_STEP:
			// プレゼンタに、N フレーム前方へステップすることを要求している。
			// プレゼンタは次の N-1 個のフレームを破棄し、N 番目のフレームを表示すべきである。

			return this->PrepareFrameStep( ((DWORD)(ulParam)) );


		case MFVP_MESSAGE_CANCELSTEP:
			// フレームのステッピングがキャンセルされた。

			return this->CancelFrameStep();
		}

		return E_INVALIDARG;
	}
	STDMETHODIMP CustomEVRPresenter::GetCurrentMediaType( IMFVideoMediaType **ppMediaType )
	{
		// from MSDN:
		//
		// GetCurrentMediaType メソッドは、プレゼンタのメディアタイプを返す。
		// メディアタイプは IMFVideoMediaType ポインタとして返される。
		// IMFMediaType から IMFVideoMediaType を取得するには、QueryInterface を使う。

		HRESULT hr = S_OK;
		AutoLock lock( m_ObjectLock );

		if( ppMediaType == NULL )
			return E_POINTER;

		if( this->m_pMediaType == NULL )
			return MF_E_NOT_INITIALIZED;

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );

		
		// IMFMediaType から IMFVideoMediaType を取り出して返す。
		hr = this->m_pMediaType->QueryInterface( __uuidof(IMFVideoMediaType), (void**)&ppMediaType );	// エラーチェックなし
		return hr;
	}


	// IMFClockStateSink 実装

	STDMETHODIMP CustomEVRPresenter::OnClockStart( MFTIME hnsSystemTime, LONGLONG llClockStartOffset )
	{
		// from MSDN:
		//
		// 1. プレゼンタのステータスを started にセットする。
		//
		// 2. llClockStartOffset が PRESENTATION_CURRENT_POSITION ではないなら、プレゼンタのサンプルキューをフラッシュする。
		//    （これは MFVP_MESSAGE_FLUSH メッセージの受信と等しい。）
		//
		// 3. 前のフレームステップの要求がまだ保留中である場合は、要求を処理する。それ以外の場合は、ミキサーからの出力の処理を試みる。
		//
		// プレゼンタがシャットダウンされている場合、メソッドは失敗する。
		//

		HRESULT hr = S_OK;
		AutoLock lock( m_ObjectLock );

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );


		// (A) 止まっていない状態から開始された

		if( this->b再生中または一時停止中である() )
		{
			this->m_RenderState.Set( RENDER_STATE_再生中 );

			if( llClockStartOffset != PRESENTATION_CURRENT_POSITION )
			{
				// クロックがアクティブのときにクロック位置が変更された場合、それは seek 要求である。
				// このときには、すべての溜まっているサンプルを破棄する必要がある。

				tFAILEDなら帰還( hr = this->Flush() );
			}
		}

		// (B) 停止状態から開始された
		else
		{
			this->m_RenderState.Set( RENDER_STATE_再生中 );

			// 現在、フレームステッピングの途中にいるか、または、フレームステップキューにいくつかの待機サンプルがある可能性がある。
			// まずはこれらのケースを処理する。

			tFAILEDなら帰還( hr = this->StartFrameStep() );
		}

		
		// ミキサーから新しい出力サンプルの取得を試みる。

		this->ProcessOutputLoop();

		return S_OK;
	}
	STDMETHODIMP CustomEVRPresenter::OnClockStop( MFTIME hnsSystemTime )
	{
		// from MSDN:
		//
		// 1. プレゼンタのステータスを stopped にセットする。
		// 2. プレゼンターのサンプルキューをフラッシュする。
		// 3. 保留されているすべてのフレームステップ操作をキャンセルする。
		//
		// プレゼンタがシャットダウンされている場合、メソッドは失敗する。
		//

		HRESULT hr = S_OK;
		AutoLock lock( m_ObjectLock );

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );

		// ステータスを「停止中」にする。

		if( this->m_RenderState.Get() ==  RENDER_STATE_停止中 )
			return S_OK;	// すでに停止状態ならスルー。

		this->m_RenderState.Set( RENDER_STATE_停止中 );
		

		// サンプルキューをフラッシュする。

		tFAILEDなら帰還( hr = this->Flush() );


		// フレームステップ中なら、それを直ちにキャンセルする。

		if( this->m_FrameStep.state != FRAMESTEP_してない )
			tFAILEDなら帰還( hr = this->CancelFrameStep() );

		return S_OK;
	}
	STDMETHODIMP CustomEVRPresenter::OnClockPause( MFTIME hnsSystemTime )
	{
		// from MSDN:
		//
		// プレゼンタのステータスを paused にセットする。
		//
		// プレゼンタがシャットダウンされている場合、メソッドは失敗する。
		//

		HRESULT hr = S_OK;
		AutoLock lock( m_ObjectLock );

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );

		this->m_RenderState.Set( RENDER_STATE_一時停止中 );
		
		return S_OK;
	}
	STDMETHODIMP CustomEVRPresenter::OnClockRestart( MFTIME hnsSystemTime )
	{
		// from MSDN:
		//
		// このメソッドは、paused 状態のときにしか呼ばれてはいけない。
		// OnClockStart() と同じに扱うが、サンプルキューのフラッシュはしない。
		//
		// プレゼンタがシャットダウンされている場合、メソッドは失敗する。
		//

		AutoLock lock( m_ObjectLock );
		HRESULT hr = S_OK;

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );

		if( this->m_RenderState.Get() != RENDER_STATE_一時停止中 )
			return E_FAIL;	// 一時停止中じゃなきゃ呼んではダメ。
		
		
		// ステータスを「再生中」にする。
		
		this->m_RenderState.Set( RENDER_STATE_再生中 );


		// 現在、フレームステッピングの途中にいるか、または、フレームステップキューにいくつかの待機サンプルがある可能性がある。
		// まずはこれらのケースを処理する。

		tFAILEDなら帰還( hr = this->StartFrameStep() );


		// ミキサーから新しい出力サンプルの取得を試みる。

		this->ProcessOutputLoop();

		return S_OK;
	}
	STDMETHODIMP CustomEVRPresenter::OnClockSetRate( MFTIME hnsSystemTime, float fRate )
	{
		// from MSDN:
		//
		// 1. ゼロから非ゼロに変更された場合は、フレームステッピングをキャンセルする。
		// 2. 新しいクロックレートを格納する。クロックレートは、サンプルを表示されるタイミングに影響を与える。
		//
		// プレゼンタがシャットダウンされている場合、メソッドは失敗する。
		//
		// 備考：
		// ここでは、EVR は最大レートを保証するものと仮定する。
		// 本来は、プレゼンタの IMFRateSupport インターフェースから最大レートを確認する。

		AutoLock lock( m_ObjectLock );
		HRESULT hr = S_OK;

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );

		if( ( this->m_fRate == 0.0f ) && ( fRate != 0.0f ) )
		{
			tFAILEDなら帰還( hr = this->CancelFrameStep() );
			this->m_FrameStep.samples.Clear();
		}

		this->m_fRate = fRate;
		this->m_scheduler.tクロックレートを設定する( fRate );

		return S_OK;
	}


	// IMFRateSupport 実装

	STDMETHODIMP CustomEVRPresenter::GetSlowestRate( MFRATE_DIRECTION eDirection, BOOL bThin, float *pfRate )
	{
		// from MSDN:
		//
		// 最低速度がない場合は 0 を返す。
		// プレゼンタがシャットダウンされている場合、メソッドは失敗する。
		//

		if( pfRate == NULL )
			return E_POINTER;

		AutoLock lock( m_ObjectLock );
		HRESULT hr = S_OK;

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );

		// 最小レートは存在しないので、0 を返す。
		*pfRate = 0; 

		return S_OK;
	}
	STDMETHODIMP CustomEVRPresenter::GetFastestRate( MFRATE_DIRECTION eDirection, BOOL bThin, float *pfRate )
	{
		// from MSDN:
		//
		// 再生速度は、モニタのリフレッシュレートを超えてはならない。
		// すなわち、最大速度＝リフレッシュレート[Hz]÷ビデオのフレームレート[fps] である。
		// ビデオのフレームレートは、プレゼンタのメディアタイプ内に指定されている。
		//
		// 間伐再生するなら、再生速度は無制限である。この場合は FLT_MAX 値を返す。
		// 実際には、ソースとデコーダは間伐再生期間の要素の制限を受けるだろう。
		//
		// 逆再生する場合は、最大速度の負数を返す。
		//
		// プレゼンタがシャットダウンされている場合、メソッドは失敗する。
		//

		if( pfRate == NULL )
			return E_POINTER;

		AutoLock lock( m_ObjectLock );
		HRESULT hr = S_OK;

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );

		
		// 最大の前方レートを取得する。
		
		float fMaxRate = this->GetMaxRate( bThin );


		// 逆再生中ならレートを反転する。
		
		if( eDirection == MFRATE_REVERSE )
			fMaxRate = -fMaxRate;


		// レートを返す。

		*pfRate = fMaxRate;

		return S_OK;
	}
	STDMETHODIMP CustomEVRPresenter::IsRateSupported( BOOL bThin, float fRate, float *pfNearestSupportedRate )
	{
		// from MSDN:
		//
		// fRate の絶対値がプレゼンタの最大再生速度を超えた場合、MF_E_UNSUPPORTED_RATE を返す。
		// 最大速度については、GetFastestRate で述べた方法で計算する。
		//
		// プレゼンタがシャットダウンされている場合、メソッドは失敗する。
		//

		HRESULT hr = S_OK;

		AutoLock lock( m_ObjectLock );

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す( ) );

		float fMaxRate = 0.0f;
		float fNearestRate = fRate;   // fRate をサポートする場合は、それは近似値である。


		// 最大前方レートを確認する。
		// 備考：最小レートは存在しない（取得すると0が返される）。

		fMaxRate = this->GetMaxRate( bThin );


		// 指定されたレートが、最大前方レートよりも大きい場合……

		if( fabsf( fRate ) > fMaxRate )
		{
			// 近似値として、指定された値の絶対値が最大レートに収められる。

			fNearestRate = fMaxRate;	
			if( fRate < 0 )
				fNearestRate = -fNearestRate;		// 負のレートが指定された場合は反転する。

			hr = MF_E_UNSUPPORTED_RATE;		// このエラーが返される。
		}


		// サポートする値として、近似値を返す。

		if( pfNearestSupportedRate != NULL )	// NULL なら無視。
			*pfNearestSupportedRate = fNearestRate;

		return hr;
	}


	// IMFVideoDeviceID 実装

	STDMETHODIMP CustomEVRPresenter::GetDeviceID( IID* pDeviceID )
	{
		// from MSDN:
		//
		// 標準的なミキサーやプレゼンタは、どちらも Direct3D9 を使用するため、デバイスGUIDは IID_IDirect3DDevice9 である。
		// 標準的なミキサーとカスタムプレゼンタを連携させたい場合は、プレゼンタのデバイス GUID は IID_IDirect3DDevice9 でなければならない。
		// 両方のコンポーネントを置き換えるなら、新しいデバイスGUIDを定義できる。
		// このメソッドは、プレゼンタがシャットダウンされている時でも成功する必要がある。

		if( pDeviceID == NULL )
			return E_POINTER;

		*pDeviceID = __uuidof(IDirect3DDevice9);

		return S_OK;
	}


	// IMFVideoDisplayControl 実装

	STDMETHODIMP CustomEVRPresenter::SetVideoPosition( const MFVideoNormalizedRect *pnrcSource, const LPRECT prcDest )
	{
		HRESULT hr = S_OK;

//		AutoLock lock( m_ObjectLock );		デッドロックの危険あり

		#pragma region [ 条件チェック ]
		//-----------------
		if( pnrcSource == NULL && prcDest == NULL )
			return E_POINTER;

		if( pnrcSource )
		{
			if( ( pnrcSource->left > pnrcSource->right ) || ( pnrcSource->top > pnrcSource->bottom ) )
				return E_INVALIDARG;

			if( ( pnrcSource->left < 0 ) || ( pnrcSource->right > 1 ) || ( pnrcSource->top < 0 ) || ( pnrcSource->bottom > 1 ) )
				return E_INVALIDARG;
		}

		if( prcDest )
		{
			if( ( prcDest->left > prcDest->right ) || ( prcDest->top > prcDest->bottom ) )
				return E_INVALIDARG;
		}
		//-----------------
		#pragma endregion

		#pragma region [ 転送元矩形の指定がある場合は、ミキサーに登録する。]
		//-----------------
		if( pnrcSource )
		{
			this->m_nrcSource = *pnrcSource;

			if( this->m_pMixer )
			{
				tFAILEDなら帰還( hr = this->SetMixerSourceRect( this->m_pMixer, this->m_nrcSource ) );
			}
		}
		//-----------------
		#pragma endregion

		#pragma region [ 転送先矩形の指定がある場合は、更新する。]
		//-----------------
		if( prcDest )
		{
			RECT rcOldDest = this->m_rcDest;

			if( !::EqualRect( &rcOldDest, prcDest ) )		// 矩形が変更された場合。
			{
				this->m_rcDest = *prcDest;

				//// 新しいメディアタイプをミキサーとネゴシエーションする。

				//if( this->m_pMixer )
				//{
				//	hr = this->RenegotiateMediaType();
				//	
				//	if( hr == MF_E_TRANSFORM_TYPE_NOT_SET )
				//	{
				//		// This error means that the mixer is not ready for the media type.
				//		// Not a failure case -- the EVR will notify us when we need to set
				//		// the type on the mixer.
				//		hr = S_OK;
				//	}
				//	else
				//	{
				//		if( FAILED( hr ) )
				//			return hr;

				//		// The media type changed. Request a repaint of the current frame.
				//		this->m_bRepaint = TRUE;
				//		this->ProcessOutput(); // Ignore errors, the mixer might not have a video frame.
				//	}
				//}
			}
		}
		//-----------------
		#pragma endregion

		return hr;
	}
	STDMETHODIMP CustomEVRPresenter::GetVideoPosition( MFVideoNormalizedRect* pnrcSource, LPRECT prcDest )
	{
		AutoLock lock( m_ObjectLock );

		if( pnrcSource == NULL || prcDest == NULL )
			return E_POINTER;

		*pnrcSource = this->m_nrcSource;
		*prcDest = this->m_rcDest;

		return S_OK;
	}
	STDMETHODIMP CustomEVRPresenter::SetVideoWindow( HWND hwndVideo )
	{
		AutoLock lock( m_ObjectLock );

		if( !::IsWindow( hwndVideo ) )
			return E_INVALIDARG;

		if( this->m_pPresenterEngine == NULL )
			return MF_E_NOT_INITIALIZED;

		return this->m_pPresenterEngine->SetVideoWindow( hwndVideo );
	}
	STDMETHODIMP CustomEVRPresenter::GetVideoWindow( HWND* phwndVideo )
	{
		AutoLock lock( m_ObjectLock );

		if( phwndVideo == NULL )
			return E_POINTER;

		if( this->m_pPresenterEngine == NULL )
		{
			*phwndVideo = NULL;
			return MF_E_NOT_INITIALIZED;
		}

		*phwndVideo = this->m_pPresenterEngine->GetVideoWindow();

		return S_OK;
	}
	STDMETHODIMP CustomEVRPresenter::RepaintVideo()
	{
		AutoLock lock( m_ObjectLock );

		HRESULT hr = S_OK;

		tFAILEDなら帰還( hr = this->tシャットダウン状態ならエラーを返す() );


		// まだサンプルがないなら無視する。

		if( this->m_bPrerolled )
		{
			this->m_bRepaint = TRUE;
			return this->ProcessOutput();
		}

		return hr;
	}
	STDMETHODIMP CustomEVRPresenter::SetRenderingPrefs( DWORD dwRenderFlags )
	{
		switch( dwRenderFlags )
		{

		case MFVideoRenderPrefs_DisplayChange:
			
			if( this->m_pMediaEventSink )		// EVR に対して、D3Dデバイスが変更されたことを示す EC_DISPLAY_CHANGED を通知する。
				return this->m_pMediaEventSink->Notify( EC_DISPLAY_CHANGED, 0, 0 );

		case MFVideoRenderPrefs_DisplaySample:
			
			if( this->m_pPresenterEngine )		// 最新のサンプルを描画する。
				this->m_pPresenterEngine->tサンプルを描画する( NULL, &this->m_rcDest, this->uテクスチャの透明度, this->uテクスチャの加算合成 );
			
			return S_OK;

		case MFVideoRenderPrefs_RequestNextSample:

			if( this->m_pPresenterEngine )		// 次のサンプルを要求する。
				this->m_pPresenterEngine->t次のサンプルを要求する();

			return S_OK;
		}

		return E_INVALIDARG;
	}
	STDMETHODIMP CustomEVRPresenter::GetNativeVideoSize( SIZE* pszVideo, SIZE* pszARVideo )
	{
		if( pszVideo )
			*pszVideo = this->m_pPresenterEngine->GetVideoSize();

		return S_OK;
	}


	// IMFActivate 実装

	STDMETHODIMP CustomEVRPresenter::ActivateObject( REFIID riid, void **ppv )
	{
		return CustomEVRPresenter::CreateInstance( NULL, riid, ppv );
	}
	STDMETHODIMP CustomEVRPresenter::ShutdownObject()
	{
		return S_OK;
	}


	// IMFAttributes 実装

	STDMETHODIMP CustomEVRPresenter::GetUINT32( REFGUID guidKey, UINT32 *punValue )
	{
		if( punValue == NULL )
			return E_POINTER;

		if( IsEqualGUID( guidKey, GUIDofテクスチャの透明度 ) )
		{
			*punValue = this->uテクスチャの透明度;
		}
		else if( IsEqualGUID( guidKey, GUIDofテクスチャの加算合成 ) )
		{
			*punValue = this->uテクスチャの加算合成;
		}
		else
		{
			return MF_E_INVALID_KEY;
		}

		return S_OK;
	}
	STDMETHODIMP CustomEVRPresenter::SetUINT32( REFGUID guidKey, UINT32 unValue )
	{
		if( IsEqualGUID( guidKey, GUIDofテクスチャの透明度 ) )
		{
			this->uテクスチャの透明度 = unValue;

			if( this->uテクスチャの透明度 > 255 )
				this->uテクスチャの透明度 = 255;
		}
		else if( IsEqualGUID( guidKey, GUIDofテクスチャの加算合成 ) )
		{
			this->uテクスチャの加算合成 = unValue;
		}
		else
		{
			return MF_E_INVALID_KEY;
		}

		return S_OK;
	}


// protected:

	CustomEVRPresenter::CustomEVRPresenter( HRESULT &hr ) :
		m_SampleFreeCB( this, &CustomEVRPresenter::OnSampleFree )
	{
		this->m_RenderState.Set( RENDER_STATE_シャットダウン済み );
		this->m_pPresenterEngine = NULL;
		this->m_pClock = NULL;
		this->m_pMixer = NULL;
		this->m_pMediaEventSink = NULL;
		this->m_pMediaType = NULL;
		this->m_bSampleNotify = FALSE;
		this->m_bRepaint = FALSE;
		this->m_bEndStreaming = FALSE;
		this->m_bPrerolled = FALSE;
		this->m_fRate = 1.0f;
		this->m_TokenCounter = 0;

		hr = S_OK;

		this->n参照カウンタ = 1;
		this->g_DefaultFrameRate.Numerator  = 30;
		this->g_DefaultFrameRate.Denominator = 1;
		this->uテクスチャの透明度 = 255;
		this->uテクスチャの加算合成 = 0;
		::SetRectEmpty( &this->m_rcDest );

		// 転送元初期矩形 = (0,0,1,1)

		m_nrcSource.top = 0;
		m_nrcSource.left = 0;
		m_nrcSource.bottom = 1;
		m_nrcSource.right = 1;


		// プレゼンタエンジンを生成。

		this->m_pPresenterEngine = new PresenterEngine( hr );

		if( this->m_pPresenterEngine == NULL)
			hr = E_OUTOFMEMORY;

		
		// 失敗したらここで終了。

		if( FAILED( hr ) )
		{
			SAFE_DELETE( this->m_pPresenterEngine );
			return;
		}


		// スケジューラに、プレゼンタエンジンへのコールバックを設定。

		this->m_scheduler.tコールバックを設定する( this->m_pPresenterEngine );
	}
	CustomEVRPresenter::~CustomEVRPresenter()
	{
		SAFE_COM_RELEASE( this->m_pClock );
		SAFE_COM_RELEASE( this->m_pMixer );
		SAFE_COM_RELEASE( this->m_pMediaEventSink );
		SAFE_COM_RELEASE( this->m_pMediaType );
		SAFE_DELETE( this->m_pPresenterEngine );
	}

	HRESULT CustomEVRPresenter::tシャットダウン状態ならエラーを返す()
	{
		return ( this->m_RenderState.Get() == RENDER_STATE_シャットダウン済み ) ? MF_E_SHUTDOWN : S_OK;
	}
	BOOL	CustomEVRPresenter::b再生中または一時停止中である()
	{
		RENDER_STATE state = this->m_RenderState.Get();
		return ( ( RENDER_STATE_再生中 == state ) || ( RENDER_STATE_一時停止中 == state ) );
	}
	BOOL	CustomEVRPresenter::IsScrubbing() const
	{
		return ( m_fRate == 0.0f );
	}
	HRESULT	CustomEVRPresenter::tEVRにイベントを通知する( long EventCode, LONG_PTR Param1, LONG_PTR Param2 )
	{
		// from MSDN:
		//
		// プレゼンタは、EVR に様々なイベントを通知しなければならない。
		// これを行うには、EVR の IMediaEventSink を使用する。
		// このインターフェースは EVR がプレゼンタの IMFTopologyServiceLookupClient::InitServicePointers() を呼び出した時に取得できる。
		// （IMediaEventSink インターフェースはもともと DirectShow のインターフェースであるが、Media Foundation でも使用されている。）
		//

		HRESULT hr = S_OK;

		if( this->m_pMediaEventSink == NULL )
			return MF_E_NOT_INITIALIZED;

		return this->m_pMediaEventSink->Notify( EventCode, Param1, Param2 );
	}
	float	CustomEVRPresenter::GetMaxRate( BOOL bThin )
	{
		// (A) Thinning = TRUE の場合：
		// 　最大レートとして FLT_MAX を返す。
		//
		// (B) Thinning = FALSE の場合：
		// 　正しいフレームレートとモニタのリフレッシュレートを取得できる場合は、そのレートを最大レートとして返す。
		// 　それ以外の場合は FLT_MAX を返す。

		float   fMaxRate = FLT_MAX;
		MFRatio fps = { 0, 0 };
		UINT    uリフレッシュレートHz = 0; 

		if( !bThin && ( this->m_pMediaType != NULL ) )
		{
			if( FAILED( tメディアタイプの属性からフレームレートを返す( this->m_pMediaType, &fps ) ) )
			{
				fps.Numerator = 0;		// 取得失敗
				fps.Denominator = 0;
			}

			uリフレッシュレートHz = this->m_pPresenterEngine->RefreshRate();

			if( fps.Denominator && fps.Numerator && uリフレッシュレートHz )
			{
				// 最大レート = リフレッシュレート÷フレームレート
				fMaxRate = (float) ::MulDiv( uリフレッシュレートHz, fps.Denominator, fps.Numerator );
			}
		}

		return fMaxRate;
	}
	float	CustomEVRPresenter::MFOffsetToFloat( const MFOffset& offset )
	{
		return (float)offset.value + (float)offset.value / 65536.0f;
	}

	// ミキサー操作

	HRESULT	CustomEVRPresenter::tミキサのDeviceIDを確認し転送元矩形を設定する( IMFTransform *pMixer )
	{
		HRESULT hr = S_OK;
		IID deviceID = GUID_NULL;
		IMFVideoDeviceID *pDeviceID = NULL;

		try
		{
			// ミキサーが同じデバイスIDを持っていることを確認する。
				
			tFAILEDなら帰還( hr = pMixer->QueryInterface( __uuidof( IMFVideoDeviceID ), (void**) &pDeviceID ) );	// ミキサーから IMFVideoDeviceID を取得。
			tFAILEDなら帰還( hr = pDeviceID->GetDeviceID( &deviceID ) );											// ミキサーからデバイスIDを取得。

			if( !::IsEqualGUID( deviceID, __uuidof( IDirect3DDevice9 ) ) )
				return MF_E_INVALIDREQUEST;		// ミキサーの DeviceID が IDirect3DDevice9 ではない。

			// ミキサの転送元矩形を設定する。

			tFAILEDなら帰還( hr = this->SetMixerSourceRect( pMixer, this->m_nrcSource ) );
		}
		finally
		{
			SAFE_COM_RELEASE( pDeviceID );
		}

		return S_OK;
	}

	// フォーマット

	HRESULT	CustomEVRPresenter::CreateOptimalVideoType( IMFMediaType *pProposedType, IMFMediaType **ppOptimalType )
	{
		HRESULT hr = S_OK;
	
		RECT rcOutput;
		::ZeroMemory( &rcOutput, sizeof(rcOutput) );
		
		MFVideoArea displayArea;
		::ZeroMemory( &displayArea, sizeof(displayArea) );
		
		VideoTypeBuilder *pmtOptimal = NULL;

		try
		{
			// メディアタイプ操作用の VideoTypeBuilder を作成。
			tFAILEDなら帰還( hr = VideoTypeBuilder::Create( &pmtOptimal ) );

			// 指定されたメディアタイプから複写。
			tFAILEDなら帰還( hr = pmtOptimal->CopyFrom( pProposedType ) );

			// MF_MT_PIXEL_ASPECT_RATIO 属性へのディスプレイのPARを (1:1) に設定。
			tFAILEDなら帰還( hr = pmtOptimal->SetPixelAspectRatio( 1, 1 ) );

			// 拡張色情報の設定。ここでは BT.709 を使用する。
			tFAILEDなら帰還( hr = pmtOptimal->SetYUVMatrix( MFVideoTransferMatrix_BT709 ) );
			tFAILEDなら帰還( hr = pmtOptimal->SetTransferFunction( MFVideoTransFunc_709 ) );
			tFAILEDなら帰還( hr = pmtOptimal->SetVideoPrimaries( MFVideoPrimaries_BT709 ) );
			tFAILEDなら帰還( hr = pmtOptimal->SetVideoNominalRange( MFNominalRange_16_235 ) );
			tFAILEDなら帰還( hr = pmtOptimal->SetVideoLighting( MFVideoLighting_dim ) );
				
			// ソースのオリジナルサイズをエンジンに登録。
			UINT32 w, h;
			tFAILEDなら帰還( hr = pmtOptimal->GetFrameDimensions( &w, &h ) );	// ソース動画のオリジナルサイズの取得
			this->m_pPresenterEngine->SetVideoSize( w, h );						// 登録

			// 出力矩形 rcOutput をソースのオリジナルサイズに設定。
			rcOutput.right = w;
			rcOutput.bottom = h;

			// MF_MT_FRAME_SIZE 属性へ Direct3D サーフェイスに希望する幅と高さを設定。
			tFAILEDなら帰還( hr = pmtOptimal->SetFrameDimensions( rcOutput.right, rcOutput.bottom ) );

			// MF_MT_PAN_SCAN_ENABLED 属性へ FALSE 値を設定。
			tFAILEDなら帰還( hr = pmtOptimal->SetPanScanEnabled( FALSE ) );

			// MF_MT_GEOMETRIC_APERTURE 属性には、Direct3D サーフェイス内の矩形をセットする。
			// ミキサーが出力フレームを生成すると、ソース画像がこの矩形に描画される。
			// この矩形は、サーフェイスよりも大きくしたり、サーフェイス内のサブ矩形にすることもできる。

			displayArea = MakeArea( 0, 0, rcOutput.right, rcOutput.bottom );
			tFAILEDなら帰還( hr = pmtOptimal->SetGeometricAperture( displayArea ) );
			tFAILEDなら帰還( hr = pmtOptimal->SetPanScanAperture( displayArea ) );
			tFAILEDなら帰還( hr = pmtOptimal->SetMinDisplayAperture( displayArea ) );

			// 完成したメディアタイプを ppOptimalType へ出力。
			tFAILEDなら帰還( hr = pmtOptimal->GetMediaType( ppOptimalType ) );
		}
		finally
		{
			SAFE_COM_RELEASE( pmtOptimal );
		}

		return S_OK;
	}
	HRESULT	CustomEVRPresenter::SetMediaType( IMFMediaType *pMediaType )
	{
		//Debug::Assert( pMediaType != NULL );		メディアタイプをクリアするには NULL を指定する仕様。

		#pragma region [ メディアタイプをクリアする場合。]
		//-----------------
		if( pMediaType == NULL )
		{
			// メディアタイプのクリアは、どんな状態であっても可能（シャットダウン状態含む）。
			SAFE_COM_RELEASE( m_pMediaType );
			this->ReleaseResources();
			return S_OK;	// クリア完了。
		}
		//-----------------
		#pragma endregion

		HRESULT hr = S_OK;
		MFRatio fps = { 0, 0 };
		CVideoSampleList sampleQueue;
	
		try
		{
			tFAILEDなら帰還( hr = tシャットダウン状態ならエラーを返す() );

			// メディアタイプに変更がないなら何もせず終了。
			if( this->AreMediaTypesEqual( m_pMediaType, pMediaType ) )	// この関数の引数には NULL を指定可能。
				return S_OK;

			// 既存のメディアタイプを解放。
			SAFE_COM_RELEASE( m_pMediaType );
			this->ReleaseResources();
			
			// 新しいメディアタイプを使ってプレゼンタエンジンを初期化する。具体的には新しいサンプルを確保させる。
			tFAILEDなら帰還( hr = m_pPresenterEngine->CreateVideoSamples( pMediaType, sampleQueue ) );

			// サンプルキュー内のサンプルすべてについて、MFSamplePresenter_SampleCounter 属性にトークンカウンタを設定する。
			// この数値は、サンプルが無効になった場合に増加し、サンプルが無効になったことを示すことに使う。

			IMFSample *pSample = NULL;
			for( CVideoSampleList::POSITION pos = sampleQueue.FrontPosition( ); pos != sampleQueue.EndPosition( ); pos = sampleQueue.Next( pos ) )
			{
				tFAILEDなら帰還( hr = sampleQueue.GetItemPos( pos, &pSample ) );
				tFAILEDなら帰還( hr = pSample->SetUINT32( MFSamplePresenter_SampleCounter, m_TokenCounter ) );
				SAFE_COM_RELEASE( pSample );
			}

			// サンプルプールにサンプルキューのサンプルを追加する。
			tFAILEDなら帰還( hr = m_SamplePool.t初期化する( sampleQueue ) );

			// スケジューラにフレームレートを設定する。
			if( SUCCEEDED( FDK::tメディアタイプの属性からフレームレートを返す( pMediaType, &fps ) ) && ( fps.Numerator != 0 ) && ( fps.Denominator != 0 ) )
				this->m_scheduler.tフレームレートを設定する( fps );
			else
				this->m_scheduler.tフレームレートを設定する( g_DefaultFrameRate );

			// メディアタイプを保存する。
			m_pMediaType = pMediaType;
			m_pMediaType->AddRef();
			
			hr = S_OK;
		}
		finally
		{
			if( FAILED( hr ) )
				this->ReleaseResources();
		}
		return hr;
	}
	HRESULT	CustomEVRPresenter::IsMediaTypeSupported( IMFMediaType *pMediaType )
	{
		HRESULT                 hr = S_OK;
		VideoTypeBuilder		*pmtProposed = NULL;

		try
		{
			// メディアタイプのラッパーオブジェクトを作成。
			tFAILEDなら帰還( hr = VideoTypeBuilder::Create( pMediaType, &pmtProposed ) );

			// 圧縮フォーマットは拒否する。
			BOOL b圧縮 = FALSE;
			tFAILEDなら帰還( hr = pmtProposed->IsCompressedFormat( &b圧縮 ) );
			if( b圧縮 )
				return MF_E_INVALIDMEDIATYPE;

			// フォーマットチェック。
			D3DFORMAT d3dFormat = D3DFMT_UNKNOWN;
			tFAILEDなら帰還( hr = pmtProposed->GetFourCC( (DWORD*) &d3dFormat ) );
			tFAILEDなら帰還( hr = m_pPresenterEngine->t指定されたフォーマットが利用可能か調べる( d3dFormat ) );

			// インタレースモードはプログレッシブのみ許可する。インタレースは不許可。
			MFVideoInterlaceMode InterlaceMode = MFVideoInterlace_Unknown;
			tFAILEDなら帰還( hr = pmtProposed->GetInterlaceMode( &InterlaceMode ) );
			if( InterlaceMode != MFVideoInterlace_Progressive )
				return MF_E_INVALIDMEDIATYPE;

			// フレームサイズに対する各種アパチャを設定する。
			// メディアタイプが必要とするアパチャであるかどうかについては単に無視する。
			
			UINT32 width = 0, height = 0;
			MFVideoArea VideoCropArea;

			tFAILEDなら帰還( hr = pmtProposed->GetFrameDimensions( &width, &height ) );

			if( SUCCEEDED( pmtProposed->GetPanScanAperture( &VideoCropArea ) ) )
				this->ValidateVideoArea( VideoCropArea, width, height );	// エラーチェックなし

			if( SUCCEEDED( pmtProposed->GetGeometricAperture( &VideoCropArea ) ) )
				this->ValidateVideoArea( VideoCropArea, width, height );	// 同上

			if( SUCCEEDED( pmtProposed->GetMinDisplayAperture( &VideoCropArea ) ) )
				this->ValidateVideoArea( VideoCropArea, width, height );	// 同上
		}
		finally
		{
			SAFE_COM_RELEASE( pmtProposed );
		}
		return S_OK;
	}

	// メッセージハンドリング

	HRESULT	CustomEVRPresenter::Flush()
	{
		HRESULT hr = S_OK;

		this->m_bPrerolled = FALSE;

		// スケジューラにフラッシュを指示する。
		// スケジューラは表示時刻を待っているサンプルを持っているかも知れない。
		// この呼び出しは、スケジューラスレッドがすべてのサンプルを破棄するまでブロックする。
		tFAILEDなら帰還( hr = m_scheduler.tサンプルキューをフラッシュする() );

		// フレームステップキューをフラッシュする。
		m_FrameStep.samples.Clear();

		// 停止中なら黒で再描画する。
		if( m_RenderState.Get() == RENDER_STATE_停止中 )
			tFAILEDなら帰還( hr = m_pPresenterEngine->tサンプルを受け取る( NULL, 0 ) );

		return S_OK; 
	}
	HRESULT	CustomEVRPresenter::RenegotiateMediaType()
	{
		// プレゼンタは、EVR から MFVP_MESSAGE_INVALIDATEMEDIATYPE メッセージを受け取った時はいつでも、
		// ミキサーの出力フォーマットを以下のように設定しなければならない。

		HRESULT hr = S_OK;
		IMFMediaType *pMixerType = NULL;
		IMFMediaType *pOptimalType = NULL;
		IMFVideoMediaType *pVideoType = NULL;

		// ミキサーがないなら帰還。
		if( !m_pMixer )
			return MF_E_INVALIDREQUEST;

		try
		{
			// ミキサーが対応可能なすべての出力タイプについて……

			DWORD iTypeIndex = 0;

			while( hr != MF_E_NO_MORE_TYPES )
			{
				SAFE_COM_RELEASE( pMixerType );
				SAFE_COM_RELEASE( pOptimalType );


				//-----------------
				// (1)ミキサーの IMFTransform::GetOutputAvailableType() を呼び出し、可能な出力タイプを取得する。
				//    このタイプは、ミキサーが入力ストリームから生成することができるフォーマットと、グラフィックデバイスのビデオプロセッシング能力を記述する。

				hr = m_pMixer->GetOutputAvailableType( 0, iTypeIndex++, &pMixerType );
				if( FAILED( hr ) )	// 指定したインデックスのタイプがなかったら、ミキサーは MF_E_NO_MORE_TYPES を返す。
					break;			// そこでループ終了。


				//-----------------
				// (2)プレゼンタがこのメディアタイプをレンダリングフォーマットとして使用できるか否か確認する。
				//    タイプが許容できない場合は、ステップ(1) に戻り、ミキサーの次の推奨タイプを取得する。

				if( FAILED( hr = this->IsMediaTypeSupported( pMixerType ) ) )
					continue;


				//-----------------
				// (3)オリジナルタイプのクローンとして新しいメディアタイプを作成する。

				if( FAILED( hr = this->CreateOptimalVideoType( pMixerType, &pOptimalType ) ) )
					continue;


				//-----------------
				// (4)ミキサーが修正された出力タイプを受け入れるかどうかテストするために、MFT_SET_TYPE_TEST_ONLY フラグを指定して
				//    IMFTransform::SetOutputType() を呼び出す。
				//    ミキサーがタイプを拒絶したら、ステップ(1)に戻って次のタイプを取得する。

				if( FAILED( hr = m_pMixer->SetOutputType( 0, pOptimalType, MFT_SET_TYPE_TEST_ONLY ) ) )
					continue;


				//-----------------
				// (5)Direct3D サーフェイスのプールを割り当てる。
				//    ミキサーはこれらのサーフェイスを使って合成済みビデオフレームを描画する。

				if( FAILED( hr = this->SetMediaType( pOptimalType ) ) )
					continue;


				//-----------------
				// (6)フラグなしで SetOutputType を呼び出し、ミキサー上に出力タイプをセットする。
				//    ステップ(4)での SetOutputType() が成功している場合、この呼び出しも成功するだろう。

				if( FAILED( hr = m_pMixer->SetOutputType( 0, pOptimalType, 0 ) ) )
				{
					this->SetMediaType( NULL );	// ここまで来てアウトだったら、メディアタイプをクリアする。
					continue;
				}

				break;	// 見つけた
			}
		}
		finally
		{
			SAFE_COM_RELEASE( pMixerType );
			SAFE_COM_RELEASE( pOptimalType );
			SAFE_COM_RELEASE( pVideoType );
		}
		return S_OK;
	}
	HRESULT	CustomEVRPresenter::ProcessInputNotify()
	{
		HRESULT hr = S_OK;

		// ミキサーが新しいサンプルを作ったことを通知するフラグを立てる。
		this->m_bSampleNotify = TRUE;

		// メディアタイプが未設定ならエラー。
		if( this->m_pMediaType == NULL )
			return MF_E_TRANSFORM_TYPE_NOT_SET;

		this->ProcessOutputLoop();
		return S_OK;
	}
	HRESULT	CustomEVRPresenter::BeginStreaming()
	{
		return this->m_scheduler.tスケジューラをワーカスレッドで開始する( this->m_pClock );
	}
	HRESULT	CustomEVRPresenter::EndStreaming()
	{
		return this->m_scheduler.tスケジューラを停止しワーカスレッドを終了する();
	}
	HRESULT	CustomEVRPresenter::CheckEndOfStream()
	{
		if( !m_bEndStreaming )
			return S_OK;	// EVR は MFVP_MESSAGE_ENDOFSTREAM を送信しないので注意。

		if( m_bSampleNotify )
			return S_OK;	// ミキサーがまだサンプルを持っている。

		if( m_SamplePool.bサンプルが存在している() )
			return S_OK;	// 描画用にスケジュールされているサンプルがある。

		// すべて完了しているので、EVR に終了を告げる。

		tEVRにイベントを通知する( EC_COMPLETE, (LONG_PTR) S_OK, 0 );
		m_bEndStreaming = FALSE;

		return S_OK;
	}

	// サンプル管理

	void	CustomEVRPresenter::ProcessOutputLoop()
	{
		HRESULT hr = S_OK;

		// 可能な限り多くのサンプルを処理する。

		while( hr == S_OK )
		{
			// ミキサーが入力サンプルを持っていないならループを抜ける。
			if( !m_bSampleNotify )
			{
				hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
				break;
			}

			// サンプルを処理する。サンプルを処理しなかった場合は S_FALSE が返されるので、ループを抜ける。
			hr = this->ProcessOutput();
		}

		// ミキサーが入力データの処理中である場合、ストリームが終了していないか確認する。
		if( hr == MF_E_TRANSFORM_NEED_MORE_INPUT )
			hr = this->CheckEndOfStream();
	}
	HRESULT	CustomEVRPresenter::ProcessOutput()
	{
		HRESULT     hr = S_OK;
		DWORD       dwStatus = 0;
		LONGLONG    mixerStartTime = 0, mixerEndTime = 0;
		MFTIME      systemTime = 0;
		BOOL        bRepaint = m_bRepaint;
		IMFSample	*pSample = NULL;

		MFT_OUTPUT_DATA_BUFFER dataBuffer;
		::ZeroMemory( &dataBuffer, sizeof( dataBuffer ) );

		try
		{
			//-----------------
			// 	1. クロックステータスを確認する。
			//     クロックが paused の場合、MFVP_MESSAGE_PROCESSINPUTNOTIFY メッセージは、これが最初のビデオフレームでない限り無視すること。
			//     クロックが running である、またはこれが最初のビデオフレームである場合は、続ける。

			if( m_RenderState.Get() != RENDER_STATE_再生中 &&	// 再生を開始していない、かつ、
				!m_bRepaint &&									// 再描画要求もない、かつ、
				m_bPrerolled )									// 初めての描画じゃないなら、帰る。
			{
				return S_FALSE;
			}

			if( m_pMixer == NULL )
				return MF_E_INVALIDREQUEST;		// 描画条件を満たしてるのにミキサーがないのはおかしい。


			//-----------------
			// 2. 利用可能なサンプルのキューからサンプルを得る。
			//    キューが空の場合、それはすべての割り当て済みサンプルが表示スケジュールされていることを意味する。
			//    この場合、MFVP_MESSAGE_PROCESSINPUTNOTIFY メッセージはこの時点で無視する。
			//    次のサンプルが利用可能になったとき、ここに記載されている手順を繰り返す。

			if( ( hr = m_SamplePool.tプールからサンプルを取り出す( &pSample ) ) == MF_E_SAMPLEALLOCATOR_EMPTY )
				return S_FALSE;		// 正常系

			tFAILEDなら帰還( hr );	// 準正常系


			//-----------------
			// 	3. 再描画要求がある場合は、サンプルに最後のサンプル時刻を設定する。
			//     再描画要求がない場合は、サンプルの描画希望時刻をクリアする。

			// (a) 再描画要求の場合
			if( m_bRepaint )
			{
				tFAILEDなら帰還( hr = this->SetDesiredSampleTime( pSample, m_scheduler.ll最後のサンプル時刻(), m_scheduler.llフレーム間隔() ) );
				m_bRepaint = FALSE;		// 完了。再描画要求をクリア。
			}

			// (b) 再描画要求ではない場合
			else
			{
				// サンプルの描画希望時刻をクリアする。ミキサーは次のフレームでこれを扱う。
				tFAILEDなら帰還( hr = this->ClearDesiredSampleTime( pSample ) );

				// （オプション）クロックが利用可能である場合、現在のクロックタイムを取得する。
				if( m_pClock )
				{
					// レイテンシ用に、ProcessOutput() が開始された時刻を記憶しておく。
					(void) m_pClock->GetCorrelatedTime( 0, &mixerStartTime, &systemTime );
				}
			}


			//-----------------
			// 4. ミキサーの IMFTransform::ProcessOutput を呼び出す。
			//    成功した場合、サンプルはビデオフレームを含んでいる。
			//    失敗した場合、返されたコードをチェックすること。

			dataBuffer.dwStreamID = 0;
			dataBuffer.pSample = pSample;
			dataBuffer.dwStatus = 0;

			hr = m_pMixer->ProcessOutput( 0, 1, &dataBuffer, &dwStatus );

			if( FAILED( hr ) )
			{
				#pragma region [ (a) 失敗 ]
				//-----------------

				HRESULT hr2 = m_SamplePool.tサンプルをプールに戻す( pSample );

				if( FAILED( hr2 ) )
					tFAILEDなら帰還( hr = hr2 );

				// ProcessOutput から得られる以下のエラーコードは致命的な失敗ではない。

				if( hr == MF_E_TRANSFORM_TYPE_NOT_SET )
				{
					// ミキサーは新しいメディアタイプを要求している。
					// このエラーコードを取得した場合、ミキサーの出力タイプをネゴシエーションすること。

					hr = this->RenegotiateMediaType();
				}
				else if( hr == MF_E_TRANSFORM_STREAM_CHANGE )
				{
					// ミキサーの出力タイプが無効である。おそらく上流のフォーマット変更が原因である。
					// このエラーコードを取得した場合、プレゼンタのメディアタイプを NULL に設定すること。
					// EVR は新しいフォーマットを要求するだろう。

					this->SetMediaType( NULL );
				}
				else if( hr == MF_E_TRANSFORM_NEED_MORE_INPUT )
				{
					// ミキサーは、新しいフレームを生成するためにさらなる入力を必要としている。
					// このエラーコードを取得した場合、EVR がストリームの終端に到達したかどうかを確認し、応答する。
					// それ以外の場合は、このメッセージを無視すること。

					// ミキサーの ProcessOutput() メソッドが MF_E_TRANSFORM_NEED_MORE_INPUT を返した場合、
					// それは、ミキサーが出力を生成できないことを意味するため、プレゼンタは m_fSampleNotify フラグをクリアする。
					m_bSampleNotify = FALSE;
				}
				//-----------------
				#pragma endregion
			}
			else
			{
				#pragma region [ (b) 失敗 ]
				//-----------------

				// 5.（オプション）クロックが利用可能な場合は、現在のクロックタイムを取得する（T2）。
				//   ミキサーによって導かれる遅延量は (T2 - T1) である。
				//   この値をもって、EVR に EC_PROCESSING_LATENCY イベントを送ること。EVR はこの値を品質制御に用いる。
				//   クロックは使えない場合は、EC_PROCESSING_LATENCY を送信する理由はない。

				if( m_pClock && !bRepaint )
				{
					(void) m_pClock->GetCorrelatedTime( 0, &mixerEndTime, &systemTime );

					LONGLONG latencyTime = mixerEndTime - mixerStartTime;
					tFAILEDなら帰還( hr = this->tEVRにイベントを通知する( EC_PROCESSING_LATENCY, (LONG_PTR) &latencyTime, 0 ) );
				}

				
				// 6.（オプション）サンプルに IMFTrackedSample を問い合わせ、IMFTrackedSample::SetAllocator を呼び出す。

				tFAILEDなら帰還( hr = this->TrackSample( pSample ) );

				
				// 7. サンプルをプレゼンテーション用にスケジューリングする。

				if( ( m_FrameStep.state == FRAMESTEP_してない ) || bRepaint )
				{
					tFAILEDなら帰還( hr = DeliverSample( pSample, bRepaint ) );
				}
				else
				{
					tFAILEDなら帰還( hr = DeliverFrameStepSample( pSample ) );
				}

				// 少なくとも１つのサンプルを描画できた。
				m_bPrerolled = TRUE;

				//-----------------
				#pragma endregion
			}
		}
		finally
		{
			SAFE_COM_RELEASE( dataBuffer.pEvents );
			SAFE_COM_RELEASE( pSample );
		}

		return hr;
	}
	HRESULT	CustomEVRPresenter::DeliverSample( IMFSample *pSample, BOOL bRepaint )
	{
		HRESULT hr = S_OK;
		PresenterEngine::D3DDEVICE_STATE state = PresenterEngine::D3DDEVICE_STATE_OK;

		// 再生中ではない or コマ送り中ではない or 再描画要求ではない　→　サンプルを即時描画するかスケジューラに入れろ
		BOOL bPresentNow = ( ( this->m_RenderState.Get() != RENDER_STATE_再生中 ) || this->IsScrubbing() || bRepaint );

		hr = CDirect3DNative::tデバイスの状態を取得する();
		if( SUCCEEDED( hr ) )
			hr = this->m_scheduler.t新しいサンプルを表示するかスケジュールキューに入れる( pSample, bPresentNow );

		// 失敗したら即中断。
		if( hr == D3DERR_DEVICEREMOVED || hr == D3DERR_OUTOFVIDEOMEMORY )
			this->tEVRにイベントを通知する(EC_ERRORABORT, hr, 0);

		return hr;
	}

	// サンプルに、解放された時にコールバック(IMFAsyncCallback::Invoke)を呼び出すよう設定する。
	HRESULT	CustomEVRPresenter::TrackSample( IMFSample *pSample )
	{
		HRESULT hr = S_OK;
		IMFTrackedSample *pTracked = NULL;

		try
		{
			// 設定には、サンプルの IMFTrackedSample インターフェースを使用する。
			tFAILEDなら帰還( hr = pSample->QueryInterface( __uuidof( IMFTrackedSample ), (void**) &pTracked ) );
			
			// 呼び出す IMFAsyncCallback オブジェクトは m_SampleFreeCB。これは、IMFAsyncCallback::Invoke 時に this->OnSampleFree() を呼び出すよう、本クラスのコンストラクタで固定されている。
			tFAILEDなら帰還( hr = pTracked->SetAllocator( &m_SampleFreeCB, NULL ) );
		}
		finally
		{
			SAFE_COM_RELEASE( pTracked );
		}

		return hr;
	}
	void	CustomEVRPresenter::ReleaseResources()
	{
		HRESULT hr = S_OK;

		// トークンカウンターを増やすことで、すべてのビデオサンプルが無効化されたことを全スレッドに通知する。
		// → OnSampleFree() コールバックでプレゼンタが誤って無効化されたサンプルを使ってしまうことを避ける。

		this->m_TokenCounter++;


		// すべてのサンプルを解放し、破棄する。

		this->Flush();
		this->m_SamplePool.Clear();
		this->m_pPresenterEngine->ReleaseResources();
	}

	// フレームステッピング（コマ送り）

	HRESULT	CustomEVRPresenter::PrepareFrameStep( DWORD cSteps )
	{
		HRESULT hr = S_OK;

		// ステップカウントに加算。
		m_FrameStep.steps += cSteps;

		// フレームステップの状態を設定。
		m_FrameStep.state = FRAMESTEP_してるけどクロックは始まってない;

		// クロックが既に始まっているなら、フレームステッピングをすぐに開始できる。
		if( m_RenderState.Get() == RENDER_STATE_再生中 )
			tFAILEDなら帰還( hr = this->StartFrameStep() );

		return S_OK;
	}
	HRESULT	CustomEVRPresenter::StartFrameStep()
	{
		HRESULT hr = S_OK;
		IMFSample *pSample = NULL;

		try
		{
			if( this->m_FrameStep.state == FRAMESTEP_してるけどクロックは始まってない )
			{
				// ただいまサンプル待ちの状態。
				this->m_FrameStep.state = FRAMESTEP_クロックは始まっていてサンプルを待っている;

				// キューにあるすべてのサンプルについて……
				while( !this->m_FrameStep.samples.IsEmpty() &&													// フレームステップキューが空になるか
					 ( this->m_FrameStep.state == FRAMESTEP_クロックは始まっていてサンプルを待っている ) )		// フレームステップ操作が完了すればループを抜ける。
				{
					tFAILEDなら帰還( hr = this->m_FrameStep.samples.RemoveFront( &pSample ) );
					tFAILEDなら帰還( hr = this->DeliverFrameStepSample( pSample ) );
				}
			}
			else if( m_FrameStep.state == FRAMESTEP_してない )
			{
				// フレームステップ中じゃないのにサンプルがある場合はそれを普通に処理する。
				while( !m_FrameStep.samples.IsEmpty() )
				{
					tFAILEDなら帰還( hr = m_FrameStep.samples.RemoveFront( &pSample ) );
					tFAILEDなら帰還( hr = DeliverSample( pSample, FALSE ) );
				}
			}
		}
		finally
		{
			SAFE_COM_RELEASE( pSample );
		}

		return S_OK;
	}
	HRESULT	CustomEVRPresenter::DeliverFrameStepSample( IMFSample *pSample )
	{
		HRESULT hr = S_OK;
		IUnknown *pUnk = NULL;

		#pragma region [ このサンプルを使わないなら無視して終わり。]
		//-----------------
		if( this->IsScrubbing() &&									// フレームステッピング中で、
			this->m_pClock &&										// クロックが存在し、
			this->IsSampleTimePassed( this->m_pClock, pSample ) )	// サンプルの描画時刻が過ぎてるなら、
		{
			return S_OK;											// このサンプルは無視する。
		}
		//-----------------
		#pragma endregion

		#pragma region [ 既に描画済みなら、次回もこのサンプルを表示するために、またフレームステップキューに戻して終わり。]
		//-----------------
		if( this->m_FrameStep.state == FRAMESTEP_STATE::FRAMESTEP_描画するサンプルが受領された ||
			this->m_FrameStep.state == FRAMESTEP_STATE::FRAMESTEP_サンプルの描画は完了した )
		{
			return this->m_FrameStep.samples.InsertBack( pSample );
		}
		//-----------------
		#pragma endregion

		#pragma region [ まだサンプル更新のタイミングじゃないなら何もせず終わり。]
		//-----------------
		// ステップ数を減らす。
		if( this->m_FrameStep.steps > 0 )
			this->m_FrameStep.steps--;

		// まだ最後のステップではないなら、何もしない。
		if( this->m_FrameStep.steps > 0 )
			return S_OK;
		//-----------------
		#pragma endregion

		#pragma region [ クロックが開始されてないなら、このサンプルをフレームステップキューに戻して、終わり。]
		//-----------------
		if( this->m_FrameStep.state == FRAMESTEP_STATE::FRAMESTEP_してるけどクロックは始まってない )
			return this->m_FrameStep.samples.InsertBack( pSample );
		//-----------------
		#pragma endregion

		try
		{
			// クロックが開始されているので、このサンプルを配信する。
			tFAILEDなら帰還( hr = this->DeliverSample( pSample, FALSE ) );

			// 後でサンプルを識別するために、IUnknown を取得しておく。
			// ※ COMでは、同一のオブジェクトに対して IUnknown を問い合わせると同一のポインタが返される。
			tFAILEDなら帰還( hr = pSample->QueryInterface( __uuidof( IUnknown ), (void**) &pUnk ) );
			this->m_FrameStep.pSampleNoRef = (DWORD_PTR) pUnk;		// 参照ではなく識別子として使うだけなので、AddRef() はしない。

			// 受領完了。状態を更新。
			this->m_FrameStep.state = FRAMESTEP_描画するサンプルが受領された;
		}
		finally
		{
			SAFE_COM_RELEASE( pUnk );
		}

		return S_OK;
	}
	HRESULT	CustomEVRPresenter::CompleteFrameStep( IMFSample *pSample )
	{
		HRESULT hr = S_OK;
		MFTIME hnsSampleTime = 0;
		MFTIME hnsSystemTime = 0;

		// 状態を「完了」に更新し、描画サンプルの識別子(IUnknownポインタ)をクリアする。
		m_FrameStep.state = FRAMESTEP_サンプルの描画は完了した;
		m_FrameStep.pSampleNoRef = NULL;

		// EVR に、フレームステッピングが完了したことを通知する。
		tFAILEDなら帰還( hr = tEVRにイベントを通知する( EC_STEP_COMPLETE, FALSE, 0 ) );	// FALSE = 完了（キャンセルされてない）を意味する

		// 停止中なら、EVR に停止時刻イベントも通知する。
		if( this->IsScrubbing() )
		{
			// サンプルの描画時刻を取得。
			hr = pSample->GetSampleTime( &hnsSampleTime );
			
			// 取得に失敗したら、現在のプレゼンテーション時刻を使う。
			if( FAILED( hr ) )
			{
				if( m_pClock )
					(void) m_pClock->GetCorrelatedTime( 0, &hnsSampleTime, &hnsSystemTime );		// エラーチェックなし
			}

			// EVR へ時刻イベントを通知。
			tFAILEDなら帰還( this->tEVRにイベントを通知する( EC_SCRUB_TIME, LODWORD( hnsSampleTime ), HIDWORD( hnsSampleTime ) ) );
		}

		return S_OK;
	}
	HRESULT	CustomEVRPresenter::CancelFrameStep()
	{
		HRESULT hr = S_OK;
		FRAMESTEP_STATE oldState = this->m_FrameStep.state;

		// ※後で再開するかも知れないので、キャンセルの段階ではまだフレームステップキューはクリアしないこと。

		this->m_FrameStep.state = FRAMESTEP_してない;
		this->m_FrameStep.steps = 0;
		this->m_FrameStep.pSampleNoRef = NULL;

		if( oldState == FRAMESTEP_STATE::FRAMESTEP_してるけどクロックは始まってない ||
			oldState == FRAMESTEP_STATE::FRAMESTEP_クロックは始まっていてサンプルを待っている ||
			oldState == FRAMESTEP_STATE::FRAMESTEP_描画するサンプルが受領された )
		{
			// EVRへ、フレームステッピングの最中にキャンセルされたことを通知。
			tFAILEDなら帰還( hr = tEVRにイベントを通知する( EC_STEP_COMPLETE, TRUE, 0 ) );	// TRUE = キャンセルされた（完了ではない）を意味する
		}

		return S_OK;
	}

	// コールバック

	HRESULT	CustomEVRPresenter::OnSampleFree( IMFAsyncResult *pResult )
	{
		HRESULT hr = S_OK;
		IUnknown *pObject = NULL;
		IMFSample *pSample = NULL;
		IUnknown *pUnk = NULL;

		try
		{
			// 非同期結果オブジェクト pResult からサンプルを取得する。

			tFAILEDなら帰還( hr = pResult->GetObject( &pObject ) );											// IUnknown 属性から取得
			tFAILEDなら帰還( hr = pObject->QueryInterface( __uuidof( IMFSample ), (void**) &pSample ) );	// IMFSample へ変換


			// サンプルがフレームステッピング用に受領されているなら、フレームステップを完了する。
			
			if( this->m_FrameStep.state == FRAMESTEP_STATE::FRAMESTEP_描画するサンプルが受領された )
			{
				// サンプルの識別子(IUnknownポインタ)を取得
				tFAILEDなら帰還( hr = pSample->QueryInterface( __uuidof( IMFSample ), (void**) &pUnk ) );	// pObject は使えないので注意！

				// Deliver 済みのサンプルと同一の識別子なら、EVR へフレームステップ完了を通知する。
				if( this->m_FrameStep.pSampleNoRef == (DWORD_PTR) pUnk )
					tFAILEDなら帰還( hr = this->CompleteFrameStep( pSample ) );
			}


			// サンプルがまだ無効でない（トークンカウンタが変わってない）なら、サンプルをプールに戻して描画する。

			AutoLock lock( this->m_ObjectLock );

			if( ::MFGetAttributeUINT32( pSample, MFSamplePresenter_SampleCounter, (UINT32) -1 ) == m_TokenCounter )
			{
				tFAILEDなら帰還( hr = m_SamplePool.tサンプルをプールに戻す( pSample ) );
				(void) ProcessOutputLoop();		// エラーチェックなし
			}
		}
		finally
		{
			if( FAILED( hr ) )
				(void) this->tEVRにイベントを通知する( EC_ERRORABORT, hr, 0 );		// エラーチェックなし

			SAFE_COM_RELEASE( pObject );
			SAFE_COM_RELEASE( pSample );
			SAFE_COM_RELEASE( pUnk );
		}

		return S_OK;
	}


// private:

	RECT	CustomEVRPresenter::CorrectAspectRatio( const RECT& src, const MFRatio& srcPAR, const MFRatio& destPAR )
	{
		// 矩形の初期値は転送元矩形であるが、それは (0,0) からのオフセットであるとみなす。
		RECT rc = { 0, 0, src.right - src.left, src.bottom - src.top };

		// 転送元と転送先の PAR が同じなら、何もすることはない。
		// 異なるなら、以下の手順で画像サイズを調整する。
		//   1. 転送元 PAR から 1:1 に変形する。
		//   2. 1:1 から転送先 PAR に変形する。
		
		if( ( srcPAR.Numerator != destPAR.Numerator ) || ( srcPAR.Denominator != destPAR.Denominator ) )	// 異なっている
		{
			//------------------
			// 1. 転送元の PAR を 1:1 に変換する。
			
			if( srcPAR.Numerator > srcPAR.Denominator )
			{
				// 転送元の PAR は横長である。
				rc.right = ::MulDiv( rc.right, srcPAR.Numerator, srcPAR.Denominator );
			}
			else if( srcPAR.Numerator < srcPAR.Denominator )
			{
				// 転送元の PAR は縦長である。
				rc.bottom = ::MulDiv( rc.bottom, srcPAR.Denominator, srcPAR.Numerator );
			}
			else
			{
				// 転送元の PAR は既に 1:1 である。
			}

			//------------------
			// 2. 1:1 から転送先の PAR に変換する。

			if( destPAR.Numerator > destPAR.Denominator )
			{
				// 転送先の PAR は横長である。
				rc.bottom = MulDiv( rc.bottom, destPAR.Numerator, destPAR.Denominator );
			}
			else if( destPAR.Numerator < destPAR.Denominator )
			{
				// 転送先の PAR は縦長である。
				rc.right = MulDiv( rc.right, destPAR.Denominator, destPAR.Numerator );
			}
			else
			{
				// 転送先の PAR は 1:1 である。
			}
		}

		return rc;
	}
	BOOL	CustomEVRPresenter::AreMediaTypesEqual( IMFMediaType *pType1, IMFMediaType *pType2 )
	{
		// どっちも NULL なら等しいと見なす。
		if( ( pType1 == NULL ) && ( pType2 == NULL ) )
			return TRUE;

		// いずれかが NULL なら異なると見なす。
		if( ( pType1 == NULL ) || ( pType2 == NULL ) )
			return FALSE;

		// 両者が等しいか比較する。
		DWORD dwFlags = 0;
		HRESULT hr = pType1->IsEqual( pType2, &dwFlags );
		return ( hr == S_OK );
	}
	HRESULT	CustomEVRPresenter::ValidateVideoArea( const MFVideoArea& area, UINT32 width, UINT32 height )
	{
		float fOffsetX = this->MFOffsetToFloat( area.OffsetX );
		float fOffsetY = this->MFOffsetToFloat( area.OffsetY );

		if( ( (LONG) fOffsetX + area.Area.cx > (LONG) width ) ||
			( (LONG) fOffsetY + area.Area.cy > (LONG) height ) )
		{
			return MF_E_INVALIDMEDIATYPE;
		}
		else
		{
			return S_OK;
		}
	}
	HRESULT	CustomEVRPresenter::SetDesiredSampleTime( IMFSample *pSample, const LONGLONG& hnsSampleTime, const LONGLONG& hnsDuration )
	{
		if( pSample == NULL )
			return E_POINTER;

		HRESULT hr = S_OK;
		IMFDesiredSample *pDesired = NULL;

		try
		{
			tFAILEDなら帰還( hr = pSample->QueryInterface( __uuidof( IMFDesiredSample ), (void**) &pDesired ) );
			pDesired->SetDesiredSampleTimeAndDuration( hnsSampleTime, hnsDuration );
		}
		finally
		{
			SAFE_COM_RELEASE( pDesired );
		}

		return S_OK;
	}
	HRESULT	CustomEVRPresenter::ClearDesiredSampleTime( IMFSample *pSample )
	{
		if( pSample == NULL )
			return E_POINTER;

		HRESULT hr = S_OK;
		IMFDesiredSample *pDesired = NULL;
		IUnknown *pUnkTexture = NULL;
    
		try
		{
			// 属性を保存する。
			UINT32 counter = ::MFGetAttributeUINT32( pSample, MFSamplePresenter_SampleCounter, (UINT32) -1 );	// 属性からサンプルのトークンカウンター値を取得。
			(void) pSample->GetUnknown( MFSamplePresenter_Texture, IID_IUnknown, (void**) &pUnkTexture );		// 属性からサンプルのテクスチャを取得。この属性を持たないかも知れないので、エラーチェックは後で。

			// IMFSample から IMFDesireSample を取得し、クリアする。
			tFAILEDなら帰還( hr = pSample->QueryInterface( __uuidof( IMFDesiredSample ), (void**) &pDesired ) );
			pDesired->Clear();	// このメソッドは、サンプルのすべての属性も削除してしまう。

			// 改めて属性を登録する。
			tFAILEDなら帰還( hr = pSample->SetUINT32( MFSamplePresenter_SampleCounter, counter ) );				// 属性としてトークンカウンター値を登録。
			if( pUnkTexture )
				tFAILEDなら帰還( hr = pSample->SetUnknown( MFSamplePresenter_Texture, pUnkTexture ) );			// 属性としてテクスチャ（あるなら）を登録。
		}
		finally
		{
			SAFE_COM_RELEASE( pUnkTexture );
			SAFE_COM_RELEASE( pDesired );
		}

		return S_OK;
	}
	BOOL	CustomEVRPresenter::IsSampleTimePassed( IMFClock *pClock, IMFSample *pSample )
	{
		if( pSample == NULL || pClock == NULL )
			return E_POINTER;

		HRESULT hr = S_OK;
		MFTIME hnsTimeNow = 0;
		MFTIME hnsSystemTime = 0;
		MFTIME hnsSampleStart = 0;
		MFTIME hnsSampleDuration = 0;

		// ※サンプルがタイムスタンプや期間を持っているとは限らないこと、また、クロックが時刻を返せるとは限らないことに注意する。

		if( FAILED( hr = pClock->GetCorrelatedTime( 0, &hnsTimeNow, &hnsSystemTime ) ) )
			return FALSE;

		if( FAILED( hr = pSample->GetSampleTime( &hnsSampleStart ) ) )
			return FALSE;

		if( FAILED( hr = pSample->GetSampleDuration( &hnsSampleDuration ) ) )
			return FALSE;

		if( hnsSampleStart + hnsSampleDuration < hnsTimeNow )
			return TRUE;

		return FALSE;
	}
	HRESULT	CustomEVRPresenter::SetMixerSourceRect( IMFTransform *pMixer, const MFVideoNormalizedRect& nrcSource )
	{
		if( pMixer == NULL )
			return E_POINTER;

		HRESULT hr = S_OK;
		IMFAttributes *pAttributes = NULL;

		try
		{
			tFAILEDなら帰還( hr = pMixer->GetAttributes( &pAttributes ) );
			tFAILEDなら帰還( hr = pAttributes->SetBlob( VIDEO_ZOOM_RECT, (const UINT8*) &nrcSource, sizeof( nrcSource ) ) );
		}
		finally
		{
			SAFE_COM_RELEASE( pAttributes );
		}

		return S_OK;
	}
}

