2 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
3 // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
4 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
7 // Copyright (c) Microsoft Corporation. All rights reserved.
15 #define _CRTDBG_MAP_ALLOC
17 #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
20 #include "sf_memory.h"
28 IMFMediaSourcePtr CreateMediaSource(const std::wstring& pszURL);
30 IMFTopologyPtr CreatePlaybackTopology(IMFMediaSourcePtr pSource,
31 IMFPresentationDescriptorPtr pPD, HWND hVideoWnd);
33 // Player_
\83I
\83u
\83W
\83F
\83N
\83g
\82ð
\90¶
\90¬
\82·
\82é
\83t
\83\8a\81[
\8aÖ
\90\94
34 PlayerPtr CreatePlayer(
35 HWND hVideo, // Video window.
36 HWND hEvent // Window to receive notifications.
40 PlayerPtr p(new Player(hVideo, hEvent));
44 throw win32_error_exception(E_OUTOFMEMORY);
46 p->process_event(ev::Init());
48 // p->processInitialize();
52 void Player_::initialize(const ev::Init& ev)
54 // Start up Media Foundation platform.
55 THROW_IF_ERR(MFStartup(MF_VERSION));
56 m_hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
57 THROW_IF_ERR(HRESULT_FROM_WIN32(GetLastError()));
60 Player_::Player_(HWND hVideo, HWND hEvent) :
71 assert(m_pSession.Get() == NULL);
72 // If FALSE, the app did not call Shutdown().
74 // When Player_ calls IMediaEventGenerator::BeginGetEvent on the
75 // media session, it causes the media session to hold a reference
76 // count on the Player_.
78 // This creates a circular reference count between Player_ and the
79 // media session. Calling Shutdown breaks the circular reference
82 // If CreateInstance fails, the application will not call
83 // Shutdown. To handle that case, call Shutdown in the destructor.
85 shutdown(ev::Close());
90 HRESULT Player_::QueryInterface(REFIID riid, void** ppv)
92 static const QITAB qit[] =
94 QITABENT(Player_, IMFAsyncCallback),
97 return QISearch(this, qit, riid, ppv);
100 ULONG Player_::AddRef()
102 return InterlockedIncrement(&m_nRefCount);
105 ULONG Player_::Release()
107 ULONG uCount = InterlockedDecrement(&m_nRefCount);
115 // Open a URL for playback.
116 void Player_::open_url( ev::OpenURL const& openurl)
118 // 1. Create a new media session.
119 // 2. Create the media source.
120 // 3. Create the topology.
121 // 4. Queue the topology [asynchronous]
122 // 5. Start playback [asynchronous - does not happen in this method.]
124 IMFTopologyPtr pTopology;
125 IMFPresentationDescriptorPtr pSourcePD;
127 // Create the media session.
130 // Create the media source.
131 m_pSource = CreateMediaSource(openurl.url());
133 // Create the presentation descriptor for the media source.
134 THROW_IF_ERR(m_pSource->CreatePresentationDescriptor(&pSourcePD));
136 // Create a partial topology.
137 pTopology = CreatePlaybackTopology(m_pSource, pSourcePD, m_hwndVideo);
139 // Set the topology on the media session.
140 THROW_IF_ERR(m_pSession->SetTopology(0, pTopology.Get()));
144 // m_state = OpenPending;
146 // If SetTopology succeeds, the media session will queue an
147 // MESessionTopologySet event.
151 void Player_::pause( ev::Pause const& ev)
153 // if (m_state != Started)
155 // throw win32_error_exception(MF_E_INVALIDREQUEST);
158 // if (!m_pSession || !m_pSource)
160 // throw win32_error_exception(E_UNEXPECTED);
162 THROW_IF_ERR(m_pSession->Pause());
163 OnPause()(static_cast<this_type&>(*this));
169 void Player_::stop( ev::Stop const& ev)
171 //if (m_state != Started && m_state != Paused)
173 // throw win32_error_exception( MF_E_INVALIDREQUEST );
177 // throw win32_error_exception( E_UNEXPECTED);
180 THROW_IF_ERR(m_pSession->Stop());
181 OnStop()(static_cast<this_type&>(*this));
184 // Repaint the video window. Call this method on WM_PAINT.
185 void Player_::Repaint()
189 m_pVideoDisplay->RepaintVideo();
193 // Resize the video rectangle.
195 // Call this method if the size of the video window changes.
197 void Player_::ResizeVideo(WORD width, WORD height)
201 // Set the destination rectangle.
202 // Leave the default source rectangle (0,0,1,1).
204 RECT rcDest = { 0, 0, width, height };
206 THROW_IF_ERR(m_pVideoDisplay->SetVideoPosition(NULL, &rcDest));
210 // Callback for the asynchronous BeginGetEvent method.
212 HRESULT Player_::Invoke(IMFAsyncResult *pResult)
214 MediaEventType meType = MEUnknown; // Event type
216 IMFMediaEventPtr pEvent;
219 // Get the event from the event queue.
220 THROW_IF_ERR(m_pSession->EndGetEvent(pResult, pEvent.GetAddressOf()));
222 // Get the event type.
223 THROW_IF_ERR(pEvent->GetType(&meType));
225 if (meType == MESessionClosed)
227 // The session was closed.
228 // The application is waiting on the m_hCloseEvent event handle.
229 SetEvent(m_hCloseEvent);
233 // For all other events, get the next event in the queue.
234 THROW_IF_ERR(m_pSession->BeginGetEvent(this, NULL));
237 // Check the application state.
239 // If a call to IMFMediaSession::Close is pending, it means the
240 // application is waiting on the m_hCloseEvent event and
241 // the application's message loop is blocked.
243 // Otherwise, post a private window message to the application.
245 //if (m_state != Closing)
247 // // Leave a reference count on the event.
249 // //PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT,
250 // // (WPARAM) pEvent.Detach(), (LPARAM)meType);
253 HandleEvent((UINT_PTR)pEvent.Detach());
256 } catch (win32_error_exception& e)
262 void Player_::HandleEvent(UINT_PTR pEventPtr)
264 MediaEventType meType = MEUnknown;
266 IMFMediaEventPtr pEvent;
267 // pEvent.Attach((IMFMediaEvent*)pEventPtr);
268 pEvent.Attach(reinterpret_cast<IMFMediaEvent*>(pEventPtr));
272 throw win32_error_exception(E_POINTER);
275 // Get the event type.
276 THROW_IF_ERR(pEvent->GetType(&meType));
278 // Get the event status. If the operation that triggered the event
279 // did not succeed, the status is a failure code.
280 HRESULT hrStatus = S_OK;
281 THROW_IF_ERR(pEvent->GetStatus(&hrStatus));
282 // Check if the async operation succeeded.
283 THROW_IF_ERR(hrStatus);
287 case MESessionTopologyStatus:
288 OnTopologyStatus(pEvent);
291 case MEEndOfPresentation:
292 OnPresentationEnded(pEvent);
295 case MENewPresentation:
296 OnNewPresentation(pEvent);
300 OnSessionEvent(pEvent, meType);
305 // Release all resources held by this object.
306 void Player_::shutdown( ev::Close const& ev)
311 // Shutdown the Media Foundation platform
316 CloseHandle(m_hCloseEvent);
317 m_hCloseEvent = NULL;
321 /// Protected methods
323 void Player_::OnTopologyStatus(IMFMediaEventPtr pEvent)
327 THROW_IF_ERR(pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status));
328 if (status == MF_TOPOSTATUS_READY)
330 safe_release(m_pVideoDisplay);
332 // Get the IMFVideoDisplayControl interface from EVR. This call is
333 // expected to fail if the media file does not have a video stream.
335 (void)MFGetService(m_pSession.Get(), MR_VIDEO_RENDER_SERVICE,
336 IID_PPV_ARGS(m_pVideoDisplay.GetAddressOf()));
337 static_cast<this_type&>(*this).process_event(ev::OpenComplete());
343 // Handler for MEEndOfPresentation event.
344 void Player_::OnPresentationEnded(IMFMediaEventPtr pEvent)
346 // The session puts itself into the stopped state automatically.
347 // m_state = Stopped;
348 static_cast<this_type&>(*this).process_event(ev::Stop());
351 // Handler for MENewPresentation event.
353 // This event is sent if the media source has a new presentation, which
354 // requires a new topology.
356 void Player_::OnNewPresentation(IMFMediaEventPtr pEvent)
358 IMFPresentationDescriptorPtr pPD;
359 IMFTopologyPtr pTopology;
361 // Get the presentation descriptor from the event.
362 THROW_IF_ERR(GetEventObject(pEvent.Get(), pPD.GetAddressOf()));
364 // Create a partial topology.
365 pTopology = CreatePlaybackTopology(m_pSource, pPD, m_hwndVideo);
367 // Set the topology on the media session.
368 THROW_IF_ERR(m_pSession->SetTopology(0, pTopology.Get()));
370 // m_state = OpenPending;
374 // Create a new instance of the media session.
375 void Player_::CreateSession()
377 // Close the old session, if any.
380 // assert(m_state == Closed);
382 // Create the media session.
383 THROW_IF_ERR(MFCreateMediaSession(NULL, &m_pSession));
385 // Start pulling events from the media session
386 THROW_IF_ERR(m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL));
390 // Close the media session.
391 void Player_::CloseSession()
393 // The IMFMediaSession::Close method is asynchronous, but the
394 // Player_::CloseSession method waits on the MESessionClosed event.
396 // MESessionClosed is guaranteed to be the last event that the
397 // media session fires.
399 m_pVideoDisplay.Reset();
401 // First close the media session.
404 DWORD dwWaitResult = 0;
408 THROW_IF_ERR(m_pSession->Close());
409 // Wait for the close operation to complete
410 dwWaitResult = WaitForSingleObject(m_hCloseEvent, 5000);
411 if (dwWaitResult == WAIT_TIMEOUT)
415 // Now there will be no more events from this session.
420 m_pSource->Shutdown();
422 // Shut down the media session. (Synchronous operation, no events.)
425 m_pSession->Shutdown();
433 // Start playback from the current position.
434 void Player_::StartPlayback()
436 assert(m_pSession != NULL);
438 prop_variant varStart;
439 // PROPVARIANT varStart;
440 // PropVariantInit(&varStart);
442 THROW_IF_ERR(m_pSession->Start(&GUID_NULL, &varStart));
443 // Note: Start is an asynchronous operation. However, we
444 // can treat our state as being already started. If Start
445 // fails later, we'll get an MESessionStarted event with
446 // an error code, and we will update our state then.
450 // Start playback from paused or stopped.
451 void Player_::play( ev::Play const& ev)
453 /* if (m_state != Paused && m_state != Stopped)
455 THROW_IF_ERR(MF_E_INVALIDREQUEST);
457 if (!m_pSession || !m_pSource)
459 THROW_IF_ERR(E_UNEXPECTED);
462 OnStart()(static_cast<this_type&>(*this));
466 // Create a media source from a URL.
467 IMFMediaSourcePtr CreateMediaSource(const std::wstring& sURL)
469 MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
471 IMFSourceResolverPtr pSourceResolver;
472 Microsoft::WRL::ComPtr<IUnknown> pSource;
473 IMFMediaSourcePtr pSourceReturn;
474 // Create the source resolver.
475 THROW_IF_ERR(MFCreateSourceResolver(pSourceResolver.GetAddressOf()));
477 // Use the source resolver to create the media source.
479 // Note: For simplicity this sample uses the synchronous method to create
480 // the media source. However, creating a media source can take a noticeable
481 // amount of time, especially for a network source. For a more responsive
482 // UI, use the asynchronous BeginCreateObjectFromURL method.
484 THROW_IF_ERR(pSourceResolver->CreateObjectFromURL(
485 sURL.c_str(), // URL of the source.
486 MF_RESOLUTION_MEDIASOURCE, // Create a source object.
487 NULL, // Optional property store.
488 &ObjectType, // Receives the created object type.
489 &pSource // Receives a pointer to the media source.
492 // Get the IMFMediaSource interface from the media source.
493 THROW_IF_ERR(pSource.As<IMFMediaSource>(&pSourceReturn));
494 return pSourceReturn;
497 // Create an activation object for a renderer, based on the stream media type.
499 IMFActivatePtr CreateMediaSinkActivate(
500 IMFStreamDescriptor* pSourceSD, // Pointer to the stream descriptor.
501 HWND hVideoWindow // Handle to the video clipping window.
504 IMFMediaTypeHandlerPtr pHandler;
505 IMFActivatePtr pActivate;
507 // Get the media type handler for the stream.
508 THROW_IF_ERR(pSourceSD->GetMediaTypeHandler(pHandler.GetAddressOf()));
510 // Get the major media type.
512 THROW_IF_ERR(pHandler->GetMajorType(&guidMajorType));
514 // Create an IMFActivate object for the renderer, based on the media type.
515 if (MFMediaType_Audio == guidMajorType)
517 // Create the audio renderer.
518 THROW_IF_ERR(MFCreateAudioRendererActivate(pActivate.GetAddressOf()));
520 else if (MFMediaType_Video == guidMajorType)
522 // Create the video renderer.
523 THROW_IF_ERR(MFCreateVideoRendererActivate(hVideoWindow, pActivate.GetAddressOf()));
527 // Unknown stream type.
528 THROW_IF_ERR(E_FAIL);
529 // Optionally, you could deselect this stream instead of failing.
532 // Return IMFActivate pointer to caller.
536 // Add a source node to a topology.
537 IMFTopologyNodePtr AddSourceNode(
538 IMFTopologyPtr pTopology, // Topology.
539 IMFMediaSourcePtr pSource, // Media source.
540 IMFPresentationDescriptorPtr pPD, // Presentation descriptor.
541 IMFStreamDescriptorPtr pSD) // Stream descriptor.
543 IMFTopologyNodePtr pNode;
546 THROW_IF_ERR(MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, pNode.GetAddressOf()));
548 // Set the attributes.
549 THROW_IF_ERR(pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource.Get()));
551 THROW_IF_ERR(pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD.Get()));
553 THROW_IF_ERR(pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD.Get()));
555 // Add the node to the topology.
556 THROW_IF_ERR(pTopology->AddNode(pNode.Get()));
558 // Return the pointer to the caller.
562 // Add an output node to a topology.
563 IMFTopologyNodePtr AddOutputNode(
564 IMFTopologyPtr pTopology, // Topology.
565 IMFActivatePtr pActivate, // Media sink activation object.
566 DWORD dwId // Identifier of the stream sink.
569 IMFTopologyNodePtr pNode;
572 THROW_IF_ERR(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode));
574 // Set the object pointer.
575 THROW_IF_ERR(pNode->SetObject(pActivate.Get()));
577 // Set the stream sink ID attribute.
578 THROW_IF_ERR(pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId));
580 THROW_IF_ERR(pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE));
582 // Add the node to the topology.
583 THROW_IF_ERR(pTopology->AddNode(pNode.Get()));
588 //</SnippetPlayer.cpp>
590 // Add a topology branch for one stream.
592 // For each stream, this function does the following:
594 // 1. Creates a source node associated with the stream.
595 // 2. Creates an output node for the renderer.
596 // 3. Connects the two nodes.
598 // The media session will add any decoders that are needed.
600 void AddBranchToPartialTopology(
601 IMFTopologyPtr pTopology, // Topology.
602 IMFMediaSourcePtr pSource, // Media source.
603 IMFPresentationDescriptorPtr pPD, // Presentation descriptor.
604 DWORD iStream, // Stream index.
605 HWND hVideoWnd) // Window for video playback.
607 IMFStreamDescriptorPtr pSD;
608 IMFActivatePtr pSinkActivate;
609 IMFTopologyNodePtr pSourceNode;
610 IMFTopologyNodePtr pOutputNode;
612 BOOL fSelected = FALSE;
614 THROW_IF_ERR(pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD));
618 // Create the media sink activation object.
619 pSinkActivate = CreateMediaSinkActivate(pSD.Get(), hVideoWnd);
621 // Add a source node for this stream.
622 pSourceNode = AddSourceNode(pTopology, pSource, pPD, pSD);
624 // Create the output node for the renderer.
625 pOutputNode = AddOutputNode(pTopology, pSinkActivate, 0);
627 // Connect the source node to the output node.
628 THROW_IF_ERR(pSourceNode->ConnectOutput(0, pOutputNode.Get(), 0));
630 // else: If not selected, don't add the branch.
633 // Create a playback topology from a media source.
634 // Receives a pointer to the topology.
635 IMFTopologyPtr CreatePlaybackTopology(
636 IMFMediaSourcePtr pSource, // Media source.
637 IMFPresentationDescriptorPtr pPD, // Presentation descriptor.
638 HWND hVideoWnd // Video window.
641 IMFTopologyPtr pTopology;
642 DWORD cSourceStreams = 0;
644 // Create a new topology.
645 THROW_IF_ERR(MFCreateTopology(&pTopology));
647 // Get the number of streams in the media source.
648 THROW_IF_ERR(pPD->GetStreamDescriptorCount(&cSourceStreams));
650 // For each stream, create the topology nodes and add them to the topology.
651 for (DWORD i = 0; i < cSourceStreams; i++)
653 AddBranchToPartialTopology(pTopology, pSource, pPD, i, hVideoWnd);
656 // Return the IMFTopology pointer to the caller.
660 void print_mft_(const GUID& guid,std::wfstream& out)
662 co_task_memory<IMFActivate*> activate;
666 HRESULT hr = MFTEnumEx(guid,MFT_ENUM_FLAG_ALL,NULL,NULL,&activate,&count);
668 if(SUCCEEDED(hr) && count > 0)
670 for(int i = 0; i < count;++i)
674 activate.get()[i]->GetStringLength(MFT_FRIENDLY_NAME_Attribute,&l);
675 std::unique_ptr<wchar_t[]> name(new wchar_t[l+1]);
676 memset(name.get(),0,l+1);
677 hr = activate.get()[i]->GetString(MFT_FRIENDLY_NAME_Attribute,name.get(),l+1,&l1);
678 out << name.get() << std::endl;
679 activate.get()[i]->Release();
681 //CoTaskMemFree(activate);
687 std::wfstream out(L"MFT.txt",std::ios_base::out | std::ios_base::trunc);
689 out << std::endl << "**" << L"MFT_CATEGORY_AUDIO_DECODER" << L"**" << std::endl << std::endl;
691 print_mft_(MFT_CATEGORY_AUDIO_DECODER,out);
693 out << std::endl << L"**" << L"MFT_CATEGORY_AUDIO_EFFECT" << L"**" << std::endl << std::endl;
694 print_mft_(MFT_CATEGORY_AUDIO_EFFECT,out);
696 out << std::endl << L"**" << L"MFT_CATEGORY_AUDIO_ENCODER" << L"**" << std::endl << std::endl;
697 print_mft_(MFT_CATEGORY_AUDIO_ENCODER,out);
699 out << std::endl << L"**" << L"MFT_CATEGORY_DEMULTIPLEXER" << L"**" << std::endl << std::endl;
700 print_mft_(MFT_CATEGORY_DEMULTIPLEXER,out);
702 out << std::endl << L"**" << L"MFT_CATEGORY_MULTIPLEXER" << L"**" << std::endl << std::endl;
703 print_mft_(MFT_CATEGORY_MULTIPLEXER,out);
705 out << std::endl << L"**" << L"MFT_CATEGORY_OTHER" << L"**" << std::endl << std::endl;
706 print_mft_(MFT_CATEGORY_OTHER,out);
708 out << std::endl << L"**" << L"MFT_CATEGORY_VIDEO_DECODER" << L"**" << std::endl << std::endl;
709 print_mft_(MFT_CATEGORY_VIDEO_DECODER,out);
711 out << std::endl << L"**" << L"MFT_CATEGORY_VIDEO_EFFECT" << L"**" << std::endl << std::endl;
712 print_mft_(MFT_CATEGORY_VIDEO_EFFECT,out);
714 out << std::endl << L"**" << L"MFT_CATEGORY_VIDEO_ENCODER" << L"**" << std::endl << std::endl;
715 print_mft_(MFT_CATEGORY_VIDEO_ENCODER,out);
717 out << std::endl << L"**" << L"MFT_CATEGORY_VIDEO_PROCESSOR" << L"**" << std::endl << std::endl;
718 print_mft_(MFT_CATEGORY_VIDEO_PROCESSOR,out);