开发视频插件
本文展示如何开发一个与声网 Web SDK 4.x 搭配使用的视频插件。
技术原理
声网为开发视频插件提供以下抽象类:
- 必需的基础类:
Extension
:实现插件的初始化,包括创建插件、设置日志上报、设置事件上报等。VideoProcessor
:实现视频数据的处理能力,包括接收、处理和返回处理完的视频数据。
- 可选的辅助类:
Ticker
:帮助管理周期性任务。Logger
:向声网 SDK 上传日志。Reporter
:向声网 SDK 报告事件。
前提条件
开发前,请确保你的开发环境满足以下要求:
- Windows 或 macOS 计算机,需满足以下要求:
- 下载声网 Web SDK 支持的浏览器。声网强烈推荐使用最新稳定版 Google Chrome 浏览器。
- 具备物理音视频采集设备。
- 可连接到互联网。如果你的网络环境部署了防火墙,请参考应对防火墙限制以正常使用声网服务。
- 搭载 2.2 GHz Intel 第二代 i3/i5/i7 处理器或同等性能的其他处理器。
- 安装 Node.js 及 npm。
准备开发环境
通过 npm 将插件开发模块(agora-rte-extension)集成到你的项目中:
-
运行以下命令安装:
Shellnpm install --save agora-rte-extension
-
在你的
.js
文件中加入以下代码导入插件开发模块:JavaScriptimport {AudioExtension, AudioProcessor} from 'agora-rte-extension'
开发视频插件
本节详细介绍开发视频插件必须实现的 API。你还可以根据实际需要实现一些辅助类 API,优化插件的设计和性能,详见 API 参考。
实现插件初始化
插件初始化通过 Extension
类实现。你需要实现 Extension
类的 createProcessor
方法:
abstract class Extension<T extends BaseProcessor> {
createProcessor(): T;
}
创建 VideoProcessor
实例。
插件使用者调用 extension.createProcessor
时,SDK 会调用该方法。你需要在该方法中返回创建好的 VideoProcessor
实例。
实现视频数据处理
实现视频数据处理包括以下步骤:
- 接收待处理的视频:通过
VideoProcessor
类的onTrack
方法实现。 - 处理视频:需要你自行实现。
- 输出处理后的视频:通过
VideoProcessor
类的output
方法实现。
onTrack
abstract onTrack?(track: MediaStreamTrack, context: IProcessorContext): void;
从前一个节点接收到待处理的视频数据回调。
参数
-
track
:前一个MediaStreamTrack
。MediaStreamTrack
是 Web API 提供的接口类,详见MediaStreamTrack
。 -
context
:当前视频处理管道的 context。
output
output(track: MediaStreamTrack, context: IProcessorContext): void;
输出处理后的视频数据。
参数
-
track
:处理后的MediaStreamTrack
。 -
context
:当前视频处理管道的 context。
示例项目
声网提供一个 Web 插件开发的示例项目供你参考,你可以前往下载或查看其中的源代码。
- Gitee:ExtensionDemo
- GitHub:ExtensionDemo
以下代码展示了如何开发一个视频插件:
class YourExtension extends Extension<YourProcessor> {
// 创建 Processor
protected _createProcessor(): YourProcessor {
return new YourProcessor();
}
}
class CustomVideoProcessor extends VideoProcessor {
// 从前一个 MediaStreamTrack 接收到视频数据
onTrack(track: MediaStreamTrack, context: IProcessorContext) {
this.videoElement.srcObject = new MediaStream([track]);
this.videoElement.play();
this.loop();
}
// 连续抽取视频帧为 ImageData 对象,用于后续的视频处理
loop() {
this.ctx.drawImage(this.videoElement, 0, 0);
const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
this.process(imageData);
requestAnimationFrame(() => this.loop());
}
process() {
//TODO:添加你的插件的视频处理逻辑
}
doneProcessing() {
// 组装成 MediaStreamTrack
const msStream = this.canvas.captureStream(30);
const outputTrack = msStream.getVideoTracks()[0];
// 输出处理后的视频数据
if (this.context) {
this.output(outputTrack, this.context);
}
}
}
API 参考
本节介绍开发视频插件的辅助类 API。
VideoProcessor
name
name: string;
Processor
的名称。
enabled
enabled :boolean;
Processor
的开启状态。
ID
public readonly ID:string;
Processor
的标识符。
kind
public get Kind():'video' | 'audio';
Processor
的类型,分为视频和音频两种。
context
protected context?: IProcessorContext;
用于 Processor
请求原始媒体流并重新进行采集。
onPiped
abstract onPiped?(context: IProcessorContext): void;
LocalVideoTrack
连接到当前媒体处理管道回调。
如果只是连接多个 Processor
,不会触发该回调。例如:插件使用者调用 processorA.pipe(processorB)
。
onUnpiped
abstract onUnPiped?(): void;
Processor
断开与媒体处理管道的连接回调。
onEnableChange
abstract onEnableChange?(enabled: boolean): void | Promise<void>;
Processor
的开启状态变化回调。
inputTrack
protected inputTrack?:MediaStreamTrack;
当前 Processor
从前一个 Processor
或者 LocalVideoTrack
接收到的视频。
outputTrack
protected outputTrack?:MediaStreamTrack;
当前 Processor
处理后的视频。调用 output
方法会自动设置 outputTrack
。
Ticker
constructor
public constructor(type:"Timer" | "RAF" | "Oscillator", interval: number):Ticker;
Ticker
的构建函数。
参数
-
type
:类型,支持以下三种:-
Timer
: 使用setTimeout
作为插件内部的计时器。 -
(推荐)
RAF
: 使用requestAnimationFrame
作为插件内部的计时器。大多数情况下,该类型的Ticker
渲染性能最好。 -
Oscillator
: 使用WebAudio
的OscillatorNode
作为插件内部的计时器。该类型的Ticker
在浏览器页面不被浏览时也能运行。
-
-
interval
:两次回调之间的间隔。Ticker
会按照这个间隔执行,但无法保证 100% 精准。
Ticker.add
public add(fn: Function): void;
添加计时任务。
Ticker.remove
public remove():void;
取消添加的计时任务。
Ticker.start
public start():void;
开始计时。
Ticker.stop
public stop():void;
停止计时。
Logger
interface IExtensionLogger {
debug(...args: any): void;
error(...args: any): void;
info(...args: any): void;
warning(...args: any): void;
}
提供四种级别的日志。
插件使用者如果在调用 AgoraRTC.registerExtension
注册插件时选择了上传日志,通过 Logger
类上报日志的插件状态会更新。
Reporter
Reporter.reportApiInvoke
public reportApiInvoke<T>(params: ReportApiInvokeParams): AgoraApiExecutor<T>;
向 SDK 报告 API 调用的相关事件。
其中 ReportApiInvokeParams
和 AgoraApiExecutor
的定义如下:
interface ReportApiInvokeParams {
// 被调用的 API 的名称
name: string;
// 与该 API 有关的参数或选项
options: any;
// 是否报告该 API 的调用结果
reportResult?: boolean;
// 定义多久为调用超时
timeout?: number;
}
interface AgoraApiExecutor<T> {
// API 调用成功
onSuccess: (result: T) => void;
// API 调用失败
onError: (err: Error) => void;
}