iOS端WebRTC如何提取RTCVideoFrame实现远端视频实时录制
实现方案
你需要通过实现WebRTC提供的RTCVideoSink协议接收实时RTCVideoFrame,这是官方提供的标准帧提取入口,不会影响原有视频渲染链路。
步骤1:创建自定义帧接收器
实现RTCVideoSink协议,在回调方法中获取每帧视频数据:
import WebRTC class VideoRecordSink: NSObject, RTCVideoSink { // 定义帧回调闭包,你可以在这里把RTCVideoFrame转成CMSampleBuffer喂给AVAssetWriter var onFrameCaptured: ((RTCVideoFrame) -> Void)? func onCapturedFrame(_ frame: RTCVideoFrame) { // 注意:这个回调默认在WebRTC内部队列执行,不要直接操作UI onFrameCaptured?(frame) } }
步骤2:给远程视频轨绑定接收器
在你获取到remoteVideoTrack的位置(也就是RTCPeerConnectionDelegate的didAdd stream方法中),给视频轨添加你自定义的sink:
首先你需要在homeViewModel中持有sink实例,避免被释放导致回调中断:
// 先在homeViewModel中添加属性 var recordSink: VideoRecordSink?
修改代理方法:
func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) { dLog("") remoteVideoTrack = stream.videoTracks.first remoteVideoTrack?.isEnabled = true refreshRemoteVideoTrack = true // 初始化帧接收器,绑定帧回调 let recordSink = VideoRecordSink() recordSink.onFrameCaptured = { [weak self] frame in // 在这里处理帧转换、写入AVAssetWriter的逻辑 guard let self = self else { return } // 你已经知道的RTCVideoFrame转CMSampleBuffer、写入逻辑放在这里即可 } // 给视频轨添加sink remoteVideoTrack?.add(recordSink) // 持有sink实例,避免释放 self.recordSink = recordSink }
注意事项
- 停止录制的时候需要调用
remoteVideoTrack?.remove(recordSink)移除接收器,避免内存泄漏 onCapturedFrame回调不在主线程,你可以把帧处理、写入逻辑放到专门的串行队列执行,避免阻塞WebRTC线程- 要确保
AVAssetWriter已经调用startWriting和startSession(atSourceTime:)之后再开始写入CMSampleBuffer,避免写入失败 - 同一个视频轨可以添加多个sink,新增的录制sink不会影响原有
RTCEAGLVideoView的渲染逻辑
内容的提问来源于stack exchange,提问作者윤지훈
相关产品推荐
相关产品推荐

