ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

DSPACK 2.3.4实战:Delphi 7下用DirectShow实现摄像头预览与抓帧

DSPACK 2.3.4实战:Delphi 7下用DirectShow实现摄像头预览与抓帧 简介DSPACK2.3.4 for Delphi5 6 7 是一套基于 DirectShow 与 DirectX 的 Delphi 多媒体开发库面向需要在 Delphi 5/6/7 中快速实现音视频采集、播放、录制、文件处理及摄像头控制的开发者。资源包共 277 个文件体积约 1.78MB以 pas 源码、dpr 工程、dfm 窗体、lib 库、dpk 包及 bat 脚本为主便于查看组件实现、重新编译或直接注册使用。目前已有 498 人学习/下载适合有一定 Delphi 基础、希望绕过复杂 DirectShow 底层细节的中级开发者。借助其中的 DirectShow 过滤器图接口可完成实时预览、视频录制、编码与文件输出DirectSound 与 DirectMusic 支持则能实现音频播放、混音与效果处理摄像头控制部分允许调整分辨率、帧率并获取实时视频流可直接用于视频聊天、监控系统或简单视频编辑器。附带的示例项目、文档以及 clean/reg 类脚本能帮助初学者快速掌握从视频捕获到存储播放的完整链路并简化环境配置是一套适合起步与参考的多媒体开发工具包。1. 为什么 2024 年还有人翻出 DSPACK 2.3.4 这个老古董接手老项目的人应该都有体会一套 Delphi 7 写的工控软件界面朴素、代码老派但偏偏要接摄像头、要录视频、要做实时画面预览。这时候你会发现VCL 自带的 TMediaPlayer 控件简直像上个世纪的玩具——它只能调系统播放器能播文件但拿不到帧更别说控制摄像头了。于是老程序员们会在论坛里翻出一个名字DSPACK。DSPACK 2.3.4 是 Delphi 社区里流传最广的一套 DirectShow 封装库。它把 Windows 底层的 DirectShow 接口Filter Graph、Pin、Media Control 那一整套 COM 对象包成了 VCL 组件让 Delphi 5、6、7 的老用户也能像拖 Button 一样拖出一个视频窗口组件然后几行代码实现播放、暂停、抓帧、采集摄像头画面。在那个年代这是 Delphi 做多媒体开发最顺手的路没有之一。为什么到现在还在找它因为老项目不会因为技术迭代就自动消失。工厂里的检测设备、教学录播系统、老旧监控上位机很多还在 Windows XP 或 32 位 Win7 上跑着。新人不一定愿意去移植老板也不愿意花大价钱重构。DSPACK 2.3.4 就是这些系统里最稳的一块垫脚石——它的源码里直接带了 DirectX 9 的头文件翻译这在当年是难得的完整版。很多使用 Delphi 5、6、7 的老团队装上之后能少写几百行 COM 接口调用代码。这套库的核心价值就一句话让 Delphi 程序员用普通组件的思维去操作 DirectShow。你不用自己去 QueryInterface、Release、枚举 Filter这些脏活累活 DSPACK 全包了。值得说的是2.3.4 这个版本号对应的是 2004 年左右发布的最终版后面作者基本停止更新了所以它天然带着那个时代 Delphi 的烙印——非 Unicode、D7 优化、32 位编译。2. 从下载到组件面板安装和编译的硬细节DSPACK 2.3.4 的压缩包解压后目录结构并不复杂。核心是src文件夹里面装着一大堆.pas单元文件还有按 Delphi 版本区分的.dpk包文件。我第一次装的时候差点犯浑以为要把所有.pas全塞进工程里其实完全没必要。2.1 目录里那些文件分别干什么说几个最关键的单元其余的可以等遇到再翻DirectShow9.pas整个库的基石翻译自 DirectX 9 SDK 里的头文件定义了 DirectShow 几乎所有 COM 接口和常量。老版本 DSPACK 用的是 DirectShow8.pas2.3.4 升级到 9兼容性更广。DSPack.pas组件主单元TFilterGraph、TFilter、TSampleGrabber 等核心类的声明都在这。DSCap.pas视频捕获相关组件TVideoCapture 就在这里。DSUtil.pas/DXSUtil.pas一堆辅助函数比如枚举设备、媒体类型转换写工具时经常要用到。VideoWin.pasTDSVideoWindow 组件负责把视频渲染到窗口。2.2 编译包的步骤和路径配置如果你用的是 Delphi 7直接打开Packages\DSPack_D7.dpk右键 Install 就行。但别急着点先做三件事把src目录加到 Tools Environment Options Library Library Path 里。这个不配编译包的时候会报找不到 DirectShow9.pas。确认 Delphi 7 的 Update Pack 装到 1.1 以上否则某些编译单元会过不去。D7 老用户应该都懂这个坑。关闭运行时包选项改成静态链接发布 exe 的时候少带几个 bpl 文件。Delphi 5、6 操作类似对应打开DSPack_D5.dpk或DSPack_D6.dpk。但有个细节提醒一下Delphi 6 的老用户经常因为 IDE 的包管理方式不同导致安装失败我个人建议用 Delphi 7 跑 DSPACK 最省心编译报错最少。2.3 不装组件也能用的轻量方案如果你的项目只是临时用一下某个功能不想污染组件面板可以直接绕开包安装把src目录加入搜索路径然后在代码里 uses 需要的单元手动创建组件实例。比如uses DSPack, DirectShow9; procedure TForm1.FormCreate(Sender: TObject); var FG: TFilterGraph; begin FG : TFilterGraph.Create(nil); FG.Active : True; // 后面手动添加 filter、连接 pin end;这种方式特别适合做无窗体的控制台类工具或者给老项目加一个视频预览模块但不想大改工程结构的场景。缺点是无法在设计期拖组件、看属性调试稍微费劲一点。我自己的习惯是维护一个最小 DSPACK 模板工程里面只放一个窗体、一个 TFilterGraph、一个 TVideoWindow所有连线都在代码里做这样换新环境时验证库是否编译通过特别快。3. 组件面板上的东西到底对应 DirectShow 里的什么第一次打开装了 DSPACK 的 Delphi组件面板会多出一排图标。很多人看到这几十个组件直接懵了其实它们的关系是有迹可循的。3.1 核心对象关系表我把常用组件和它们的 DirectShow 底层对象列了一张对照表搞清楚这张表后面写代码就清晰了组件底层 COM 对象主要用途TFilterGraphIFilterGraph / IGraphBuilder整个图表的管理者add filter、connect pin 都靠它TFilterIBaseFilter代表一个输入或处理节点TSampleGrabberISampleGrabber在链路中截取媒体样本用于抓帧TDSVideoWindowIVideoWindow视频显示窗口控制画面位置、大小TVideoCaptureICaptureGraphBuilder2视频采集安装摄像头设备时用TAudioCaptureICaptureGraphBuilder2音频采集TDXInputIDirectInput8输入设备处理手柄、摇杆等TDXDrawIDirectDraw7老式 2D 绘图表面游戏开发用3.2 组件和 Filter Graph 的配合逻辑DirectShow 的基本模型是一条管道数据从 Source Filter 出来经过若干 Transform Filter 处理最后到达 Renderer Filter 显示或保存。DSPACK 组件相当于把管道里每个节点的控制权抽出来封装好但管道本身的结构还是要你用代码搭建。举个例子播放一个视频文件的流程是TFilterGraph 作为容器TFilter 负责加载文件源TDSVideoWindow 作为视频渲染器外加系统自带的音频渲染器。DSPACK 不会帮你决定播放哪个文件和用哪个解码器它只提供组装管道的手脚架。理解这一点很重要——很多新手以为拖一个 TDSVideoWindow 就能放视频结果发现黑屏就是因为没有把 Filter Graph 完整搭起来。3.3 为什么组件不是越多越好组件面板看着丰富实际项目里常用的就那么几个。TDXDraw、TDXInput 这类游戏向组件和工控视频处理完全没关系装了纯粹占地方。哪怕 TVideoCapture 和 TSampleGrabber 之间也有功能重叠后面我会说到怎么取舍。我的建议是先用 TFilterGraph TDSVideoWindow TSampleGrabber 这套组合跑通一个小 demo再根据需求回到组件面板里找其他组件不要一上来堆满组件排错会非常痛苦。4. 实战搭一个摄像头预览加抓帧的小工具说再多理论不如跑一个例子。下面这个是我在 Delphi 7 环境下一个标准的摄像头预览加抓帧方案功能虽然简单但覆盖了 DSPACK 日常使用的核心操作枚举设备、连接 Filter、控制媒体流、抓数据。4.1 界面布置和组件挂接窗体上放两个组件一个 TFilterGraph命名为 FG一个 TDSVideoWindow命名为 VidWin。注意 TDSVideoWindow 在设计期需要指定它的 FilterGraph 属性指向 FG这一步不设置运行时画面不会显示。然后放两个按钮一个叫打开摄像头一个叫抓帧。再加一个 TImage 用来显示抓到的画面一个 TSaveDialog 用于保存。4.2 打开摄像头并预览uses DSPack, DirectShow9, DSPackConst, DSUtil; procedure TForm1.btnOpenCamClick(Sender: TObject); var GraphBuilder: IGraphBuilder; CapBuilder: ICaptureGraphBuilder2; IBaseFilter: IBaseFilter; DeviceName: string; HR: HRESULT; begin // 1. 获取当前的 filter graph 接口 GraphBuilder : FG as IGraphBuilder; CapBuilder : nil; // 2. 创建 Capture Graph Builder并让它指向同一个 graph HR : CoCreateInstance(CLSID_CaptureGraphBuilder2, nil, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, CapBuilder); if Failed(HR) then raise Exception.Create(无法创建 CaptureGraphBuilder); CapBuilder.SetFiltergraph(GraphBuilder); // 3. 枚举系统里的视频输入设备 DeviceName : ; GetVideoCaptureDeviceNames(nil, TStringList.Create); // 这里仅是示意真实代码要遍历设备名 // 4. 创建视频采集 filter 并添加进 graph CoCreateInstance(CLSID_VideoInputDeviceCategory, nil, CLSCTX_INPROC_SERVER, IID_IBaseFilter, IBaseFilter); // 简化写法用 DSPACK 的辅助函数创建采集设备并渲染到窗口 HR : CapBuilder.RenderStream(PIN_CATEGORY_PREVIEW, MEDIATYPE_Video, IBaseFilter, nil, nil); if Failed(HR) then begin FG.Active : False; raise Exception.Create(无法连接摄像头设备请检查驱动或占用状态); end; // 5. 激活 graph开始预览 FG.Active : True; VidWin.Visible : True; VidWin.SetBounds(10, 10, 640, 480); end;上面代码里的 GetVideoCaptureDeviceNames 在 DSUtil 单元里有更完整的形式需要传入一个 TStrings 来接收设备列表。真实项目中我习惯把第一步和第二步封装成一个函数返回主采集设备对应的 IBaseFilter并在 combo box 里列出所有设备方便用户切换。抓帧之前最好用 MediaControl 的 Run 方法确认 graph 已经处于运行状态否则抓到的数据可能是空的。4.3 抓帧功能的两种思路抓帧是 DSPACK 用得最多、也最容易写错的需求。常见做法有两种第一种是用 TSampleGrabber 插入链路在 buffer 里拦截样本转成位图。这个方式能拿到未压缩的原始帧适合做后续的图像处理。核心代码是设置 TSampleGrabber 的 BufferSize 和媒体类型然后在 OnBuffer 事件里把 buffer 数据拷出来。第二种是用 IMediaDet 去抓单帧适合从视频文件里提取画面不适合实时摄像头预览场景因为每次抓取都要重新走一遍 graph。我强烈建议实时预览场景用 TSampleGrabber。示例procedure TForm1.btnGrabClick(Sender: TObject); var SampleGrabber: TSampleGrabber; MediaType: TAMMediaType; Bitmap: TBitmap; BmpInfo: PBitmapInfoHeader; Buffer: Pointer; BufferSize: integer; begin SampleGrabber : TSampleGrabber.Create(nil); try SampleGrabber.FilterGraph : FG; SampleGrabber.MediaType.majortype : MEDIATYPE_Video; SampleGrabber.MediaType.subtype : MEDIASUBTYPE_RGB24; SampleGrabber.BufferSize : 640 * 480 * 3; SampleGrabber.SetBufferSamples(True); SampleGrabber.SetOneShot(False); // 这里需要把 SampleGrabber filter 插入到预览链路中 // 正常做法是在 RenderStream 之前 AddFilter并手动连接 pin。 // 篇幅有限只列出核心调用 SampleGrabber.GetBitmap(Bitmap); // DSPACK 提供的便捷方法 Image1.Picture.Bitmap : Bitmap; finally SampleGrabber.Free; end; end;关键点SampleGrabber.GetBitmap 是 DSPACK 封装得比较好的一个方法它会从内部缓冲直接解析出 TBitmap。但有个前提——你必须在建立链路时把 SampleGrabber 的 media type 指向 RGB24否则 GetBitmap 拿到的数据色彩通道可能是乱的。在这个环节我踩过坑后面单独讲。5. 编译不过、黑屏、没声音我把那些问题排查了一遍老库 新系统 老 Delphi这三者叠加起来问题只会多不会少。我把几个最典型的问题和完整排查链路列出来希望能帮你省下几个通宵。5.1 典型编译报错和原因最常见的报错有两类Unit DirectShow9 was compiled with a different version of Windows这是 Delphi 7 的系统 RTL 版本和 DSPACK 不匹配导致的优先给 D7 打上最新 Update Pack再把 DSPACK 的包重新编译一遍。File not found: DirectShow9.dcu纯粹是 Library Path 没配对把src路径加进去就有。Unresolved external这类多半出现在连接解码器相关的代码检查你是否漏 uses 了DSPACKConst或DSUtil。我把这两个单元作为常用引用写进模板工程后这类错误基本消失了。5.2 预览画面黑屏的排查链路黑屏这个问题有十几种成因别慌按顺序查检查 graph 是否真的 Active 了在FG.Active : True后面加一句FG.MediaControl.Run确认媒体控制接口能正常启动。检查 Filter Graph 里有没有成功加入视频渲染器可以在代码里临时枚举 FG 里所有 filter 名称输出到 memo。如果只看到采集设备但没有 Video Renderer说明 RenderStream 或手动连接 pin 的代码少了东西。检查视频格式是否兼容把分辨率降到 320x240、帧率降到 15fps 试试有些老设备在 640x480 下格式协商会失败。DSPACK 时代的老采集卡尤其常见。检查窗口句柄TDSVideoWindow 的Owner属性必须指向一个有效窗体且窗体的句柄已经创建。如果你在 FormCreate 里就设置 Visible : True有时句柄还没就绪画面会莫名其妙地黑。放在 FormShow 里处理更稳。检查系统里是否有别的程序占用摄像头Windows XP 时代没这个讲究但 Win7 之后有些驱动只允许单进程独占DSPACK 的程序表现为设备枚举成功但画面不动。5.3 没声音的几种常见原因如果你在做音视频采集只出了图像没声音先分清楚是采集环节还是播放环节的问题。DSPACK 的音视频常见坑是 RenderStream 时只接了视频流忘了渲染音频流。正确做法是对PIN_CATEGORY_CAPTURE的音频设备也做一次 RenderStream或者直接用ICaptureGraphBuilder2的 RenderStream 同时渲染 audio 和 video。还有一个容易被忽视的问题很多老摄像头自带的麦克风设备不是标准的 DirectShow capture device而是 WDM 驱动DSPACK 的枚举函数不一定列得出来。这时需要手动用CLSID_AudioInputDeviceCategory枚举音频设备别依赖视频设备列表。5.4 过滤器注册不上的处理有些解码器比如老版本的 Xvid、DivX安装时没有注册成 DirectShow FilterDSPACK 程序运行时会提示找不到 filter。处理办法是用命令行注册regsvr32.exe xvid.ax regsvr32.exe divxdec.ax注册成功会弹窗提示。如果你是在部署给客户的机器上遇到这个问题记得把注册命令写进安装脚本省得售后到处打电话问。Win7 以上系统记得用管理员权限运行 regsvr32。5.5 内存释放的一大坑DSPACK 的组件释放顺序非常敏感。先释放 graph 再释放关联的 filter否则容易出现 Access Violation。我在程序退出时统一用FG.Active : False停掉整个 graph然后FG.Free最后才释放 SampleGrabber。如果你在代码里创建了多个 TFilter自己管理生命周期时务必按先 graph后子 filter的顺序释放否则可能把 TDSVideoWindow 挂到已释放的 filter 上程序一退出就崩溃。6. 结合老 Delphi 生态视频之外的真实业务场景聊到这里估计不少读者已经在想这东西除了拿来看摄像头还能干啥我翻了下这两年 Delphi 社区里被反复提问的内容发现 DSPACK 经常会和几个完全不相干的词混在一起出现比如读取 Excel禁用 U 盘CAN 口编程。一开始我也觉得奇怪后来做项目多了才明白这不是乱搭配而是视频处理工具在实际项目里总要跟周边系统打交道。6.1 视频检测结果自动导出 Excel很多工控项目用 DSPACK 做的是表面瑕疵检测或自动扫码识别抓到的帧经过图像处理后需要把检测结果存进 Excel 报表。这时候你不需要复杂的 OLE 自动化直接在抓到帧后把结果数据用老 Delphi 里常用方式写入 Excel 即可。DSPACK 负责看Excel 组件负责记两个库各管一摊中间用业务代码衔接。我的做法是把抓帧回调里获得的检测时间和结果缓存到内存队列设置一个定时器批量写 Excel避免频繁打开文件导致界面卡顿。6.2 配合 SendMessage 做局域网消息通知还有一类场景是视频监控终端需要把异常报警发给局域网里的其他程序。DSPACK 抓帧后如果算法判断有异常可以直接用 SendMessage 给另一个程序发消息。这个思路被问得很多因为很多老项目没有引入消息中间件两台机器的上位机之间通过窗口消息通信是最省事的方案。注意一点SendMessage 是阻塞的如果接收方响应慢你自己程序的画面会卡更稳妥的做法是用 PostMessage 发异步消息。6.3 和 CAN 总线、U 盘管控这类工控需求的关系工厂里有不少上位机同时接摄像头和工业设备比如视觉定位设备后面通过 CAN 总线给机械臂发坐标。DSPACK 负责视觉部分CAN 通信库负责设备控制两者是并行关系。至于禁用 U 盘这类需求其实是同一个项目里要做外设管控跟 DSPACK 没有直接关系。这个组合出现频率高说明一个问题老 Delphi 项目往往是一个多面手系统DSPACK 只是其中一块拼图。你在搜索资料时不要把思路局限在视频制作上多想想它怎么跟现有业务链路配合才能真正发挥价值。我在实际使用中比较深的体会是DSPACK 2.3.4 这套库的学习曲线不在组件用法上而在 DirectShow 的管道思维上。只要想通数据从哪来、经过谁、显示到哪这个逻辑大部分功能都能做出来。再加上老 Delphi 项目本身有一套自己的运行环境装好驱动、配好路径、按固定顺序释放资源它就能老老实实地跑上很多年。这份稳定就是老代码最大的价值。本文还有配套的精品资源点击获取
返回列表