实现 Alpha 透明特效

  • Android

更新时间不可用

了解实时互动 RTC 在不同平台上的 Alpha 透明特效方式,包括接入流程、关键配置和平台差异。

场景概述

在各种实时音视频互动场景下,将主播的背景进行人像分割和透明特效处理可以使互动过程更加有趣,提升互动的沉浸感、改善互动体验。以下是一些比较典型的场景:

  • 动态礼物特效:提供透明背景的动态礼物效果,避免多视频流合并时遮挡直播内容。
  • 主播背景替换:观众端可以将画面中的主播背景替换成虚拟场景,比如游戏场景、发布会、旅游景点等。
  • 游戏直播时对主播抠图:观众端可以对主播人像进行抠图,然后放在本地游戏画面的某个位置,使主播看起来像是在游戏中。

前提条件

已经在项目中实现了基本的实时音视频功能。详见实现音视频互动。

实现方案

根据实际业务场景,在以下方案中选择一种实现 Alpha 透明特效的方法。

信息

本文的 API 和示例代码均以 Android 平台为例。如目标平台为其他平台,请参考对应平台的 API 文档。

自定义视频采集场景

该场景实现流程如下图所示:

  1. 将采集到的视频数据推送到 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);
  2. 渲染本地视图并实现 Alpha 透明特效。

    1. 创建一个 TextureView 对象用于渲染本地视图。
    2. 调用 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);
  3. 渲染远端视频流在本地的视图并实现 Alpha 透明特效。

    1. 在收到 onUserJoined 回调后,创建一个 TextureView 对象用于渲染远端视图。
    2. 调用 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 采集场景

该场景实现流程如下图所示:

  1. 在发送端调用 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);
  2. 在发送端调用 setVideoEncoderConfiguration 设置视频编码属性,将 encodeAlpha 设置为 true,则 Alpha 数据会被编码并发送到远端。

    Java
    VideoEncoderConfiguration videoEncoderConfiguration = new VideoEncoderConfiguration(...);
    videoEncoderConfiguration.advanceOptions = new VideoEncoderConfiguration.AdvanceOptions(...);
    videoEncoderConfiguration.advanceOptions.encodeAlpha = true;
    rtcEngine.setVideoEncoderConfiguration(videoEncoderConfiguration);
  3. 渲染本地和远端视图并实现 Alpha 透明特效。详见自定义视频采集场景中的步骤。

原始视频数据场景

该场景实现流程如下图所示:

  1. 调用 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);
  2. 通过 onCaptureVideoFrame 回调获取采集后的视频数据并根据需要进行前处理,可以修改 Alpha 数据或直接添加 Alpha 数据。

    Java
    public boolean onCaptureVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
  3. 通过 onPreEncodeVideoFrame 回调获取本地视频编码前的视频数据,可以修改 Alpha 数据或直接添加 Alpha 数据。

    Java
    public boolean onPreEncodeVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
  4. 通过 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 属性。