├── ExtensionDemo
├── .gitignore
├── babel.config.json
├── src
│ ├── index.ts
│ ├── audio-extension.ts
│ └── video-extension.ts
├── README.md
├── package.json
├── webpack.config.js
├── tsconfig.json
└── index.html
├── .gitignore
├── src
├── assets
│ ├── github.png
│ ├── favicon.ico
│ ├── agora-logo-en.png
│ └── agora-logo-zh.png
├── example
│ ├── advanced
│ │ ├── audioEffects
│ │ │ ├── audio.mp3
│ │ │ └── HeroicAdventure.mp3
│ │ ├── customVideoSource
│ │ │ ├── assets
│ │ │ │ └── sample.mp4
│ │ │ └── index.html
│ │ ├── vadExtention
│ │ │ ├── agora-extension-vad
│ │ │ │ ├── wasm
│ │ │ │ │ ├── web-vad.wasm
│ │ │ │ │ └── web-vad_simd.wasm
│ │ │ │ ├── package.json
│ │ │ │ ├── README.md
│ │ │ │ └── index.d.ts
│ │ │ └── index.css
│ │ ├── testMediaDevices
│ │ │ ├── jquery.jsonview.min.css
│ │ │ └── jquery.jsonview.min.js
│ │ ├── adjustVideoProfile
│ │ │ └── index.html
│ │ ├── geoFencing
│ │ │ └── index.html
│ │ ├── selfCapturing
│ │ │ └── index.html
│ │ ├── displayCallStats
│ │ │ └── index.html
│ │ ├── shareTheScreen
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── screenshot
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── joinMultipleChannel
│ │ │ └── index.html
│ │ └── selfRendering
│ │ │ ├── index.html
│ │ │ └── index.js
│ ├── basic
│ │ ├── basicLive
│ │ │ ├── assets
│ │ │ │ └── doubt.png
│ │ │ └── index.html
│ │ └── basicVoiceCall
│ │ │ └── index.html
│ ├── extension
│ │ ├── spatialAudio
│ │ │ ├── resources
│ │ │ │ ├── 1.mp3
│ │ │ │ ├── 2.mp3
│ │ │ │ └── 3.mp3
│ │ │ └── audio_spatializer.wasm
│ │ ├── videoCompositor
│ │ │ ├── assets
│ │ │ │ ├── city.jpg
│ │ │ │ └── space.jpg
│ │ │ ├── agora-extension-video-compositor
│ │ │ │ ├── LICENSE.txt
│ │ │ │ ├── package.json
│ │ │ │ ├── index_cn.d.ts
│ │ │ │ ├── index_en.d.ts
│ │ │ │ └── index.d.ts
│ │ │ └── index.html
│ │ ├── vad
│ │ │ ├── agora-extension-vad
│ │ │ │ ├── wasm
│ │ │ │ │ ├── web-vad.wasm
│ │ │ │ │ └── web-vad_simd.wasm
│ │ │ │ ├── package.json
│ │ │ │ ├── README.md
│ │ │ │ └── index.d.ts
│ │ │ └── index.html
│ │ ├── aiDenoiser
│ │ │ ├── agora-extension-ai-denoiser
│ │ │ │ ├── external
│ │ │ │ │ ├── denoiser-wasm.wasm
│ │ │ │ │ └── denoiser-wasm-simd.wasm
│ │ │ │ ├── package.json
│ │ │ │ ├── README.md
│ │ │ │ └── index.d.ts
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── virtualBackground
│ │ │ ├── agora-extension-virtual-background
│ │ │ │ ├── wasms
│ │ │ │ │ └── agora-wasm.wasm
│ │ │ │ ├── package.json
│ │ │ │ ├── index_cn.d.ts
│ │ │ │ ├── index_en.d.ts
│ │ │ │ └── index.d.ts
│ │ │ └── index.html
│ │ └── superClarity
│ │ │ └── index.html
│ ├── framework
│ │ └── react
│ │ │ └── index.html
│ ├── others
│ │ ├── dualStream
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ └── pushStreamToCDN
│ │ │ └── index.html
│ └── quickStart
│ │ └── videoAndVoiceCalling
│ │ └── index.html
├── CHANGELOG.md
├── i18n
│ ├── zh-CN
│ │ └── index.json
│ ├── en
│ │ └── index.json
│ └── language.js
├── index.html
├── common
│ └── left-menu.js
└── index.js
├── package.json
├── scripts
├── server.js
└── pure.js
├── README.cn.md
└── README.md
/ExtensionDemo/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | node_modules/
3 | .code/
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | .vercel
7 |
--------------------------------------------------------------------------------
/src/assets/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/assets/github.png
--------------------------------------------------------------------------------
/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/assets/favicon.ico
--------------------------------------------------------------------------------
/ExtensionDemo/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-typescript", "@babel/preset-env"]
3 | }
4 |
--------------------------------------------------------------------------------
/src/assets/agora-logo-en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/assets/agora-logo-en.png
--------------------------------------------------------------------------------
/src/assets/agora-logo-zh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/assets/agora-logo-zh.png
--------------------------------------------------------------------------------
/src/example/advanced/audioEffects/audio.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/advanced/audioEffects/audio.mp3
--------------------------------------------------------------------------------
/src/example/basic/basicLive/assets/doubt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/basic/basicLive/assets/doubt.png
--------------------------------------------------------------------------------
/src/example/extension/spatialAudio/resources/1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/spatialAudio/resources/1.mp3
--------------------------------------------------------------------------------
/src/example/extension/spatialAudio/resources/2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/spatialAudio/resources/2.mp3
--------------------------------------------------------------------------------
/src/example/extension/spatialAudio/resources/3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/spatialAudio/resources/3.mp3
--------------------------------------------------------------------------------
/src/example/advanced/audioEffects/HeroicAdventure.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/advanced/audioEffects/HeroicAdventure.mp3
--------------------------------------------------------------------------------
/src/example/extension/videoCompositor/assets/city.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/videoCompositor/assets/city.jpg
--------------------------------------------------------------------------------
/src/example/extension/videoCompositor/assets/space.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/videoCompositor/assets/space.jpg
--------------------------------------------------------------------------------
/src/example/advanced/customVideoSource/assets/sample.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/advanced/customVideoSource/assets/sample.mp4
--------------------------------------------------------------------------------
/src/example/extension/spatialAudio/audio_spatializer.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/spatialAudio/audio_spatializer.wasm
--------------------------------------------------------------------------------
/src/example/extension/vad/agora-extension-vad/wasm/web-vad.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/vad/agora-extension-vad/wasm/web-vad.wasm
--------------------------------------------------------------------------------
/src/example/extension/videoCompositor/agora-extension-video-compositor/LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*! CommitId: 8abb6edc9e3b0b366fbf183ff1e31e44288fad1b | Version: 1.0.0-beta | Copyright AgoraInc. */
2 |
--------------------------------------------------------------------------------
/src/example/extension/vad/agora-extension-vad/wasm/web-vad_simd.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/vad/agora-extension-vad/wasm/web-vad_simd.wasm
--------------------------------------------------------------------------------
/src/example/advanced/vadExtention/agora-extension-vad/wasm/web-vad.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/advanced/vadExtention/agora-extension-vad/wasm/web-vad.wasm
--------------------------------------------------------------------------------
/src/example/advanced/vadExtention/agora-extension-vad/wasm/web-vad_simd.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/advanced/vadExtention/agora-extension-vad/wasm/web-vad_simd.wasm
--------------------------------------------------------------------------------
/ExtensionDemo/src/index.ts:
--------------------------------------------------------------------------------
1 | import { SimpleAudioExtension } from "./audio-extension";
2 | import { SimpleVideoExtension } from "./video-extension";
3 |
4 | export { SimpleAudioExtension, SimpleVideoExtension };
5 |
--------------------------------------------------------------------------------
/src/example/extension/aiDenoiser/agora-extension-ai-denoiser/external/denoiser-wasm.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/aiDenoiser/agora-extension-ai-denoiser/external/denoiser-wasm.wasm
--------------------------------------------------------------------------------
/src/example/extension/aiDenoiser/agora-extension-ai-denoiser/external/denoiser-wasm-simd.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/aiDenoiser/agora-extension-ai-denoiser/external/denoiser-wasm-simd.wasm
--------------------------------------------------------------------------------
/src/example/extension/virtualBackground/agora-extension-virtual-background/wasms/agora-wasm.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/HEAD/src/example/extension/virtualBackground/agora-extension-virtual-background/wasms/agora-wasm.wasm
--------------------------------------------------------------------------------
/src/example/extension/aiDenoiser/agora-extension-ai-denoiser/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agora-extension-ai-denoiser",
3 | "version": "1.1.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "index.js",
7 | "module": "index.esm.js",
8 | "typings": "index.d.ts",
9 | "author": "agora",
10 | "peerDependencies": {
11 | "agora-rtc-sdk-ng": ">=4.15.0"
12 | },
13 | "license": "MIT"
14 | }
15 |
--------------------------------------------------------------------------------
/src/example/extension/videoCompositor/agora-extension-video-compositor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agora-extension-video-compositor",
3 | "version": "1.0.0-beta",
4 | "description": "Video track compositing plugin of Agora WebSDK",
5 | "keywords": [],
6 | "main": "agora-extension-video-compositor.js",
7 | "typings": "index.d.ts",
8 | "declaration": true,
9 | "declarationMap": true,
10 | "author": "agora",
11 | "license": ""
12 | }
13 |
--------------------------------------------------------------------------------
/ExtensionDemo/README.md:
--------------------------------------------------------------------------------
1 | # Agora-RTE-Extension Demo
2 |
3 | A simple demo for how to create video and audio extension for Agora Web SDK NG.
4 |
5 | Detailed documentation [here](https://docs.agora.io/en/extension_vendor/plugin_web_ng?platform=Web).
6 |
7 | ## Install Dependencies
8 |
9 | ```shell
10 | npm install
11 | ```
12 |
13 | ## Build
14 |
15 | ```shell
16 | npm run build
17 | ```
18 |
19 | ## Run the demo
20 |
21 | ```shell
22 | npm run demo
23 | ```
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api-examples-web",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "node ./scripts/server.js",
8 | "pure": "node ./scripts/pure.js",
9 | "lint": "prettier . --write --ignore-unknown"
10 | },
11 | "devDependencies": {
12 | "express": "^4.18.2",
13 | "prettier": "^3.3.3"
14 | },
15 | "author": "qz",
16 | "license": "ISC",
17 | "packageManager": "yarn@1.22.22"
18 | }
19 |
--------------------------------------------------------------------------------
/src/example/extension/virtualBackground/agora-extension-virtual-background/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agora-extension-virtual-background",
3 | "version": "1.1.3",
4 | "description": "",
5 | "keywords": [],
6 | "main": "agora-extension-virtual-background.js",
7 | "typings": "index.d.ts",
8 | "declaration": true,
9 | "declarationMap": true,
10 | "author": "agora",
11 | "peerDependencies": {
12 | "agora-rtc-sdk-ng": ">=4.15.0"
13 | },
14 | "license": ""
15 | }
16 |
--------------------------------------------------------------------------------
/scripts/server.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const path = require("path");
3 |
4 | // change the port if necessary
5 | const PORT = 3001;
6 | const URL = `http://localhost:${PORT}/index.html`;
7 |
8 | const dir = path.join(__dirname, "../src");
9 | const app = express();
10 | app.use(express.static(dir));
11 |
12 | app.listen(PORT, () => {
13 | console.info(`\n---------------------------------------\n`);
14 | console.info(`please visit: ${URL}`);
15 | console.info(`\n---------------------------------------\n`);
16 | });
17 |
--------------------------------------------------------------------------------
/ExtensionDemo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "",
3 | "main": "dist/index.js",
4 | "scripts": {
5 | "test": "echo \"Error: no test specified\" && exit 1",
6 | "build": "webpack",
7 | "demo": "live-server ."
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "agora-rte-extension": "^1.0.23"
13 | },
14 | "devDependencies": {
15 | "@babel/core": "^7.16.7",
16 | "@babel/preset-env": "^7.16.11",
17 | "@babel/preset-typescript": "^7.16.7",
18 | "babel-loader": "^8.2.3",
19 | "typescript": "^4.6.3",
20 | "webpack": "^5.71.0",
21 | "webpack-cli": "^4.9.2",
22 | "live-server": "^1.2.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ExtensionDemo/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const ROOT = __dirname;
3 |
4 | module.exports = {
5 | mode: "production",
6 | entry: path.resolve(ROOT, "src", "index.ts"),
7 | output: {
8 | path: path.resolve(ROOT, "dist"),
9 | filename: "index.js",
10 | library: {
11 | type: "umd",
12 | name: "SimpleExtension",
13 | },
14 | },
15 | module: {
16 | rules: [
17 | {
18 | test: /.ts$/,
19 | include: path.resolve(ROOT, "src"),
20 | exclude: /node_modules/,
21 | loader: "babel-loader",
22 | },
23 | ],
24 | },
25 | resolve: {
26 | extensions: [".ts", ".js"],
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/src/example/extension/vad/agora-extension-vad/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agora-extension-vad",
3 | "version": "1.0.0-beta.1",
4 | "description": "Agora Web SDK plugin for voice activity detection",
5 | "main": "index.js",
6 | "module": "index.esm.js",
7 | "types": "index.d.ts",
8 | "declaration": true,
9 | "author": "Agora WebRTC Team",
10 | "keywords": [
11 | "VAD",
12 | "Web VAD",
13 | "WebRTC",
14 | "agora"
15 | ],
16 | "license": "MIT",
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/AgoraIO/agora-rtc-web.git"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/AgoraIO/agora-rtc-web/issues"
23 | },
24 | "homepage": "https://api-ref.agora.io/en/voice-sdk/web/4.x/index.html"
25 | }
26 |
--------------------------------------------------------------------------------
/src/example/advanced/vadExtention/agora-extension-vad/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agora-extension-vad",
3 | "version": "1.0.0-beta.1",
4 | "description": "Agora Web SDK plugin for voice activity detection",
5 | "main": "index.js",
6 | "module": "index.esm.js",
7 | "types": "index.d.ts",
8 | "declaration": true,
9 | "author": "Agora WebRTC Team",
10 | "keywords": [
11 | "VAD",
12 | "Web VAD",
13 | "WebRTC",
14 | "agora"
15 | ],
16 | "license": "MIT",
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/AgoraIO/agora-rtc-web.git"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/AgoraIO/agora-rtc-web/issues"
23 | },
24 | "homepage": "https://api-ref.agora.io/en/voice-sdk/web/4.x/index.html"
25 | }
26 |
--------------------------------------------------------------------------------
/ExtensionDemo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/tsconfig",
3 | "compilerOptions": {
4 | "moduleResolution": "node",
5 | "target": "es5",
6 | "module": "umd",
7 | "lib": ["es2015", "es2016", "es2017", "dom"],
8 | "strict": true,
9 | "strictNullChecks": true,
10 | "strictPropertyInitialization": true,
11 | "sourceMap": false,
12 | "declaration": true,
13 | "declarationMap": true,
14 | "allowJs": true,
15 | "emitDeclarationOnly": true,
16 | "allowSyntheticDefaultImports": true,
17 | "experimentalDecorators": true,
18 | "emitDecoratorMetadata": true,
19 | "esModuleInterop": true,
20 | "stripInternal": true,
21 | "isolatedModules": true,
22 | "outDir": "./dist",
23 | "typeRoots": ["node_modules/@types"]
24 | },
25 | "include": ["src/**/*"]
26 | }
27 |
--------------------------------------------------------------------------------
/src/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [2.1.0] - 2024-11-28
4 |
5 | 1. Address the issue of screen sharing being unavailable in Safari.
6 | 2. Disable the autoplay of custom videos in the mobile version of Safari.
7 | 3. Enable the selection of MP3 files on iOS devices for audio effect testing.
8 | 4. Rectify the problem of the device still capturing from the camera and microphone when not in use.
9 | 5. Resolve the inability to adjust audio sound effects on mobile devices.
10 | 6. Replace VAD with Voice Activity Detection.
11 | 7. Introduce a dropdown menu for selecting to subscribe to remote users.
12 | 8. Add a direct link to GitHub for convenient access.
13 | 9. Support the addition of dynamic tokens for channel entry.
14 |
15 | ## [2.1.0] - 2024-12-30
16 |
17 | 1. Add translate
18 | 2. Add rule verification for appid
19 | 3. Enter appid to save automatically
20 | 4. Corrected some boot links
21 |
--------------------------------------------------------------------------------
/src/example/advanced/testMediaDevices/jquery.jsonview.min.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";.jsonview{font-family:monospace;font-size:1.1em;white-space:pre-wrap}.jsonview .prop{font-weight:700;text-decoration:none;color:#000}.jsonview .null,.jsonview .undefined{color:red}.jsonview .bool,.jsonview .num{color:#00f}.jsonview .string{color:green;white-space:pre-wrap}.jsonview .string.multiline{display:inline-block;vertical-align:text-top}.jsonview .collapser{position:absolute;left:-1em;cursor:pointer}.jsonview .collapsible{transition:height 1.2s;transition:width 1.2s}.jsonview .collapsible.collapsed{height:.8em;width:1em;display:inline-block;overflow:hidden;margin:0}.jsonview .collapsible.collapsed:before{content:"…";width:1em;margin-left:.2em}.jsonview .collapser.collapsed{transform:rotate(0)}.jsonview .q{display:inline-block;width:0;color:transparent}.jsonview li{position:relative}.jsonview ul{list-style:none;margin:0 0 0 2em;padding:0}.jsonview h1{font-size:1.2em}
--------------------------------------------------------------------------------
/ExtensionDemo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 | Document
11 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/example/advanced/vadExtention/index.css:
--------------------------------------------------------------------------------
1 | .banner {
2 | padding: 0;
3 | background-color: #52575c;
4 | color: white;
5 | }
6 |
7 | .banner-text {
8 | padding: 8px 20px;
9 | margin: 0;
10 | }
11 |
12 | #join-form {
13 | margin-top: 10px;
14 | }
15 |
16 | .tips {
17 | font-size: 12px;
18 | margin-bottom: 2px;
19 | color: gray;
20 | }
21 |
22 |
23 |
24 | input {
25 | width: 100%;
26 | margin-bottom: 2px;
27 | }
28 |
29 | .player {
30 | width: 480px;
31 | height: 320px;
32 | }
33 |
34 | .player-name {
35 | margin: 8px 0;
36 | }
37 |
38 | #success-alert,
39 | #success-alert-with-token {
40 | display: none;
41 | }
42 |
43 | @media (max-width: 640px) {
44 | .player {
45 | width: 320px;
46 | height: 240px;
47 | }
48 | }
49 |
50 | .collapse-wrapper {
51 | display: inline-block;
52 | position: relative;
53 | }
54 |
55 | .collapse-content {
56 | display: flex;
57 | flex-direction: column;
58 | justify-content: center;
59 | align-items: center;
60 | z-index: 999;
61 | }
62 |
63 | .collapse-wrapper .collapse-btn {
64 | padding-left: 6.5rem;
65 | padding-right: 6.5rem;
66 | }
67 |
68 | @media (max-width: 640px) {
69 | .collapse-wrapper {
70 | margin-top: 10px;
71 | }
72 | }
73 |
74 | @media (max-width: 380px) {
75 | .collapse-wrapper .collapse-btn {
76 | padding-left: 4.5rem;
77 | padding-right: 4.5rem;
78 | }
79 | }
80 |
81 | .custom-switch {
82 | height: 30px;
83 | line-break: 30px;
84 | }
85 |
--------------------------------------------------------------------------------
/src/i18n/zh-CN/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "settingMenu": "设置",
3 | "quickMenu": "快速开始",
4 | "basicMenu": "基础示例",
5 | "videoAndVoiceCalling": "音视频通话",
6 | "interactiveLiveStreamingStandard":"极速直播",
7 | "interactiveLiveStreamingPremium":"互动直播",
8 | "advancedMenu": "进阶示例",
9 | "othersMenu": "其他示例",
10 | "extensionMenu": "插件示例",
11 | "frameworkMenu": "框架示例",
12 | "initializeSettings": "初始化设置",
13 | "basicVoiceCall": "基础语音通话",
14 | "basicVideoCall": "基础视频通话",
15 | "basicLive": "基础视频直播",
16 | "shareTheScreen": "屏幕共享",
17 | "testMediaDevices": "音视频设备检测",
18 | "adjustVideoProfile": "视频编码属性",
19 | "displayCallStats": "通话中质量检测",
20 | "audioEffects": "音频音效",
21 | "joinMultipleChannel": "加入多频道",
22 | "customVideoSource": "自定义视频源",
23 | "selfRendering": "视频自渲染",
24 | "selfCapturing": "视频自采集",
25 | "screenshot": "视频截图",
26 | "geoFencing": "区域访问限制",
27 | "pushStreamToCDN": "推流到CDN",
28 | "dualStream": "大小流",
29 | "virtualBackground": "虚拟背景",
30 | "beauty": "美颜",
31 | "aiDenoiser": "AI降噪",
32 | "superClarity": "AI画质",
33 | "spatialAudio": "空间音频",
34 | "vad": "语音活动检测",
35 | "videoCompositor": "本地合图",
36 | "vue": "使用Vue",
37 | "react": "使用React",
38 | "setting": "设置",
39 | "cloudProxy": "云代理",
40 | "certificate": "App 证书 (可选)",
41 | "language": "语言",
42 | "agora": "声网",
43 | "clickChange": "点击修改",
44 | "needHelp": "需要帮助请跳转至",
45 | "agoraDocs": "声网文档",
46 | "stt": "语音转文字",
47 | "autoPlayFailed": "自动播放失败,点击即可播放。",
48 | "appIdTips": "点击这里 了解App ID",
49 | "appCertificateTips": "点击这里 了解App 证书",
50 | "cloudProxyTips": "点击这里 了解云代理",
51 | "chooseCustom": "选择自定义,可手动填写项目的 App ID 和 App 证书(如有)。",
52 | "sampleCode": "示例代码",
53 | "and": "和"
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/example/framework/react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Use React -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/ExtensionDemo/src/audio-extension.ts:
--------------------------------------------------------------------------------
1 | import { AudioExtension, AudioProcessor, IAudioProcessorContext } from "agora-rte-extension";
2 |
3 | class SimpleAudioExtension extends AudioExtension {
4 | protected _createProcessor(): SimpleAudioProcessor {
5 | return new SimpleAudioProcessor();
6 | }
7 | }
8 |
9 | class SimpleAudioProcessor extends AudioProcessor {
10 | public name: string = "SimpleAudioProcessor";
11 | private gainNode?: GainNode;
12 | private oscillatorNode?: OscillatorNode;
13 |
14 | protected onPiped(context: IAudioProcessorContext): void {
15 | const audioContext = context.getAudioContext();
16 | const gainNode = audioContext.createGain();
17 | const oscillatorNode = audioContext.createOscillator();
18 | oscillatorNode.frequency.setValueAtTime(880, audioContext.currentTime);
19 | oscillatorNode.start();
20 | oscillatorNode.connect(gainNode);
21 |
22 | this.gainNode = gainNode;
23 | this.oscillatorNode = oscillatorNode;
24 | }
25 |
26 | protected onUnpiped() {
27 | this.gainNode = undefined;
28 | this.oscillatorNode?.stop();
29 | this.oscillatorNode = undefined;
30 | }
31 |
32 | protected onEnableChange(enabled: boolean): void | Promise {
33 | if (this.context && this.oscillatorNode && this.gainNode) {
34 | if (enabled) {
35 | this.oscillatorNode.connect(this.gainNode);
36 | } else {
37 | this.oscillatorNode.disconnect();
38 | }
39 | }
40 | }
41 |
42 | protected onNode(audioNode: AudioNode, context: IAudioProcessorContext): void | Promise {
43 | if (this.gainNode && this.oscillatorNode) {
44 | audioNode.connect(this.gainNode);
45 |
46 | this.output(this.gainNode, context);
47 | }
48 | }
49 | }
50 |
51 | export { SimpleAudioExtension };
52 |
--------------------------------------------------------------------------------
/src/i18n/en/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "settingMenu": "Setting",
3 | "quickMenu": "Quick Start",
4 | "basicMenu": "Basic Examples",
5 | "videoAndVoiceCalling": "Video and Voice Calling",
6 | "interactiveLiveStreamingStandard":"Interactive Live Streaming Standard",
7 | "interactiveLiveStreamingPremium":"Interactive Live Streaming Premium",
8 | "advancedMenu": "Advanced Examples",
9 | "othersMenu": "Other Examples",
10 | "extensionMenu": "Plugin Examples",
11 | "frameworkMenu": "Framework Examples",
12 | "initializeSettings": "Initialize Settings",
13 | "basicVoiceCall": "Basic Voice Calling",
14 | "basicVideoCall": "Basic Video Calling",
15 | "basicLive": "Basic Live Streaming",
16 | "shareTheScreen": "Screen Share",
17 | "testMediaDevices": "Test/Switch Media Devices",
18 | "adjustVideoProfile": "Adjust Video Profile",
19 | "displayCallStats": "Display In-call Statistics",
20 | "audioEffects": "Audio Effects",
21 | "joinMultipleChannel": "Join Multiple Channels",
22 | "customVideoSource": "Custom Video Source",
23 | "selfRendering": "Video Self-Rendering",
24 | "selfCapturing": "Video Self-Capturing",
25 | "screenshot": "Video Screenshot",
26 | "geoFencing": "Network Geofencing",
27 | "pushStreamToCDN": "Push Streams to CDN",
28 | "dualStream": "Enable Dual-stream Mode",
29 | "virtualBackground": "Virtual Background",
30 | "beauty": "Beauty",
31 | "aiDenoiser": "AI Denoiser",
32 | "superClarity": "Super Clarity",
33 | "spatialAudio": "Spatial Audio",
34 | "vad": "Voice Activity Detection",
35 | "videoCompositor": "Local Video Compositing",
36 | "vue": "Vue",
37 | "react": "React",
38 | "setting": "SetUp",
39 | "cloudProxy": "Cloud Proxy",
40 | "certificate": "APP Certificate(optional)",
41 | "language": "Language",
42 | "agora": "Agora",
43 | "clickChange": "Click to change",
44 | "needHelp": "If you need help, please visit the official website",
45 | "agoraDocs": "Agora Docs",
46 | "sampleCode": "Sample Code",
47 | "stt": "Speech To Text",
48 | "autoPlayFailed": " The autoplay has failed; please click to initiate playback.",
49 | "appIdTips": "If you don`t know what is your appid, checkout this ",
50 | "appCertificateTips": " If you don`t know what is your app certificate, checkout this ",
51 | "cloudProxyTips": "If you don`t know what is Cloud Proxy, checkout this ",
52 | "chooseCustom": "Choose Custom Option to manually enter the project's App ID and App Certificate (optional).",
53 | "and": "and"
54 | }
--------------------------------------------------------------------------------
/ExtensionDemo/src/video-extension.ts:
--------------------------------------------------------------------------------
1 | import { Extension, VideoProcessor, Ticker, IProcessorContext } from "agora-rte-extension";
2 |
3 | class SimpleVideoExtension extends Extension {
4 | protected _createProcessor(): SimpleVideoProcessor {
5 | return new SimpleVideoProcessor();
6 | }
7 | }
8 |
9 | class SimpleVideoProcessor extends VideoProcessor {
10 | public name: string = "SimpleVideoProcessor";
11 | private canvas: HTMLCanvasElement;
12 | private ctx: CanvasRenderingContext2D;
13 | private ticker: Ticker;
14 | private videoElement: HTMLVideoElement;
15 | private canvasTrack: MediaStreamTrack;
16 |
17 | public constructor() {
18 | super();
19 |
20 | this.canvas = document.createElement("canvas");
21 | this.canvas.width = 640;
22 | this.canvas.height = 480;
23 | this.ctx = this.canvas.getContext("2d")!;
24 | this.videoElement = document.createElement("video");
25 | this.videoElement.muted = true;
26 | const outputStream = this.canvas.captureStream(30);
27 | this.canvasTrack = outputStream.getVideoTracks()[0];
28 |
29 | this.ticker = new Ticker("RAF", 1000 / 30);
30 | this.ticker.add(this.process);
31 | }
32 |
33 | private process = () => {
34 | if (this.inputTrack) {
35 | this.ctx.drawImage(this.videoElement, 0, 0);
36 | this.ctx.fillStyle = "red";
37 | this.ctx.fillRect(0, 0, 100, 100);
38 | }
39 | };
40 |
41 | protected onEnableChange(enabled: boolean): void | Promise {
42 | if (!this.context) {
43 | return;
44 | }
45 |
46 | if (enabled) {
47 | this.ticker.start();
48 | this.output(this.canvasTrack, this.context);
49 | } else {
50 | this.ticker.stop();
51 | if (this.inputTrack) {
52 | this.output(this.inputTrack, this.context);
53 | }
54 | }
55 | }
56 |
57 | protected onTrack(track: MediaStreamTrack, ctx: IProcessorContext): void | Promise {
58 | this.videoElement.srcObject = new MediaStream([track]);
59 | this.videoElement.play();
60 | this.videoElement.onplaying = () => {
61 | this.canvas.width = this.videoElement.videoWidth;
62 | this.canvas.height = this.videoElement.videoHeight;
63 | };
64 |
65 | if (this.enabled) {
66 | this.ticker.start();
67 | this.output(this.canvasTrack, ctx);
68 | } else {
69 | this.ticker.stop();
70 | this.output(track, ctx);
71 | }
72 | }
73 |
74 | protected onUnpiped(): void {
75 | this.ticker.stop();
76 | }
77 | }
78 |
79 | export { SimpleVideoExtension };
80 |
--------------------------------------------------------------------------------
/README.cn.md:
--------------------------------------------------------------------------------
1 | # Agora RTC Web SDK 4.x 示例项目
2 |
3 | _[English](README.md) | 简体中文_
4 |
5 | ## 简介
6 |
7 | 此仓库包含基于 Agora RTC Web SDK 4.x 的示例项目。
8 |
9 |
10 |
11 | Web SDK 4.x 是基于 Web SDK 3.x 开发的全量重构版本,在继承了 Web SDK 3.x 功能的基础上,优化了 SDK 的内部架构,提高了 API 的易用性。
12 |
13 | Web SDK 4.x 具有以下优势:
14 |
15 | - 面向开发者提供更先进的 API 架构和范式。
16 | - 所有异步场景的 API 使用 Promise 替代 Callback,提升集成代码的质量和健壮性。
17 | - 优化频道事件通知机制,统一频道内事件的命名和回调参数的格式,降低断线重连的处理难度。
18 | - 提供清晰和完善的错误码,方便错误排查。
19 | - 支持 TypeScript。
20 |
21 | ## 示例项目(使用 jQuery 和 Bootstrap)
22 |
23 | | 功能 | 示例项目位置 |
24 | | -------------- | ------------------------------------------------------------ |
25 | | 基础示例 | [/src/example/basic](/src/example/basic) |
26 | | 进阶示例 | [/src/example/advanced](/src/example/advanced) |
27 | | 插件示例 | [/src/example/plugin](/src/example/plugin) |
28 | | 其他示例 | [/src/example/others](/src/example/others) |
29 | | vue 框架示例 | [/src/example/framework/vue](/src/example/framework/vue) |
30 | | react 框架示例 | [/src/example/framework/react](/src/example/framework/react) |
31 |
32 | ### 如何运行示例项目
33 |
34 | #### 前提条件
35 |
36 | - 你必须使用 SDK 支持的浏览器运行示例项目。 关于支持的浏览器列表参考 [浏览器兼容性和已知问题](https://doc.shengwang.cn/doc/rtc/javascript/overview/browser-compatibility)。
37 |
38 | #### 运行步骤
39 |
40 | 1. 在项目根路径运行下面的命令安装依赖项。
41 |
42 | ```shell
43 | npm install
44 | ```
45 |
46 | 2. 运行下面的命令启动示例项目。
47 |
48 | ```shell
49 | npm run dev
50 | ```
51 |
52 | 3. 在浏览器打开 [http://localhost:3001/index.html](http://localhost:3001/index.html)
53 |
54 | 4. 在示例项目设置页面上,输入 App ID 和 App Certificate,然后点击设置按钮。
55 | - 关于 App ID 和 App Certificate 的获取方法参考 [开始使用 Agora 平台](https://docs.agora.io/cn/Agora%20Platform/get_appid_token)。
56 |
57 | ## 参考
58 |
59 | - [Web SDK 4.x 产品概述](https://doc.shengwang.cn/doc/rtc/javascript/overview/product-overview)
60 | - [Web SDK 4.x API 参考](https://doc.shengwang.cn/api-ref/rtc/javascript/overview)
61 | - [基于本仓库部署的在线 demo](https://webdemo.agora.io/)
62 |
63 | ## 反馈
64 |
65 | 如果你有任何问题或建议,可以通过 issue 的形式反馈。
66 |
67 | ## 相关资源
68 |
69 | - 你可以先参阅 [常见问题](https://doc.shengwang.cn/faq/list?category=integration-issues&platform=javascript&product=rtc)
70 | - 如果你想了解更多官方示例,可以参考 [官方 SDK 示例](https://github.com/AgoraIO)
71 | - 如果你想了解声网 SDK 在复杂场景下的应用,可以参考 [官方场景案例](https://github.com/AgoraIO-usecase)
72 | - 如果你想了解声网的一些社区开发者维护的项目,可以查看 [社区](https://github.com/AgoraIO-Community)
73 | - 若遇到问题需要开发者帮助,你可以到 [开发者社区](https://rtcdeveloper.com/) 提问
74 | - 如果需要售后技术支持, 你可以在 [Agora Dashboard](https://console.shengwang.cn/overview) 提交工单
75 |
76 | ## 许可证
77 |
78 | 示例项目遵守 MIT 许可证。
79 |
--------------------------------------------------------------------------------
/src/example/extension/aiDenoiser/agora-extension-ai-denoiser/README.md:
--------------------------------------------------------------------------------
1 | ### Install AI denoiser extension with Agora web SDK.
2 |
3 | ```javascript
4 | import { AIDenoiserExtension } from "agora-extension-ai-denoiser";
5 | // Create AIDenoiserExtension instance, assetsPath is the path of wasm files.
6 | const extension = new AIDenoiserExtension({ assetsPath: "./external" });
7 |
8 | // Register AI denoiser extension into AgoraRTC.
9 | AgoraRTC.registerExtensions([extension]);
10 |
11 | // listen the loaderror callback to handle loading module failed.
12 | extension.onloaderror = (e) => {
13 | // if loading denoiser is failed, disable the function of denoiser. For example, set your button disbled.
14 | openDenoiserButton.enabled = false;
15 | console.log(e);
16 | };
17 | ```
18 |
19 | ### Create a processor by denioser extension.
20 |
21 | ```javascript
22 | const processor = extension.createProcessor();
23 |
24 | // If you want to enable the processor by default.
25 | await processor.enable();
26 |
27 | // If you want to disable the processor by default.
28 | // await processor.disable();
29 |
30 | // Optional, listen the processor`s overlaod callback to catch overload message
31 | processor.onoverload = async (elapsedTimeInMs) => {
32 | console.log("overload!!!", elapsedTimeInMs);
33 | // fallback or disable
34 | // await processor.setMode("STATIONARY_NS");
35 | await processor.disable();
36 | };
37 | ```
38 |
39 | ### Connect the processor to microphone audio track to process the audio pipeline.
40 |
41 | ```javascript
42 | const audioTrack = await AgoraRTC.createMicrophoneAudioTrack();
43 |
44 | audioTrack.pipe(processor).pipe(audioTrack.processorDestination);
45 |
46 | await processor.enable();
47 | ```
48 |
49 | ### Control the denoiser function enabled or disabled.
50 |
51 | ```javascript
52 | () => {
53 | if (processor.enabled) {
54 | await processor.disable();
55 | } else {
56 | await processor.enable();
57 | }
58 | }
59 | ```
60 |
61 | ### Change the denoiser mode and level.
62 |
63 | ```javascript
64 | await processor.setMode("NSNG"); // recommended
65 | await processor.setMode("STATIONARY_NS");
66 |
67 | await processor.setLevel("LEVEL26"); // recommended
68 | await processor.setLevel("LEVEL40");
69 | ```
70 |
71 | ### Dump audio (download files which are 30s audio file before the method called and two audio files 30s after the method called)
72 |
73 | ```javascript
74 | processor.ondump = (blob, name) => {
75 | const objectURL = URL.createObjectURL(blob);
76 | const tag = document.createElement("a");
77 | tag.download = name;
78 | tag.href = objectURL;
79 | tag.click();
80 | setTimeout(() => {
81 | URL.revokeObjectURL(objectURL);
82 | }, 0);
83 | };
84 |
85 | processor.ondumpend = () => {
86 | console.log("dump ended!!");
87 | };
88 |
89 | processor.dump();
90 | ```
91 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Agora WebSDK Demos
10 |
11 |
12 |
13 |
14 |
15 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/example/extension/virtualBackground/agora-extension-virtual-background/index_cn.d.ts:
--------------------------------------------------------------------------------
1 |
2 | import { EventEmitter } from 'events';
3 | import { Extension } from 'agora-rte-extension';
4 | import type { IBaseProcessor } from 'agora-rte-extension';
5 | import type { IExtension } from 'agora-rte-extension';
6 | import { IProcessorContext } from 'agora-rte-extension';
7 | import { VideoProcessor } from 'agora-rte-extension';
8 |
9 | export declare type IVirtualBackgroundExtension = IExtension;
10 |
11 | export declare interface IVirtualBackgroundProcessor extends IBaseProcessor {
12 | init(wasmDir: string): Promise;
13 | release(): Promise;
14 | setOptions(options: VirtualBackgroundEffectOptions): void;
15 | onoverload?: () => void;
16 | getProcessedTrack(): Promise;
17 | }
18 |
19 | /**
20 | * @public
21 | *
22 | * 插件实例的属性。用于 {@link setOptions} 方法。
23 | */
24 | export declare type VirtualBackgroundEffectOptions = {
25 | /**
26 | * 设置背景类型。
27 | *
28 | * "color": 纯色
29 | * "img": 图片
30 | * "blur": 虚化
31 | * "none": 无背景(透明)
32 | */
33 | type: string;
34 | /**
35 | * 设置背景颜色,值为有效的CSS颜色。
36 | *
37 | */
38 | color?: string;
39 | /**
40 | * 设置自定义背景图。
41 | *
42 | * 如果出现 "texture bound to texture unit 2 is not renderable. It might be non-power-of-2 or have incompatible texture filtering (maybe)?" 报错,请检查图片的分辨率相乘后是否是 2 的倍数。
43 | */
44 | source?: HTMLImageElement | HTMLVideoElement;
45 | /**
46 | * 设置背景模糊程度。
47 | *
48 | * 1: 轻度
49 | * 2: 中度
50 | * 3: 重度
51 | */
52 | blurDegree?: Number;
53 | /**
54 | * 背景填充方式。
55 | *
56 | * "contain": 保持原图比例显示完整背景,用黑色填充不足部分
57 | * "cover": 保持原图比例,覆盖显示区域,裁切超出内容
58 | * "fill": 拉伸背景以填充显示区域
59 | */
60 | fit?: 'contain' | 'cover' | 'fill';
61 | };
62 |
63 | declare class VirtualBackgroundExtension extends Extension implements IVirtualBackgroundExtension {
64 | constructor();
65 | checkCompatibility(): boolean;
66 | protected _createProcessor(): VirtualBackgroundProcessor;
67 | }
68 | export default VirtualBackgroundExtension;
69 |
70 | declare class VirtualBackgroundProcessor extends VideoProcessor implements IVirtualBackgroundProcessor {
71 | name: string;
72 | private segWorker;
73 | private processed_track;
74 | eventBus: EventEmitter;
75 | private analyzer;
76 | onoverload?: () => void;
77 | private initialized;
78 | private piped;
79 | private forceEnable;
80 | private avgCost;
81 | private stats;
82 | constructor();
83 | init(wasmDir: string): Promise;
84 | setOptions(options: VirtualBackgroundEffectOptions): void;
85 | getProcessedTrack(): Promise;
86 | protected onEnableChange(enabled: boolean): Promise;
87 | private getStats;
88 | protected onTrack(inputTrack: MediaStreamTrack, context: IProcessorContext): Promise;
89 | release(): Promise;
90 | protected onPiped(context: IProcessorContext): void;
91 | protected onUnpiped(): void;
92 | }
93 |
94 | export { }
95 |
--------------------------------------------------------------------------------
/src/example/extension/aiDenoiser/agora-extension-ai-denoiser/index.d.ts:
--------------------------------------------------------------------------------
1 | import { AudioExtension } from "agora-rte-extension";
2 | import { AudioProcessor } from "agora-rte-extension";
3 | import type { IAudioExtension } from "agora-rte-extension";
4 | import type { IAudioProcessor } from "agora-rte-extension";
5 | import type { IAudioProcessorContext } from "agora-rte-extension";
6 |
7 | /** @public */
8 | export declare class AIDenoiserExtension
9 | extends AudioExtension
10 | implements IAIDenoiserExtension
11 | {
12 | /**
13 | * @deprecated use processor.on('loaderror', ...) instead
14 | */
15 | onloaderror?: (error: Error) => void;
16 |
17 | constructor(options: AIDenoiserExtensionOptions);
18 | checkCompatibility(): boolean;
19 | protected _createProcessor(): AIDenoiserProcessor;
20 | }
21 |
22 | /** @public */
23 | export declare interface AIDenoiserExtensionOptions {
24 | /** @public*/
25 | assetsPath: string;
26 | }
27 |
28 | /** @public */
29 | export declare class AIDenoiserProcessor extends AudioProcessor implements IAIDenoiserProcessor {
30 | readonly name: string;
31 | /**
32 | * @deprecated use processor.on('dump', ...) instead
33 | */
34 | ondump?: (blob: Blob, name: string) => void;
35 | /**
36 | * @deprecated use processor.on('dumpend', ...) instead
37 | */
38 | ondumpend?: () => void;
39 | /**
40 | * @deprecated use processor.on('overload', ...) instead
41 | */
42 | onoverload?: (elapsedTime: number) => void;
43 |
44 | /** @public */
45 | dump(): void;
46 | /** @public */
47 | setMode(mode: AIDenoiserProcessorMode): Promise;
48 | /** @public */
49 | setLevel(level: AIDenoiserProcessorLevel): Promise;
50 | }
51 |
52 | /** @public */
53 | export declare enum AIDenoiserProcessorLevel {
54 | SOFT = "SOFT",
55 | AGGRESSIVE = "AGGRESSIVE",
56 | }
57 |
58 | /** @public */
59 | export declare enum AIDenoiserProcessorMode {
60 | STATIONARY_NS = "STATIONARY_NS",
61 | NSNG = "NSNG",
62 | }
63 |
64 | /** @public */
65 | export declare interface IAIDenoiserExtension extends IAudioExtension {
66 | createProcessor(): IAIDenoiserProcessor;
67 | onloaderror?: (error: Error) => void;
68 | }
69 |
70 | /** @public*/
71 | export declare interface IAIDenoiserProcessor extends IAudioProcessor {
72 | readonly name: string;
73 | readonly ID: string;
74 | get kind(): "video" | "audio";
75 | get enabled(): boolean;
76 | pipe(processor: IAudioProcessor): IAudioProcessor;
77 | unpipe(): void;
78 | disable(): void | Promise;
79 | enable(): void | Promise;
80 | setMode(mode: AIDenoiserProcessorMode): Promise;
81 | setLevel(level: AIDenoiserProcessorLevel): Promise;
82 | dump(): void;
83 | /**
84 | * @deprecated use processor.on('dump', ...) instead
85 | */
86 | ondump?: (blob: Blob, name: string) => void;
87 | /**
88 | * @deprecated use processor.on('dumpend', ...) instead
89 | */
90 | ondumpend?: () => void;
91 | /**
92 | * @deprecated use processor.on('overload', ...) instead
93 | */
94 | onoverload?: (elapsedTime: number) => void;
95 | on(event: "loaderror", listener: (error: Error) => void): void;
96 | on(event: "dump", listener: (blob: Blob, name: string) => void): void;
97 | on(event: "dumpend", listener: () => void): void;
98 | on(event: "overload", listener: (elapsedTime: number) => void): void;
99 | }
100 |
101 | export {};
102 |
--------------------------------------------------------------------------------
/src/example/extension/vad/agora-extension-vad/README.md:
--------------------------------------------------------------------------------
1 | # Web VAD
2 |
3 | ## Install Web VAD plugin with Agora web SDK.
4 |
5 | ```ts
6 | import AgoraRTC from "agora-rtc-sdk-ng";
7 | import { VADExtension } from "agora-extension-vad";
8 | // Create VADExtension instance, assetsPath is the path of wasm files.
9 | const extension = new VADExtension({ assetsPath: "./wasm" });
10 |
11 | // Check compatibility
12 | if (!extension.checkCompatibility()) {
13 | return console.warn("vad plugin is not support!");
14 | }
15 |
16 | // listen the loaderror callback to handle loading module failed.
17 | extension.onloaderror = (error) => {
18 | // if loading vad is failed, do something here.
19 | console.error(error);
20 | };
21 |
22 | // Register VAD plugin into AgoraRTC.
23 | AgoraRTC.registerExtensions([extension]);
24 | ```
25 |
26 | ## Create Web VAD processor node
27 |
28 | ```ts
29 | const processor = extension.createProcessor();
30 |
31 | // If you want to enable the processor by default.
32 | // await processor.enable();
33 |
34 | // If you want to disable the processor by default.
35 | // await processor.disable();
36 |
37 | // Optional, listen the processor`s overload event to catch overload message
38 | processor.on("overload", async () => {
39 | console.log("overload!!!");
40 | // disable or ignore
41 | // await processor.disable();
42 | });
43 | ```
44 |
45 | ## Pipe audio track to processor node
46 |
47 | ```ts
48 | const audioTrack = await AgoraRTC.createMicrophoneAudioTrack();
49 |
50 | audioTrack.pipe(processor).pipe(audioTrack.processorDestination);
51 |
52 | await processor.enable();
53 | ```
54 |
55 | ## Enable or Disable Web VAD
56 |
57 | ```ts
58 | async function enable() {
59 | if (processor) {
60 | await processor.enable();
61 | }
62 | }
63 |
64 | async function disable() {
65 | if (processor) {
66 | await processor.disable();
67 | }
68 | }
69 | ```
70 |
71 | ## Pipe or UnPipe Web VAD processor
72 |
73 | ```ts
74 | async function handlePipeError(error: Error) {
75 | console.error(error);
76 | await unPipe();
77 | }
78 |
79 | async function handleOverload() {
80 | console.warn("processor may overload");
81 | }
82 |
83 | function handleResult(result) {
84 | console.info("vad result: ", result);
85 | }
86 |
87 | async function pipe() {
88 | try {
89 | if (processor) {
90 | processor.on("pipeerror", handlePipeError);
91 | processor.on("overload", handleOverload);
92 | processor.on("result", handleResult);
93 |
94 | audioTrack && audioTrack.pipe(processor).pipe(audioTrack.processorDestination);
95 | }
96 | } catch (error) {
97 | console.error(error);
98 | }
99 | }
100 |
101 | async function unPipe() {
102 | try {
103 | if (processor) {
104 | processor.off("pipeerror", handlePipeError);
105 | processor.off("overload", handleOverload);
106 | processor.off("result", handleResult);
107 |
108 | processor.unpipe();
109 |
110 | audioTrack && audioTrack.unpipe();
111 | audioTrack && audioTrack.pipe(audioTrack.processorDestination);
112 | }
113 | } catch (error) {
114 | console.error(error);
115 | }
116 | }
117 | ```
118 |
119 | ## Handler VAD Result
120 |
121 | ```ts
122 | function handleResult(result) {
123 | console.info("voiceProb: ", result.voiceProb);
124 | }
125 | ```
126 |
127 | The `voiceProb` is a float number ranging from 0 to 1 and represents the current probability of someone is talking.
128 |
--------------------------------------------------------------------------------
/src/example/advanced/vadExtention/agora-extension-vad/README.md:
--------------------------------------------------------------------------------
1 | # Web VAD
2 |
3 | ## Install Web VAD plugin with Agora web SDK.
4 |
5 | ```ts
6 | import AgoraRTC from "agora-rtc-sdk-ng";
7 | import { VADExtension } from "agora-extension-vad";
8 | // Create VADExtension instance, assetsPath is the path of wasm files.
9 | const extension = new VADExtension({ assetsPath: "./wasm" });
10 |
11 | // Check compatibility
12 | if (!extension.checkCompatibility()) {
13 | return console.warn("vad plugin is not support!");
14 | }
15 |
16 | // listen the loaderror callback to handle loading module failed.
17 | extension.onloaderror = (error) => {
18 | // if loading vad is failed, do something here.
19 | console.error(error);
20 | };
21 |
22 | // Register VAD plugin into AgoraRTC.
23 | AgoraRTC.registerExtensions([extension]);
24 | ```
25 |
26 | ## Create Web VAD processor node
27 |
28 | ```ts
29 | const processor = extension.createProcessor();
30 |
31 | // If you want to enable the processor by default.
32 | // await processor.enable();
33 |
34 | // If you want to disable the processor by default.
35 | // await processor.disable();
36 |
37 | // Optional, listen the processor`s overload event to catch overload message
38 | processor.on("overload", async () => {
39 | console.log("overload!!!");
40 | // disable or ignore
41 | // await processor.disable();
42 | });
43 | ```
44 |
45 | ## Pipe audio track to processor node
46 |
47 | ```ts
48 | const audioTrack = await AgoraRTC.createMicrophoneAudioTrack();
49 |
50 | audioTrack.pipe(processor).pipe(audioTrack.processorDestination);
51 |
52 | await processor.enable();
53 | ```
54 |
55 | ## Enable or Disable Web VAD
56 |
57 | ```ts
58 | async function enable() {
59 | if (processor) {
60 | await processor.enable();
61 | }
62 | }
63 |
64 | async function disable() {
65 | if (processor) {
66 | await processor.disable();
67 | }
68 | }
69 | ```
70 |
71 | ## Pipe or UnPipe Web VAD processor
72 |
73 | ```ts
74 | async function handlePipeError(error: Error) {
75 | console.error(error);
76 | await unPipe();
77 | }
78 |
79 | async function handleOverload() {
80 | console.warn("processor may overload");
81 | }
82 |
83 | function handleResult(result) {
84 | console.info("vad result: ", result);
85 | }
86 |
87 | async function pipe() {
88 | try {
89 | if (processor) {
90 | processor.on("pipeerror", handlePipeError);
91 | processor.on("overload", handleOverload);
92 | processor.on("result", handleResult);
93 |
94 | audioTrack && audioTrack.pipe(processor).pipe(audioTrack.processorDestination);
95 | }
96 | } catch (error) {
97 | console.error(error);
98 | }
99 | }
100 |
101 | async function unPipe() {
102 | try {
103 | if (processor) {
104 | processor.off("pipeerror", handlePipeError);
105 | processor.off("overload", handleOverload);
106 | processor.off("result", handleResult);
107 |
108 | processor.unpipe();
109 |
110 | audioTrack && audioTrack.unpipe();
111 | audioTrack && audioTrack.pipe(audioTrack.processorDestination);
112 | }
113 | } catch (error) {
114 | console.error(error);
115 | }
116 | }
117 | ```
118 |
119 | ## Handler VAD Result
120 |
121 | ```ts
122 | function handleResult(result) {
123 | console.info("voiceProb: ", result.voiceProb);
124 | }
125 | ```
126 |
127 | The `voiceProb` is a float number ranging from 0 to 1 and represents the current probability of someone is talking.
128 |
--------------------------------------------------------------------------------
/scripts/pure.js:
--------------------------------------------------------------------------------
1 | // TIP: This script is used to purge all SSO-related code from the project.
2 | // This file does not require attention.
3 |
4 | const { readdirSync, existsSync, readFileSync, writeFileSync, statSync } = require("fs");
5 | const path = require("path");
6 | const { exec } = require("child_process");
7 |
8 | const TARGET_DIR = path.resolve(__dirname, "../src");
9 | const REG_SSO_SCRIPT = /^\s+\ from html file
81 | const removeSSOScriptTag = (htmlPath) => {
82 | console.log(htmlPath);
83 | let htmlContent = readFileSync(htmlPath, "utf-8");
84 | htmlContent = htmlContent.replace(REG_SSO_SCRIPT, "");
85 | writeFileSync(htmlPath, htmlContent);
86 | };
87 |
88 | const removeSSOCode = (jsPath) => {
89 | console.log(jsPath);
90 | let jsContent = readFileSync(jsPath, "utf-8");
91 | jsContent = jsContent.replace(REG_SSO_CODE, "");
92 | writeFileSync(jsPath, jsContent);
93 | };
94 |
95 | function run() {
96 | console.log("----- pure run start -----");
97 | delSSORelatedFile();
98 | delSSORelatedHTMLCode();
99 | delSSORelatedJSCode();
100 | console.log("----- pure run end -----");
101 | }
102 |
103 | run();
104 |
--------------------------------------------------------------------------------
/src/example/advanced/adjustVideoProfile/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Adjust Video Profile -- Agora
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
19 |
20 |
21 |
22 |
44 |
45 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/src/example/extension/virtualBackground/agora-extension-virtual-background/index_en.d.ts:
--------------------------------------------------------------------------------
1 |
2 | import { EventEmitter } from 'events';
3 | import { Extension } from 'agora-rte-extension';
4 | import type { IBaseProcessor } from 'agora-rte-extension';
5 | import type { IExtension } from 'agora-rte-extension';
6 | import { IProcessorContext } from 'agora-rte-extension';
7 | import { VideoProcessor } from 'agora-rte-extension';
8 |
9 | export declare type IVirtualBackgroundExtension = IExtension;
10 |
11 | export declare interface IVirtualBackgroundProcessor extends IBaseProcessor {
12 | init(wasmDir: string): Promise;
13 | release(): Promise;
14 | setOptions(options: VirtualBackgroundEffectOptions): void;
15 | onoverload?: () => void;
16 | getProcessedTrack(): Promise;
17 | }
18 |
19 | /**
20 | * Options of the virtual background plugin. Used when calling {@link setOptions}.
21 | */
22 | export declare type VirtualBackgroundEffectOptions = {
23 | /**
24 | * Sets the background type.
25 | *
26 | * "color": pure color
27 | * "img": image
28 | * "blur": blurring
29 | */
30 | type: string;
31 | /**
32 | * Sets the background color, the value should be a CSS supported color.
33 | */
34 | color?: string;
35 | /**
36 | * Sets the background image.
37 | *
38 | * If the error "texture bound to texture unit 2 is not renderable. It might be non-power-of-2 or have incompatible texture filtering (maybe)?" appears, please check whether the product of the picture's width and height is a multiple of 2.
39 | */
40 | source?: HTMLImageElement | HTMLVideoElement;
41 | /**
42 | * Sets background blurring degree.
43 | *
44 | * 1: low
45 | * 2: medium
46 | * 3: high
47 | */
48 | blurDegree?: Number;
49 | /**
50 | * How an element responds to the height and width of its content box.
51 | *
52 | * "contain": increases or decreases the size of the background to fill the video whilst preserving its aspect-ratio
53 | * "cover": the background will fill the height and width of its box, once again maintaining its aspect ratio but often cropping the video.
54 | * "fill": stretches the background to fit the video.
55 | */
56 | fit?: 'contain' | 'cover' | 'fill';
57 | };
58 |
59 | declare class VirtualBackgroundExtension extends Extension implements IVirtualBackgroundExtension {
60 | constructor();
61 | checkCompatibility(): boolean;
62 | protected _createProcessor(): VirtualBackgroundProcessor;
63 | }
64 | export default VirtualBackgroundExtension;
65 |
66 | declare class VirtualBackgroundProcessor extends VideoProcessor implements IVirtualBackgroundProcessor {
67 | name: string;
68 | private segWorker;
69 | private processed_track;
70 | eventBus: EventEmitter;
71 | private analyzer;
72 | onoverload?: () => void;
73 | private initialized;
74 | private piped;
75 | private forceEnable;
76 | private avgCost;
77 | private stats;
78 | constructor();
79 | init(wasmDir: string): Promise;
80 | setOptions(options: VirtualBackgroundEffectOptions): void;
81 | getProcessedTrack(): Promise;
82 | protected onEnableChange(enabled: boolean): Promise;
83 | private getStats;
84 | protected onTrack(inputTrack: MediaStreamTrack, context: IProcessorContext): Promise;
85 | release(): Promise;
86 | protected onPiped(context: IProcessorContext): void;
87 | protected onUnpiped(): void;
88 | }
89 |
90 | export { }
91 |
--------------------------------------------------------------------------------
/src/example/extension/videoCompositor/agora-extension-video-compositor/index_cn.d.ts:
--------------------------------------------------------------------------------
1 | import { Extension } from 'agora-rte-extension';
2 | import { IBaseProcessor } from 'agora-rte-extension';
3 | import { IExtension } from 'agora-rte-extension';
4 | import { IProcessorContext } from 'agora-rte-extension';
5 | import { VideoProcessor } from 'agora-rte-extension';
6 |
7 | export declare type IVideoCompositingExtension = IExtension;
8 |
9 | export declare interface IVideoTrackCompositor extends IBaseProcessor {
10 | /**
11 | * 创建输入端点。
12 | *
13 | * "option": 图层显示选项
14 | *
15 | * @return IBaseProcessor 接口实例
16 | */
17 | createInputEndpoint(option: LayerOption): IBaseProcessor;
18 | /**
19 | * 增加图像层。
20 | *
21 | * "url": 图像URL
22 | * "option": 图层显示选项
23 | *
24 | * @return 一个 HTMLImageElement 对象
25 | */
26 | addImage(url: string, option: LayerOption): HTMLImageElement;
27 | /**
28 | * 删除图像层。
29 | *
30 | * "imgElement": 一个 HTMLImageElement 对象
31 | */
32 | removeImage(imgElement: HTMLImageElement): void;
33 | /**
34 | * 设置输出选项。
35 | *
36 | * "width": 输出视频的宽度
37 | * "height": 输出视频的高度
38 | * "fps": 输出视频的帧率
39 | */
40 | setOutputOptions(width: number, height: number, fps?: number): void;
41 | /**
42 | * 启动合成。
43 | */
44 | start(): Promise;
45 | /**
46 | * 停止合成。
47 | */
48 | stop(): Promise;
49 | }
50 |
51 | export declare type LayerOption = {
52 | /**
53 | * 视频显示容器的水平坐标。
54 | */
55 | x: number;
56 | /**
57 | * 视频显示容器的垂直坐标。
58 | */
59 | y: number;
60 | /**
61 | * y location of content box.
62 | */
63 | width: number;
64 | /**
65 | * 视频显示高度。
66 | */
67 | height: number;
68 | /**
69 | * 视频填充方式。
70 | *
71 | * "contain": 保持画面比例显示完整内容
72 | * "cover": 保持画面比例,填充显示容器,必要时裁切
73 | * "fill": 拉伸画面以填充容器所有区域,画面比列会更改为与容器相同
74 | */
75 | fit?: 'contain' | 'cover' | 'fill';
76 | };
77 |
78 | declare class VideoCompositingExtension extends Extension implements IVideoCompositingExtension {
79 | constructor();
80 | protected _createProcessor(): VideoTrackCompositor;
81 | }
82 | export default VideoCompositingExtension;
83 |
84 | declare class VideoTrackCompositor extends VideoProcessor implements IVideoTrackCompositor {
85 | name: 'VideoTrackCompositor';
86 | private _canvas2d;
87 | private _ctx2d;
88 | private _backgroundImage;
89 | private _intervalId;
90 | private _layers;
91 | private _canvasTrack;
92 | private _options;
93 | private _medias;
94 | private _width;
95 | private _height;
96 | private _fps;
97 | constructor();
98 | private draw;
99 | addLayer(track: MediaStreamTrack, option: LayerOption): void;
100 | removeLayer(track: MediaStreamTrack): void;
101 | setOutputOptions(width: number, height: number, fps?: number): void;
102 | addImage(url: string, option: LayerOption): HTMLImageElement;
103 | removeImage(imgElement: HTMLImageElement): void;
104 | setBackground(backgroundImage: HTMLImageElement): void;
105 | start(): Promise;
106 | stop(): Promise;
107 | createInputEndpoint(option: LayerOption): IBaseProcessor;
108 | protected onTrack(track: MediaStreamTrack, context: IProcessorContext): Promise;
109 | protected onUnpiped?(context?: IProcessorContext): void;
110 | private getStats;
111 | }
112 |
113 | export { }
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sample projects for Agora RTC Web SDK 4.x
2 |
3 | _English | [简体中文](README.cn.md)_
4 |
5 | ## Overview
6 |
7 | This repository contains sample projects using the Agora RTC Web SDK 4.x.
8 |
9 |
10 |
11 | The Web SDK 4.x refactors the Web SDK 3.x. Based on the features of 3.x, 4.x fully optimizes the internal architecture of the SDK and provides more flexible and easy-to-use APIs.
12 |
13 | Compared to the Web SDK 3.x, the Web SDK 4.x has the following advantages:
14 |
15 | - Uses promises for asynchronous operations, which improves the robustness and readability of your code.
16 | - Supports TypeScript.
17 | - Replaces the Stream object with Track objects for separate and flexible control over audio and video.
18 | - Improves the channel event notification mechanism, making it easier for you to deal with reconnection.
19 | - Provides more accurate and comprehensive error codes for troubleshooting.
20 |
21 | ## Projects using jQuery and Bootstrap
22 |
23 | | Feature | Sample project location |
24 | | ----------------------- | ------------------------------------------------------------ |
25 | | Basic Examples | [/src/example/basic](/src/example/basic) |
26 | | Advanced Examples | [/src/example/advanced](/src/example/advanced) |
27 | | Plugin Examples | [/src/example/plugin](/src/example/plugin) |
28 | | Other Examples | [/src/example/others](/src/example/others) |
29 | | Vue Framework Example | [/src/example/framework/vue](/src/example/framework/vue) |
30 | | React Framework Example | [/src/example/framework/react](/src/example/framework/react) |
31 |
32 | ### How to run the sample projects
33 |
34 | #### Prerequisites
35 |
36 | You need a supported browser to run the sample projects. See [Product Overview](https://docs.agora.io/en/Interactive%20Broadcast/product_live?platform=Web#compatibility) for a list of supported browsers.
37 |
38 | #### Steps to run
39 |
40 | 1. In the project root path run the following command to install dependencies.
41 |
42 | ```shell
43 | npm install
44 | ```
45 |
46 | 2. Use the following command to run the sample project.
47 |
48 | ```shell
49 | npm run dev
50 | ```
51 |
52 | 3. Open link [http://localhost:3001/index.html](http://localhost:3001/index.html) in browser.
53 |
54 | 4. In the demo setting page, enter your App ID and App Certificate, then click SetUp button.
55 | - See [Get Started with Agora](https://docs.agora.io/en/Agora%20Platform/get_appid_token) to learn how to get an App ID and App Certificate.
56 |
57 | ## Reference
58 |
59 | - [Web SDK 4.x Product Overview](https://docs.agora.io/en/Interactive%20Broadcast/product_live?platform=Web)
60 | - [Web SDK 4.x API Reference](https://docs.agora.io/en/Interactive%20Broadcast/API%20Reference/web_ng/index.html)
61 | - [Online demo deployed from this repo](https://webdemo.agora.io/)
62 |
63 | ## Feedback
64 |
65 | If you have any problems or suggestions regarding the sample projects, feel free to file an issue.
66 |
67 | ## Related resources
68 |
69 | - Check our [FAQ](https://docs.agora.io/en/faq) to see if your issue has been recorded.
70 | - Dive into [Agora SDK Samples](https://github.com/AgoraIO) to see more tutorials
71 | - Take a look at [Agora Use Case](https://github.com/AgoraIO-usecase) for more complicated real use case
72 | - Repositories managed by developer communities can be found at [Agora Community](https://github.com/AgoraIO-Community)
73 | - If you encounter problems during integration, feel free to ask questions in [Stack Overflow](https://stackoverflow.com/questions/tagged/agora.io)
74 |
75 | ## License
76 |
77 | The sample projects are under the MIT license.
78 |
--------------------------------------------------------------------------------
/src/example/extension/videoCompositor/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Video Compositor -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/src/example/advanced/geoFencing/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Geo Fencing -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/src/example/others/dualStream/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Dual Stream -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/src/example/advanced/selfCapturing/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Video Self-Capturing -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/src/example/advanced/displayCallStats/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Display Call Stats -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
39 |
40 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/example/advanced/shareTheScreen/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Agora WebSDK Demos
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/src/example/advanced/screenshot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Video Screenshot -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/i18n/language.js:
--------------------------------------------------------------------------------
1 | let LANGUAGE_CACHE_KEY = "__language_data__";
2 | let LANGUAGE_CACHE_DATA = JSON.parse(sessionStorage.getItem(LANGUAGE_CACHE_KEY)) || {};
3 |
4 | function __insertIe8nText(data) {
5 | var insertEle = $(".i18n");
6 | insertEle.each(function () {
7 | let dataName = $(this).attr("name");
8 | let i18nTxt = data[dataName];
9 | $(this).html(i18nTxt);
10 | });
11 | }
12 |
13 | function __getLanguageData(language, success, fail) {
14 | let url = "";
15 | if (LANGUAGE_CACHE_DATA[language] && LANGUAGE_CACHE_DATA[language].sampleCode) {
16 | success(LANGUAGE_CACHE_DATA[language]);
17 | return;
18 | }
19 |
20 | switch (language) {
21 | case "zh":
22 | url = `${ORIGIN_URL}/i18n/zh-CN/index.json`;
23 | break;
24 | case "zh-CN":
25 | url = `${ORIGIN_URL}/i18n/zh-CN/index.json`;
26 | break;
27 | case "en":
28 | url = `${ORIGIN_URL}/i18n/en/index.json`;
29 | break;
30 | default:
31 | url = `${ORIGIN_URL}/i18n/en/index.json`;
32 | }
33 |
34 | $.ajax({
35 | url: url,
36 | async: true,
37 | cache: false,
38 | dataType: "json",
39 | success: success,
40 | error: fail,
41 | });
42 | }
43 |
44 | function __execI18n() {
45 | const language = getLanguage();
46 |
47 | const success = function (data, status) {
48 | LANGUAGE_CACHE_DATA[language] = data;
49 | sessionStorage.setItem(LANGUAGE_CACHE_KEY, JSON.stringify(LANGUAGE_CACHE_DATA));
50 | __insertIe8nText(data);
51 | };
52 |
53 | const fail = function (jqXHR, textStatus, errorThrown) {
54 | let msg = "get language data failed!";
55 | let href = window.location.href;
56 | if (/^file/.test(href)) {
57 | msg += "can not get language data from local file, please use http server!";
58 | }
59 | console.error(msg, jqXHR, textStatus);
60 | };
61 |
62 | __getLanguageData(language, success, fail);
63 | }
64 |
65 | function __insertLanguageSwitch() {
66 | const languageSwitchHtml = `
67 |
68 |
69 |
70 |
71 | English
72 | 简体中文
73 |
74 |
`;
75 |
76 | $("#language-switch-wrapper").html(languageSwitchHtml);
77 |
78 | const language = getLanguage();
79 |
80 | if (language == "en") {
81 | $("#language-english").addClass("active");
82 | $("#language-text").text("English");
83 | } else {
84 | $("#language-zh").addClass("active");
85 | $("#language-text").text("简体中文");
86 | }
87 |
88 | function switchLanguage(v) {
89 | if (language == v) {
90 | return;
91 | }
92 | let options = getOptionsFromLocal();
93 | if (v == "zh-CN" || v == "zh") {
94 | $("#language-zh").addClass("active");
95 | $("#language-english").removeClass("active");
96 | $("#language-text").text("简体中文");
97 | options.language = "zh-CN";
98 | setOptionsToLocal(options);
99 | } else {
100 | $("#language-english").addClass("active");
101 | $("#language-zh").removeClass("active");
102 | $("#language-text").text("English");
103 | options.language = "en";
104 | setOptionsToLocal(options);
105 | }
106 | window.location.reload();
107 | }
108 |
109 | $("#language-english").click(function (e) {
110 | switchLanguage("en");
111 | });
112 |
113 | $("#language-zh").click(function (e) {
114 | switchLanguage("zh-CN");
115 | });
116 | }
117 |
118 | window.addEventListener("pageshow", function (e) {
119 | __insertLanguageSwitch();
120 | __execI18n();
121 | });
122 |
--------------------------------------------------------------------------------
/src/example/extension/videoCompositor/agora-extension-video-compositor/index_en.d.ts:
--------------------------------------------------------------------------------
1 | import { Extension } from 'agora-rte-extension';
2 | import { IBaseProcessor } from 'agora-rte-extension';
3 | import { IExtension } from 'agora-rte-extension';
4 | import { IProcessorContext } from 'agora-rte-extension';
5 | import { VideoProcessor } from 'agora-rte-extension';
6 |
7 | export declare type IVideoCompositingExtension = IExtension;
8 |
9 | export declare interface IVideoTrackCompositor extends IBaseProcessor {
10 | /**
11 | * Create input endpoint.
12 | *
13 | * "option": Display options of this layer
14 | *
15 | * @return An instance of IBaseProcessor interface
16 | */
17 | createInputEndpoint(option: LayerOption): IBaseProcessor;
18 | /**
19 | * Create an image layer.
20 | *
21 | * "url": Image source
22 | * "option": Display options of this layer
23 | *
24 | * @return A HTMLImageElement Object
25 | */
26 | addImage(url: string, option: LayerOption): HTMLImageElement;
27 | /**
28 | * Remove an image layer.
29 | *
30 | * "imgElement": A HTMLImageElement Object
31 | */
32 | removeImage(imgElement: HTMLImageElement): void;
33 | /**
34 | * Sets the background media.
35 | *
36 | * "width": Width of output video
37 | * "height": Height of output video
38 | * "fps": Framerate of output video
39 | */
40 | setOutputOptions(width: number, height: number, fps?: number): void;
41 | /**
42 | * Start compositing.
43 | */
44 | start(): Promise;
45 | /**
46 | * Stop compositing.
47 | */
48 | stop(): Promise;
49 | }
50 |
51 | export declare type LayerOption = {
52 | /**
53 | * x location of content box.
54 | */
55 | x: number;
56 | y: number;
57 | /**
58 | * Width of content box.
59 | */
60 | width: number;
61 | /**
62 | * Height of content box.
63 | */
64 | height: number;
65 | /**
66 | * How an element responds to the height and width of its content box.
67 | *
68 | * "contain": increases or decreases the size of the video to fill the box whilst preserving its aspect-ratio
69 | * "cover": the video will fill the height and width of its box, once again maintaining its aspect ratio but often cropping the video.
70 | * "fill": stretches the video to fit the content box, regardless of its aspect-ratio.
71 | */
72 | fit?: 'contain' | 'cover' | 'fill';
73 | };
74 |
75 | declare class VideoCompositingExtension extends Extension implements IVideoCompositingExtension {
76 | constructor();
77 | protected _createProcessor(): VideoTrackCompositor;
78 | }
79 | export default VideoCompositingExtension;
80 |
81 | declare class VideoTrackCompositor extends VideoProcessor implements IVideoTrackCompositor {
82 | name: 'VideoTrackCompositor';
83 | private _canvas2d;
84 | private _ctx2d;
85 | private _backgroundImage;
86 | private _intervalId;
87 | private _layers;
88 | private _canvasTrack;
89 | private _options;
90 | private _medias;
91 | private _width;
92 | private _height;
93 | private _fps;
94 | constructor();
95 | private draw;
96 | addLayer(track: MediaStreamTrack, option: LayerOption): void;
97 | removeLayer(track: MediaStreamTrack): void;
98 | setOutputOptions(width: number, height: number, fps?: number): void;
99 | addImage(url: string, option: LayerOption): HTMLImageElement;
100 | removeImage(imgElement: HTMLImageElement): void;
101 | setBackground(backgroundImage: HTMLImageElement): void;
102 | start(): Promise;
103 | stop(): Promise;
104 | createInputEndpoint(option: LayerOption): IBaseProcessor;
105 | protected onTrack(track: MediaStreamTrack, context: IProcessorContext): Promise;
106 | protected onUnpiped?(context?: IProcessorContext): void;
107 | private getStats;
108 | }
109 |
110 | export { }
111 |
--------------------------------------------------------------------------------
/src/example/extension/vad/agora-extension-vad/index.d.ts:
--------------------------------------------------------------------------------
1 | import { AudioExtension } from "agora-rte-extension";
2 | import { AudioProcessor } from "agora-rte-extension";
3 | import type { IAudioExtension } from "agora-rte-extension";
4 | import type { IAudioProcessor } from "agora-rte-extension";
5 |
6 | /** @public */
7 | export declare interface IVADExtension extends IAudioExtension {
8 | /**
9 | * @deprecated will be removed in recent releases, please use processor.on("pipeerror", ...) instead
10 | */
11 | onloaderror?: () => void | Promise;
12 | checkCompatibility(): boolean;
13 | }
14 |
15 | /** @public */
16 | export declare interface IVADProcessor extends IAudioProcessor {
17 | dump(): Promise;
18 | destroy(): Promise;
19 | once(event: "pipeerror", listener: VADProcessorPipeErrorCallback): void;
20 | on(event: "pipeerror", listener: VADProcessorPipeErrorCallback): void;
21 | off(event: "pipeerror", listener: VADProcessorPipeErrorCallback): void;
22 | getListeners(event: "pipeerror"): Array;
23 | removeAllListeners(event: "pipeerror"): void;
24 | once(event: "overload", listener: VADProcessorOverloadCallback): void;
25 | on(event: "overload", listener: VADProcessorOverloadCallback): void;
26 | off(event: "overload", listener: VADProcessorOverloadCallback): void;
27 | getListeners(event: "overload"): Array;
28 | removeAllListeners(event: "overload"): void;
29 | once(event: "dump", listener: VADProcessorDumpCallback): void;
30 | on(event: "dump", listener: VADProcessorDumpCallback): void;
31 | off(event: "dump", listener: VADProcessorDumpCallback): void;
32 | getListeners(event: "dump"): Array;
33 | removeAllListeners(event: "dump"): void;
34 | once(event: "dumpend", listener: VADProcessorDumpEndCallback): void;
35 | on(event: "dumpend", listener: VADProcessorDumpEndCallback): void;
36 | off(event: "dumpend", listener: VADProcessorDumpEndCallback): void;
37 | getListeners(event: "dumpend"): Array;
38 | removeAllListeners(event: "dumpend"): void;
39 | once(event: "result", listener: VADProcessorResultCallback): void;
40 | on(event: "result", listener: VADProcessorResultCallback): void;
41 | off(event: "result", listener: VADProcessorResultCallback): void;
42 | getListeners(event: "result"): Array;
43 | removeAllListeners(event: "result"): void;
44 | }
45 |
46 | /** @public */
47 | export declare class VADExtension extends AudioExtension implements IVADExtension {
48 | static setLogLevel(level: number): void;
49 | /**
50 | * @deprecated will be removed in recent releases, please use processor.on("pipeerror", ...) instead
51 | */
52 | onloaderror?: () => void | Promise;
53 | constructor(options: VADExtensionOptions);
54 | checkCompatibility(): boolean;
55 | }
56 |
57 | /** @public */
58 | export declare type VADExtensionOptions = {
59 | assetsPath: string;
60 | fetchOptions?: RequestInit;
61 | };
62 |
63 | /** @public */
64 | export declare class VADProcessor extends AudioProcessor implements IVADProcessor {
65 | readonly name: string;
66 | dump(): Promise;
67 | destroy(): Promise;
68 | }
69 |
70 | /** @public */
71 | export declare type VADProcessorDumpCallback = (blob: Blob, name: string) => void | Promise;
72 |
73 | /** @public */
74 | export declare type VADProcessorDumpEndCallback = () => void | Promise;
75 |
76 | /** @public */
77 | export declare type VADProcessorLatency = "LOW" | "FULL";
78 |
79 | /** @public */
80 | export declare type VADProcessorLevel = "SOFT" | "AGGRESSIVE";
81 |
82 | /** @public */
83 | export declare type VADProcessorMode = "NSNG" | "STATIONARY_NS";
84 |
85 | /** @public */
86 | export declare type VADProcessorOverloadCallback = () => void | Promise;
87 |
88 | /** @public */
89 | export declare type VADProcessorPipeErrorCallback = (error: Error) => void | Promise;
90 |
91 | /** @public */
92 | export declare type VADProcessorResultCallback = (
93 | result: VADResultMessageData
94 | ) => void | Promise;
95 |
96 | /** @public */
97 | export declare type VADResultMessageData = {
98 | voiceProb: number;
99 | musicProb: number;
100 | pitchFreq: number;
101 | };
102 |
103 | export {};
104 |
--------------------------------------------------------------------------------
/src/example/advanced/vadExtention/agora-extension-vad/index.d.ts:
--------------------------------------------------------------------------------
1 | import { AudioExtension } from "agora-rte-extension";
2 | import { AudioProcessor } from "agora-rte-extension";
3 | import type { IAudioExtension } from "agora-rte-extension";
4 | import type { IAudioProcessor } from "agora-rte-extension";
5 |
6 | /** @public */
7 | export declare interface IVADExtension extends IAudioExtension {
8 | /**
9 | * @deprecated will be removed in recent releases, please use processor.on("pipeerror", ...) instead
10 | */
11 | onloaderror?: () => void | Promise;
12 | checkCompatibility(): boolean;
13 | }
14 |
15 | /** @public */
16 | export declare interface IVADProcessor extends IAudioProcessor {
17 | dump(): Promise;
18 | destroy(): Promise;
19 | once(event: "pipeerror", listener: VADProcessorPipeErrorCallback): void;
20 | on(event: "pipeerror", listener: VADProcessorPipeErrorCallback): void;
21 | off(event: "pipeerror", listener: VADProcessorPipeErrorCallback): void;
22 | getListeners(event: "pipeerror"): Array;
23 | removeAllListeners(event: "pipeerror"): void;
24 | once(event: "overload", listener: VADProcessorOverloadCallback): void;
25 | on(event: "overload", listener: VADProcessorOverloadCallback): void;
26 | off(event: "overload", listener: VADProcessorOverloadCallback): void;
27 | getListeners(event: "overload"): Array;
28 | removeAllListeners(event: "overload"): void;
29 | once(event: "dump", listener: VADProcessorDumpCallback): void;
30 | on(event: "dump", listener: VADProcessorDumpCallback): void;
31 | off(event: "dump", listener: VADProcessorDumpCallback): void;
32 | getListeners(event: "dump"): Array;
33 | removeAllListeners(event: "dump"): void;
34 | once(event: "dumpend", listener: VADProcessorDumpEndCallback): void;
35 | on(event: "dumpend", listener: VADProcessorDumpEndCallback): void;
36 | off(event: "dumpend", listener: VADProcessorDumpEndCallback): void;
37 | getListeners(event: "dumpend"): Array;
38 | removeAllListeners(event: "dumpend"): void;
39 | once(event: "result", listener: VADProcessorResultCallback): void;
40 | on(event: "result", listener: VADProcessorResultCallback): void;
41 | off(event: "result", listener: VADProcessorResultCallback): void;
42 | getListeners(event: "result"): Array;
43 | removeAllListeners(event: "result"): void;
44 | }
45 |
46 | /** @public */
47 | export declare class VADExtension extends AudioExtension implements IVADExtension {
48 | static setLogLevel(level: number): void;
49 | /**
50 | * @deprecated will be removed in recent releases, please use processor.on("pipeerror", ...) instead
51 | */
52 | onloaderror?: () => void | Promise;
53 | constructor(options: VADExtensionOptions);
54 | checkCompatibility(): boolean;
55 | }
56 |
57 | /** @public */
58 | export declare type VADExtensionOptions = {
59 | assetsPath: string;
60 | fetchOptions?: RequestInit;
61 | };
62 |
63 | /** @public */
64 | export declare class VADProcessor extends AudioProcessor implements IVADProcessor {
65 | readonly name: string;
66 | dump(): Promise;
67 | destroy(): Promise;
68 | }
69 |
70 | /** @public */
71 | export declare type VADProcessorDumpCallback = (blob: Blob, name: string) => void | Promise;
72 |
73 | /** @public */
74 | export declare type VADProcessorDumpEndCallback = () => void | Promise;
75 |
76 | /** @public */
77 | export declare type VADProcessorLatency = "LOW" | "FULL";
78 |
79 | /** @public */
80 | export declare type VADProcessorLevel = "SOFT" | "AGGRESSIVE";
81 |
82 | /** @public */
83 | export declare type VADProcessorMode = "NSNG" | "STATIONARY_NS";
84 |
85 | /** @public */
86 | export declare type VADProcessorOverloadCallback = () => void | Promise;
87 |
88 | /** @public */
89 | export declare type VADProcessorPipeErrorCallback = (error: Error) => void | Promise;
90 |
91 | /** @public */
92 | export declare type VADProcessorResultCallback = (
93 | result: VADResultMessageData
94 | ) => void | Promise;
95 |
96 | /** @public */
97 | export declare type VADResultMessageData = {
98 | voiceProb: number;
99 | musicProb: number;
100 | pitchFreq: number;
101 | };
102 |
103 | export {};
104 |
--------------------------------------------------------------------------------
/src/example/extension/vad/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | VAD -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/src/example/advanced/testMediaDevices/jquery.jsonview.min.js:
--------------------------------------------------------------------------------
1 | !function(e){var t,n,r,l,o;return o=["object","array","number","string","boolean","null"],r=function(){function t(e){null==e&&(e={}),this.options=e}return t.prototype.htmlEncode=function(e){return null!==e?e.toString().replace(/&/g,"&").replace(/"/g,""").replace(//g,">"):""},t.prototype.jsString=function(e){return e=JSON.stringify(e).slice(1,-1),this.htmlEncode(e)},t.prototype.decorateWithSpan=function(e,t){return''+this.htmlEncode(e)+" "},t.prototype.valueToHTML=function(t,n){var r;if(null==n&&(n=0),r=Object.prototype.toString.call(t).match(/\s(.+)]/)[1].toLowerCase(),this.options.strict&&!e.inArray(r,o))throw new Error(""+r+" is not a valid JSON value type");return this[""+r+"ToHTML"].call(this,t,n)},t.prototype.nullToHTML=function(e){return this.decorateWithSpan("null","null")},t.prototype.undefinedToHTML=function(){return this.decorateWithSpan("undefined","undefined")},t.prototype.numberToHTML=function(e){return this.decorateWithSpan(e,"num")},t.prototype.stringToHTML=function(e){var t,n;return/^(http|https|file):\/\/[^\s]+$/i.test(e)?'" '+this.jsString(e)+'" ':(t="",e=this.jsString(e),this.options.nl2br&&(n=/([^>\\r\\n]?)(\\r\\n|\\n\\r|\\r|\\n)/g,n.test(e)&&(t=" multiline",e=(e+"").replace(n,"$1 "))),'"'+e+'" ')},t.prototype.booleanToHTML=function(e){return this.decorateWithSpan(e,"bool")},t.prototype.arrayToHTML=function(e,t){var n,r,l,o,i,s,a,p;for(null==t&&(t=0),r=!1,i="",o=e.length,l=a=0,p=e.length;p>a;l=++a)s=e[l],r=!0,i+=""+this.valueToHTML(s,t+1),o>1&&(i+=","),i+=" ",o--;return r?(n=0===t?"":" collapsible",'[]"):"[ ]"},t.prototype.objectToHTML=function(e,t){var n,r,l,o,i,s,a;null==t&&(t=0),r=!1,i="",o=0;for(s in e)o++;for(s in e)a=e[s],r=!0,l=this.options.escape?this.jsString(s):s,i+='" '+l+'" : '+this.valueToHTML(a,t+1),o>1&&(i+=","),i+=" ",o--;return r?(n=0===t?"":" collapsible",'{}"):"{ }"},t.prototype.jsonToHTML=function(e){return''+this.valueToHTML(e)+"
"},t}(),"undefined"!=typeof module&&null!==module&&(module.exports=r),n=function(){function e(){}return e.bindEvent=function(e,t){var n;return e.firstChild.addEventListener("click",function(e){return function(n){return e.toggle(n.target.parentNode.firstChild,t)}}(this)),n=document.createElement("div"),n.className="collapser",n.innerHTML=t.collapsed?"+":"-",n.addEventListener("click",function(e){return function(n){return e.toggle(n.target,t)}}(this)),e.insertBefore(n,e.firstChild),t.collapsed?this.collapse(n):void 0},e.expand=function(e){var t,n;return n=this.collapseTarget(e),""!==n.style.display?(t=n.parentNode.getElementsByClassName("ellipsis")[0],n.parentNode.removeChild(t),n.style.display="",e.innerHTML="-"):void 0},e.collapse=function(e){var t,n;return n=this.collapseTarget(e),"none"!==n.style.display?(n.style.display="none",t=document.createElement("span"),t.className="ellipsis",t.innerHTML=" … ",n.parentNode.insertBefore(t,n),e.innerHTML="+"):void 0},e.toggle=function(e,t){var n,r,l,o,i,s;if(null==t&&(t={}),l=this.collapseTarget(e),n="none"===l.style.display?"expand":"collapse",t.recursive_collapser){for(r=e.parentNode.getElementsByClassName("collapser"),s=[],o=0,i=r.length;i>o;o++)e=r[o],s.push(this[n](e));return s}return this[n](e)},e.collapseTarget=function(e){var t,n;return n=e.parentNode.getElementsByClassName("collapsible"),n.length?t=n[0]:void 0},e}(),t=e,l={collapse:function(e){return"-"===e.innerHTML?n.collapse(e):void 0},expand:function(e){return"+"===e.innerHTML?n.expand(e):void 0},toggle:function(e){return n.toggle(e)}},t.fn.JSONView=function(){var e,o,i,s,a,p,c;return e=arguments,null!=l[e[0]]?(a=e[0],this.each(function(){var n,r;return n=t(this),null!=e[1]?(r=e[1],n.find(".jsonview .collapsible.level"+r).siblings(".collapser").each(function(){return l[a](this)})):n.find(".jsonview > ul > li .collapsible").siblings(".collapser").each(function(){return l[a](this)})})):(s=e[0],p=e[1]||{},o={collapsed:!1,nl2br:!1,recursive_collapser:!1,escape:!0,strict:!1},p=t.extend(o,p),i=new r(p),"[object String]"===Object.prototype.toString.call(s)&&(s=JSON.parse(s)),c=i.jsonToHTML(s),this.each(function(){var e,r,l,o,i,s;for(e=t(this),e.html(c),l=e[0].getElementsByClassName("collapsible"),s=[],o=0,i=l.length;i>o;o++)r=l[o],"LI"===r.parentNode.nodeName?s.push(n.bindEvent(r.parentNode,p)):s.push(void 0);return s}))}}(jQuery);
--------------------------------------------------------------------------------
/src/example/extension/virtualBackground/agora-extension-virtual-background/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { EventEmitter } from "events";
4 | import { Extension } from "agora-rte-extension";
5 | import type { IBaseProcessor } from "agora-rte-extension";
6 | import type { IExtension } from "agora-rte-extension";
7 | import { IProcessorContext } from "agora-rte-extension";
8 | import { VideoProcessor } from "agora-rte-extension";
9 |
10 | export declare type IVirtualBackgroundExtension = IExtension;
11 |
12 | export declare interface IVirtualBackgroundProcessor extends IBaseProcessor {
13 | init(wasmDir: string): Promise;
14 | release(): Promise;
15 | setOptions(options: VirtualBackgroundEffectOptions): void;
16 | onoverload?: () => void;
17 | getProcessedTrack(): Promise;
18 | }
19 |
20 | /**
21 | * @public
22 | *
23 | * 插件实例的属性。用于 {@link setOptions} 方法。
24 | */
25 | /** @en
26 | * Options of the virtual background plugin. Used when calling {@link setOptions}.
27 | */
28 | export declare type VirtualBackgroundEffectOptions = {
29 | /**
30 | * 设置背景类型。
31 | *
32 | * "color": 纯色
33 | * "img": 图片
34 | * "blur": 虚化
35 | * "none": 无背景(透明)
36 | */
37 | /** @en
38 | * Sets the background type.
39 | *
40 | * "color": pure color
41 | * "img": image
42 | * "blur": blurring
43 | */
44 | type: string;
45 | /**
46 | * 设置背景颜色,值为有效的CSS颜色。
47 | *
48 | */
49 | /** @en
50 | * Sets the background color, the value should be a CSS supported color.
51 | */
52 | color?: string;
53 | /**
54 | * 设置自定义背景图。
55 | *
56 | * 如果出现 "texture bound to texture unit 2 is not renderable. It might be non-power-of-2 or have incompatible texture filtering (maybe)?" 报错,请检查图片的分辨率相乘后是否是 2 的倍数。
57 | */
58 | /** @en
59 | * Sets the background image.
60 | *
61 | * If the error "texture bound to texture unit 2 is not renderable. It might be non-power-of-2 or have incompatible texture filtering (maybe)?" appears, please check whether the product of the picture's width and height is a multiple of 2.
62 | */
63 | source?: HTMLImageElement | HTMLVideoElement;
64 | /**
65 | * 设置背景模糊程度。
66 | *
67 | * 1: 轻度
68 | * 2: 中度
69 | * 3: 重度
70 | */
71 | /** @en
72 | * Sets background blurring degree.
73 | *
74 | * 1: low
75 | * 2: medium
76 | * 3: high
77 | */
78 | blurDegree?: Number;
79 | /**
80 | * 背景填充方式。
81 | *
82 | * "contain": 保持原图比例显示完整背景,用黑色填充不足部分
83 | * "cover": 保持原图比例,覆盖显示区域,裁切超出内容
84 | * "fill": 拉伸背景以填充显示区域
85 | */
86 | /** @en
87 | * How an element responds to the height and width of its content box.
88 | *
89 | * "contain": increases or decreases the size of the background to fill the video whilst preserving its aspect-ratio
90 | * "cover": the background will fill the height and width of its box, once again maintaining its aspect ratio but often cropping the video.
91 | * "fill": stretches the background to fit the video.
92 | */
93 | fit?: "contain" | "cover" | "fill";
94 | };
95 |
96 | declare class VirtualBackgroundExtension
97 | extends Extension
98 | implements IVirtualBackgroundExtension
99 | {
100 | constructor();
101 | checkCompatibility(): boolean;
102 | protected _createProcessor(): VirtualBackgroundProcessor;
103 | }
104 | export default VirtualBackgroundExtension;
105 |
106 | declare class VirtualBackgroundProcessor
107 | extends VideoProcessor
108 | implements IVirtualBackgroundProcessor
109 | {
110 | name: string;
111 | private segWorker;
112 | private processed_track;
113 | eventBus: EventEmitter;
114 | private analyzer;
115 | onoverload?: () => void;
116 | private initialized;
117 | private piped;
118 | private forceEnable;
119 | private avgCost;
120 | private stats;
121 | constructor();
122 | init(wasmDir: string): Promise;
123 | setOptions(options: VirtualBackgroundEffectOptions): void;
124 | getProcessedTrack(): Promise;
125 | protected onEnableChange(enabled: boolean): Promise;
126 | private getStats;
127 | protected onTrack(inputTrack: MediaStreamTrack, context: IProcessorContext): Promise;
128 | release(): Promise;
129 | protected onPiped(context: IProcessorContext): void;
130 | protected onUnpiped(): void;
131 | }
132 |
133 | export {};
134 |
--------------------------------------------------------------------------------
/src/example/extension/aiDenoiser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Ai Denoiser -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/src/example/others/pushStreamToCDN/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Push Stream To CDN -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/example/advanced/joinMultipleChannel/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Join Multiple Channel -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/src/example/advanced/customVideoSource/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Custom Video Source -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
41 |
42 |
86 |
87 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/src/example/advanced/selfRendering/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Video Self-Rendering -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
70 |
71 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/src/example/advanced/screenshot/index.js:
--------------------------------------------------------------------------------
1 | // create Agora client
2 | var client = AgoraRTC.createClient({
3 | mode: "rtc",
4 | codec: "vp8",
5 | });
6 | var localTracks = {
7 | videoTrack: null,
8 | audioTrack: null,
9 | };
10 | var remoteUsers = {};
11 | // Agora client options
12 | var options = getOptionsFromLocal();
13 |
14 | $("#join-form").submit(async function (e) {
15 | e.preventDefault();
16 | $("#join").attr("disabled", true);
17 | try {
18 | options.channel = $("#channel").val();
19 | options.uid = Number($("#uid").val());
20 | options.token = await agoraGetAppData(options);
21 | await join();
22 | setOptionsToLocal(options);
23 | message.success("join channel success!");
24 | } catch (error) {
25 | console.error(error);
26 | message.error(error.message);
27 | } finally {
28 | $("#leave").attr("disabled", false);
29 | }
30 | });
31 |
32 | $("#leave").click(function (e) {
33 | leave();
34 | });
35 |
36 | $("#takeScreenshot").click(async function (e) {
37 | try {
38 | //get imageData object picture from local video track.
39 | const imageData = localTracks.videoTrack.getCurrentFrameData();
40 | downloadImageData(imageData);
41 | } catch (error) {
42 | console.error(error);
43 | message.error(error.message);
44 | }
45 | });
46 |
47 | async function join() {
48 | client.on("user-published", handleUserPublished);
49 | client.on("user-unpublished", handleUserUnpublished);
50 | // start Proxy if needed
51 | const mode = Number(options.proxyMode);
52 | if (mode != 0 && !isNaN(mode)) {
53 | client.startProxyServer(mode);
54 | }
55 | // join the channel
56 | options.uid = await client.join(
57 | options.appid,
58 | options.channel,
59 | options.token || null,
60 | options.uid || null,
61 | );
62 | // create local audio and video tracks
63 | if (!localTracks.audioTrack) {
64 | localTracks.audioTrack = await AgoraRTC.createMicrophoneAudioTrack({
65 | encoderConfig: "music_standard",
66 | });
67 | }
68 | if (!localTracks.videoTrack) {
69 | localTracks.videoTrack = await AgoraRTC.createCameraVideoTrack();
70 | }
71 | // play local video track
72 | localTracks.videoTrack.play("local-player");
73 | $("#local-player-name").text(`uid: ${options.uid}`);
74 | // publish local tracks to channel
75 | await client.publish(Object.values(localTracks));
76 | console.log("publish success");
77 | }
78 |
79 | async function leave() {
80 | for (trackName in localTracks) {
81 | var track = localTracks[trackName];
82 | if (track) {
83 | track.stop();
84 | track.close();
85 | localTracks[trackName] = undefined;
86 | }
87 | }
88 |
89 | // remove remote users and player views
90 | remoteUsers = {};
91 | $("#remote-playerlist").html("");
92 |
93 | // leave the channel
94 | await client.leave();
95 | $("#local-player-name").text("");
96 | $("#join").attr("disabled", false);
97 | $("#leave").attr("disabled", true);
98 | console.log("client leaves channel success");
99 | }
100 |
101 | async function subscribe(user, mediaType) {
102 | const uid = user.uid;
103 | // subscribe to a remote user
104 | await client.subscribe(user, mediaType);
105 | console.log("subscribe success");
106 | if (mediaType === "video") {
107 | const player = $(`
108 |
113 | `);
114 | $("#remote-playerlist").append(player);
115 | user.videoTrack.play(`player-${uid}`);
116 | }
117 | if (mediaType === "audio") {
118 | user.audioTrack.play();
119 | }
120 | }
121 |
122 | function handleUserPublished(user, mediaType) {
123 | const id = user.uid;
124 | remoteUsers[id] = user;
125 | subscribe(user, mediaType);
126 | }
127 |
128 | function handleUserUnpublished(user, mediaType) {
129 | if (mediaType === "video") {
130 | const id = user.uid;
131 | delete remoteUsers[id];
132 | $(`#player-wrapper-${id}`).remove();
133 | }
134 | }
135 |
136 | //download imageData picture.
137 | async function downloadImageData(data) {
138 | //create canvas,set the canvas's width and height as data(imageData object)'s width and height.
139 | const canvas = document.createElement("canvas");
140 | canvas.width = data.width;
141 | canvas.height = data.height;
142 | const ctx = canvas.getContext("2d");
143 | if (!ctx) return;
144 | //put imageData object value into canvas
145 | ctx.putImageData(data, 0, 0);
146 | //convert the canvas picture to base64 format.
147 | const dataURL = canvas.toDataURL();
148 | //create a hyperlink, and set the value as previous 'dataURL' value, so that we can download as capture.png file.
149 | const link = document.createElement("a");
150 | link.download = "capture.png";
151 | link.href = dataURL;
152 | document.body.appendChild(link);
153 | link.click();
154 | link.remove();
155 | canvas.remove();
156 | }
157 |
--------------------------------------------------------------------------------
/src/example/quickStart/videoAndVoiceCalling/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Basic Live -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
40 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/src/example/extension/videoCompositor/agora-extension-video-compositor/index.d.ts:
--------------------------------------------------------------------------------
1 | import { Extension } from 'agora-rte-extension';
2 | import { IBaseProcessor } from 'agora-rte-extension';
3 | import { IExtension } from 'agora-rte-extension';
4 | import { IProcessorContext } from 'agora-rte-extension';
5 | import { VideoProcessor } from 'agora-rte-extension';
6 |
7 | export declare type IVideoCompositingExtension = IExtension;
8 |
9 | export declare interface IVideoTrackCompositor extends IBaseProcessor {
10 | /**
11 | * 创建输入端点。
12 | *
13 | * "option": 图层显示选项
14 | *
15 | * @return IBaseProcessor 接口实例
16 | */
17 | /** @en
18 | * Create input endpoint.
19 | *
20 | * "option": Display options of this layer
21 | *
22 | * @return An instance of IBaseProcessor interface
23 | */
24 | createInputEndpoint(option: LayerOption): IBaseProcessor;
25 | /**
26 | * 增加图像层。
27 | *
28 | * "url": 图像URL
29 | * "option": 图层显示选项
30 | *
31 | * @return 一个 HTMLImageElement 对象
32 | */
33 | /** @en
34 | * Create an image layer.
35 | *
36 | * "url": Image source
37 | * "option": Display options of this layer
38 | *
39 | * @return A HTMLImageElement Object
40 | */
41 | addImage(url: string, option: LayerOption): HTMLImageElement;
42 | /**
43 | * 删除图像层。
44 | *
45 | * "imgElement": 一个 HTMLImageElement 对象
46 | */
47 | /** @en
48 | * Remove an image layer.
49 | *
50 | * "imgElement": A HTMLImageElement Object
51 | */
52 | removeImage(imgElement: HTMLImageElement): void;
53 | /**
54 | * 设置输出选项。
55 | *
56 | * "width": 输出视频的宽度
57 | * "height": 输出视频的高度
58 | * "fps": 输出视频的帧率
59 | */
60 | /** @en
61 | * Sets the background media.
62 | *
63 | * "width": Width of output video
64 | * "height": Height of output video
65 | * "fps": Framerate of output video
66 | */
67 | setOutputOptions(width: number, height: number, fps?: number): void;
68 | /**
69 | * 启动合成。
70 | */
71 | /** @en
72 | * Start compositing.
73 | */
74 | start(): Promise;
75 | /**
76 | * 停止合成。
77 | */
78 | /** @en
79 | * Stop compositing.
80 | */
81 | stop(): Promise;
82 | }
83 |
84 | export declare type LayerOption = {
85 | /**
86 | * 视频显示容器的水平坐标。
87 | */
88 | /** @en
89 | * x location of content box.
90 | */
91 | x: number;
92 | /**
93 | * 视频显示容器的垂直坐标。
94 | */
95 | y: number;
96 | /**
97 | * y location of content box.
98 | */
99 | /** @en
100 | * Width of content box.
101 | */
102 | width: number;
103 | /**
104 | * 视频显示高度。
105 | */
106 | /** @en
107 | * Height of content box.
108 | */
109 | height: number;
110 | /**
111 | * 视频填充方式。
112 | *
113 | * "contain": 保持画面比例显示完整内容
114 | * "cover": 保持画面比例,填充显示容器,必要时裁切
115 | * "fill": 拉伸画面以填充容器所有区域,画面比列会更改为与容器相同
116 | */
117 | /** @en
118 | * How an element responds to the height and width of its content box.
119 | *
120 | * "contain": increases or decreases the size of the video to fill the box whilst preserving its aspect-ratio
121 | * "cover": the video will fill the height and width of its box, once again maintaining its aspect ratio but often cropping the video.
122 | * "fill": stretches the video to fit the content box, regardless of its aspect-ratio.
123 | */
124 | fit?: 'contain' | 'cover' | 'fill';
125 | };
126 |
127 | declare class VideoCompositingExtension extends Extension implements IVideoCompositingExtension {
128 | constructor();
129 | protected _createProcessor(): VideoTrackCompositor;
130 | }
131 | export default VideoCompositingExtension;
132 |
133 | declare class VideoTrackCompositor extends VideoProcessor implements IVideoTrackCompositor {
134 | name: 'VideoTrackCompositor';
135 | private _canvas2d;
136 | private _ctx2d;
137 | private _backgroundImage;
138 | private _intervalId;
139 | private _layers;
140 | private _canvasTrack;
141 | private _options;
142 | private _medias;
143 | private _width;
144 | private _height;
145 | private _fps;
146 | constructor();
147 | private draw;
148 | addLayer(track: MediaStreamTrack, option: LayerOption): void;
149 | removeLayer(track: MediaStreamTrack): void;
150 | setOutputOptions(width: number, height: number, fps?: number): void;
151 | addImage(url: string, option: LayerOption): HTMLImageElement;
152 | removeImage(imgElement: HTMLImageElement): void;
153 | setBackground(backgroundImage: HTMLImageElement): void;
154 | start(): Promise;
155 | stop(): Promise;
156 | createInputEndpoint(option: LayerOption): IBaseProcessor;
157 | protected onTrack(track: MediaStreamTrack, context: IProcessorContext): Promise;
158 | protected onUnpiped?(context?: IProcessorContext): void;
159 | private getStats;
160 | }
161 |
162 | export { }
163 |
--------------------------------------------------------------------------------
/src/example/others/dualStream/index.js:
--------------------------------------------------------------------------------
1 | // create Agora client
2 | var client = AgoraRTC.createClient({
3 | mode: "rtc",
4 | codec: "vp8",
5 | });
6 | AgoraRTC.enableLogUpload();
7 | var localTracks = {
8 | videoTrack: null,
9 | audioTrack: null,
10 | };
11 | var remoteUsers = {};
12 | // Agora client options
13 | var options = getOptionsFromLocal();
14 |
15 | $("body").on("click", ".player", async function (e) {
16 | e.preventDefault();
17 | const player = e.target.parentElement.parentElement;
18 | const uid = Number(player.dataset.uid);
19 |
20 | try {
21 | // Set user clicked to HQ/LQ Stream
22 | await changeSomeUserStream(uid);
23 | } catch (error) {
24 | console.error(error);
25 | message.error(error.message);
26 | }
27 | });
28 |
29 | $("#join-form").submit(async function (e) {
30 | e.preventDefault();
31 | $("#join").attr("disabled", true);
32 | try {
33 | options.channel = $("#channel").val();
34 | options.uid = Number($("#uid").val());
35 | options.token = await agoraGetAppData(options);
36 | await join();
37 | setOptionsToLocal(options);
38 | message.success("join channel success!");
39 | } catch (error) {
40 | console.error(error);
41 | message.error(error.message);
42 | } finally {
43 | $("#leave").attr("disabled", false);
44 | }
45 | });
46 |
47 | $("#leave").click(function (e) {
48 | leave();
49 | });
50 |
51 | async function join() {
52 | // add event listener to play remote tracks when remote user publishs.
53 | client.on("user-published", handleUserPublished);
54 | client.on("user-unpublished", handleUserUnpublished);
55 |
56 | // start Proxy if needed
57 | const mode = Number(options.proxyMode);
58 | if (mode != 0 && !isNaN(mode)) {
59 | client.startProxyServer(mode);
60 | }
61 |
62 | // Customize the video profile of the low-quality stream: 160 × 120, 15 fps, 120 Kbps.
63 | client.setLowStreamParameter({
64 | width: 160,
65 | height: 120,
66 | framerate: 15,
67 | bitrate: 120,
68 | });
69 |
70 | // Enable dual-stream mode.
71 | await client.enableDualStream();
72 |
73 | // join a channel and create local tracks, we can use Promise.all to run them concurrently
74 | [options.uid, localTracks.audioTrack, localTracks.videoTrack] = await Promise.all([
75 | // join the channel
76 | client.join(options.appid, options.channel, options.token || null, options.uid || null),
77 | // create local tracks, using microphone and camera
78 | AgoraRTC.createMicrophoneAudioTrack({
79 | encoderConfig: "music_standard",
80 | }),
81 | AgoraRTC.createCameraVideoTrack(),
82 | ]);
83 |
84 | // play local video track
85 | localTracks.videoTrack.play("local-player");
86 | $("#local-player-name").text(`uid: ${options.uid}`);
87 |
88 | // publish local tracks to channel
89 | await client.publish(Object.values(localTracks));
90 | console.log("publish success");
91 | }
92 |
93 | async function changeSomeUserStream(uid) {
94 | const streamType = $(`#stream-type-${uid}`).text();
95 | if (streamType == "HQ Stream") {
96 | // Set the remote user to the low-quality video stream.
97 | await client.setRemoteVideoStreamType(uid, 1);
98 | $(`#stream-type-${uid}`).text("LQ Stream");
99 | } else if (streamType == "LQ Stream") {
100 | // Set the remote user to the high-quality video stream.
101 | await client.setRemoteVideoStreamType(uid, 0);
102 | $(`#stream-type-${uid}`).text("HQ Stream");
103 | }
104 | }
105 |
106 | async function leave() {
107 | for (trackName in localTracks) {
108 | var track = localTracks[trackName];
109 | if (track) {
110 | track.stop();
111 | track.close();
112 | localTracks[trackName] = undefined;
113 | }
114 | }
115 |
116 | // remove remote users and player views
117 | remoteUsers = {};
118 | $("#remote-playerlist").html("");
119 |
120 | // disable dual-stream mode
121 | await client.disableDualStream();
122 |
123 | // leave the channel
124 | await client.leave();
125 | $("#local-player-name").text("");
126 | $("#join").attr("disabled", false);
127 | $("#leave").attr("disabled", true);
128 | console.log("client leaves channel success");
129 | }
130 |
131 | async function subscribe(user, mediaType) {
132 | const uid = user.uid;
133 | // subscribe to a remote user
134 | await client.subscribe(user, mediaType);
135 | console.log("subscribe success");
136 | if (mediaType === "video") {
137 | const player = $(`
138 |
139 |
140 |
uid: ${uid}
141 | HQ Stream
142 |
143 |
144 |
145 | `);
146 | $("#remote-playerlist").append(player);
147 | user.videoTrack.play(`player-${uid}`);
148 | }
149 | if (mediaType === "audio") {
150 | user.audioTrack.play();
151 | }
152 | }
153 |
154 | function handleUserPublished(user, mediaType) {
155 | const id = user.uid;
156 | remoteUsers[id] = user;
157 | subscribe(user, mediaType);
158 | }
159 |
160 | function handleUserUnpublished(user, mediaType) {
161 | if (mediaType === "video") {
162 | const id = user.uid;
163 | delete remoteUsers[id];
164 | $(`#player-wrapper-${id}`).remove();
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/common/left-menu.js:
--------------------------------------------------------------------------------
1 | const DEFAULT_ZH_DOC_URL = "https://doc.shengwang.cn/doc/rtc/javascript/get-started/enable-service";
2 | const DEFAULT_EN_DOC_URL =
3 | "https://docs.agora.io/en/video-calling/get-started/authentication-workflow?platform=web";
4 |
5 | function __isSmallScreen() {
6 | const SM_WIDTH = 576; // small screen width (phone)
7 | const windowWidth = $(window).width();
8 | return windowWidth <= SM_WIDTH;
9 | }
10 |
11 | function __transListToMenuDom(list) {
12 | return list
13 | .map((item) => {
14 | return ``;
31 | })
32 | .join("");
33 | }
34 |
35 | function __initMenu() {
36 | let language = getLanguage();
37 | let href = "";
38 | let logoImgSrc = "";
39 | if (language == "en") {
40 | href = DEFAULT_EN_DOC_URL;
41 | logoImgSrc = `${ORIGIN_URL}/assets/agora-logo-en.png`;
42 | } else {
43 | href = DEFAULT_ZH_DOC_URL;
44 | logoImgSrc = `${ORIGIN_URL}/assets/agora-logo-zh.png`;
45 | }
46 | let logoHtml = ` `;
47 | let menuHtml = `
48 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/src/example/extension/aiDenoiser/index.js:
--------------------------------------------------------------------------------
1 | var client = AgoraRTC.createClient({
2 | mode: "rtc",
3 | codec: "vp8",
4 | });
5 | var localTracks = {
6 | videoTrack: null,
7 | audioTrack: null,
8 | };
9 | var remoteUsers = {};
10 | var options = getOptionsFromLocal();
11 | let extension = null;
12 | let processor = null;
13 | let processorEnable = true;
14 |
15 | $("#openAiDenosier").attr("disabled", true);
16 | $("#enableAiDenosier").attr("disabled", true);
17 | $("#dump").attr("disabled", true);
18 |
19 | /*
20 | * When a user clicks Join or Leave in the HTML form, this procedure gathers the information
21 | * entered in the form and calls join asynchronously. The UI is updated to match the options entered
22 | * by the user.
23 | */
24 | $("#join-form").submit(async function (e) {
25 | e.preventDefault();
26 | $("#join").attr("disabled", true);
27 | try {
28 | options.channel = $("#channel").val();
29 | options.uid = Number($("#uid").val());
30 | options.token = await agoraGetAppData(options);
31 | await join();
32 | setOptionsToLocal(options);
33 | message.success("join channel success!");
34 | $("#openAiDenosier").attr("disabled", false);
35 | } catch (error) {
36 | console.error(error);
37 | message.error(error.message);
38 | } finally {
39 | $("#leave").attr("disabled", false);
40 | }
41 | });
42 |
43 | $("#leave").click(function (e) {
44 | leave();
45 | });
46 |
47 | /*
48 | * Join a channel, then create local video and audio tracks and publish them to the channel.
49 | */
50 | async function join() {
51 | // Add an event listener to play remote tracks when remote user publishes.
52 | client.on("user-published", handleUserPublished);
53 | client.on("user-unpublished", handleUserUnpublished);
54 | [options.uid, localTracks.audioTrack, localTracks.videoTrack] = await Promise.all([
55 | client.join(options.appid, options.channel, options.token || null, options.uid || null),
56 | AgoraRTC.createMicrophoneAudioTrack({
57 | encoderConfig: "music_standard",
58 | }),
59 | AgoraRTC.createCameraVideoTrack(),
60 | ]);
61 | localTracks.videoTrack.play("local-player");
62 | $("#local-player-name").text(`uid: ${options.uid}`);
63 |
64 | // localTracks.audioTrack.play();
65 | await client.publish(Object.values(localTracks));
66 | console.log("publish success");
67 | }
68 |
69 | async function leave() {
70 | for (trackName in localTracks) {
71 | var track = localTracks[trackName];
72 | if (track) {
73 | track.stop();
74 | track.close();
75 | localTracks[trackName] = undefined;
76 | }
77 | }
78 |
79 | // Remove remote users and player views.
80 | remoteUsers = {};
81 | $("#remote-playerlist").html("");
82 |
83 | // leave the channel
84 | await client.leave();
85 | $("#local-player-name").text("");
86 | $("#join").attr("disabled", false);
87 | $("#leave").attr("disabled", true);
88 | console.log("client leaves channel success");
89 | }
90 |
91 | /*
92 | * Add the local use to a remote channel.
93 | *
94 | * @param {IAgoraRTCRemoteUser} user - The {@link https://docs.agora.io/en/Voice/API%20Reference/web_ng/interfaces/iagorartcremoteuser.html| remote user} to add.
95 | * @param {trackMediaType - The {@link https://docs.agora.io/en/Voice/API%20Reference/web_ng/interfaces/itrack.html#trackmediatype | media type} to add.
96 | */
97 | async function subscribe(user, mediaType) {
98 | const uid = user.uid;
99 | // subscribe to a remote user
100 | await client.subscribe(user, mediaType);
101 | console.log("subscribe success");
102 | if (mediaType === "video") {
103 | const player = $(`
104 |
109 | `);
110 | $("#remote-playerlist").append(player);
111 | user.videoTrack.play(`player-${uid}`);
112 | }
113 | if (mediaType === "audio") {
114 | user.audioTrack.play();
115 | }
116 | }
117 |
118 | function handleUserPublished(user, mediaType) {
119 | const id = user.uid;
120 | remoteUsers[id] = user;
121 | subscribe(user, mediaType);
122 | }
123 |
124 | function handleUserUnpublished(user, mediaType) {
125 | if (mediaType === "video") {
126 | const id = user.uid;
127 | delete remoteUsers[id];
128 | $(`#player-wrapper-${id}`).remove();
129 | }
130 | }
131 |
132 | $("#openAiDenosier").click(async (e) => {
133 | let extension = new AIDenoiser.AIDenoiserExtension({
134 | assetsPath: "./agora-extension-ai-denoiser/external",
135 | });
136 | AgoraRTC.registerExtensions([extension]);
137 | extension.onloaderror = (e) => {
138 | console.error(e);
139 | processor = null;
140 | };
141 |
142 | processor = extension.createProcessor();
143 |
144 | processor.onoverload = async () => {
145 | await processor.disable();
146 | $("#enableAiDenosier").val("Enable AIDenoiser");
147 | $("#ai-status-tip").text("AIDenoiser is disabled");
148 | processorEnable = true;
149 | };
150 |
151 | localTracks.audioTrack.pipe(processor).pipe(localTracks.audioTrack.processorDestination);
152 |
153 | message.success("open AIDenoiser success!");
154 | $("#openAiDenosier").attr("disabled", true);
155 | $("#enableAiDenosier").attr("disabled", false);
156 | $("#dump").attr("disabled", false);
157 | });
158 |
159 | $("#enableAiDenosier").click(async (e) => {
160 | try {
161 | $("#enableAiDenosier").attr("disabled", true);
162 | if (processorEnable) {
163 | await processor.enable();
164 | $("#enableAiDenosier").text("Disable AIDenoiser");
165 | $("#ai-status-tip").text("AIDenoiser is enabled");
166 | processorEnable = false;
167 | } else {
168 | await processor.disable();
169 | $("#enableAiDenosier").text("Enable AIDenoiser");
170 | $("#ai-status-tip").text("AIDenoiser is disabled");
171 | processorEnable = true;
172 | }
173 | } catch (e) {
174 | console.error("enable AIDenoiser failure", e);
175 | } finally {
176 | $("#enableAiDenosier").attr("disabled", false);
177 | }
178 | });
179 |
180 | $("#dump").click(async (e) => {
181 | if (!processor) {
182 | return;
183 | }
184 | processor.ondump = (blob, name) => {
185 | const objectURL = URL.createObjectURL(blob);
186 | const tag = document.createElement("a");
187 | tag.download = name + ".wav";
188 | tag.href = objectURL;
189 | tag.click();
190 | };
191 | processor.ondumpend = () => {
192 | console.log("dump ended!!");
193 | };
194 | processor.dump();
195 | });
196 |
--------------------------------------------------------------------------------