CPlayerクラスを改造する(3) - やっぱりBugあり

公開:2012-12-29 21:56
更新:2020-02-15 04:37
カテゴリ:media foundation,audio,c++,windows,windows api

昨日作ったコードはやっぱりBugっていた。修正したコードは以下。まだBugは潜んでいそうだなー。

player.h


// 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.
//
// Copyright (c) Microsoft Corporation. All rights reserved.

#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"

_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 {

template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

const UINT WM_APP_PLAYER_EVENT = WM_APP + 1;   

    // WPARAM = IMFMediaEvent*, WPARAM = MediaEventType

enum 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 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_;
};

class Player;

typedef Microsoft::WRL::ComPtr<Player> PlayerPtr;

PlayerPtr CreatePlayer(HWND hVideo, HWND hEvent);

class Player : public IMFAsyncCallback
{
  friend PlayerPtr CreatePlayer(HWND hVideo, HWND hEvent);
public:

    // 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);

    // Playback
    void       OpenURL(const std::wstring& sURL);
    void       Play();
    void       Pause();
    void       Stop();
    void       Shutdown();
    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);  }

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 Initialize();
    void CreateSession();
    void CloseSession();
    void StartPlayback();

    // 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; 
    }

protected:
    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.
};

inline void intrusive_ptr_add_ref(Player *p)
{
  p->AddRef();
};

inline void intrusive_ptr_release(Player *p)
{
  p->Release();
};

}
#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.
//
// Copyright (c) Microsoft Corporation. All rights reserved.

#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 {
  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);

  //  Static class method to create the Player object.

  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->Initialize();
    return p;
  }

  void Player::Initialize()
  {
    // 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();
  }

  // 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::OpenURL(const std::wstring& sURL)
  {
    // 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(sURL);

    // 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()));

    m_state = OpenPending;

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

  //  Pause playback.
  void Player::Pause()    
  {
    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());
    m_state = Paused;

  }

  // Stop playback.
  void Player::Stop()
  {
    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());
  }

  //  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.Get(), (LPARAM)meType);
      }
      return S_OK;
    } catch (win32_error_exception& e) 
    {
      return e.hresult();
    }
  }

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

    IMFMediaEventPtr pEvent = (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()
  {
    // 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()));

      StartPlayback();
    }
  }


  //  Handler for MEEndOfPresentation event.
  void Player::OnPresentationEnded(IMFMediaEventPtr pEvent)
  {
    // The session puts itself into the stopped state automatically.
    m_state = Stopped;
  }

  //  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()
  {
    if (m_state != Paused && m_state != Stopped)
    {
      THROW_IF_ERR(MF_E_INVALIDREQUEST);
    }
    if (m_pSession == NULL || m_pSource == NULL)
    {
      THROW_IF_ERR(E_UNEXPECTED);
    }
    StartPlayback();
  }


  //  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;
  }
}