跳過第二章對工具TopoEdit的介紹,直接進行媒體播放的學習。
媒體播放這章,介紹了個檔案播放的例子,想起當初學習dshow的時候,一開始也是個檔案播放例子,不過那個例子比較簡單,沒多少代碼,nnd,而這個demo徹底瞎了,就一個簡單的檔案播放就這麼多代碼,哎。
這章的第一要義是說MF廣泛使用COM技術但又不是真正的COM接口,也就是MF混合了COM和正常的對象,使用COM的話很明顯得初始化COM。
以下按照播放一個檔案的流程,介紹使用到的MF相關概念:
1. 初始化COM,初始化MF。(特别說明:MF的API隻有unicode版本沒有ASCII版本)
2. 引入一個重要的概念Media Session,相當于dshow的graph builder,Media Session主要用來控制媒體管線。就這個檔案播放的例子來說Media Session在内部從source采樣(取資料),然後給MFTs(transform component,解碼元件),最後再渲染;在外部則是控制播放暫停并且傳遞MF的内部事件等一系列功能,跟dshow的graph非常相似啊。
a)建立Media Session:MFCreateMediaSession(NULL, &m_pSession);第一個參數預設,第二個參數即新建立的指向media session的指針
b)設定media session的異步事件回調:m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL); 第一個參數為指定的回調對象,需要繼承至IMFAsyncCallback;異步事件會回調給IMFAsyncCallback接口的Invoke函數,在IMFAsyncCallback中Invoke函數為純虛函數,需要繼承者實作。
3.引入概念topology,網絡釋義拓撲結構,在該demo中是建立檔案播放的拓撲結構,所謂拓撲結構就是解析出需要的component,以便建立最終的媒體管線。以下分析topology的建構過程:
a)建立MF source:MF有個自帶的元件名為source resolve,它根據檔案URL(也可以是流媒體)解析出相應的容器和資料格式,并建立對應的source component。
一. 建立source resolve:MFCreateSourceResolver(&pSourceResolver);
二. 建立MF source:
// Use the synchronous source resolver to create the media source.
hr = pSourceResolver->CreateObjectFromURL(
sURL, // URL of the source.
MF_RESOLUTION_MEDIASOURCE |
MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE,
// indicate that we want a source object, and
// pass in optional source search parameters
NULL, // Optional property store for extra parameters
&objectType, // Receives the created object type.
&pSource // Receives a pointer to the media source.
);
英文注釋很詳細,這裡再說明下第二個參數,它是用flag标記來訓示如何建立并查找比對的MF source,MF_RESOLUTION_MEDIASOURCE:建立一個media source;MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE:該标記訓示媒體源的比對除了根據檔案擴充名和MIME type外,如果失敗,還會去比對其它已注冊的媒體源。這樣media source有了。
(還需說明的是上述方法是以同步方式來建構MF source,還有異步方式,異步方式主要是針對網絡流媒體或是其它需要一定時間才能通路到的資料源)
b)建構topology:首先說明的是該例子的topology是partial,部分的,它隻是建構source和sink,沒有加入transform。以下簡化該例子建構topology的步驟:
一. 得到MF source包含的流描述符,描述符裡包含有檔案(source)的媒體流資訊,例如流數目,以及相關屬性例如是音頻還是視訊,等等屬性
二.分别針對每股流建立source node,節點,相當于dshow的pin腳,作為component的代表,然後再分别針對相應的流類型(音頻或者視訊)建立sink node,這裡的sink用來render(渲染)
三. 連接配接source node和sink node,這裡就有個問題,那就是說source出來的資料sink不一定能直接用,例如編碼的資料是不能用來直接渲染的,而是應該在這之間加入MFTs,是以說這個topology是partial,not all ,Oh yeah! 截個例子中的代碼看看,注釋很銷魂:
// Connect the source node to the sink node. The resolver will find the
// intermediate nodes needed to convert media types.
hr = pSourceNode->ConnectOutput(0, pOutputNode, 0);
以上還需說明的是node的連接配接不代表真實對象(元件)的連接配接,node隻是表示component有這樣的屬性,實際元件的連接配接(真實pipeline的形成)還需要靠media session來完成,just hint!
c)解析上面的partial topology使其形成真正的media pipeline:這個工作由media seesion來完成 ,上面的描述其實忽略了一個步驟,就是把topology綁定到media session裡,就是調用一個函數:SetTopology,該函數的說明可以參考msdn。解析這個部分topology其實就是激活topology裡面的元件,并插入必要的用來轉換媒體類型的元件,哎,不知道有沒有說清楚,分兩步把:
一.激活topology裡的node所描述的元件,b裡建立了sink node但還沒有執行個體化對應的audio render/video render,為了節省資源隻有在真正需要的時候(just here)才執行個體化使其可用;(書裡這麼描述,實際隻有看源碼才知道,)
二.插入适當的MFTs,使source出來的媒體類型能轉化為sink(render)可接受的媒體類型,也就是用來做媒體資料類型的轉換,完成整個pipeline的協商,使其能真正地去play。這是media session為我們做的事情,可以說是把partial topology變為真正的media pipeline。
ok,that‘s all. 該文沒怎麼描述API,可以參考msdn,很詳細,隻是對流程做了記錄。
----部分内容引用《Developing Microsoft® Media Foundation Applications》一書