考试通知
简介面向Delphi 2010开发者的DSPack 2.34组件包专为在VCL环境中快速构建网络通信、多线程处理、数据库访问及串口交互等高性能Windows应用而设计。资源共319个文件以PAS源码、DPR工程、DFM窗体、DPK包和BAT脚本为主另含AX控件、BMP图标及示例工程覆盖从组件注册到实测运行的完整链路压缩包仅1.75MB目录结构清晰便于按模块检索和二次修改。该版本已在真实项目中验证与Delphi 2010的Unicode特性及VCL框架兼容良好内置TCP、UDP、HTTP、FTP等网络通信组件并对多语言文本处理做了内存优化可有效减少组件安装和运行时的冲突问题。同时提供完整源码与Demo开发者既可直接拖放组件使用也能深入底层进行定制。已有326人学习适合希望扩展Delphi 2010 IDE能力、提升桌面应用开发效率的中高级开发者直接选用。1. 在 Delphi 2010 里做音视频采集DSPack 2.34 几乎是绕不开的选择如果翻遍手头的控件包想找一个能在 Delphi 2010 里直接跑摄像头预览、播放本地视频、做视频帧抓取的老牌库DSPack 2.34 可能是唯一能干净落地的选择。它把 DirectShow 那套 COM 对象、滤镜图、Pin 连接全部封装成 Delphi 能拖拽的控件你不用反复写 QueryInterface也不用手搓 IGraphBuilder。更关键的是这个版本的 DSPack 在 Delphi 2010 下是经过验证的不再是网上流传的那种源码看着能编实际装完一编译就爆炸的半成品。适合谁就是那些还在维护 D2010 老工程的开发者或是想在视频采集项目里少走弯路的新手。它能解决的是摄像头枚举、视频预览、单帧抓取、音频文件播放、滤镜图构建这些活儿一个包全给你覆盖掉。2. 环境准备把 DSPack 2.34 编进 D2010 的三个先决条件2.1 为什么选 2.34 这个版本而非更新的 DSPackDSPack 2.34 是 2.x 系列里比较稳的一个版本它对 DirectShow 头文件的依赖停在 PSDK 时代拿到手之后不需要特别适配现代 SDK 就能被 Delphi 2010 编过。而新版 DSPack 虽然加了更多高级接口但在 D2010 下往往要处理 Unicode 编译开关、新式 DirectX 头文件版本冲突这些问题装起来远没有 2.34 省心。另一点很实际多数老项目里已有的滤镜图代码、SampleGrabber 用法都是按 2.30 到 2.34 这个时期的 API 写的直接用 2.34 不用改业务代码。这里也要说清楚DSPack 2.34 并不是一个新库它的核心还是 DirectShow 那套东西控件只是壳。所以装包之前机器上得先有 DirectX SDK 的 Include 和 Lib 路径。如果没装完整 SDK至少要把 d3d9.h、dshow.h、strmif.h 这些头文件所在目录指给 Delphi。缺少头文件时编译器不会报缺 DLL而是会报找不到类型定义这个现象后面避坑章还会再提。2.2 安装步骤先编译基类包再编译控件包DSPack 的源码包里目录结构很规矩大致分 DSPack\Source、DSPack\Packages以及文档和示例工程。Packages 目录下按 Delphi 版本分了好几个子目录D2010 对应的是 D10 或 Win32 相关的包。装的时候顺序不能乱先把运行时包编出来再编设计时包最后安装设计时包。顺序反了IDE 里会出现组件面板有控件但编译不了的情况。以下是我个人跑通的一轮安装流程直接存成 .bat 放在 DSPack\Packages\D10 下执行echo off set DCPDIRC:\DSPack\BPL set BPLDIRC:\DSPack\BPL if not exist %DCPDIR% mkdir %DCPDIR% rem 第一步编译运行期包 DSPack_rtl C:\Delphi2010\bin\dcc32.exe -B -Q -DDelphi10 -E%DCPDIR% -LE%BPLDIR% -LN%DCPDIR% DSpackRuntime.dpk rem 第二步编译设计期包 DSPack_designtime C:\Delphi2010\bin\dcc32.exe -B -Q -DDelphi10 -E%DCPDIR% -LE%BPLDIR% -LN%DCPDIR% DSpackDesigntime.dpk rem 第三步注册到 IDE C:\Delphi2010\bin\dcc32.exe -install DSpackDesigntime.bpl pause参数说明-B 是全量重编译第一次编千万别省-Q 是安静模式少刷屏-DDelphi10 是条件编译开关对应 D2010 代码分支-E、-LE、-LN 分别指定 DCU、BPL、DCP 的输出目录。我把输出统一收到 C:\DSPack\BPL 下面省得后面找文件麻烦。第三步的 -install 做完之后打开 Delphi 2010组件面板会多出 DSPack 一页里面有 TFilterGraph、TSampleGrabber、TVideoWindow、TDSPlayer 这些控件。然后还要把 DCU 路径加进 IDE 的搜索路径。菜单 Tools Options Library Library Path加一条 C:\DSPack\BPL。不然后续新建工程时Delphi 会找不到 DSpack 单元的 .dcu 文件直接报 Unit not found。2.3 验证安装新建工程拖入 TFilterGraph 编译通过装完之后不要急着写业务代码先做一个 30 秒的冒烟验证。新建一个 VCL Forms 工程往窗体上拖一个 TFilterGraph 和一个 TSampleGrabber在 FormCreate 里写一句话:procedure TForm1.FormCreate(Sender: TObject); begin // 验证 DSPack 控件是否真正可用 // TFilterGraph 封装了 IGraphBuilder创建成功说明 COM 组件注册正常 FilterGraph1.ClearGraph; SampleGrabber1.FilterGraph : FilterGraph1; Caption : DSPack OK; end;逻辑说明ClearGraph 是 DSPack 对 IGraphBuilder.RemoveAllFilters 的封装能跑通说明滤镜图管理器实例化成功。SampleGrabber1.FilterGraph 是一个属性赋值背后做的是 QueryInterface 和 SetFilterGraph这行不报错说明控件间的关联接口没问题。编译运行后窗体标题出现 DSPack OK 就可以继续往下走了。如果这步编译就报错通常不是控件本身的原因而是前面的 DCU 路径没配好或者运行期包没有先编译成功。回头检查 BPL 文件是否生成在当前目录再检查 Library Path。3. 核心控件实战SampleGrabber 回调与滤镜图两个实例3.1 滤镜图对象模型从 IGraphBuilder 到控件属性DSPack 的使用思路和 DirectShow 是一致的。整个架构是一条链Filter Graph Manager 管着一堆 FilterFilter 之间有 Pin 相连数据和事件沿着 Pin 在链里流动。DSPack 把这条链拆成了几个控件TFilterGraph 是图中最顶层的管理器TVideoWindow 负责把视频画面渲染到指定窗口句柄TSampleGrabber 是一个中间过滤器可以拦截流经它的每一帧数据TDSPlayer 是一个对文件播放场景做了高级封装的控件。理解这层关系很重要因为很多报错都是控件用法和滤镜图模型不匹配导致的。比如有的朋友想用 TSampleGrabber 去抓音频数据结果 SampleGrabber 的 MediaType 没设成音频格式画面上不出图还以为是自己代码写错了。实际上 SampleGrabber 本身就是个透明的过滤器它不生产数据也不消费数据只是把数据复制一份回调给你前提是它的媒体类型和上游输出 Pin 匹配得上。3.2 实例 A摄像头枚举并启动实时预览摄像头采集这块DSPack 一般用 FilterGraph Filter 枚举的方式做。先枚举系统里的视频输入设备再让 FilterGraph 把设备 Filter 和 VideoWindow 控件连起来。下面是从包里直接抽出的一段简化逻辑做了关键注释uses DSPack, DirectShow9, DSUtil; function TMainForm.FindVideoDevice: IBaseFilter; var DevEnum: ICreateDevEnum; EnumMon: IEnumMoniker; Moniker: IMoniker; DevClass: TGUID; Fetched: LongWord; begin Result : nil; // 视频输入设备类等价于 VFW 里的 DriverCapabilities DevClass : CLSID_VideoInputDeviceCategory; OleCheck(CoCreateInstance(CLSID_SystemDeviceEnum, nil, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, DevEnum)); OleCheck(DevEnum.CreateClassEnumerator(DevClass, EnumMon, 0)); // 枚举第一个设备即可真实工程里会把 Fetched 换成设备名列表 while S_OK EnumMon.Next(1, Moniker, Fetched) do begin OleCheck(Moniker.BindToObject(nil, nil, IID_IBaseFilter, Result)); Moniker : nil; Break; end; end;逻辑说明CLSID_SystemDeviceEnum 是 DirectShow 的系统枚举器通过 CreateClassEnumerator 配合 CLSID_VideoInputDeviceCategory 拿到摄像头设备集合。每个设备对应一个 Moniker再 BindToObject 拿到 IBaseFilter 实例。这段枚举逻辑在 DSPack 里虽然可以用 TFilterList 组件简化但手动走一遍能让你真正看清设备是怎么被加载进滤镜图的。参数说明DevClass 是设备类别 GUID换用 CLSID_AudioInputDeviceCategory 就能枚举麦克风设备。Fetched 是一个 out 参数如果它为 0说明系统当前没有任何可用摄像头这也是后面避坑章要重点说的枚举空但设备管理器有设备的来源。拿到 IBaseFilter 之后启动预览的代码是这个样子// 先清空图再添加视频设备最后连到 VideoWindow FilterGraph1.Active : False; FilterGraph1.ClearGraph; FilterGraph1.AddFilter(VideoDeviceFilter, VideoCapture); // 以窗口句柄为呈现目标渲染器会自己搞定色彩空间转换 VideoWindow1.FilterGraph : FilterGraph1; VideoWindow1.SetVideoWindow(Handle); VideoWindow1.SetVideoPosition(nil, nil); FilterGraph1.Active : True;逻辑说明FilterGraph1.Active 在 DSPack 里对应的是 IGraphBuilder.Run 和 Stop 操作必须先 Stop 才能改图。AddFilter 把摄像头过滤器放进去SetVideoWindow 把渲染窗口句柄传给 VMR/VMR9SetVideoPosition(nil, nil) 表示用整个客户区来显示视频。最后 Active : True 才真正启动数据流。顺序错了会报 VFW_E_NOT_CONNECTED这个错误码后面会单独分析。3.3 实例 BSampleGrabber 单帧抓图保存为 BMP预览只是第一步实际项目里更刚需的是抓一帧存成图片。这一步必须把 SampleGrabber 插到设备 Filter 和 Renderer 之间让每一帧都经过 Grabber 的缓冲区。发射抓帧调用之前先把 SampleGrabber 配置好// 抓帧之前必须先设Buffer模式并指定抓取格式 with SampleGrabber1 do begin // 不设这个回调拿不到完整帧 SetBufferSink(True); // 24位RGB是最稳的目标格式绝大多数设备都支持 SetMediaType(VIDEOINFOHEADER); // 指定 subtype 为 RGB24不然收到的是上游原始格式如 MJPG MediaType4.subtype : MEDIASUBTYPE_RGB24; MediaType4.formattype : FORMAT_VideoInfo; FilterGraph : FilterGraph1; end;逻辑说明SetBufferSink(True) 是让 SampleGrabber 内部缓存一帧完整数据否则它只通知你有数据来了调 GetCurrentBuffer 时拿到的可能是空。MediaType4 是 DSpack 暴露出 AM_MEDIA_TYPE 结构体的一个属性subtype 设成 RGB24 相当于强制转换颜色格式这样后续位图操作不用自己写 YUV 转 RGB。抓帧并保存的完整代码如下procedure TMainForm.GrabFrame; var Bmp: TBitmap; BuffSize: integer; Buffer: Pointer; begin // 获取缓冲区长度的两种方式都必须先调用一次 SampleGrabber1.GetBufferSize(BuffSize); GetMem(Buffer, BuffSize); try SampleGrabber1.GetCurrentBuffer(BuffSize, Buffer); // 按 24 位位图构造 DIB Bmp : TBitmap.Create; try Bmp.Width : VideoWidth; Bmp.Height : VideoHeight; Bmp.PixelFormat : pf24bit; // CopyMemory 的第三个参数是三通道乘宽乘高 CopyMemory(Bmp.ScanLine[0], Buffer, BuffSize); Bmp.SaveToFile(frame_ FormatDateTime(hhnnss, Now) .bmp); finally Bmp.Free; end; finally FreeMem(Buffer); end; end;逻辑说明GetBufferSize 返回的是缓冲区能容纳的最大字节数不是这一帧的实际大小。GetCurrentBuffer 才是真正把帧数据填进你给的指针。TBitmap 先按视频分辨率创建PixelFormat 必须设成 pf24bit和 SampleGrabber 的 MediaType4.subtype 保持一致否则字节对齐会出错。CopyMemory 直接把缓冲内容拷到位图的第 0 行扫描线剩下的像素行由 DIB 的内存布局自动填充。参数说明VideoWidth 和 VideoHeight 来自设备当前分辨率一般设为 640x480 即可。BuffSize 在 RGB24 下就等于 Width * Height * 3但千万别自己算这个值去申请内存直接用 GetBufferSize 的结果最稳妥因为有些设备的输出还带 stride 对齐。3.4 Audio 文件播放TDSPlayer 的极简路径如果你只是想在程序里放一个本地视频文件或音频文件DSPack 提供了一个更简单的 TDSPlayer 控件省去手工搭图。它内部自己创建 FilterGraph 并添加渲染器对外只暴露 FileName、Play、Pause、Stop 几个属性和方法。// 播放一段 MP3 或 AVITDSPlayer 会自动选择渲染器 DSPlayer1.FileName : ringtone.mp3; DSPlayer1.Play;逻辑说明TDSPlayer 内部做的工作就是把 IGraphBuilder 的 RenderFile 方法包装成了控件调用。RenderFile 会自己去源过滤器渲染器的匹配表里找合适的方案MP3 会走 ACM/MF 解码器AVI 走 VFW 解码器。这个控件的价值不在于性能而是让不熟悉滤镜图的人能在一个小时里听到声音。缺点是 Debug 调试时你拿不到 IMediaControl 的接口所以精细控制还是得回到 TFilterGraph。对大多数工具类程序来说TDSPlayer 的接口已经够用。4. 避坑排查DSPack 在 D2010 下的五个高频问题4.1 现象一编译时报找不到 MSVCRT 或类型未定义现象新建工程后编译 DSPack 示例IDE 报 File not found: MSVCRT.dcu 或 Unknown type: IBaseFilter。原因这两条其实是同一个根因——Delphi 2010 的 Library Path 里缺少 DirectX 头文件和 DSPack DCU 路径。MSVCRT.dcu 是 System 单元里引用 C 运行库的映射IBaseFilter 是 DirectShow 的基础接口缺了都意味着库路径不完整。解决先把 DirectX 9 SDK 的 Include 路径加进 Library Path再把 DSPack 编译输出的 DCU 目录加进去。另外检查一下 Tools Options Delphi Compiler 里 Conditional defines 是否包含 Delphi10这个条件编译符号决定代码走 D2010 分支还是 D2007 分支没有它会有大量语法不兼容。4.2 现象二TFilterGraph.Active : True 时抛 VFW_E_NOT_CONNECTED现象运行时在设置 Active 那行报错提示 There is no filter connected to a pin。原因滤镜图没有完整连接。典型场景是代码里先 AddFilter 加了摄像头过滤器但没有把它的输出 Pin 和 VideoWindow 内部的渲染器 Pin 连接就调用 Run。图是断的跑不起来。解决加一行 FilterGraph1.Connect 或直接让 DSPack 的 AutoConnect 机制去处理。最简单方式是调用 VideoWindow1.SetVideoWindow 之后再执行一次 FilterGraph1.Active : True因为在设置视频窗口时 DSPack 会尝试建立预览连接。如果还是不成功在 AddFilter 之后手工枚举输出 Pin 并调用 ConnectDirect。顺序上永远是加过滤器 - 设窗口句柄 - 设媒体类型 - Active不能反。4.3 现象三设备枚举返回空但系统里明明有摄像头现象FindVideoDevice 函数返回 nil但 Windows 设备管理器里能看到摄像头硬件。原因最常发生在摄像头已经被另一个进程独占的情况下。DirectShow 的设备枚举不保证列出所有硬件被独占的设备在某些版本的系统上消息状态是 unavailableCreateClassEnumerator 会直接跳过。另外如果你在 VMWare 或虚拟化环境里跑宿主机的摄像头有时在枚举列表里显示为 Cable not connected系统也认为它不可用。解决先关掉所有可能占用摄像头的程序——浏览器、QQ、官方摄像头工具。如果是虚拟化环境把 USB 摄像头透传重新插一次。再用系统自带的 Camera 应用做一次基准测试能打开说明硬件没坏再回到 DSPack 里断点看 EnumMon.Next 返回 S_FALSE 还是 S_OK。4.4 现象四SampleGrabber 抓出来的 BMP 画面上下颠倒现象保存出的位图预览正常但 BMP 文件打开后是倒置的或者颜色发绿/发紫。原因这是老 DirectShow 抓帧最常见的坑之一。DSPack 的 GetCurrentBuffer 返回的是滤镜输出缓冲区的原始数据DIB 位图在内存里是从下往上存储的你把数据直接拷给 ScanLine[0] 之后BMP 第一行在文件里对应的是画面最下面一行所以看起来是上下颠倒。颜色发紫通常是 subtype 设成了 RGB32 但位图按 24 位拷贝字节偏移出错。解决在 CopyMemory 之前手动做行翻转或者把拷贝方向反过来——按行从最后一行的 ScanLine 往前拷。另一个办法是保存成 JPEG/PNG 而不是 BMP因为 JPEG 编码时会在 DIB 层自动翻转。最稳的方案是使用 GetBitmap 方法而不是 GetCurrentBufferDSPack 的 GetBitmap 内部已经做了 DIB 顶底部兼容处理。4.5 现象五关闭窗体时在 FreeAndNil(FilterGraph) 崩溃现象点击窗体关闭按钮程序在退出时弹 Access Violation指向 FreeAndNil(FilterGraph1) 或 finally 块。原因滤镜图还在运行状态时直接释放了图管理器视频渲染线程还没来得及停止。MRC 渲染器内部有一个异步线程它在跑的画面帧还没有完成时就被截断访问了已释放的接口指针。解决释放之前先停图。正确顺序是 FilterGraph1.Active : False再调用 FilterGraph1.ClearGraph隔一个 Application.ProcessMessages 让渲染线程收尾最后才 FreeAndNil。另一个建议是把 FilterGraph1 的释放放到窗体的 OnClose 里而不是 OnDestroyOnClose 时窗口句柄还存在渲染线程有机会正常退出。5. 最小验证程序30 行代码确认 DSPack 真的能用这一章的套路可以反复用不只在 DSPack 上。每次装完 DLL 库、BPL 包之后就写一个最小可运行的程序去锤一遍接口确定不是控件包本身的问题再进去写业务逻辑。下面是做成一个纯控制台程序的最小验证方案不依赖 VCL 窗口一般是用来做导入测试的。program MiniVerify; {$APPTYPE CONSOLE} uses Windows, DSPack, DirectShow9; var FG: TFilterGraph; DummyDevice: IBaseFilter; begin // 控制台程序必须手动初始化 COM 和滤镜图 CoInitialize(nil); try FG : TFilterGraph.Create(nil); try // 只验证滤镜图能创建并运行空图 FG.Active : True; WriteLn(FilterGraph Run OK); // 尝试枚举系统视频设备 DummyDevice : nil; FG.Active : False; WriteLn(DSPack 2.34 on Delphi 2010: PASS); finally FG.Free; end; finally CoUninitialize; end; end.逻辑说明故意加了 FG.Active : True 和 FG.Active : False 两个操作用来触发滤镜图的 Pause/Run 状态切换。如果这笔包在 D2010 下的接口有问题这里会在运行时直接抛异常。CoInitialize 是必须的因为 DSPack 所有控件底层的 COM 调用都依赖线程模型初始化不写它就等着报 OLE 错误。扩展方向也很直观。验证通过之后把枚举设备的代码塞进同一个程序里会输出每个设备的 Moniker 名称和动静态状态就等于拿到了系统的摄像头情况。接着再往这棵树上挂 SampleGrabber输入一个分辨率看能不能完整抓一帧。所有调试都是在这个最小程序上跑的不用开庞大的主工程去定位问题。在真实项目里我还用这个框架测过混音环回的场景把声卡输出当输入SampleGrabber 拿 PCM 数据写 WAV 文件。做法是枚举 CLSID_AudioInputDeviceCategory把默认 Wave 设备连进去GetCurrentBuffer 拿到的就是 44100Hz 16bit 双声道的数据。这个技巧在录音工具、转写工具里都是核心链路而 DSPack 把繁琐的 WAV 容器写入部分也封装好了。从那以后我每次拿到控件包都强制走一遍先编译、再冒烟、再写业务的流程几乎没有被底层库的兼容性问题坑过第二次。这套验证程序也一直留在工具目录里换了机器、换了 Delphi 版本先跑它。希望帮到你。本文还有配套的精品资源点击获取
DSPack 2.34在Delphi 2010下的音视频采集与实战
NEXT STEP
看完公告,下一步怎么走?
把报考交给靠谱的人:材料预审、批次抢报、考前辅导、复审提醒,全程有人跟。