每個pin都支持一個IPin::EnumMediaTypes方法,可以來枚舉pin支持的媒體類型。它返回一個IEnumMediaTypes接口,這個接口的方法IEnumMediaTypes::Next返回一個指向AM_MEDIA_TYPE類型的指針。可以參考上面的代碼來遍歷pin所支持的媒體類型。
Seeking Filter graph
主要講述瞭如何在一個媒體數據流中定位,任意指定開始播放的位置。
1、檢查是否支持seek
Directshow通過IMediaSeeking接口支持seeking。Filter graph管理器支持這個接口,但是實際seeking的功能是有graph中的filter來實現的。
有一些數據是不能seek的,例如,你不可能seek從照相機中採集的活動的視頻流。如果一個數據流可以被seek,但是,seek的類型還分以下幾種類型,可以給你的數據流選擇一種
1) 定位到數據流中的一個絕對位置
2) 返回數據流的持續時間
3) 返回數據流中的當前播放位置
4) 回放。
IMediaSeeking接口定義了一套標誌AM_SEEKING_SEEKING_CAPABILITIES,用來描述可能支持的seek功能。
typedef enum AM_SEEKING_SeekingCapabilities { AM_SEEKING_CanSeekAbsolute = 0x1, AM_SEEKING_CanSeekForwards = 0x2, AM_SEEKING_CanSeekBackwards = 0x4, AM_SEEKING_CanGetCurrentPos = 0x8, AM_SEEKING_CanGetStopPos = 0x10, AM_SEEKING_CanGetDuration = 0x20, AM_SEEKING_CanPlayBackwards = 0x40, AM_SEEKING_CanDoSegments = 0x80, AM_SEEKING_Source = 0x100 } AM_SEEKING_SEEKING_CAPABILITIES; |
可以通過IMediaSeeking::GetCapabilities查看數據流支持的seek能力都有哪些。應用程序可以採取 &測試每一項。例如,下面的代碼檢查了graph是否可以seek 一個任意的位置
DWORD dwCap = 0; HRESULT hr = pSeek->GetCapabilities(&dwCap); if (AM_SEEKING_CanSeekAbsolute & dwCap) { // Graph can seek to absolute positions. } |
2、Setting and Retrieving the Position
Filter graph包含兩個位置,當前位置和停止位置,定義如下:
1) 當前位置,當一個graph正處於運行的時候,當前位置就是當前的回放位置,相對於開始的位置而言。如果graph處於停止或者暫停狀態的時候,當前位置就是數據流下次開始播放的位置點。
2) 停止位置,停止位置就是數據流將要停止的位置,當一個graph到達一個停止位置時,將沒有數據流,filter graph管理器將會發送一個EC_COMPLETE事件。
可以通過IMediaSeeking::GetPositions方法可以獲取這些位置值。返回值都是相對於原始的開始位置。
通過IMediaSeeking::SetPositions方法可以seek一個新的位置,見下面:
#define ONE_SECOND 10000000 REFERENCE_TIME rtNow = 2 * ONE_SECOND, rtStop = 5 * ONE_SECOND; hr = pSeek->SetPositions( &rtNow, AM_SEEKING_AbsolutePositioning, &rtStop, AM_SEEKING_AbsolutePositioning ); |
注:1秒是10,000,000參考時間單位。爲了方便,這個例子將這個值定義爲ONE_SECOND,如果你使用的dshow的基類,常量CUITS的值和這個值相等。
RtNow參數指定新的當前位置,第二個參數用來標示如何來定位rtNow參數。在這個例子中,AM_SEEKING_AbsolutePositioning 標誌表示rtNow指定的位置是一個絕對的位置。RtStop參數指定了停止時間,最後一個參數也指定了絕對位置。
如果想指定一個相對的位置,可以指定一個AM_SEEKING_RelativePositioning參數,爲了設置這個位置不能改變,可以指定一個AM_SEEKING_NoPositioning參數。此時,參考時間應該設置爲NULL。下面的例子將位置向前seek 10秒,然後停止位置不變。
REFERENCE_TIME rtNow = 10 * ONE_SECOND; hr = pSeek->SetPositions( &rtNow, AM_SEEKING_RelativePositioning, NULL, AM_SEEKING_NoPositioning ); |
3、Setting the Playback Rate
調用IMediaSeeking::SetRate方法可以改變回放的速率。通過將新的速率設置成原來速率的倍數就可以設置新的速率,例如,pSeek->SetRate(2.0),將新的速率設置爲原來速率的兩倍。比率大於1說明回放的速度比原來的大,如果介於0和1之間,就比正常的速度慢。
如果我們不考慮回放速率,當前位置和停止位置相對於開始位置都是不變的。舉個例子,如果我們有一個可以播放20秒的文件,將當前時間設置爲10秒就會將播放位置設置到中間,如果播放的速率提高要原來的2倍,如果停止時間是20秒,你將播放位置設置到原來的10秒處,結果現在只能播放5秒了,因爲速度提高了兩倍。
4、Time Formats For Seek Commands
IMediaSeeking接口中的許多函數的參數都要求指定一個位置值,比如當前位置,或者停止位置,缺省的情況下這些參數是以of 100 nanoseconds爲時間單位的,稱爲參考時間,任何支持seek的filter必須支持按參考時間來進行定位。一些filter也支持採取其他時間單位進行定位。例如,根據指定的楨的數量,或在數據流偏移的字節數進行定位。
這種用來定位的時間單位稱爲時間格式,採用一個GUID來標示。Directshow定義了一系列的時間格式,詳細地可以參考SDK。第三方也可以定義自己的時間格式。
爲了確定graph中的當前的filter是否支持特定的時間格式,可以調用
IMediaSeeking::IsFormatSupported方法,如果filter支持該時間格式,該函數返回ok否則返回false或者一個錯誤碼。如果filter支持某種指定的時間格式,可以調用IMediaSeeking::SetTimeFormat方法切換到其他的時間格式。如果SetTimeFormat方法成功,下面的seek命令就要使用新的時間格式。
下面的代碼檢查graph是否支持用楨的數量進行定位,如果支持,定位到第20楨。
hr = pSeek->IsFormatSupported(&TIME_FORMAT_FRAME); if (hr == S_OK) { hr = pSeek->SetTimeFormat(&TIME_FORMAT_FRAME); if (SUCCEEDED(hr)) { // Seek to frame number 20. LONGLONG rtNow = 20; hr = pSeek->SetPositions(&rtNow, AM_SEEKING_AbsolutePositioning,0, AM_SEEKING_NoPositioning); } } |
6、如何設置Graph時鐘(Setting Graph Clock)
當你構建了一個graph後,graph管理器會自動地給你的graph選擇一個參考時鐘的。Graph中的所有filter都同步於時鍾。特別的,Renderer filter還要根據參考時鐘的時間來決定每一個sample的Presentation 時間。
通常的情況下,應用程序是沒有必要重新設置graph管理器選擇好的參考時鐘的。但是,如果你想修改參考時鐘,你可以通過graph管理器提供的IMediaFilter::SetSyncSource方法來重新設置參考時鐘。這個方法的參數是一個時鐘的IReferenceClock接口指針。可以在graph停止的時候調用這個函數,下面的例子演示瞭如何指定一個時鐘
IGraphBuilder *pGraph = 0; IReferenceClock *pClock = 0; CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph); // Build the graph. pGraph->RenderFile(L"C:\Example.avi", 0); // Create your clock. hr = CreateMyPrivateClock(&pClock); if (SUCCEEDED(hr)) { // Set the graph clock. IMediaFilter *pMediaFilter = 0; pGraph->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter); pMediaFilter->SetSyncSource(pClock); pClock->Release(); pMediaFilter->Release(); } |
這段代碼假定CreateMyPrivateClock 是應用程序定義的一個函數,用來創建一個時鐘,然後返回一個IReferenceClock接口。
你也可以在graph沒有設置時鐘的情況下運行graph。當SetSyncSource 函數的參數爲NULL的時候就給graph設置了一個空的參考時鐘。如果graph沒有時鐘,graph將運行的快許多。因爲renderer 不用再按照sample的presentation 時間了,只要sample到達了renderer filter,就可以立即被提交。所以,當你想處理數據儘可能快,而不是還要考慮預覽的實際時間,你就可以給graph設置一個空的時間。