当你使用 RTC 实现实时音视频通信时,RTC 会使用默认的音视频模块进行视频采集和渲染。在一些场景下,你可能会发现默认模块无法满足需求,比如:
在这些场景下,你可以参考本文,将自定义设备采集的视频源,交由 RTC 进行编码和传输。
使用 setVideoSourceType 指定自定义视频源。你需要使用自采集模块驱动采集设备对视频进行采集,采集的视频帧通过 pushExternalVideoFrame 发送给 SDK。
你已经集成了 3.33 及以上版本的 RTC SDK,实现了基本的音视频通话。
setVideoSourceType 指定自定义视频源。pushExternalVideoFrame 中的 VideoFrame 参数,对采集到的视频数据进行编码前处理。比如,设置 VideoFrame 参数中的 rotation 为 180,即可使视频帧顺时针旋转 180 度。当然,你也可以加入自行实现的其它前处理逻辑。pushExternalVideoFrame 将采集到的视频帧,交由 RTC 进行编码和传输。你必须根据自采集视频编码数据格式,使用对应的
VideoFrame参数:
API 调用时序可以参考下图:

自定义视频采集的数据流转如下图:
以下是各个平台的相关 API 参考:
说明
VideoFrame。RtcVideoFrame,和 VideoFrame 的差别仅在于名称不同。ExtVideoFrame,和 VideoFrame 的差别仅在于:ExtVideoFrame 中不包含 buffer_type。参考以下 java 代码,在你的应用中使用自定义视频采集功能。
setVideoSourceType 指定自定义视频源。// 指定自定义视频源
mInstance.setVideoSourceType(VIDEO_SOURCE_TYPE_EXTERNAL);
javapushExternalVideoFrame 将采集到的视频帧,交由 RTC 进行编码和传输。根据您使用的视频采集设备的不同,采集输出的视频信号可能会采取不同的视频编码方式。你需要根据不同的视频编码方式,采用不同的参数。
以下演示采集视频分别采用纹理方案,YUV420 码流,和 RGBA 格式时的代码。
public void frameAvailable(SurfaceTexture texture) {
// 预览渲染逻辑
...
// 设置外部视频帧
GLTextureVideoFrameBuilder builder = new GLTextureVideoFrameBuilder(VideoPixelFormat.kVideoPixelFormatTextureOES)//或 TEXTURE_2D
.setEGLContext(sharedEglContext)
.setTextureID(textureID)
.setTextureMatrix(transformMatrix)
.setWidth(videoWidth)
.setHeight(videoHeight)
.setRotation(VideoRotation.VIDEO_ROTATION_270)//设置旋转角度
.setTimeStampUs(System.currentTimeMillis() * TimeUnit.MILLISECONDS.toNanos(1))//设置时间戳
//继续处理后续视频帧,直到处理完最后一帧
...
// 向 SDK 推送外部视频帧
if (mVideoCallback != null && mIsPushExternalVideoSource) {
mVideoCallback.pushExternalVideoFrame(builder.build());
}
}
javaprivate void initCamera(SurfaceTexture surfaceTexture, Camera.PreviewCallback previewCallback) {
// 预览渲染逻辑
...
// 设置外部视频帧
mPushStreamHandler.post(() -> {
RTCVideoFrame videoFrame = new RTCVideoFrame();
videoFrame.format = RTCVideoFrame.PixelFormat.I420; //或 NV12,NV21
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = parameters.getPreviewSize();
videoFrame.stride = size.width;
videoFrame.height = size.height;
videoFrame.rotation = 270; //设置旋转角度
byte[] arr = CommonUtil.nv21ToI420(data, videoFrame.stride, videoFrame.height);
videoFrame.buffer = ByteBuffer.wrap(arr);
videoFrame.timestamp = System.currentTimeMillis() * TimeUnit.MILLISECONDS.toNanos(1); //设置时间戳
//向 SDK 推送外部视频帧
if (mInstance != null) {
mInstance.pushExternalVideoFrame(videoFrame);
});
}
javapublic VideoFrame createRGBAFrame(byte[] RGBABuffer, ColorSpace colorSpace) {
CpuBufferVideoFrameBuilder builder = new CpuBufferVideoFrameBuilder(VideoPixelFormat.kVideoPixelFormatRGBA);
builder.setWidth(width)
.setHeight(height)
.setRotation(VideoRotation.VIDEO_ROTATION_0)
.setTimeStampUs(System.currentTimeMillis() * TimeUnit.MILLISECONDS.toNanos(1))
.setPlaneData(0, RGBABuffer)
.setColorSpace(colorSpace)
.setPlaneStride(0, width * 4);
return builder.build();
}
java