ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

DSPack 2.34 在 Delphi 2010 老工程中的视频播放与截帧实战

DSPack 2.34 在 Delphi 2010 老工程中的视频播放与截帧实战 简介DSPack 2.34 是面向 Delphi 2010 开发者的组件包长期活跃于 Delphi 社区用于扩展 IDE 能力简化网络通信TCP/UDP/HTTP/FTP、多线程TThread、数据库访问ODBC/ADO和串口控制等复杂功能的实现该版本已经过实际项目测试兼容性有保障适合需要快速构建高性能 Windows 应用的系统开发者。资源压缩包体积轻量仅 1.75MB共 319 个文件主体由 95 个 PAS 源代码、40 个 DPR 工程文件、39 个 DFM 窗体文件组成另有批处理脚本、DCU 编译单元和少量图片资源可支撑组件安装、示例运行与二次开发目录结构便于按需检索。目前已有 327 人浏览学习。包内附带可直接运行的示例工程覆盖网络、数据库、串口与多媒体等常见场景可参考组件的调用方式和调试流程同时提供注册与清理脚本便于快速安装和测试组件环境。将压缩包导入 Delphi 2010 后可直接拖拽使用网络、串口和多媒体组件大幅减少底层封装工作量。1. Delphi 2010 还能用 DSPack 2.34老工程视频方案的最后一根稻草前阵子接手一个停留在 Delphi 2010 的老监控系统要在不升级 IDE、不引入大型框架的前提下加入视频回放和单帧抓取。考察了一圈组件新库不是依赖 FireMonkey 就是早放弃 Win32直到翻出 DSPack 2.34 这版仍在维护周期内兼容 Delphi 2010 的 DirectShow 封装库重新编译、装组件、跑通播放和截帧整个过程验证下来完全可用。它解决的问题非常具体在 32 位进程里用 DirectShow 做播放、采集、抓帧不需要换编译环境不需要额外 runtime特别适合还在维护老 Delphi 工程、或必须锁定 IDE 版本的视频开发场景。下面按我实测的完整流程展开每一步尽量写清操作和参数。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 则是插在管线中间的抓帧器可以在视频数据流过时拿到原始样本。它们三者协作关系类似一条流水线源 Filter 读文件解码 Filter 做格式变换渲染 Filter 把画面输出到窗口。TFilterGraph 把节点的连接逻辑隐藏起来开发者只需调 RenderFile 或 RenderGraph它内部会根据媒体类型去系统注册表搜索能连接的 filter。我要强调一个容易被绕晕的边界TFilterGraph 管全图TFilter 只管单个节点。一个图里可以有多个 TFilter 实例但图管理器只有一个 TFilterGraph 实例。长期用下来我的习惯是标准播放场景无脑用 TFilterGraph 的自动连接一旦涉及非标准格式或需要自定义解码顺序才手动创建 TFilter 实例、指定输入输出 Pin。DSPack 把控制权留给了开发者但大多数项目根本走不到手动拉线那一步。2.2 DirectShow 处理链条源、变换、渲染三层再展开讲一下这个管线模型方便后面代码落地。完整的 Filter Graph 分三层源 Filter 读取文件、摄像头或网络流输出压缩或未压缩的媒体样本变换 Filter 完成解码、格式转换DSPack 自带一批 transform filter也能用系统里注册的第三方解码器渲染 Filter 把媒体数据送到显卡或声卡视频走 Video Renderer / VMR7 / VMR9音频走 Audio Renderer。每个 filter 的输入输出 Pin 之间要协商媒体类型比如视频的 RGB24、YUY2音频的 PCM以及压缩流的 subtype GUID。协商失败时 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。两边在接口边界上一错位就出现了我前面说的中文路径失败、文件找不到这类问题。具体现象是调 RenderFile 传含中文或特殊字符的路径时DirectShow 返回 VFW_E_NOT_FOUND 或 VFW_E_CANNOT_CONNECT哪怕文件确实存在。我实测发现直接用 WideString 显式声明再传指针就行不要让 Delphi 在边界处隐式转换 ANSI。var Path: WideString; begin Path : C:\测试视频\sample.avi; FilterGraph1.RenderFile(PWideChar(Path)); end;逻辑说明这里先把路径声明为 WideString再取 PWideChar 指针传给 RenderFile确保 DirectShow 收到的是宽字符不经过任何 ANSI 转换。参数说明RenderFile 是 TFilterGraph 的自动建图入口传完整路径即可。路径存在但含中文时用这个写法能绕开大部分编码问题如果路径不含中文普通字符串直接传也问题不大。2.4 为什么选 2.34 而不是其他版本对比过几条路线顺便说明选型理由。Delphi 2010 自带的 TMediaPlayer 走 MCI 播放单纯放个 AVI 没问题但截帧、摄像头采集、自定义媒体源完全用不上。FFmpeg 的 Delphi 封装性能好但要引入额外 DLL 和线程模型管理对一个几万行的老 VCL 工程来说太重。DSPack 2.34 则是完整的 DirectShow 封装播放、截帧、采集都能在同一个 COM 模型下处理API 风格统一。方案播放截帧采集Delphi 2010 原生编译外部依赖DSPack 2.34支持支持支持是无TMediaPlayer支持否否是MCI 系统组件FFmpeg 封装支持支持支持需第三方动态库上面表格里能直观看到唯一同时满足播放、截帧、采集且无动态库依赖的就是 DSPack 2.34。它最后一代完整支持 Delphi 2009/2010 的包文件是 2.34这也是我锁定这个版本的理由。3. 安装配置从源码包到 Component 菜单的完整流程3.1 装之前先确认环境源码包拿到手先别急着点安装花几分钟确认三件事机器上装的是 Delphi 2010 Update 5 或更高版本系统至少是 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 页面就会出现在组件面板上。cd /d %~dp0 regsvr32 /s filter\*.ax echo DSPack filters registered.逻辑说明这是一段批量注册脚本把 DSPack 自带的 DirectShow filter.ax 文件通常放在 filter 子目录注册到系统。Windows 7 及以上系统建议用管理员权限打开命令行再执行否则注册会静默失败。参数说明/s 表示静默模式不弹出成功提示/u 可用来反注册。如果机器是 64 位系统且 IDE 是 32 位DSPack 的 .ax 组件通常注册到 SysWOW64 目录即可不需要跑到 System32 里。包文件全部编译完成后还有一步必须做把 DSPack 的 source 目录加入 IDE 的 Library 搜索路径。打开 IDE 工具 → 选项 → 库路径追加 source 目录路径。这一步漏掉的话新建工程引用 DSPack 单元时Delphi 会提示找不到 DSUtil.dcu。3.3 安装后的环境检查清单装完别急着写业务代码先跑一遍自带 Demo我踩过的坑里有一半出在没验证基础环境。打开 samples 下的视频播放示例工程编译运行后传入一个现有 AVI 文件路径。能正常播放并拖动进度条说明三条链路都没问题第一个是 IDE 集成说明包文件安装正确第二个是 DirectShow 基本链路说明系统里渲染器和解码器可用第三个是 filter 注册状态说明 Graph 构建能正常匹配。检查工程平台选项也在此列。Delphi 2010 支持 Win32 和 Win64 目标但 DSPack 2.34 的源码只面向 32 位平台。在 Project → Options → Delphi Compiler 里确认 Target 是 32-bit Windows这点我在 Win64 目标下翻过车后面避坑章节详细写。还有一个容易被忽略的检查项确认系统里已注册的 DirectShow 解码器是否干扰 RenderGraph。可以用系统自带的 GraphEdit 工具打开同一个 AVI 查看图结构如果自动匹配拉到第三方解码器或奇怪的变换 filter播放可能卡顿或黑屏。这时候就要走手动建图的路线而不是继续依赖自动连接。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 开始播放。参数说明RenderFile 接受完整文件路径路径含中文或空格时按第 2 章里 WideString 的写法传参更稳。VideoWindow1.Owner 必须是已创建好的窗口化控件一般用 Panel不能画到普通 Label 上。Visible 必须设为 True否则视频画面不显示但声音会正常播放这是个容易误判的现象。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 通过这个名字定位。参数说明SetMediaType 必须设置否则 Grabber 不知道输出什么媒体类型回调不会触发VIDEOINFOHEADER 是最通用的设置包含画面尺寸、颜色格式和帧率。ConnectPins 的参数是滤波器名和 Pin 名实际名称可能因 filter 的实现而不同调试时用 GraphEdit 查看准确的 Pin 名再填。接完后实测发现Grabber 插入后如果媒体类型不匹配DirectShow 可能自动插入转换 filter播放仍正常但没有回调。这种情况下要把 Grabber 的输出类型设成和下游一致或者干脆在 Grabber 之后手动连接 Renderer。4.3 参数调整播放窗口、音量与音视频同步TFilterGraph 上有几个高频参数值得专门说明。AutoMon 属性控制是否按视频原始尺寸自动调整播放窗口设为 False 时画面会适应 Owner 控件尺寸设为 True 时可能撑爆或截断画面。Volume 设置音频音量范围是 -10000 到 0单位是百分之一分贝这里最容易踩坑很多人传正数发现没声音其实必须用负数。Balance 控制左右声道平衡-10000 全左10000 全右。播放定位时用 Seek 方法进度参数基于 TimeFormat。默认 TimeFormat 是 TIME_FORMAT_MEDIA_TIME单位是 100 纳秒配合 GetDuration 获取总时长可以精确换算进度条比例。音视频同步问题我也遇到过文件较大时画面和声音不同步常见原因是系统默认渲染器在 VMR9 模式下需要更精确的时钟源或者是显卡驱动渲染队列缓冲太大。处理方式有两个一是把 TFilterGraph 的 SyncMode 设为 smClock强制使用系统时钟二是把 VMR 的 CompositionMode 打开让渲染器自己做合成减少画面延迟。5. 避坑记录Delphi 2010 调 DSPack 常见问题与排查5.1 编译报错Cannot find unit DSUtil.dcu现象新建工程后引用 DSPack 单元编译提示找不到 DSUtil.dcu但安装阶段明明没报错。原因source 目录没有加入 IDE 的 Library 搜索路径或者 runtime 包编译产物没有生成到默认输出目录。解决回到安装章节把 source 目录追加到工具 → 选项 → 库路径然后重新编译 DSPACK_D2010.DPK确认 DCU 文件确实生成。检查 Search Path 里有没有残留旧版本的 DSUtil.pas有就清掉避免编译时取到旧文件。5.2 视频窗口黑屏但有声音现象播放能听到声音视频窗口里全是黑色线程没有卡死。原因VideoWindow1.Owner 指向的 Panel 尚未创建或者在窗口初始化之前就调用了 Play。VMR 模式下渲染器对窗口句柄的创建时机非常敏感窗体还没显示时绑定窗口画面就丢了。解决保证在 FormShow 之后再创建绑定不要把绑定逻辑放在 FormCreate 里。绑定后显式设置 VideoWindow1.Visible : True并确认 Panel 的 DoubleBuffered 属性已在窗体显示前打开否则画面可能闪烁或不出图。5.3 中文路径播放失败现象RenderFile 传含中文的文件路径时返回 VFW_E_NOT_FOUND换成纯英文路径就正常。原因Delphi 2010 的字符串在边界处被转成 ANSIDirectShow 期望的是宽字符。DSPack 2.34 源码内部对 PChar 的处理没有完全适配 Unicode 字符串模型路径一旦出 ASCII 范围就乱码。解决传参时把路径声明成 WideString用 PWideChar 传进 RenderFile。如果路径中既有中文又有空格避免手写拼接用 ExtractFilePath 和 ExtractFileName 组合后再转 WideString减少中间层转换。5.4 64 位编译直接翻车现象把 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 收不到回调现象Grabber 加进图中播放正常但回调函数一次也不触发。原因没设置媒体类型或 Grabber 的 BufferSize 太小回调被丢弃或者设置了 OneShot 模式后只在第一帧回调一次后续全无。解决先检查 Grabber.SetMediaType 是否传入 VIDEOINFOHEADER再确认 BufferSize 至少能容纳一帧 RGB24 数据。调试时先把 OneShot 设为 True 验证单帧回调通过后再改成连续模式。5.6 音频采集没有声音输出现象程序里 TAudioCapture 能采集到波形数据但外放没有声音。原因声卡驱动默认输出格式和 Audio Renderer 协商失败或者采集链路到渲染器之间走了错误的 filter 路径。解决在采集源和渲染器之间加一个 SampleGrabber 做格式中转或者手动指定音频渲染器的输出格式为 PCM。部分主板驱动默认开启独占模式需要在声卡控制面板里关闭应用程序独占控制否则 DSPack 拿不到混音数据。6. 进阶SampleGrabber 截帧与 Buffer 回调缓冲实战SampleGrabber 的 Buffer 回调是做视频抓帧最直接的手段。先实现一个回调类继承自 TInterfacedObject 并实现 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 字节的 RGB24 数据写入文件 Result : S_OK; end;逻辑说明BufferCB 在每一帧经过 Grabber 时被调用pBuffer 指向一帧原始像素数据Len 是缓冲长度Time 是媒体时间戳。SampleCB 在样本级触发不需要处理媒体类型时就避开它BufferCB 更可控。参数说明回调注册需要调用 Grabber.SetCallBack(TFrameCallback.Create, 0)。第二个参数为 0 表示只回调 BufferCB为 1 表示回调 SampleCB。回调类注意保持引用有效不能让对象在回调触发前被释放否则直接访问非法内存。实际操作时把回调里拿到的 Buffer 根据当前视频分辨率计算出帧大小校验 Len 是否等于 Width × Height × 3不对就丢弃。这个校验帮我排掉过不完整帧某次项目里没校验抓出来的相册库里混了一堆半截图从那以后我每次在回调里都强制校验帧尺寸和宽度高度走一帧验证一帧再入库。希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表