media-player
1340 строк · 31.6 Кб
1//https://docs.microsoft.com/en-us/windows/win32/medfound/media-session-playback-example
2
3#include <stdio.h>
4#include <windows.h>
5#include <shobjidl.h>
6#include <shlwapi.h>
7#include <strsafe.h>
8#include <assert.h>
9#include <new>
10
11// Media Foundation headers
12#include <mfapi.h>
13#include <mfidl.h>
14#include <mferror.h>
15#include <evr.h>
16
17#pragma comment(lib, "shlwapi")
18#pragma comment(lib, "Mf.lib")
19#pragma comment(lib, "Mfplat.lib")
20
21PWSTR pszFilePath = L"C:\\Media\\file.avi";
22
23#define MY_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
24EXTERN_C const GUID DECLSPEC_SELECTANY name \
25= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
26
27MY_DEFINE_GUID(MFMediaType_Audio,
280x73647561, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
29MY_DEFINE_GUID(MFMediaType_Video,
300x73646976, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
31MY_DEFINE_GUID(MF_EVENT_TOPOLOGY_STATUS,
320x30c5018d, 0x9a53, 0x454b, 0xad, 0x9e, 0x6d, 0x5f, 0x8f, 0xa7, 0xc4, 0x3b);
33MY_DEFINE_GUID(MR_VIDEO_RENDER_SERVICE,
340x1092a86c,
350xab1a,
360x459a,
370xa3, 0x36, 0x83, 0x1f, 0xbc, 0x4d, 0x11, 0xff
38);
39
40const UINT WM_APP_PLAYER_EVENT = WM_APP + 1; // WPARAM = IMFMediaEvent*, WPARAM = MediaEventType
41
42template <class T> void SafeRelease(T **ppT)
43{
44if (*ppT)
45{
46(*ppT)->Release();
47*ppT = NULL;
48}
49}
50
51
52enum PlayerState
53{
54Closed = 0, // No session.
55Ready, // Session was created, ready to open a file.
56OpenPending, // Session is opening a file.
57Started, // Session is playing a file.
58Paused, // Session is paused.
59Stopped, // Session is stopped (ready to play).
60Closing // Application has closed the session, but is waiting for MESessionClosed.
61};
62
63HRESULT CreateMediaSource(PCWSTR pszURL, IMFMediaSource **ppSource);
64
65HRESULT CreatePlaybackTopology(IMFMediaSource *pSource,
66IMFPresentationDescriptor *pPD, HWND hVideoWnd,IMFTopology **ppTopology);
67
68class CPlayer : public IMFAsyncCallback
69{
70public:
71static HRESULT CreateInstance(HWND hVideo, HWND hEvent, CPlayer **ppPlayer);
72
73// IUnknown methods
74STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
75STDMETHODIMP_(ULONG) AddRef();
76STDMETHODIMP_(ULONG) Release();
77
78// IMFAsyncCallback methods
79STDMETHODIMP GetParameters(DWORD*, DWORD*)
80{
81// Implementation of this method is optional.
82return E_NOTIMPL;
83}
84STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult);
85
86// Playback
87HRESULT OpenURL(const WCHAR *sURL);
88HRESULT Play();
89HRESULT Pause();
90HRESULT Stop();
91HRESULT Shutdown();
92HRESULT HandleEvent(UINT_PTR pUnkPtr);
93PlayerState GetState() const { return m_state; }
94
95// Video functionality
96HRESULT Repaint();
97HRESULT ResizeVideo(WORD width, WORD height);
98
99BOOL HasVideo() const { return (m_pVideoDisplay != NULL); }
100
101protected:
102
103// Constructor is private. Use static CreateInstance method to instantiate.
104CPlayer(HWND hVideo, HWND hEvent);
105
106// Destructor is private. Caller should call Release.
107virtual ~CPlayer();
108
109HRESULT Initialize();
110HRESULT CreateSession();
111HRESULT CloseSession();
112HRESULT StartPlayback();
113
114// Media event handlers
115virtual HRESULT OnTopologyStatus(IMFMediaEvent *pEvent);
116virtual HRESULT OnPresentationEnded(IMFMediaEvent *pEvent);
117virtual HRESULT OnNewPresentation(IMFMediaEvent *pEvent);
118
119// Override to handle additional session events.
120virtual HRESULT OnSessionEvent(IMFMediaEvent*, MediaEventType)
121{
122return S_OK;
123}
124
125protected:
126long m_nRefCount; // Reference count.
127
128IMFMediaSession *m_pSession;
129IMFMediaSource *m_pSource;
130IMFVideoDisplayControl *m_pVideoDisplay;
131
132HWND m_hwndVideo; // Video window.
133HWND m_hwndEvent; // App window to receive events.
134PlayerState m_state; // Current state of the media session.
135HANDLE m_hCloseEvent; // Event to wait on while closing.
136};
137
138template <class Q>
139HRESULT GetEventObject(IMFMediaEvent *pEvent, Q **ppObject)
140{
141*ppObject = NULL; // zero output
142
143PROPVARIANT var;
144HRESULT hr = pEvent->GetValue(&var);
145if (SUCCEEDED(hr))
146{
147if (var.vt == VT_UNKNOWN)
148{
149hr = var.punkVal->QueryInterface(ppObject);
150}
151else
152{
153hr = MF_E_INVALIDTYPE;
154}
155PropVariantClear(&var);
156}
157return hr;
158}
159
160HRESULT CreateMediaSource(PCWSTR pszURL, IMFMediaSource **ppSource);
161
162HRESULT CreatePlaybackTopology(IMFMediaSource *pSource,
163IMFPresentationDescriptor *pPD, HWND hVideoWnd,IMFTopology **ppTopology);
164
165// Static class method to create the CPlayer object.
166
167HRESULT CPlayer::CreateInstance(
168HWND hVideo, // Video window.
169HWND hEvent, // Window to receive notifications.
170CPlayer **ppPlayer) // Receives a pointer to the CPlayer object.
171{
172if (ppPlayer == NULL)
173{
174return E_POINTER;
175}
176
177CPlayer *pPlayer = new (std::nothrow) CPlayer(hVideo, hEvent);
178if (pPlayer == NULL)
179{
180return E_OUTOFMEMORY;
181}
182
183HRESULT hr = pPlayer->Initialize();
184if (SUCCEEDED(hr))
185{
186*ppPlayer = pPlayer;
187}
188else
189{
190pPlayer->Release();
191}
192return hr;
193}
194
195HRESULT CPlayer::Initialize()
196{
197// Start up Media Foundation platform.
198HRESULT hr = MFStartup(MF_VERSION);
199if (SUCCEEDED(hr))
200{
201m_hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
202if (m_hCloseEvent == NULL)
203{
204hr = HRESULT_FROM_WIN32(GetLastError());
205}
206}
207return hr;
208}
209
210CPlayer::CPlayer(HWND hVideo, HWND hEvent) :
211m_pSession(NULL),
212m_pSource(NULL),
213m_pVideoDisplay(NULL),
214m_hwndVideo(hVideo),
215m_hwndEvent(hEvent),
216m_state(Closed),
217m_hCloseEvent(NULL),
218m_nRefCount(1)
219{
220}
221
222CPlayer::~CPlayer()
223{
224assert(m_pSession == NULL);
225// If FALSE, the app did not call Shutdown().
226
227// When CPlayer calls IMediaEventGenerator::BeginGetEvent on the
228// media session, it causes the media session to hold a reference
229// count on the CPlayer.
230
231// This creates a circular reference count between CPlayer and the
232// media session. Calling Shutdown breaks the circular reference
233// count.
234
235// If CreateInstance fails, the application will not call
236// Shutdown. To handle that case, call Shutdown in the destructor.
237
238Shutdown();
239}
240
241// IUnknown methods
242
243HRESULT CPlayer::QueryInterface(REFIID riid, void** ppv)
244{
245static const QITAB qit[] =
246{
247QITABENT(CPlayer, IMFAsyncCallback),
248{ 0 }
249};
250return QISearch(this, qit, riid, ppv);
251}
252
253ULONG CPlayer::AddRef()
254{
255return InterlockedIncrement(&m_nRefCount);
256}
257
258ULONG CPlayer::Release()
259{
260ULONG uCount = InterlockedDecrement(&m_nRefCount);
261if (uCount == 0)
262{
263delete this;
264}
265return uCount;
266}
267
268// Open a URL for playback.
269HRESULT CPlayer::OpenURL(const WCHAR *sURL)
270{
271// 1. Create a new media session.
272// 2. Create the media source.
273// 3. Create the topology.
274// 4. Queue the topology [asynchronous]
275// 5. Start playback [asynchronous - does not happen in this method.]
276
277IMFTopology *pTopology = NULL;
278IMFPresentationDescriptor* pSourcePD = NULL;
279
280// Create the media session.
281HRESULT hr = CreateSession();
282if (FAILED(hr))
283{
284goto done;
285}
286
287// Create the media source.
288hr = CreateMediaSource(sURL, &m_pSource);
289if (FAILED(hr))
290{
291goto done;
292}
293
294// Create the presentation descriptor for the media source.
295hr = m_pSource->CreatePresentationDescriptor(&pSourcePD);
296if (FAILED(hr))
297{
298goto done;
299}
300
301// Create a partial topology.
302hr = CreatePlaybackTopology(m_pSource, pSourcePD, m_hwndVideo, &pTopology);
303if (FAILED(hr))
304{
305goto done;
306}
307
308// Set the topology on the media session.
309hr = m_pSession->SetTopology(0, pTopology);
310if (FAILED(hr))
311{
312goto done;
313}
314
315m_state = OpenPending;
316
317// If SetTopology succeeds, the media session will queue an
318// MESessionTopologySet event.
319
320done:
321if (FAILED(hr))
322{
323m_state = Closed;
324}
325
326SafeRelease(&pSourcePD);
327SafeRelease(&pTopology);
328return hr;
329}
330
331// Pause playback.
332HRESULT CPlayer::Pause()
333{
334if (m_state != Started)
335{
336return MF_E_INVALIDREQUEST;
337}
338if (m_pSession == NULL || m_pSource == NULL)
339{
340return E_UNEXPECTED;
341}
342
343HRESULT hr = m_pSession->Pause();
344if (SUCCEEDED(hr))
345{
346m_state = Paused;
347}
348
349return hr;
350}
351
352// Stop playback.
353HRESULT CPlayer::Stop()
354{
355if (m_state != Started && m_state != Paused)
356{
357return MF_E_INVALIDREQUEST;
358}
359if (m_pSession == NULL)
360{
361return E_UNEXPECTED;
362}
363
364HRESULT hr = m_pSession->Stop();
365if (SUCCEEDED(hr))
366{
367m_state = Stopped;
368}
369return hr;
370}
371
372// Repaint the video window. Call this method on WM_PAINT.
373
374HRESULT CPlayer::Repaint()
375{
376if (m_pVideoDisplay)
377{
378return m_pVideoDisplay->RepaintVideo();
379}
380else
381{
382return S_OK;
383}
384}
385
386// Resize the video rectangle.
387//
388// Call this method if the size of the video window changes.
389
390HRESULT CPlayer::ResizeVideo(WORD width, WORD height)
391{
392if (m_pVideoDisplay)
393{
394// Set the destination rectangle.
395// Leave the default source rectangle (0,0,1,1).
396
397RECT rcDest = { 0, 0, width, height };
398
399return m_pVideoDisplay->SetVideoPosition(NULL, &rcDest);
400}
401else
402{
403return S_OK;
404}
405}
406
407// Callback for the asynchronous BeginGetEvent method.
408
409HRESULT CPlayer::Invoke(IMFAsyncResult *pResult)
410{
411MediaEventType meType = MEUnknown; // Event type
412
413IMFMediaEvent *pEvent = NULL;
414
415// Get the event from the event queue.
416HRESULT hr = m_pSession->EndGetEvent(pResult, &pEvent);
417if (FAILED(hr))
418{
419goto done;
420}
421
422// Get the event type.
423hr = pEvent->GetType(&meType);
424if (FAILED(hr))
425{
426goto done;
427}
428
429if (meType == MESessionClosed)
430{
431// The session was closed.
432// The application is waiting on the m_hCloseEvent event handle.
433SetEvent(m_hCloseEvent);
434}
435else
436{
437// For all other events, get the next event in the queue.
438hr = m_pSession->BeginGetEvent(this, NULL);
439if (FAILED(hr))
440{
441goto done;
442}
443}
444
445// Check the application state.
446
447// If a call to IMFMediaSession::Close is pending, it means the
448// application is waiting on the m_hCloseEvent event and
449// the application's message loop is blocked.
450
451// Otherwise, post a private window message to the application.
452
453if (m_state != Closing)
454{
455// Leave a reference count on the event.
456pEvent->AddRef();
457
458PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT,
459(WPARAM)pEvent, (LPARAM)meType);
460}
461
462done:
463SafeRelease(&pEvent);
464return S_OK;
465}
466
467HRESULT CPlayer::HandleEvent(UINT_PTR pEventPtr)
468{
469HRESULT hrStatus = S_OK;
470MediaEventType meType = MEUnknown;
471
472IMFMediaEvent *pEvent = (IMFMediaEvent*)pEventPtr;
473
474if (pEvent == NULL)
475{
476return E_POINTER;
477}
478
479// Get the event type.
480HRESULT hr = pEvent->GetType(&meType);
481if (FAILED(hr))
482{
483goto done;
484}
485
486// Get the event status. If the operation that triggered the event
487// did not succeed, the status is a failure code.
488hr = pEvent->GetStatus(&hrStatus);
489
490// Check if the async operation succeeded.
491if (SUCCEEDED(hr) && FAILED(hrStatus))
492{
493hr = hrStatus;
494}
495if (FAILED(hr))
496{
497goto done;
498}
499
500switch(meType)
501{
502case MESessionTopologyStatus:
503hr = OnTopologyStatus(pEvent);
504break;
505
506case MEEndOfPresentation:
507hr = OnPresentationEnded(pEvent);
508break;
509
510case MENewPresentation:
511hr = OnNewPresentation(pEvent);
512break;
513
514default:
515hr = OnSessionEvent(pEvent, meType);
516break;
517}
518
519done:
520SafeRelease(&pEvent);
521return hr;
522}
523
524// Release all resources held by this object.
525HRESULT CPlayer::Shutdown()
526{
527// Close the session
528HRESULT hr = CloseSession();
529
530// Shutdown the Media Foundation platform
531MFShutdown();
532
533if (m_hCloseEvent)
534{
535CloseHandle(m_hCloseEvent);
536m_hCloseEvent = NULL;
537}
538
539return hr;
540}
541
542/// Protected methods
543
544HRESULT CPlayer::OnTopologyStatus(IMFMediaEvent *pEvent)
545{
546UINT32 status;
547
548HRESULT hr = pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status);
549if (SUCCEEDED(hr) && (status == MF_TOPOSTATUS_READY))
550{
551SafeRelease(&m_pVideoDisplay);
552
553// Get the IMFVideoDisplayControl interface from EVR. This call is
554// expected to fail if the media file does not have a video stream.
555
556(void)MFGetService(m_pSession, MR_VIDEO_RENDER_SERVICE,
557IID_PPV_ARGS(&m_pVideoDisplay));
558
559hr = StartPlayback();
560}
561return hr;
562}
563
564
565// Handler for MEEndOfPresentation event.
566HRESULT CPlayer::OnPresentationEnded(IMFMediaEvent *pEvent)
567{
568// The session puts itself into the stopped state automatically.
569m_state = Stopped;
570return S_OK;
571}
572
573// Handler for MENewPresentation event.
574//
575// This event is sent if the media source has a new presentation, which
576// requires a new topology.
577
578HRESULT CPlayer::OnNewPresentation(IMFMediaEvent *pEvent)
579{
580IMFPresentationDescriptor *pPD = NULL;
581IMFTopology *pTopology = NULL;
582
583// Get the presentation descriptor from the event.
584HRESULT hr = GetEventObject(pEvent, &pPD);
585if (FAILED(hr))
586{
587goto done;
588}
589
590// Create a partial topology.
591hr = CreatePlaybackTopology(m_pSource, pPD, m_hwndVideo,&pTopology);
592if (FAILED(hr))
593{
594goto done;
595}
596
597// Set the topology on the media session.
598hr = m_pSession->SetTopology(0, pTopology);
599if (FAILED(hr))
600{
601goto done;
602}
603
604m_state = OpenPending;
605
606done:
607SafeRelease(&pTopology);
608SafeRelease(&pPD);
609return S_OK;
610}
611
612// Create a new instance of the media session.
613HRESULT CPlayer::CreateSession()
614{
615// Close the old session, if any.
616HRESULT hr = CloseSession();
617if (FAILED(hr))
618{
619goto done;
620}
621
622assert(m_state == Closed);
623
624// Create the media session.
625hr = MFCreateMediaSession(NULL, &m_pSession);
626if (FAILED(hr))
627{
628goto done;
629}
630
631// Start pulling events from the media session
632hr = m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL);
633if (FAILED(hr))
634{
635goto done;
636}
637
638m_state = Ready;
639
640done:
641return hr;
642}
643
644// Close the media session.
645HRESULT CPlayer::CloseSession()
646{
647// The IMFMediaSession::Close method is asynchronous, but the
648// CPlayer::CloseSession method waits on the MESessionClosed event.
649//
650// MESessionClosed is guaranteed to be the last event that the
651// media session fires.
652
653HRESULT hr = S_OK;
654
655SafeRelease(&m_pVideoDisplay);
656
657// First close the media session.
658if (m_pSession)
659{
660DWORD dwWaitResult = 0;
661
662m_state = Closing;
663
664hr = m_pSession->Close();
665// Wait for the close operation to complete
666if (SUCCEEDED(hr))
667{
668dwWaitResult = WaitForSingleObject(m_hCloseEvent, 5000);
669if (dwWaitResult == WAIT_TIMEOUT)
670{
671assert(FALSE);
672}
673// Now there will be no more events from this session.
674}
675}
676
677// Complete shutdown operations.
678if (SUCCEEDED(hr))
679{
680// Shut down the media source. (Synchronous operation, no events.)
681if (m_pSource)
682{
683(void)m_pSource->Shutdown();
684}
685// Shut down the media session. (Synchronous operation, no events.)
686if (m_pSession)
687{
688(void)m_pSession->Shutdown();
689}
690}
691
692SafeRelease(&m_pSource);
693SafeRelease(&m_pSession);
694m_state = Closed;
695return hr;
696}
697
698// Start playback from the current position.
699HRESULT CPlayer::StartPlayback()
700{
701assert(m_pSession != NULL);
702
703PROPVARIANT varStart;
704PropVariantInit(&varStart);
705
706HRESULT hr = m_pSession->Start(&GUID_NULL, &varStart);
707if (SUCCEEDED(hr))
708{
709// Note: Start is an asynchronous operation. However, we
710// can treat our state as being already started. If Start
711// fails later, we'll get an MESessionStarted event with
712// an error code, and we will update our state then.
713m_state = Started;
714}
715PropVariantClear(&varStart);
716return hr;
717}
718
719// Start playback from paused or stopped.
720HRESULT CPlayer::Play()
721{
722if (m_state != Paused && m_state != Stopped)
723{
724return MF_E_INVALIDREQUEST;
725}
726if (m_pSession == NULL || m_pSource == NULL)
727{
728return E_UNEXPECTED;
729}
730return StartPlayback();
731}
732
733
734// Create a media source from a URL.
735HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource)
736{
737MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
738
739IMFSourceResolver* pSourceResolver = NULL;
740IUnknown* pSource = NULL;
741
742// Create the source resolver.
743HRESULT hr = MFCreateSourceResolver(&pSourceResolver);
744if (FAILED(hr))
745{
746goto done;
747}
748
749// Use the source resolver to create the media source.
750
751// Note: For simplicity this sample uses the synchronous method to create
752// the media source. However, creating a media source can take a noticeable
753// amount of time, especially for a network source. For a more responsive
754// UI, use the asynchronous BeginCreateObjectFromURL method.
755
756hr = pSourceResolver->CreateObjectFromURL(
757sURL, // URL of the source.
758MF_RESOLUTION_MEDIASOURCE, // Create a source object.
759NULL, // Optional property store.
760&ObjectType, // Receives the created object type.
761&pSource // Receives a pointer to the media source.
762);
763if (FAILED(hr))
764{
765goto done;
766}
767
768// Get the IMFMediaSource interface from the media source.
769hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));
770
771done:
772SafeRelease(&pSourceResolver);
773SafeRelease(&pSource);
774return hr;
775}
776
777// Create an activation object for a renderer, based on the stream media type.
778
779HRESULT CreateMediaSinkActivate(
780IMFStreamDescriptor *pSourceSD, // Pointer to the stream descriptor.
781HWND hVideoWindow, // Handle to the video clipping window.
782IMFActivate **ppActivate
783)
784{
785IMFMediaTypeHandler *pHandler = NULL;
786IMFActivate *pActivate = NULL;
787
788// Get the media type handler for the stream.
789HRESULT hr = pSourceSD->GetMediaTypeHandler(&pHandler);
790if (FAILED(hr))
791{
792goto done;
793}
794
795// Get the major media type.
796GUID guidMajorType;
797hr = pHandler->GetMajorType(&guidMajorType);
798if (FAILED(hr))
799{
800goto done;
801}
802
803// Create an IMFActivate object for the renderer, based on the media type.
804if (MFMediaType_Audio == guidMajorType)
805{
806// Create the audio renderer.
807hr = MFCreateAudioRendererActivate(&pActivate);
808}
809else if (MFMediaType_Video == guidMajorType)
810{
811// Create the video renderer.
812hr = MFCreateVideoRendererActivate(hVideoWindow, &pActivate);
813}
814else
815{
816// Unknown stream type.
817hr = E_FAIL;
818// Optionally, you could deselect this stream instead of failing.
819}
820if (FAILED(hr))
821{
822goto done;
823}
824
825// Return IMFActivate pointer to caller.
826*ppActivate = pActivate;
827(*ppActivate)->AddRef();
828
829done:
830SafeRelease(&pHandler);
831SafeRelease(&pActivate);
832return hr;
833}
834
835// Add a source node to a topology.
836HRESULT AddSourceNode(
837IMFTopology *pTopology, // Topology.
838IMFMediaSource *pSource, // Media source.
839IMFPresentationDescriptor *pPD, // Presentation descriptor.
840IMFStreamDescriptor *pSD, // Stream descriptor.
841IMFTopologyNode **ppNode) // Receives the node pointer.
842{
843IMFTopologyNode *pNode = NULL;
844
845// Create the node.
846HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
847if (FAILED(hr))
848{
849goto done;
850}
851
852// Set the attributes.
853hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
854if (FAILED(hr))
855{
856goto done;
857}
858
859hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD);
860if (FAILED(hr))
861{
862goto done;
863}
864
865hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD);
866if (FAILED(hr))
867{
868goto done;
869}
870
871// Add the node to the topology.
872hr = pTopology->AddNode(pNode);
873if (FAILED(hr))
874{
875goto done;
876}
877
878// Return the pointer to the caller.
879*ppNode = pNode;
880(*ppNode)->AddRef();
881
882done:
883SafeRelease(&pNode);
884return hr;
885}
886
887// Add an output node to a topology.
888HRESULT AddOutputNode(
889IMFTopology *pTopology, // Topology.
890IMFActivate *pActivate, // Media sink activation object.
891DWORD dwId, // Identifier of the stream sink.
892IMFTopologyNode **ppNode) // Receives the node pointer.
893{
894IMFTopologyNode *pNode = NULL;
895
896// Create the node.
897HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
898if (FAILED(hr))
899{
900goto done;
901}
902
903// Set the object pointer.
904hr = pNode->SetObject(pActivate);
905if (FAILED(hr))
906{
907goto done;
908}
909
910// Set the stream sink ID attribute.
911hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId);
912if (FAILED(hr))
913{
914goto done;
915}
916
917hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
918if (FAILED(hr))
919{
920goto done;
921}
922
923// Add the node to the topology.
924hr = pTopology->AddNode(pNode);
925if (FAILED(hr))
926{
927goto done;
928}
929
930// Return the pointer to the caller.
931*ppNode = pNode;
932(*ppNode)->AddRef();
933
934done:
935SafeRelease(&pNode);
936return hr;
937}
938//</SnippetPlayer.cpp>
939
940// Add a topology branch for one stream.
941//
942// For each stream, this function does the following:
943//
944// 1. Creates a source node associated with the stream.
945// 2. Creates an output node for the renderer.
946// 3. Connects the two nodes.
947//
948// The media session will add any decoders that are needed.
949
950HRESULT AddBranchToPartialTopology(
951IMFTopology *pTopology, // Topology.
952IMFMediaSource *pSource, // Media source.
953IMFPresentationDescriptor *pPD, // Presentation descriptor.
954DWORD iStream, // Stream index.
955HWND hVideoWnd) // Window for video playback.
956{
957IMFStreamDescriptor *pSD = NULL;
958IMFActivate *pSinkActivate = NULL;
959IMFTopologyNode *pSourceNode = NULL;
960IMFTopologyNode *pOutputNode = NULL;
961
962BOOL fSelected = FALSE;
963
964HRESULT hr = pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD);
965if (FAILED(hr))
966{
967goto done;
968}
969
970if (fSelected)
971{
972// Create the media sink activation object.
973hr = CreateMediaSinkActivate(pSD, hVideoWnd, &pSinkActivate);
974if (FAILED(hr))
975{
976goto done;
977}
978
979// Add a source node for this stream.
980hr = AddSourceNode(pTopology, pSource, pPD, pSD, &pSourceNode);
981if (FAILED(hr))
982{
983goto done;
984}
985
986// Create the output node for the renderer.
987hr = AddOutputNode(pTopology, pSinkActivate, 0, &pOutputNode);
988if (FAILED(hr))
989{
990goto done;
991}
992
993// Connect the source node to the output node.
994hr = pSourceNode->ConnectOutput(0, pOutputNode, 0);
995}
996// else: If not selected, don't add the branch.
997
998done:
999SafeRelease(&pSD);
1000SafeRelease(&pSinkActivate);
1001SafeRelease(&pSourceNode);
1002SafeRelease(&pOutputNode);
1003return hr;
1004}
1005
1006// Create a playback topology from a media source.
1007HRESULT CreatePlaybackTopology(
1008IMFMediaSource *pSource, // Media source.
1009IMFPresentationDescriptor *pPD, // Presentation descriptor.
1010HWND hVideoWnd, // Video window.
1011IMFTopology **ppTopology) // Receives a pointer to the topology.
1012{
1013IMFTopology *pTopology = NULL;
1014DWORD cSourceStreams = 0;
1015
1016// Create a new topology.
1017HRESULT hr = MFCreateTopology(&pTopology);
1018if (FAILED(hr))
1019{
1020goto done;
1021}
1022
1023
1024
1025
1026// Get the number of streams in the media source.
1027hr = pPD->GetStreamDescriptorCount(&cSourceStreams);
1028if (FAILED(hr))
1029{
1030goto done;
1031}
1032
1033// For each stream, create the topology nodes and add them to the topology.
1034for (DWORD i = 0; i < cSourceStreams; i++)
1035{
1036hr = AddBranchToPartialTopology(pTopology, pSource, pPD, i, hVideoWnd);
1037if (FAILED(hr))
1038{
1039goto done;
1040}
1041}
1042
1043// Return the IMFTopology pointer to the caller.
1044*ppTopology = pTopology;
1045(*ppTopology)->AddRef();
1046
1047done:
1048SafeRelease(&pTopology);
1049return hr;
1050}
1051
1052//***** Application *********
1053
1054PCWSTR szTitle = L"BasicPlayback";
1055PCWSTR szWindowClass = L"MFBASICPLAYBACK";
1056
1057HINSTANCE g_hInstance; // current instance
1058BOOL g_bRepaintClient = TRUE; // Repaint the application client area?
1059CPlayer *g_pPlayer = NULL; // Global player object.
1060HWND hWnd=NULL;
1061
1062// Note: After WM_CREATE is processed, g_pPlayer remains valid until the
1063// window is destroyed.
1064
1065// Forward declarations of functions included in this code module:
1066BOOL InitInstance(HINSTANCE, int);
1067LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
1068INT_PTR CALLBACK OpenUrlDialogProc(HWND, UINT, WPARAM, LPARAM);
1069void NotifyError(HWND hwnd, const WCHAR *sErrorMessage, HRESULT hr);
1070void UpdateUI(HWND hwnd, PlayerState state);
1071HRESULT AllocGetWindowText(HWND hwnd, WCHAR **pszText, DWORD *pcchLen);
1072
1073// Message handlers
1074LRESULT OnCreateWindow(HWND hwnd);
1075void OnFileOpen(HWND hwnd);
1076void OnOpenURL(HWND hwnd);
1077void OnPlayerEvent(HWND hwnd, WPARAM pUnkPtr);
1078void OnPaint(HWND hwnd);
1079void OnResize(WORD width, WORD height);
1080void OnKeyPress(WPARAM key);
1081
1082// Create the application window.
1083BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
1084{
1085WNDCLASSEX wcex;
1086
1087g_hInstance = hInst; // Store the instance handle.
1088
1089// Register the window class.
1090ZeroMemory(&wcex, sizeof(WNDCLASSEX));
1091wcex.cbSize = sizeof(WNDCLASSEX);
1092wcex.style = CS_HREDRAW | CS_VREDRAW;
1093wcex.lpfnWndProc = WndProc;
1094wcex.hInstance = hInst;
1095wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
1096wcex.lpszMenuName = NULL;
1097wcex.hCursor = LoadCursor(NULL,IDC_ARROW);
1098wcex.lpszClassName = szWindowClass;
1099
1100if (RegisterClassEx(&wcex) == 0)
1101{
1102return FALSE;
1103}
1104
1105// Create the application window.
1106hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
1107CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL);
1108
1109if (hWnd == 0)
1110{
1111return FALSE;
1112}
1113
1114ShowWindow(hWnd, nCmdShow);
1115UpdateWindow(hWnd);
1116
1117return TRUE;
1118}
1119
1120// Message handler for the main window.
1121
1122LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1123{
1124LRESULT res=0;
1125
1126switch (message)
1127{
1128case WM_CREATE:
1129res= OnCreateWindow(hwnd);
1130
1131return res;
1132
1133case WM_PAINT:
1134OnPaint(hwnd);
1135break;
1136
1137case WM_SIZE:
1138OnResize(LOWORD(lParam), HIWORD(lParam));
1139break;
1140
1141case WM_ERASEBKGND:
1142// Suppress window erasing, to reduce flickering while the video is playing.
1143return 1;
1144
1145case WM_DESTROY:
1146PostQuitMessage(0);
1147break;
1148
1149case WM_CHAR:
1150
1151OnKeyPress(wParam);
1152break;
1153
1154case WM_APP_PLAYER_EVENT:
1155OnPlayerEvent(hwnd, wParam);
1156break;
1157
1158default:
1159return DefWindowProc(hwnd, message, wParam, lParam);
1160}
1161return 0;
1162}
1163
1164// Open an audio/video file.
1165void OnFileOpen(PWSTR file)
1166{
1167// Display the file name to the user.
1168HRESULT hr = g_pPlayer->OpenURL(file);
1169if (SUCCEEDED(hr))
1170{
1171UpdateUI(hWnd, OpenPending);
1172}
1173
1174done:
1175if (FAILED(hr))
1176{
1177NotifyError(hWnd, L"Could not open the file.", hr);
1178UpdateUI(hWnd, Closed);
1179}
1180}
1181
1182// Handler for WM_CREATE message.
1183LRESULT OnCreateWindow(HWND hwnd)
1184{
1185// Initialize the player object.
1186HRESULT hr = CPlayer::CreateInstance(hwnd, hwnd, &g_pPlayer);
1187if (SUCCEEDED(hr))
1188{
1189UpdateUI(hwnd, Closed);
1190return 0; // Success.
1191}
1192else
1193{
1194NotifyError(NULL, L"Could not initialize the player object.", hr);
1195return -1; // Destroy the window
1196}
1197}
1198
1199// Handler for WM_PAINT messages.
1200void OnPaint(HWND hwnd)
1201{
1202PAINTSTRUCT ps;
1203HDC hdc = BeginPaint(hwnd, &ps);
1204
1205if (g_pPlayer && g_pPlayer->HasVideo())
1206{
1207// Video is playing. Ask the player to repaint.
1208g_pPlayer->Repaint();
1209}
1210else
1211{
1212// The video is not playing, so we must paint the application window.
1213RECT rc;
1214GetClientRect(hwnd, &rc);
1215FillRect(hdc, &rc, (HBRUSH) COLOR_WINDOW);
1216}
1217EndPaint(hwnd, &ps);
1218}
1219
1220// Handler for WM_SIZE messages.
1221void OnResize(WORD width, WORD height)
1222{
1223if (g_pPlayer)
1224{
1225g_pPlayer->ResizeVideo(width, height);
1226}
1227}
1228
1229
1230// Handler for WM_CHAR messages.
1231void OnKeyPress(WPARAM key)
1232{
1233switch (key)
1234{
1235// Space key toggles between running and paused
1236case VK_SPACE:
1237if (g_pPlayer->GetState() == Started)
1238{
1239g_pPlayer->Pause();
1240}
1241else if (g_pPlayer->GetState() == Paused)
1242{
1243g_pPlayer->Play();
1244}
1245break;
1246}
1247}
1248
1249// Update the application UI to reflect the current state.
1250
1251void UpdateUI(HWND hwnd, PlayerState state)
1252{
1253BOOL bWaiting = FALSE;
1254BOOL bPlayback = FALSE;
1255
1256assert(g_pPlayer != NULL);
1257
1258switch (state)
1259{
1260case OpenPending:
1261bWaiting = TRUE;
1262break;
1263
1264case Started:
1265bPlayback = TRUE;
1266break;
1267
1268case Paused:
1269bPlayback = TRUE;
1270break;
1271}
1272
1273if (bPlayback && g_pPlayer->HasVideo())
1274{
1275g_bRepaintClient = FALSE;
1276}
1277else
1278{
1279g_bRepaintClient = TRUE;
1280}
1281}
1282
1283// Show a message box with an error message.
1284void NotifyError(HWND hwnd, PCWSTR pszErrorMessage, HRESULT hrErr)
1285{
1286const size_t MESSAGE_LEN = 512;
1287WCHAR message[MESSAGE_LEN];
1288
1289if (SUCCEEDED(StringCchPrintf(message, MESSAGE_LEN, L"%s (HRESULT = 0x%X)",
1290pszErrorMessage, hrErr)))
1291{
1292MessageBox(hwnd, message, NULL, MB_OK | MB_ICONERROR);
1293}
1294}
1295
1296// Handler for Media Session events.
1297void OnPlayerEvent(HWND hwnd, WPARAM pUnkPtr)
1298{
1299HRESULT hr = g_pPlayer->HandleEvent(pUnkPtr);
1300if (FAILED(hr))
1301{
1302NotifyError(hwnd, L"OnPlayerEvent error", hr);
1303}
1304UpdateUI(hwnd, g_pPlayer->GetState());
1305}
1306
1307//*******************************************************
1308
1309int main()
1310{
1311// Perform application initialization.
1312if (!InitInstance(GetModuleHandle(NULL), SW_SHOW))
1313{
1314NotifyError(NULL, L"Could not initialize the application.",
1315HRESULT_FROM_WIN32(GetLastError()));
1316return FALSE;
1317}
1318
1319OnFileOpen(pszFilePath);
1320g_pPlayer->Play();
1321
1322MSG msg;
1323ZeroMemory(&msg, sizeof(msg));
1324
1325// Main message loop.
1326while (GetMessage(&msg, NULL, 0, 0))
1327{
1328TranslateMessage(&msg);
1329DispatchMessage(&msg);
1330}
1331
1332// Clean up.
1333if (g_pPlayer)
1334{
1335g_pPlayer->Shutdown();
1336SafeRelease(&g_pPlayer);
1337}
1338
1339return 0;
1340}
1341