背景
用webrtc实现P2P的屏幕+声音共享,不使用云端信令服务器,分享屏幕的同时,推送当前系统播放的声音,实现类似云渲染的效果。
分析
- 信令
- 信令服务是必须存在的,否则无法在设备间交换WEBRTC的SDP和ICE Candidate以及业务的通话指令,在局域网,可以在其中一个设备上,把自己当成信令服务器,让其它端通过IP地址或者扫码连接的方式,连接上这个信令服务器,建立socket或者websocket连接。
- 在github可以找到现成的,局域网内直接能用的扫码连接P2P通话的工程:github.com/meshenger-a…
- 编码
- 低端手机上,性能不够,所以需要选择合适的硬编码器,提升编码效率,最新版本的官方SDK已经支持H265硬编码了,所以在编码器的优先级设置上,把H265设置为优先选项,具体逻辑参考工程内的: HardwareExtendedVideoEncoderFactory.kt
- 延迟
- webrtc内部使用jitter做抖动缓冲,但是也间接的导致了延迟的增加,在部分场景下,宁愿牺牲流畅性,换来更低的延时,之前测试过在webrtc的m115版本中,可以通过字段设置,减少延迟,实测收益能达到30ms以上
strFieldTrails += "WebRTC-ForcePlayoutDelay/min_ms:0,max_ms:50/"
PeerConnectionFactory.InitializationOptions.builder(callActivity!!.getContext())
.setEnableInternalTracer(true)
.setFieldTrials(strFieldTrails)
.createInitializationOptions()
这里需要注意的是,之前在iOS上设置这个字段,实际效果远不如Android,主要有几个方面
- 延迟并没有明显降低 --- 可以通过修改C++层代码解决,实测能达到和Android接近的效果
- 导致开画速度很慢 --- 可以通过修改C++层代码解决,实测能达到和Android接近的效果
- 导致渲染掉帧(objc层的渲染是保留最后一帧,但是渲染的触发是按刷新率来的,例如33ms渲染一次,减少延迟导致这33ms期间有多帧已经准备好了,但是没有渲染的机会) --- 可以通过修改objc层代码解决,实测能达到和Android接近的效果
- 系统声音
- 直接用webrtc的API,就能很方便的创建本地音频
MediaConstraints->AudioSource->AudioTrack->RtpSender
但是这样创建的本地音轨,使用的是设备的麦克风,在通话场景中是合适的,但是在类似云渲染的场景下,要同时把屏幕和设备内部的播放声音一起推流出来,就需要做定制(不修改官方的SDK源码),在业务层,通过ForegroundService + mediaProjection + AudioRecord 录制系统内部音频。我们不生产代码,我们只是代码的搬运工,前面所说的录制系统声音部分,直接从github.com/ant-media/W… 这个代码仓库搬运过来即可。核心代码参考工程内的: WebRtcAudioRecord.java
其它
- 原工程使用的是摄像头呼叫的效果,把新代码中的isScreencast改成false就能回滚到原工程效果,使用摄像头呼叫通话
- demo直接运行起来,就能进行点对点的屏幕+声音分享,代码仓库:github.com/baihua666/m…
- 偶现崩溃 调试过程中,因为使用了java同类的类覆盖实现修改webrtc默认行为的方式,导致偶尔出现运行时没有加载我们写的类,出现方法找不到的崩溃问题,解决办法:
- 把官方aar下载下来,手动删除里面的class文件,再重新打包成aar的方式,新的aar已经上传,如果遇到问题,只需要把gradle文件中的加载本地aar的选项打开并关闭远程依赖就可以了
implementation 'io.github.webrtc-sdk:android:114.5735.10'
//如果出现方法找不到导致的crash,可以尝试使用本地aar(手动删除了aar中的JavaAudioDeviceModule和AudioDeviceModule)
// implementation fileTree(include: ['*.aar'], dir: 'libs')
- flutter版本
- 基于flutter-webrtc的版本,也进行了同样的定制:实现代码:github.com/baihua666/f…