読者です 読者をやめる 読者になる 読者になる

S.F. Page

Programming,Music,etc...

Media Foundationの再生サンプルの状態管理をMSMで行う

Boost Media Foundation C++ Windows Audio Windows API

今どうなっているかというと、コンパイルが通り実行できるようになった。状態管理はMSMで行って遷移と同時にクラス外にイベントを飛ばすのはBoost.Signalで行う。

まだ状態テーブル定義は完全ではない。コードを見ていただくとわかると思うが、実装中に状態を見て分岐したり、状態を更新するコードはなくなっている。すべてはBoost.MSMに任せたので。でもAPIとのつなぎ部分、APIからのイベントはインターフェースでコールバックされるので、その状態を見て分岐するコードはまだ残っている。これらもうまくやればMSMに任せることができるかもしれない。

状態マシン自体が状態を変化させる必要があるのだが、process_eventをそのまま呼び出すことはできない。実際にインスタンス化されるのはmsm::back::state_machine<>でtypedefされたもので、process_eventはmsm::back::state_machineテンプレート中にあるからだ。呼び出し方はドキュメントに書いてあったが、static_cast<msm::back::state_machine<this_class>&>(*this).process_event()みたいにする。

できる限りUIとは疎にしておきたいので、ウィンドウメッセージを飛ばすコードはやめ、すべてboost.signalで飛ばすことにした。あとでUIを差し替えるときにらくなので。少し気になるのはスレッドとの絡みだが今のところは気にしないことにしよう。

昔はsignalにconnectするのにファンクタやメンバ関数ポインタにbindする必要があったり何かと面倒だったが、ラムダ式のおかげで簡潔にかけるようになったのがうれしいね。

     // ファイルオープン完了後の処理
      sf::player::PlayerPtr player = application::instance()->Player();
      player->OnOpenComplete().connect([this](sf::player::Player &) -> void
      {
        player_ready();
      }
        );

      // 再生時イベントの処理
      player->OnStart().connect([this](sf::player::Player &) -> void 
      {
        play_();
      }
        );

      // Pauseイベントの処理
      player->OnPause().connect([this](sf::player::Player &) -> void 
      {
        pause_();
      }
        );

      // 停止イベントの処理
      player->OnStop().connect([this](sf::player::Player &) -> void 
      {
        stop_();
      }
        );

そういうわけで動画の読み込み・再生・停止が行えるようになった。

20130126

ソースコード

player.h

#ifndef PLAYER_H
#define PLAYER_H

#include <new>
#include <windows.h>
#include <shobjidl.h> 
#include <shlwapi.h>
#include <assert.h>
#include <strsafe.h>

// Media Foundation headers
#include <mfapi.h>
#include <mfidl.h>
#include <mferror.h>
#include <evr.h>

#include "resource.h"

#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>

_WRL_PTR_TYPEDEF(IMFMediaSession);
_WRL_PTR_TYPEDEF(IMFMediaSource);
_WRL_PTR_TYPEDEF(IMFVideoDisplayControl);
_WRL_PTR_TYPEDEF(IMFTopology);
_WRL_PTR_TYPEDEF(IMFPresentationDescriptor);
_WRL_PTR_TYPEDEF(IMFMediaEvent);
_WRL_PTR_TYPEDEF(IMFSourceResolver);
_WRL_PTR_TYPEDEF(IMFStreamDescriptor);
_WRL_PTR_TYPEDEF(IMFTopologyNode);
_WRL_PTR_TYPEDEF(IMFActivate);
_WRL_PTR_TYPEDEF(IMFMediaTypeHandler);
//_WRL_PTR_TYPEDEF();
//_WRL_PTR_TYPEDEF();

namespace sf {
  namespace player {
    namespace msmf = boost::msm::front;

    // const UINT WM_APP_PLAYER_EVENT = WM_APP + 1;   
    // WPARAM = IMFMediaEvent*, WPARAM = MediaEventType

    enum struct PlayerState
    {
        Closed = 0,     // No session.
        Ready,          // Session was created, ready to open a file. 
        OpenPending,    // Session is opening a file.
        Started,        // Session is playing a file.
        Paused,         // Session is paused.
        Stopped,        // Session is stopped (ready to play). 
        Closing         // Application has closed the session, but is waiting for MESessionClosed.
    };

    // 状態クラス定義
    struct Closed : msmf::state<> {}; // セッションなし
    struct Ready : msmf::state<> {}; // セッションが作成され、ファイルを開く準備ができている。
    struct OpenPending :msmf::state<> {};// セッションはファイルをオープンしている
    struct Started : msmf::state<> {};// セッションは演奏している。
    struct Paused : msmf::state<> {};// セッションは一時停止している。
    struct Stopped : msmf::state<> {};// セッションは停止している(演奏可能状態である)。
    struct Closing : msmf::state<> {};// アプリケーションはセッションを閉じたが、MESessionClosed状態を待っている。

    // プロパティ用バリアントのラッパ
    struct prop_variant 
    {
      prop_variant()
      {
        PropVariantInit(&value_);// 
      }

      ~prop_variant()
      {
        PropVariantClear(&value_);
      }

      PROPVARIANT* get(){ return &value_;};

      PROPVARIANT* operator &(){return get();}

      operator PROPVARIANT*() {return get();}

    private:
      PROPVARIANT value_;
    };


    namespace ev 
    {
      struct Init {}; // 初期化イベント
      struct OpenURL  // ファイルを開くイベント
      {
        OpenURL() {};
        OpenURL(const OpenURL& s) : url_(s.url()) {}
        explicit OpenURL(const std::wstring& u) : url_(u) {}
        const std::wstring& url() const {return url_;}
      private:
        std::wstring url_;
      };
      struct OpenComplete {};// ファイルオープン完了
      struct Play {};// 演奏開始
      struct End {};// 終了
      struct Pause {};// 一時停止
      struct Stop {};// 停止
      struct Close {};// 閉じる
    }

    struct Player_ : public IMFAsyncCallback,public boost::msm::front::state_machine_def<sf::player::Player_>
    {
      typedef boost::msm::back::state_machine< Player_ > this_type;
      typedef Microsoft::WRL::ComPtr<this_type> ptr_type;
      friend ptr_type CreatePlayer(HWND hVideo, HWND hEvent);
      friend struct transition_table;
      typedef boost::signals2::signal<void( this_type &)> signal_t;


      // IUnknown methods
      STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
      STDMETHODIMP_(ULONG) AddRef();
      STDMETHODIMP_(ULONG) Release();

      // IMFAsyncCallback methods
      STDMETHODIMP  GetParameters(DWORD*, DWORD*)
      {
        // Implementation of this method is optional.
        return E_NOTIMPL;
      }
      STDMETHODIMP  Invoke(IMFAsyncResult* pAsyncResult);

      void       HandleEvent(UINT_PTR pUnkPtr);
 //     PlayerState   GetState() const { return m_state; }

      // Video functionality
      void       Repaint();
      void       ResizeVideo(WORD width, WORD height);

      BOOL          HasVideo() const { return (m_pVideoDisplay != NULL);  }

      signal_t& OnReady(){return OnReady_;}
      signal_t& OnOpenURL() {return OnOpenURL_;}
      signal_t& OnStart(){return OnStart_;}
      signal_t& OnEnd(){return OnEnd_;}
      signal_t& OnPause(){return OnPause_;}
      signal_t& OnStop(){return OnStop_;}
      signal_t& OnOpenComplete(){return OnOpenComplete_;}

      void OpenComplete()
      {
        OnOpenComplete_(static_cast<this_type&>(*this));
      }


    protected:


      // Constructor is private. Use static CreateInstance method to instantiate.
      Player_(HWND hVideo, HWND hEvent);

      // Destructor is private. Caller should call Release.
      virtual ~Player_(); 

      void CreateSession();
      void CloseSession();
      void StartPlayback();

      // Playback
      void initialize( ev::Init const& ev);
      void       open_url( ev::OpenURL const& openurl);
      void       play( ev::Play const& ev);
      void       pause( ev::Pause const& ev);
      void       stop( ev::Stop const& ev);
      void       shutdown( ev::Close const& ev);
      void  open_complete(ev::OpenComplete const&)
      {
        OnOpenComplete()(static_cast<this_type&>(*this));
      }

      // Media event handlers
      virtual void OnTopologyStatus(IMFMediaEventPtr pEvent);
      virtual void OnPresentationEnded(IMFMediaEventPtr pEvent);
      virtual void OnNewPresentation(IMFMediaEventPtr pEvent);

      // Override to handle additional session events.
      virtual void OnSessionEvent(IMFMediaEventPtr, MediaEventType) 
      { 
        //return S_OK; 
      }


      long                    m_nRefCount;        // Reference count.

      IMFMediaSessionPtr         m_pSession;
      IMFMediaSourcePtr          m_pSource;
      IMFVideoDisplayControlPtr  m_pVideoDisplay;

      HWND                    m_hwndVideo;        // Video window.
      HWND                    m_hwndEvent;        // App window to receive events.
     // PlayerState             m_state;            // Current state of the media session.
      HANDLE                  m_hCloseEvent;      // Event to wait on while closing.

    private:

      // 外部にイベントを飛ばすためのシグナル
      signal_t OnReady_;
      signal_t OnOpenURL_;
      signal_t OnOpenComplete_;
      signal_t OnStart_;
      signal_t OnEnd_;
      signal_t OnPause_;
      signal_t OnStop_;

    public:
// 状態遷移テーブル
struct transition_table : boost::mpl::vector
  //            現在状態      ,イベント           , 次の状態      , アクション               , ガード 
  < a_row       <Closed        ,ev::Init          ,Ready          ,&sf::player::Player_::initialize      >,
    a_row       <Ready         ,ev::OpenURL       ,OpenPending    ,&sf::player::Player_::open_url         >,
    a_row        <OpenPending   ,ev::OpenComplete  ,Stopped        ,&sf::player::Player_::open_complete>,
    a_row       <Started       ,ev::Pause         ,Paused         ,&sf::player::Player_::pause           >,
    a_row       <Started       ,ev::Stop          ,Stopped        ,&sf::player::Player_::stop            >,
    _row        <Started       ,ev::End           ,Stopped        >,
    a_row       <Paused        ,ev::Play          ,Started        ,&sf::player::Player_::play            >,
    a_row       <Paused        ,ev::Stop          ,Stopped        ,&sf::player::Player_::stop            >,
    a_row       <Stopped       ,ev::Play          ,Started        ,&sf::player::Player_::play            >,
    a_row       <Stopped       ,ev::OpenURL       ,OpenPending    ,&sf::player::Player_::open_url        >//,
 //   a_row       <msmf::none    ,ev::Close         ,Closed         ,&Player_::shutdown>
  >
{};
      typedef Closed initial_state;


      template <class FSM,class Event>
      void no_transition(Event const& e, FSM&,int state)
      {
        throw exception(L"No Transition");
      }

    };

    typedef boost::msm::back::state_machine< Player_ > Player;

    typedef Microsoft::WRL::ComPtr<Player> PlayerPtr;
    PlayerPtr CreatePlayer(HWND hVideo, HWND hEvent);

  }
}
#endif PLAYER_H

player.cpp


// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
#include "stdafx.h"
#include "Player.h"
#include <assert.h>

#if _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

#pragma comment(lib, "shlwapi")
#pragma comment(lib,"Mfplat.lib")
#pragma comment(lib,"Mf.lib")
#pragma comment(lib,"Mfuuid.lib")
#pragma comment(lib,"Strmiids.lib")

//Mf.lib Mfidl.h
namespace sf {
  namespace player {
    template <class Q>
    HRESULT GetEventObject(IMFMediaEvent *pEvent, Q **ppObject)
    {
      *ppObject = NULL;   // zero output

      PROPVARIANT var;
      HRESULT hr = pEvent->GetValue(&var);
      if (SUCCEEDED(hr))
      {
        if (var.vt == VT_UNKNOWN)
        {
          hr = var.punkVal->QueryInterface(ppObject);
        }
        else
        {
          hr = MF_E_INVALIDTYPE;
        }
        PropVariantClear(&var);
      }
      return hr;
    }

    IMFMediaSourcePtr CreateMediaSource(const std::wstring& pszURL);

    IMFTopologyPtr CreatePlaybackTopology(IMFMediaSourcePtr pSource, 
      IMFPresentationDescriptorPtr pPD, HWND hVideoWnd);

    //  Player_オブジェクトを生成するフリー関数
    PlayerPtr CreatePlayer(
      HWND hVideo,                  // Video window.
      HWND hEvent                  // Window to receive notifications.
      )           
    {

      PlayerPtr p(new Player(hVideo, hEvent));

      if (!p)
      {
        throw win32_error_exception(E_OUTOFMEMORY);
      }
      p->process_event(ev::Init());
//      p->processInitialize();
      return p;
    }

    void Player_::initialize(const ev::Init& ev)
    {
      // Start up Media Foundation platform.
      THROW_IF_ERR(MFStartup(MF_VERSION));
      m_hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
      THROW_IF_ERR(HRESULT_FROM_WIN32(GetLastError()));
    }

    Player_::Player_(HWND hVideo, HWND hEvent) : 
      m_hwndVideo(hVideo),
      m_hwndEvent(hEvent),
//      m_state(Closed),
      m_hCloseEvent(NULL),
      m_nRefCount(0)
    {
    }

    Player_::~Player_()
    {
      assert(m_pSession.Get() == NULL);  
      // If FALSE, the app did not call Shutdown().

      // When Player_ calls IMediaEventGenerator::BeginGetEvent on the
      // media session, it causes the media session to hold a reference 
      // count on the Player_. 

      // This creates a circular reference count between Player_ and the 
      // media session. Calling Shutdown breaks the circular reference 
      // count.

      // If CreateInstance fails, the application will not call 
      // Shutdown. To handle that case, call Shutdown in the destructor. 

      shutdown(ev::Close());
    }

    // IUnknown methods

    HRESULT Player_::QueryInterface(REFIID riid, void** ppv)
    {
      static const QITAB qit[] = 
      {
        QITABENT(Player_, IMFAsyncCallback),
        { 0 }
      };
      return QISearch(this, qit, riid, ppv);
    }

    ULONG Player_::AddRef()
    {
      return InterlockedIncrement(&m_nRefCount);
    }

    ULONG Player_::Release()
    {
      ULONG uCount = InterlockedDecrement(&m_nRefCount);
      if (uCount == 0)
      {
        delete this;
      }
      return uCount;
    }

    //  Open a URL for playback.
    void Player_::open_url( ev::OpenURL const& openurl)
    {
      // 1. Create a new media session.
      // 2. Create the media source.
      // 3. Create the topology.
      // 4. Queue the topology [asynchronous]
      // 5. Start playback [asynchronous - does not happen in this method.]

      IMFTopologyPtr pTopology;
      IMFPresentationDescriptorPtr pSourcePD;

      // Create the media session.
      CreateSession();

      // Create the media source.
      m_pSource = CreateMediaSource(openurl.url());

      // Create the presentation descriptor for the media source.
      THROW_IF_ERR(m_pSource->CreatePresentationDescriptor(&pSourcePD));

      // Create a partial topology.
      pTopology = CreatePlaybackTopology(m_pSource, pSourcePD, m_hwndVideo);

      // Set the topology on the media session.
      THROW_IF_ERR(m_pSession->SetTopology(0, pTopology.Get()));

//      OnOpenURL_();

      // m_state = OpenPending;

      // If SetTopology succeeds, the media session will queue an 
      // MESessionTopologySet event.
    }

    //  Pause playback.
    void Player_::pause( ev::Pause const& ev)    
    {
//      if (m_state != Started)
//      {
//        throw win32_error_exception(MF_E_INVALIDREQUEST);
//      }
//
//      if (!m_pSession  || !m_pSource)
//      {
//       throw win32_error_exception(E_UNEXPECTED);
//      }

      THROW_IF_ERR(m_pSession->Pause());
      OnPause()(static_cast<this_type&>(*this));
      
      //m_state = Paused;
    }

    // Stop playback.
    void Player_::stop( ev::Stop const& ev)
    {
      //if (m_state != Started && m_state != Paused)
      //{
      //  throw win32_error_exception( MF_E_INVALIDREQUEST );
      //}
      //if (!m_pSession)
      //{
      //  throw win32_error_exception( E_UNEXPECTED);
      //}

      THROW_IF_ERR(m_pSession->Stop());
      OnStop()(static_cast<this_type&>(*this));
    }

    //  Repaint the video window. Call this method on WM_PAINT.
    void Player_::Repaint()
    {
      if (m_pVideoDisplay)
      {
        m_pVideoDisplay->RepaintVideo();
      }
    }

    //  Resize the video rectangle.
    //
    //  Call this method if the size of the video window changes.

    void Player_::ResizeVideo(WORD width, WORD height)
    {
      if (m_pVideoDisplay)
      {
        // Set the destination rectangle.
        // Leave the default source rectangle (0,0,1,1).

        RECT rcDest = { 0, 0, width, height };

        THROW_IF_ERR(m_pVideoDisplay->SetVideoPosition(NULL, &rcDest));
      }
    }

    //  Callback for the asynchronous BeginGetEvent method.

    HRESULT Player_::Invoke(IMFAsyncResult *pResult)
    {
      MediaEventType meType = MEUnknown;  // Event type

      IMFMediaEventPtr pEvent;

      try {
        // Get the event from the event queue.
        THROW_IF_ERR(m_pSession->EndGetEvent(pResult, pEvent.GetAddressOf()));

        // Get the event type. 
        THROW_IF_ERR(pEvent->GetType(&meType));

        if (meType == MESessionClosed)
        {
          // The session was closed. 
          // The application is waiting on the m_hCloseEvent event handle. 
          SetEvent(m_hCloseEvent);
        }
        else
        {
          // For all other events, get the next event in the queue.
          THROW_IF_ERR(m_pSession->BeginGetEvent(this, NULL));
        }

        // Check the application state. 

        // If a call to IMFMediaSession::Close is pending, it means the 
        // application is waiting on the m_hCloseEvent event and
        // the application's message loop is blocked. 

        // Otherwise, post a private window message to the application. 

        //if (m_state != Closing)
        //{
        //  // Leave a reference count on the event.

        //  //PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT, 
        //  //  (WPARAM) pEvent.Detach(), (LPARAM)meType);
        //}

        HandleEvent((UINT_PTR)pEvent.Detach());

        return S_OK;
      } catch (win32_error_exception& e) 
      {
        return e.hresult();
      }
    }

    void Player_::HandleEvent(UINT_PTR pEventPtr)
    {
      MediaEventType meType = MEUnknown;  

      IMFMediaEventPtr pEvent;
      //    pEvent.Attach((IMFMediaEvent*)pEventPtr);
      pEvent.Attach(reinterpret_cast<IMFMediaEvent*>(pEventPtr));

      if (!pEvent)
      {
        throw win32_error_exception(E_POINTER);
      }

      // Get the event type.
      THROW_IF_ERR(pEvent->GetType(&meType));

      // Get the event status. If the operation that triggered the event 
      // did not succeed, the status is a failure code.
      HRESULT hrStatus = S_OK;
      THROW_IF_ERR(pEvent->GetStatus(&hrStatus));
      // Check if the async operation succeeded.
      THROW_IF_ERR(hrStatus);

      switch(meType)
      {
      case MESessionTopologyStatus:
        OnTopologyStatus(pEvent);
        break;

      case MEEndOfPresentation:
        OnPresentationEnded(pEvent);
        break;

      case MENewPresentation:
        OnNewPresentation(pEvent);
        break;

      default:
        OnSessionEvent(pEvent, meType);
        break;
      }
    }

    //  Release all resources held by this object.
    void Player_::shutdown( ev::Close const& ev)
    {
      // Close the session
      CloseSession();

      // Shutdown the Media Foundation platform
      MFShutdown();

      if (m_hCloseEvent)
      {
        CloseHandle(m_hCloseEvent);
        m_hCloseEvent = NULL;
      }
    }

    /// Protected methods

    void Player_::OnTopologyStatus(IMFMediaEventPtr pEvent)
    {
      UINT32 status; 

      THROW_IF_ERR(pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status));
      if (status == MF_TOPOSTATUS_READY)
      {
        safe_release(m_pVideoDisplay);

        // Get the IMFVideoDisplayControl interface from EVR. This call is
        // expected to fail if the media file does not have a video stream.

        (void)MFGetService(m_pSession.Get(), MR_VIDEO_RENDER_SERVICE, 
          IID_PPV_ARGS(m_pVideoDisplay.GetAddressOf()));
        static_cast<this_type&>(*this).process_event(ev::OpenComplete());
        //StartPlayback();
      }
    }


    //  Handler for MEEndOfPresentation event.
    void Player_::OnPresentationEnded(IMFMediaEventPtr pEvent)
    {
      // The session puts itself into the stopped state automatically.
      // m_state = Stopped;
      static_cast<this_type&>(*this).process_event(ev::Stop());
    }

    //  Handler for MENewPresentation event.
    //
    //  This event is sent if the media source has a new presentation, which 
    //  requires a new topology. 

    void Player_::OnNewPresentation(IMFMediaEventPtr pEvent)
    {
      IMFPresentationDescriptorPtr pPD;
      IMFTopologyPtr pTopology;

      // Get the presentation descriptor from the event.
      THROW_IF_ERR(GetEventObject(pEvent.Get(), pPD.GetAddressOf()));

      // Create a partial topology.
      pTopology = CreatePlaybackTopology(m_pSource, pPD,  m_hwndVideo);

      // Set the topology on the media session.
      THROW_IF_ERR(m_pSession->SetTopology(0, pTopology.Get()));

      // m_state = OpenPending;

    }

    //  Create a new instance of the media session.
    void Player_::CreateSession()
    {
      // Close the old session, if any.
      CloseSession();

      // assert(m_state == Closed);

      // Create the media session.
      THROW_IF_ERR(MFCreateMediaSession(NULL, &m_pSession));

      // Start pulling events from the media session
      THROW_IF_ERR(m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL));
      // m_state = Ready;
    }

    //  Close the media session. 
    void Player_::CloseSession()
    {
      //  The IMFMediaSession::Close method is asynchronous, but the 
      //  Player_::CloseSession method waits on the MESessionClosed event.
      //  
      //  MESessionClosed is guaranteed to be the last event that the 
      //  media session fires.

      m_pVideoDisplay.Reset();

      // First close the media session.
      if (m_pSession)
      {
        DWORD dwWaitResult = 0;

        //m_state = Closing;

        THROW_IF_ERR(m_pSession->Close());
        // Wait for the close operation to complete
        dwWaitResult = WaitForSingleObject(m_hCloseEvent, 5000);
        if (dwWaitResult == WAIT_TIMEOUT)
        {
          assert(FALSE);
        }
        // Now there will be no more events from this session.
      }

      if (m_pSource)
      {
        m_pSource->Shutdown();
      }
      // Shut down the media session. (Synchronous operation, no events.)
      if (m_pSession)
      {
        m_pSession->Shutdown();
      }

      m_pSource.Reset();
      m_pSession.Reset();
      //m_state = Closed;
    }

    //  Start playback from the current position. 
    void Player_::StartPlayback()
    {
      assert(m_pSession != NULL);

      prop_variant varStart;
      //    PROPVARIANT varStart;
      //    PropVariantInit(&varStart);

      THROW_IF_ERR(m_pSession->Start(&GUID_NULL, &varStart));
      // Note: Start is an asynchronous operation. However, we
      // can treat our state as being already started. If Start
      // fails later, we'll get an MESessionStarted event with
      // an error code, and we will update our state then.
      //m_state = Started;
    }

    //  Start playback from paused or stopped.
    void Player_::play( ev::Play const& ev)
    {
 /*     if (m_state != Paused && m_state != Stopped)
      {
        THROW_IF_ERR(MF_E_INVALIDREQUEST);
      }*/
      if (!m_pSession || !m_pSource)
      {
        THROW_IF_ERR(E_UNEXPECTED);
      }
      StartPlayback();
      OnStart()(static_cast<this_type&>(*this));
    }


    //  Create a media source from a URL.
    IMFMediaSourcePtr CreateMediaSource(const std::wstring& sURL)
    {
      MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

      IMFSourceResolverPtr pSourceResolver;
      Microsoft::WRL::ComPtr<IUnknown> pSource;
      IMFMediaSourcePtr pSourceReturn;
      // Create the source resolver.
      THROW_IF_ERR(MFCreateSourceResolver(pSourceResolver.GetAddressOf()));

      // Use the source resolver to create the media source.

      // Note: For simplicity this sample uses the synchronous method to create 
      // the media source. However, creating a media source can take a noticeable
      // amount of time, especially for a network source. For a more responsive 
      // UI, use the asynchronous BeginCreateObjectFromURL method.

      THROW_IF_ERR(pSourceResolver->CreateObjectFromURL(
        sURL.c_str(),                       // URL of the source.
        MF_RESOLUTION_MEDIASOURCE,  // Create a source object.
        NULL,                       // Optional property store.
        &ObjectType,        // Receives the created object type. 
        &pSource            // Receives a pointer to the media source.
        ));

      // Get the IMFMediaSource interface from the media source.
      THROW_IF_ERR(pSource.As<IMFMediaSource>(&pSourceReturn));
      return pSourceReturn;
    }

    //  Create an activation object for a renderer, based on the stream media type.

    IMFActivatePtr CreateMediaSinkActivate(
      IMFStreamDescriptor* pSourceSD,     // Pointer to the stream descriptor.
      HWND hVideoWindow                 // Handle to the video clipping window.
      )
    {
      IMFMediaTypeHandlerPtr pHandler;
      IMFActivatePtr pActivate;

      // Get the media type handler for the stream.
      THROW_IF_ERR(pSourceSD->GetMediaTypeHandler(pHandler.GetAddressOf()));

      // Get the major media type.
      GUID guidMajorType;
      THROW_IF_ERR(pHandler->GetMajorType(&guidMajorType));

      // Create an IMFActivate object for the renderer, based on the media type.
      if (MFMediaType_Audio == guidMajorType)
      {
        // Create the audio renderer.
        THROW_IF_ERR(MFCreateAudioRendererActivate(pActivate.GetAddressOf()));
      }
      else if (MFMediaType_Video == guidMajorType)
      {
        // Create the video renderer.
        THROW_IF_ERR(MFCreateVideoRendererActivate(hVideoWindow, pActivate.GetAddressOf()));
      }
      else
      {
        // Unknown stream type. 
        THROW_IF_ERR(E_FAIL);
        // Optionally, you could deselect this stream instead of failing.
      }

      // Return IMFActivate pointer to caller.
      return pActivate;
    }

    // Add a source node to a topology.
    IMFTopologyNodePtr AddSourceNode(
      IMFTopologyPtr pTopology,           // Topology.
      IMFMediaSourcePtr pSource,          // Media source.
      IMFPresentationDescriptorPtr pPD,   // Presentation descriptor.
      IMFStreamDescriptorPtr pSD)         // Stream descriptor.
    {
      IMFTopologyNodePtr pNode;

      // Create the node.
      THROW_IF_ERR(MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, pNode.GetAddressOf()));

      // Set the attributes.
      THROW_IF_ERR(pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource.Get()));

      THROW_IF_ERR(pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD.Get()));

      THROW_IF_ERR(pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD.Get()));

      // Add the node to the topology.
      THROW_IF_ERR(pTopology->AddNode(pNode.Get()));

      // Return the pointer to the caller.
      return pNode;
    }

    // Add an output node to a topology.
    IMFTopologyNodePtr AddOutputNode(
      IMFTopologyPtr pTopology,     // Topology.
      IMFActivatePtr pActivate,     // Media sink activation object.
      DWORD dwId                 // Identifier of the stream sink.
      ) 
    {
      IMFTopologyNodePtr pNode;

      // Create the node.
      THROW_IF_ERR(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode));

      // Set the object pointer.
      THROW_IF_ERR(pNode->SetObject(pActivate.Get()));

      // Set the stream sink ID attribute.
      THROW_IF_ERR(pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId));

      THROW_IF_ERR(pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE));

      // Add the node to the topology.
      THROW_IF_ERR(pTopology->AddNode(pNode.Get()));

      return pNode;

    }
    //</SnippetPlayer.cpp>

    //  Add a topology branch for one stream.
    //
    //  For each stream, this function does the following:
    //
    //    1. Creates a source node associated with the stream. 
    //    2. Creates an output node for the renderer. 
    //    3. Connects the two nodes.
    //
    //  The media session will add any decoders that are needed.

    void AddBranchToPartialTopology(
      IMFTopologyPtr pTopology,         // Topology.
      IMFMediaSourcePtr pSource,        // Media source.
      IMFPresentationDescriptorPtr pPD, // Presentation descriptor.
      DWORD iStream,                  // Stream index.
      HWND hVideoWnd)                 // Window for video playback.
    {
      IMFStreamDescriptorPtr pSD;
      IMFActivatePtr         pSinkActivate;
      IMFTopologyNodePtr     pSourceNode;
      IMFTopologyNodePtr     pOutputNode;

      BOOL fSelected = FALSE;

      THROW_IF_ERR(pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD));

      if (fSelected)
      {
        // Create the media sink activation object.
        pSinkActivate = CreateMediaSinkActivate(pSD.Get(), hVideoWnd);

        // Add a source node for this stream.
        pSourceNode = AddSourceNode(pTopology, pSource, pPD, pSD);

        // Create the output node for the renderer.
        pOutputNode = AddOutputNode(pTopology, pSinkActivate, 0);

        // Connect the source node to the output node.
        THROW_IF_ERR(pSourceNode->ConnectOutput(0, pOutputNode.Get(), 0));
      }
      // else: If not selected, don't add the branch. 
    }

    //  Create a playback topology from a media source.
    // Receives a pointer to the topology.
    IMFTopologyPtr CreatePlaybackTopology(
      IMFMediaSourcePtr pSource,          // Media source.
      IMFPresentationDescriptorPtr pPD,   // Presentation descriptor.
      HWND hVideoWnd                   // Video window.
      )        
    {
      IMFTopologyPtr pTopology;
      DWORD cSourceStreams = 0;

      // Create a new topology.
      THROW_IF_ERR(MFCreateTopology(&pTopology));

      // Get the number of streams in the media source.
      THROW_IF_ERR(pPD->GetStreamDescriptorCount(&cSourceStreams));

      // For each stream, create the topology nodes and add them to the topology.
      for (DWORD i = 0; i < cSourceStreams; i++)
      {
        AddBranchToPartialTopology(pTopology, pSource, pPD, i, hVideoWnd);
      }

      // Return the IMFTopology pointer to the caller.
      return pTopology;
    }
  }
}

プロジェクト全体のソースコード