当你使用 RTC 实现实时音视频通信时,RTC 会使用默认的音视频模块进行视频采集和渲染。在一些场景下,你可能会发现默认模块无法满足需求,比如:

在这些场景下,你可以参考本文,将自定义设备采集的视频源,交由 RTC 进行编码和传输。

使用 setVideoSourceType 指定自定义视频源。你需要使用自采集模块驱动采集设备对视频进行采集,采集的视频帧通过 pushExternalVideoFrame 发送给 SDK。

前提条件

你已经集成了 3.33 及以上版本的 RTC SDK,实现了基本的音视频通话。

集成步骤
  1. 通过调用 setVideoSourceType 指定自定义视频源。
  2. 你必须自行实现视频采集逻辑。
  3. 你可以使用 pushExternalVideoFrame 中的 VideoFrame 参数,对采集到的视频数据进行编码前处理。比如,设置 VideoFrame 参数中的 rotation 为 180,即可使视频帧顺时针旋转 180 度。当然,你也可以加入自行实现的其它前处理逻辑。
  4. 调用 pushExternalVideoFrame 将采集到的视频帧,交由 RTC 进行编码和传输。

你必须根据自采集视频编码数据格式,使用对应的 VideoFrame 参数:

API 调用时序可以参考下图:

alt

自定义视频采集的数据流转如下图:

image.png

API 参考

以下是各个平台的相关 API 参考:

示例代码

说明

  • 以下代码基于 3.33+,此版本下,视频封装类为 VideoFrame。
  • 3.32 到 3.18 版本中,视频封装类为 RtcVideoFrame,和 VideoFrame 的差别仅在于名称不同。
  • 对于 3.18 以下的版本,视频封装类为 ExtVideoFrame,和 VideoFrame 的差别仅在于:ExtVideoFrame 中不包含 buffer_type。

参考以下 java 代码,在你的应用中使用自定义视频采集功能。

  1. 通过调用 setVideoSourceType 指定自定义视频源。
// 指定自定义视频源
mInstance.setVideoSourceType(VIDEO_SOURCE_TYPE_EXTERNAL);
java
  1. 自行实现视频采集模块,并开始视频自采集。
  2. 外部采集源视频帧更新后,进行预览渲染。
  3. 调用 pushExternalVideoFrame 将采集到的视频帧,交由 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());
    }
}    
java
YUV420 码流
private 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);
	});
}
java
RGBA 格式
public 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
相关文档

切换自定义和内部视频采集/编码模块