首先从目录结构分析,视频采集模块位于modules下,主要层级关系如下图。其中最外层是公共实现部分,包括对外API,内部数据配置参数和定义,以及具体实现部分;内部的目录中主要是对于各个平台具体的实现,如window和Linux。

整个采集模块最核心的内容包括三大块:首先是对外API定义在video_capture.h描述了视频采集模块对外提供的能力。第二部分是公共实现部分video_capture_impl主要是对于公共功能的实现,数据流的控制等;第三部分是实际采集的模块,根据不同平台需要进行不同的处理,这部分就是为了跨平台而实现,具体数据采集过程发生在这里。
视频采集对外API主要包括将采集数据回调到视频引擎,采集设备的开关;技术配置的设置等基本操作。
本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓
- //回调函数类
- template <typename VideoFrameT>
- class VideoSinkInterface {
- public:
- virtual ~VideoSinkInterface() = default;
- //核心函数将采集数据回传
- virtual void OnFrame(const VideoFrameT& frame) = 0;
- //QOS丢帧操作
- virtual void OnDiscardedFrame() {}
- //修改配置
- virtual void OnConstraintsChanged(
- const webrtc::VideoTrackSourceConstraints& constraints) {}
- };
- //注册回调函数
- virtual void RegisterCaptureDataCallback(
- rtc::VideoSinkInterface
* dataCallback) = 0; - //注销
- virtual void DeRegisterCaptureDataCallback() = 0;
- //启动采集
- virtual int32_t StartCapture(const VideoCaptureCapability& capability) = 0;
- //关闭采集
- virtual int32_t StopCapture() = 0;
- //获取采集设备名称
- virtual const char* CurrentDeviceName() const = 0;
- //判断采集是否开启
- virtual bool CaptureStarted() = 0;
- //设置采集配置
- virtual int32_t CaptureSettings(VideoCaptureCapability& settings) = 0;
- //设置旋转角度
- virtual int32_t SetCaptureRotation(VideoRotation rotation) = 0;
- //启动旋转
- virtual bool SetApplyRotation(bool enable) = 0;
首先是采集流程,采集过程是由采集线程来完成的,整个采集过程同不断的由采集设备读取视频帧加入到采集队列然后由采集线程将数据读出,转换为视频帧后回调到视频引擎处理。

采集启动流程,首先会进行检查是否已经开启采集设备如果开启,如果分辨率不一致将先关闭采集设备,重新打开采集设备,第二步是设置视频参数,包括分辨率,采集原始数据类型,帧率等等;第三步分配共享内存,用于采集帧存放;第四步启动采集线程并开启采集设备。
selectIO复用技术,主要是为了等待视频设备数据输入,同时可以设置超时时间。作为一个触发器和定时器的作用。
- //这里只进行读事件检查
- select(_deviceFd + 1, &rSet, NULL, NULL, &timeout);
内存映射技术,这样做的优势在于实现数据的零拷贝加快数据传输效率,提升数据发送速度。这部分涉及到Linux v4l2架构,这里不进行深入分析。
- //1 初始化内存
- //开启视频流
- ioctl(_deviceFd, VIDIOC_STREAMON, &type)
- //1.1 初始化,向驱动提出申请内存的请求
- ioctl(_deviceFd, VIDIOC_REQBUFS, &rbuffer)
- //1.2 检查是否分配成功
- ioctl(_deviceFd, VIDIOC_QUERYBUF, &buffer)
- //1.3 将内存映射得到的buffer指针保存到缓冲池。
- _pool[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
- MAP_SHARED, _deviceFd, buffer.m.offset);
- //1.4 将空闲的内存加入可捕获视频的队列
- ioctl(_deviceFd, VIDIOC_QBUF, &buffer)
- //2 共享内存使用
- //2.1 将已经捕获好视频的内存拉出已捕获视频的队列
- ioctl(_deviceFd, VIDIOC_DQBUF, &buf)
- //2.2 将空闲的内存加入可捕获视频的队列
- ioctl(_deviceFd, VIDIOC_QBUF, &buf)
- //3 共享内存释放
- //3.1 释放共享内存
- munmap(_pool[i].start, _pool[i].length);
- //3.2 关闭视频流
- ioctl(_deviceFd, VIDIOC_STREAMOFF, &type)
互斥锁使用,这里主要是为了保护采集线程与开关线程之间资源竞争问题,防止设备被关闭有采集线程仍然在读设备造成异常。

采集模块主要进行数据流的整合,与外界进行数据交互,处理通用操作,而屏蔽平台的实现细节。
其核心函数是IncomingFrame主要实现功能是将平台内部的YUV数据帧,转换为I420帧格式,并且调用回调将视频帧回传到视频引擎。
- //创建I420帧
- I420Buffer::Create
- //将内部YUV数据转成I420
- libyuv::ConvertToI420
- //创建视频帧
- VideoFrame::Builder()
- //回调函数传出视频帧
- DeliverCapturedFrame
帧率计算:这里采用的是平滑算法,分配90元素的数组,利用滑动的窗口计算最近2s内的帧数,同时保存当前帧的时间(ns);计算帧率是利用当前时间减去两秒内最先记录的时间差,再通过帧数除以时间差,得到当前的帧率。
本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击领取↓↓