npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agora-rte-extension-ng

v1.0.3

Published

Agora RTE Extension — useExtension 模式的视频/音频插件开发框架

Readme

Agora RTE Extension v2

简介

Agora RTE Extension v2 是新一代插件 SDK,为 Agora Web SDK 提供 useExtension 模式的视频/音频插件开发能力,完全替代 v1 的 pipe 模式。

与 v1 不同,新框架由 SDK 统一管理媒体数据的获取、转换和输出,插件只需实现核心处理逻辑。视频插件在共享的 WebGL2 context 上直接操作 GPU texture,避免不必要的 CPU↔GPU 拷贝;音频插件通过 AudioWorklet 在实时音频线程中处理数据,需要更大帧长或 WASM 资源的算法由 worklet 侧插件自行缓冲和管理。

安装

npm install agora-rte-extension-ng@^1.0.0

架构概览

视频插件

摄像头 Track → texImage2D → inputTexture (GPU)
  → ext1.applyEffect({input:A, output:B}) → swap A↔B
  → ext2.applyEffect({input:B, output:A}) → swap A↔B
  → blitFramebuffer → canvas → 输出 Track

框架创建共享的 WebGL2 context,将视频帧上传为 GPU texture,然后按 useExtension 顺序依次调用每个 enabled 插件的 applyEffect,通过 ping-pong 交换 input/output texture。最终结果 blit 到 canvas 输出。

音频插件

麦克风 Track → AudioWorkletNode (框架)
  → 128 采样 quantum → AudioWorklet 侧插件 processAudioFrame(input)
  → 输出 Track

框架拥有 AudioWorklet 管线。主线程上的 AudioExtension 只承载控制面状态和生命周期;实时处理类在 AudioWorklet 模块中通过 registerAudioWorkletExtension() 注册,框架按 registrationName 实例化后,在 process() 回调中同步调用其 processAudioFrame()

快速开始

视频插件

import { VideoExtension } from "agora-rte-extension-ng";
import type { VideoExtensionContext, Frame } from "agora-rte-extension-ng";

class MyVideoExtension extends VideoExtension {
  public name = "MyVideoExtension";

  public init(context: VideoExtensionContext): void {
    super.init(context);
    // context.gl — 共享的 WebGL2RenderingContext
    // context.canvas — 共享的 HTMLCanvasElement
    // 在此初始化 WASM、shader 等资源
  }

  public destroy(): void {
    // 释放资源
    super.destroy();
  }

  public async applyEffect(frame: Frame): Promise<void> {
    if (frame.kind === "cpu") {
      // CPU 模式:frame.videoFrame 是 VideoFrame
      return;
    }
    // GPU 模式:
    // frame.input  — 当前帧输入纹理(只读)
    // frame.output — 必须将处理结果写入此纹理
    // frame.width / height — 帧尺寸

    // 示例:直接拷贝(直通)
    this.blitTexture(frame.input, frame.output, frame.width, frame.height);
  }
}

使用:

const ext = localVideoTrack.useExtension(MyVideoExtension);
await ext.disable();  // 框架跳过该插件处理,开销很低
await ext.enable();   // 重新启用
ext.destroy();        // 移除并销毁

音频插件

// my-audio-extension.ts(主线程控制面)
import { AudioExtension } from "agora-rte-extension-ng";
import type { AudioExtensionContext } from "agora-rte-extension-ng";

export class MyAudioExtension extends AudioExtension {
  public static extensionDefinition = {
    moduleURL: new URL("./my-audio-worklet.js", import.meta.url).href,
    registrationName: "my-audio-extension",
  };

  public name = "MyAudioExtension";

  public init(context: AudioExtensionContext): void {
    super.init(context);
    // context.audioContext — AudioContext
    // context.sampleRate — 采样率
  }
}
// my-audio-worklet.ts(AudioWorklet 侧实时处理)
import { registerAudioWorkletExtension } from "agora-rte-extension-ng";
import type {
  AudioWorkletExtensionContext,
  IAudioWorkletExtension,
} from "agora-rte-extension-ng";

class MyAudioWorkletExtension implements IAudioWorkletExtension {
  public init(context: AudioWorkletExtensionContext): void {
    // context.sampleRate — 采样率
    // context.extensionOptions — useExtension() 传入的初始配置
  }

  /**
   * 框架在 AudioWorklet 的 process() 中调用此方法。
   * 传入 128 采样的 Float32Array,必须同步返回处理后的 128 采样。
   * 如果算法帧长大于 128,在 worklet 侧维护缓冲,并同步返回本 quantum 的输出。
   */
  public processAudioFrame(input: Float32Array): Float32Array {
    // 示例:直通
    return input;
  }
}

registerAudioWorkletExtension("my-audio-extension", MyAudioWorkletExtension);

API 参考

VideoExtension(视频插件基类)

| 属性/方法 | 说明 | |-----------|------| | name: string | 插件名称(必须实现) | | enabled: boolean | 当前启用状态(只读) | | mode: VideoExtensionMode | 处理模式,默认 TEXTURE | | supportsCPUMode: boolean | 是否支持 CPU 回退模式,默认 false | | init(context) | 初始化,子类必须调用 super.init(context) | | destroy(options?) | 销毁,子类必须调用 super.destroy(options)。框架在回退/恢复过程中可能传入 { reason: "context-loss" }。 | | enable() | 启用插件 | | disable() | 禁用插件(框架跳过该插件处理,开销很低) | | applyEffect(frame) | TEXTURE 模式处理方法(子类覆写)。接收 Framekind: "gpu"kind: "cpu" | | processVideoFrame(frame) | VIDEO_FRAME 模式处理方法(子类覆写) | | setOptions(options) | 声明式配置入口(唯一入站通道)。子类覆写以应用配置;框架在 init() 后用初始 extensionOptions 自动调用一次。options 中的图片 / ArrayBuffer 会自动跨 worker 边界 transfer。 | | on(event, handler) / off(event, handler) | 监听插件通过 emit(event, data) 推送出的数据(唯一出站通道),例如逐帧 face landmark。 |

受保护的 GL 工具方法

| 方法 | 说明 | |------|------| | blitTexture(src, dst, w, h) | 通过 blitFramebuffer 拷贝 texture | | drawTextureToFbo(tex, fbo, w, h) | 使用直通 shader 渲染到 FBO | | createTexture(w, h) | 创建 RGBA texture | | createFbo(texture) | 创建绑定到 texture 的 FBO | | compileProgram(vert, frag) | 编译链接 shader program | | pixelToGL(px, py, w, h) | 像素坐标转 GL NDC 坐标(框架 Y 翻转约定) |

AudioExtension(音频插件基类)

| 属性/方法 | 说明 | |-----------|------| | name: string | 插件名称(必须实现) | | enabled: boolean | 当前启用状态(只读) | | init(context) | 初始化主线程控制面,子类必须调用 super.init(context) | | destroy(options?) | 销毁主线程控制面资源,子类必须调用 super.destroy(options)。 | | enable() | 启用插件 | | disable() | 禁用插件 |

AudioWorklet 侧插件

| API | 说明 | |-----|------| | AudioExtension.extensionDefinition(静态字段) | 声明 moduleURL / registrationName,供框架加载 worklet 模块 | | registerAudioWorkletExtension(name, ctor) | 在 AudioWorklet 模块中注册实时处理类 | | IAudioWorkletExtension.init(context) | 初始化 worklet 侧插件,可读取 sampleRate 与初始 extensionOptions | | IAudioWorkletExtension.processAudioFrame(input, referenceInputs?) | 实时音频处理方法;每次接收 128 采样,必须同步、低分配地返回 128 采样 |

VideoExtensionMode(枚举)

| 值 | 说明 | |----|------| | TEXTURE | GPU Texture 模式(默认,推荐) | | VIDEO_FRAME | VideoFrame 模式(旧插件兼容) |

Frame(类型)

type Frame =
  | { kind: "gpu"; input: WebGLTexture; output: WebGLTexture; width: number; height: number }
  | { kind: "cpu"; width: number; height: number; videoFrame: VideoFrame };

VideoExtensionContext / AudioExtensionContext(类型)

type VideoExtensionContext = {
  backend: "gpu" | "cpu";
  threadMode: "worker" | "main";
  canvas?: HTMLCanvasElement | OffscreenCanvas;
  gl?: WebGL2RenderingContext;
};

type AudioExtensionContext = {
  audioContext: AudioContext;
  sampleRate: number;
  extensionOptions?: Record<string, unknown>;
};

type AudioWorkletExtensionContext = {
  sampleRate: number;
  extensionOptions?: Record<string, unknown>;
};

视频路径说明

帧尺寸偶数对齐

框架会自动将帧尺寸向下对齐到偶数(如 641→640, 481→480)。这是因为视频编解码器使用的 YUV 色度子采样(NV12/I420)要求宽高为偶数,奇数尺寸会导致编解码链路中出现绿屏。插件收到的 frame.width / frame.height 始终是偶数对齐后的值,与 texture 尺寸一致。

WebGL (GPU) 主路径

直接读 input texture,通过 shader 或 WASM 处理,写 output texture。常规 GPU 插件推荐使用这一路径。

适用:超分辨率、水印叠加、色彩调整等以 GPU 为主的处理。

CPU 数据兼容路径

如插件确实需要 CPU 侧像素数据,可使用 VIDEO_FRAME 模式(声明 mode = VideoExtensionMode.VIDEO_FRAME),框架会自动做 texture ↔ VideoFrame 转换。或者在 applyEffect 中处理 frame.kind === "cpu" 分支。

新插件应优先使用 WebGL (GPU) 主路径,仅在确有需要时使用 VIDEO_FRAME 模式。

与 v1 pipe 模式的区别

| | v1 pipe 模式 | v2 useExtension 模式 | |---|---|---| | 数据流 | Processor 链,每个 Processor 自行管理 MediaStreamTrack | 框架统一管理 GPU texture,插件只操作 texture | | GL context | 每个 Processor 自建 | 框架创建共享 context | | CPU↔GPU 拷贝 | 每个 Processor 至少一次 | WebGL (GPU) 主路径通常无额外 CPU 拷贝;CPU 数据兼容路径会增加一次 readPixels | | 生命周期 | registerExtension → createProcessor → pipe → unpipe | useExtension → enable/disable → destroy | | 互斥 | 不能与 useExtension 同时使用 | 不能与 pipe 同时使用 |

构建

npm run build

输出到 dist/ 目录:

  • AgoraRTC_N-extension.js — UMD
  • AgoraRTC_N-extension.esm.mjs — ESM
  • extension.d.ts — TypeScript 类型声明

测试

npm test

运行当前提交的单元测试,覆盖 SDK 基类、管理器/运行时行为以及 WebGL context loss/restore 处理。