)
簡介DSPack 2.34 是面向 Delphi 2010 開發(fā)者的組件包長期活躍于 Delphi 社區(qū)用于擴展 IDE 能力簡化網(wǎng)絡通信TCP/UDP/HTTP/FTP、多線程TThread、數(shù)據(jù)庫訪問ODBC/ADO和串口控制等復雜功能的實現(xiàn)該版本已經(jīng)過實際項目測試兼容性有保障適合需要快速構建高性能 Windows 應用的系統(tǒng)開發(fā)者。資源壓縮包體積輕量僅 1.75MB共 319 個文件主體由 95 個 PAS 源代碼、40 個 DPR 工程文件、39 個 DFM 窗體文件組成另有批處理腳本、DCU 編譯單元和少量圖片資源可支撐組件安裝、示例運行與二次開發(fā)目錄結構便于按需檢索。目前已有 327 人瀏覽學習。包內附帶可直接運行的示例工程覆蓋網(wǎng)絡、數(shù)據(jù)庫、串口與多媒體等常見場景可參考組件的調用方式和調試流程同時提供注冊與清理腳本便于快速安裝和測試組件環(huán)境。將壓縮包導入 Delphi 2010 后可直接拖拽使用網(wǎng)絡、串口和多媒體組件大幅減少底層封裝工作量。1. Delphi 2010 還能用 DSPack 2.34老工程視頻方案的最后一根稻草前陣子接手一個停留在 Delphi 2010 的老監(jiān)控系統(tǒng)要在不升級 IDE、不引入大型框架的前提下加入視頻回放和單幀抓取。考察了一圈組件新庫不是依賴 FireMonkey 就是早放棄 Win32直到翻出 DSPack 2.34 這版仍在維護周期內兼容 Delphi 2010 的 DirectShow 封裝庫重新編譯、裝組件、跑通播放和截幀整個過程驗證下來完全可用。它解決的問題非常具體在 32 位進程里用 DirectShow 做播放、采集、抓幀不需要換編譯環(huán)境不需要額外 runtime特別適合還在維護老 Delphi 工程、或必須鎖定 IDE 版本的視頻開發(fā)場景。下面按我實測的完整流程展開每一步盡量寫清操作和參數(shù)。2. DSPack 2.34 為什么能跑在 Delphi 2010 上DirectShow 封裝與 Unicode 邊界2.1 組件骨架TFilterGraph、TVideoWindow、TSampleGrabber 的角色DSPack 的思路是把 Windows 多媒體層的 DirectShow COM 接口包成 Delphi 類讓窗體設計器直接放組件操作。最常見的三個組件是 TFilterGraph、TVideoWindow、TSampleGrabber。TFilterGraph 對應 Filter Graph Manager負責把源文件、解碼器、渲染器編排成一條可播放的管線核心接口是 IGraphBuilder。TVideoWindow 是視頻渲染窗口對應 IVideoWindow負責把畫面畫到指定控件上。TSampleGrabber 則是插在管線中間的抓幀器可以在視頻數(shù)據(jù)流過時拿到原始樣本。它們三者協(xié)作關系類似一條流水線源 Filter 讀文件解碼 Filter 做格式變換渲染 Filter 把畫面輸出到窗口。TFilterGraph 把節(jié)點的連接邏輯隱藏起來開發(fā)者只需調 RenderFile 或 RenderGraph它內部會根據(jù)媒體類型去系統(tǒng)注冊表搜索能連接的 filter。我要強調一個容易被繞暈的邊界TFilterGraph 管全圖TFilter 只管單個節(jié)點。一個圖里可以有多個 TFilter 實例但圖管理器只有一個 TFilterGraph 實例。長期用下來我的習慣是標準播放場景無腦用 TFilterGraph 的自動連接一旦涉及非標準格式或需要自定義解碼順序才手動創(chuàng)建 TFilter 實例、指定輸入輸出 Pin。DSPack 把控制權留給了開發(fā)者但大多數(shù)項目根本走不到手動拉線那一步。2.2 DirectShow 處理鏈條源、變換、渲染三層再展開講一下這個管線模型方便后面代碼落地。完整的 Filter Graph 分三層源 Filter 讀取文件、攝像頭或網(wǎng)絡流輸出壓縮或未壓縮的媒體樣本變換 Filter 完成解碼、格式轉換DSPack 自帶一批 transform filter也能用系統(tǒng)里注冊的第三方解碼器渲染 Filter 把媒體數(shù)據(jù)送到顯卡或聲卡視頻走 Video Renderer / VMR7 / VMR9音頻走 Audio Renderer。每個 filter 的輸入輸出 Pin 之間要協(xié)商媒體類型比如視頻的 RGB24、YUY2音頻的 PCM以及壓縮流的 subtype GUID。協(xié)商失敗時 Graph Builder 會嘗試插入轉換 filter比如從 YUY2 轉 RGB24 的 Color Space Converter這是 DirectShow 自動配置的核心能力也是 TFilterGraph.RenderFile 最省事的原因。調試的時候有個辦法非常好用TFilterGraph.Graph 屬性直接暴露 IGraphBuilder 接口可以用它枚舉當前圖里的 filter 和 pin 連接情況。我習慣連接失敗后先把圖 dump 出來看是哪個 pin 沒匹配上比瞎猜快得多。2.3 Unicode 邊界Delphi 2010 字符串模型變更帶來的坑Delphi 2009 開始默認 string 變成 UnicodeString這對老組件是個大地震。DSPack 2.34 的大量源碼在編寫時用的是 PChar 邏輯放到 Delphi 2010 里PChar 默認是 PAnsiChar而 DirectShow 的 COM 接口期望的是寬字符串 LPCWSTR。兩邊在接口邊界上一錯位就出現(xiàn)了我前面說的中文路徑失敗、文件找不到這類問題。具體現(xiàn)象是調 RenderFile 傳含中文或特殊字符的路徑時DirectShow 返回 VFW_E_NOT_FOUND 或 VFW_E_CANNOT_CONNECT哪怕文件確實存在。我實測發(fā)現(xiàn)直接用 WideString 顯式聲明再傳指針就行不要讓 Delphi 在邊界處隱式轉換 ANSI。var Path: WideString; begin Path : C:\測試視頻\sample.avi; FilterGraph1.RenderFile(PWideChar(Path)); end;邏輯說明這里先把路徑聲明為 WideString再取 PWideChar 指針傳給 RenderFile確保 DirectShow 收到的是寬字符不經(jīng)過任何 ANSI 轉換。參數(shù)說明RenderFile 是 TFilterGraph 的自動建圖入口傳完整路徑即可。路徑存在但含中文時用這個寫法能繞開大部分編碼問題如果路徑不含中文普通字符串直接傳也問題不大。2.4 為什么選 2.34 而不是其他版本對比過幾條路線順便說明選型理由。Delphi 2010 自帶的 TMediaPlayer 走 MCI 播放單純放個 AVI 沒問題但截幀、攝像頭采集、自定義媒體源完全用不上。FFmpeg 的 Delphi 封裝性能好但要引入額外 DLL 和線程模型管理對一個幾萬行的老 VCL 工程來說太重。DSPack 2.34 則是完整的 DirectShow 封裝播放、截幀、采集都能在同一個 COM 模型下處理API 風格統(tǒng)一。方案播放截幀采集Delphi 2010 原生編譯外部依賴DSPack 2.34支持支持支持是無TMediaPlayer支持否否是MCI 系統(tǒng)組件FFmpeg 封裝支持支持支持需第三方動態(tài)庫上面表格里能直觀看到唯一同時滿足播放、截幀、采集且無動態(tài)庫依賴的就是 DSPack 2.34。它最后一代完整支持 Delphi 2009/2010 的包文件是 2.34這也是我鎖定這個版本的理由。3. 安裝配置從源碼包到 Component 菜單的完整流程3.1 裝之前先確認環(huán)境源碼包拿到手先別急著點安裝花幾分鐘確認三件事機器上裝的是 Delphi 2010 Update 5 或更高版本系統(tǒng)至少是 Win7 以上Windows SDK 里的 DirectShow 頭文件版本不要太舊。DSPack 源碼自帶兩個關鍵的包文件DSPACK_D2010.DPK 是運行時包DSPACK_D2010_DESIGN.DPK 是設計時包。前者負責編譯鏈接后者負責把組件注冊到 IDE 面板。源碼目錄里通常有 source、packages、samples 還有 filter 幾個目錄分別是主要單元源碼、包工程文件、演示工程和部分附帶 filter 源。安裝前重點檢查 packages 目錄下的 .dpk 文件是否齊全比如有沒有 .dproj 工程文件這會影響 Delphi 2010 打開時的格式兼容。我吃過一次虧拿到源碼包后沒看說明直接在 IDE 里雙擊 .dpk 就點 Install結果報錯提示 design 包依賴 runtime 包。這個順序問題幾乎是每個第一次裝 DSPack 的人都會遇到的。3.2 編譯 runtime 與 design 包安裝的順序必須是先編譯 runtime 包再編譯 design 包并安裝。在 Delphi 2010 里操作文件 → 打開項目 → 定位到 packages 目錄下的 DSPACK_D2010.DPK。項目管理器里右鍵這個包先選 Compile這一步會生成 DSUtil.dcu、DSVideo.dcu 等運行時文件。編譯成功后再打開 DSPACK_D2010_DESIGN.DPK同樣先 Compile然后右鍵 Install。等 IDE 提示組件包已注冊DSPack 頁面就會出現(xiàn)在組件面板上。cd /d %~dp0 regsvr32 /s filter\*.ax echo DSPack filters registered.邏輯說明這是一段批量注冊腳本把 DSPack 自帶的 DirectShow filter.ax 文件通常放在 filter 子目錄注冊到系統(tǒng)。Windows 7 及以上系統(tǒng)建議用管理員權限打開命令行再執(zhí)行否則注冊會靜默失敗。參數(shù)說明/s 表示靜默模式不彈出成功提示/u 可用來反注冊。如果機器是 64 位系統(tǒng)且 IDE 是 32 位DSPack 的 .ax 組件通常注冊到 SysWOW64 目錄即可不需要跑到 System32 里。包文件全部編譯完成后還有一步必須做把 DSPack 的 source 目錄加入 IDE 的 Library 搜索路徑。打開 IDE 工具 → 選項 → 庫路徑追加 source 目錄路徑。這一步漏掉的話新建工程引用 DSPack 單元時Delphi 會提示找不到 DSUtil.dcu。3.3 安裝后的環(huán)境檢查清單裝完別急著寫業(yè)務代碼先跑一遍自帶 Demo我踩過的坑里有一半出在沒驗證基礎環(huán)境。打開 samples 下的視頻播放示例工程編譯運行后傳入一個現(xiàn)有 AVI 文件路徑。能正常播放并拖動進度條說明三條鏈路都沒問題第一個是 IDE 集成說明包文件安裝正確第二個是 DirectShow 基本鏈路說明系統(tǒng)里渲染器和解碼器可用第三個是 filter 注冊狀態(tài)說明 Graph 構建能正常匹配。檢查工程平臺選項也在此列。Delphi 2010 支持 Win32 和 Win64 目標但 DSPack 2.34 的源碼只面向 32 位平臺。在 Project → Options → Delphi Compiler 里確認 Target 是 32-bit Windows這點我在 Win64 目標下翻過車后面避坑章節(jié)詳細寫。還有一個容易被忽略的檢查項確認系統(tǒng)里已注冊的 DirectShow 解碼器是否干擾 RenderGraph。可以用系統(tǒng)自帶的 GraphEdit 工具打開同一個 AVI 查看圖結構如果自動匹配拉到第三方解碼器或奇怪的變換 filter播放可能卡頓或黑屏。這時候就要走手動建圖的路線而不是繼續(xù)依賴自動連接。4. 寫第一個播放器RenderGraph 一行出畫面到手動拉線4.1 最小可運行的播放器代碼新建一個 VCL 應用往窗體上放 TFilterGraph、TVideoWindow、TButton 和一個 TPanel按鈕點擊事件里寫下面這段procedure TForm1.btnPlayClick(Sender: TObject); begin FilterGraph1.Active : False; FilterGraph1.ClearGraph; FilterGraph1.RenderFile(C:\videos\sample.avi); VideoWindow1.FilterGraph : FilterGraph1; VideoWindow1.Owner : Panel1; VideoWindow1.Visible : True; FilterGraph1.Play; end;邏輯說明第一步把濾波圖管理器停掉并清空上次的圖第二步調 RenderFile 讓 DirectShow 自動建圖第三步把 VideoWindow 綁定到 FilterGraph 并指定它把畫面畫到 Panel1 上最后 Play 開始播放。參數(shù)說明RenderFile 接受完整文件路徑路徑含中文或空格時按第 2 章里 WideString 的寫法傳參更穩(wěn)。VideoWindow1.Owner 必須是已創(chuàng)建好的窗口化控件一般用 Panel不能畫到普通 Label 上。Visible 必須設為 True否則視頻畫面不顯示但聲音會正常播放這是個容易誤判的現(xiàn)象。4.2 手動 Filter Graph 連接方式需要精確控制 filter 鏈時RenderFile 的自動建圖就不夠用了。常見做法是先用 RenderFile 把基礎鏈路拉起來再把 SampleGrabber 插入指定位置比如截幀或預覽前級處理。procedure TForm1.BuildGraph; var Grabber: TSampleGrabber; begin FilterGraph1.Active : False; FilterGraph1.ClearGraph; FilterGraph1.RenderFile(C:\videos\sample.avi); Grabber : TSampleGrabber.Create(nil); try Grabber.FilterGraph : FilterGraph1; Grabber.SetMediaType(VIDEOINFOHEADER); FilterGraph1.AddFilter(Grabber, MyGrabber); FilterGraph1.ConnectPins(Source, Output, MyGrabber, Input); FilterGraph1.Play; finally Grabber.Free; end; end;邏輯說明這段代碼先讓 RenderFile 自動建好源和渲染段再新建 SampleGrabber 加入圖里用 ConnectPins 把源 Filter 的輸出 Pin 接到 Grabber 的輸入 Pin。Grabber 的 filter name 用字符串標識ConnectPins 通過這個名字定位。參數(shù)說明SetMediaType 必須設置否則 Grabber 不知道輸出什么媒體類型回調不會觸發(fā)VIDEOINFOHEADER 是最通用的設置包含畫面尺寸、顏色格式和幀率。ConnectPins 的參數(shù)是濾波器名和 Pin 名實際名稱可能因 filter 的實現(xiàn)而不同調試時用 GraphEdit 查看準確的 Pin 名再填。接完后實測發(fā)現(xiàn)Grabber 插入后如果媒體類型不匹配DirectShow 可能自動插入轉換 filter播放仍正常但沒有回調。這種情況下要把 Grabber 的輸出類型設成和下游一致或者干脆在 Grabber 之后手動連接 Renderer。4.3 參數(shù)調整播放窗口、音量與音視頻同步TFilterGraph 上有幾個高頻參數(shù)值得專門說明。AutoMon 屬性控制是否按視頻原始尺寸自動調整播放窗口設為 False 時畫面會適應 Owner 控件尺寸設為 True 時可能撐爆或截斷畫面。Volume 設置音頻音量范圍是 -10000 到 0單位是百分之一分貝這里最容易踩坑很多人傳正數(shù)發(fā)現(xiàn)沒聲音其實必須用負數(shù)。Balance 控制左右聲道平衡-10000 全左10000 全右。播放定位時用 Seek 方法進度參數(shù)基于 TimeFormat。默認 TimeFormat 是 TIME_FORMAT_MEDIA_TIME單位是 100 納秒配合 GetDuration 獲取總時長可以精確換算進度條比例。音視頻同步問題我也遇到過文件較大時畫面和聲音不同步常見原因是系統(tǒng)默認渲染器在 VMR9 模式下需要更精確的時鐘源或者是顯卡驅動渲染隊列緩沖太大。處理方式有兩個一是把 TFilterGraph 的 SyncMode 設為 smClock強制使用系統(tǒng)時鐘二是把 VMR 的 CompositionMode 打開讓渲染器自己做合成減少畫面延遲。5. 避坑記錄Delphi 2010 調 DSPack 常見問題與排查5.1 編譯報錯Cannot find unit DSUtil.dcu現(xiàn)象新建工程后引用 DSPack 單元編譯提示找不到 DSUtil.dcu但安裝階段明明沒報錯。原因source 目錄沒有加入 IDE 的 Library 搜索路徑或者 runtime 包編譯產(chǎn)物沒有生成到默認輸出目錄。解決回到安裝章節(jié)把 source 目錄追加到工具 → 選項 → 庫路徑然后重新編譯 DSPACK_D2010.DPK確認 DCU 文件確實生成。檢查 Search Path 里有沒有殘留舊版本的 DSUtil.pas有就清掉避免編譯時取到舊文件。5.2 視頻窗口黑屏但有聲音現(xiàn)象播放能聽到聲音視頻窗口里全是黑色線程沒有卡死。原因VideoWindow1.Owner 指向的 Panel 尚未創(chuàng)建或者在窗口初始化之前就調用了 Play。VMR 模式下渲染器對窗口句柄的創(chuàng)建時機非常敏感窗體還沒顯示時綁定窗口畫面就丟了。解決保證在 FormShow 之后再創(chuàng)建綁定不要把綁定邏輯放在 FormCreate 里。綁定后顯式設置 VideoWindow1.Visible : True并確認 Panel 的 DoubleBuffered 屬性已在窗體顯示前打開否則畫面可能閃爍或不出圖。5.3 中文路徑播放失敗現(xiàn)象RenderFile 傳含中文的文件路徑時返回 VFW_E_NOT_FOUND換成純英文路徑就正常。原因Delphi 2010 的字符串在邊界處被轉成 ANSIDirectShow 期望的是寬字符。DSPack 2.34 源碼內部對 PChar 的處理沒有完全適配 Unicode 字符串模型路徑一旦出 ASCII 范圍就亂碼。解決傳參時把路徑聲明成 WideString用 PWideChar 傳進 RenderFile。如果路徑中既有中文又有空格避免手寫拼接用 ExtractFilePath 和 ExtractFileName 組合后再轉 WideString減少中間層轉換。5.4 64 位編譯直接翻車現(xiàn)象把 Project Target 切到 64-bit Windows重新編譯報大量類型不匹配錯誤多半是 Cardinal / LongWord 和 NativeUInt 互轉問題。原因DSPack 2.34 的源碼只支持 32 位編譯沒有為 Delphi 2010 的 Win64 目標做過適配。解決工程目標平臺鎖死在 32-bit Windows。在 Project Options 里把 Target 設為 Win32不用嘗試用條件編譯去修補源碼兼容性工作量遠大于收益。5.5 SampleGrabber 收不到回調現(xiàn)象Grabber 加進圖中播放正常但回調函數(shù)一次也不觸發(fā)。原因沒設置媒體類型或 Grabber 的 BufferSize 太小回調被丟棄或者設置了 OneShot 模式后只在第一幀回調一次后續(xù)全無。解決先檢查 Grabber.SetMediaType 是否傳入 VIDEOINFOHEADER再確認 BufferSize 至少能容納一幀 RGB24 數(shù)據(jù)。調試時先把 OneShot 設為 True 驗證單幀回調通過后再改成連續(xù)模式。5.6 音頻采集沒有聲音輸出現(xiàn)象程序里 TAudioCapture 能采集到波形數(shù)據(jù)但外放沒有聲音。原因聲卡驅動默認輸出格式和 Audio Renderer 協(xié)商失敗或者采集鏈路到渲染器之間走了錯誤的 filter 路徑。解決在采集源和渲染器之間加一個 SampleGrabber 做格式中轉或者手動指定音頻渲染器的輸出格式為 PCM。部分主板驅動默認開啟獨占模式需要在聲卡控制面板里關閉應用程序獨占控制否則 DSPack 拿不到混音數(shù)據(jù)。6. 進階SampleGrabber 截幀與 Buffer 回調緩沖實戰(zhàn)SampleGrabber 的 Buffer 回調是做視頻抓幀最直接的手段。先實現(xiàn)一個回調類繼承自 TInterfacedObject 并實現(xiàn) ISampleGrabberCB 接口的三個方法type TFrameCallback class(TInterfacedObject, ISampleGrabberCB) function SampleCB(Time: Double; pSample: IMediaSample): HResult; stdcall; function BufferCB(Time: Double; pBuffer: PByte; Len: Integer): HResult; stdcall; end; function TFrameCallback.BufferCB(Time: Double; pBuffer: PByte; Len: Integer): HResult; begin // 在這里把 Len 字節(jié)的 RGB24 數(shù)據(jù)寫入文件 Result : S_OK; end;邏輯說明BufferCB 在每一幀經(jīng)過 Grabber 時被調用pBuffer 指向一幀原始像素數(shù)據(jù)Len 是緩沖長度Time 是媒體時間戳。SampleCB 在樣本級觸發(fā)不需要處理媒體類型時就避開它BufferCB 更可控。參數(shù)說明回調注冊需要調用 Grabber.SetCallBack(TFrameCallback.Create, 0)。第二個參數(shù)為 0 表示只回調 BufferCB為 1 表示回調 SampleCB。回調類注意保持引用有效不能讓對象在回調觸發(fā)前被釋放否則直接訪問非法內存。實際操作時把回調里拿到的 Buffer 根據(jù)當前視頻分辨率計算出幀大小校驗 Len 是否等于 Width × Height × 3不對就丟棄。這個校驗幫我排掉過不完整幀某次項目里沒校驗抓出來的相冊庫里混了一堆半截圖從那以后我每次在回調里都強制校驗幀尺寸和寬度高度走一幀驗證一幀再入庫。希望幫到你。本文還有配套的精品資源點擊獲取