{"versions":{"1.0.22":{"name":"agora-rte-extension","version":"1.0.22","description":"Agora RTE Extension","main":"./dist/agora-rte-extension.js","typings":"./dist/agora-rte-extension.d.ts","scripts":{"type":"api-extractor run --local --verbose","build":"rm -rf ./lib ./dist && webpack && tsc && yarn type","dev":"webpack --mode=development --watch","test":"npx karma start"},"author":"","license":"ISC","devDependencies":{"@babel/core":"^7.16.7","@babel/plugin-transform-runtime":"^7.16.10","@babel/preset-env":"^7.16.11","@babel/preset-typescript":"^7.16.7","@babel/runtime-corejs3":"^7.16.8","@microsoft/api-extractor":"^7.19.4","@types/chai":"^4.3.0","@types/mocha":"^9.1.0","babel-loader":"^8.2.3","chai":"^4.3.6","core-js":"3","karma":"^6.3.16","karma-chai":"^0.1.0","karma-chrome-launcher":"^3.1.0","karma-mocha":"^2.0.1","karma-typescript":"^5.5.3","mocha":"^9.2.0","typescript":"^4.5.4","webpack":"^5.65.0","webpack-cli":"^4.9.1"},"browserslist":["chrome >= 58","ios >= 11","safari >= 11","firefox >= 56"],"gitHead":"07be6efe945fdd988b1fc59876d04437c0564b11","_id":"agora-rte-extension@1.0.22","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-hK9oNjcondwt0IxOT6701BxhHddXiNZlJSx7jqJRVe8gtasZld4aWE4rfD7uGQFz+SPTg7aUnbNnm4iRu58+mA==","shasum":"39aae810bca00576865b852af8964907d50e32e7","tarball":"http://123.232.10.234:8212/nexus/content/groups/npm-public/agora-rte-extension/-/agora-rte-extension-1.0.22.tgz","fileCount":6,"unpackedSize":101447,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiMvsNACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrwUA//WZfjCEI0xbzu7YEZeEUm0DEt71+wYN/KxA4dHEde0/T/n6/m\r\nuFiaJK/yF23DSlOIvzB82dGL1o1dXxCGRKXNdEP96tf9/J1q7D66kFkfXFru\r\n8Qt55r0Te8wqNK7C3BaaKcuJraC2jA0GsZoSMcx+zw4PK4I+Ml5JuCU8bNDk\r\nw0Qpf4OXUtHbWI+8uUzbPL/ajqjxFP/QxFes5UK6l+J37V8Zc/EdnKaOKPoq\r\n2VuomXY6gak2nFdxfFbUE87LvgLvTv8t0AGsibVmKa4gTURqQhb4sHBvvNZS\r\nF04/tqfMW6u8KbWfQF7pB5FaHE8Gcf964wKbHz3epGl7Sobt/09KgTnNKzxo\r\nF73t+KsPDocsRd3moNjow8C3cUP8S2lM7tl+bVx1ir9POA67PBwZFnM1h+YH\r\nwzDDXutnYSjLfcUxpZ4jNcvDJGuEKw8zqzl76uwMdz7J1xQuU9rDPC7XwIyf\r\nnJZ4MMplPi/OUVV/p+maxiM0baIHe08FRg3IXzGoWsxszQArcNXV+s8KbQhI\r\nfyC1NGRAT1KiwkHmvWQQs4WqxnJ0WcpdufYmmf0xBeXNl4vGdTn1IGdBd+v8\r\nevFERHAe/fti0tLbDkLS6BmHJ6mzE0bnVUwUEv4G2OZJsFxNX+QZ8M3yGDxU\r\nqj4E+0DkYbouzH63S4+atoTGAbpnQC/HFJg=\r\n=NNE+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGRs/6xXWh3oZgVTYnYX5scz9QD5GGgyk8AVekiq4s0uAiBbW3LFcHID/vf9xztkfKvnyfLxXtGRS3uYGyf0Dn7O2g=="}]},"_npmUser":{"name":"anonymous","email":"Sales@agora.io"},"directories":{},"maintainers":[{"name":"anonymous","email":"Sales@agora.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/agora-rte-extension_1.0.22_1647508237739_0.6129188073819973"},"_hasShrinkwrap":false,"contributors":[]},"1.0.23":{"name":"agora-rte-extension","version":"1.0.23","description":"Agora RTE Extension","main":"./dist/agora-rte-extension.js","typings":"./dist/agora-rte-extension.d.ts","scripts":{"type":"api-extractor run --local --verbose","build":"rm -rf ./lib ./dist && webpack && tsc && yarn type","dev":"webpack --mode=development --watch","test":"npx karma start"},"author":"","license":"ISC","devDependencies":{"@babel/core":"^7.16.7","@babel/plugin-transform-runtime":"^7.16.10","@babel/preset-env":"^7.16.11","@babel/preset-typescript":"^7.16.7","@babel/runtime-corejs3":"^7.16.8","@microsoft/api-extractor":"^7.19.4","@types/chai":"^4.3.0","@types/mocha":"^9.1.0","babel-loader":"^8.2.3","chai":"^4.3.6","core-js":"3","karma":"^6.3.16","karma-chai":"^0.1.0","karma-chrome-launcher":"^3.1.0","karma-mocha":"^2.0.1","karma-typescript":"^5.5.3","mocha":"^9.2.0","typescript":"^4.5.4","webpack":"^5.65.0","webpack-cli":"^4.9.1"},"browserslist":["chrome >= 58","ios >= 11","safari >= 11","firefox >= 56"],"gitHead":"8b0e222519f98c5ce7ad9958bc71bbfca1720db7","_id":"agora-rte-extension@1.0.23","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-X2cGBg+L5ZJIFU91qvMASvRsBfg1HXTktVG3YROw9wxHsILSI7jgF9R9XraLc3fNX/UjovaYAlUW+hiJe0v6Xw==","shasum":"54a7215fd2cdcf2553d83570530950054f1e59a6","tarball":"http://123.232.10.234:8212/nexus/content/groups/npm-public/agora-rte-extension/-/agora-rte-extension-1.0.23.tgz","fileCount":6,"unpackedSize":101436,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiM/N3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpUJA//V0Su2qND+HFspED0JI9ox3/mZjhowOaMhCj629H8NIvXAf51\r\nzwwBh4ZeB1Lki01M52uKpVKDJX83P1tE50BxvHdMARZPsliOMg15NKCxYc0j\r\ns70PtiXa6uOhoY8w+m2YM7MjmIcdM6OAs6J/II1tOPEAvlgaoHemMb1X2OuH\r\n3uu++4j1nxq13eBqjhQwB6TI/bm7uZqFnelrHYDqN3JykHpz17LIVcKI0IVt\r\nBBkoRuSMA02Qwx7JautjK/OPK5FcaFpQxwoK9AT1wuccqc2VUNTRVX5QQbh7\r\n6C7pKdLqupn0Jan1FtRjNDsrePZTZ5+U5Ogag11ypfzbIkyxm5kXxvn9txQs\r\nlMz8n2+ob4NUdD6IJLgOsegF86EPsp1BtdwTK1Rn1slGrA9eMrbW6Uk1DgG2\r\nlqKCp+ijAStj43bMNsprZZl4OAauOWF3UD+Zh87lSjcdtJxWh0xHu4uyPHFX\r\np7aPvvQFS9Hqqs1UAZyIvhzs385HBH94ptWrrHMsj3e+O6VWRL6rzFmPljiA\r\nGL2YYg2ySPWK7PZLzlKnhaaCuxI9ReJQohG7zn18tr627RYrahTBYGGtywQf\r\naZtGR2fb3fsQGUkLU8kGiVybVHDgHEOsj1TNxORu0zuC0NtYFG0wk59oHW6Q\r\n9w9g/jxoeH3O0rEsqU57mygY5o8SCKT85nY=\r\n=EANA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIATG/fbO7Oq6Z+Bjjv4fSRcGR1HEdkOjNUW8rfz0ghrAAiEA7pkIEp312Nrm0HqKJH/xl2b9CxtvzY0obtHxilGm39k="}]},"_npmUser":{"name":"anonymous","email":"Sales@agora.io"},"directories":{},"maintainers":[{"name":"anonymous","email":"Sales@agora.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/agora-rte-extension_1.0.23_1647571831639_0.5286710680654569"},"_hasShrinkwrap":false,"contributors":[]},"1.1.0":{"name":"agora-rte-extension","version":"1.1.0","description":"Agora RTE Extension","main":"./dist/agora-rte-extension.js","typings":"./dist/agora-rte-extension.d.ts","scripts":{"type":"api-extractor run --local --verbose","build":"rm -rf ./lib ./dist && webpack && tsc && yarn type","dev":"webpack --mode=development --watch","test":"npx karma start","wb":"rm -rf ./dist-white-brand && yarn build && wb"},"author":"","license":"ISC","devDependencies":{"@babel/core":"^7.16.7","@babel/plugin-transform-runtime":"^7.16.10","@babel/preset-env":"^7.16.11","@babel/preset-typescript":"^7.16.7","@babel/runtime-corejs3":"^7.16.8","@microsoft/api-extractor":"^7.19.4","@types/chai":"^4.3.0","@types/mocha":"^9.1.0","babel-loader":"^8.2.3","chai":"^4.3.6","core-js":"3","karma":"^6.3.16","karma-chai":"^0.1.0","karma-chrome-launcher":"^3.1.0","karma-mocha":"^2.0.1","karma-typescript":"^5.5.3","mocha":"^9.2.0","typescript":"^4.5.4","webpack":"^5.65.0","webpack-cli":"^4.9.1","white-brand-cli":"^1.2.3"},"browserslist":["chrome >= 58","ios >= 11","safari >= 11","firefox >= 56"],"gitHead":"cbe12ad0423dff3fb0f3d3a865050b649554e5d8","_id":"agora-rte-extension@1.1.0","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-HGsr7dJ5FHeCVHTLHCUfQ6cvKVLJz9b11F/FZbSQ/IqYlGZwRdu5E6hVdWenjWyYiqmpZm+nCP9IIqXSA+BPwA==","shasum":"197db76d6231068a2929594c7eea1a96419ca2d6","tarball":"http://123.232.10.234:8212/nexus/content/groups/npm-public/agora-rte-extension/-/agora-rte-extension-1.1.0.tgz","fileCount":6,"unpackedSize":97397,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHEnitF+KazWB61yO0vECAi6IH0i3Sad+HF4ljgwtyotAiEAwr7Mg2kSm3Wqo2m4/sPlS6DV9h9FzMU9Gth7gZCkvRE="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi83olACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmoqlw/+MzZ7PaKWNM+ysIbiMHmCkBaKB48us08CrBWqLp42sM8FMZTi\r\nrUMF4oA343MpT9ZCxhEA9126lP5XYfQJQMcAibeVu59d49EPDQk/CnjIwoBX\r\nfYPRQU+P9fOrkf5WKEUI+tEhEDr8KPjezYY6UhM1xvIrYOPgKks/WPgJeCgU\r\n+Be11qg5/2DSlgnfS+u5LgFKzBV1hdX38BVLcnwmD4QX5MpGtFXQi/Fo0enQ\r\n13NCfYMAbYe9TMu6Ed6bS69/f64LM6vixvpM/1toR6+I8BQFYqrw/BByUDRz\r\nEp9UE2SY3xm0nBDjhx2ins1LVFrf9ygr5EjJ4iTtXFgTTG2m5mu+B7uSG1Sf\r\ncLywKWmLFx9g6AYun7htpFJo6Ti7MOluJxtORiyUoJgIf5ej7QcMieUc/0yJ\r\n3UtPdc8pz8yzbHTZhfCnAgjABru7oXOVFBGfb3HDHHoZ2sqUizMeFDKCZX3r\r\nzMbeNA67Nl5D1SqtHmsnjxqHtRaLMWzSXf6EaFVE51mDAMGorvCVioGuLxtd\r\nZzvQLl2ClXN85hXV5lQP3ZgP0609YXf7okS6FpamiCWMa2j7/1zkKLRg8NVo\r\nQJzULqdyG1CKn492JhCbAse62r3MSoR+J7mXASZl9ABCoV/UrD/Vu/wXhHVx\r\nNllmt4i1r1TPDGLsXuisGwVBzlPUXJSKWZQ=\r\n=mTLj\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"anonymous","email":"Sales@agora.io"},"directories":{},"maintainers":[{"name":"anonymous","email":"Sales@agora.io"},{"name":"anonymous","email":"wangzhi@agora.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/agora-rte-extension_1.1.0_1660123685716_0.7405625304886649"},"_hasShrinkwrap":false,"contributors":[]},"1.2.3":{"name":"agora-rte-extension","version":"1.2.3","description":"Agora RTE Extension","main":"./dist/agora-rte-extension.js","typings":"./dist/agora-rte-extension.d.ts","scripts":{"type":"api-extractor run --local --verbose","build":"rm -rf ./lib ./dist && webpack && tsc && yarn type","dev":"webpack --mode=development --watch","test":"npx karma start","wb":"rm -rf ./dist-white-brand && yarn build && wb"},"author":"","license":"ISC","devDependencies":{"@babel/core":"^7.16.7","@babel/plugin-transform-runtime":"^7.16.10","@babel/preset-env":"^7.16.11","@babel/preset-typescript":"^7.16.7","@babel/runtime-corejs3":"^7.16.8","@microsoft/api-extractor":"^7.19.4","@types/chai":"^4.3.0","@types/mocha":"^9.1.0","babel-loader":"^8.2.3","chai":"^4.3.6","core-js":"3","karma":"^6.3.16","karma-chai":"^0.1.0","karma-chrome-launcher":"^3.1.0","karma-mocha":"^2.0.1","karma-typescript":"^5.5.3","mocha":"^9.2.0","typescript":"^4.5.4","webpack":"^5.65.0","webpack-cli":"^4.9.1","white-brand-cli":"^1.2.3"},"browserslist":["chrome >= 58","ios >= 11","safari >= 11","firefox >= 56"],"gitHead":"2c36c6e7ead8f983904c9b39611165128061295e","_id":"agora-rte-extension@1.2.3","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-k3yNrYVyzJRoQJjaJUktKUI1XRtf8J1XsW8OzYKFqGlS8WQRMsES1+Phj2rfuEriiLObfuyuCimG6KHQCt5tiw==","shasum":"979b96df0146300296f9f37212ffa67656c698ef","tarball":"http://123.232.10.234:8212/nexus/content/groups/npm-public/agora-rte-extension/-/agora-rte-extension-1.2.3.tgz","fileCount":6,"unpackedSize":99303,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDoH84738Jeqa3Eh8+C10t45gTdd50mzsMQOUhVQksVFgIgXd1XFuQ8yxd77cpqJHcKq3koQ1I8GLyjC3gD5M3JJf0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjaNfgACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrwHA/8DRfXNk3BunWmgArQMh5ESVpPycBPZ5rHAGdFfTscXXgjl92G\r\nc1aAST8OvQv7bvRqnWUDRU8qvDF/GcYfYAyh1L7kGx9qSkB3+QOqm/Dnlj6d\r\n5BhcH97/ggLCZ51HE4NO6RvZVtgXAluT++AH3Dat3x6nq8KcBf4R8n0K/Xp/\r\nqcVe5XKi4g7KGpdLk/2IXwSNfMVVzMzw8ZNDZgSjnvTOFuCQ9lA/tZhw57je\r\nl8T/+nf/WEWOaEPkkfhnPrk1Ur3jRR4ywaayzTFaSFA8hK1q9n7IwMPm1HAj\r\n3nIOpni3YNeYrTPQ9h4ve/jK/TiA65sXHNtG7RocRCMglsQ5XvbiUM3OEBxt\r\nTsF8POZ/ujinWTYTjroi/Cm/JnJMl1O59fgVyAG62T+vOjcmNqbmzfhgL33W\r\nuNhOvDO4blSFv1fMTQ+0mvwl07IxORIsX6OHzjxQJQd1ph0mODPy6m2WiPp/\r\ndDJX7J8XHwDYkewuf0E7RZACF1T/59HtDUEOZyL6CWv38yxq4UoVpCMAGeF2\r\nnrPIllPsw67dymSC6lEL4WYEwFcTfO7ybCwFUA9h+497o84em/gdhINxVleO\r\nvLS4cS3b9ppq1jmQ/D5INdJauYCRzf0QutadRS18zVnJNbJL/b5dxTlYE9+s\r\ns3ZuekqNUpRlccwn9BmTTbiJKf7nKJ7KKL8=\r\n=i+EZ\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"anonymous","email":"Sales@agora.io"},"directories":{},"maintainers":[{"name":"anonymous","email":"Sales@agora.io"},{"name":"anonymous","email":"sdk-jira-robot@agora.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/agora-rte-extension_1.2.3_1667815392604_0.1308254511664566"},"_hasShrinkwrap":false,"contributors":[]},"1.2.4":{"name":"agora-rte-extension","version":"1.2.4","description":"Agora RTE Extension","main":"./dist/agora-rte-extension.js","typings":"./dist/agora-rte-extension.d.ts","scripts":{"type":"api-extractor run --local --verbose","build":"rm -rf ./lib ./dist && webpack && tsc && yarn type","dev":"webpack --mode=development --watch","test":"npx karma start","wb":"rm -rf ./dist-white-brand && yarn build && wb"},"author":"","license":"ISC","devDependencies":{"@babel/core":"^7.16.7","@babel/plugin-transform-runtime":"^7.16.10","@babel/preset-env":"^7.16.11","@babel/preset-typescript":"^7.16.7","@babel/runtime-corejs3":"^7.16.8","@microsoft/api-extractor":"^7.19.4","@types/chai":"^4.3.0","@types/mocha":"^9.1.0","babel-loader":"^8.2.3","chai":"^4.3.6","core-js":"3","karma":"^6.3.16","karma-chai":"^0.1.0","karma-chrome-launcher":"^3.1.0","karma-mocha":"^2.0.1","karma-typescript":"^5.5.3","mocha":"^9.2.0","typescript":"^4.5.4","webpack":"^5.65.0","webpack-cli":"^4.9.1","white-brand-cli":"^1.2.3"},"browserslist":["chrome >= 58","ios >= 11","safari >= 11","firefox >= 56"],"gitHead":"879c58719e1b4c056aec84469e89cf06f5207652","_id":"agora-rte-extension@1.2.4","_nodeVersion":"18.16.0","_npmVersion":"9.5.1","dist":{"integrity":"sha512-0ovZz1lbe30QraG1cU+ji7EnQ8aUu+Hf3F+a8xPml3wPOyUQEK6CTdxV9kMecr9t+fIDrGeW7wgJTsM1DQE7Nw==","shasum":"6ba1742b16191ea9eba69376b067d4b2e0fe9d34","tarball":"http://123.232.10.234:8212/nexus/content/groups/npm-public/agora-rte-extension/-/agora-rte-extension-1.2.4.tgz","fileCount":6,"unpackedSize":133768,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE355Uh2MMRcSgWFaT6lFxcAAq/9VQtWjqFLO6i94nI2AiEA9WOnpcdlpqFLJzvRvRbbauCi7fjnba7kuznmeQHwcQ4="}]},"_npmUser":{"name":"anonymous","email":"agorabuilder@shengwang.cn"},"directories":{},"maintainers":[{"name":"anonymous","email":"agorabuilder@shengwang.cn"},{"name":"anonymous","email":"sdk-jira-robot@agora.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/agora-rte-extension_1.2.4_1702035301934_0.39300032744897395"},"_hasShrinkwrap":false,"contributors":[]}},"dist-tags":{"latest":"1.2.4"},"name":"agora-rte-extension","time":{"created":"2022-03-17T09:11:02.042Z","1.0.22":"2022-03-17T09:10:37.881Z","modified":"2023-12-08T11:37:18.652Z","1.0.23":"2022-03-18T02:50:31.843Z","1.1.0":"2022-08-10T09:28:05.870Z","1.2.3":"2022-11-07T10:03:12.789Z","1.2.4":"2023-12-08T11:35:02.120Z"},"readme":"[TOC]\n\n# Agora-RTE-Extension\n\n## Introduction\n\nAgora RTE Extension provides the ability for extension developer to interact with Agora RTC SDK NG's VideoTrack and\nAudioTrack object, making video and audio processing possible.\n\nBy receiving `MediaStreamTrack` or `AudioNode` as input, running custom processing procedure such as `WASM` module\nor `AudioWorkletNode`, and finally generating processed `MediaStreamTrack` or `AudioNode`, it will construct a media\nprocessing pipeline to allow custom media processing provided by developers.\n\n## How Extension and Processor Interacts With Agora RTC SDK NG\n\nA `Processor` basically connects to other `Processor`s with `pipe` method:\n\n```typescript\nprocessorA.pipe(processorB);\n```\n\nThe `pipe` method returns the `Processor` passed as parameter itself, making a function chaining style:\n\n```typescript\n//processor actually is processorB\nconst processor = processorA.pipe(processorB);\n\n//function chaining \nprocessorA.pipe(processorB).pipe(processorC);\n```\n\nOn AgoraRTC SDK NG v4.10.0 and afterwards, the `ILocalVideoTrack` and `ILocalAudioTrack` objects also have `pipe` method\non it:\n\n```typescript\nconst localVideoTrack = AgoraRTC.createCameraVideoTrack();\n\nlocalVideoTrack.pipe(videoProcessor);\n```\n\nTo make the processed media rendering locally and transmitting through WebRTC, `processorDestination` property\non `ILocalVideoTrack` and `ILocalAudioTrack` has to be the final processor through the pipeline:\n\n```typescript\nlocalVideoTrack.pipe(videoProcessor).pipe(localVideoTrack.processorDestination);\n```\n\n----\nAn `Extension` receives injected utility functionality such as `logger` and `reporter`\nduring `AgoraRTC.registerExtensions` function call:\n\n```typescript\nAgoraRTC.registerExtensions([videoExtension, audioExtension]);\n```\n\n`Extension` also provides `createProcessor` method for constructing `Processor` instance:\n\n```typescript\nconst videoProcessor = videoExtension.createProcessor();\n```\n\n----\nWrap it up:\n\n```typescript\nconst videoExtension = new VideoExtension();\nAgoraRTC.registerExtensions([videoExtension]);\n\nconst localVideoTrack = await AgoraRTC.createCameraVideoTrack();\nconst videoProcessor = videoExtension.createProcessor();\n\nlocalVideoTrack.pipe(videoProcessor).pipe(localVideoTrack.processorDestination);\n```\n\n## Extension and Processor APIs for extension developers\n\n### Extension\n\n#### `Extension._createProcessor`\n\nAbstract class `Extension` has one abstract method `_createProcessor` needs to be implemented:\n\n```typescript\nabstract class Extension<T extends BaseProcessor> {\n  abstract _createProcessor(): T;\n}\n```\n\nWhen implemented, it should return a `VideoProcessor` or `AudioProcessor` instance.\n\n`AgoraRTC` developer calling `extension.createProcessor()` will return the processor returned by `_createProcessor`.\n\n#### `Extension.setLogLevel`\n\nAbstract class `Extension` has one static method `setLogLevel` :\n\n```typescript\nabstract class Extension<T extends BaseProcessor> {\n  public static setLogLevel(level: number): void\n}\n```\n\n`AgoraRTC` developer calling `Extension.setLogLevel(level)`  will set the output log level of the extension.\n\n#### `Extension.checkCompatibility`\n\nAbstract class `Extension` has one optional abstract public method `checkCompatibility` could be implemented:\n\n```typescript\nabstract class Extension<T extends BaseProcessor> {\n  public abstract checkCompatibility?(): boolean;\n}\n```\n\nWhen implemented, it should return a `boolean` value indicating whether extension could be run inside current browser environment.\n\n### VideoProcessor\n\n#### `VideoProcessor.name`\n\nAbstract property `name` on `VideoProcessor` has to be implemented in order to name processor:\n\n```typescript\nabstract name: string;\n```\n\n#### `VideoProcessor.onPiped`\n\nAbstract optional method `onPiped` could be implemented in order to be notified when processor connected to a pipeline\nwith `ILocalVideoTrack` as it's source:\n\n```typescript\nabstract onPiped?(context: IProcessorContext): void;\n```\n\nIt will only be called when an `ILocalVideoTrack` object from `AgoraRTC` was connected to the pipeline, or when the\nprocessor was connected to a pipeline with `ILocalVideoTrack` as its source.\n> Pipeline without an `ILocalVideoTrack` as it's source, `onPiped` method will not be called for processors belonging to this pipeline until an `ILocalVideoTrack` connected to it.\n\n```typescript\nvideoTrack.pipe(processor);//will be called\n\nprocessorA.pipe(processorB);//will NOT be called\nvideoTrack.pipe(processorA);//will be called for both processorA and processorB\n```\n\n#### `VideoProcessor.onUnpiped`\n\nAbstract optional method `onUnpiped` could be implemented in order to be notified when processor disconnected to a\npipeline:\n\n```typescript\nabstract onUnPiped?(): void;\n```\n\n#### `VideoProcessor.onTrack`\n\nAbstract optional method `onTrack` could be implemented in order to be notified when the previous processor\nor `ILocalVideoTrack` feeds output `MediaStreamTrack` to the current processor:\n\n```typescript\nabstract onTrack?(track: MediaStreamTrack, context: IProcessorContext): void;\n```\n\n#### `VideoProcessor.onEnableChange`\nAbstract optional method `onEnableChange` could be implemented in order to be notified when processor's `_enabled`\nproperty has changed:\n```typescript\nabstract onEnableChange?(enabled: boolean): void | Promise<void>;\n```\n`AgoraRTC` developer calling `processor.enable()` and `processor.disable()` may change `_enabled` property and consequently calling `onEnableChange`, but enabling an already enabled processor or disabling an already disabled processor will not.\n#### `VideoProcessor._enabled`\nproperty `_enabled` describes enabled status of the current processor.\n\n```typescript\nprotected _enabled :boolean = true;\n```\n\nIt defaults to `true` ,  but could be change inside processor constructor:\n\n```typescript\nclass CustomProcessor extends VideoProcessor {\n  public constructor(){\n    this._enabled = false;\n  }\n}\n```\n\nOther than that, it should not be modified directly.\n\n#### `VideoProcessor.enabled`\n\nGetter `enabled` describes enabled status of the current processor.\n\n```typescript\npublic get enabled(): boolean;\n```\n\n#### `VideoProcessor.inputTrack`\n\nOptional property `inputTrack` will be setted when the previous processor or `ILocalVideoTrack` feeds output track on the current processor:\n\n```typescript\nprotected inputTrack?:MediaStreamTrack;\n```\n\n#### `VideoProcessor.outputTrack`\n\nOptional property `outputTrack` will be setted when the current processor calling `output()`  to generate output `MediaStreamTrack`:\n\n```typescript\nprotected outputTrack?:MediaStreamTrack;\n```\n\n#### `VideoProcessor.ID`\n\nReadonly property `ID` is a random ID for the current processor instance:\n\n```typescript\npublic readonly ID:string;\n```\n\n#### `VideoProcessor.kind`\n\nGetter `kind` describes current processor's kind, which is either `audio` or `video`:\n\n```typescript\npublic get Kind():'video' | 'audio';\n```\n\n#### `VideoProcessor.context`\n\nOptional property `context` is the current processor's `IProcessorContext` :\n\n```typescript\nprotected context?: IProcessorContext;\n```\n\n#### `VideoProcessor.output`\nmethod `output` should be called when processor was about to generate processed `MediaStreamTrack`:\n```typescript\noutput(track: MediaStreamTrack, context: IProcessorContext): void;\n```\n\n\n### AudioProcessor\n\n`AudioProcessor` shares almost all the property/methods with `VideoProcessor`, with 1 exception that `AudioProcessor`'s processorContext is `IAudioProcessorContext`; and with several additions:\n\n#### `AudioProcessor.onNode`\n\nAbstract optional method `onNode` could be implemented in order to be notified when the previous processor\nor `ILocalAudioTrack` feeds output `AudioNode` to the current audio processor:\n\n```typescript\nabstract onNode?(node: AudioNode, context: IAudioProcessorContext): void;\n```\n\n#### `AudioProcessor.output`\n\nmethod `output` should be called when audio processor was about to generate processed `MediaStreamTrack` or `AudioNode`:\n\n```typescript\noutput(track: MediaStreamTrack | AudioNode, context: IProcessorContext): void;\n```\n\n#### `AudioProcessor.inputNode`\n\nOptional property `inputNode` will be setted when the previous processor or `ILocalAudioTrack` feeds output audio node on the current processor:\n\n```typescript\nprotected inputNode?:AudioNode;\n```\n\n####`AudioProcessor.outputNode`\n\nOptional property `outputNode` will be setted when the current processor calling `output()`  to generate output `AudioNode`:\n\n```typescript\nprotected outputNode?:AudioNode;\n```\n\n### ProcessorContext\n\n`ProcessorContext` provides the ability to interact with the process pipeline's source which is `ILocalVideoTrack` or `ILocalAudioTrack`, and possiblly affecting media capture.\n\n`ProcessorContext` will be assgined to the processor once the processor was connected with a pipeline has `ILocalVideoTrack` or `ILocalAudioTrack` as it's source.\n\n#### `ProcessorContext.requestApplyConstraints`\n\nMethod `requestApplyConstraints` provides the ability to change the `MediaTrackConstraints` used for getting pipeline source's `MediaStreamTrack` :\n\n```typescript\npublic requestApplyConstraints(constraints: MediaTrackConstraints, processor: IVideoProcessor): Promise<void>;\n```\n\nConstraints supplied in `requestApplyConstraints` will be merged with the original constraints used for creating `ICameraVideoTrack`. If several processors inside the same pipline all request to apply additional constraints, the pipe order will be considered to make the final constraints.\n\n#### `ProcessorContext.requestRevertConstraints`\n\nMethod`requestRevertConstraints` provides the ability to revert previous constraints request using `requestApplyConstraints`:\n\n```typescript\npublic requestRevertConstraints(processor: IVideoProcessor):void;\n```\n### AudioProcessorContext\n`AudioProceesorContext` inherits all the methods provided by `ProcessorContext`, with one addition `getAudioContext`.\n#### `getAudioContext`\nMethod `getAudioContext` provides the ability to get `AudioContext` object of the current pipeline:\n```typescript\npublic getAudioContext(): AudioContext;\n```\n### Ticker\n`Ticker` is a utitly class that helps with periodic tasks.\n\n`Ticker` provides simple interface for choosing periodic task implementation, add/remove task and start/stop task.\n\n#### `new Ticker`\n\n`Ticker` constructor requires ticker type and tick interval as parameter:\n\n```typescript\nclass Ticker{\n  public constructor(type:\"Timer\" | \"RAF\" | \"Oscillator\", interval: number):Ticker; \n}\n```\n\n`Ticker` has three implementation to choose from:\n\n- `Timer`: uses `setTimeout` as the internal timer\n- `RAF`: uses `requestAnimationFrame` as the internal timer. Most users should choose this type of `Ticker` as it provides best rendering performance\n- `Osciilator`: uses `WebAudio`'s `OscillatorNode` as the internal timer. Can still keep running even the browser tab is not focused.\n\n`interval` sets the time between the next callback. It is a best effort timing not an exactly timing.\n\n#### `Ticker.add`\n\n`Ticker.add` adds a task to the ticker:\n\n```typescript\npublic add(fn: Function): void;\n```\n\n#### `Ticker.remove`\n\n`Ticker.remove` removes the task added to the ticker previously:\n\n```typescript\npublic remove():void;\n```\n\n#### `Ticker.start`\n\n`Ticker.start` starts the already add task with settled ticker type and interval:\n\n```typescript\npublic start():void;\n```\n\n####`Ticker.stop`\n\n`Ticker.stop` stops the previously add task:\n\n```typescript\npublic stop():void;\n```\n\n### Logger\n\n`Logger` is a global utility singleton that helps the logging. It provides four log levels to log to the console.\n\nWhen the extension was registered with `AgoraRTC.registerExtension`, and the `AgoraRTC` developer choose to upload log, extension logs loged with `Logger` will also been uploaded.\n\n#### `Logger.info`, `Logger.debug`, `Logger.warning`, `Logger.error`\n\nTheses methods log with different level:\n\n```typescript\npublic info(...args:any[]):void;\npublic debug(...args:any[]):void;\npublic warning(...args:any[]):void;\npublic error(...args:any[]):void;\n```\n\n#### `Logger.setLogLevel`\n\n`Logger.setLogLevel` set the output log level of the extension.\n\n```typescript\npublic setLogLevel(level: number): void;\n```\n\n### Reporter\n\n`Reporter` is a global utility singleton that helps with event reporting to Agora analysis platform:\n\n#### `Reporter.reportApiInvoke`\n\n`Repoter.reportApiInvoke` can report public API calling event to Agora analysis platform:\n\n```typescript\ninterface ReportApiInvokeParams {\n  name: string;\n  options: any;\n  reportResult?: boolean;\n  timeout?: number;\n}\ninterface AgoraApiExecutor<T> {\n  onSuccess: (result: T) => void;\n  onError: (err: Error) => void;\n}\n\npublic reportApiInvoke<T>(params: ReportApiInvokeParams): AgoraApiExecutor<T>;\n```\n\nIt accepts `ReportAPIInvokeParams` as parameter:\n\n- `ReportAPIInvokeParams.name`: the name of the public API\n- `options`: the arguments, or any other options related to this API invoke\n- `reportResult`: whether to report API invoke result\n- `timeout`: specifies how long it is `Reporter` thinks the API calling is timeout.\n\nIt reports two callback methods, `onSuccess` and `onEror`, which can be called when the API calling success or failed accordingly.\n\n## Extending Extension\nExtending an `Extension` is fairly straightforward as we only need to implement `_createProcessor` abstract method:\n\n```typescript\nimport {Extension} from 'agora-rte-extension'\n\nclass YourExtension extends Extension<YourProcessor> {\n  protected _createProcessor(): YourProcessor {\n    return new YourProcessor(); \n  }\n}\n```\n\n## Extending Processor\n\nThere are several abstract methods could be implemented and they will be called at the different timing of the processing pipeline.\n\n### `onTrack` and `onNode`\n\n`onTrack` and `onNode` method will be called when the previous processor/LocalTrack generated output. They are the main entry point for us to process media:\n\n```typescript\nclass CustomVideoProcessor extends VideoProcesor {\n  protected onTrack(track: MediaStreamTrack, context: IProcessorContext){}\n}\n\nclass CustomAudioProcessor extends AudioProcessor {\n  protected onNode(node: AudioNode, context: IAudioProcessorContext){} \n}\n```\n\n### Video Processing\n\nTypically, doing video processing requests extracting each video frame as `ImageData` or `ArrayBuffer`.\n\nAs for now `InsertableStream` have not been globally supported by browser vendors yet, we  use `canvas` API here to extract video frame data:\n\n```typescript\nclass CustomVideoProcessor extends VideoProcessor {\n  private canvas: HTMLCanvasElement;\n  private ctx: CanvasRenderingContext2D;\n  private videoElement:HTMLVideoElement;\n \n  constructor(){\n    super();\n    \n   \t//initialize canvas element\n    this.canvas = document.createElement('canvas');\n    this.canvas.width = 640;  // canvas's width and height will be your output video streams video dimension \n    this.canvas.height = 480;\n    this.ctx = this.canvas.getContext('2d')!;\n    \n    //initialize video element\n    this.videoElement = document.createElement('video');\n    this.videoElement.muted = true;\n  }\n  \n  onTrack(track:MediaStreamTrack, context: IProcessorContext){\n    //loding MediaStreamTrack into HTMLVideoElement\n    this.videoElement.srcObject = new MediaStream([track]);\n    this.videoElement.play();\n    \n    //extract ImageData\n    this.ctx.drawImage(this.videoElement, 0, 0);\n    const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);\n  }\n}\n```\n\nAs we can see here video frame data was only been eatracted once inside the `onTrack` method, but we need to run it inside a constant loop to ouput constant frame rate.  Luckily, we can leverage `requestAnimationFrame`to do this for us:\n\n```typescript\nclass CustomVideoProcessor extends VideoProcessor {\n  onTrack(track:MediaStreamTrack, context: IProcessorContext){\n    this.videoElement.srcObject = new MediaStream([track]);\n    this.videoElement.play();\n    \n    this.loop();\n  }\n  \n  loop(){\n    this.ctx.drawImage(this.videoElement, 0, 0);\n    const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);\n    \n    this.process(imageData);\n    \n    requestAnimationFrame(()=>this.loop());\n  }\n  \n  process(){\n    //your custom video processing logic\n  }\n}\n```\n### Generating Video Processing Output\n\nWhen we've done video processing, `Processor`'s `output` method should be used to generate video putput. `output` methods requires `MediaStreamTrack` and `IProcessorContext` as it's parameter, so we will need to assemble video buffer into a `MediaStreamTrack`.\n\nUsually `canvas`'s `captureStream` helps us with it:\n\n```typescript\nclass CustomVideoProcessor extends VideoProcessor {\n  doneProcessing(){\n    // making an MediaStream from canvas and get MediaStreamTrack\n    const msStream = this.canvas.captureStream(30);\n    const outputTrack = msStream.getVideoTracks()[0];\n    \n    //output processed track\n    if(this.context){\n      this.output(outputTrack, this.context);\n    }\n  }\n}\n```\n\n### Audio Processing\nAudio processing differs with video processing as that audio processing typically requires `WebAudio`'s capability to do custom audio processing.\n\nWe can implement `onNode` method to receive notification when the previous audio processor/`ILocalAudioTrack` generated output AudioNode:\n\n```typescript\nclass CustomAudioProcessor extends AudioProcessor {\n  onNode(node: AudioNode, context: IAudioProcessorContext) {}\n}\n```\nWe can call `IAudioProcessorContext.getAudioContext` to get `AudioContext` to create our own audioNode:\n```typescript\nclass CustomAudioProcessor extends AudioProcessor {\n  onNode(node: AudioNode, context: IAudioProcessorContext) {\n    //accuire AudioContext\n    const audioContext = context.getAudioContext();\n    \n    //create custom gaiNode\n    const gainNode = audioContext.createGain();\n  }\n}\n```\nAlso don't forget to connect the input audio node to our custom audio node:\n```typescript\nclass CustomAudioProcessor extends AudioProcessor {\n  onNode(node: AudioNode, context: IAudioProcessorContext) {\n    const audioContext = context.getAudioContext();\n\n    const gainNode = audioContext.createGain();\n    \n    //connect\n    node.connect(gainNode);\n  }\n}\n```\n### Generating Audio Processing Output\nWhen we've done audio processing, Processor's `output` method should be used to generate audio output. `output` methods requires `MediaStreamTrack`/`AudioNode` and `IAudioProcessorContext` as its parameter:\n```typescript\n\nclass CustomAudioProcessor extends AudioProcessor {\n  onNode(node: AudioNode, context: IAudioProcessorContext) {\n    const audioContext = context.getAudioContext();\n\n    const gainNode = audioContext.createGain();\n\n    node.connect(gainNode);\n    \n    //output\n    this.output(gainNode, context);\n  }\n}\n```\n## Testing\nWIP\n## Best Practices\n\n### Audio Graph Connecting\n\n### Handling Enable and Disable\n\n### Error Handling","users":{}}