背景:要在视频通话中进行相关录制,具体要求如下:
(1) 视频通话录像,并保存为AVI格式,并上传到服务端,服务端下发给设备message部分,但是管理机本身不存储录像;
(2) 能监控其他的设备,并且支持录像
因此需要现在Android系统支持视频录制和监控录制两种类型录制。在设备上是有开发相关录制功能的,但是设备整体硬件和性能决定了之前的框架运行是有问题,同时通话录制和监控录制分数不同的框架,因此需要两套框架都支持。
1.Android相机框架
Android相机框架
1.1 应用框架
应用代码位于应用框架级别,它使用
Camera 2
API 与相机硬件进行交互。在内部,这些代码会调用相应的
Binder
接口,以访问与相机互动的原生代码。
1.2 AIDL
与 CameraService 关联的 Binder 接口可在
frameworks/av/camera/aidl/android/hardware
中找到。生成的代码会调用较低级别的原生代码以获取对实体相机的访问权限,并返回用于在框架级别创建
CameraDevice
并最终创建
CameraCaptureSession
对象的数据。
1.3 原生框架
此框架位于 frameworks/av/ 中,并提供相当于
CameraDevice
和
CameraCaptureSession
类的原生类。另请参阅
NDK camera2 参考
。
1.4 Binder IPC 接口
IPC binder 接口用于实现跨越进程边界的通信。调用相机服务的若干个相机 Binder 类位于 frameworks/av/camera/camera/aidl/android/hardware 目录中。
ICameraService
是相机服务的接口;
ICameraDeviceUser
是已打开的特定相机设备的接口;
ICameraServiceListener
和
ICameraDeviceCallbacks
分别是对应用框架的 CameraService 和 CameraDevice 回调。
1.5 相机服务
位于 frameworks/av/services/camera/libcameraservice/CameraService。cpp 下的相机服务是与 HAL 进行互动的实际代码。
1.6 HAL
硬件抽象层定义了由相机服务调用、且您必须实现以确保相机硬件正常运行的标准接口。
2.Webrtc部分
每个路径对应一个模块, 每个模块的功能大致为:
组件
描述
common_audio
声音通用函数, 主要放了抽象出来的声音处理函数
common_video
视频通用函数, 主要放了抽象出来的图像处理函数
media
媒体相关内容目录
modules
各种模块, 包括codecs, neteq, 混音, 比特率控制, 工具类等
system_wrapper
操作系统相关库函数接口,具体实现分操作系统win, linux,android,mac
audio
语音引擎, 感觉有点像是逻辑层, 处理所有语音相关的操作逻辑以及统计一些信息, 对audio channel的管理与维护
video
视频引擎, 视频相关的操作逻辑及统计信息, 对video channel的管理与维护
2.1 WebRTC中录制框架:
2.1.1 音频录制流程
音频录制流程主要又可分为三个子流程, 即: 开启音频录制流程/音频录制进行中的流程/关闭音频录制流程。
2.1.1.1 开启/关闭音频录制流程
webrtc对Java层提供了ave_VoE_StartRecording和ave_VoE_StopRecording两个接口用于开启/关闭音频录制。
开启音频录制流程:
关闭音频录制流程:
2.1.1.2音频录制进行中的流程
音频录制进行中的流程为整个录制的核心, 其核心为TransmitMixer对象,该对象可以获取到每个通道及本地麦克风的音频数据, 然后对它们统一进行混音,再通过文件句柄写入文件中。 与视频录制流程不同, 这个过程是统一的, 且与采集音频数据属于同一个线程。音频采集, 混合, 写入文件流程:
2.1.2 图像采集流程
webrtc中图像采集流程是从VideoCaptureAndroid中始发,并将采集好的图像返回给webrtc中ViECapture类进行处理,包括融合、编码、发送等。
2.1.3 视频录制流程
视频录制流程主要又可分为三个子流程, 即: 开启视频录制流程/视频录制进行中的流程/关闭视频录制流程。
2.1.3.1开启/关闭视频录制流程
webrtc对Java层提供了ave_ViE_StartRecording和ave_ViE_StopRecording两个接口用于开启/关闭视频录制。
开启视频录制流程:
关闭视频录制流程:
视频录制进行中的流程为整个录制的核心, 其核心为Capturer对象及录制线程, Capturer对象可以获取到每个通道及本地摄像头的图像数据,然后对它们统一进行混合, 再添加到Buffer中。 录制线程则通过Timer对Buffer进行消化, 将图像数据编码并通过文件句柄写入文件中。与音频录制流程不同, 录制的过程中,图像采集混合与声音采集混合均在各自线程中处理,图像编码和文件写入则在开启录制时创建的线程中处理, 总共涉及到3个线程的操作。
声音采集混合流程3。2 音频录制流程中提到的音频录制过程中的流程基本一致,唯一不同的是音频录制中是将混合好的音频数据直接写入文件, 而在视频录制中,则是将混合好的音频数据写到音频队列Buffer中,用于在独立的录制线程与视频数据一起写入文件。
2.1.3.2 图像编码及写文件流程
图像编码及文件写入流程(以AviRecorder::Process作为对象分析):
2.2 Webrtc录制中的问题
2.2.1 录制卡顿问题
录制时候音频和视频是两个不同的线程,同时视频是按照接收数据解码后和本地数据融合mix后,再进行编码,然后写入数据文件的。因此就增加性能开销,大部分时间都消耗到mix和encode上,cpu占用比较大,同时会丢失掉非常多的其他视频数据帧,因此录制的视频表现为非常卡顿,但是声音是正常的。
2.2.2 FrameRate问题
针对AVI文件,在WebRTC代码实现中,AVI头是最先生成头文件内容,再按照音视频数据一帧一帧交叉写入的。因此这时候的帧率等相关视频数据按照默认值直接写入的。相对设备性能差距蛮大的,因此这时写入的数据并不是真实的视频参数,因此导致视频播放卡顿现象。
2.2.3 音视频不同步问题
\1. 音视频数据量不一致;
音视频采集和写文件线程都是独立的,而音频采集数据和接收数据都比较稳定,写文件也比较稳定,基本上10ms一帧数据进行写文件。而视频数据比较大,接收时候速率不稳定,容易造成一帧数据在不同线程中处理时间不确定,容易丢失一部分数据。
\2. 网络稳定性导致接收视频速率不稳定;
3.Webrtc中解决方案
3.1 增加缓存
增加video缓存机制,最大限度保存所有接受到的视频帧。
3.2 移除编码
因为图像编码需要消耗太多时间,而客户需求中并不需要本地视频,因此将编码步骤从整个流程中剔除。
3.3 帧率统计
视频不同步,通话过程中,帧率是动态变化的,按照之前写死一个固定值的方式,可能导致不能完全匹配所有的情况,导致音视频不同步;同时最初的统计值也是不准确的。
l 新增方案:按照音频的时长和视频帧数计算帧率;
l 优选方案:开始录制和结束录制分别记录时间点,然后用两者时间差和帧数计算帧率;
两者都存在误差,但是优选方案会更接近实际帧率。
3.4 新技术探讨
(1)丢帧和补帧策略:
就是输入帧率是25,输出帧率是30.或者30到25等等帧率由小到大,或由大到小,且输出帧率是固定帧率。这种情况下,就涉及到要做丢帧或补帧处理。比如输入25,输出30,那么就需要补5帧,如果输入30,输出25,那么就需要丢5帧。问题关键在于应该丢哪些帧,补哪些帧。一般我们觉得比如30->25,丢5帧,那么是不是可以每6帧丢一帧,刚好30帧能丢5帧。
(2)录制格式自定义:
现在客户要求是视频录制为AVI和音频为WAV格式,可以增加不同格式的选项,WebRTC中可以支持的各种格式录制。这个需要WebRTC全方位支撑,包括特定格式编码和解码、特定格式文件操作等。
(3)录制SDK封装:
将录制功能封装成简易的SDK,方便提供给客户进行二次开发。
★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。
见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓