# 实现 Alpha 透明特效 (/zh-CN/realtime-media/rtc/build/video/alpha-effect)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="android" />
## 场景概述 [#场景概述]

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

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

<DocsImage alt="应用场景1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo1.png" displayWidth="30%" />

<DocsImage alt="应用场景2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo2.png" displayWidth="60%" />

## 前提条件 [#前提条件]

已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方案 [#实现方案]

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

:::info[信息]
本文的 API 和示例代码均以 Android 平台为例。如目标平台为其他平台，请参考对应平台的 API 文档。
:::

### 自定义视频采集场景 [#自定义视频采集场景]

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

<DocsImage alt="自定义视频采集流程1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-1.svg" displayWidth="80%" />

1. 将采集到的视频数据推送到 SDK，同时设置视频帧中的 Alpha 数据。有以下两种方式可供选择。

   - 方案一：调用 [`pushExternalVideoFrame`[2/2]](/zh-CN/api-reference/rtc/android/video/video-custom-capturenrendering#api_irtcengine_pushvideoframe4) 方法，并通过设置 `alphaBuffer` 参数为视频帧指定 Alpha 通道数据。该数据跟视频帧的尺寸一致，每个像素点的取值范围为 [0,255]，其中 0 代表背景；255 代表前景（人像）。

     :::error[警告]
     请务必确保 `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);
     ```

1. 渲染本地视图并实现 Alpha 透明特效。

    1. 创建一个 `TextureView` 对象用于渲染本地视图。
    1. 调用 [`setupLocalVideo`](/zh-CN/api-reference/rtc/android/video/video-rendering#api_irtcengine_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);
    ```

1. 渲染远端视频流在本地的视图并实现 Alpha 透明特效。

    1. 在收到 `onUserJoined` 回调后，创建一个 `TextureView` 对象用于渲染远端视图。
    1. 调用  [`setupRemoteVideo`](/zh-CN/api-reference/rtc/android/video/video-rendering#api_irtcengine_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 采集场景 [#sdk-采集场景]

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

<DocsImage alt="自定义视频采集流程2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-2.svg" displayWidth="80%" />

1. 在发送端调用 [`enableVirtualBackground`[2/2]](/zh-CN/api-reference/rtc/android/video/video-prenpro/virtualbackground#api_irtcengine_enablevirtualbackground2) 开启背景分割算法，得到人像区域的 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`](/zh-CN/api-reference/rtc/android/video/video-basic#api_irtcengine_setvideoencoderconfiguration) 设置视频编码属性，将 `encodeAlpha` 设置为 `true`，则 Alpha 数据会被编码并发送到远端。

    ```java
    VideoEncoderConfiguration videoEncoderConfiguration = new VideoEncoderConfiguration(...);
    videoEncoderConfiguration.advanceOptions = new VideoEncoderConfiguration.AdvanceOptions(...);
    videoEncoderConfiguration.advanceOptions.encodeAlpha = true;
    rtcEngine.setVideoEncoderConfiguration(videoEncoderConfiguration);
    ```

2. 渲染本地和远端视图并实现 Alpha 透明特效。详见[自定义视频采集场景](#自定义视频采集场景)中的步骤。

### 原始视频数据场景 [#原始视频数据场景]

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

<DocsImage alt="自定义视频采集流程3" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-3.svg" displayWidth="80%" />

1. 调用 [`registerVideoFrameObserver`](/zh-CN/api-reference/rtc/android/video/video-raw#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);
    ```

1. 通过 [`onCaptureVideoFrame`](/zh-CN/api-reference/rtc/android/video/video-raw#callback_ivideoframeobserver_oncapturevideoframe) 回调获取采集后的视频数据并根据需要进行前处理，可以修改 Alpha 数据或直接添加 Alpha 数据。

    ```java
    public boolean onCaptureVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

2. 通过 [`onPreEncodeVideoFrame`](/zh-CN/api-reference/rtc/android/video/video-raw#callback_ivideoframeobserver_onpreencodevideoframe) 回调获取本地视频编码前的视频数据，可以修改 Alpha 数据或直接添加 Alpha 数据。

    ```java
    public boolean onPreEncodeVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

3. 通过 [`onRenderVideoFrame`](/zh-CN/api-reference/rtc/android/video/video-raw#callback_ivideoframeobserver_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`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw) 创建单独的窗口，并设置高透明属性。如果目标窗口是子窗口，则需要额外指定 [`WS_POPUP`](https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles) 属性。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="ios" />
## 场景概述 [#场景概述-1]

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

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

<DocsImage alt="应用场景1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo1.png" displayWidth="30%" />

<DocsImage alt="应用场景2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo2.png" displayWidth="60%" />

## 前提条件 [#前提条件-1]

已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方案 [#实现方案-1]

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

:::info[信息]
本文的 API 和示例代码均以 Android 平台为例。如目标平台为其他平台，请参考对应平台的 API 文档。
:::

### 自定义视频采集场景 [#自定义视频采集场景-1]

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

<DocsImage alt="自定义视频采集流程1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-1.svg" displayWidth="80%" />

1. 将采集到的视频数据推送到 SDK，同时设置视频帧中的 Alpha 数据。有以下两种方式可供选择。

   - 方案一：调用 [`pushExternalVideoFrame`[2/2]](/zh-CN/api-reference/rtc/android/video/video-custom-capturenrendering#api_irtcengine_pushvideoframe4) 方法，并通过设置 `alphaBuffer` 参数为视频帧指定 Alpha 通道数据。该数据跟视频帧的尺寸一致，每个像素点的取值范围为 [0,255]，其中 0 代表背景；255 代表前景（人像）。

     :::error[警告]
     请务必确保 `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);
     ```

1. 渲染本地视图并实现 Alpha 透明特效。

    1. 创建一个 `TextureView` 对象用于渲染本地视图。
    1. 调用 [`setupLocalVideo`](/zh-CN/api-reference/rtc/ios/video/video-rendering#api_irtcengine_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);
    ```

1. 渲染远端视频流在本地的视图并实现 Alpha 透明特效。

    1. 在收到 `onUserJoined` 回调后，创建一个 `TextureView` 对象用于渲染远端视图。
    1. 调用  [`setupRemoteVideo`](/zh-CN/api-reference/rtc/ios/video/video-rendering#api_irtcengine_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 采集场景 [#sdk-采集场景-1]

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

<DocsImage alt="自定义视频采集流程2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-2.svg" displayWidth="80%" />

1. 在发送端调用 [`enableVirtualBackground`[2/2]](/zh-CN/api-reference/rtc/ios/video/video-prenpro/virtualbackground#api_irtcengine_enablevirtualbackground2) 开启背景分割算法，得到人像区域的 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`](/zh-CN/api-reference/rtc/ios/video/video-basic#api_irtcengine_setvideoencoderconfiguration) 设置视频编码属性，将 `encodeAlpha` 设置为 `true`，则 Alpha 数据会被编码并发送到远端。

    ```java
    VideoEncoderConfiguration videoEncoderConfiguration = new VideoEncoderConfiguration(...);
    videoEncoderConfiguration.advanceOptions = new VideoEncoderConfiguration.AdvanceOptions(...);
    videoEncoderConfiguration.advanceOptions.encodeAlpha = true;
    rtcEngine.setVideoEncoderConfiguration(videoEncoderConfiguration);
    ```

2. 渲染本地和远端视图并实现 Alpha 透明特效。详见[自定义视频采集场景](#自定义视频采集场景-1)中的步骤。

### 原始视频数据场景 [#原始视频数据场景-1]

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

<DocsImage alt="自定义视频采集流程3" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-3.svg" displayWidth="80%" />

1. 调用 [`registerVideoFrameObserver`](/zh-CN/api-reference/rtc/ios/video/video-raw#api_irtcengine_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);
    ```

1. 通过 [`onCaptureVideoFrame`](/zh-CN/api-reference/rtc/ios/video/video-raw#callback_ivideoframeobserver_oncapturevideoframe) 回调获取采集后的视频数据并根据需要进行前处理，可以修改 Alpha 数据或直接添加 Alpha 数据。

    ```java
    public boolean onCaptureVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

2. 通过 [`onPreEncodeVideoFrame`](/zh-CN/api-reference/rtc/ios/video/video-raw#callback_ivideoframeobserver_onpreencodevideoframe) 回调获取本地视频编码前的视频数据，可以修改 Alpha 数据或直接添加 Alpha 数据。

    ```java
    public boolean onPreEncodeVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

3. 通过 [`onRenderVideoFrame`](/zh-CN/api-reference/rtc/ios/video/video-raw#callback_ivideoframeobserver_onrendervideoframe) 回调获取远端发送的视频在本地渲染前的视频数据，可以修改 Alpha 数据、直接添加 Alpha 数据或根据得到的 Alpha 数据自行渲染视频图像。

    ```java
    public boolean onRenderVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据、直接添加 Alpha 数据或根据得到的 Alpha 数据自行渲染视频图像
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

## 开发注意事项 [#开发注意事项-1]

你需要自行实现 App 窗口的透明度属性，并处理多个 App 窗口叠加时的透明度关系（通过调整窗口的 `zOrder` 实现）。除此之外，对于不同的目标平台，需遵循以下限制：

- 对于 Android 平台，由于系统限制，设置视图时仅支持使用 `TextureView`、不支持使用 `SurfaceView`。
- 对于 iOS 平台，在设置视图时需要将该视图 `layer` 属性中的 `opaque` 设置为 `false`。
- 对于 macOS 平台，不需要额外设置窗口的透明属性，也可以遵循上一条 iOS 的设置方法。
- 对于 Windows 平台，你需要在创建项目时使用 [`CreateWindowEx`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw) 创建单独的窗口，并设置高透明属性。如果目标窗口是子窗口，则需要额外指定 [`WS_POPUP`](https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles) 属性。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="macos" />
## 场景概述 [#场景概述-2]

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

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

<DocsImage alt="应用场景1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo1.png" displayWidth="30%" />

<DocsImage alt="应用场景2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo2.png" displayWidth="60%" />

## 前提条件 [#前提条件-2]

已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方案 [#实现方案-2]

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

:::info[信息]
本文的 API 和示例代码均以 Android 平台为例。如目标平台为其他平台，请参考对应平台的 API 文档。
:::

### 自定义视频采集场景 [#自定义视频采集场景-2]

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

<DocsImage alt="自定义视频采集流程1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-1.svg" displayWidth="80%" />

1. 将采集到的视频数据推送到 SDK，同时设置视频帧中的 Alpha 数据。有以下两种方式可供选择。

   - 方案一：调用 [`pushExternalVideoFrame`[2/2]](/zh-CN/api-reference/rtc/android/video/video-custom-capturenrendering#api_irtcengine_pushvideoframe4) 方法，并通过设置 `alphaBuffer` 参数为视频帧指定 Alpha 通道数据。该数据跟视频帧的尺寸一致，每个像素点的取值范围为 [0,255]，其中 0 代表背景；255 代表前景（人像）。

     :::error[警告]
     请务必确保 `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);
     ```

1. 渲染本地视图并实现 Alpha 透明特效。

    1. 创建一个 `TextureView` 对象用于渲染本地视图。
    1. 调用 [`setupLocalVideo`](/zh-CN/api-reference/rtc/macos/video/video-rendering#api_irtcengine_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);
    ```

1. 渲染远端视频流在本地的视图并实现 Alpha 透明特效。

    1. 在收到 `onUserJoined` 回调后，创建一个 `TextureView` 对象用于渲染远端视图。
    1. 调用  [`setupRemoteVideo`](/zh-CN/api-reference/rtc/macos/video/video-rendering#api_irtcengine_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 采集场景 [#sdk-采集场景-2]

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

<DocsImage alt="自定义视频采集流程2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-2.svg" displayWidth="80%" />

1. 在发送端调用 [`enableVirtualBackground`[2/2]](/zh-CN/api-reference/rtc/macos/video/video-prenpro/virtualbackground#api_irtcengine_enablevirtualbackground2) 开启背景分割算法，得到人像区域的 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`](/zh-CN/api-reference/rtc/macos/video/video-basic#api_irtcengine_setvideoencoderconfiguration) 设置视频编码属性，将 `encodeAlpha` 设置为 `true`，则 Alpha 数据会被编码并发送到远端。

    ```java
    VideoEncoderConfiguration videoEncoderConfiguration = new VideoEncoderConfiguration(...);
    videoEncoderConfiguration.advanceOptions = new VideoEncoderConfiguration.AdvanceOptions(...);
    videoEncoderConfiguration.advanceOptions.encodeAlpha = true;
    rtcEngine.setVideoEncoderConfiguration(videoEncoderConfiguration);
    ```

2. 渲染本地和远端视图并实现 Alpha 透明特效。详见[自定义视频采集场景](#自定义视频采集场景-2)中的步骤。

### 原始视频数据场景 [#原始视频数据场景-2]

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

<DocsImage alt="自定义视频采集流程3" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-3.svg" displayWidth="80%" />

1. 调用 [`registerVideoFrameObserver`](/zh-CN/api-reference/rtc/macos/video/video-raw#api_irtcengine_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);
    ```

1. 通过 [`onCaptureVideoFrame`](/zh-CN/api-reference/rtc/macos/video/video-raw#callback_ivideoframeobserver_oncapturevideoframe) 回调获取采集后的视频数据并根据需要进行前处理，可以修改 Alpha 数据或直接添加 Alpha 数据。

    ```java
    public boolean onCaptureVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

2. 通过 [`onPreEncodeVideoFrame`](/zh-CN/api-reference/rtc/macos/video/video-raw#callback_ivideoframeobserver_onpreencodevideoframe) 回调获取本地视频编码前的视频数据，可以修改 Alpha 数据或直接添加 Alpha 数据。

    ```java
    public boolean onPreEncodeVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

3. 通过 [`onRenderVideoFrame`](/zh-CN/api-reference/rtc/macos/video/video-raw#callback_ivideoframeobserver_onrendervideoframe) 回调获取远端发送的视频在本地渲染前的视频数据，可以修改 Alpha 数据、直接添加 Alpha 数据或根据得到的 Alpha 数据自行渲染视频图像。

    ```java
    public boolean onRenderVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据、直接添加 Alpha 数据或根据得到的 Alpha 数据自行渲染视频图像
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

## 开发注意事项 [#开发注意事项-2]

你需要自行实现 App 窗口的透明度属性，并处理多个 App 窗口叠加时的透明度关系（通过调整窗口的 `zOrder` 实现）。除此之外，对于不同的目标平台，需遵循以下限制：

- 对于 Android 平台，由于系统限制，设置视图时仅支持使用 `TextureView`、不支持使用 `SurfaceView`。
- 对于 iOS 平台，在设置视图时需要将该视图 `layer` 属性中的 `opaque` 设置为 `false`。
- 对于 macOS 平台，不需要额外设置窗口的透明属性，也可以遵循上一条 iOS 的设置方法。
- 对于 Windows 平台，你需要在创建项目时使用 [`CreateWindowEx`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw) 创建单独的窗口，并设置高透明属性。如果目标窗口是子窗口，则需要额外指定 [`WS_POPUP`](https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles) 属性。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="windows" />
## 场景概述 [#场景概述-3]

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

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

<DocsImage alt="应用场景1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo1.png" displayWidth="30%" />

<DocsImage alt="应用场景2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo2.png" displayWidth="60%" />

## 前提条件 [#前提条件-3]

已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方案 [#实现方案-3]

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

:::info[信息]
本文的 API 和示例代码均以 Android 平台为例。如目标平台为其他平台，请参考对应平台的 API 文档。
:::

### 自定义视频采集场景 [#自定义视频采集场景-3]

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

<DocsImage alt="自定义视频采集流程1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-1.svg" displayWidth="80%" />

1. 将采集到的视频数据推送到 SDK，同时设置视频帧中的 Alpha 数据。有以下两种方式可供选择。

   - 方案一：调用 [`pushExternalVideoFrame`[2/2]](/zh-CN/api-reference/rtc/android/video/video-custom-capturenrendering#api_irtcengine_pushvideoframe4) 方法，并通过设置 `alphaBuffer` 参数为视频帧指定 Alpha 通道数据。该数据跟视频帧的尺寸一致，每个像素点的取值范围为 [0,255]，其中 0 代表背景；255 代表前景（人像）。

     :::error[警告]
     请务必确保 `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);
     ```

1. 渲染本地视图并实现 Alpha 透明特效。

    1. 创建一个 `TextureView` 对象用于渲染本地视图。
    1. 调用 [`setupLocalVideo`](/zh-CN/api-reference/rtc/cpp-all-platforms/video/video-rendering#api_irtcengine_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);
    ```

1. 渲染远端视频流在本地的视图并实现 Alpha 透明特效。

    1. 在收到 `onUserJoined` 回调后，创建一个 `TextureView` 对象用于渲染远端视图。
    1. 调用  [`setupRemoteVideo`](/zh-CN/api-reference/rtc/cpp-all-platforms/video/video-rendering#api_irtcengine_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 采集场景 [#sdk-采集场景-3]

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

<DocsImage alt="自定义视频采集流程2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-2.svg" displayWidth="80%" />

1. 在发送端调用 [`enableVirtualBackground`[2/2]](/zh-CN/api-reference/rtc/cpp-all-platforms/video/video-prenpro/virtualbackground#api_irtcengine_enablevirtualbackground2) 开启背景分割算法，得到人像区域的 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`](/zh-CN/api-reference/rtc/cpp-all-platforms/video/video-basic#api_irtcengine_setvideoencoderconfiguration) 设置视频编码属性，将 `encodeAlpha` 设置为 `true`，则 Alpha 数据会被编码并发送到远端。

    ```java
    VideoEncoderConfiguration videoEncoderConfiguration = new VideoEncoderConfiguration(...);
    videoEncoderConfiguration.advanceOptions = new VideoEncoderConfiguration.AdvanceOptions(...);
    videoEncoderConfiguration.advanceOptions.encodeAlpha = true;
    rtcEngine.setVideoEncoderConfiguration(videoEncoderConfiguration);
    ```

2. 渲染本地和远端视图并实现 Alpha 透明特效。详见[自定义视频采集场景](#自定义视频采集场景-3)中的步骤。

### 原始视频数据场景 [#原始视频数据场景-3]

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

<DocsImage alt="自定义视频采集流程3" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-3.svg" displayWidth="80%" />

1. 调用 [`registerVideoFrameObserver`](/zh-CN/api-reference/rtc/cpp-all-platforms/video/video-raw#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);
    ```

1. 通过 [`onCaptureVideoFrame`](/zh-CN/api-reference/rtc/cpp-all-platforms/video/video-raw#callback_ivideoframeobserver_oncapturevideoframe) 回调获取采集后的视频数据并根据需要进行前处理，可以修改 Alpha 数据或直接添加 Alpha 数据。

    ```java
    public boolean onCaptureVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

2. 通过 [`onPreEncodeVideoFrame`](/zh-CN/api-reference/rtc/cpp-all-platforms/video/video-raw#callback_ivideoframeobserver_onpreencodevideoframe) 回调获取本地视频编码前的视频数据，可以修改 Alpha 数据或直接添加 Alpha 数据。

    ```java
    public boolean onPreEncodeVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据或直接添加 Alpha 数据
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

3. 通过 [`onRenderVideoFrame`](/zh-CN/api-reference/rtc/cpp-all-platforms/video/video-raw#callback_ivideoframeobserver_onrendervideoframe) 回调获取远端发送的视频在本地渲染前的视频数据，可以修改 Alpha 数据、直接添加 Alpha 数据或根据得到的 Alpha 数据自行渲染视频图像。

    ```java
    public boolean onRenderVideoFrame(int type, VideoFrame videoFrame) {
        // 修改 Alpha 数据、直接添加 Alpha 数据或根据得到的 Alpha 数据自行渲染视频图像
        ByteBuffer alphaBuffer = videoFrame.getAlphaBuffer();
        ...
        videoFrame.fillAlphaData(byteBuffer);
        return false;
    }
    ```

## 开发注意事项 [#开发注意事项-3]

你需要自行实现 App 窗口的透明度属性，并处理多个 App 窗口叠加时的透明度关系（通过调整窗口的 `zOrder` 实现）。除此之外，对于不同的目标平台，需遵循以下限制：

- 对于 Android 平台，由于系统限制，设置视图时仅支持使用 `TextureView`、不支持使用 `SurfaceView`。
- 对于 iOS 平台，在设置视图时需要将该视图 `layer` 属性中的 `opaque` 设置为 `false`。
- 对于 macOS 平台，不需要额外设置窗口的透明属性，也可以遵循上一条 iOS 的设置方法。
- 对于 Windows 平台，你需要在创建项目时使用 [`CreateWindowEx`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw) 创建单独的窗口，并设置高透明属性。如果目标窗口是子窗口，则需要额外指定 [`WS_POPUP`](https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles) 属性。
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="unity" />
## 场景概述 [#场景概述-4]

Alpha 透明特效是指通过调整图像中每个像素的 Alpha 通道值来控制其透明度，从而实现不同的视觉效果，如渐变和阴影。在各种实时音视频互动场景下，将主播的背景进行人像分割和透明特效处理可以使互动过程更加有趣，提升互动的沉浸感、改善互动体验。以下是一些比较典型的场景：

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

<DocsImage alt="应用场景1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo1.png" displayWidth="30%" />

## 前提条件 [#前提条件-4]

已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方案 [#实现方案-4]

根据实际业务场景选择一种实现 Alpha 透明特效的方法。

### SDK 采集场景 [#sdk-采集场景-4]

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

<DocsImage alt="自定义视频采集流程2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-2.svg" displayWidth="80%" />

1. 在使用 `VideoSurface` 组件渲染本地视频时，调用 [`SetLocalVideoDataSourcePosition`](/zh-CN/api-reference/rtc/unity/video/video-rendering#api_irtcengine_setlocalvideodatasourceposition) 获取视频帧的位置。将 `position` 设为 `POSITION_POST_CAPTURER`，表示获取本地视频采集完成后的视频帧。

   ```csharp
   //设置本地视频帧的位置为 POSITION_POST_CAPTURER
   RtcEngine.SetLocalVideoDataSourcePosition(VIDEO_MODULE_POSITION.POSITION_POST_CAPTURER);
   ```

2. 在发送端调用 [`EnableVirtualBackground`](/zh-CN/api-reference/rtc/unity/video/video-prenpro/virtualbackground#api_irtcengine_enablevirtualbackground) 开启背景分割算法，得到人像区域的 Alpha 数据。建议设置参数如下：
    - `enabled`：设置为 `true`，开启虚拟背景。
    - `backgroundSourceType`：设置为 `BACKGROUND_NONE`(0)，即：将人像和背景进行分割，并将背景处理为 Alpha 通道。

    ```csharp
    var source = new VirtualBackgroundSource();
    source.background_source_type = BACKGROUND_SOURCE_TYPE.BACKGROUND_NONE;
    var segproperty = new SegmentationProperty();
    var nRet = RtcEngine.EnableVirtualBackground(true, source, segproperty, MEDIA_SOURCE_TYPE.PRIMARY_CAMERA_SOURCE);
    this.Log.UpdateLog("EnableVirtualBackground true :" + nRet);
    ```

3. 调用 [`SetVideoEncoderConfiguration`](/zh-CN/api-reference/rtc/unity/video/video-basic#api_irtcengine_setvideoencoderconfiguration) 将 `encodeAlpha` 设为 `true`，将 Alpha 信息进行编码并发送至远端，然后调用 [`JoinChannel`[2/2]](/zh-CN/api-reference/rtc/unity/channel#api_irtcengine_joinchannel2) 加入频道。

   ```csharp
   VideoEncoderConfiguration videoEncoderConfiguration = new VideoEncoderConfiguration();
   videoEncoderConfiguration.advanceOptions.encodeAlpha = true;
   RtcEngine.SetVideoEncoderConfiguration(videoEncoderConfiguration);
   RtcEngine.JoinChannel(_token, _channelName, "", 0);
   ```

4. 通过 `VideoSurfaceYUVA` 组件来渲染带有 Alpha 透明特效的视图。

   ```csharp
   private VideoSurface MakeImageSurface(string goName)
   {
       ......
       // 使用 VideoSurfaceYUVA 进行渲染
       VideoSurface videoSurface = go.AddComponent<VideoSurfaceYUVA>();
       return videoSurface;
   }
   ```

### 自定义视频采集场景 [#自定义视频采集场景-4]

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

<DocsImage alt="自定义视频采集流程1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-1.svg" displayWidth="80%" />

声网提供以下两种方案把自采集且带有 Alpha 信息的视频帧推送给 SDK 发布至频道内。你可以根据需求选择一种来实现。

:::warning[注意]
由于 Unity SDK 的限制，目前无法在本地通过 `VideoSurfaceYUVA` 组件来预览需要推送的自定义采集的视频流，建议在接收端查看推流效果。
:::

<Tabs defaultValue="fillalphabuffer">
<TabsList>
  <TabsTrigger value="fillalphabuffer">为视频帧设置 Alpha 信息</TabsTrigger>
  <TabsTrigger value="alphastitchmode">将视频数据和 Alpha 数据拼接</TabsTrigger>
</TabsList>

<TabsContent value="fillalphabuffer">

你可以参考下列步骤来对自采集的视频数据进行处理、在频道内推送自采集的视频流、并为每一帧单独设置 Alpha 信息，以实现动态视觉效果。下列示例代码以推送下图为例。

<DocsImage alt="推送图片" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/unity-alpha1.png" displayWidth="30%" />

1. 处理纹理数据。将 Texture2D 对象的颜色数据提取并转换为两个字节数组，一个包含 RGBA 数据，另一个包含 Alpha 通道数据，以便后续处理和传输。

   ```csharp
   // Read/Write Enable 属性设置为 true，并且绑定到 TextureWithAlphaBuffer 变量上
   public Texture2D TextureWithAlphaBuffer;
   private byte[] _rgbaData = null;
   private byte[] _alphaData = null;
   private int _textureWidth;
   private int _textureHeight;
   // 自定义视频轨道 ID
   private uint _videoTrackId = 0;

   // 将颜色数据从 Color32 数组转换为 rgbaData 和 alphaData 数组
   private void ConvertColor32(Color32[] colors, byte[] rgbaData, byte[] alphaData)
   {
       int i = 0;
       int l = 0;
       foreach (var color in colors)
       {
           rgbaData[i++] = color.r;
           rgbaData[i++] = color.g;
           rgbaData[i++] = color.b;
           rgbaData[i++] = color.a;

           alphaData[l++] = color.a;
       }
   }

   // 从纹理中读取颜色数据，并初始化 _rgbaData 和 _alphaData 数组，同时记录纹理的宽度和高度
   public void InitTextureDataWithAlphaBuffer()
   {
       Color32[] color32s = TextureWithAlphaBuffer.GetPixels32(0);
       _rgbaData = new byte[color32s.Length * 4];
       _alphaData = new byte[color32s.Length];
       ConvertColor32(color32s, _rgbaData, _alphaData);
       _textureWidth = TextureWithAlphaBuffer.width;
       _textureHeight = TextureWithAlphaBuffer.height;
       this.Log.UpdateLog(string.Format("AlphaBuffer width: {0}, height: {1}", _textureWidth, _textureHeight));
   }
   ```

2. 配置视频编码参数来把 Alpha 数据发送至远端。调用 [`SetVideoEncoderConfiguration`](/zh-CN/api-reference/rtc/unity/video/video-basic#api_irtcengine_setvideoencoderconfiguration) 设置视频编码属性，将 `encodeAlpha` 设为 `true` 来编码并发送 Alpha 数据至远端。

3. 创建一个视频轨道用于发布自定义采集的视频到频道内。调用 [`CreateCustomVideoTrack`](/zh-CN/api-reference/rtc/unity/video/video-custom-capturenrendering#api_irtcengine_createcustomvideotrack) 创建一个自定义的视频轨道。

4. 调用 [`JoinChannel`[2/2]](/zh-CN/api-reference/rtc/unity/channel#api_irtcengine_joinchannel2) 加入频道，并设置频道媒体选项：
   - 将 `customVideoTrackId` 设置为第 3 步中获得的视频轨道 ID。
   - 将 `publishCameraTrack` 设为 `false`，不发布摄像头采集的视频流。
   - 将 `publishCustomVideoTrack` 设置为 `true`，发布自定义采集的视频流。

   示例代码如下：

   ```csharp
   public void OnStartPushVideoFrameWithAlphaBuffer()
   {
       // 读取图片中的 RGBA 数据和 Alpha 数据
       InitTextureDataWithAlphaBuffer();

       // 设置视频编码属性
       VideoEncoderConfiguration videoEncoderConfiguration = new VideoEncoderConfiguration();
       videoEncoderConfiguration.dimensions.width = _textureWidth;
       videoEncoderConfiguration.dimensions.height = _textureHeight;
       // 设置发送编码 Alpha 数据
       videoEncoderConfiguration.advanceOptions.encodeAlpha = true;
       RtcEngine.SetVideoEncoderConfiguration(videoEncoderConfiguration);
       // 创建自定义视频轨道用于在频道内发布自定义采集的视频流
       _videoTrackId = RtcEngine.CreateCustomVideoTrack();
       // 设置频道媒体选项
       var options = new ChannelMediaOptions();
       // 不发布摄像头采集的视频流
       options.publishCameraTrack.SetValue(false);
       // 发布麦克风采集的音频流
       options.publishMicrophoneTrack.SetValue(true);
       // 发布自定义采集的视频流
       options.publishCustomVideoTrack.SetValue(true);
       // 把 customVideoTrackId 设为你在调用 CreateCustomVideoTrack 方法时返回的视频轨道 ID
       options.customVideoTrackId.SetValue(_videoTrackId);
       // 加入频道
       RtcEngine.JoinChannel(_token, _channelName, 0, options)
       InvokeRepeating("UpdateForPushVideoFrameWithAlphaBuffer", 0.1f, 0.1f);
   }
   ```

5. 推送自定义采集的视频流至 SDK。调用 [`PushVideoFrame`](/zh-CN/api-reference/rtc/unity/video/video-custom-capturenrendering#api_imediaengine_pushvideoframe) 方法来推送视频帧，同时为视频帧设置 Alpha 数据。你可以通过设置 `alphaBuffer` 或 `fillAlphaBuffer` 参数来为视频帧设置 Alpha 通道数据。
   - `alphaBuffer`：Alpha 通道数据。该数据跟视频帧的尺寸一致，每个像素点的取值范围为 [0,255]，其中 0 代表背景；255 代表前景（人像）。
   - `fillAlphaBuffer`：将该参数设为 `true` 后 SDK 可自动提取并填充 Alpha 通道数据，无需额外设置 `alphaBuffer`。`fillAlphaBuffer` 参数目前仅支持 BGRA 或 RGBA 格式的视频数据。

   :::warning[注意]
   请务必确保 `alphaBuffer` 跟视频帧的尺寸 (width × height) 完全一致，否则可能会导致 App 崩溃。
   :::

    ```csharp
    public void UpdateForPushVideoFrameWithAlphaBuffer()
    {
        var timetick = System.DateTime.Now.Ticks / 10000;
        ExternalVideoFrame externalVideoFrame = new ExternalVideoFrame();
        // 视频 buffer 类型为原始数据
        externalVideoFrame.type = VIDEO_BUFFER_TYPE.VIDEO_BUFFER_RAW_DATA;
        // 视频像素为 RGBA 格式
        externalVideoFrame.format = VIDEO_PIXEL_FORMAT.VIDEO_PIXEL_RGBA;
        externalVideoFrame.buffer = _rgbaData;
        externalVideoFrame.alphaStitchMode = ALPHA_STITCH_MODE.NO_ALPHA_STITCH;
        externalVideoFrame.alphaBuffer = null;
        // 提取并填充 Alpha 通道数据，当前仅对 RGBA 或 BGRA 格式的视频数据生效
        externalVideoFrame.fillAlphaBuffer = true;
        // 如果你要推送的视频数据格式不是 RGBA 或 BGRA，你需要设置 alphaBuffer 参数，并将 fillAlphaBuffer 设为 false
        //externalVideoFrame.alphaBuffer = _alphaData;
        //externalVideoFrame.fillAlphaBuffer = false
        externalVideoFrame.stride = _textureWidth;
        externalVideoFrame.height = _textureHeight;
        externalVideoFrame.rotation = 180;
        externalVideoFrame.timestamp = timetick;
        // 推送自定义采集的视频至 SDK
        var ret = RtcEngine.PushVideoFrame(externalVideoFrame, _videoTrackId);
        Debug.Log("PushVideoFrame ret = " + ret + " time: " + timetick);
    }
    ```

</TabsContent>

<TabsContent value="alphastitchmode">

参考下列步骤将采集的视频数据与 Alpha 通道数据进行拼接，并将拼接后的视频数据推送至 SDK 以发布到频道内。下列步骤展示如何将 Alpha 通道数据拼接至视频数据上方。

原始视频数据：

<DocsImage alt="原始图片" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/unity-alpha1.png" displayWidth="30%" />

拼接后的数据：

<DocsImage alt="拼接后图片" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/unity-alpha2.png" displayWidth="30%" />

1. 把采集的视频数据和 Alpha 数据拼接。

   ```csharp
   public void InitTextureDataWithAlphaStitchMode()
   {
       // 读取原始纹理数据
       Color32[] color32s = TextureWithAlphaBuffer.GetPixels32(0);

       // 获取原始纹理的宽度和高度
       var width = TextureWithAlphaBuffer.width;
       var height = TextureWithAlphaBuffer.height;

       // 创建一个新的纹理，其高度是原始纹理高度的两倍
       TextureWithAlphaStitchMode = new Texture2D(width, height * 2, TextureFormat.RGBA32, false);

       // 初始化一个新的颜色数组，用于存储拼接后的像素数据
       Color32[] newColor32s = new Color32[color32s.Length * 2];

       // 将原始颜色数据复制到新颜色数组的上半部分
       Array.Copy(color32s, newColor32s, color32s.Length);

       // 将 Alpha 通道数据拼接到新颜色数组的下半部分
       for (int i = color32s.Length; i < newColor32s.Length; i++)
       {
           newColor32s[i].r = color32s[i - color32s.Length].a;
           newColor32s[i].g = newColor32s[i].r;
           newColor32s[i].b = newColor32s[i].r;
           newColor32s[i].a = 255;
       }

       // 将新颜色数组应用到新的纹理中
       TextureWithAlphaStitchMode.SetPixels32(newColor32s);
       TextureWithAlphaStitchMode.Apply();

       // 转换颜色数据并存储在 _rgbaData 和 _alphaData 数组中
       _rgbaData = new byte[newColor32s.Length * 4];
       _alphaData = new byte[newColor32s.Length];
       ConvertColor32(newColor32s, _rgbaData, _alphaData);

       // 更新纹理宽度和高度
       _textureWidth = TextureWithAlphaStitchMode.width;
       _textureHeight = TextureWithAlphaStitchMode.height;
       this.Log.UpdateLog(string.Format("AlphaStitchMode width: {0}, height: {1}", _textureWidth, _textureHeight));

       // 将新纹理绑定到 UI 中的 RawImage 组件
       GameObject.Find("RawImage").GetComponent<RawImage>().texture = TextureWithAlphaStitchMode;
       GameObject.Find("RawImage").GetComponent<RectTransform>().sizeDelta = new Vector2(_textureWidth, _textureHeight);
   }
   ```

2. 配置视频编码参数来把 Alpha 数据发送至远端。调用 [`SetVideoEncoderConfiguration`](/zh-CN/api-reference/rtc/unity/video/video-basic#api_irtcengine_setvideoencoderconfiguration) 设置视频编码属性，将 `encodeAlpha` 设为 `true` 来编码并发送 Alpha 数据至远端。

3. 创建一个视频轨道用于发布自定义采集的视频到频道内。调用 [`CreateCustomVideoTrack`](/zh-CN/api-reference/rtc/unity/video/video-custom-capturenrendering#api_irtcengine_createcustomvideotrack) 创建一个自定义的视频轨道。

4. 调用 [`JoinChannel`[2/2]](/zh-CN/api-reference/rtc/unity/channel#api_irtcengine_joinchannel2) 加入频道，并设置频道媒体选项：
   - 将 `customVideoTrackId` 设置为第 3 步中获得的视频轨道 ID。
   - 将 `publishCameraTrack` 设为 `false`，不发布摄像头采集的视频流。
   - 将 `publishCustomVideoTrack` 设置为 `true`，发布自定义采集的视频流。

   示例代码如下：

   ```csharp
   public void OnStartPushVideoFrameWithAlphaStitchMode()
   {
       InitTextureDataWithAlphaStitchMode();
       VideoEncoderConfiguration videoEncoderConfiguration = new VideoEncoderConfiguration();
       videoEncoderConfiguration.dimensions.width = _textureWidth;
       //实际发送的高度为拼接后纹理高度的一半
       videoEncoderConfiguration.dimensions.height = _textureHeight / 2;
       // 编码并发送 Alpha 数据至远端
       videoEncoderConfiguration.advanceOptions.encodeAlpha = true;
       // 设置视频编码属性
       RtcEngine.SetVideoEncoderConfiguration(videoEncoderConfiguration);
       // 创建自定义视频轨道用于在频道内发布自定义采集的视频流
       _videoTrackId = RtcEngine.CreateCustomVideoTrack();
       var options = new ChannelMediaOptions();
       // 设置不发布摄像头采集的视频流
       options.publishCameraTrack.SetValue(false);
       // 设置发布麦克风采集的音频流
       options.publishMicrophoneTrack.SetValue(true);
       // 设置发布自定义采集的视频流
       options.publishCustomVideoTrack.SetValue(true);
       // 把 customVideoTrackId 设为你在调用 CreateCustomVideoTrack 方法时返回的视频轨道 ID
       options.customVideoTrackId.SetValue(_videoTrackId);
       // 加入频道
       RtcEngine.JoinChannel(_token, _channelName, 0, options);
       InvokeRepeating("UpdateForPushVideoFrameWithAlphaStitchMode", 0.1f, 0.1f);
   }
   ```

5. 推送拼接后的视频帧至 SDK。调用 [`PushVideoFrame`](/zh-CN/api-reference/rtc/unity/video/video-custom-capturenrendering#pushvideoframe) 方法并通过 `alphaStitchMode` 参数来设置 Alpha 数据和视频帧的相对位置。

   示例代码如下：

   ```csharp
   public void UpdateForPushVideoFrameWithAlphaStitchMode()
   {
       var timetick = System.DateTime.Now.Ticks / 10000;
       ExternalVideoFrame externalVideoFrame = new ExternalVideoFrame();
       // 视频 buffer 类型为原始数据
       externalVideoFrame.type = VIDEO_BUFFER_TYPE.VIDEO_BUFFER_RAW_DATA;
       // 视频像素为 RGBA 格式
       externalVideoFrame.format = VIDEO_PIXEL_FORMAT.VIDEO_PIXEL_RGBA;
       externalVideoFrame.buffer = _rgbaData;
       // highlight-start
       //由于 Unity 纹理坐标和 SDK 坐标原点不同，如需将 Alpha 数据拼接至原始数据的上方，则这里 SDK 读取时需要设置拼接位置为下方，
       externalVideoFrame.alphaStitchMode = ALPHA_STITCH_MODE.ALPHA_STITCH_BELOW;
       // highlight-end
       externalVideoFrame.stride = _textureWidth;
       externalVideoFrame.height = _textureHeight;
       externalVideoFrame.rotation = 180;
       externalVideoFrame.timestamp = timetick;
       // 推送视频帧至 SDK
       var ret = RtcEngine.PushVideoFrame(externalVideoFrame, _videoTrackId);
       Debug.Log("PushVideoFrame ret = " + ret + " time: " + timetick);
   }
   ```

</TabsContent>
</Tabs>
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="android" platform="react-native" />
## 场景概述 [#场景概述-5]

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

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

<DocsImage alt="应用场景1" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo1.png" displayWidth="30%" />

<DocsImage alt="应用场景2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/demo2.png" displayWidth="60%" />

## 前提条件 [#前提条件-5]

已经在项目中实现了基本的实时音视频功能。详见[实现音视频互动](/zh-CN/realtime-media/rtc/get-started/quick-start)。

## 实现方案 [#实现方案-5]

使用 SDK 采集视频帧场景下，可以通过以下方式实现 Alpha 透明特效：

<DocsImage alt="自定义视频采集流程2" src="https://web-cdn.agora.io/docs-portal/img/rtc/alpha/alpha-process-2.svg" displayWidth="80%" />

### 开启虚拟背景 [#开启虚拟背景]

在发送端调用 [`enableVirtualBackground`](/zh-CN/api-reference/rtc/react-native/video/video-prenpro/virtualbackground#enablevirtualbackground) 开启背景分割算法，得到人像区域的 Alpha 数据。建议设置参数如下：

- `enabled`：设置为 `true`，开启虚拟背景。
- `backgroundSource` 中的 `background_source_type`：设置为 `BackgroundNone`(0)，即：将人像和背景进行分割，并将背景处理为 Alpha 数据。

```typescript
// 初始化 rtcEngine 对象
const rtcEngine = createAgoraRtcEngine();

// 开启虚拟背景
rtcEngine.enableVirtualBackground(
    true, 
    backgroundSource, 
    { modelType: SegModelType.SegModelAi }, 
    { background_source_type: BackgroundSourceType.BackgroundNone }
);
```

### 设置视频编码属性 [#设置视频编码属性]

在发送端调用 [`setVideoEncoderConfiguration`](/zh-CN/api-reference/rtc/react-native/video/video-basic#api_irtcengine_setvideoencoderconfiguration) 设置视频编码属性，将 `encodeAlpha` 设置为 `true`，则 Alpha 数据会被编码并发送到远端。

```typescript
rtcEngine.setVideoEncoderConfiguration({advanceOptions: {encodeAlpha: true}});
```

### 开启 Alpha 渲染 [#开启-alpha-渲染]

1. 使用 [`RtcSurfaceView`](/zh-CN/api-reference/rtc/react-native/rtc-interface-class#class_rtcsurfaceview) 组件创建本地视图和远端视图。
2. 在 [`RtcRenderViewProps`](/zh-CN/api-reference/rtc/react-native/class-rtcrendererviewprops) 属性的 `canvas` 参数中，将 [`VideoCanvas`](/zh-CN/api-reference/rtc/react-native/class-videocanvas) 的 `enableAlphaMask` 设置为 `true` 开启 Alpha 遮罩渲染并实现 Alpha 透明特效。

```jsx
<RtcSurfaceView 
    canvas={{ uid: 0, enableAlphaMask: true }} 
    style={styles.videoView} 
/>
```
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>
