ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Delphi 12.3视频采集指南:TVideoGrabber SDK安装、预览与帧率调优

Delphi 12.3视频采集指南:TVideoGrabber SDK安装、预览与帧率调优 简介Delphi 12.3环境下可用的TVideoGrabber视频处理控件SDK版本为15.2.5.3支持全平台面向需要在Windows、macOS、Android、iOS等操作系统中集成视频采集、预览、录制与网络流播放功能的客户端开发者。压缩包内共942个文件整体体积约101MB除了组件核心的运行时包、编译单元、动态链接库与静态库之外还配置了界面资源文件、窗体定义文件、工程源码示例、头文件以及帮助文档可供使用Delphi或CBuilder进行原生开发时直接引用也保留了用于快速验证的二进制模块。目前已有85人学习下载。该SDK特别适合需要接入USB摄像头、IP摄像机、HDMI采集卡或本地视频文件的桌面应用项目借助完整的示例工程和跨平台库文件开发者不必从底层视频驱动开始编写只需设置控件属性、响应事件即可实现视频画面显示、抓帧、录制保存和推流等操作从而降低多平台适配的工作量。资源内置的文档与模块分层清晰对中高级客户端工程师和需要二次封装视频能力的团队都具有参考价值。1. 从一次“摄像头预览不到 30 帧”的选型说起Delphi 12.3 下的 TVideoGrabber SDK 到底解决了什么如果你在 Delphi 12.3 里接过摄像头大概率经历过这种场面原本用 DirectShow 自绘预览把画面画到 TCanvas 上分辨率一提到 1080p帧率直接掉到 15 帧上下想做截图还要维护一整套帧同步逻辑代码越写越像黑匣子。标题里这个 Datastead 出的 TVideoGrabber SDK 控件v15.2.5.3 是 2023 年 4 月发布的版本后面跟着 All Platforms意味着它面向 Windows、macOS、Android 等多平台把采集、预览、录制、截图、RTSP 拉流这一整套能力收敛到单个控件里。它解决的问题很具体让做视频采集的 Delphi 开发者不再自己折腾底层 DirectShow 封装而是靠属性赋值和事件回调完成 80% 的日常需求。适合谁正在做安防客户端、医疗影像、视频会议、直播推流工具的 Delphi 程序员以及刚接触视频采集但不想从零写驱动的桌面端开发者。2. 装进 Delphi 12.3TVideoGrabber SDK 控件包的安装与库路径配置很多人在这一步就翻车了。压缩包解压以后不先认文件直接双击安装脚本然后 IDE 报错找不到 bpl或者干脆控件拖不上窗体。这一章把安装路径上的坑先排掉。2.1 先认全压缩包解压之后你会看到什么一个视频采集 SDK 控件包和解普通 Delphi 库不一样不能只关心那个最大的可执行文件。用压缩包自带的目录结构来说常见会包含几类东西运行时包bpl、编译接口文件dcp、源码单元pas、示例工程samples和帮助文档docs。其中 bpl 是 IDE 设计期要加载的dcp 是编译期要引用的pas 则是你后续想跟读源码或修改控件行为时才需要的东西。samples 目录往往价值最高——很多参数在帮助文件里写得不清楚但示例工程一跑就明白用法。我一般会先把整个包解压到一个没有空格的路径下比如D:\Components\TVideoGrabber而不是放在C:\Program Files这种带空格的位置。空格问题在 Delphi 的 Library path 和老旧安装脚本里非常常见后面编译报错时排查起来最浪费时间。解压后在命令行里快速确认文件是否完整这一步可以省掉后面大量无头绪的排查。cd /d D:\Components\TVideoGrabber dir /b *.bpl *.dcp *.pas这段命令的逻辑很简单dir /b只列出文件名不加多余信息适合快速扫一眼关键文件是否都在。如果发现只有 exe 或者空目录先别继续安装八成是解压过程中安全软件把运行库拦截了这种时候装出来的控件通常会在运行时报“类未注册”之类的错而且很难定位到根因。2.2 在 RAD Studio 12.3 里装控件的标准动作安装路径正确后接下来才是 IDE 层面的动作。我在 RAD Studio 12.3 下安装这类第三方控件的顺序基本固定不会直接双击安装脚本因为脚本经常写死了旧版 Delphi 的安装路径在 12.3 上会装错位置。第一件事是把包目录整体放到无空格路径然后打开 IDE进入Component Install Packages点Add选择对应版本的 bpl 文件。如果包提供的是源码方式安装那就用File Open打开 .dpk 工程在右侧 Project Manager 里先右键Compile再右键Install。这里有个关键区别Compile 只生成文件Install 才把控件注册进 Tool Palette只编译不安装是“装完拖不出来”的第一大原因。uses TVideoGrabber; procedure TForm1.FormCreate(Sender: TObject); begin // 如果 IDE 能编译过这一行说明控件单元已正确引入 // 此时设计期应该能在 Tool Palette 搜到 TVideoGrabber end;这段代码看起来简单实际是安装后的冒烟测试。uses TVideoGrabber能否编译通过直接反映出源码路径和 dcp 引用是否正确。如果在这里报找不到单元问题基本锁定在 Library path 没加或加错了路径和代码本身无关。安装完成后还要做一个动作Tools Options Environment Options Delphi Options Library把源码目录加进 Library path。这一步很多新手会漏但它是打开示例工程时能不能编译的关键。示例工程引用的不只是 bpl还有 pas 源码IDE 需要靠 Library path 找到它们。路径配好后我习惯把示例工程里最简单的那个先编译一遍能跑起来才算安装真正结束。2.3 装完却拖不出来控件三个最常犯的安装疏漏第一个疏漏就是前面说的只 Compile 没 Install。Delphi 的包机制里Compile 只是在当前 IDE 会话里生成编译产物不写进注册表只有 Install 操作才会把控件注册到 Tool Palette。遇到拖不出来时先回 Project Manager 看有没有执行 Install。第二个疏漏是版本混装。机器上装了多个 Delphi 版本比如老的 XE8 和新版 12.3 并存bpl 文件如果用的是旧版编译器产物安装在 12.3 里要么提示 “Package 版本不受支持”要么安装了但拖到窗体时 IDE 直接崩溃。解决办法是把包源码在有问题的 IDE 版本里重新 Compile 一遍生成对应版本的 bpl再 Install。标题里的 v15.2.5.3 是 2023 年的版本和 12.3 的 RAD Studio 基本处于同一代主版本冲突的概率不大但旧版升级上来的机器要格外注意。第三个疏漏是路径里有中文或空格导致的隐性问题。Delphi 12.3 的 IDE 对 Unicode 路径支持已经很好了但第三方控件的安装脚本未必跟上常见表现是安装时一切正常打开示例工程却报“Cannot open file”。这类问题最有迷惑性因为错误信息跟路径完全对不上。我的习惯是从第一步就避开非 ASCII 路径不在这上面赌运气。把这些点都排掉后TVideoGrabber 控件出现在 Tool Palette 里、拖到窗体上能正常显示安装阶段才算真的过关。3. 跑通第一个采集合成预览、视频源切换与画面抓取安装只是热身真正的开始是把摄像头画面在窗体上显示出来。这一章给出最小可运行代码从设备枚举到帧回调再到截图保存最后落到视频源切换。全程用 Delphi 12.3 默认的 VCL 工程讲解不涉及第三方显示控件。3.1 最小初始化代码先枚举设备再 Open别用设备索引硬编码刚接触 TVideoGrabber 时最容易犯的错误是直接从网上抄一段代码把VideoDevice写成0之类的设备索引。这样做在开发者自己机器上可能没问题但换到用户机器上摄像头设备的枚举顺序完全可能不一样索引 0 可能指向一个虚拟摄像头也可能指向一个采集卡。我习惯的做法是启动时把设备列表读出来让用户选或者至少做一个下拉框。procedure TForm1.FormCreate(Sender: TObject); var I: Integer; begin // 把系统当前可用的视频设备名全部列出来 for I : 0 to TVideoGrabber1.VideoInputList.Count - 1 do ComboBox1.Items.Add(TVideoGrabber1.VideoInputList[I]); end;这段代码的逻辑很直接VideoInputList返回当前系统里所有可用的视频输入设备逐个添加进下拉框。这里要注意的是枚举动作必须在Open之前做否则列表可能为空。设备名在不同系统下会变比如集成摄像头叫 “Integrated Camera”USB 摄像头叫 “USB Camera #2”所以设计期不要写死设备名运行期从列表里取才是稳定做法。设备名拿到之后打开设备就简单了procedure TForm1.btnOpenClick(Sender: TObject); begin // 视频源指向摄像头而不是本地文件或网络流 TVideoGrabber1.VideoSource : vsVideoSource; // 设备名从下拉框取避免硬编码索引 TVideoGrabber1.VideoDevice : ComboBox1.Text; // 打开设备启动画面采集 TVideoGrabber1.Open; end;VideoSource是 TVideoGrabber 最核心的属性之一它决定控件从哪拿画面。vsVideoSource表示本地摄像头vsFile表示读取本地视频文件vsNetworkStream对应网络流这在后面做视频源切换时会用到。VideoDevice赋值设备名后调用Open才真正开始采集。需要说明的是Open是一个同步调用设备打开失败时会有异常抛出正式项目里要包一层 try/except并给出友好的错误提示。3.2 OnFrame 事件采集线程与 UI 线程的同步边界TVideoGrabber SDK 的帧回调机制是它最好用也最容易踩坑的地方。控件内部会启动采集线程每一帧到达时触发OnFrame事件事件参数里的Bitmap就是当前帧画面。很多新手第一次写回调时直接在事件里把 Bitmap 赋给 TImage然后发现界面卡顿甚至程序崩溃——问题出在线程边界上。OnFrame里的 Bitmap 是控件内部复用的内存对象这一帧处理完下一帧到达时这块内存可能已经被改写。所以回调里绝对不能保存 Bitmap 的引用只能深拷贝。同时直接在这里操作 VCL 控件比如 TImage是不安全的因为在不同线程里访问 UI 对象会造成不可预料的后果。procedure TForm1.TVideoGrabber1Frame(Sender: TObject; Bitmap: TBitmap); var Bmp: TBitmap; begin // 先深拷贝把当前帧复制到独立的内存里 Bmp : TBitmap.Create; Bmp.Assign(Bitmap); // 通过 TThread.Queue 切回主线程再更新 UI TThread.Queue(nil, procedure begin try Image1.Picture.Bitmap.Assign(Bmp); finally Bmp.Free; end; end); end;这段代码解决了两个问题第一Assign把 Bitmap 的内存完整复制了一份后续帧到达不会影响这份数据第二TThread.Queue把 UI 更新操作投递到主线程队列避免跨线程访问 TImage。这里为什么不直接Synchronize因为Synchronize是阻塞的采集线程会等主线程处理完才继续帧率一高回调就被 UI 卡住画面出现明显延迟。Queue是非阻塞的主线程空闲时处理忙时自然丢弃积累的更新请求语义上更适合视频预览。这段代码里要特别注意Bmp.Free的位置。我在finally里释放保证即使主线程里的Assign出错也不会泄漏。深拷贝带来的额外开销在一路视频时完全可接受但四路视频时就要考虑优化——这个后面第 4 章再展开。3.3 从本地文件到网络流视频源切换的三个合法姿势TVideoGrabber 做视频源切换比很多原生实现要省事因为VideoSource已经帮你分好了类。从摄像头切到本地视频文件只需要改两个属性再重新Openprocedure TForm1.btnPlayFileClick(Sender: TObject); begin // 先停止当前采集否则切换会失败 TVideoGrabber1.Close; // 视频源类型改成本地文件 TVideoGrabber1.VideoSource : vsFile; TVideoGrabber1.VideoFile : D:\videos\test.mp4; TVideoGrabber1.Open; end;这段代码看着简单但Close这一步很容易被忽略。如果不在切换前关闭当前设备有些版本的 SDK 会直接忽略新设置停留在旧画面有些版本则会抛异常。在 Delphi 12.3 下我的做法是把切换动作封装成一个公共方法内部统一执行Close - 设置属性 - Open避免每次手工操作丢步骤。切换到网络流比如 RTSP 拉流时原理一样把VideoSource设为vsNetworkStream再在对应属性里填 URL。这里有一个实际经验RTSP 地址往往对延迟敏感如果预览画面停顿先检查流地址是不是走了 UDP 端口被防火墙拦截SDK 层面能设置的只是连接超时时间。这类问题在开发环境里很难复现部署到客户内网才冒出来属于典型的“环境相关”问题。从文件或网络流切回摄像头时同样走Close - VideoSource : vsVideoSource - VideoDevice 赋值 - Open的流程。我在实际项目里还会保留一个“设备打开失败自动重试”的逻辑因为 USB 摄像头设备被其他进程占用的概率并不低而电视会议软件比如钉钉、腾讯会议在后台挂着时经常独占摄像头——这里只是举个例子实际项目里我遇到过好几次这种占用冲突重试通常能解决问题。4. 参数调优分辨率、帧率、像素格式与多路视频的平衡画面能出来了接下来就是你跟真实产品之间的差距分辨率设多少合适、为什么设了 60 帧实际只有 30 帧、四路视频是否扛得住。这一章不讲玄学直接给参数语义和取舍逻辑。4.1 分辨率与帧率设备支持列表才是唯一合法的参数来源TVideoGrabber 控件里有VideoWidth、VideoHeight、FrameRate三个属性表面上是“赋值就生效”但实际行为更接近“请求值”——驱动会根据自己的能力协商一个最接近的结果。比如你设了 1920x1080但摄像头硬件本身只支持 1280x720 下的 30 帧那最终拿到的就是 720p 的画面而且控件的实时状态里会显示实际协商结果。这解释了为什么很多人发现“我设了 1080p 但画面明显不是 1080p”。正确做法是在Open之前先读取设备支持的分辨率列表从里面选一个而不是拍脑袋写死数值。SDK 在枚举设备后通常会提供对应格式列表15.2 版本里这个列表的名字跟早期版本略有差异但思路一致先枚举再选择最后设置。procedure TForm1.ApplyResolution(AWidth, AHeight, AFPS: Integer); begin // 设置前先保证设备已关闭避免参数在运行中失效 TVideoGrabber1.Close; TVideoGrabber1.VideoWidth : AWidth; TVideoGrabber1.VideoHeight : AHeight; TVideoGrabber1.FrameRate : AFPS; TVideoGrabber1.Open; end;这段代码暴露了一个容易被忽略的细节参数修改要在设备关闭状态下进行。如果设备处于打开状态某些版本的 SDK 会忽略参数变化你的赋值操作没有报错但也不生效看起来就像“参数没调对”。我先Close再赋值再Open从流程上堵住这个坑。帧率方面我会在运行时区分两个概念你设置的FrameRate是请求值实际帧率可能受环境光照、USB 带宽、CPU 负载影响。USB 2.0 摄像头在 720p 下做到 30 帧已经很紧张想稳定 60 帧通常需要换 USB 3.0 设备或者降低分辨率。这个不是控件能解决的是硬件链路的天花板。4.2 像素格式同样 1080pRGB 和 YUV 的带宽差接近一半像素格式是最容易被忽略的性能参数。摄像头传感器输出的原始画面通常是 YUV 格式但很多应用为了显示方便会转换成 RGB。TVideoGrabber 控件允许你控制这个转换链路的末端格式选错了带宽和 CPU 开销都上去了。像素格式1080p 单帧数据量典型用途代价RGB24约 6.2 MB图像处理、直接显示带宽最大帧率最容易掉YUY2约 4.1 MB通用预览、录制部分处理场景需要转 RGBI420约 3.1 MB视频编码、推流、识别显示前转换开销数据量计算不复杂1920 乘以 1080 再乘以每像素字节数RGB24 每像素 3 字节YUY2 每像素 2 字节I420 每像素 1.5 字节。三者在 1080p 下相差接近一半的带宽。如果你的应用下一步是做 OpenCV 识别通常需要 RGB如果只是预览加录制用 YUV 格式能省下不少带宽把帧率余量留给其他环节。在 TVideoGrabber 里设置像素格式的入口不叫 PixelFormat不同版本有不同命名有的叫VideoFormat有的叫PixelsFormat。我建议你在 15.2.5.3 这个版本的帮助文档里查一下确切名称不要凭旧版本记忆去硬找。这里提醒一个排查方法当你发现相同分辨率下 CPU 占用率异常偏高先把像素格式改成 YUV 类型如果 CPU 明显下降说明之前花在格式转换上的开销不是白来的。4.3 四路摄像头是常见需求多实例与线程模型做安防或监控类产品的读者早晚会遇到四路甚至更多路视频同屏的需求。TVideoGrabber 的模型是一路视频对应一个控件实例所以四路视频就是窗体上放四个 TVideoGrabber或者动态创建四个实例。这个模型本身没什么问题但要注意它背后是四套独立的采集线程。procedure TForm1.AddSecondCamera(AParent: TWinControl); var Grabber2: TVideoGrabber; begin // 运行时动态创建第二个采集实例不依赖设计期控件 Grabber2 : TVideoGrabber.Create(Self); Grabber2.Parent : AParent; Grabber2.Align : alClient; Grabber2.VideoSource : vsVideoSource; Grabber2.VideoDevice : USB Camera #2; Grabber2.Open; end;这段代码演示了动态创建实例的基本写法。关键在于Parent和Align动态创建的控件如果不设置这两个属性画面不会显示在预期区域。设计期摆放四个控件虽然直观但布局调整和参数管理都麻烦我一般只放一个模板控件运行时复制参数动态创建。四路同时跑时OnFrame 回调频率是单路的四倍如果每路回调里都做Bitmap.Assign深拷贝内存分配压力会非常明显。我的做法是回调里只做“移交指针所有权”式的操作把 Bitmap 存入队列由独立处理线程消费。这样每一帧只发生一次拷贝且拷贝发生在生产者侧消费侧不会再复制。这个模式放在后面第 6 章给完整示例。回到线程模型上四路采集会带来四个工作线程加一个 UI 线程的并发结构Delphi 的 TThread.Queue 仍然是安全的但要克制在主线程里做的操作量避免 UI 渲染成为瓶颈。5. 血泪避坑TVideoGrabber 安装与运行中的五个高频问题把常见问题一条条列出来每条都按“现象 - 原因 - 解决”来写。这里面的经验来自真实项目里踩过的坑不是从帮助文件里抄来的。5.1 编译报错找不到 TVideoGrabber 单元现象新建工程后在代码里uses TVideoGrabber编译直接报F2613 Unit TVideoGrabber not found。很多人第一反应是控件没安装但重新 Install 后问题依旧。原因控件安装了但 IDE 没有把源码目录加进 Library path。Delphi 的编译单元搜索路径和包安装是两个独立体系安装包只解决设计期放置问题编译期查找 pas/dcu 需要靠 Library path。解决打开Tools Options Environment Options Delphi Options Library把包源码目录加进去点Save后重新编译。注意 64 位平台要在同一页面里切到 64-bit Windows 平台再检查一遍路径32 位和 64 位的路径配置是分开记忆的只配了 32 位64 位编译依旧找不到单元。5.2 Open 后黑屏但摄像头在其他软件里正常现象控件不出错画面区域黑屏但打开系统相机或者钉钉视频能看到画面。此时控件的状态显示设备已打开。原因摄像头被其他进程占用或者操作系统的相机隐私设置关闭了桌面应用访问权限。Windows 10/11 都有“相机隐私”开关某些软件会自动关闭非商店应用的相机访问权限这种现象在 Windows 11 上尤其常见。解决先去系统设置里的隐私与安全性 - 相机确认“桌面应用可以访问相机”是开启状态。再检查有没有后台进程占用摄像头常见的占用来源包括会议软件、远程协助工具等。TVideoGrabber 本身没有抢占机制设备被占时Open不一定报错但画面一直黑着这时候只能靠排查外部因素。5.3 设定 60 帧实际只有 30 帧始终到不了标称值现象FrameRate设为 60运行后实际帧率稳定在 30 左右怎么调都上不去。原因多半是设备输出端就锁在 30 帧或者 USB 2.0 带宽不足以支撑当前分辨率和像素格式下的 60 帧传输。USB 2.0 的极限有效带宽在 280 Mbps 左右720p 的 RGB24 在 30 帧时已经要 330 Mbps早就超标了驱动会主动降帧。解决先看设备在不同分辨率下的能力表很多摄像头最大只支持 60 帧是在 VGA 分辨率下。把分辨率降低到 640x480 再试 60 帧如果能到说明瓶颈在带宽不在代码。如果降分辨率还是只能 30 帧那就是摄像头的固件锁死了帧率换设备才能解决。5.4 程序退出时偶发崩溃出错地址每次都不一样现象主界面关闭后程序闪退调用栈指向的位置不固定有时在释放代码里有时干脆停在系统库内部。Debug 模式比 Release 模式更频繁。原因窗体销毁时TVideoGrabber 控件和它的采集线程发生了竞争。采集线程还在回调 OnFrame主线程已经把窗体对象释放了回调里的代码成了对已释放内存的访问。这是典型的释放时序问题崩溃点随机是它的标志性特征。解决在窗体关闭流程里加显式停止动作先停止采集再释放对象。procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin // 先停采集线程避免 OnFrame 在释放期间被触发 TVideoGrabber1.StopPreview; TVideoGrabber1.Close; end;这段代码的关键是StopPreview和Close的先后顺序。StopPreview拉停采集线程Close释放设备资源两步都做完后采集线程不会再执行 OnFrame窗体再销毁控件才是安全的。如果你的代码里还有TThread.Queue投递的匿名方法排队未执行Release 模式下这些方法可能在新窗体实例上运行造成更难查的花屏问题。这个属于 Delphi 匿名方法队列的经典坑我通常会在 OnFrame 里用一个布尔标识FClosing做开关FormClose先置位再关闭设备双保险。5.5 从非官方渠道拿到的带 CRACK 标记的包装上后各种诡异问题现象安装完成后第一次编译能过但运行起来控件属性设置总是不生效或者 IDE 运行时弹出奇怪窗口更常见的是杀毒软件把主程序和 bpl 一起隔离。于是你开始怀疑自己代码写错了折腾一整天无果。原因像标题里那种带 CRACK 标记的包内部文件已经被人为修改过用来绕过注册校验。这类修改会影响控件对 SDK 本身的初始化逻辑表现为间歇性失灵另一方面杀毒软件对这类修改过的二进制文件高度敏感隔离操作会进一步破坏文件完整性。解决我的建议很明确不要在这种包上继续排错浪费时间也没有结果。Datastead 官方提供评估版本能覆盖到学习和原型验证阶段。如果是公司项目直接购买正式授权视频采集 SDK 是基础组件跟着项目走好几年后期遇到摄像头兼容性问题需要官方支持时一个正规授权能省下的时间和沟通成本远超授权费本身。我见过太多团队因为省了这笔预算最后在兼容性排查上付出更大代价。验证包是否被修改的方法也简单对比解压文件的签名信息或文件大小和官方发布说明核对任何一个文件对不上都直接丢弃。6. 进阶技巧把 OnFrame 的帧喂给图像识别线程一份值得抄的队列代码前面第 3 章给的 OnFrame 代码里深拷贝后直接交给主线程更新 UI这在单路预览场景下够用。但如果你要做人脸识别、车牌识别或者任何图像分析类功能把图像识别放到主线程是灾难性的——识别一帧可能花 100 毫秒UI 直接卡死。正确思路是生产者-消费者队列采集线程当生产者识别线程当消费者。这里给一份 Delphi 12.3 下可直接用的实现。TThreadedQueue是 Delphi 自带的泛型线程安全队列不需要额外引入第三方库。初始化时指定队列长度、压入超时和弹出超时然后采集回调往里面放帧识别线程从里面取// 在 Form1 的私有成员里声明: // FQueue: TThreadedQueueTBitmap; procedure TForm1.TVideoGrabber1Frame(Sender: TObject; Bitmap: TBitmap); var Bmp: TBitmap; begin // 深拷贝是必须的Bitmap 是控件内部的复用对象 Bmp : TBitmap.Create; Bmp.Assign(Bitmap); // 队列满时 PushItem 返回超时此时直接丢弃当前帧 if FQueue.PushItem(Bmp, 0) wrSignaled then Bmp.Free; end;PushItem(Bmp, 0)是一个带超时的压入操作超时设为 0 表示“满了就不等了”。这个丢弃策略是我调出来的关键识别线程一旦慢了保活机制应该优先保证采集线程不被阻塞画面预览不卡识别结果慢一两秒问题不大。反过来如果采集线程被阻塞整个链路都会连锁卡顿。识别线程这边的消费循环写法相对固定while not Terminated do begin Bmp : FQueue.PopItem(200); if Bmp nil then begin // 在这里做识别、编码或存盘 // 处理完必须释放所有权已经转移到消费者这边 Bmp.Free; end; end;PopItem(200)表示最多等 200 毫秒超时返回时不阻塞主流程这样线程退出时不用等队列消费完响应也快。识别代码如果需要在处理过程中显示进度到 UI用TThread.Queue投递结果不要在识别线程里直接改界面。这套结构的核心收益是OnFrame 回调里只做一次分配和一次拷贝消费端独占处理帧率波动被缓存吸收识别线程的偶发慢帧不会反噬采集线程。我做视频采集端有一个习惯每一路视频交付前都测一遍快速开关窗和长时间挂机出现任何崩溃先怀疑释放时序。这份队列代码能解决大部分帧回调安全问题剩下的一小部分靠日志定位总比靠猜可靠。希望帮到你。本文还有配套的精品资源点击获取
返回列表