Media Foundation学习笔记(四)Media Foundation的架构 Platform API

Media Foundation Platform API

 

初始化

HRESULT hr = MFStartup(MF_VERSION);

注意:如果应用程序编译时使用的和系统上Media Foundation的dll不匹配的头文件,MFStartup会返回MF_E_BAD_STARTUP_VERSION错误!

 

MFShutdown();

 

异步方法

在Media Foundation中,大部分操作是使用的异步方式。总所周知,异步操作可以提升性能。一个异步操作在Media Foundation中是用Beginxxx和Endxxx开头字符定义的一对方法,这2个函数定义了流上的一个异步操作。

Beginxxx方法总是包含2个参数:一个IMFAsyncCallback接口对象和一个可选的状态对象。应用程序对流对象调用Beginxxx方法成功后,流对象执行完成会调用这个IMFAsyncCallback接口对象的IMFAsyncCallback::Invoke(IMFAsyncResult* pResult)方法通知应用程序,应用程序应该在IMFAsyncCallback接口对象的Invoke方法被调用后,在Invoke方法里面或者其他线程里面调用Endxxx方法。通常要将在Invoke方法中收到的IMFAsyncResult对象指针作为参数传给Endxxx方法

Beginxxx方法如果返回一个失败代码,则回调函数不会被流对象调用。

Endxxx方法返回异步操作是成功还是失败的结果。

IMFAsyncCallback::Invoke方法是被另外的线程调用的,应用程序要保证Invoke中的代码是线程安全的。Invoke方法中不要阻塞,不然可能会阻塞流的Pipeline。

 

CMyCallback展现了如何实现IMFAsyncCallback接口:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <shlwapi.h>  
  2.   
  3. class CAsyncCallback : public IMFAsyncCallback   
  4. {  
  5. public:  
  6.     CAsyncCallback () : m_cRef(1) { }  
  7.     virtual ~CAsyncCallback() { }  
  8.   
  9.     STDMETHODIMP QueryInterface(REFIID riid, void** ppv)  
  10.     {  
  11.         static const QITAB qit[] =   
  12.         {  
  13.             QITABENT(CAsyncCallback, IMFAsyncCallback),  
  14.             { 0 }  
  15.         };  
  16.         return QISearch(this, qit, riid, ppv);  
  17.     }  
  18.   
  19.     STDMETHODIMP_(ULONG) AddRef()  
  20.     {  
  21.         return InterlockedIncrement(&m_cRef);  
  22.     }  
  23.     STDMETHODIMP_(ULONG) Release()  
  24.     {  
  25.         long cRef = InterlockedDecrement(&m_cRef);  
  26.         if (cRef == 0)  
  27.         {  
  28.             delete this;  
  29.         }  
  30.         return cRef;  
  31.     }  
  32.   
  33.     STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)  
  34.     {  
  35.         // Implementation of this method is optional.  
  36.         return E_NOTIMPL;  
  37.     }  
  38.   
  39.     STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult) = 0;  
  40.     // TODO: Implement this method.   
  41.   
  42.     // Inside Invoke, IMFAsyncResult::GetStatus to get the status.  
  43.     // Then call the EndX method to complete the operation.   
  44.   
  45. private:  
  46.     long    m_cRef;  
  47. };  
  48.   
  49. class CMyCallback : public CAsyncCallback  
  50. {  
  51.     HANDLE m_hEvent;  
  52.     IMFByteStream *m_pStream;  
  53.   
  54.     HRESULT m_hrStatus;  
  55.     ULONG   m_cbRead;  
  56.   
  57. public:  
  58.   
  59.     CMyCallback(IMFByteStream *pStream, HRESULT *phr)   
  60.         : m_pStream(pStream), m_hrStatus(E_PENDING), m_cbRead(0)  
  61.     {  
  62.         *phr = S_OK;  
  63.   
  64.         m_pStream->AddRef();  
  65.   
  66.         m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);  
  67.   
  68.         if (m_hEvent == NULL)  
  69.         {  
  70.             *phr = HRESULT_FROM_WIN32(GetLastError());  
  71.         }  
  72.     }  
  73.     ~CMyCallback()  
  74.     {  
  75.         m_pStream->Release();  
  76.         CloseHandle(m_hEvent);  
  77.     }  
  78.   
  79.     HRESULT WaitForCompletion(DWORD msec)  
  80.     {  
  81.         DWORD result = WaitForSingleObject(m_hEvent, msec);  
  82.   
  83.         switch (result)  
  84.         {  
  85.         case WAIT_TIMEOUT:  
  86.             return E_PENDING;  
  87.   
  88.         case WAIT_ABANDONED:  
  89.         case WAIT_OBJECT_0:  
  90.             return m_hrStatus;  
  91.   
  92.         default:  
  93.             return HRESULT_FROM_WIN32(GetLastError());  
  94.         }  
  95.     }  
  96.   
  97.     ULONG   GetBytesRead() const { return m_cbRead; }  
  98.   
  99.     STDMETHODIMP Invoke(IMFAsyncResult* pResult)  
  100.     {  
  101.         m_hrStatus = m_pStream->EndRead(pResult, &m_cbRead);  
  102.   
  103.         SetEvent(m_hEvent);  
  104.   
  105.         return S_OK;  
  106.     }  
  107. };  


Work Queues

Work Queues中的Work Items都是实现了IMFAsyncCallback接口的对象,Work Queue拥有一个线程不断从Work Queue取出Work Item并分发它们。

Media Foundation创建了几个标准的Work Queues,称为“Platform work queues”。应用程序可以自己创建Work Queues,称为“Private work queues”。Work Queue用标识符来识别,例如,MFASYNC_CALLBACK_QUEUE_TIMER。

 

创建Work Queue:MFAllocateWorkQueue。

 

添加Work Item:MFPutWorkItem和MFPutItemEx。

 

添加定时执行的 Work Item:MFC ScheduledWorkItem和MFC ScheduledWorkItemEx。定时执行的Work Item总是使用标识符是MFASYNC_CALLBACK_QUEUE_TIMER的“Platform work queues”。

取消定时执行的Work Item:MFCancelWorkItem。

 

添加周期执行的回调:MFAddPeriodicCallback。定时周期是固定的不能被改变,大约是10毫秒,可以通过MFGetTimerPeriodicity获取精确的间隔。周期执行的回调总是使用标识符是MFASYNC_CALLBACK_QUEUE_TIMER的“Platform work queues”。注意,不像其他的Work Item,周期执行的回调不使用IMFAsyncCallbak接口,而是使用函数指针MFPERIODICCALLBACK

 

取消周期执行的回调:MFRemovePeriodicCallback。

 

如果有超过一个的线程或组件共享Work Queue,需要使用MFLockWorkQueue和MFUnlockWorkQueue来为Work Queue加锁(防止Work Queue被platform释放,该加锁实际是addref,并不会导致第二次调用MFLockQorkQueue阻塞)和解锁。注意:MFAllocateWorkQueue中自动调用了一次MFLockQWorkQueue,因此,MFAllocateWorkQueue后必须要出现一次MFUnlockWorkQueue。

 

使用Work Queue的例子:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. DWORD idWorkQueue = 0;  
  2. HRESULT hr = S_OK;  
  3.   
  4. // Create a new work queue.  
  5. hr = MFAllocateWorkQueue(&idWorkQueue);  
  6.   
  7. // Put an item on the queue.  
  8. if (SUCCEEDED(hr))  
  9. {  
  10.     hr = MFPutWorkItem(idWorkQueue, pCallback, NULL);  
  11. }  
  12.   
  13. // Wait for the callback to be invoked.  
  14. if (SUCCEEDED(hr))  
  15. {  
  16.     WaitForSingleObject(hEvent, INFINITE);  
  17. }  
  18.   
  19. // Release the work queue.  
  20. if (SUCCEEDED(hr))  
  21. {  
  22.     hr = MFUnlockWorkQueue(idWorkQueue);  
  23. }  

MFShutdown会自动关闭Work Queues的线程,所以应用程序需要在调用MFShutDown之前确保Work Queues占用的资源都被释放,否则会有内存泄露。如果在调用MFShutDown之前调用了MFLockPlatform,则MFShutDown不会马上关闭Work Queues的资源,而会等等几百毫秒,再关闭。调用MFLockPlatform,然后释放资源,释放资源后,需要调用MFUnlockPlatform。IMFAsyncResult接口的默认实现是,当IMFAsyncResult接口对象被创建的时候会自动锁住Media Foundation Platform,当IMFAsyncResult接口对象被释放的时候自动解锁platform,如果自己实现IMFAsyncResult接口,则需要手动调用MFLockPlatform和MFUnlockPlatform来锁住platfrom。

 

MFC ScheduledWorkItem和MFC ScheduledWorkItemEx的参数是负值,单位是毫秒,例如:设定一个5秒后执行的回调,参数是-5000。

 

Media Event Generators

对象使用events来通知异步操作的完成,或将对象状态相关的改变通知应用程序。

 

如果对象要发送时间,就需要暴露IMFMediaEventGenerator接口。应用程序使用以下2种方式查询event:

1)       IMFMediaEventGenerator::GetEvent,这个方法是同步方法。如果对象的event队列中有event,则立即返回,如果没有,则阻塞或者返回失败,是否阻塞还是返回失败依传入的参数而定;

2)       IMFMediaEventGenerator::BeginGetEvent,这个方法是异步方法。像前面讲的,提供一个IMFAsyncCallback接口,Invoke被调用后,调用EndGetEvent方法。

 

Event对象提供IMFMediaEvent接口。IMFMediaEvent从IMFAttributes继承。

 

Event对象队列:MFCreateEventQueue。

Event对象队列接口:IMFMediaEventQueue。

添加Event对象:IMFMediaEventQueue::QueueEvent、IMFMediaEventQueue::QueueEventParamVar、IMFMediaEventQueue::QueueEventParamUnk。

 

服务接口

在Media Foundataion中,有些接口不能通过QueryInterface获取,而只能通过IMFGetService::GetService获取。IMFGetService::GetService接收服务标识符的GUID,返回服务对象的接口指针。不同于QueryInterface,IMFGetService::GetService获取的对象是另一个对象而不是被调用对象。例如,在DShow中使用Enhanced Video Render就只能使用IMFGetService::GetService获取IMFVideoDisplayControl指针来设置播放窗口。

 

Activation对象

Activation对象用于创建其他对象,有点类似于类工厂。Activation对象暴露IMFActivate接口。

Activation对象让应用可以延迟创建目标对象。应用创建对象前可以通过检查Activation对象的属性来获取目标对象的信息。Activation对象也可以被序列化,这就使得可以在其他进程创建目标对象。

创建和销毁目标对象:IMFActivate::ActivateObject和IMFActivate::ShutdownObject。

Activation对象能拥有属性,IMFActivate接口从IMFAttributes接口继承。一些activation对象属性来配置要创建的对象。

 

PresentationClock

Presentation clock是为Presentation产生时钟时间的对象。Presentation clock中报告的时间称为“Presentation时间”。Presentation中所有的流都被同步到Presentation时间。

接口:IMFPresentationClock、IMFRateControl、IMFTimer和IMFShutdown。

 

Media Sink使用Presentation时间来安排什么时候Render样本。Media Sources和transforms不使用Presentation Clock,因为它们不安排什么时候Deliver样本,而是在Pipeline需要新样本的时候产生样本。

Media Session处理了“创建Presentation Clock”、“选择时间源”等等所有的细节,因此应用程序除了在回放过程中“获取当前的Presentation时间”外,不需要调用Presentation Clock的其他方法。

 

获取当前的Presentation时间使用IMFPresentationClock::GetTime。得到的时间是以100-nano秒(1万个为1毫秒)为单位,因此1秒就是10^7。

 

开始时钟:IMFPresentationClock::Start;

暂停时钟:IMFPresentationClock::Pause;

停止时钟:IMFPresentationClock::Stop;

时钟的跳动步进是1.0(100-nano秒)。要改变步进,获取IMFRateControl接口,使用IMFRateControl::SetRate。

对象可以获取Presentation Clock的状态改变(包括步进的改变)的通知。获取方式:实现IMFClockStateSink接口,调用IMFPresentationClock::AddClockStateSink,取消获取调用IMFPresentationClock::RemoveClockStateSink。

 

创建和销毁Presentation Clock:MFCreatePresentationClock和IMFShutdown::Shutdown。

 

Presentation Clock实际上并不实现一个时钟,它是从其他称为“Presentation时间源”的对象获取时钟时间。时间源对象是一个实现了IMFPresentationTimeSource接口和IMFClockStateSink接口的对象。

 

Presentation Clock对象被创建的时候,并不拥有一个时间源对象,需要调用IMFPresentationClock::SetTimeSources为它设置一个时间源对象。通常,由于音频Renderke可以使用声卡的频率最为时钟,因此音频Render可以作为时间源对象;另外,也可以通过MFCreateSystemTimeSource创建一个基于系统时间的时间源对象,系统时间源可以在没有Media Sink提供时间源的情况下使用。

 

有2中情况Media Sink不遵循Presentation Clock:

1)rateless Media Sink:如写文件。对该类Media Sink调用IMFMediaSink::GetCharacteristic方法返回MEDIASINK_RATELESS。

2)一些Media Sink不能Match Rate的时候。这时调用IMFMediaSink::GetCharacteristic返回MEDIASINK_CANNOT_MATCH_CLOCK。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值