实现 Alpha 透明特效
- Android
更新时间不可用
了解实时互动 RTC 在不同平台上的 Alpha 透明特效方式,包括接入流程、关键配置和平台差异。
场景概述
在各种实时音视频互动场景下,将主播的背景进行人像分割和透明特效处理可以使互动过程更加有趣,提升互动的沉浸感、改善互动体验。以下是一些比较典型的场景:
- 动态礼物特效:提供透明背景的动态礼物效果,避免多视频流合并时遮挡直播内容。
- 主播背景替换:观众端可以将画面中的主播背景替换成虚拟场景,比如游戏场景、发布会、旅游景点等。
- 游戏直播时对主播抠图:观众端可以对主播人像进行抠图,然后放在本地游戏画面的某个位置,使主播看起来像是在游戏中。
前提条件
已经在项目中实现了基本的实时音视频功能。详见实现音视频互动。
实现方案
根据实际业务场景,在以下方案中选择一种实现 Alpha 透明特效的方法。
信息
本文的 API 和示例代码均以 Android 平台为例。如目标平台为其他平台,请参考对应平台的 API 文档。
自定义视频采集场景
该场景实现流程如下图所示:
-
将采集到的视频数据推送到 SDK,同时设置视频帧中的 Alpha 数据。有以下两种方式可供选择。
-
方案一:调用
pushExternalVideoFrame[2/2] 方法,并通过设置alphaBuffer参数为视频帧指定 Alpha 通道数据。该数据跟视频帧的尺寸一致,每个像素点的取值范围为 [0,255],其中 0 代表背景;255 代表前景(人像)。警告
请务必确保
alphaBuffer跟视频帧的尺寸 (width × height) 完全一致,否则可能会导致 App 崩溃。Java JavaI420Buffer javaI420Buffer = JavaI420Buffer.wrap(width, height, dataY, width, dataU, strideUV, dataV, strideUV, null); VideoFrame frame = new VideoFrame(javaI420Buffer, 0, timestamp); ByteBuffer alphaBuffer = ByteBuffer.allocateDirect(width * height); frame.fillAlphaData(alphaBuffer); rtcEngine.pushExternalVideoFrame(frame); -
方案二:调用
pushExternalVideoFrame[2/2] 方法,并通过调用VideoFrame类中的setAlphaStitchMode方法设置 Alpha 的拼接方式,构造一个拼接了 Alpha 数据的VideoFrame。Java JavaI420Buffer javaI420Buffer = JavaI420Buffer.wrap(width, height, dataY, width, dataU, strideUV, dataV, strideUV, null); VideoFrame frame = new VideoFrame(javaI420Buffer, 0, timestamp); // 设置 Alpha 拼接方式,以下示例代码中设置 Alpha 在视频图像下方 frame.setAlphaStitchMode(Constants.VIDEO_ALPHA_STITCH_BELOW); rtcEngine.pushExternalVideoFrame(frame);
-
-
渲染本地视图并实现 Alpha 透明特效。
- 创建一个
TextureView对象用于渲染本地视图。 - 调用
setupLocalVideo方法设置本地视图:- 设置
enableAlphaMask参数为true,开启 Alpha 遮罩渲染。 - 设置
TextureView为本地视频的显示窗口。
- 设置
Java // 发送端有 Alpha 数据输入,并且开启 Alpha 传输 VideoEncoderConfiguration config = new VideoEncoderConfiguration(...); // 设置编码参数的时候需要开启 Alpha 传输 config.advanceOptions.encodeAlpha = true; rtcEngine.setVideoEncoderConfiguration(videoEncoderConfiguration) TextureView textureView = new TextureView(context); textureView.setOpaque(false); fl_local.addView(textureView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); VideoCanvas local = new VideoCanvas(textureView, RENDER_MODE_FIT, 0); local.enableAlphaMask = true; rtcEngine.setupLocalVideo(local); - 创建一个
-
渲染远端视频流在本地的视图并实现 Alpha 透明特效。
- 在收到
onUserJoined回调后,创建一个TextureView对象用于渲染远端视图。 - 调用
setupRemoteVideo方法设置远端视图:- 设置
enableAlphaMask参数为true,开启 Alpha 遮罩渲染。 - 设置远端视频流在本地视频的显示窗口。
- 设置
Java TextureView textureView = new TextureView(context); textureView.setOpaque(false); fl_remote.addView(textureView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); VideoCanvas remote = new VideoCanvas(textureView, VideoCanvas.RENDER_MODE_FIT, uid); remote.enableAlphaMask = true; rtcEngine.setupRemoteVideo(remote); - 在收到
SDK 采集场景
该场景实现流程如下图所示:
-
在发送端调用
enableVirtualBackground[2/2] 开启背景分割算法,得到人像区域的 Alpha 数据。建议设置参数如下:enabled:设置为true,开启虚拟背景。backgroundSourceType:设置为BACKGROUND_NONE(0),即:将人像和背景进行分割,并将背景处理为 Alpha 数据。
Java VirtualBackgroundSource virtualBackgroundSource = new VirtualBackgroundSource(...); virtualBackgroundSource.backgroundSourceType = VirtualBackgroundSource.BACKGROUND_NONE;//仅生成alpha,不替换背景 SegmentationProperty segmentationProperty = new SegmentationProperty(...); rtcEngine.enableVirtualBackground(true, virtualBackgroundSource, segmentationProperty, sourceType); -
在发送端调用
setVideoEncoderConfiguration设置视频编码属性,将encodeAlpha设置为true,则 Alpha 数据会被编码并发送到远端。Java VideoEncoderConfiguration videoEncoderConfiguration = new VideoEncoderConfiguration(...); videoEncoderConfiguration.advanceOptions = new VideoEncoderConfiguration.AdvanceOptions(...); videoEncoderConfiguration.advanceOptions.encodeAlpha = true; rtcEngine.setVideoEncoderConfiguration(videoEncoderConfiguration); -
渲染本地和远端视图并实现 Alpha 透明特效。详见自定义视频采集场景中的步骤。
原始视频数据场景
该场景实现流程如下图所示:
-
调用
registerVideoFrameObserver方法注册一个原始视频观测器,并根据需要注册相应回调。Java // 注册 IVideoFrameObserver public class MyVideoFrameObserver implements IVideoFrameObserver{ @Override public boolean onRenderVideoFrame(String channelId, int uId, VideoFrame videoFrame) { ... return false; } @Override public boolean onCaptureVideoFrame(int type, VideoFrame videoFrame) { ... return false; } @Override public boolean onPreEncodeVideoFrame(int type, VideoFrame videoFrame) { ... return false; } } MyVideoFrameObserver observer = new MyVideoFrameObserver(); rtcEngine.registerVideoFrameObserver(observer); -
通过
onCaptureVideoFrame回调获取采集后的视频数据并根据需要进行前处理,可以修改 Alpha 数据或直接添加 Alpha 数据。Java public boolean onCaptureVideoFrame(int type, VideoFrame videoFrame) { // 修改 Alpha 数据或直接添加 Alpha 数据 ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer(); ... videoFrame.fillAlphaData(byteBuffer); return false; } -
通过
onPreEncodeVideoFrame回调获取本地视频编码前的视频数据,可以修改 Alpha 数据或直接添加 Alpha 数据。Java public boolean onPreEncodeVideoFrame(int type, VideoFrame videoFrame) { // 修改 Alpha 数据或直接添加 Alpha 数据 ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer(); ... videoFrame.fillAlphaData(byteBuffer); return false; } -
通过
onRenderVideoFrame回调获取远端发送的视频在本地渲染前的视频数据,可以修改 Alpha 数据、直接添加 Alpha 数据或根据得到的 Alpha 数据自行渲染视频图像。Java public boolean onRenderVideoFrame(int type, VideoFrame videoFrame) { // 修改 Alpha 数据、直接添加 Alpha 数据或根据得到的 Alpha 数据自行渲染视频图像 ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer(); ... videoFrame.fillAlphaData(byteBuffer); return false; }
开发注意事项
你需要自行实现 App 窗口的透明度属性,并处理多个 App 窗口叠加时的透明度关系(通过调整窗口的 zOrder 实现)。除此之外,对于不同的目标平台,需遵循以下限制:
- 对于 Android 平台,由于系统限制,设置视图时仅支持使用
TextureView、不支持使用SurfaceView。 - 对于 iOS 平台,在设置视图时需要将该视图
layer属性中的opaque设置为false。 - 对于 macOS 平台,不需要额外设置窗口的透明属性,也可以遵循上一条 iOS 的设置方法。
- 对于 Windows 平台,你需要在创建项目时使用
CreateWindowEx创建单独的窗口,并设置高透明属性。如果目标窗口是子窗口,则需要额外指定WS_POPUP属性。