#include "stdafx.h"
#include "CMediaSessionNative.h"
#include "CustomEVRPresenter.h"
#include "CustomGUID.h"

namespace FDK
{
	CMediaSessionNative::CMediaSessionNative()
	{
		try
		{
			AllInstances::t追加( this );

			this->n参照カウンタ = 1;
			this->Status.Set( CStatus::EStatus::再生終了 );

			this->pMediaSession = NULL;
			this->pSequencerSource = NULL;
			this->pMediaSession = NULL;
			this->pControl = NULL;
			this->pAttributes = NULL;

			for( int i = 0; i < 2; i++ )
			{
				this->Segments[ i ].TopologyID = 0;
				this->Segments[ i ].SegmentID = 0;
				this->Segments[ i ].pTopology = NULL;
				this->Segments[ i ].b再生中 = false;
			}
			this->bループする.Set( false );
			this->n表示中のindex.Set( 0 );

			#pragma region [ MediaSession イベント通知用イベントの作成 ]
			//-----------------
			tNULLなら例外発生( L"MESessionClosed イベントの作成",
				this->evMESessionClosed = ::CreateEvent( NULL, TRUE, FALSE, L"MediaSessionがクローズされた" ) );

			tNULLなら例外発生( L"MESessionStarted イベントの作成",
				this->evMESessionStarted = ::CreateEvent( NULL, TRUE, FALSE, L"プレゼンタが描画を開始した" ) );

			tNULLなら例外発生( L"MESessionStopped イベントの作成",
				this->evMESessionStopped = ::CreateEvent( NULL, TRUE, FALSE, L"再生を停止した" ) );
			//-----------------
			#pragma endregion

			{
				AutoLock lock( this->csファイル名 );
				this->stgファイル名[ 0 ] = '\0';
			}
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"初期化に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	CMediaSessionNative::CMediaSessionNative( HWND hWnd, DWORD dwMediaSessionイベント処理スレッドID, LPCWSTR stgファイル名, bool b音声あり, bool bループする )
		: CMediaSessionNative()
	{
		this->t構築する( hWnd, dwMediaSessionイベント処理スレッドID, stgファイル名, b音声あり, bループする );
	}
	CMediaSessionNative::~CMediaSessionNative()
	{
		// ※ GC から CMediaSession のファイナライザ経由で呼び出される可能性があるので、例外は発生させない。

		HRESULT hr = S_OK;

		::ResetEvent( this->evMESessionStopped );
		::ResetEvent( this->evMESessionClosed );

		this->pMediaSession->Stop();							// Stop（非同期）
		::WaitForSingleObject( this->evMESessionStopped, INFINITE );

		this->pSequencerMediaSource->Shutdown();				// シーケンサーソースシャットダウン

		this->pMediaSession->Close();							// Close（非同期）
		::WaitForSingleObject( this->evMESessionClosed, INFINITE );

		this->Status.Set( CStatus::EStatus::閉鎖待ち );			// これ以降、MediaSession イベントを処理しない。

		this->pMediaSession->Shutdown();						// Shutdown

		SAFE_COM_RELEASE( this->Segments[ 0 ].pTopology );
		SAFE_COM_RELEASE( this->Segments[ 1 ].pTopology );
		SAFE_COM_RELEASE( this->pSequencerMediaSource );
		SAFE_COM_RELEASE( this->pSequencerSource );
		SAFE_COM_RELEASE( this->pMediaSession );
		SAFE_COM_RELEASE( this->pAttributes );	// IMFAttributes の解放
		SAFE_COM_RELEASE( this->pControl );		// IMFVideoDisplayControl の解放

		::CloseHandle( this->evMESessionClosed );
		::CloseHandle( this->evMESessionStarted );
		::CloseHandle( this->evMESessionStopped );

		AllInstances::t削除( this );
	}

	void CMediaSessionNative::t構築する( HWND hWnd, DWORD dwMediaSessionイベント処理スレッドID, LPCWSTR stgファイル名, bool b音声あり, bool bループする )
	{
		HRESULT hr = S_OK;

		if( NULL != this->pMediaSession )
			throw gcnew Exception( L"MediaSession はすでに構築済みです。" );

		try
		{
			// パラメータを保存。

			this->dwイベント処理スレッドID = dwMediaSessionイベント処理スレッドID;
			this->bループする.Set( bループする );

			#pragma region [ ファイル名を保存。]
			//-----------------
			{
				TCHAR stgDrive[ _MAX_DRIVE ];
				TCHAR stgDir[ _MAX_DIR ];
				TCHAR stgFname[ _MAX_FNAME ];
				TCHAR stgExt[ _MAX_EXT ];

				errno_t err = _wsplitpath_s( stgファイル名, stgDrive, _MAX_DRIVE, stgDir, _MAX_DIR, stgFname, _MAX_FNAME, stgExt, _MAX_EXT );
				if( err != 0 )
					throw gcnew Exception( L"ファイル名の解析に失敗しました。" );

				AutoLock l( this->csファイル名 );

				int n = swprintf_s( this->stgファイル名, MAX_PATH, L"%s%s", stgFname, stgExt );
				if( n <= 0 )
					throw gcnew Exception( L"ファイル名の再構築に失敗しました。" );
			}
			//-----------------
			#pragma endregion


			// MediaSession を作成。

			#pragma region [ MediaSession を作成し、イベント取得を開始する。]
			//-----------------
			tFAILEDなら例外発生( L"MediaSession の作成",
				hr = ::MFCreateMediaSession( NULL, &( this->pMediaSession ) ) );

			tFAILEDなら例外発生( L"MediaSession へのコールバックオブジェクトの登録",
				hr = this->pMediaSession->BeginGetEvent( this, NULL ) );
			//-----------------
			#pragma endregion


			// シーケンサーソースを作成する。

			#pragma region [ シーケンサーソースとそのメディアソースを作成する。]
			//-------------------------
			tFAILEDなら例外発生( L"シーケンサーソースの作成",
				hr = ::MFCreateSequencerSource( NULL, &( this->pSequencerSource ) ) );

			tFAILEDなら例外発生( L"シーケンサーソースからのメディアソースの取得",
				hr = this->pSequencerSource->QueryInterface( IID_PPV_ARGS( &( this->pSequencerMediaSource ) ) ) );
			//-------------------------
		#pragma endregion


			// トポロジを作成し、シーケンサーソースに登録する。

			if( this->bループする.Get() )
			{
				#pragma region [ (A) ループする場合 ]
				//-------------------------
				this->tトポロジを作成する( hWnd, stgファイル名, b音声あり, &( this->Segments[ 0 ].pTopology ) );

				tFAILEDなら例外発生( L"シーケンサーソースへのトポロジの登録（第１セグメント）",
					hr = this->pSequencerSource->AppendTopology( this->Segments[ 0 ].pTopology, 0/*Lastフラグなし*/, &( this->Segments[ 0 ].SegmentID ) ) );

				tFAILEDなら例外発生( L"トポロジIDの取得（第１セグメント）",
					hr = this->Segments[ 0 ].pTopology->GetTopologyID( &( this->Segments[ 0 ].TopologyID ) ) );


				this->tトポロジを作成する( hWnd, stgファイル名, b音声あり, &( this->Segments[ 1 ].pTopology ) );

				tFAILEDなら例外発生( L"シーケンサーソースへのトポロジの登録（第２セグメント）",
					hr = this->pSequencerSource->AppendTopology( this->Segments[ 1 ].pTopology, 0, &( this->Segments[ 1 ].SegmentID ) ) );

				tFAILEDなら例外発生( L"トポロジIDの取得（第２セグメント）",
					hr = this->Segments[ 1 ].pTopology->GetTopologyID( &( this->Segments[ 1 ].TopologyID ) ) );
				//-------------------------
				#pragma endregion
			}
			else
			{
				#pragma region [ (B) ループしない場合 ]
				//-------------------------
				this->tトポロジを作成する( hWnd, stgファイル名, b音声あり, &( this->Segments[ 0 ].pTopology ) );

				tFAILEDなら例外発生( L"シーケンサーソースへのトポロジの登録",
					hr = this->pSequencerSource->AppendTopology( this->Segments[ 0 ].pTopology, MFSequencerTopologyFlags::SequencerTopologyFlags_Last, &( this->Segments[ 0 ].SegmentID ) ) );

				tFAILEDなら例外発生( L"トポロジIDの取得",
					hr = this->Segments[ 0 ].pTopology->GetTopologyID( &( this->Segments[ 0 ].TopologyID ) ) );

				this->Segments[ 1 ].SegmentID = 0;
				this->Segments[ 1 ].TopologyID = 0;
				//-------------------------
				#pragma endregion
			}

			// 最初のトポロジを MediaSession に登録する。

			#pragma region [ 最初のトポロジを MediaSession に登録する。]
			//-------------------------
			IMFPresentationDescriptor *pPD = NULL;

			try
			{
				tFAILEDなら例外発生( L"最初のプレゼンテーション記述子の取得",
					hr = this->pSequencerMediaSource->CreatePresentationDescriptor( &pPD ) );

				this->t次のセグメントをMediaSessionに登録する( pPD );
			}
			finally	
			{
				SAFE_COM_RELEASE( pPD );
			}
			//-------------------------
			#pragma endregion
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			SAFE_COM_RELEASE( this->Segments[ 0 ].pTopology );
			SAFE_COM_RELEASE( this->Segments[ 1 ].pTopology );
			SAFE_COM_RELEASE( this->pSequencerMediaSource );
			SAFE_COM_RELEASE( this->pSequencerSource );
			SAFE_COM_RELEASE( this->pMediaSession );
			throw gcnew Exception( L"MediaSession の構築に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CMediaSessionNative::t再生する( UINT64 n再生開始位置100ns )
	{
		HRESULT hr = S_OK;

		try
		{
			// リセット

			tFALSEなら例外発生( L"MESessionStartedイベントのリセット",
				::ResetEvent( this->evMESessionStarted ) );


			// 再生開始指示。

			PROPVARIANT prop;
			::PropVariantInit( &prop );
			prop.vt = VT_I8;
			prop.hVal.QuadPart = n再生開始位置100ns;

			tFAILEDなら例外発生( L"IMFMediaSession::Start() ",
				hr = this->pMediaSession->Start( NULL, &prop ) );

			this->Status.Set( CStatus::EStatus::再生準備中 );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"再生に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CMediaSessionNative::t停止する()
	{
		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"IMFMediaSession::Stop() ",
				hr = this->pMediaSession->Stop() );

			this->Status.Set( CStatus::EStatus::停止準備中 );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"停止に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CMediaSessionNative::t一時停止する()
	{
		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"IMFMediaSession::Pause() ",
				hr = this->pMediaSession->Pause() );

			this->Status.Set( CStatus::EStatus::一時停止準備中 );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"一時停止に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CMediaSessionNative::t再生を再開する()
	{
		HRESULT hr = S_OK;

		try
		{
			tFALSEなら例外発生( L"MESessionStarted イベントのリセット",
				::ResetEvent( this->evMESessionStarted ) );

			PROPVARIANT prop;
			::PropVariantInit( &prop );
			prop.vt = VT_EMPTY;	// 現在位置から再生

			tFAILEDなら例外発生( L"IMFMediaSession::Start() ",
				hr = this->pMediaSession->Start( NULL, &prop ) );

			this->Status.Set( CStatus::EStatus::再生準備中 );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"再生の再開に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}

	void CMediaSessionNative::t再生処理が完了するまで待機する()
	{
		// 特にエラーチェックはなし。
		HRESULT hr = ::WaitForSingleObject( this->evMESessionStarted, INFINITE );

		// イベントをリセットしておく。
		::ResetEvent( this->evMESessionStarted );
	}

	bool CMediaSessionNative::b再生中である()
	{
		CStatus::EStatus nowStatus = this->Status.Get();
		return ( CStatus::EStatus::再生準備中 == nowStatus || CStatus::EStatus::再生中 == nowStatus );
	}
	UInt64 CMediaSessionNative::n現在のクロックを返す()
	{
		Debug::Assert( this->pMediaSession != NULL );

		HRESULT hr = S_OK;
		LONGLONG clockTime;
		MFTIME systemTime;

		try
		{
			IMFClock *pClock;
			tFAILEDなら例外発生( L"IMFClock の取得",
				hr = this->pMediaSession->GetClock( &pClock ) );

			Debug::Assert( pClock != NULL );

			tFAILEDなら例外発生( L"経過時間の取得",
				hr = pClock->GetCorrelatedTime( NULL, &clockTime, &systemTime ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"現在のクロックの取得に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}

		return clockTime;
	}
	void CMediaSessionNative::t透明度を設定する( UINT32 u透明度 )
	{
		Debug::Assert( this->pAttributes != NULL );

		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"MediaSessionの透明度の設定",
				hr = this->pAttributes->SetUINT32( GUIDofテクスチャの透明度, u透明度 ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"MediaSessionの透明度の設定に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CMediaSessionNative::t加算合成する( bool b加算合成する )
	{
		Debug::Assert( this->pAttributes != NULL );

		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"MediaSessionの加算合成の設定",
				hr = this->pAttributes->SetUINT32( GUIDofテクスチャの加算合成, ( b加算合成する ) ? 1 : 0 ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"MediaSessionの加算合成の設定に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}

	void CMediaSessionNative::t次のサンプルを要求する()
	{
		//Debug::Assert( this->pControl != NULL );		動画ループ時にASSERTにひっかかってしまう。
		if( NULL == this->pControl )
			return;


		// 再生中だろうがそうでなかろうが呼び出されるので要チェック。

		if( CStatus::EStatus::再生中 != this->Status.Get() )
			return;


		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"次のサンプルの要求",
				hr = this->pControl->SetRenderingPrefs( MFVideoRenderPrefs_RequestNextSample ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"次のサンプルの要求に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CMediaSessionNative::t最新のサンプルを描画する( LPRECT prc描画先 )
	{
		Debug::Assert( this->pControl != NULL );


		// 再生中だろうがそうでなかろうが呼び出されるので要チェック。

		if( CStatus::EStatus::再生中 != this->Status.Get() )
			return;


		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"描画位置の通知",
				hr = this->pControl->SetVideoPosition( NULL, prc描画先 ) );


			tFAILEDなら例外発生( L"独自定義メッセージによるプレゼンタへの描画通知",
				hr = this->pControl->SetRenderingPrefs( MFVideoRenderPrefs_DisplaySample ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"最新のサンプルの描画に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}
	void CMediaSessionNative::t画像サイズを取得する( SIZE *pSize )
	{
		Debug::Assert( this->pControl != NULL );


		// 再生中だろうがそうでなかろうが呼び出されるので要チェック。

		if( CStatus::EStatus::再生中 != this->Status.Get() )
			return;


		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"ネイティブビデオサイズの取得",
				hr = this->pControl->GetNativeVideoSize( pSize, NULL ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"画像サイズの取得に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}

	HRESULT CMediaSessionNative::tMediaSessionイベントを処理する( IMFMediaEvent* pEvent )
	{
		// ※ このメソッドは、構築時に指定したスレッドから呼び出される。

		HRESULT hr = S_OK;
		BOOL bResult = false;

		// イベントタイプを取得。
		MediaEventType meType = MEUnknown;
		if( FAILED( hr = pEvent->GetType( &meType ) ) )
			goto failed;

		// イベントタイプに応じて処理分岐。

		switch( meType )
		{
		case MESessionTopologySet:
			#pragma region [ トポロジが完成した。]
			//-------------------------
			this->csファイル名.Lock();
			Trace::TraceInformation( "MESessionTopologySet: トポロジが完成しました。[{0}]", gcnew String( this->stgファイル名 ) );
			this->csファイル名.Unlock();
			break;
			//-------------------------
			#pragma endregion

		case MESessionTopologyStatus:
			#pragma region [ トポロジのステータスが変化した。]
			//-----------------
			Trace::TraceInformation( "MESessionTopologyStatus: トポロジのステータスが変化しました。" );
			Trace::Indent();
			{
				MF_TOPOSTATUS status = MF_TOPOSTATUS_INVALID;
				TOPOID topoID = 0;

				#pragma region [ イベントからトポロジIDを取得する。]
				//-------------------------
				IMFTopology *pTopology = NULL;
				PROPVARIANT var;
				::PropVariantInit( &var );
				if( FAILED( hr = pEvent->GetValue( &var ) ) ||
					( VT_UNKNOWN != var.vt ) ||
					( FAILED( hr = var.punkVal->QueryInterface( __uuidof( IMFTopology ), (void**) &pTopology ) ) ) )
				{
					tFAILEDならTraceError( "MESessionTopologyStatus: トポロジの取得", hr );
					::PropVariantClear( &var );
					Trace::Unindent();
					goto failed;
				}
				if( FAILED( hr = pTopology->GetTopologyID( &topoID ) ) )
				{
					tFAILEDならTraceError( "MESessionTopologyStatus: トポロジIDの取得", hr );
					SAFE_COM_RELEASE( pTopology );
					::PropVariantClear( &var );
					Trace::Unindent();
					goto failed;
				}
				SAFE_COM_RELEASE( pTopology );
				::PropVariantClear( &var );
				//-------------------------
				#pragma endregion
				#pragma region [ イベントからトポロジステータスコードを取得する。]
				//-----------------
				UINT32 nコード;

				if( FAILED( hr = pEvent->GetUINT32( MF_EVENT_TOPOLOGY_STATUS, &nコード ) ) )
				{
					tFAILEDならTraceError( "MESessionTopologyStatus: トポロジステータスコードの取得", hr );
					Trace::Unindent();
					goto failed;
				}

				status = (MF_TOPOSTATUS) nコード;
				//-----------------
				#pragma endregion

				int index = ( topoID == this->Segments[ 0 ].TopologyID ) ? 0 : 1;


				// ステータスコードによって処理分岐。

				switch( status )
				{
				case MF_TOPOSTATUS_READY:
					#pragma region [ トポロジの準備が完了した。]
					//-----------------
					Trace::TraceInformation( "MF_TOPOSTATUS_READY[ {0} ]; トポロジの準備が完了しました。", index );

					// IMFVideoDisplayControl の取得

					SAFE_COM_RELEASE( this->pControl );
					Debug::Assert( SUCCEEDED( hr = ::MFGetService( this->pMediaSession, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS( &( this->pControl ) ) ) ) );		// Debugで成功してReleaseで失敗するケースはないだろうと想定。

					// IMFAttributes の取得

					SAFE_COM_RELEASE( this->pAttributes );
					Debug::Assert( SUCCEEDED( hr = ::MFGetService( this->pMediaSession, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS( &( this->pAttributes ) ) ) ) );	// 同上。

					break;
					//-----------------
					#pragma endregion

				case MF_TOPOSTATUS_STARTED_SOURCE:
					#pragma region [ トポロジのメディアソースからの読み込みを開始した。]
					//-------------------------
					Trace::TraceInformation( "MF_TOPOSTATUS_STARTED_SOURCE[ {0} ]; MediaSessionがトポロジのメディアソースから読み込みを開始しました。", index );
					this->Segments[ index ].b再生中 = true;
					this->t必要ならセグメントローテーションを行う();
					break;
					//-------------------------
					#pragma endregion

				case MF_TOPOSTATUS_SINK_SWITCHED:
					#pragma region [ メディアシンクの接続元がこのトポロジに切り替えられた。]
					//-------------------------
					Trace::TraceInformation( "MF_TOPOSTATUS_SINK_SWITCHED[ {0} ]; メディアシンクの接続元がこのトポロジに切り替えられました。", index );
					break;
					//-------------------------
					#pragma endregion

				case MF_TOPOSTATUS_ENDED:
					#pragma region [ トポロジの再生が完了した。]
					//-------------------------
					Trace::TraceInformation( "MF_TOPOSTATUS_ENDED[ {0} ]; トポロジの再生が完了しました。（MediaSessionはまだトポロジを使用している可能性がある。）", index );
					this->Segments[ index ].b再生中 = false;
					this->t必要ならセグメントローテーションを行う();
					break;
					//-------------------------
					#pragma endregion

				default:
					Trace::TraceInformation( String::Format( "Unkonwn Status: {0}", (int) status ) );
					break;
				}
			}
			Trace::Unindent();
			break;
			//-----------------
			#pragma endregion

		case MESessionStarted:
			#pragma region [ 再生が開始された（再生開始準備完了）。]
			//-----------------
			Trace::TraceInformation( "MESessionStarted; 再生が開始されました。" );
			Trace::Indent();
			{
				// ステータスを「再生中」に変更。

				this->Status.Set( CStatus::EStatus::再生中 );


				// 描画開始イベントを発火。

				if( FALSE == ( bResult = ::SetEvent( this->evMESessionStarted ) ) )
				{
					tFALSEならTraceError( "CMediaSessionNative::evMESessionStated の発火", bResult );
					Trace::Unindent();
					goto failed;
				}
			}
			Trace::Unindent();
			break;
			//-----------------
			#pragma endregion

		case MESessionStopped:
			#pragma region [ 再生が停止された（再生停止準備完了）。]
			//-----------------
			Trace::TraceInformation( "MESessionStopped; 再生が停止されました。" );
			Trace::Indent();
			{
				// ステータスを「停止中」に変更。

				this->Status.Set( CStatus::EStatus::停止中 );


				// 描画停止フラグを立て、イベントを発火。

				if( FALSE == ( bResult = ::SetEvent( this->evMESessionStopped ) ) )
				{
					tFALSEならTraceError( "CMediaSessionNative::evMESessionStopped の発火", bResult );
					Trace::Unindent();
					goto failed;
				}
			}
			Trace::Unindent();
			break;
			//-----------------
			#pragma endregion

		case MESessionPaused:
			#pragma region [ 再生が一時停止された（再生一時停止準備完了）。]
			//-----------------
			Trace::TraceInformation( "MESessionPaused; 再生が一時停止されました。" );
			Trace::Indent();
			{
				// ステータスを「一時停止中」に変更。

				this->Status.Set( CStatus::EStatus::一時停止中 );
			}
			Trace::Unindent();
			break;
			//-----------------
			#pragma endregion

		case MEEndOfPresentation:
			#pragma region [ メディアソースのデータが尽きた。（再生はまだ終了していない可能性が高い。）]
			//-------------------------
			Trace::TraceInformation( "MEEndOfPresentation; メディアソースが終了しました。" );
			Trace::Indent();
			{
			}
			Trace::Unindent();
			break;
			//-------------------------
			#pragma endregion

		case MEEndOfPresentationSegment:
			#pragma region [ セグメントのメディアソースのデータが尽きた。（再生はまだ終了していない可能性が高い。）]
			//-------------------------
			Trace::TraceInformation( "MEEndOfPresentationSegment; セグメントのメディアソースが終了しました。" );
			Trace::Indent();
			{
			}
			Trace::Unindent();
			break;
			//-------------------------
			#pragma endregion

		case MESessionEnded:
			#pragma region [ 再生が終了した。]
			//-----------------
			Trace::TraceInformation( "MESessionEnded; 再生が終了されました。" );
			Trace::Indent();
			{
				// ステータスを「再生終了」に変更。

				this->Status.Set( CStatus::EStatus::再生終了 );
			}
			Trace::Unindent();
			break;
			//-----------------
			#pragma endregion

		case MENewPresentation:
			#pragma region [ 次のセグメントをMediaSessionにキューイングするよう指示された。]
			//-----------------
			Trace::TraceInformation( "MENewPresentatio; 次のセグメントの再生準備が指示されました。" );
			Trace::Indent();
			{
				IMFPresentationDescriptor *pPD = NULL;

				try
				{
					#pragma region [ pEvent からプレゼンテーション記述子 pPD を取得。]
					//-------------------------
					PROPVARIANT var;
					::PropVariantInit( &var );
					if( FAILED( hr = pEvent->GetValue( &var ) ) ||
						( VT_UNKNOWN != var.vt ) ||
						( FAILED( hr = var.punkVal->QueryInterface( __uuidof( IMFPresentationDescriptor ), (void**) &pPD ) ) ) )
					{
						tFAILEDならTraceError( "MENewPresentation: プレゼンテーション記述子の取得", hr );
						::PropVariantClear( &var );
						Trace::Unindent();
						goto failed;
					}
					//-------------------------
					#pragma endregion

					this->t次のセグメントをMediaSessionに登録する( pPD );
				}
				finally
				{
					SAFE_COM_RELEASE( pPD );
				}
			}
			Trace::Unindent();
			break;
			//-----------------
#pragma endregion

		case MESessionNotifyPresentationTime:
			#pragma region [ 新しい再生が開始された。]
			//-------------------------
			Trace::TraceInformation( "MESessionNotifyPresentationTime; 新しいプレゼンテーションが開始されました。" );
			Trace::Indent();
			{
				UINT64 startPresentationTime;
				if( SUCCEEDED( hr = pEvent->GetUINT64( MF_EVENT_START_PRESENTATION_TIME, &startPresentationTime ) ) )
					Trace::TraceInformation( "プレゼンテーション開始時刻 = {0}", startPresentationTime );

				UINT64 presentationTimeOffset;
				if( SUCCEEDED( hr = pEvent->GetUINT64( MF_EVENT_PRESENTATION_TIME_OFFSET, &presentationTimeOffset ) ) )
					Trace::TraceInformation( "ソースとプレゼンテーションの時刻オフセット = {0}", presentationTimeOffset );

				UINT64 startPresentationTimeAtOutput;
				if( SUCCEEDED( hr = pEvent->GetUINT64( MF_EVENT_START_PRESENTATION_TIME_AT_OUTPUT, &startPresentationTimeAtOutput ) ) )
					Trace::TraceInformation( "メディアシンクが描画を開始する時刻 = {0}", startPresentationTimeAtOutput );
			}
			Trace::Unindent();
			break;
			//-------------------------
			#pragma endregion

		case MESessionCapabilitiesChanged:
			#pragma region [ セッションの能力が変化した。]
			//-------------------------
			//Trace::TraceInformation( "MESessionCapabilitiesChanged; MediaSession の能力値(caps)が変化しました。" );
			//Trace::Indent();
			//{
			//	UINT32 sessionCaps;
			//	if( SUCCEEDED( hr = pEvent->GetUINT32( MF_EVENT_SESSIONCAPS, &sessionCaps ) ) )
			//		Trace::TraceInformation( "新しいCAPS = 0x{0:X8}", sessionCaps );

			//	UINT32 sessionCapsDelta;
			//	if( SUCCEEDED( hr = pEvent->GetUINT32( MF_EVENT_SESSIONCAPS_DELTA, &sessionCapsDelta ) ) )
			//		Trace::TraceInformation( "CAPSの差分 = 0x{0:X8}", sessionCapsDelta );
			//}
			//Trace::Unindent();
			break;
			//-------------------------
			#pragma endregion

		case MESessionStreamSinkFormatChanged:
			#pragma region [ メディアシンクのフォーマットが変更された。]
			//-------------------------
			//Trace::TraceInformation( "MESessionStreamSinkFormatChanged; メディアシンクのフォーマットが変更されました。" );
			//Trace::Indent();
			//{
			//	UINT64 outputNode;
			//	if( SUCCEEDED( hr = pEvent->GetUINT64( MF_EVENT_OUTPUT_NODE, &outputNode ) ) )
			//		Trace::TraceInformation( "新しいシンクのトポロジID: 0x{0:X16}", outputNode );
			//}
			//Trace::Unindent();
			break;
			//-------------------------
			#pragma endregion

		default:
			Trace::TraceInformation( String::Format( "MediaEventType: {0} はサポートされていません。", meType ) );
			break;
		}

	failed:
		SAFE_COM_RELEASE( pEvent );
		return hr;
	}


	// IMFAsyncCallback 実装。例外を発生させてはならない。

	STDMETHODIMP CMediaSessionNative::GetParameters( DWORD *pdwFlags, DWORD *pdwQueue )
	{
		*pdwFlags = MFASYNC_FAST_IO_PROCESSING_CALLBACK;	// 「コールバックは 1ms 以内に完了する」宣言
		*pdwQueue = MFASYNC_CALLBACK_QUEUE_STANDARD;		// 「ワークキューは MediaFoundation の標準のものを使用する」宣言
		return S_OK;
	}
	STDMETHODIMP CMediaSessionNative::Invoke( IMFAsyncResult *pAsyncResult )
	{
		// ※ このスレッドはワークキュースレッドが実行する。

		HRESULT hr = S_OK;
		HRESULT hrイベントステータス = S_OK;
		IMFMediaEvent *pイベント = NULL;
		MediaEventType eイベントタイプ = MEUnknown;

		try
		{
			#pragma region [ イベントとイベントタイプを取得する。]
			//-------------------------
			tFAILEDなら帰還( // MesiaSession からのメディアイベントの取得
				hr = this->pMediaSession->EndGetEvent( pAsyncResult, &pイベント ) );    // 非同期処理の終了操作なので End.. である。このメソッドの最後で再び Begin... する。

			tFAILEDなら帰還( // メディアイベントからのイベントタイプの取得
				hr = pイベント->GetType( &eイベントタイプ ) );

			tFAILEDなら帰還( // メディアイベントからのイベントステータスの取得
				hr = pイベント->GetStatus( &hrイベントステータス ) );

			tFAILEDなら帰還( //イベントステータスの確認; イベントステータスが「失敗」ならここで終了。
				hrイベントステータス );
			//-------------------------
			#pragma endregion

			#pragma region [ MESessionClosed イベントなら終了イベントを発火して通知し、そうでなければ次のイベント待ちを開始する。]
			//-------------------------
			if( MESessionClosed == eイベントタイプ )
			{
				// evMESessionClosedイベントを発火。
				if( ::SetEvent( this->evMESessionClosed ) == FALSE )
					return E_FAIL;

				Trace::TraceInformation( "MESessionClosed; MediaSession のクローズが完了しました。" );
			}
			else
			{
				// 次のイベント待ちを開始。
				tFAILEDなら帰還( hr = this->pMediaSession->BeginGetEvent( this, NULL ) );
			}
			//-------------------------
			#pragma endregion

			#pragma region [ アプリが MESessionClosed 待ちに入ってないなら、MediaSessionイベントをアプリの進行スレッドへ PostThreadMessage する。]
			//-------------------------
			if( this->Status.Get() != CStatus::EStatus::閉鎖待ち )
			{
				pイベント->AddRef();

				if( 0 != this->dwイベント処理スレッドID )
				{
					STMSEVENT *pstEvent = (STMSEVENT*) malloc( sizeof( STMSEVENT ) );
					if( NULL == pstEvent )
						return E_OUTOFMEMORY;
					pstEvent->pMediaSessionNative = this;
					pstEvent->pEvent = pイベント;
					::PostThreadMessage( this->dwイベント処理スレッドID, WM_APP_MEDIASESSION_EVENT, (WPARAM) pstEvent, NULL );	// WPARAM は、常にターゲットOSのポインタに変換可能。
				}
			}
			//-------------------------
			#pragma endregion
		}
		finally
		{
			SAFE_COM_RELEASE( pイベント );
		}

		return S_OK;
	}

	// IUnknown 実装。例外を発生させてはならない。

	STDMETHODIMP CMediaSessionNative::QueryInterface( REFIID riid, void **ppvObject )
	{
		if( riid == __uuidof( IUnknown ) )
		{
			Debug::Assert( ppvObject != NULL );
			*ppvObject = static_cast<IUnknown*>( this );
		}

		#pragma region [ その他（エラー）]
		//-----------------
		else
		{
			*ppvObject = NULL;
			return E_NOINTERFACE;
		}
		//-----------------
		#pragma endregion

		this->AddRef();

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

		if( c == 0 )
			delete this;

		return c;
	}

//private:

	void CMediaSessionNative::tトポロジを作成する( HWND hWnd, LPCWSTR stgファイル名, bool b音声あり, IMFTopology** ppTopology )
	{
		HRESULT hr = S_OK;

		*ppTopology = NULL;

		IMFTopology* pTopology = NULL;
		IMFMediaSource *pMediaSource = NULL;
		IMFPresentationDescriptor *pプレゼンテーション記述子 = NULL;

		try
		{
			// メディアソースを作成する。

			#pragma region [ メディアソースを stgファイル名から作成して pMediaSource に取得。]
			//-----------------
			{
				IMFSourceResolver *pSourceResolver = NULL;
				IUnknown *pSourceObjectIUnknown = NULL;

				try
				{
					// ソースリゾルバを作成する。

					tFAILEDなら例外発生( L"ソースリゾルバの作成",
						hr = ::MFCreateSourceResolver( &pSourceResolver ) );


					// ソースオブジェクトを IUnknown で取得。

					MF_OBJECT_TYPE eObjectType = MF_OBJECT_INVALID;
					tFAILEDなら例外発生( L"ソースオブジェクト(IUnknown)の取得",
						hr = pSourceResolver->CreateObjectFromURL( stgファイル名, MF_RESOLUTION_MEDIASOURCE, NULL, &eObjectType, &pSourceObjectIUnknown ) );


					// IUnknown から IMFMediaSource を取得。

					tFAILEDなら例外発生( L"ソースオブジェクトの IUnknown からの IMFMediaSource の取得",
						hr = pSourceObjectIUnknown->QueryInterface( IID_IMFMediaSource, (void**) &pMediaSource ) );
				}
				finally
				{
					// ソースリゾルバ他を解放。

					SAFE_COM_RELEASE( pSourceObjectIUnknown );
					SAFE_COM_RELEASE( pSourceResolver );
				}
			}
			//-----------------
			#pragma endregion
			#pragma region [ メディアソースのプレゼンテーション記述子をメディアソースから pプレゼンテーション記述子に取得。]
			//-----------------
			tFAILEDなら例外発生( L"メディアソースからのプレゼンテーション記述子の取得",
				hr = pMediaSource->CreatePresentationDescriptor( &pプレゼンテーション記述子 ) );
			//-----------------
			#pragma endregion


			// トポロジを作成する。

			#pragma region [ 新しいトポロジを作成する。]
			//-----------------
			tFAILEDなら例外発生( L"新しいトポロジの作成",
				hr = ::MFCreateTopology( &pTopology ) );
			//-----------------
	#pragma endregion
			
			DWORD dwストリーム数 = 0;

			#pragma region [ メディアソースのストリーム数を、プレゼンテーション記述子から dwストリーム数 に取得。]
			//-----------------
			tFAILEDなら例外発生( L"メディアソースのストリーム数の取得",
				hr = pプレゼンテーション記述子->GetStreamDescriptorCount( &dwストリーム数 ) );
			//-----------------
			#pragma endregion
			#pragma region [ すべてのストリームをトポロジに登録する。]
			//-----------------
			for( DWORD dwストリーム番号 = 0; dwストリーム番号 < dwストリーム数; dwストリーム番号++ )
			{
				IMFStreamDescriptor *pストリーム記述子 = NULL;
				IMFTopologyNode *pストリームのソースノード = NULL;
				IMFTopologyNode *pストリームの出力ノード = NULL;
				GUID guidMajorType;

				try
				{
					#pragma region [ ストリーム記述子を取得する。]
					//-----------------
					{
						BOOL bストリームが選択されている = FALSE;

						tFAILEDなら例外発生( L"現在のストリーム番号に対応するストリーム記述子の取得",
							hr = pプレゼンテーション記述子->GetStreamDescriptorByIndex( dwストリーム番号, &bストリームが選択されている, &pストリーム記述子 ) );

						if( !bストリームが選択されている )
							continue;		// 選択されていないストリームならば無視。
					}
					//-----------------
					#pragma endregion

					#pragma region [ ストリームのソースノードを作成する。]
					//-----------------
					tFAILEDなら例外発生( L"新しいソースノード（ソースストリームノード）の作成",
						hr = ::MFCreateTopologyNode( MF_TOPOLOGY_SOURCESTREAM_NODE, &pストリームのソースノード ) );

					// 必須の３属性を設定する。

					tFAILEDなら例外発生( L"ソースノードの属性へのメディアソースの登録",
						hr = pストリームのソースノード->SetUnknown( MF_TOPONODE_SOURCE, pMediaSource ) );

					tFAILEDなら例外発生( L"ソースノードの属性へのプレゼンテーション記述子の登録",
						hr = pストリームのソースノード->SetUnknown( MF_TOPONODE_PRESENTATION_DESCRIPTOR, pプレゼンテーション記述子 ) );

					tFAILEDなら例外発生( L"ソースノードの属性へのストリーム記述子の登録",
						hr = pストリームのソースノード->SetUnknown( MF_TOPONODE_STREAM_DESCRIPTOR, pストリーム記述子 ) );
					//-----------------
					#pragma endregion

					#pragma region [ ストリームの出力ノードを作成する。]
					//-----------------
					{
						IMFActivate *pRendererActivate = NULL;

						try
						{
							#pragma region [ 新しい出力ノードを作成する。]
							//-----------------
							tFAILEDなら例外発生( L"新しい出力ノードの作成",
								hr = MFCreateTopologyNode( MF_TOPOLOGY_OUTPUT_NODE, &pストリームの出力ノード ) );
							//-----------------
							#pragma endregion

							#pragma region [ ストリームのメジャータイプを取得する。]
							//-----------------
							{
								IMFMediaTypeHandler *pMediaTypeHandler = NULL;

								try
								{
									tFAILEDなら例外発生( L"ストリームからのメディアタイプハンドラの取得",
										hr = pストリーム記述子->GetMediaTypeHandler( &pMediaTypeHandler ) );

									tFAILEDなら例外発生( L"メディアタイプハンドラからのメジャータイプGUIDの取得",
										hr = pMediaTypeHandler->GetMajorType( &guidMajorType ) );
								}
								finally
								{
									SAFE_COM_RELEASE( pMediaTypeHandler );
								}
							}
							//-----------------
							#pragma endregion

							#pragma region [ ビデオ or オーディオレンダラアクティベートを作成。]
							//-----------------
							if( MFMediaType_Audio == guidMajorType )
							{
								#pragma region [ (A) オーディオレンダラアクティベートの作成 ]
								//-----------------
								tFAILEDなら例外発生( L"オーディオレンダラの IMFActive の作成",
									hr = MFCreateAudioRendererActivate( &pRendererActivate ) );
								//-----------------
								#pragma endregion
							}
							else if( MFMediaType_Video == guidMajorType )
							{
								#pragma region [ (B) ビデオレンダラ(EVR)アクティベート with カスタムプレゼンタ の作成 ]
								//-----------------
								IUnknown *pActivate = NULL;

								try
								{
									// ビデオレンダラアクティベートを作成する。

									tFAILEDなら例外発生( L"ビデオレンダラの IMFActivate の作成",
										hr = ::MFCreateVideoRendererActivate( hWnd, &pRendererActivate ) );


									// カスタムプレゼンタアクティベートを生成する。

									CustomEVRPresenter::CreateInstance( NULL, IID_IUnknown, (void**) &pActivate );


									// カスタムプレゼンタアクティベートをレンダラアクティベートに登録する。

									tFAILEDなら例外発生( L"レンダラへのカスタムプレゼンタの設定",
										hr = pRendererActivate->SetUnknown( MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, pActivate ) );
								}
								finally
								{
									SAFE_COM_RELEASE( pActivate );
								}
								//-----------------
								#pragma endregion
							}
							else
							{
								tHRESULT付き例外発生( MF_E_INVALIDTYPE, L"未知のメディアタイプです。" );
							}
							//-----------------
							#pragma endregion

							#pragma region [ ビデオ or オーディオレンダラアクティベートを出力ノードへ設定。]
							//-----------------
							tFAILEDなら例外発生( L"作成した IMFActivate の出力ノードへの設定",
								hr = pストリームの出力ノード->SetObject( pRendererActivate ) );
							//-----------------
							#pragma endregion
						}
						finally
						{
							SAFE_COM_RELEASE( pRendererActivate );
						}
					}
					//-----------------
					#pragma endregion

					if( b音声あり || guidMajorType != MFMediaType_Audio )	// 音声なしならオーディオレンダラは追加しない。
					{
						// MediaFoundation の SAR はWASAPI共有モードを使用する。b音声あり = true で、かつ、ここの時点で既にWASAPI排他モードが稼働している場合は、トポロジが完成せずにハングアップするので注意。

						#pragma region [ ソースノードと出力ノードをトポロジに追加して接続する。]
						//-----------------
						tFAILEDなら例外発生( L"トポロジへのソースノードの追加",
							hr = pTopology->AddNode( pストリームのソースノード ) );

						tFAILEDなら例外発生( L"トポロジへの出力ノードの追加",
							hr = pTopology->AddNode( pストリームの出力ノード ) );

						DWORD dw出力インデックス = 0;
						DWORD dw出力ノードの入力インデックス = 0;
						tFAILEDなら例外発生( L"トポロジでのノード間の接続",
							hr = pストリームのソースノード->ConnectOutput( dw出力インデックス, pストリームの出力ノード, dw出力ノードの入力インデックス ) );
						//-----------------
						#pragma endregion
					}
				}
				finally
				{
					SAFE_COM_RELEASE( pストリーム記述子 );
					SAFE_COM_RELEASE( pストリームのソースノード );
					SAFE_COM_RELEASE( pストリームの出力ノード );
				}
			}
			//-----------------
			#pragma endregion
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			SAFE_COM_RELEASE( pTopology );
			throw gcnew Exception( L"MediaSession の構築に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
		finally
		{
			SAFE_COM_RELEASE( pプレゼンテーション記述子 );
			SAFE_COM_RELEASE( pMediaSource );
		}

		*ppTopology = pTopology;	// 参照カウンタも引き継ぐ。
	}
	void CMediaSessionNative::t次のセグメントをMediaSessionに登録する( IMFPresentationDescriptor *pPD )
	{
		HRESULT hr = S_OK;

		IMFMediaSourceTopologyProvider *pTopoProvider = NULL;
		IMFTopology *pTopology = NULL;

		try
		{
			tFAILEDなら例外発生( L"トポロジープロバイダの取得",
				hr = this->pSequencerSource->QueryInterface( IID_PPV_ARGS( &pTopoProvider ) ) );

			tFAILEDなら例外発生( L"プレゼンテーション記述子に対応するトポロジの取得",
				hr = pTopoProvider->GetMediaSourceTopology( pPD, &pTopology ) );

			tFAILEDなら例外発生( L"MediaSessionへのトポロジの設定",
				hr = this->pMediaSession->SetTopology( NULL, pTopology ) );
		}
		finally
		{
			SAFE_COM_RELEASE( pTopology );
			SAFE_COM_RELEASE( pTopoProvider );
		}
	}
	void CMediaSessionNative::t必要ならセグメントローテーションを行う()
	{
		// MF_TOPOSTATUS_STARTED_SOURCE, MF_TOPOSTATUS_ENDED から呼び出される。

		if( false == this->bループする.Get() )
			return;

		HRESULT hr = S_OK;

		int n表示中のIndex = this->n表示中のindex.Get();
		bool b再生中0 = this->Segments[ 0 ].b再生中;
		bool b再生中1 = this->Segments[ 1 ].b再生中;

		if( 0 == n表示中のIndex )
		{
			if( ( false == b再生中0 ) && ( true == b再生中1 ) )		// 0 の再生が終了し、1 の再生が開始された。
			{
				if( SUCCEEDED( hr ) )
					hr = this->pSequencerSource->DeleteTopology( this->Segments[ 0 ].SegmentID );												// 0 のセグメントを削除し、

				if( SUCCEEDED( hr ) )
					hr = this->pSequencerSource->AppendTopology( this->Segments[ 0 ].pTopology, 0, &( this->Segments[ 0 ].SegmentID ) );		// 再びシーケンサーソースの末尾に追加。

				this->n表示中のindex.Set( 1 );

				Trace::TraceInformation( "セグメント 0 を削除し、新しくトポロジをシーケンサーソースの末尾に追加しました。" );
			}
		}
		else
		{
			if( ( true == b再生中0 ) && ( false == b再生中1 ) )		// 1 の再生が終了し、0 の再生が開始された。
			{
				if( SUCCEEDED( hr ) )
					hr = this->pSequencerSource->DeleteTopology( this->Segments[ 1 ].SegmentID );												// 1 のセグメントを削除し、

				if( SUCCEEDED( hr ) )
					hr = this->pSequencerSource->AppendTopology( this->Segments[ 1 ].pTopology, 0, &( this->Segments[ 1 ].SegmentID ) );		// 再びシーケンサーソースの末尾に追加。

				this->n表示中のindex.Set( 0 );

				Trace::TraceInformation( "セグメント 1 を削除し、新しくトポロジをシーケンサーソースの末尾に追加しました。" );
			}
		}
	}
	void CMediaSessionNative::tEVRへデバイス変更通知を送る()
	{
		Debug::Assert( this->pControl != NULL );

		HRESULT hr = S_OK;

		try
		{
			tFAILEDなら例外発生( L"独自定義メッセージでのプレゼンタへの通知",
				hr = pControl->SetRenderingPrefs( MFVideoRenderPrefs_DisplayChange ) );
		}
		catch( Exception^ e原因 )
		{
			#pragma region [ 失敗 ]
			//-----------------
			throw gcnew Exception( L"EVRへのデバイス変更通知に失敗しました。", e原因 );
			//-----------------
			#pragma endregion
		}
	}

//public:

	// 全インスタンスの管理。

	bool CMediaSessionNative::AllInstances::b初期化済み = false;
	CritSec CMediaSessionNative::AllInstances::csInstances;
	CMediaSessionNative* CMediaSessionNative::AllInstances::Instances[ MAX_MEDIASESSION_INSTANCES ];

	void CMediaSessionNative::AllInstances::t追加( CMediaSessionNative *pInstance )
	{
		AutoLock lock( csInstances );

		#pragma region [ 初めての追加なら初期化する。]
		//-------------------------
		if( false == b初期化済み )
		{
			for( int i = 0; i < MAX_MEDIASESSION_INSTANCES; i++ )
				Instances[ i ] = NULL;

			b初期化済み = true;
		}
		//-------------------------
		#pragma endregion


		for( int i = 0; i < MAX_MEDIASESSION_INSTANCES; i++ )
		{
			if( NULL == Instances[ i ] )
			{
				Instances[ i ] = pInstance;
				break;
			}
		}
	}
	void CMediaSessionNative::AllInstances::t削除( CMediaSessionNative *pInstance )
	{
		AutoLock lock( csInstances );

		for( int i = 0; i < MAX_MEDIASESSION_INSTANCES; i++ )
		{
			if( Instances[ i ] == pInstance )
			{
				Instances[ i ] = NULL;
				break;
			}
		}
	}
	
	void CMediaSessionNative::AllInstances::tEVRへデバイス変更通知を送る()
	{
		if( false == b初期化済み )
			return;

		AutoLock lock( csInstances );

		for( int i = 0; i < MAX_MEDIASESSION_INSTANCES; i++ )
		{
			if( NULL != Instances[ i ] )
			{
				Instances[ i ]->tEVRへデバイス変更通知を送る();
				break;
			}
		}
	}
	void CMediaSessionNative::AllInstances::t次のサンプルを要求する()
	{
		if( false == b初期化済み )
			return;

		AutoLock lock( csInstances );

		for( int i = 0; i < MAX_MEDIASESSION_INSTANCES; i++ )
		{
			if( NULL != Instances[ i ] )
			{
				Instances[ i ]->t次のサンプルを要求する();
				break;
			}
		}
	}
	void CMediaSessionNative::AllInstances::t停止する()
	{
		if( false == b初期化済み )
			return;

		AutoLock lock( csInstances );

		for( int i = 0; i < MAX_MEDIASESSION_INSTANCES; i++ )
		{
			if( NULL != Instances[ i ] )
			{
				Instances[ i ]->t停止する();
				break;
			}
		}
	}
}