# 使用 AI 画质插件 (Beta) (/zh-CN/realtime-media/rtc/build/extensions/web/super-clarity)

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

## 使用 AI 画质插件（Beta 版） [#使用-ai-画质插件beta-版]

AI 画质插件封装了声网自研的 AI 画质增强算法，可以在不改变分辨率的条件下智能提升视频画质，优化接收端的观看体验。AI 画质增强算法对移动端机型的覆盖率达到 95% 以上，并且在低端机上也能保证较大分辨率视频的渲染画质。

## 实现原理 [#实现原理]

声网 Web SDK 的媒体传输管道包含采集、前处理、编码、传输、解码、后处理和播放等环节。 在后处理阶段，插件可以对管道中的音视频数据进行处理，从而实现 AI 画质等功能。

<DocsImage alt="后处理插件原理" src="https://web-cdn.agora.io/docs-portal/img/rtc/post-process-tech.png" displayWidth="90%" />

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

开始之前，请确保你满足以下条件：

- 已集成 v4.19.0 或以上版本的 Web SDK，实现基础的实时音视频互动功能，具体参考[快速开始](../../../get-started/quick-start?platform=web)文档。
- 使用下表列出的浏览器版本，未列出的浏览器暂不支持：

    | 浏览器                | 版本要求        |
    | --------------------- | --------------- |
    | 桌面端、移动端 Chrome | 96 或以上       |
    | 桌面端 Safari         | 15.4 或以上     |
    | 移动端 Safari         | iOS 16.0 或以上 |

## 示例代码 [#示例代码]

以下是一段使用 AI 画质插件的完整代码，供开发者参考。

<Accordions>
<Accordion title="完整示例代码">

```javascript
const extension = new SuperClarityExtension();
AgoraRTC.registerExtensions([extension]);

const context = {
  uid: undefined,
  client: undefined,
  track: undefined,
  processor: undefined,
};

async function join(appid, channel) {
  context.client = AgoraRTC.createClient({ mode: "live", codec: "vp8", role: "host" });
  client.on("user-published", onUserPublished);
  client.on("user-unpublished", onUserUnpublished);

  await client.join(appid, channel, null);
}

async function onUserPublished(user, mediaType) {
  if (!context.client || mediaType !== "video" || !!context.uid) {
    return;
  }
  context.uid = user.uid;
  context.track = await context.client.subscribe(user, mediaType);
  context.processor = extension.createProcessor();

  context.processor.on("first-video-frame", (stats) => {
    console.log("plugin have first video frame, stats:", stats);
  });
  context.processor.on("error", (msg) => {
    console.log("plugin error:", msg);
  });
  context.processor.on("stats", (stats) => {
    console.log("plugin stats:", Date.now(), stats);
  });
  context.track.pipe(context.processor).pipe(context.track.processorDestination);
  await context.processor.enable();
  context.track.play('any element id')
}

async function onUserUnpublished(user, mediaType) {
  if (!context.client || mediaType !== "video" || context.uid !== user.uid) {
    return;
  }
  context.processor.unpipe();
  context.track.unpipe();
  await context.processor.release();
  context.processor = undefined;
  context.track.stop();
  context.track = undefined;
}


join('your-appid', 'your-channel');
```

</Accordion>
</Accordions>

## 实现步骤 [#实现步骤]

本节介绍使用 AI 画质插件的主要流程。

### 1. 集成插件 [#1-集成插件]

1. 通过 npm 将 AI 画质插件 [agora-extension-super-clarity](https://www.npmjs.com/package/agora-extension-super-clarity) 集成到你的项目中：

   ```bash
   npm install agora-extension-super-clarity
   ```

2. 通过以下任意一种方式引入 AI 画质插件。

   - 方法一：在 JavaScript 文件中加入以下代码引入。

      ```typescript
      import {
        SuperClarityExtension,
        SuperClarityEvents,
        SuperClarityProcessor,
      } from 'agora-extension-super-clarity';
      ```

   - 方法二：在 HTML 文件中通过 Script 标签引入。引入后即可在 JavaScript 文件中直接使用 `SuperClarityExtension` 对象。

      ```html
      <script src="./agora-extension-super-clarity.js"></script>
      ```

### 2. 注册插件 [#2-注册插件]

在加入频道之前，创建 `SuperClarityExtension` 对象并调用 SDK 的[`registerExtensions`](/zh-CN/api-reference/rtc/web/interfaces/iagorartc#registerextensions) 方法注册插件。

```typescript
// 创建 SuperClarityExtension 对象
const extension = new SuperClarityExtension();
// 注册插件
AgoraRTC.registerExtensions([extension]);

const context = {
  uid: undefined,
  client: undefined,
  track: undefined,
  processor: undefined,
};

async function join(appid, channel) {
  context.client = AgoraRTC.createClient({ mode: "live", codec: "vp8", role: "host" });
  client.on("user-published", onUserPublished);
  client.on("user-unpublished", onUserUnpublished);

  await client.join(appid, channel, null);
}

// 加入频道
join('your-appid', 'your-channel');
```

### 3. 开启插件 [#3-开启插件]

订阅远端视频轨道时，按照以下步骤开启 AI 画质插件：

1. 调用插件的 `createProcessor` 方法创建插件的 `processor`，并监听相关事件。
2. 调用 SDK 的 [`IRemoteVideoTrack.pipe`](/zh-CN/api-reference/rtc/web/interfaces/iremotevideotrack#pipe) 方法连接 AI 画质插件与该视频轨道之间的管线。
3. 调用插件的 `enable` 方法开启插件。开启后，播放该视频轨道即可看到 AI 画质插件的效果。

 :::warning[注意]
 为了避免多个 `processor` 同时工作导致性能问题，目前最多支持创建两个 `processor`，即 AI 画质插件最多同时对两个视频轨道生效。
 :::

```typescript
async function onUserPublished(user, mediaType) {
  if (!context.client || mediaType !== "video" || !!context.uid) {
    return;
  }
  context.uid = user.uid;
  context.track = await context.client.subscribe(user, mediaType);
  // 创建 processor
  context.processor = extension.createProcessor();

  // 监听 processor 相关事件
  context.processor.on("first-video-frame", (stats) => {
    console.log("plugin have first video frame, stats:", stats);
  });
  context.processor.on("error", (msg) => {
    console.log("plugin error:", msg);
  });
  context.processor.on("stats", (stats) => {
    console.log("plugin stats:", Date.now(), stats);
  });

  // 连接 processor 与视频轨道之间的管线
  context.track.pipe(context.processor).pipe(context.track.processorDestination);

  // 开启插件
  await context.processor.enable();
  context.track.play('any element id')
}
```

### 4. 切换视频轨道 [#4-切换视频轨道]

虽然插件限制了创建的 `processor` 数量，但你可以通过切换视频轨道的方式，实现使用同一个 `processor` 对多个视频轨道进行 AI 画质处理。

需要切换视频轨道时，调用 SDK 的 [`IRemoteVideotrack.unpipe`](/zh-CN/api-reference/rtc/web/interfaces/iremotevideotrack#unpipe) 方法断开 `processor` 与当前视频轨道的连接，然后调用 SDK 的 `IRemoteVideotrack.pipe` 方法连接新的视频轨道。

```javascript
{
    // 取消 processor 和当前视频轨道的连接管线
    await processor.disable();
    processor.unpipe();
    track?.unpipe();
    track?.pipe(
    track.processorDestination
    );
}
{
    // 连接新的视频轨道
    track2.pipe(processor).pipe(track2.processorDestination);
    await processor.enable()
    track2.play(ele)
}
```

### 5. 销毁插件 [#5-销毁插件]

需要停止使用 AI 画质插件时，调用 SDK 的 `IRemoteVideotrack.unpipe` 方法断开 `processor` 与当前视频轨道的管线，然后调用插件的 `release` 方法销毁 `processor`、调用 SDK 的 `stop` 方法停止视频轨道并置空销毁，否则可能存在异常风险。

```javascript
async function onUserUnpublished(user, mediaType) {
  if (!context.client || mediaType !== "video" || context.uid !== user.uid) {
    return;
  }
  // 先断开 processor 与当前视频轨道的管线
  context.processor.unpipe();
  context.track.unpipe();
  // 再销毁 processor 和视频轨道
  await context.processor.release();
  context.processor = undefined;
  context.track.stop();
  context.track = undefined;
}
```

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

AI 画质插件在 iOS 设备上有可能无法自适应分辨率切换，因此当远端视频分辨率发生变化时，你需要先销毁 `processor` 再重新创建。

<Accordions>
<Accordion title="以下示例代码可供参考">

```typescript
const isIOS =
  // @ts-ignore
  /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
const versions = navigator.userAgent.match(/Version\/([\d.]+).*Safari/);
let version = 0;
if (versions && versions.length > 1) {
  version = parseInt(versions[1]);
}
var shouldCleanResource: boolean =
  isIOS && isSafari && version < 16 ? true : false;


var extension: SuperClarityExtension | undefined = undefined;

var initSCProcessor = async () => {
  if (!extension) {
    extension = new SuperClarityExtension();
    AgoraRTC.registerExtensions([extension]);
  }

  var processor = extension.createProcessor();

  processor.on(SuperClarityEvents.FIRST_VIDEO_FRAME, (stats: any) => {
    console.log('plugin have first video frame, stats:', stats);
  });
  processor.on(SuperClarityEvents.ERROR, (msg: any) => {
    console.log('plugin error:', msg);
  });
  processor.on(SuperClarityEvents.STATS, (stats: any) => {
    console.log('plugin stats:', Date.now(), stats);
  });
  return processor;
};
var destroySCProcessor = async (processor) => {
  if (!processor ) {
    return;
  }
  processor.removeAllListeners(SuperClarityEvents.FIRST_VIDEO_FRAME);
  processor.removeAllListeners(SuperClarityEvents.ERROR);
  processor.removeAllListeners(SuperClarityEvents.STATS);
  await processor .release();
  processor= undefined;
};
// 刷新 processor
{
    var info:{ w?:number, h?:number}={}
    if (shouldCleanResource) {
      setInterval(async ()=>{
        !info.w && info.w = track?.getStats().receiveResolutionWidth;
        !info.h && info.h = track?.getStats().receiveResolutionHeight;
        if (
        (track?.getStats().receiveResolutionWidth &&
          info.w &&
          track?.getStats().receiveResolutionWidth !== info.w) ||
        (track?.getStats().receiveResolutionHeight &&
          info.h &&
          track?.getStats().receiveResolutionHeight !== info.h)
      ) {
        info.w = track?.getStats().receiveResolutionWidth;
        info.h = track?.getStats().receiveResolutionHeight;
        if (processor) {
          processor.unpipe();
          track?.unpipe();
          track?.pipe(track.processorDestination);
          await destroySCProcessor(processor);
          processor= undefined;
        }
          processor = await initSCProcessor();
            track!.pipe(processor !).pipe(track!.processorDestination);
            await processor !.enable();
        }
      }, 1000);
    }
}
```

</Accordion>
</Accordions>

## API 参考 [#api-参考]

本节提供 AI 画质插件的 API 参考。

### ISuperClarityExtension [#isuperclarityextension]

#### createProcessor [#createprocessor]

```typescript
createProcessor(): ISuperClarityProcessor;
```

创建 `ISuperClarityProcessor` 实例。

### ISuperClarityProcessor [#isuperclarityprocessor]

#### enable [#enable]

```typescript
enable(): void | Promise<void>;
```

开启 AI 画质功能。

#### disable [#disable]

```typescript
disable(): void | Promise<void>;
```

停止 AI 画质功能。

#### release [#release]

```typescript
release(): Promise<void>;
```

释放插件占用的所有资源。
