(轉)DirectShow實踐經驗雜談

1.當向Filter Graph中加入同一個Filter的多個實例時,使用Intelligent connect,優先使用最晚加入Filter Graph中的那個Filter實例。

2.使用IGraphConfig接口可以將Filter加入Cache,以在Intelligent connect時,提高該Filter的連接優先級。如果要加入Cache的Filter已在Graph中,確信它的所有Pin處於斷開狀態,而且調用IGraphConfig::AddFilterToCache之後,Graph中的Filter實例會自動Remove掉;當Intelligent connect之後,使用了Cache中的某個Filter實例,則這個Filter實例會被自動加入到Graph,而Cache中這個Filter實例也會被Remove掉。

3.在調試Filter時,每次進入CheckMediaType的Media type,我們在VC中只能看到一串UUID數字,很麻煩!下面的代碼能將Media type的描述信息Dump到VC的Output window:
void DisplayMediaType(TCHAR *pDescription,const CMediaType *pmt)
{
// Dump the GUID types and a short description
DbgLog((LOG_TRACE,0,TEXT("")));
DbgLog((LOG_TRACE,0,TEXT("%s"),pDescription));
DbgLog((LOG_TRACE,0,TEXT("")));
DbgLog((LOG_TRACE,0,TEXT("Media Type Description")));
DbgLog((LOG_TRACE,0,TEXT("Major type: %s"),GuidNames[*pmt->Type()]));
DbgLog((LOG_TRACE,0,TEXT("Subtype: %s"),GuidNames[*pmt->Subtype()]));
DbgLog((LOG_TRACE,0,TEXT("Subtype description: %s"),GetSubtypeName(pmt->Subtype())));
DbgLog((LOG_TRACE,0,TEXT("Format size: %d"),pmt->cbFormat));
// Dump the generic media types
DbgLog((LOG_TRACE,0,TEXT("Fixed size sample %d"),pmt->IsFixedSize()));
DbgLog((LOG_TRACE,0,TEXT("Temporal compression %d"),pmt->IsTemporalCompressed()));
DbgLog((LOG_TRACE,0,TEXT("Sample size %d"),pmt->GetSampleSize()));
}

4.調試Filter時,想要知道程序是否進入某個函數,可以使用如下的宏定義:
#define DbgFunc(a) DbgLog(( LOG_TRACE /
, 0 /
, TEXT("CFltTracer(Instance %d)::%s") /
, mThisInstance /
, TEXT(a) /
));
使用方法爲:
CFltTracer::~CFltTracer()
{
// Other cleaning work...
DbgFunc("~CFltTracer");
}

5.如下的代碼,可以將DirectShow的錯誤碼以文本的方式顯示出來:
void ShowError(HRESULT hr)
{
if (FAILED(hr))
{
TCHAR szErr[MAX_ERROR_TEXT_LEN];
DWORD res = AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
if (res == 0)
{
wsprintf(szErr, "Unknown Error: 0x%2x", hr);
}
MessageBox(0, szErr, TEXT("Error!"), MB_OK | MB_ICONERROR);
}
}

6.0進行DV camcorder編程時要注意,當Filter Graph在運行,而將Camcorder置於Paused狀態,"Microsoft DV Camera and VCR"仍然會不停地將Paused時刻的那一幀不斷地發送出來(因爲DirectShow主要是爲Playback設計的,所以這一點對於壓縮、合成、寫文件的應用非常頭疼),送出的Sample時間戳線性遞增;而將Camcorder置於Stopped狀態,"Microsoft DV Camera and VCR"也就不再送出數據。而Filter Graph上的Pause、Stop操作並不會影響到Camcorder本身的狀態。

6.1 Camcorder機器Paused狀態持續大約三分鐘(測試機爲JVC)後,機器的Preview畫面會變成藍屏。此時,即使Filter Graph在運行狀態,"Microsoft DV Camera and VCR"也停止輸出數據。

7.DirectShow加入Device Filter一般都要靠枚舉。Device的名字一般是在枚舉的時候,通過IPropertyBag::Read(L"FriendlyName", &varName, 0)獲得。對於DV camcorder,這種方式得到的名字爲"Microsoft DV Camera and VCR";而另一種方法(必須在Windows Me或XP下),通過讀取"Description"屬性,可以更詳細地得到Camcorder的生產廠商名字。參考代碼如下:
VARIANT varName;
VariantInit(&varName);
hr = pPropBag->Read(L"Description", &varName, 0);
if (FAILED(hr))
{
hr = pPropBag->Read(L"FriendlyName", &varName, 0);
}

8.Playback快進(慢進)的支持,可以通過IMediaSeeking::SetRate(或IMediaPosition::put_Rate)來實現。參數爲1.0表示正常速度,2.0爲雙倍速度,0.5爲半速;理論上參數爲負數時能夠實現倒放,但絕大部分Filter不支持倒放。對於Rate改變的響應,主要工作在負責打時間戳的Filter上,有可能是Parser Filter(如AVI Splitter),也有可能是Push模式下的Source Filter;這些Filter需要根據新的Rate重新打好時間戳。一般Decoder Filter不用對Rate改變做出響應,它們收到NewSegment後只要往下傳遞就行了,但對於一些可能需要改變時間戳的Decoder,則也需要考慮這個Rate的改變。Video Renderer一般也不需要關心Rate的改變,因爲進來的Sample已經是按照新的Rate打好時間戳了;Audio Renderer需要對Rate進行更新,因爲一般Audio Decoder不會因爲Rate的改變而作相應的轉換。還有一點,SetRate的調用之前,Filter Graph Manager會先將Filter Graph停止;調用之後,新的Sample時間戳從0開始。Playback Rate的改變會導致Audio很難聽,所以此時最好將Audio靜音處理。

9.Playback的單幀控制,可以通過Filter Graph Manager上獲得的IVideoFrameStep接口來實現。Filter Graph Manager也主要是協同Video Renderer或者Overlay Mixer來實現單幀播放的。IVideoFrameStep接口方法不僅支持單幀跳進,也支持多幀的跳進。在使用接口方法時,最好先調用IVideoFrameStep::CanStep判斷是否支持你指定的跳幀操作。調用IVideoFrameStep::Step實現跳進,其中第二個參數爲實際實現跳幀功能的Filter,爲NULL時Filter Graph Manager默認使用Video Renderer。當Step函數調用成功,Filter Graph Manager會嚮應用程序發送一個EC_STEP_COMPLETE事件,Filter Graph也自動轉入Paused狀態。

10.Mpeg1文件的播放可以直接使用微軟的MPEG-1 Stream Splitter、MPEG Video Decoder和MPEG Audio Decoder。Video Decoder可以通過IMpegVideoDecoder接口(在mpgcodec.h中定義)進行參數修改,比如可以通過set_GreyScaleOutput設置輸出圖像是彩色的還是黑白的等;Audio Decoder可以通過IMpegAudioDecoder接口(在mpegtype.h中定義)進行參數設置,比如可以控制解碼輸出mono或是stereo,左右聲道切換(注意:在解碼輸出mono的時候,不支持動態切換左右聲道)等。

 

11.AVI Splitter, Mpeg1 Stream splitter, Mpeg2 splitter只能工作在Pull模式;Mpeg2 Demultiplexer在非Window XP下工作在Push模式,而在Window XP下也能工作在Pull模式。所以,網絡客戶端接收壓縮數據要進行解碼時,一般將接收器Source Filter寫成Pull模式(因爲現成的Parser Filter大都只能工作在Pull模式)。如果要將接收器寫成Push模式,則這個Source Filter送出來的數據應該是已經做過Parse處理的(即Video和Audio已分離)。

12.ACM(Audio Compression Manager)和VCM(Video Compression Manager)在DirectShow中都是通過包裝Filter應用的。ACM Wrapper Filter,作爲解壓用時(主要進行Audio格式的轉換,輸出PCM數據),註冊在"DirectShow Filters"目錄下,Merit值爲MERIT_NORMAL;作爲壓縮用時,各個Audio壓縮器註冊在"Audio Compressors"目錄(CLSID_AudioCompressorCategory)下,Merit值爲MERIT_DO_NOT_USE。注意:這裏的Audio Compressor不能通過CoCreateInstance創建,而只能通過系統枚舉。VCM包裝Filter作爲解壓用時,即爲AVI Decompressor Filter,一般實現Video從YUV到RGB格式的轉換(注意:MPEG數據的壓縮/解碼都不是通過VCM包裝Filter實現的);作爲壓縮用時,各壓縮器註冊在"Video Compressors"目錄(CLSID_VideoCompressorCategory)下,Merit值爲MERIT_DO_NOT_USE。注意:這裏的Video Compressor也不能直接通過CoCreateInstance創建。

13.微軟提供了兩個Tee Filter:Smart Tee和Infinite Pin Tee Filter。前者有兩個Output pin,且Preview pin輸出的Sample已經去掉時間戳;後者,可以動態產生無數個Output pin,而且各個Output pin輸出的Sample是完全一樣的。

14.在Filtr Graph運行狀態下,可以動態加入新的Filter,但不能刪掉Filter,也不能斷開Filter之間的Pin連接。Filter從Filter Graph中刪除必須在Stopped狀態,刪除前將這個Filter的Pin斷開不是必要的!(連接着的Pin可以通過IFilterGraph:Disconnect斷開,並且連接兩頭的Pin都要調用一次!)

15.AVI Mux問題。四種工作模式(通過Filter上的IConfigInterleaving::put_Mode設置):INTERLEAVE_NONE,對輸入的數據不緩存,各幀數據按照它們到達Mux的順序直接寫入文件中,速度最快;INTERLEAVE_FULL,對Video和Audio的交叉打包精確控制,每次合成都要阻塞等待等量的Video、Audio數據,適合文件源的視頻編輯等應用;INTERLEAVE_CAPTURE,處於前兩種模式之間,儘量不使用數據緩存,在一個Pin上數據到達後等待另一個Pin數據到達時,可能會丟幀,適合Live Source Capture應用,注意此時Audio的Capture buffer應該小於0.5秒;INTERLEAVE_NONE_BUFFERED,僅在Win XP下用,類似於INTERLEAVE_NONE,只是這種模式生成的文件較小。還有另外一個接口方法IConfigInterleaving::put_Interleaving,設置打包頻率和Audio的預留(Preroll),推薦打包頻率爲1秒鐘一次,Audio預留750ms。

16.AVI 1.0文件大小有上限1GB,AVI 2.0文件沒有這個限制。AVI Mux默認總是生成2.0的文件(1.0的文件主要是老的VFW生成的),也可以通過AVI Mux上的IConfigAviMux::SetOutputCompatibilityIndex來設置生成1.0的文件,以保持向後兼容。另一個接口方法IConfigAviMux::SetMasterStream,用以同步多個輸入流,特別是在不同源的video、Audio Capture時,兩個源的Capture rate可能有細微的差別。設置了Master stream之後,Mux會修改AVI文件的Playback rates(AVIStreamHeader結構的dwScale和dwRate兩個成員變量)。推薦將Audio stream設爲Master。

17.在同一個Filter Graph中,既有Live Source(比如Camcorder,capture devices,mpeg2 demux等,他們自帶Reference Clock),又有Default DirectSound Device(聲卡帶有時鐘),默認情況下,Filter Graph Manager選擇Live Source的參考時鐘。有時候會出現Live Source數據流傳輸了幾十個Sample後會不在傳送Sample出來。可能的原因是,有多個參考時鐘時回放速率匹配問題。解決的方法是,選擇聲卡的時鐘,或者設置整個Filter Graph不使用時鐘。

18.0目前(DirectX8.1),微軟並沒有提供.dv文件的“拉”Filter。如果要使用DV文件,一個變通的方法是,將DV數據保存到AVI文件中。播放這種文件,AVI Splitter會識別出DV數據,並送DV Splitter(微軟提供,Push模式)和DV Video Decoder(微軟提供)進行解碼。

18.1在回放含有DV的AVI文件時,我們發現,真正實現IMediaSeeking的是AVI Splitter,而不是DV Splitter(微軟提供)。DV Splitter的Audio output pin或者Video Output pin均將IMediaSeeking的請求通過其Input pin傳遞到Up stream去了。

18.2如果自己寫一個"DV Parser Filter",需要注意的是,從Filter Source中拉數據,每次拉的數據大小一般是按512字節對齊的,所以不會是每次拉一個DV幀(PAL爲144000字節,NTSC爲120000字節)。而微軟的DV Splitter輸入的Sample要求是一幀一幀的數據(注意:如果每次送的數據太多了,會導致無法正常解碼;如果送少了,導致後續Filter沒有足夠的數據進行解碼,會發生Filter Graph的狀態轉換出錯,即可能無法轉入Paused狀態),所以我們必須在DV Parser上對拉出的數據進行必要的緩存。而且,在處理Seek時,新的Seeking位置也要考慮“字節對齊”的問題。
其實,爲DV文件寫一個Source Filter,直接將DV數據從文件中讀出後push出去,問題就要簡單一點!

19.Tee Filter問題。微軟提供的Smart Tee(沒有源碼)與Infinite Pin Tee Filter(有源碼)相比,後者的性能要好一點。兩者的區別是,前者將Preview pin出來的Sample進行了“去時間戳”處理,而後者只是簡單地將一個Sample分別在各個output pin上輸出。

20.寫多進一出,或者是一進多出的Filter,基類可以選擇CTransformFilter或者CTransInPlaceFilter,雖然它們在BaseClasses中的實現都是一進一出的。一般的做法爲,保持原有的Input pin和Output pin爲“主流”的鏈路,以這個標準Input pin的數據“流入”,來驅動協調其他Pin數據處理後,再輸入。需要注意的是,必須重載基類Filter的GetPinCount、GetPin實現,以增加新的Pin;重載FindPin支持新加pin;重載Filter的Pause或Stop實現,因爲可能還要同步新增加的pin的數據流(如果新增加的pin在Filter要轉入Paused狀態時還處於死循環或阻塞,則可能導致Filter狀態轉換失敗,出現Frozen現象);新增加Input pin一般從CBaseInputPin上繼承,Output pin從CBaseOutputPin上繼承,而且一般都要重載以下幾個函數的實現:CheckMediaType、Receive、EndOfStream、BeginFlush、EndFlush。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章