數(shù)據(jù)與數(shù)據(jù)緩沖區(qū)
數(shù)據(jù)保存在緩沖區(qū)中,緩沖區(qū)只是字節(jié)數(shù)組。 每個(gè)緩沖區(qū)由名為 MediaSample
的 COM 對(duì)象包裝车荔,該對(duì)象實(shí)現(xiàn) IMediaSample 接口。 MediaSample
由另一種類型的對(duì)象(稱為分配器)創(chuàng)建砾赔,該對(duì)象實(shí)現(xiàn) IMemAllocator 接口。 雖然兩個(gè)或更多個(gè)pin
連接可能共享同一分配器青灼,但為每個(gè)pin
連接分配一個(gè)分配器暴心。 下圖演示了此過(guò)程。
MediaSample
是承載數(shù)據(jù)的載體
每個(gè)分配器創(chuàng)建MediaSample
池杂拨,并為每個(gè)MediaSample
分配緩沖區(qū)专普。 每當(dāng)filter
需要使用數(shù)據(jù)填充緩沖區(qū)時(shí),它都會(huì)通過(guò)調(diào)用 IMemAllocator::GetBuffer 從分配器請(qǐng)求MediaSample
弹沽。 如果分配器具有其他filter
當(dāng)前未使用的任何MediaSample
檀夹, GetBuffer 方法將立即返回,并返回指向MediaSample
的指針策橘。 如果分配器的所有MediaSample
都在使用中炸渡,則 方法將阻塞,直到MediaSample
可用丽已。 當(dāng) 方法返回MediaSample
時(shí)蚌堵,filter
會(huì)將數(shù)據(jù)放入MediaSample
里面的緩沖區(qū), (設(shè)置相應(yīng)的標(biāo)志沛婴,通常包括時(shí)間戳) 吼畏,并將MediaSample
傳送到下游。
當(dāng)呈現(xiàn)器filter
收到MediaSample
時(shí)嘁灯,它會(huì)檢查時(shí)間戳并保留MediaSample
泻蚊,直到filter
圖的引用時(shí)鐘指示應(yīng)呈現(xiàn)數(shù)據(jù)。 filter
呈現(xiàn)數(shù)據(jù)后旁仿,會(huì)釋放MediaSample
藕夫。 在MediaSample
的引用計(jì)數(shù)為零之前孽糖,MediaSample
不會(huì)返回到分配器的MediaSample
池中枯冈,這意味著每個(gè)filter
都釋放了MediaSample
毅贮。 下圖演示了此過(guò)程。
MediaSample
可以作為上游生產(chǎn)者和下游消費(fèi)者中間的緩沖幀
上游filter
可能在呈現(xiàn)器之前運(yùn)行尘奏,也就是說(shuō)滩褥,它填充緩沖區(qū)的速度可能比呈現(xiàn)器使用緩沖區(qū)快。 即便如此炫加,MediaSample
也不會(huì)提前呈現(xiàn)瑰煎,因?yàn)槌尸F(xiàn)器會(huì)保留每個(gè)MediaSample
,直到其呈現(xiàn)時(shí)間為止俗孝。 此外酒甸,上游filter
不會(huì)意外覆蓋緩沖區(qū),因?yàn)?GetSample 僅返回未使用的示例赋铝。 上游filter
可以提前運(yùn)行的數(shù)量取決于分配器池中的MediaSample
數(shù)插勤。
上圖僅顯示一個(gè)分配器,但通常每個(gè)流有多個(gè)分配器革骨。 因此农尖,當(dāng)呈現(xiàn)器釋放示例時(shí),它可以具有級(jí)聯(lián)效果良哲。 下圖顯示了解碼器在等待呈現(xiàn)器釋放示例時(shí)保存壓縮的視頻幀的情況盛卡。 分析器filter
也在等待解碼器發(fā)布示例。
當(dāng)呈現(xiàn)器釋放其示例時(shí)筑凫,解碼器對(duì) GetBuffer 的掛起調(diào)用將返回滑沧。 然后,解碼器可以解碼壓縮的視頻幀并釋放它所持有的MediaSample
巍实,從而解除阻塞程序掛起的 GetBuffer 調(diào)用滓技。
本地內(nèi)存的數(shù)據(jù)傳輸機(jī)制
DirectShow 定義了本地內(nèi)存?zhèn)鬏數(shù)膬煞N機(jī)制:推送模型和拉取模型。
在推送模型中蔫浆,源filter
生成數(shù)據(jù)并將其傳遞到下游的下一個(gè)filter
殖属。 該filter
被動(dòng)接收數(shù)據(jù)、處理數(shù)據(jù)瓦盛,并將數(shù)據(jù)發(fā)送到下游洗显。
在拉取模型中,源filter
連接到分析器filter
原环。 分析程序filter
從源filter
請(qǐng)求數(shù)據(jù)挠唆。 源filter
通過(guò)傳遞數(shù)據(jù)來(lái)響應(yīng)請(qǐng)求。
推送模型使用 IMemInputPin 接口嘱吗,拉取模型使用 IAsyncReader 接口玄组。
推送模型比拉取模型更常見滔驾。 因此,以下文章采用推送模型俄讹。 本部分的最后一篇文章 “拉取模型”介紹了 IAsyncReader 接口與 IMemInputPin 的區(qū)別哆致。
當(dāng)pin
將媒體數(shù)據(jù)傳送到另一個(gè)pin
時(shí),它不會(huì)將直接指針傳遞給內(nèi)存緩沖區(qū)患膛。 相反摊阀,它傳遞指向管理內(nèi)存的 COM 對(duì)象的指針。 此對(duì)象稱為 MediaSample
踪蹬,公開 IMediaSample 接口胞此。 接收pin
通過(guò)調(diào)用 IMediaSample::GetPointer、IMediaSample::GetSize 和 IMediaSample::GetActualDataLength 等方法來(lái)訪問(wèn)內(nèi)存緩沖區(qū)跃捣。
MediaSample
始終從輸出pin
到輸入pin
向下游移動(dòng)漱牵。 在推送模型中,輸出pin
通過(guò)在輸入pin
上調(diào)用 IMemInputPin::Receive 來(lái)提供MediaSample
疚漆。 輸入pin
將完全在 Receive 方法內(nèi)同步 (處理數(shù)據(jù)) 酣胀,或在工作線程上異步處理數(shù)據(jù)。 如果需要等待資源愿卸,則允許輸入pin
在 Receive 方法中阻塞灵临。
在 IMemInputPin 接口中,上游filter
確定要發(fā)送的數(shù)據(jù)趴荸,并將數(shù)據(jù)推送到下游filter
儒溉。 對(duì)于某些filter
, 拉取模型更合適发钝。 此處顿涣,下游filter
從上游filter
請(qǐng)求數(shù)據(jù)。 MediaSample
仍會(huì)從輸出pin
到輸入pin
向下游移動(dòng)酝豪,但下游filter
會(huì)啟動(dòng)數(shù)據(jù)流涛碑。 這種類型的連接使用 IAsyncReader 接口。
拉取模型的典型用途是在文件播放中孵淘。 例如蒲障,在 AVI 播放圖中, 異步文件源 filter
執(zhí)行一般文件讀取操作瘫证,并將數(shù)據(jù)作為字節(jié)流傳遞揉阎,而沒有格式信息。 AVI 拆分器filter
讀取 AVI 標(biāo)頭并將流分析為視頻和音頻MediaSample
背捌。 與異步文件源filter
相比毙籽,AVI 拆分器可以確定所需的數(shù)據(jù),因此它使用 IAsyncReader 而不是 IMemInputPin毡庆。
若要從輸出pin
請(qǐng)求數(shù)據(jù)坑赡,輸入pin
調(diào)用以下方法之一:
IAsyncReader::Request
IAsyncReader::SyncRead
IAsyncReader::SyncReadAligned烙如。
第一種方法是異步的,用于支持多個(gè)重疊讀取毅否。 其他是同步的亚铁。
理論上,任何filter
都可以支持 IAsyncReader搀突,但在實(shí)踐中刀闷,它專為連接到分析程序filter
的源filter
而設(shè)計(jì)熊泵。 分析程序的作用非常類似于推送模型中的源filter
仰迁。 暫停時(shí),它會(huì)創(chuàng)建一個(gè)流式處理線程顽分,該線程從 IAsyncReader 連接拉取數(shù)據(jù)并將其推送到下游徐许。 輸出pin
使用 IMemInputPin,圖形的其余部分使用標(biāo)準(zhǔn)推送模型卒蘸。