自定义视频采集
默认的声网视频模块可以与设备上已安装的 App 实现无缝互动。另外,声网 SDK 也支持使用自定义视频源,为你的 App 增加更多视频特性。
实时视频传输过程中,声网 SDK 通常会启动默认的视频模块进行采集。然而,在以下场景中,你可能会发现默认的视频模块无法满足开发需求。例如:
- App 中已有自己的视频模块。
- 希望使用非摄像头采集的视频源,如录屏数据等。
- 需要使用自定义的前处理库(如美颜库)处理视频。
- 某些视频采集设备被系统独占。为避免与其他业务产生冲突,需要灵活的设备管理策略。
使用自定义视频渲染器管理视频采集和播放时,需要自行调用声网 SDK 以外的方法实现。
技术原理
声网 SDK 提供 setExternalVideoSource
和 pushExternalVideoFrame
方法自定义视频采集。下图展示自定义视频采集 API 调用时序:
视频数据传输
下图展示在 Push 模式下进行自定义视频采集时,视频数据的传输过程:
- 你需要使用 SDK 外部方法自行实现采集模块。
- 通过
pushExternalVideoFrame
方法,将采集到的视频帧发送至 SDK。
前提条件
在进行操作之前,请确保你已经在项目中实现了基本的实时音视频功能,详见实现音视频互动。
实现方法
参考如下步骤,在你的项目中实现自定义视频采集功能:
-
加入频道前,调用
setExternalVideoSource
开启自定义视频采集。一旦开启后,你将无法使用 SDK 中的方法采集视频帧。Swift// 调用 setExternalVideoSource 通知 SDK 你的 App 在使用自定义视频采集
agoraKit.setExternalVideoSource(true, useTexture: true, encodedFrame: true) -
实现自定义视频采集。一旦开启自定义视频采集,你需要调用 SDK 以外的方法,自行实现视频的采集。例如,示例项目定义了
AgoraCameraSourcePush
类,并使用系统原生方法实现视频采集。Swiftclass AgoraCameraSourcePush: NSObject {
fileprivate var delegate: AgoraCameraSourcePushDelegate?
private var videoView: CustomVideoSourcePreview
private var currentCamera = Camera.defaultCamera()
private let captureSession: AVCaptureSession
private let captureQueue: DispatchQueue
private var currentOutput: AVCaptureVideoDataOutput? {
if let outputs = self.captureSession.outputs as? [AVCaptureVideoDataOutput] {
return outputs.first
} else {
return nil
}
}
// 初始化自定义视频采集
init(delegate: AgoraCameraSourcePushDelegate?, videoView: CustomVideoSourcePreview) {
self.delegate = delegate
self.videoView = videoView
captureSession = AVCaptureSession()
captureSession.usesApplicationAudioSession = false
let captureOutput = AVCaptureVideoDataOutput()
captureOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]
if captureSession.canAddOutput(captureOutput) {
captureSession.addOutput(captureOutput)
}
captureQueue = DispatchQueue(label: "MyCaptureQueue")
// 将采集到的画面在图层上进行渲染
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoView.insertCaptureVideoPreviewLayer(previewLayer: previewLayer)
}
deinit {
captureSession.stopRunning()
}
// 开始视频帧采集
func startCapture(ofCamera camera: Camera) {
guard let currentOutput = currentOutput else {
return
}
// 将采集设备设置为当前摄像头
currentCamera = camera
currentOutput.setSampleBufferDelegate(self, queue: captureQueue)
captureQueue.async { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.changeCaptureDevice(toIndex: camera.rawValue, ofSession: strongSelf.captureSession)
strongSelf.captureSession.beginConfiguration()
if strongSelf.captureSession.canSetSessionPreset(AVCaptureSession.Preset.vga640x480) {
strongSelf.captureSession.sessionPreset = AVCaptureSession.Preset.vga640x480
}
strongSelf.captureSession.commitConfiguration()
strongSelf.captureSession.startRunning()
}
}
// 结束视频帧采集
func stopCapture() {
currentOutput?.setSampleBufferDelegate(nil, queue: nil)
captureQueue.async { [weak self] in
self?.captureSession.stopRunning()
}
}
// 切换采集摄像头
func switchCamera() {
stopCapture()
currentCamera = currentCamera.next()
startCapture(ofCamera: currentCamera)
}
}AgoraCameraSourcePushDelegate
类被用于接收采集到的视频帧。Swiftprotocol AgoraCameraSourcePushDelegate {
func myVideoCapture(_ capture: AgoraCameraSourcePush, didOutputSampleBuffer pixelBuffer: CVPixelBuffer, rotation: Int, timeStamp: CMTime)
} -
实现自定义视频渲染。在 push 模式下,声网 SDK 不支持对采集到的视频进行渲染。因此,你需要调用 SDK 外部方法实现自定义视频渲染。在示例项目中,我们基于系统原生的
AVCaptureVideoPreviewLayer
类定义了一个CustomVideoSourcePreview
类。Swift// 初始化 localVideo
var localVideo = CustomVideoSourcePreview(frame: CGRect.zero)
// 定义 CustomVideoSourcePreview 类
class CustomVideoSourcePreview : UIView {
private var previewLayer: AVCaptureVideoPreviewLayer?
func insertCaptureVideoPreviewLayer(previewLayer: AVCaptureVideoPreviewLayer) {
self.previewLayer?.removeFromSuperlayer()
previewLayer.frame = bounds
layer.insertSublayer(previewLayer, below: layer.sublayers?.first)
self.previewLayer = previewLayer
}
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
previewLayer?.frame = bounds
}
} -
开始视频帧的采集和渲染。在示例项目中,我们基于
AgoraCameraSourcePush
类创建了一个customCamera
实例,之后调用startCapture
开始进行采集和渲染。Swift// 初始化 AgoraCameraSourcePush 类,将摄像头设置为采集设备
customCamera = AgoraCameraSourcePush(delegate: self, videoView:localVideo)
// 调用 AgoraCameraSourcePush 类中的 startCapture 方法,开始采集视频帧
customCamera?.startCapture(ofCamera: .defaultCamera()) -
将采集到的视频帧推送至 SDK。调用
pushExternalVideoFrame
方法,将采集到的视频帧推送至 SDK。Swiftextension CustomVideoSourcePushMain:AgoraCameraSourcePushDelegate
{
func myVideoCapture(_ capture: AgoraCameraSourcePush, didOutputSampleBuffer pixelBuffer: CVPixelBuffer, rotation: Int, timeStamp: CMTime) {
let videoFrame = AgoraVideoFrame()
videoFrame.format = 12
videoFrame.textureBuf = pixelBuffer
videoFrame.time = timeStamp
videoFrame.rotation = Int32(rotation)
// 将采集到的视频帧推送至 SDK
agoraKit?.pushExternalVideoFrame(videoFrame)
}
}
参考信息
本节介绍本文中使用方法的更多信息以及相关页面的链接。
示例项目
声网在 GitHub 上提供了一个开源的示例项目。