I took Media Session example from here http://msdn.microsoft.com/en-us/library/windows/desktop/ff728866(v=vs.85).aspx. For editing each frame i am using MFT. But if i just create MFT, not linking it with Media Session, program will begin playback and end it at this moment. Look at the AddBranchToPartialTopology and CreateMFTransform.
#include "Player.h" #include "MediaType.h" #include <assert.h> void ErrorMessage(HRESULT hr) { LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, hr, 0, // Default language (LPTSTR) &lpMsgBuf, 0, NULL ); // Process any inserts in lpMsgBuf. // ... // Display the string. MessageBox( NULL, (LPCTSTR)lpMsgBuf, L"Error", MB_OK | MB_ICONINFORMATION ); // Free the buffer. LocalFree( lpMsgBuf ); } #pragma comment(lib, "shlwapi") 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; } HRESULT CreateMediaSource(PCWSTR pszURL, IMFMediaSource **ppSource); HRESULT CreatePlaybackTopology(IMFMediaSource *pSource, IMFPresentationDescriptor *pPD, HWND hVideoWnd,IMFTopology **ppTopology); // Static class method to create the CPlayer object. HRESULT CPlayer::CreateInstance( HWND hVideo, // Video window. HWND hEvent, // Window to receive notifications. CPlayer **ppPlayer) // Receives a pointer to the CPlayer object. { if (ppPlayer == NULL) { return E_POINTER; } CPlayer *pPlayer = new (std::nothrow) CPlayer(hVideo, hEvent); if (pPlayer == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pPlayer->Initialize(); if (SUCCEEDED(hr)) { *ppPlayer = pPlayer; } else { pPlayer->Release(); } return hr; } HRESULT CPlayer::Initialize() { // Start up Media Foundation platform. HRESULT hr = MFStartup(MF_VERSION); if (SUCCEEDED(hr)) { m_hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hCloseEvent == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); } } return hr; } CPlayer::CPlayer(HWND hVideo, HWND hEvent) : m_pSession(NULL), m_pSource(NULL), m_pVideoDisplay(NULL), m_hwndVideo(hVideo), m_hwndEvent(hEvent), m_state(Closed), m_hCloseEvent(NULL), m_nRefCount(1) { } CPlayer::~CPlayer() { assert(m_pSession == NULL); // If FALSE, the app did not call Shutdown(). // When CPlayer calls IMediaEventGenerator::BeginGetEvent on the // media session, it causes the media session to hold a reference // count on the CPlayer. // This creates a circular reference count between CPlayer 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 CPlayer::QueryInterface(REFIID riid, void** ppv) { static const QITAB qit[] = { QITABENT(CPlayer, IMFAsyncCallback), { 0 } }; return QISearch(this, qit, riid, ppv); } ULONG CPlayer::AddRef() { return InterlockedIncrement(&m_nRefCount); } ULONG CPlayer::Release() { ULONG uCount = InterlockedDecrement(&m_nRefCount); if (uCount == 0) { delete this; } return uCount; } // Open a URL for playback. HRESULT CPlayer::OpenURL(const WCHAR *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.] IMFTopology *pTopology = NULL; IMFPresentationDescriptor* pSourcePD = NULL; // Create the media session. HRESULT hr = CreateSession(); if (FAILED(hr)) { goto done; } // Create the media source. hr = CreateMediaSource(sURL, &m_pSource); if (FAILED(hr)) { goto done; } // Create the presentation descriptor for the media source. hr = m_pSource->CreatePresentationDescriptor(&pSourcePD); if (FAILED(hr)) { goto done; } // Create a partial topology. hr = CreatePlaybackTopology(m_pSource, pSourcePD, m_hwndVideo, &pTopology); if (FAILED(hr)) { goto done; } // Set the topology on the media session. hr = m_pSession->SetTopology(0, pTopology); if (FAILED(hr)) { goto done; } m_state = OpenPending; // If SetTopology succeeds, the media session will queue an // MESessionTopologySet event. done: if (FAILED(hr)) { m_state = Closed; } SafeRelease(&pSourcePD); SafeRelease(&pTopology); return hr; } // Pause playback. HRESULT CPlayer::Pause() { if (m_state != Started) { return MF_E_INVALIDREQUEST; } if (m_pSession == NULL || m_pSource == NULL) { return E_UNEXPECTED; } HRESULT hr = m_pSession->Pause(); if (SUCCEEDED(hr)) { m_state = Paused; } return hr; } // Stop playback. HRESULT CPlayer::Stop() { if (m_state != Started && m_state != Paused) { return MF_E_INVALIDREQUEST; } if (m_pSession == NULL) { return E_UNEXPECTED; } HRESULT hr = m_pSession->Stop(); if (SUCCEEDED(hr)) { m_state = Stopped; } return hr; } // Repaint the video window. Call this method on WM_PAINT. HRESULT CPlayer::Repaint() { if (m_pVideoDisplay) { return m_pVideoDisplay->RepaintVideo(); } else { return S_OK; } } // Resize the video rectangle. // // Call this method if the size of the video window changes. HRESULT CPlayer::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 }; return m_pVideoDisplay->SetVideoPosition(NULL, &rcDest); } else { return S_OK; } } // Callback for the asynchronous BeginGetEvent method. HRESULT CPlayer::Invoke(IMFAsyncResult *pResult) { MediaEventType meType = MEUnknown; // Event type IMFMediaEvent *pEvent = NULL; // Get the event from the event queue. HRESULT hr = m_pSession->EndGetEvent(pResult, &pEvent); if (FAILED(hr)) { goto done; } // Get the event type. hr = pEvent->GetType(&meType); if (FAILED(hr)) { goto done; } // Get the event status. If the operation that triggered the event // did not succeed, the status is a failure code. HRESULT hrStatus = S_OK; hr = pEvent->GetStatus(&hrStatus); // Check if the async operation succeeded. if (SUCCEEDED(hr) && FAILED(hrStatus)) { hr = hrStatus; } if (FAILED(hr)) { goto done; } switch(meType) { case MESessionTopologyStatus: hr = OnTopologyStatus(pEvent); break; case MEEndOfPresentation: hr = OnPresentationEnded(pEvent); break; case MENewPresentation: hr = OnNewPresentation(pEvent); break; default: hr = OnSessionEvent(pEvent, meType); break; } 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. hr = m_pSession->BeginGetEvent(this, NULL); if (FAILED(hr)) { goto done; } } // 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. pEvent->AddRef(); PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT, (WPARAM)pEvent, (LPARAM)meType); } done: SafeRelease(&pEvent); return S_OK; } HRESULT CPlayer::HandleEvent(UINT_PTR pEventPtr) { HRESULT hrStatus = S_OK; MediaEventType meType = MEUnknown; IMFMediaEvent *pEvent = (IMFMediaEvent*)pEventPtr; if (pEvent == NULL) { return E_POINTER; } // Get the event type. HRESULT hr = pEvent->GetType(&meType); if (FAILED(hr)) { goto done; } // Get the event status. If the operation that triggered the event // did not succeed, the status is a failure code. hr = pEvent->GetStatus(&hrStatus); // Check if the async operation succeeded. if (SUCCEEDED(hr) && FAILED(hrStatus)) { hr = hrStatus; } if (FAILED(hr)) { goto done; } switch(meType) { case MESessionTopologyStatus: hr = OnTopologyStatus(pEvent); break; case MEEndOfPresentation: hr = OnPresentationEnded(pEvent); break; case MENewPresentation: hr = OnNewPresentation(pEvent); break; default: hr = OnSessionEvent(pEvent, meType); break; } done: SafeRelease(&pEvent); return hr; } // Release all resources held by this object. HRESULT CPlayer::Shutdown() { // Close the session HRESULT hr = CloseSession(); // Shutdown the Media Foundation platform MFShutdown(); if (m_hCloseEvent) { CloseHandle(m_hCloseEvent); m_hCloseEvent = NULL; } return hr; } /// Protected methods HRESULT CPlayer::OnTopologyStatus(IMFMediaEvent *pEvent) { UINT32 status; HRESULT hr = pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status); if (SUCCEEDED(hr) && (status == MF_TOPOSTATUS_READY)) { SafeRelease(&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, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_pVideoDisplay)); hr = StartPlayback(); } return hr; } // Handler for MEEndOfPresentation event. HRESULT CPlayer::OnPresentationEnded(IMFMediaEvent *pEvent) { // The session puts itself into the stopped state automatically. m_state = Stopped; return S_OK; } // Handler for MENewPresentation event. // // This event is sent if the media source has a new presentation, which // requires a new topology. HRESULT CPlayer::OnNewPresentation(IMFMediaEvent *pEvent) { IMFPresentationDescriptor *pPD = NULL; IMFTopology *pTopology = NULL; // Get the presentation descriptor from the event. HRESULT hr = GetEventObject(pEvent, &pPD); if (FAILED(hr)) { goto done; } // Create a partial topology. hr = CreatePlaybackTopology(m_pSource, pPD, m_hwndVideo,&pTopology); if (FAILED(hr)) { goto done; } // Set the topology on the media session. hr = m_pSession->SetTopology(0, pTopology); if (FAILED(hr)) { goto done; } m_state = OpenPending; done: SafeRelease(&pTopology); SafeRelease(&pPD); return S_OK; } // Create a new instance of the media session. HRESULT CPlayer::CreateSession() { // Close the old session, if any. HRESULT hr = CloseSession(); if (FAILED(hr)) { goto done; } assert(m_state == Closed); // Create the media session. hr = MFCreateMediaSession(NULL, &m_pSession); if (FAILED(hr)) { goto done; } // Start pulling events from the media session hr = m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL); if (FAILED(hr)) { goto done; } m_state = Ready; done: return hr; } // Close the media session. HRESULT CPlayer::CloseSession() { // The IMFMediaSession::Close method is asynchronous, but the // CPlayer::CloseSession method waits on the MESessionClosed event. // // MESessionClosed is guaranteed to be the last event that the // media session fires. HRESULT hr = S_OK; SafeRelease(&m_pVideoDisplay); // First close the media session. if (m_pSession) { DWORD dwWaitResult = 0; m_state = Closing; hr = m_pSession->Close(); // Wait for the close operation to complete if (SUCCEEDED(hr)) { dwWaitResult = WaitForSingleObject(m_hCloseEvent, 5000); if (dwWaitResult == WAIT_TIMEOUT) { assert(FALSE); } // Now there will be no more events from this session. } } // Complete shutdown operations. if (SUCCEEDED(hr)) { // Shut down the media source. (Synchronous operation, no events.) if (m_pSource) { (void)m_pSource->Shutdown(); } // Shut down the media session. (Synchronous operation, no events.) if (m_pSession) { (void)m_pSession->Shutdown(); } } SafeRelease(&m_pSource); SafeRelease(&m_pSession); m_state = Closed; return hr; } // Start playback from the current position. HRESULT CPlayer::StartPlayback() { assert(m_pSession != NULL); PROPVARIANT varStart; PropVariantInit(&varStart); HRESULT hr = m_pSession->Start(&GUID_NULL, &varStart); if (SUCCEEDED(hr)) { // 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; } PropVariantClear(&varStart); return hr; } // Start playback from paused or stopped. HRESULT CPlayer::Play() { if (m_state != Paused && m_state != Stopped) { return MF_E_INVALIDREQUEST; } if (m_pSession == NULL || m_pSource == NULL) { return E_UNEXPECTED; } return StartPlayback(); } // Create a media source from a URL. HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource) { MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID; IMFSourceResolver* pSourceResolver = NULL; IUnknown* pSource = NULL; // Create the source resolver. HRESULT hr = MFCreateSourceResolver(&pSourceResolver); if (FAILED(hr)) { goto done; } // 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. hr = pSourceResolver->CreateObjectFromURL( sURL, // 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. ); if (FAILED(hr)) { goto done; } // Get the IMFMediaSource interface from the media source. hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource)); done: SafeRelease(&pSourceResolver); SafeRelease(&pSource); return hr; } // Create an activation object for a renderer, based on the stream media type. HRESULT CreateMediaSinkActivate( IMFStreamDescriptor *pSourceSD, // Pointer to the stream descriptor. HWND hVideoWindow, // Handle to the video clipping window. IMFActivate **ppActivate ) { IMFMediaTypeHandler *pHandler = NULL; IMFActivate *pActivate = NULL; // Get the media type handler for the stream. HRESULT hr = pSourceSD->GetMediaTypeHandler(&pHandler); if (FAILED(hr)) { goto done; } // Get the major media type. GUID guidMajorType; hr = pHandler->GetMajorType(&guidMajorType); if (FAILED(hr)) { goto done; } // Create an IMFActivate object for the renderer, based on the media type. if (MFMediaType_Audio == guidMajorType) { // Create the audio renderer. hr = MFCreateAudioRendererActivate(&pActivate); } else if (MFMediaType_Video == guidMajorType) { // Create the video renderer. hr = MFCreateVideoRendererActivate(hVideoWindow, &pActivate); } else { // Unknown stream type. hr = E_FAIL; // Optionally, you could deselect this stream instead of failing. } if (FAILED(hr)) { goto done; } // Return IMFActivate pointer to caller. *ppActivate = pActivate; (*ppActivate)->AddRef(); done: SafeRelease(&pHandler); SafeRelease(&pActivate); return hr; } HRESULT CreateMFTransform( IMFStreamDescriptor *pSD, IMFTransform **ppDecoder ) { HRESULT hr = S_OK; UINT32 count = 0; IMFActivate **ppActivate = NULL; MFT_REGISTER_TYPE_INFO inInfo = { 0 }; IMFMediaTypeHandler *pTH = NULL; IMFMediaType *pMT = NULL; GUID subtype; pSD->GetMediaTypeHandler(&pTH); pTH->GetCurrentMediaType(&pMT); pMT->GetGUID(MF_MT_SUBTYPE, &subtype); LogMediaType(pMT); SafeRelease(&pTH); inInfo.guidMajorType = MFMediaType_Video; inInfo.guidSubtype = subtype; //UINT32 unFlags = MFT_ENUM_FLAG_SYNCMFT | // MFT_ENUM_FLAG_LOCALMFT | // MFT_ENUM_FLAG_SORTANDFILTER; UINT32 unFlags = MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_HARDWARE; hr = MFTEnumEx( MFT_CATEGORY_VIDEO_EFFECT, //MFT_CATEGORY_VIDEO_DECODER, unFlags, &inInfo, NULL,&ppActivate,&count ); if (SUCCEEDED(hr) && count == 0) { hr = MF_E_TOPO_CODEC_NOT_FOUND; } IMFTransform *pMFTransform = NULL; if (SUCCEEDED(hr)) { hr = ppActivate[0]->ActivateObject(IID_PPV_ARGS(&pMFTransform)); } hr = pMFTransform->SetInputType(0, pMT, NULL); hr = pMT->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); hr = pMT->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2); hr = pMFTransform->SetOutputType(0, pMT, NULL); //DWORD inMin, inMax, outMin, outMax, // inCount, outCount; // //hr = pMFTransform->GetStreamLimits(&inMin, &inMax, &outMin, &outMax); //hr = pMFTransform->GetStreamCount(&inCount, &outCount); //DWORD *inIDs = new DWORD[inCount], *outIDs = new DWORD[outCount]; //hr = pMFTransform->GetStreamIDs(inCount, inIDs, outCount, outIDs); // //IMFMediaType *MFMT = NULL; //int k = 0; //hr = S_OK; //while (SUCCEEDED(hr)) //{ // hr = pMFTransform->GetOutputAvailableType(0, k, &MFMT); // if(SUCCEEDED(hr)) // LogMediaType(MFMT); // SafeRelease(&MFMT); // k++; //} //k = 0; //hr = S_OK; //while (SUCCEEDED(hr)) //{ // hr = pMFTransform->GetInputAvailableType(0, k, &MFMT); // if(SUCCEEDED(hr)) // LogMediaType(MFMT); // SafeRelease(&MFMT); // k++; //} // //_MFT_INPUT_STREAM_INFO inMFInfo, outMFInfo; //hr = pMFTransform->GetInputStreamInfo(0, &inMFInfo); //hr = pMFTransform->GetInputStreamInfo(0, &outMFInfo); // //hr = pMFTransform->GetInputCurrentType(0, &pMT); //if(SUCCEEDED(hr)) // LogMediaType(pMT); //SafeRelease(&pMT); //hr = pMFTransform->GetOutputCurrentType(0, &pMT); //if(SUCCEEDED(hr)) // LogMediaType(pMT); //SafeRelease(&pMT); for (UINT32 i = 0; i < count; i++) { ppActivate[i]->Release(); } CoTaskMemFree(ppActivate); if(SUCCEEDED(hr)) *ppDecoder = pMFTransform; return hr; } // Add a source node to a topology. HRESULT AddSourceNode( IMFTopology *pTopology, // Topology. IMFMediaSource *pSource, // Media source. IMFPresentationDescriptor *pPD, // Presentation descriptor. IMFStreamDescriptor *pSD, // Stream descriptor. IMFTopologyNode **ppNode) // Receives the node pointer. { IMFTopologyNode *pNode = NULL; // Create the node. HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode); if (FAILED(hr)) { goto done; } // Set the attributes. hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource); if (FAILED(hr)) { goto done; } hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD); if (FAILED(hr)) { goto done; } hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD); if (FAILED(hr)) { goto done; } // Add the node to the topology. hr = pTopology->AddNode(pNode); if (FAILED(hr)) { goto done; } // Return the pointer to the caller. *ppNode = pNode; (*ppNode)->AddRef(); done: SafeRelease(&pNode); return hr; } // Add an output node to a topology. HRESULT AddOutputNode( IMFTopology *pTopology, // Topology. IMFActivate *pActivate, // Media sink activation object. DWORD dwId, // Identifier of the stream sink. IMFTopologyNode **ppNode) // Receives the node pointer. { IMFTopologyNode *pNode = NULL; // Create the node. HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode); if (FAILED(hr)) { goto done; } // Set the object pointer. hr = pNode->SetObject(pActivate); if (FAILED(hr)) { goto done; } // Set the stream sink ID attribute. hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId); if (FAILED(hr)) { goto done; } hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); if (FAILED(hr)) { goto done; } // Add the node to the topology. hr = pTopology->AddNode(pNode); if (FAILED(hr)) { goto done; } // Return the pointer to the caller. *ppNode = pNode; (*ppNode)->AddRef(); done: SafeRelease(&pNode); return hr; } HRESULT AddMFTNode( IMFTopology *pTopology, IMFTransform *pTransform ) { HRESULT hr = S_OK; IMFTopologyNode *pNode = NULL; // Create the node. hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &pNode); // Set the object pointer. if (SUCCEEDED(hr)) { hr = pNode->SetObject(pTransform); } // Add the node to the topology. if (SUCCEEDED(hr)) { hr = pTopology->AddNode(pNode); } return hr; } // 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. HRESULT AddBranchToPartialTopology( IMFTopology *pTopology, // Topology. IMFMediaSource *pSource, // Media source. IMFPresentationDescriptor *pPD, // Presentation descriptor. DWORD iStream, // Stream index. HWND hVideoWnd) // Window for video playback. { IMFStreamDescriptor *pSD = NULL; IMFActivate *pSinkActivate = NULL; IMFTransform *pDecoder = NULL; IMFTopologyNode *pSourceNode = NULL; IMFTopologyNode *pOutputNode = NULL; BOOL fSelected = FALSE; HRESULT hr = pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD); if (FAILED(hr)) { goto done; } if (fSelected) { // Create the media sink activation object. hr = CreateMediaSinkActivate(pSD, hVideoWnd, &pSinkActivate); if (FAILED(hr)) { goto done; } // Add a source node for this stream. hr = AddSourceNode(pTopology, pSource, pPD, pSD, &pSourceNode); if (FAILED(hr)) { goto done; } hr = CreateMFTransform(pSD, &pDecoder); if (FAILED(hr)) { goto done; } //hr = AddMFTNode(pTopology, pDecoder); if (FAILED(hr)) { goto done; } // Create the output node for the renderer. hr = AddOutputNode(pTopology, pSinkActivate, 0, &pOutputNode); if (FAILED(hr)) { goto done; } // Connect the source node to the output node. hr = pSourceNode->ConnectOutput(0, pOutputNode, 0); } // else: If not selected, don't add the branch. done: SafeRelease(&pSD); SafeRelease(&pSinkActivate); SafeRelease(&pSourceNode); SafeRelease(&pOutputNode); return hr; } // Create a playback topology from a media source. HRESULT CreatePlaybackTopology( IMFMediaSource *pSource, // Media source. IMFPresentationDescriptor *pPD, // Presentation descriptor. HWND hVideoWnd, // Video window. IMFTopology **ppTopology) // Receives a pointer to the topology. { IMFTopology *pTopology = NULL; DWORD cSourceStreams = 0; // Create a new topology. HRESULT hr = MFCreateTopology(&pTopology); if (FAILED(hr)) { goto done; } // Get the number of streams in the media source. hr = pPD->GetStreamDescriptorCount(&cSourceStreams); if (FAILED(hr)) { goto done; } // For each stream, create the topology nodes and add them to the topology. for (DWORD i = 0; i < cSourceStreams; i++) { hr = AddBranchToPartialTopology(pTopology, pSource, pPD, i, hVideoWnd); if (FAILED(hr)) { goto done; } } // Return the IMFTopology pointer to the caller. *ppTopology = pTopology; (*ppTopology)->AddRef(); done: SafeRelease(&pTopology); return hr; }