简介:TVideoGrabber.v6.7.5 是一款面向 Delphi7 与 C++Builder4/5/6 开发者的视频处理控件,适合需要在桌面端快速集成视频捕捉、压缩、录制与回放能力的中高级多媒体应用开发者。资源包共 86 个文件,约 1.2MB,包含 10 个 pas 与 10 个 cpp 源码、10 个 h 与 3 个 hpp 头文件、10 个 dfm 窗体文件、8 个 dcu 与 4 个 dcp 编译单元,以及 7 个 bpl 运行包和 3 个 lib 库文件,另附 MainDemo 演示程序、使用说明与帮助文档,覆盖 Delphi4 至 Delphi7 及多个 CBuilder 版本。控件内置 MPEG-4、H.264 等编码器,支持摄像头、电视卡等视频源,可灵活调整帧率与分辨率,并将视频保存为 AVI、MP4、WMV 等格式。包内示例涵盖运行时创建、多摄像头、帧捕获、电视调谐与画质控制等场景,便于理解 API 与事件机制。已有 449 人学习下载,适合作为多媒体项目二次开发与功能扩展的参考。
1. 视频采集卡在 Delphi7 里跑不起来:TVideoGrabber 6.7.5 到底解决了什么
接手一个老工控上位机维护项目时,最常遇到的场景是:现场一台跑着 Windows XP 或 Win7 的机器,上面是 Delphi7 编译的采集软件,摄像头、采集卡、云台控制全挂在一个叫 TVideoGrabber 的控件上。这套组合在十年前是标配,现在源码还在,但换台新机器、换个新采集卡,画面就黑屏或者直接抛异常。TVideoGrabber.v6.7.5.For.Delphi7.BCB456 这个标题,说的就是这套控件在 Delphi7 和 C++Builder 4/5/6 时代的最后一个稳定分支。它不是一个新东西,而是一个被大量存量项目依赖的采集与录像组件,封装了 DirectShow、VFW 和部分 WDM 设备的调用细节,让写业务逻辑的人不用直接跟 COM 接口和 Filter Graph 打交道。适合谁?手上有一份 Delphi7 或 BCB 老工程、需要继续维护或迁移采集功能的人。这一章先把控件的能力边界和版本定位讲清楚,后面再动手。
2. 拆开 TVideoGrabber 的能力面:采集、预览、编码、录像四条线
2.1 控件在 Delphi7 里的安装与包结构
TVideoGrabber 在 Delphi7 下不是单个 pas 文件,而是一组运行时包加设计期包。常见做法是先把源码目录里的VideoGrabber.pas、VideoGrabberUtils.pas等单元加入一个新建的 Package,再编译出dclTVideoGrabber70.bpl这类设计期包。安装前要确认三件事:Delphi7 的 Library Path 里已经包含控件源码目录;系统里没有残留旧版本的 bpl;如果同时装了 BCB456,两个 IDE 的包输出目录要分开,否则会出现「组件面板能看到但拖到窗体就报类未注册」的玄学问题。
安装步骤可以按下面这个顺序走:
// 1. 新建 Package:File -> New -> Other -> Package // 2. 在 Contains 里 Add 以下单元(按实际源码文件名为准) // VideoGrabber.pas // VideoGrabberUtils.pas // VideoGrabberTypes.pas // 3. 编译 Runtime 包,再编译 Design 包 // 4. Component -> Install Packages -> Add,指向设计期 bpl // 5. 在 System 页或新增页里找到 TVideoGrabber 组件逻辑说明:Runtime 包负责实际采集逻辑,Design 包只负责把组件注册到 IDE 面板。参数上要注意,Delphi7 的包编译默认输出到$(DELPHI)\Projects\Bpl,如果这个目录没有写权限,编译会静默失败,组件面板不会报错但也不会出现新组件。失败时先看 IDE 的 Compile 输出窗口有没有「Cannot load package」字样,再检查系统 PATH 里是否混入了其他版本的 bpl。
2.2 采集源枚举与设备选择:别在运行时硬编码设备索引
TVideoGrabber 提供VideoDevices和AudioDevices两个集合属性,用来枚举当前系统里可用的采集设备。很多老代码直接写VideoDevice = 0,换一台机器就翻车,因为设备枚举顺序跟驱动安装顺序有关。稳妥做法是在窗体创建时遍历一遍,把设备名和索引存下来,让用户在下拉框里选。
procedure TFormMain.FormCreate(Sender: TObject); var I: Integer; begin cboVideo.Items.Clear; for I := 0 to VideoGrabber1.VideoDevices.Count - 1 do cboVideo.Items.Add(VideoGrabber1.VideoDevices[I].Name); if cboVideo.Items.Count > 0 then cboVideo.ItemIndex := 0; end; procedure TFormMain.cboVideoChange(Sender: TObject); begin VideoGrabber1.Stop; VideoGrabber1.VideoDevice := cboVideo.ItemIndex; VideoGrabber1.StartPreview; end;逻辑说明:VideoDevices[I].Name返回的是驱动层暴露的设备友好名,比索引稳定。参数上,VideoDevice必须在Stop状态下切换,运行中直接赋值在部分采集卡上会导致 Filter Graph 重建失败,表现为预览窗口卡死。如果枚举结果为空,先确认设备在系统「图像设备」里能看到,再检查控件是否以管理员权限运行——部分 WDM 驱动在低权限下不暴露设备节点。
2.3 预览模式与录像模式的参数差异
预览和录像走的是两条不同的 Pin 输出路径。预览默认走Preview模式,分辨率往往被驱动自动降到 640x480 以保证流畅;录像走Capture模式,才会按VideoWidth、VideoHeight和FrameRate去协商真实格式。常见坑是:预览看着正常,一按录像就报「无法设置媒体类型」,原因是采集卡不支持当前请求的编码格式组合。
// 录像前先设置采集格式,再启动录像 VideoGrabber1.Stop; VideoGrabber1.VideoWidth := 1920; VideoGrabber1.VideoHeight := 1080; VideoGrabber1.FrameRate := 25; VideoGrabber1.VideoCompressor := 'MJPEG Compressor'; // 按系统实际可用编码器填 VideoGrabber1.RecordingFileName := 'D:\rec\capture.avi'; VideoGrabber1.StartRecording;逻辑说明:VideoCompressor为空时走未压缩流,1080p 下几分钟就能把硬盘写满。参数上,FrameRate要跟采集卡实际支持的帧率对齐,设 30 但设备只支持 25 时,部分驱动会直接拒绝。失败时看控件返回的LastError字符串,比系统弹窗信息更具体。
2.4 与 BCB456 共用时的类型映射注意点
C++Builder 4/5/6 调用同一套控件时,Pascal 的TDateTime、OleVariant和字符串类型在头文件里映射方式不同。常见问题是RecordingFileName传AnsiString正常,传WideString在 BCB6 下会截断。稳妥做法是统一用AnsiString或char*包装,不要混用。另外 BCB456 的包安装路径跟 Delphi7 不同,两个 IDE 同时装时,建议把设计期包输出到各自独立目录,避免互相覆盖。
3. 从零跑通一次采集录像:最小可复现工程
3.1 窗体搭建与组件属性初始值
新建一个 Delphi7 Application,放一个TVideoGrabber、一个TImage或TPanel作为预览容器、两个TButton(开始预览、开始录像)、一个TComboBox选设备。关键属性初始值如下表:
| 属性 | 建议初始值 | 说明 |
|---|---|---|
| VideoDevice | -1 | 启动时由枚举结果赋值 |
| PreviewMode | pm_Standard | 标准预览,兼容性最好 |
| VideoWidth / VideoHeight | 640 / 480 | 先小分辨率跑通再调大 |
| FrameRate | 25 | 跟设备支持帧率对齐 |
| RecordingFileName | 空 | 录像前再赋值 |
| AutoFileName | False | 避免自动生成不可控路径 |
逻辑说明:PreviewMode设成pm_Standard而不是pm_Optimized,是因为后者在部分老采集卡上会走 Overlay 路径,截图和录像拿不到数据。参数上,VideoWidth先设小值是为了排除带宽和编码器瓶颈,跑通后再逐步加到 1080p。
3.2 预览启动与停止的完整调用顺序
procedure TFormMain.btnPreviewClick(Sender: TObject); begin if VideoGrabber1.IsPreviewing then begin VideoGrabber1.StopPreview; btnPreview.Caption := '开始预览'; Exit; end; VideoGrabber1.VideoDevice := cboVideo.ItemIndex; VideoGrabber1.VideoWidth := 640; VideoGrabber1.VideoHeight := 480; VideoGrabber1.StartPreview; btnPreview.Caption := '停止预览'; end;逻辑说明:IsPreviewing是控件内部状态,比自己在外部维护布尔量可靠。参数上,每次StartPreview前重新赋VideoDevice,是因为部分驱动在StopPreview后会释放设备句柄,不重新绑定会拿到空设备。失败时先看LastError,再确认设备是否被其他进程占用——同一张采集卡被两个程序同时打开时,第二个程序通常只能拿到空流。
3.3 录像文件命名与磁盘写入策略
录像文件名建议带时间戳,避免覆盖。磁盘写入策略上,TVideoGrabber 默认走同步写,高码率下会阻塞 UI 线程。常见做法是开一个独立线程做文件搬运,或者把RecordingFileName指到本地 SSD 再定期转存。
procedure TFormMain.btnRecordClick(Sender: TObject); var FileName: string; begin if VideoGrabber1.IsRecording then begin VideoGrabber1.StopRecording; btnRecord.Caption := '开始录像'; Exit; end; FileName := Format('D:\rec\%s.avi', [FormatDateTime('yyyymmddhhnnss', Now)]); VideoGrabber1.RecordingFileName := FileName; VideoGrabber1.StartRecording; btnRecord.Caption := '停止录像'; end;逻辑说明:FormatDateTime生成的时间戳精确到秒,同一秒内重复点击会覆盖,实际项目里可以加毫秒或序号。参数上,RecordingFileName的目录必须提前存在,控件不会自动创建目录,路径不存在时StartRecording返回失败但错误信息可能被忽略。
3.4 用 LastError 做最小排错闭环
TVideoGrabber 的LastError属性在每次操作后更新,比系统弹窗更贴近控件内部状态。建议在每个关键调用后检查一次,写进日志。
procedure TFormMain.LogLastError(const Tag: string); begin if VideoGrabber1.LastError <> '' then MemoLog.Lines.Add(Format('[%s] %s', [Tag, VideoGrabber1.LastError])); end;逻辑说明:LastError是字符串,部分版本在成功时返回空串,部分返回'OK',判断时用<> ''并配合具体内容过滤。参数上,日志里带上操作标签,方便定位是枚举、预览还是录像阶段出的问题。
4. 避坑与排查:老控件在新系统上的五类翻车
4.1 组件面板能看到但拖到窗体报类未注册
现象:IDE 里组件面板出现 TVideoGrabber 图标,拖到窗体时弹「Class TVideoGrabber not found」。原因:设计期包和运行时包版本不一致,或者系统 PATH 里存在同名旧 bpl。解决:先卸载所有相关包,清理$(DELPHI)\Projects\Bpl下的旧文件,重新编译 Runtime 包再编译 Design 包,确保两个包引用的是同一份源码。
4.2 预览正常但录像文件只有几 KB
现象:预览窗口有画面,录像停止后文件大小异常小,播放器打不开。原因:VideoCompressor设了一个系统里不存在的编码器名,控件回退到空流。解决:先用VideoGrabber1.VideoCompressors枚举可用编码器,把名字打印出来再选,不要凭记忆填。
4.3 切换设备后预览黑屏
现象:从设备 0 切到设备 1,预览窗口全黑,LastError为空。原因:部分驱动在StopPreview后需要几百毫秒释放句柄,立即StartPreview会拿到空流。解决:在StopPreview和下一次StartPreview之间加Sleep(300)或用一个短定时器延迟启动,实测能消掉大部分黑屏。
4.4 高分辨率下 UI 卡死
现象:设 1920x1080 后,点停止录像要等好几秒才响应。原因:同步写盘阻塞了主线程。解决:把RecordingFileName指到本地 SSD,或者用独立线程做文件转存;如果必须写网络路径,先把流写到本地临时文件,再由后台线程搬运。
4.5 BCB456 下字符串参数截断
现象:C++Builder 里传WideString给RecordingFileName,实际文件名只保留了前几个字符。原因:BCB456 的头文件把该属性映射为AnsiString,宽字符被截断。解决:统一用AnsiString或char*,不要混用WideString;如果业务层是 Unicode,先在边界处转成 ANSI 再传入。
5. 把老控件用稳的几个进阶习惯
第一个习惯是给采集参数做「能力探测」而不是硬编码。在窗体创建时枚举一遍设备支持的格式组合,把分辨率、帧率、编码器列成候选表,运行时按优先级降级。这样换机器时不用改代码,控件自己会退到可用组合。第二个习惯是把LastError和系统事件日志一起看,控件报错信息有时只到「无法设置媒体类型」这一层,具体是哪个 Pin 协商失败,要结合驱动日志或采集卡厂商工具确认。
第三个习惯是录像文件做「先本地后转存」。不管最终要存到哪,先写到本地固定目录,再由后台线程搬运和清理。这样即使网络抖动或目标路径不可写,也不会影响采集本身。第四个习惯是给预览和录像分别设参数,不要共用一套。预览可以降分辨率保流畅,录像按业务要求设真实格式,两者分开配置能避免很多「预览好录像坏」的扯皮。
最后一个习惯是版本锁定。TVideoGrabber 6.7.5 这个分支在 Delphi7 下是稳定的,但不要随意混用其他版本的单元文件。我一般会把整个控件源码目录纳入版本管理,编译前先确认没有外部路径的同名单元被优先加载。这套东西不新,但在存量项目里还能再跑很多年,关键是别在参数上想当然。希望帮到你。
本文还有配套的精品资源,点击获取