Directshow 學習入門 5

3、查找媒體類型

  每個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設置一個空的時間。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章