最近忙于一个视频会议客户端(VCC)项目,该项目也是训练营课程的一部分。该项目旨在帮助初学者熟悉音视频领域的常见框架与技术栈,如 OBS 和 WebRTC,并实践音视频渲染(OpenGL、minaudio)等知识,起到一个“引路人”的作用。项目的整体系统架构如下:

在后续的分享中,我将逐步介绍客户端开发中常见的错误,许多问题也是我实际踩过的坑。今天我们先聚焦一个看似简单却令人困扰的问题:明明在代码中关闭了摄像头,为什么它的指示灯还一直亮着?
问题本质与成因 (What & Why)
摄像头指示灯常亮,其根本原因是硬件设备的句柄或视频流被进程持续占用着。在 Windows 系统中,摄像头通常被视为独占式设备(多数 USB 摄像头不支持多进程同时访问),只要有任何进程持有着它的设备句柄、未关闭视频采集流,指示灯就会保持亮起状态。换句话说,灯亮即表明设备未被正确释放。
在 OBS 二次开发的场景下,这个问题通常指向一个核心原因:OBS 的 dshow_input 插件(用于 Windows 平台的音视频采集)所创建的摄像头源,其生命周期未被完整结束,即引用计数未归零。
OBS 中许多资源(包括摄像头源)都采用引用计数机制进行管理。只有在所有引用都被移除,引用计数降为 0 时,资源才会被真正销毁,摄像头硬件才会被释放,指示灯随之熄灭。若我们仅移除了源在场景或输出中的关联,但漏掉了某个对 obs_source_t* 指针的 release 调用,就会导致引用计数残留,从而引发指示灯常亮的问题。
解决方案 (How)
既然问题的根源是存在未被释放的摄像头源实例,那么解决方法就非常明确了:在关闭摄像头的流程中,确保找到所有持有该摄像头源的指针,并调用释放函数。
释放一个摄像头源(或其他任何 OBS 源)的核心 API 是 obs_source_release。在 C/C++ 层面的代码示例如下:
obs_source_release(camera_source);
这里的 camera_source 是你通过 obs_get_source_by_name 或其他创建函数获得的 obs_source_t* 指针。每当你获取一个源,其引用计数就会增加;当你不再需要它时,必须通过 obs_source_release 来减少引用计数。
一个常见的完整生命周期管理流程应该是:
- 创建/获取源 -> 引用计数+1
- 将源添加到场景或输出中 -> (OBS内部可能会持有引用)
- 使用源
- 从场景或输出中移除源 -> (OBS内部释放其引用)
- 调用
obs_source_release -> 你的代码释放其引用,当所有引用归零,资源真正销毁。
确保你的代码逻辑,尤其是在多个模块或回调函数中操作同一个源时,没有漏掉最后的 release 调用。建议详细查阅 OBS 官方文档 中关于源的生命周期管理部分,以建立更清晰的认识。
总结
摄像头指示灯常亮只是表象,背后反映的是资源管理的不严谨。不仅仅是摄像头源,在进行任何 OBS 二次开发时,都应当严格遵循所有资源的生命周期管理规范:创建 -> 添加 -> 使用 -> 移除 -> 释放。释放环节务必彻底,确保所有获取的引用都被正确归还,否则就可能会遇到各种难以预料的问题,例如内存泄漏、设备占用、程序异常等。
希望这个踩坑经验能帮助你解决问题。如果你在音视频开发或 系统架构 设计中有其他心得或疑问,欢迎在技术社区进行交流与探讨,例如 云栈社区。
|