跳过第二章对工具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!
一.激活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,很详细,只是对流程做了记录。