├── 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 |
15 | 16 |
17 | 18 |
19 |
20 | 21 |
22 |
23 |
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 |
16 | 17 |
18 | 19 |
20 | 21 |
22 |
23 |
24 | 25 | 26 |
27 |
28 |
29 | 30 | 31 |
32 |
33 |
34 |
35 | 36 | 37 | 39 |
40 |
41 |
42 | 43 |
44 | 45 | 46 |
47 |
48 |
49 |
50 | 53 |
54 |
55 |
56 |
57 |
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 |
18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 |
26 | local stream 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 | remote stream 38 |
39 |
40 |
41 |
42 |
43 |
44 | 45 |
46 |
47 |
48 |
49 | 50 | 51 |
If you don`t know what is your channel, checkout 52 | this 53 |
54 |
55 |
56 | 57 |
58 | 60 |
61 |
62 | 63 | 65 | 66 | 68 |
69 |
70 | 71 | 72 |
73 |
74 |
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 |
21 | 22 |
23 |
local stream
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
remote stream
33 |
34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | 51 |
52 | If you don`t know what is your channel, checkout 53 | this 54 |
55 |
56 |
57 | 58 | 66 |
67 |
68 |
69 | 70 | 73 |
74 |
75 |
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 |
21 | 22 |
23 |
local stream
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
remote stream
33 |
34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | 51 |
52 | If you don`t know what is your channel, checkout 53 | this 54 |
55 |
56 |
57 | 58 | 66 |
67 |
68 | 69 | 70 | 71 |
72 | 73 | 76 |
77 |
78 |
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 |
21 | 22 |
23 |
local stream
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
remote stream
33 |
34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | 51 |
52 | If you don`t know what is your channel, checkout 53 | this 54 |
55 |
56 |
57 | 58 | 66 |
67 |
68 |
69 | 72 | 75 |
76 |
Click Remote User Video Player Change Stream Type
77 |
78 |
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 |
21 | 22 |
23 |
local stream
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
remote stream
33 |
34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | 51 |
52 | If you don`t know what is your channel, checkout 53 | this 54 |
55 |
56 |
57 | 58 | 66 |
67 |
68 | 69 |
70 | 71 | 74 |
75 | 76 |
77 | Your Browser doesn't support this Feature 78 |
79 |
80 |
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 |
21 | 22 |
23 |
local stream
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | 32 |
33 |
remote stream
34 |
35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 | 45 | 52 |
53 | If you don`t know what is your channel, checkout 54 | this 55 |
56 |
57 |
58 | 59 | 67 |
68 |
69 | 70 |
71 | 72 | 75 |
76 |
77 |
78 |
79 |
80 |
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 |
21 | 22 |
23 |
local stream
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
remote stream
33 |
34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | 51 |
52 | If you don`t know what is your channel, checkout 53 | this 54 |
55 |
56 |
57 | 58 | 66 |
67 |
68 | 69 |
70 | 73 | 76 | 79 |
80 |
81 |
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 |
21 | 22 |
23 |
local stream
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
remote stream
33 |
34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | 51 |
52 | If you don`t know what is your channel, checkout 53 | this 54 |
55 |
56 |
57 | 58 | 66 |
67 |
68 |
69 | 70 | 73 |
74 |
75 | 78 |
79 |
80 |
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 | 70 | 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 |
21 | 22 |
23 |
local stream
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 |
32 |
remote stream
33 |
34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | 51 |
52 | If you don`t know what is your channel, checkout 53 | this 54 |
55 |
56 |
57 | 58 | 66 |
67 |
68 |
69 | 70 | 73 |
74 |
75 | 78 |
79 |
80 |
isSpeaking: false
81 |
Speaking Prob: 0%
82 |
83 |
84 | 85 |
86 |
87 |
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",'[
      '+i+"
    ]"):"[ ]"},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",'{
      '+i+"
    }"):"{ }"},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 |
    21 | 22 |
    23 |
    local stream
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 | 31 |
    32 |
    remote stream
    33 |
    34 |
    35 |
    36 |
    37 |
    38 | 39 |
    40 |
    41 |
    42 |
    43 | 44 | 51 |
    52 | If you don`t know what is your channel, checkout 53 | this 54 |
    55 |
    56 |
    57 | 58 | 66 |
    67 |
    68 |
    69 | 70 | 73 |
    74 |
    75 |

    76 |

    77 | If you want to experience noise reduction, join the channel as a remote user 78 |

    79 | 82 | 85 | 88 |
    89 |
    90 |
    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 |
    21 | 22 |
    23 |
    local stream
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 | 31 |
    32 |
    remote stream
    33 |
    34 |
    35 |
    36 |
    37 |
    38 | 39 |
    40 |
    41 |
    42 |
    43 | 44 | 51 |
    52 | If you don`t know what is your channel, checkout 53 | this 54 |
    55 |
    56 |
    57 | 58 | 66 |
    67 |
    68 | 69 | 76 |
    77 |
    78 |
    79 | 80 | 83 |
    84 |
    85 | 93 | 101 |
    102 |
    103 |
    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 |
    21 | 22 |
    23 |
    local stream
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 | 31 |
    32 |
    remote stream
    33 |
    34 |
    35 |
    36 |
    37 |
    38 | 39 |
    40 |
    41 |
    42 |
    43 | 44 | 51 |
    52 | If you don`t know what is your channel, checkout 53 | this 54 |
    55 |
    56 |
    57 | 58 | 65 |
    66 | If you don`t know what is your channel, checkout 67 | this 68 |
    69 |
    70 |
    71 | 72 | 80 |
    81 |
    82 | 83 | 91 |
    92 |
    93 |
    94 | 95 | 98 |
    99 |
    100 |
    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 |
    21 | 22 |
    23 |
    local stream
    24 |
    25 |
    26 |
    27 |
    28 | 31 |
    32 |
    33 | 34 |
    35 |
    remote stream
    36 |
    37 |
    38 |
    39 |
    40 |
    41 | 42 |
    43 |
    44 |
    45 |
    46 | 47 | 54 |
    55 | If you don`t know what is your channel, checkout 56 | this 57 |
    58 |
    59 |
    60 | 61 | 69 |
    70 |
    71 |
    72 | 73 | 77 |
    78 |
    79 | 80 | 83 |
    84 |
    85 |
    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 |
    21 | 22 |
    23 |
    local stream
    24 |
    25 |
    26 |
    27 |
    28 | 60 |
    61 |
    62 | 63 |
    64 |
    remote stream
    65 |
    66 |
    67 |
    68 |
    69 |
    70 | 71 |
    72 |
    73 |
    74 |
    75 | 76 | 83 |
    84 | If you don`t know what is your channel, checkout 85 | this 86 |
    87 |
    88 |
    89 | 90 | 98 |
    99 |
    100 | 101 |
    102 | 103 | 106 |
    107 |
    108 |
    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 |
    109 |
    110 |
    uid: ${uid}
    111 |
    112 |
    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 |
    21 | 22 |
    23 |
    local stream
    24 |
    25 |
    26 |
    27 |
    28 |
    29 |
    30 | 31 |
    32 |
    remote stream
    33 |
    34 |
    35 |
    36 |
    37 |
    38 | 39 | 40 |
    41 |
    42 |
    43 | 44 | 51 |
    52 | If you don`t know what is your channel, checkout 53 | this 54 |
    55 |
    56 |
    57 |
    User ID(optional)
    58 | 66 |
    67 |
    68 | 69 | 76 |
    77 |
    78 | 81 | 84 |
    85 | 86 |
    87 |
    Video
    88 |
    89 | 92 | 95 |
    96 |
    97 | 98 |
    99 |
    Audio
    100 |
    101 | 104 | 107 |
    108 |
    109 |
    110 |
    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 = `logo`; 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 |
    105 |
    106 |
    uid: ${uid}
    107 |
    108 |
    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 | --------------------------------------------------------------------------------