OSDN Git Service

18cab3ac61cb25f61d74510bfffb1ecaa360c3cb
[winaudioj/mfsample.git] / mfsample / player.cpp
1
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
5 // PARTICULAR PURPOSE.
6 //
7 // Copyright (c) Microsoft Corporation. All rights reserved.
8
9 #include "stdafx.h"
10 #include "Player.h"
11 #include <assert.h>
12 #include <iostream>
13
14 #if _DEBUG
15 #define _CRTDBG_MAP_ALLOC
16 #include <crtdbg.h>
17 #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
18 #endif
19
20 #include "sf_memory.h"
21
22 //Mf.lib Mfidl.h
23 namespace sf {
24   namespace player {
25     void print_mft();
26
27
28     IMFMediaSourcePtr CreateMediaSource(const std::wstring& pszURL);
29
30     IMFTopologyPtr CreatePlaybackTopology(IMFMediaSourcePtr pSource, 
31       IMFPresentationDescriptorPtr pPD, HWND hVideoWnd);
32
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.
37       )           
38     {
39
40       PlayerPtr p(new Player(hVideo, hEvent));
41
42       if (!p)
43       {
44         throw win32_error_exception(E_OUTOFMEMORY);
45       }
46       p->process_event(ev::Init());
47       print_mft();
48 //      p->processInitialize();
49       return p;
50     }
51
52     void Player_::initialize(const ev::Init& ev)
53     {
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()));
58     }
59
60     Player_::Player_(HWND hVideo, HWND hEvent) : 
61       m_hwndVideo(hVideo),
62       m_hwndEvent(hEvent),
63 //      m_state(Closed),
64       m_hCloseEvent(NULL),
65       m_nRefCount(0)
66     {
67     }
68
69     Player_::~Player_()
70     {
71       assert(m_pSession.Get() == NULL);  
72       // If FALSE, the app did not call Shutdown().
73
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_. 
77
78       // This creates a circular reference count between Player_ and the 
79       // media session. Calling Shutdown breaks the circular reference 
80       // count.
81
82       // If CreateInstance fails, the application will not call 
83       // Shutdown. To handle that case, call Shutdown in the destructor. 
84
85       shutdown(ev::Close());
86     }
87
88     // IUnknown methods
89
90     HRESULT Player_::QueryInterface(REFIID riid, void** ppv)
91     {
92       static const QITAB qit[] = 
93       {
94         QITABENT(Player_, IMFAsyncCallback),
95         { 0 }
96       };
97       return QISearch(this, qit, riid, ppv);
98     }
99
100     ULONG Player_::AddRef()
101     {
102       return InterlockedIncrement(&m_nRefCount);
103     }
104
105     ULONG Player_::Release()
106     {
107       ULONG uCount = InterlockedDecrement(&m_nRefCount);
108       if (uCount == 0)
109       {
110         delete this;
111       }
112       return uCount;
113     }
114
115     //  Open a URL for playback.
116     void Player_::open_url( ev::OpenURL const& openurl)
117     {
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.]
123
124       IMFTopologyPtr pTopology;
125       IMFPresentationDescriptorPtr pSourcePD;
126
127       // Create the media session.
128       CreateSession();
129
130       // Create the media source.
131       m_pSource = CreateMediaSource(openurl.url());
132
133       // Create the presentation descriptor for the media source.
134       THROW_IF_ERR(m_pSource->CreatePresentationDescriptor(&pSourcePD));
135
136       // Create a partial topology.
137       pTopology = CreatePlaybackTopology(m_pSource, pSourcePD, m_hwndVideo);
138
139       // Set the topology on the media session.
140       THROW_IF_ERR(m_pSession->SetTopology(0, pTopology.Get()));
141
142 //      OnOpenURL_();
143
144       // m_state = OpenPending;
145
146       // If SetTopology succeeds, the media session will queue an 
147       // MESessionTopologySet event.
148     }
149
150     //  Pause playback.
151     void Player_::pause( ev::Pause const& ev)    
152     {
153 //      if (m_state != Started)
154 //      {
155 //        throw win32_error_exception(MF_E_INVALIDREQUEST);
156 //      }
157 //
158 //      if (!m_pSession  || !m_pSource)
159 //      {
160 //       throw win32_error_exception(E_UNEXPECTED);
161 //      }
162       THROW_IF_ERR(m_pSession->Pause());
163       OnPause()(static_cast<this_type&>(*this));
164       
165       //m_state = Paused;
166     }
167
168     // Stop playback.
169     void Player_::stop( ev::Stop const& ev)
170     {
171       //if (m_state != Started && m_state != Paused)
172       //{
173       //  throw win32_error_exception( MF_E_INVALIDREQUEST );
174       //}
175       //if (!m_pSession)
176       //{
177       //  throw win32_error_exception( E_UNEXPECTED);
178       //}
179
180       THROW_IF_ERR(m_pSession->Stop());
181       OnStop()(static_cast<this_type&>(*this));
182     }
183
184     //  Repaint the video window. Call this method on WM_PAINT.
185     void Player_::Repaint()
186     {
187       if (m_pVideoDisplay)
188       {
189         m_pVideoDisplay->RepaintVideo();
190       }
191     }
192
193     //  Resize the video rectangle.
194     //
195     //  Call this method if the size of the video window changes.
196
197     void Player_::ResizeVideo(WORD width, WORD height)
198     {
199       if (m_pVideoDisplay)
200       {
201         // Set the destination rectangle.
202         // Leave the default source rectangle (0,0,1,1).
203
204         RECT rcDest = { 0, 0, width, height };
205
206         THROW_IF_ERR(m_pVideoDisplay->SetVideoPosition(NULL, &rcDest));
207       }
208     }
209
210     //  Callback for the asynchronous BeginGetEvent method.
211
212     HRESULT Player_::Invoke(IMFAsyncResult *pResult)
213     {
214       MediaEventType meType = MEUnknown;  // Event type
215
216       IMFMediaEventPtr pEvent;
217
218       try {
219         // Get the event from the event queue.
220         THROW_IF_ERR(m_pSession->EndGetEvent(pResult, pEvent.GetAddressOf()));
221
222         // Get the event type. 
223         THROW_IF_ERR(pEvent->GetType(&meType));
224
225         if (meType == MESessionClosed)
226         {
227           // The session was closed. 
228           // The application is waiting on the m_hCloseEvent event handle. 
229           SetEvent(m_hCloseEvent);
230         }
231         else
232         {
233           // For all other events, get the next event in the queue.
234           THROW_IF_ERR(m_pSession->BeginGetEvent(this, NULL));
235         }
236
237         // Check the application state. 
238
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. 
242
243         // Otherwise, post a private window message to the application. 
244
245         //if (m_state != Closing)
246         //{
247         //  // Leave a reference count on the event.
248
249         //  //PostMessage(m_hwndEvent, WM_APP_PLAYER_EVENT, 
250         //  //  (WPARAM) pEvent.Detach(), (LPARAM)meType);
251         //}
252
253         HandleEvent((UINT_PTR)pEvent.Detach());
254
255         return S_OK;
256       } catch (win32_error_exception& e) 
257       {
258         return e.hresult();
259       }
260     }
261
262     void Player_::HandleEvent(UINT_PTR pEventPtr)
263     {
264       MediaEventType meType = MEUnknown;  
265
266       IMFMediaEventPtr pEvent;
267       //    pEvent.Attach((IMFMediaEvent*)pEventPtr);
268       pEvent.Attach(reinterpret_cast<IMFMediaEvent*>(pEventPtr));
269
270       if (!pEvent)
271       {
272         throw win32_error_exception(E_POINTER);
273       }
274
275       // Get the event type.
276       THROW_IF_ERR(pEvent->GetType(&meType));
277
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);
284
285       switch(meType)
286       {
287       case MESessionTopologyStatus:
288         OnTopologyStatus(pEvent);
289         break;
290
291       case MEEndOfPresentation:
292         OnPresentationEnded(pEvent);
293         break;
294
295       case MENewPresentation:
296         OnNewPresentation(pEvent);
297         break;
298
299       default:
300         OnSessionEvent(pEvent, meType);
301         break;
302       }
303     }
304
305     //  Release all resources held by this object.
306     void Player_::shutdown( ev::Close const& ev)
307     {
308       // Close the session
309       CloseSession();
310
311       // Shutdown the Media Foundation platform
312       MFShutdown();
313
314       if (m_hCloseEvent)
315       {
316         CloseHandle(m_hCloseEvent);
317         m_hCloseEvent = NULL;
318       }
319     }
320
321     /// Protected methods
322
323     void Player_::OnTopologyStatus(IMFMediaEventPtr pEvent)
324     {
325       UINT32 status; 
326
327       THROW_IF_ERR(pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status));
328       if (status == MF_TOPOSTATUS_READY)
329       {
330         safe_release(m_pVideoDisplay);
331
332         // Get the IMFVideoDisplayControl interface from EVR. This call is
333         // expected to fail if the media file does not have a video stream.
334
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());
338         //StartPlayback();
339       }
340     }
341
342
343     //  Handler for MEEndOfPresentation event.
344     void Player_::OnPresentationEnded(IMFMediaEventPtr pEvent)
345     {
346       // The session puts itself into the stopped state automatically.
347       // m_state = Stopped;
348       static_cast<this_type&>(*this).process_event(ev::Stop());
349     }
350
351     //  Handler for MENewPresentation event.
352     //
353     //  This event is sent if the media source has a new presentation, which 
354     //  requires a new topology. 
355
356     void Player_::OnNewPresentation(IMFMediaEventPtr pEvent)
357     {
358       IMFPresentationDescriptorPtr pPD;
359       IMFTopologyPtr pTopology;
360
361       // Get the presentation descriptor from the event.
362       THROW_IF_ERR(GetEventObject(pEvent.Get(), pPD.GetAddressOf()));
363
364       // Create a partial topology.
365       pTopology = CreatePlaybackTopology(m_pSource, pPD,  m_hwndVideo);
366
367       // Set the topology on the media session.
368       THROW_IF_ERR(m_pSession->SetTopology(0, pTopology.Get()));
369
370       // m_state = OpenPending;
371
372     }
373
374     //  Create a new instance of the media session.
375     void Player_::CreateSession()
376     {
377       // Close the old session, if any.
378       CloseSession();
379
380       // assert(m_state == Closed);
381
382       // Create the media session.
383       THROW_IF_ERR(MFCreateMediaSession(NULL, &m_pSession));
384
385       // Start pulling events from the media session
386       THROW_IF_ERR(m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL));
387       // m_state = Ready;
388     }
389
390     //  Close the media session. 
391     void Player_::CloseSession()
392     {
393       //  The IMFMediaSession::Close method is asynchronous, but the 
394       //  Player_::CloseSession method waits on the MESessionClosed event.
395       //  
396       //  MESessionClosed is guaranteed to be the last event that the 
397       //  media session fires.
398
399       m_pVideoDisplay.Reset();
400
401       // First close the media session.
402       if (m_pSession)
403       {
404         DWORD dwWaitResult = 0;
405
406         //m_state = Closing;
407
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)
412         {
413           assert(FALSE);
414         }
415         // Now there will be no more events from this session.
416       }
417
418       if (m_pSource)
419       {
420         m_pSource->Shutdown();
421       }
422       // Shut down the media session. (Synchronous operation, no events.)
423       if (m_pSession)
424       {
425         m_pSession->Shutdown();
426       }
427
428       m_pSource.Reset();
429       m_pSession.Reset();
430       //m_state = Closed;
431     }
432
433     //  Start playback from the current position. 
434     void Player_::StartPlayback()
435     {
436       assert(m_pSession != NULL);
437
438       prop_variant varStart;
439       //    PROPVARIANT varStart;
440       //    PropVariantInit(&varStart);
441
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.
447       //m_state = Started;
448     }
449
450     //  Start playback from paused or stopped.
451     void Player_::play( ev::Play const& ev)
452     {
453  /*     if (m_state != Paused && m_state != Stopped)
454       {
455         THROW_IF_ERR(MF_E_INVALIDREQUEST);
456       }*/
457       if (!m_pSession || !m_pSource)
458       {
459         THROW_IF_ERR(E_UNEXPECTED);
460       }
461       StartPlayback();
462       OnStart()(static_cast<this_type&>(*this));
463     }
464
465
466     //  Create a media source from a URL.
467     IMFMediaSourcePtr CreateMediaSource(const std::wstring& sURL)
468     {
469       MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
470
471       IMFSourceResolverPtr pSourceResolver;
472       Microsoft::WRL::ComPtr<IUnknown> pSource;
473       IMFMediaSourcePtr pSourceReturn;
474       // Create the source resolver.
475       THROW_IF_ERR(MFCreateSourceResolver(pSourceResolver.GetAddressOf()));
476
477       // Use the source resolver to create the media source.
478
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.
483
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.
490         ));
491
492       // Get the IMFMediaSource interface from the media source.
493       THROW_IF_ERR(pSource.As<IMFMediaSource>(&pSourceReturn));
494       return pSourceReturn;
495     }
496
497     //  Create an activation object for a renderer, based on the stream media type.
498
499     IMFActivatePtr CreateMediaSinkActivate(
500       IMFStreamDescriptor* pSourceSD,     // Pointer to the stream descriptor.
501       HWND hVideoWindow                 // Handle to the video clipping window.
502       )
503     {
504       IMFMediaTypeHandlerPtr pHandler;
505       IMFActivatePtr pActivate;
506
507       // Get the media type handler for the stream.
508       THROW_IF_ERR(pSourceSD->GetMediaTypeHandler(pHandler.GetAddressOf()));
509
510       // Get the major media type.
511       GUID guidMajorType;
512       THROW_IF_ERR(pHandler->GetMajorType(&guidMajorType));
513
514       // Create an IMFActivate object for the renderer, based on the media type.
515       if (MFMediaType_Audio == guidMajorType)
516       {
517         // Create the audio renderer.
518         THROW_IF_ERR(MFCreateAudioRendererActivate(pActivate.GetAddressOf()));
519       }
520       else if (MFMediaType_Video == guidMajorType)
521       {
522         // Create the video renderer.
523         THROW_IF_ERR(MFCreateVideoRendererActivate(hVideoWindow, pActivate.GetAddressOf()));
524       }
525       else
526       {
527         // Unknown stream type. 
528         THROW_IF_ERR(E_FAIL);
529         // Optionally, you could deselect this stream instead of failing.
530       }
531
532       // Return IMFActivate pointer to caller.
533       return pActivate;
534     }
535
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.
542     {
543       IMFTopologyNodePtr pNode;
544
545       // Create the node.
546       THROW_IF_ERR(MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, pNode.GetAddressOf()));
547
548       // Set the attributes.
549       THROW_IF_ERR(pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource.Get()));
550
551       THROW_IF_ERR(pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD.Get()));
552
553       THROW_IF_ERR(pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD.Get()));
554
555       // Add the node to the topology.
556       THROW_IF_ERR(pTopology->AddNode(pNode.Get()));
557
558       // Return the pointer to the caller.
559       return pNode;
560     }
561
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.
567       ) 
568     {
569       IMFTopologyNodePtr pNode;
570
571       // Create the node.
572       THROW_IF_ERR(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode));
573
574       // Set the object pointer.
575       THROW_IF_ERR(pNode->SetObject(pActivate.Get()));
576
577       // Set the stream sink ID attribute.
578       THROW_IF_ERR(pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId));
579
580       THROW_IF_ERR(pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE));
581
582       // Add the node to the topology.
583       THROW_IF_ERR(pTopology->AddNode(pNode.Get()));
584
585       return pNode;
586
587     }
588     //</SnippetPlayer.cpp>
589
590     //  Add a topology branch for one stream.
591     //
592     //  For each stream, this function does the following:
593     //
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.
597     //
598     //  The media session will add any decoders that are needed.
599
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.
606     {
607       IMFStreamDescriptorPtr pSD;
608       IMFActivatePtr         pSinkActivate;
609       IMFTopologyNodePtr     pSourceNode;
610       IMFTopologyNodePtr     pOutputNode;
611
612       BOOL fSelected = FALSE;
613
614       THROW_IF_ERR(pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD));
615
616       if (fSelected)
617       {
618         // Create the media sink activation object.
619         pSinkActivate = CreateMediaSinkActivate(pSD.Get(), hVideoWnd);
620
621         // Add a source node for this stream.
622         pSourceNode = AddSourceNode(pTopology, pSource, pPD, pSD);
623
624         // Create the output node for the renderer.
625         pOutputNode = AddOutputNode(pTopology, pSinkActivate, 0);
626
627         // Connect the source node to the output node.
628         THROW_IF_ERR(pSourceNode->ConnectOutput(0, pOutputNode.Get(), 0));
629       }
630       // else: If not selected, don't add the branch. 
631     }
632
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.
639       )        
640     {
641       IMFTopologyPtr pTopology;
642       DWORD cSourceStreams = 0;
643
644       // Create a new topology.
645       THROW_IF_ERR(MFCreateTopology(&pTopology));
646
647       // Get the number of streams in the media source.
648       THROW_IF_ERR(pPD->GetStreamDescriptorCount(&cSourceStreams));
649
650       // For each stream, create the topology nodes and add them to the topology.
651       for (DWORD i = 0; i < cSourceStreams; i++)
652       {
653         AddBranchToPartialTopology(pTopology, pSource, pPD, i, hVideoWnd);
654       }
655
656       // Return the IMFTopology pointer to the caller.
657       return pTopology;
658     }
659
660   void print_mft_(const GUID& guid,std::wfstream& out)
661   {
662     co_task_memory<IMFActivate*> activate;
663
664     UINT32 count = 0;
665     
666     HRESULT hr = MFTEnumEx(guid,MFT_ENUM_FLAG_ALL,NULL,NULL,&activate,&count);
667     
668     if(SUCCEEDED(hr) && count > 0)
669     {
670       for(int i = 0; i < count;++i)
671       {
672         UINT32 l = 0;
673         UINT32 l1 = 0;
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();
680       }
681       //CoTaskMemFree(activate);
682     }
683   }
684
685   void print_mft()
686   {
687     std::wfstream out(L"MFT.txt",std::ios_base::out | std::ios_base::trunc);
688
689     out << std::endl << "**" << L"MFT_CATEGORY_AUDIO_DECODER" << L"**" << std::endl << std::endl;
690
691     print_mft_(MFT_CATEGORY_AUDIO_DECODER,out);
692
693     out << std::endl << L"**" << L"MFT_CATEGORY_AUDIO_EFFECT" << L"**" << std::endl << std::endl;
694     print_mft_(MFT_CATEGORY_AUDIO_EFFECT,out);
695
696     out << std::endl << L"**" << L"MFT_CATEGORY_AUDIO_ENCODER" << L"**" << std::endl << std::endl;
697     print_mft_(MFT_CATEGORY_AUDIO_ENCODER,out);
698
699     out << std::endl << L"**" << L"MFT_CATEGORY_DEMULTIPLEXER" << L"**" << std::endl << std::endl;
700     print_mft_(MFT_CATEGORY_DEMULTIPLEXER,out);
701
702     out << std::endl << L"**" << L"MFT_CATEGORY_MULTIPLEXER" << L"**" << std::endl << std::endl;
703     print_mft_(MFT_CATEGORY_MULTIPLEXER,out);
704
705     out << std::endl << L"**" << L"MFT_CATEGORY_OTHER" << L"**" << std::endl << std::endl;
706     print_mft_(MFT_CATEGORY_OTHER,out);
707
708     out << std::endl << L"**" << L"MFT_CATEGORY_VIDEO_DECODER" << L"**" << std::endl << std::endl;
709     print_mft_(MFT_CATEGORY_VIDEO_DECODER,out);
710
711     out << std::endl << L"**" << L"MFT_CATEGORY_VIDEO_EFFECT" << L"**" << std::endl << std::endl;
712     print_mft_(MFT_CATEGORY_VIDEO_EFFECT,out);
713
714     out << std::endl << L"**" << L"MFT_CATEGORY_VIDEO_ENCODER" << L"**" << std::endl << std::endl;
715     print_mft_(MFT_CATEGORY_VIDEO_ENCODER,out);
716
717     out << std::endl << L"**" << L"MFT_CATEGORY_VIDEO_PROCESSOR" << L"**" << std::endl << std::endl;
718     print_mft_(MFT_CATEGORY_VIDEO_PROCESSOR,out);
719
720     out.close();
721   }
722 }
723
724 }