使用 AI 画质插件 (Beta)
AI 画质插件封装了声网自研的 AI 画质增强算法,可以在不改变分辨率的条件下智能提升视频画质,优化接收端的观看体验。AI 画质增强算法对移动端机型的覆盖率达到 95% 以上,并且在低端机上也能保证较大分辨率视频的渲染画质。
实现原理
声网 Web SDK 的媒体传输管道包含采集、前处理、编码、传输、解码、后处理和播放等环节。 在后处理阶段,插件可以对管道中的音视频数据进行处理,从而实现 AI 画质等功能。
前提条件
开始之前,请确保你满足以下条件:
-
已集成 v4.19.0 或以上版本的 Web SDK,实现基础的实时音视频互动功能,具体参考快速开始文档。
-
使用下表列出的浏览器版本,未列出的浏览器暂不支持:
浏览器 版本要求 桌面端、移动端 Chrome 96 或以上 桌面端 Safari 15.4 或以上 移动端 Safari iOS 16.0 或以上
示例代码
以下是一段使用 AI 画质插件的完整代码,供开发者参考。
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');
实现步骤
本节介绍使用 AI 画质插件的主要流程。
1. 集成插件
-
通过 npm 将 AI 画质插件集成到你的项目中:
Bashnpm install agora-extension-super-clarity
-
通过以下任意一种方式引入 AI 画质插件。
-
方法一:在 JavaScript 文件中加入以下代码引入。
TypeScriptimport {
SuperClarityExtension,
SuperClarityEvents,
SuperClarityProcessor,
} from 'agora-extension-super-clarity'; -
方法二:在 HTML 文件中通过 Script 标签引入。引入后即可在 JavaScript 文件中直接使用
SuperClarityExtension
对象。html<script src="./agora-extension-super-clarity.js"></script>
-
2. 注册插件
在加入频道之前,创建 SuperClarityExtension
对象并调用 SDK 的registerExtensions
方法注册插件。
// 创建 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. 开启插件
订阅远端视频轨道时,按照以下步骤开启 AI 画质插件:
- 调用插件的
createProcessor
方法创建插件的processor
,并监听相关事件。 - 调用 SDK 的
IRemoteVideoTrack.pipe
方法连接 AI 画质插件与该视频轨道之间的管线。 - 调用插件的
enable
方法开启插件。开启后,播放该视频轨道即可看到 AI 画质插件的效果。
为了避免多个 processor
同时工作导致性能问题,目前最多支持创建两个 processor
,即 AI 画质插件最多同时对两个视频轨道生效。
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. 切换视频轨道
虽然插件限制了创建的 processor
数量,但你可以通过切换视频轨道的方式,实现使用同一个 processor
对多个视频轨道进行 AI 画质处理。
需要切换视频轨道时,调用 SDK 的 IRemoteVideotrack.unpipe
方法断开 processor
与当前视频轨道的连接,然后调用 SDK 的 IRemoteVideotrack.pipe
方法连接新的视频轨道。
{
// 取消 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. 销毁插件
需要停止使用 AI 画质插件时,调用 SDK 的 IRemoteVideotrack.unpipe
方法断开 processor
与当前视频轨道的管线,然后调用插件的 release
方法销毁 processor
、调用 SDK 的 stop
方法停止视频轨道并置空销毁,否则可能存在异常风险。
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
再重新创建。
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);
}
}
API 参考
本节提供 AI 画质插件的 API 参考。
ISuperClarityExtension
createProcessor
createProcessor(): ISuperClarityProcessor;
创建 ISuperClarityProcessor
实例。
ISuperClarityProcessor
enable
enable(): void | Promise<void>;
开启 AI 画质功能。
disable
disable(): void | Promise<void>;
停止 AI 画质功能。
release
release(): Promise<void>;
释放插件占用的所有资源。