├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── interop-tests.yml │ └── test.yml ├── .gitignore ├── .npmrc ├── .stylelintrc ├── AUTHORS ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── google1b7eb21c5b594ba0.html ├── index.html ├── package.json ├── release ├── ChromeWebRTCNetworkLimiterExtension_0.1.zip ├── ChromeWebRTCNetworkLimiterExtension_0.2.1.1.zip └── desktopCaptureExtension.zip ├── src ├── content │ ├── capture │ │ ├── canvas-filter │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── canvas-pc │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── canvas-record │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── canvas-video │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── video-contenthint │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── video-pc │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── video-video │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ └── worker-process │ │ │ ├── css │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ ├── main.js │ │ │ └── worker.js │ ├── datachannel │ │ ├── basic │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ └── test.js │ │ ├── channel │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ └── test.js │ │ ├── datatransfer │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ └── test.js │ │ ├── filetransfer │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ └── test.js │ │ └── messaging │ │ │ ├── index.html │ │ │ ├── main.css │ │ │ └── main.js │ ├── devices │ │ ├── input-output │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ └── test.js │ │ └── multi │ │ │ ├── audio │ │ │ └── audio.mp3 │ │ │ ├── css │ │ │ └── main.css │ │ │ ├── images │ │ │ └── poster.jpg │ │ │ ├── index.html │ │ │ ├── js │ │ │ └── main.js │ │ │ └── video │ │ │ ├── chrome.mp4 │ │ │ ├── chrome.ogv │ │ │ └── chrome.webm │ ├── extensions │ │ ├── multipleroutes │ │ │ ├── img │ │ │ │ └── netli_1280.png │ │ │ └── src │ │ │ │ ├── README.md │ │ │ │ ├── _locales │ │ │ │ └── en │ │ │ │ │ └── messages.json │ │ │ │ ├── img │ │ │ │ ├── icon_128.png │ │ │ │ └── icon_16.png │ │ │ │ ├── manifest.json │ │ │ │ ├── options.html │ │ │ │ └── options.js │ │ └── svc │ │ │ ├── css │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ └── main.js │ ├── getusermedia │ │ ├── audio │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── canvas │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── exposure │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── filter │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── getdisplaymedia │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── gum │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ └── test.js │ │ ├── pan-tilt-zoom │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── record │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── resolution │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ └── test.js │ │ ├── source │ │ │ └── index.html │ │ └── volume │ │ │ ├── css │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ ├── main.js │ │ │ └── soundmeter.js │ ├── insertable-streams │ │ ├── audio-processing │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ └── worker.js │ │ ├── endtoend-encryption │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ ├── test.js │ │ │ │ ├── videopipe.js │ │ │ │ └── worker.js │ │ ├── video-analyzer │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── video-crop │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── main.js │ │ │ │ └── worker.js │ │ ├── video-processing │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ ├── camera-source.js │ │ │ │ ├── canvas-source.js │ │ │ │ ├── canvas-transform.js │ │ │ │ ├── main.js │ │ │ │ ├── peer-connection-pipe.js │ │ │ │ ├── peer-connection-sink.js │ │ │ │ ├── peer-connection-source.js │ │ │ │ ├── pipeline.js │ │ │ │ ├── simple-transforms.js │ │ │ │ ├── video-mirror-helper.js │ │ │ │ ├── video-sink.js │ │ │ │ ├── video-source.js │ │ │ │ ├── webcodec-transform.js │ │ │ │ └── webgl-transform.js │ │ ├── video │ │ │ └── index.html │ │ └── webgpu │ │ │ ├── css │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ ├── main.js │ │ │ ├── multi_video_main.js │ │ │ ├── multi_video_worker.js │ │ │ └── multi_video_worker_manager.js │ └── peerconnection │ │ ├── audio │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── bandwidth │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ └── main.js │ │ ├── change-codecs │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── channel │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── constraints │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ └── main.js │ │ ├── create-offer │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ └── main.js │ │ ├── dtmf │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── endtoend-encryption │ │ └── index.html │ │ ├── multiple-relay │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ └── main.js │ │ ├── multiple │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── munge-sdp │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── negotiate-timing │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── pc1 │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── per-frame-callback │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ └── main.js │ │ ├── perfect-negotiation │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── peer.js │ │ ├── pr-answer │ │ ├── index.html │ │ └── js │ │ │ └── main.js │ │ ├── restart-ice │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── states │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── trickle-ice │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── upgrade │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── test.js │ │ ├── video-analyzer │ │ └── index.html │ │ ├── webaudio-input │ │ ├── audio │ │ │ └── Shamisen-C4.wav │ │ ├── css │ │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ │ ├── main.js │ │ │ └── webaudioextended.js │ │ └── webaudio-output │ │ ├── css │ │ └── main.css │ │ ├── index.html │ │ └── js │ │ └── main.js ├── css │ └── main.css ├── images │ └── webrtc-icon-192x192.png ├── js │ ├── lib │ │ └── ga.js │ ├── third_party │ │ ├── graph.js │ │ ├── streamvisualizer.js │ │ └── webgl_teapot │ │ │ ├── cameracontroller.js │ │ │ ├── demo.js │ │ │ ├── images │ │ │ ├── bump.jpg │ │ │ ├── skybox-negx.jpg │ │ │ ├── skybox-negy.jpg │ │ │ ├── skybox-negz.jpg │ │ │ ├── skybox-posx.jpg │ │ │ ├── skybox-posy.jpg │ │ │ └── skybox-posz.jpg │ │ │ ├── matrix4x4.js │ │ │ ├── teapot-streams.js │ │ │ ├── webgl-debug.js │ │ │ └── webgl-utils.js │ └── videopipe.js └── video │ ├── chrome.mp4 │ ├── chrome.webm │ └── mixed-content.webm └── test ├── download-browsers.js ├── interop └── connection.test.js ├── steps.js ├── webdriver.js └── webrtcclient.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'extends': 'google', 3 | 'parserOptions': { 4 | 'ecmaVersion': 2017, 5 | 'sourceType': 'module', 6 | }, 7 | 'env': { 8 | 'browser': true, 9 | 'es6': true, 10 | 'node': true, 11 | 'jest': true 12 | }, 13 | 'rules': { 14 | 'max-len': 'off', 15 | 'require-jsdoc': 'off', 16 | 'arrow-parens': 'off', 17 | 'comma-dangle': 'off', 18 | 'no-throw-literal': 'off', 19 | 'camelcase': 'off', 20 | 'prefer-rest-params': 'off', 21 | 'no-invalid-this': 'off', 22 | 'eol-last': 'off', 23 | 'no-undef': 2, 24 | }, 25 | "globals": { 26 | "adapter": true, 27 | "browserSupportsIPHandlingPolicy": true, 28 | "browserSupportsNonProxiedUdpBoolean": true, 29 | "chrome": true, 30 | "ga": true, 31 | "getPolicyFromBooleans": true, 32 | "importScripts": true, 33 | // From WebGPU specification 34 | "GPUBufferUsage": true, 35 | "GPUTextureUsage": true, 36 | // From Streams specification 37 | "TransformStream": true, 38 | // From WebCodec specification 39 | "AudioData": true, 40 | "AudioEncoder": true, 41 | "AudioDecoder": true, 42 | "VideoFrame": true, 43 | "VideoEncoder": true, 44 | "VideoDecoder": true, 45 | }, 46 | "plugins": ["jest"] 47 | }; 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | # Please read first! 10 | Please use [discuss-webrtc](https://groups.google.com/forum/#!forum/discuss-webrtc) for general technical discussions and questions. 11 | If you have found an issue/bug with the native `libwebrtc` SDK or a browser's behaviour around WebRTC please create an issue in the relevant bug tracker. You can find more information on how to submit a bug and do so in the right place [here](https://webrtc.googlesource.com/src/+/refs/heads/main/docs/bug-reporting.md) 12 | 13 | - [ ] I understand that issues created here are _only_ relevant to the samples in this repo - not browser or SDK bugs 14 | - [ ] I have provided steps to reproduce 15 | - [ ] I have provided browser name and version 16 | - [ ] I have provided a link to the sample here or a modified version thereof 17 | 18 | **Note: If the checkboxes above are not checked (which you do after the issue is posted), the issue will be closed.** 19 | 20 | ## Browser affected 21 | 22 | **Browser name including version (e.g. Chrome 64.0.3282.119)** 23 | 24 | 25 | ## Description 26 | 27 | 28 | ## Steps to reproduce 29 | 30 | 31 | ## Expected results 32 | 33 | 34 | ## Actual results 35 | 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Please use the discuss-webrtc mailing list for general questions 4 | url: https://groups.google.com/g/discuss-webrtc 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Description** 2 | 3 | 4 | **Purpose** 5 | -------------------------------------------------------------------------------- /.github/workflows/interop-tests.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | - cron: "30 5 * * *" 4 | 5 | jobs: 6 | interop: 7 | runs-on: ubuntu-22.04 8 | timeout-minutes: 5 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | browserA: [chrome, firefox] 13 | browserB: [firefox, chrome] 14 | bver: [unstable] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-node@v4 18 | - run: npm install 19 | - run: sudo rm /usr/bin/chromedriver /usr/bin/geckodriver # remove preinstalled github chromedriver/geckodriver from $PATH 20 | - run: Xvfb :99 & 21 | - run: BROWSER_A=${{matrix.browserA}} BROWSER_B=${{matrix.browserB}} BVER=${{matrix.bver}} DISPLAY=:99.0 node test/download-browsers.js 22 | - run: BROWSER_A=${{matrix.browserA}} BROWSER_B=${{matrix.browserB}} BVER=${{matrix.bver}} DISPLAY=:99.0 node_modules/.bin/jest --retries=3 test/interop/ 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: lint-and-test 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-node@v4 11 | - run: npm install 12 | - run: npm run eslint 13 | - run: npm run stylelint 14 | test: 15 | needs: lint 16 | runs-on: ubuntu-22.04 17 | timeout-minutes: 5 18 | strategy: 19 | matrix: 20 | browser: [chrome] 21 | version: [stable] 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: actions/setup-node@v4 25 | - run: npm install 26 | - run: sudo rm /usr/bin/chromedriver # remove preinstalled github chromedriver from $PATH 27 | - run: Xvfb :99 & 28 | - run: BROWSER=${{matrix.browser}} BVER=${{matrix.version}} DISPLAY=:99.0 npm run jest -- --retries=3 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | browsers* 2 | .eslintcache 3 | firefox-*.tar.bz2 4 | node_modules 5 | .DS_Store 6 | validation-report.json 7 | validation-status.json 8 | .idea 9 | firefox_profile/* 10 | tests_output 11 | *.log 12 | *~ 13 | \#*# -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-recommended" 3 | } -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The WebRTC Project Authors 2 | The Chromium Authors 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # WebRTC welcomes patches/pulls for features and bug fixes! 2 | 3 | For contributors external to Google, follow the instructions given in the 4 | [Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual). 5 | In all cases, contributors must sign a contributor license agreement before a contribution can be accepted. 6 | Please complete the agreement for an [individual](https://developers.google.com/open-source/cla/individual) or 7 | a [corporation](https://developers.google.com/open-source/cla/corporate) as appropriate. 8 | 9 | If you plan to add a new sample or make significant changes to an existing sample, we recommend that you start by creating 10 | a [new issue](https://github.com/webrtc/samples/issues/new) where we can discuss the details. 11 | 12 | # How to start developing a patch, new feature or bug fix 13 | ## Clone the repo in desired folder 14 | ```bash 15 | git clone https://github.com/webrtc/samples.git 16 | ``` 17 | 18 | ## Install npm dependencies 19 | ```bash 20 | npm install 21 | ``` 22 | 23 | ## Start web server for development 24 | ```bash 25 | npm start 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, The WebRTC project authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the 13 | distribution. 14 | 15 | * Neither the name of Google nor the names of its contributors may 16 | be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebRTC Code Samples 2 | 3 | This is a repository for the WebRTC JavaScript code samples. All of the samples can be tested from [webrtc.github.io/samples](https://webrtc.github.io/samples). 4 | 5 | To run the samples locally 6 | ``` 7 | npm install && npm start 8 | ``` 9 | and open your browser on the page indicated. 10 | 11 | ## Contributing 12 | We welcome contributions and bugfixes. Please see [CONTRIBUTING.md](https://github.com/webrtc/samples/blob/gh-pages/CONTRIBUTING.md) for details. 13 | 14 | ## Bugs 15 | 16 | If you encounter a bug or problem with one of the samples, please submit a 17 | [new issue](https://github.com/webrtc/samples/issues/new) so we know about it and can fix it. 18 | 19 | Please avoid submitting issues on this repository for general problems you have with WebRTC. If you have found a bug in 20 | the WebRTC APIs, please see [webrtc.org/bugs](https://webrtc.org/support/bug-reporting) for how to submit bugs on the affected browsers. 21 | If you need support on how to implement your own WebRTC-based application, please see the 22 | [discuss-webrtc](https://groups.google.com/forum/#!forum/discuss-webrtc) Google Group. 23 | 24 | -------------------------------------------------------------------------------- /google1b7eb21c5b594ba0.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google1b7eb21c5b594ba0.html -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrtc-samples", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Project checking for WebRTC GitHub samples repo", 6 | "keywords": [ 7 | "webrtc" 8 | ], 9 | "homepage": "https://webrtc.github.io/samples/", 10 | "bugs": { 11 | "url": "https://github.com/webrtc/samples/issues" 12 | }, 13 | "license": "BSD-3-Clause", 14 | "author": "The WebRTC project authors", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/webrtc/samples.git" 18 | }, 19 | "scripts": { 20 | "start": "http-server . -c-1", 21 | "test": "npm run eslint && npm run stylelint", 22 | "eslint": "eslint 'test/**.js' 'src/content/**/*.js'", 23 | "jest": "node test/download-browsers.js && jest --testTimeout 5000 --maxWorkers=1 src/content/", 24 | "stylelint": "stylelint 'src/**/*.css'" 25 | }, 26 | "eslintIgnore": [ 27 | "'**/third_party/*.js'" 28 | ], 29 | "devDependencies": { 30 | "@puppeteer/browsers": "^2.2.0", 31 | "eslint": "^8.9.0", 32 | "eslint-config-google": "^0.14.0", 33 | "eslint-plugin-jest": "^27.4.0", 34 | "http-server": "^14.1.0", 35 | "jest": "^29.7.0", 36 | "selenium-webdriver": "^4.19.0", 37 | "stylelint": "^14.5.3", 38 | "stylelint-config-recommended": "^7.0.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /release/ChromeWebRTCNetworkLimiterExtension_0.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/release/ChromeWebRTCNetworkLimiterExtension_0.1.zip -------------------------------------------------------------------------------- /release/ChromeWebRTCNetworkLimiterExtension_0.2.1.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/release/ChromeWebRTCNetworkLimiterExtension_0.2.1.1.zip -------------------------------------------------------------------------------- /release/desktopCaptureExtension.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/release/desktopCaptureExtension.zip -------------------------------------------------------------------------------- /src/content/capture/canvas-filter/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | canvas { 10 | background-color: #ccc; 11 | --width: calc(45%); 12 | width: var(--width); 13 | height: calc(var(--width) * 0.75); 14 | margin: 1em; 15 | vertical-align: top; 16 | } 17 | 18 | video { 19 | --width: calc(45%); 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | margin: 1em; 23 | object-fit: cover; 24 | } 25 | -------------------------------------------------------------------------------- /src/content/capture/canvas-filter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Video to Canvas to video 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Stream camera via a canvas to a video element 37 |

38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |

The camera is captured to a video element, which is mapped onto a 46 | canvas, and a red square is added.

47 |

The canvas is then captured to an ImageData object, and painted 48 | onto a second canvas.

49 |

A stream is captured from the second canvas element using its 50 | captureStream() method and set as the srcObject of the video element.

51 | 52 |

The inputStream, source, 53 | canvasIn, canvasOut, 54 | result, and stream variables are in global 55 | scope, so you can 56 | inspect them from the browser console.

57 | 58 | View source on GitHub 60 | 61 |
62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/content/capture/canvas-filter/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const source = document.querySelector('#source'); 12 | // TODO(hta): Use OffscreenCanvas for the intermediate canvases. 13 | const canvasIn = document.querySelector('#canvas-source'); 14 | const canvasOut = document.querySelector('#canvas-result'); 15 | const result = document.querySelector('#result'); 16 | 17 | const stream = canvasOut.captureStream(); 18 | let inputStream = null; 19 | let imageData = null; 20 | 21 | result.srcObject = stream; 22 | 23 | function loop() { 24 | if (source.videoWidth > 0 && source.videoHeight > 0) { 25 | canvasIn.width = source.videoWidth; 26 | canvasIn.height = source.videoHeight; 27 | const ctx = canvasIn.getContext('2d'); 28 | ctx.drawImage(source, 0, 0); 29 | // Put a red square into the image, to mark it as "processed". 30 | ctx.fillStyle = '#FF0000'; 31 | ctx.fillRect(10, 10, 80, 80); 32 | imageData = ctx.getImageData(0, 0, canvasIn.width, canvasIn.height); 33 | // At this point, we have data that can be transferred. 34 | // We paint it on the second canvas. 35 | canvasOut.width = source.videoWidth; 36 | canvasOut.height = source.videoHeight; 37 | const outCtx = canvasOut.getContext('2d'); 38 | outCtx.putImageData(imageData, 0, 0); 39 | } 40 | window.requestAnimationFrame(loop); 41 | } 42 | 43 | (async () => { 44 | inputStream = await navigator.mediaDevices.getUserMedia({video: true}); 45 | source.srcObject = inputStream; 46 | source.play(); 47 | result.play(); 48 | window.requestAnimationFrame(loop); 49 | })(); 50 | -------------------------------------------------------------------------------- /src/content/capture/canvas-pc/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | canvas { 10 | background-color: #ccc; 11 | --width: calc(45%); 12 | width: var(--width); 13 | height: calc(var(--width) * 0.75); 14 | margin: 1em; 15 | vertical-align: top; 16 | } 17 | 18 | video { 19 | --width: calc(45%); 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | margin: 1em; 23 | object-fit: cover; 24 | } 25 | 26 | #autoplay { 27 | display: none; 28 | } 29 | -------------------------------------------------------------------------------- /src/content/capture/canvas-record/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 3px 10px 0; 10 | padding-left: 2px; 11 | padding-right: 2px; 12 | width: 99px; 13 | } 14 | 15 | button:last-of-type { 16 | margin: 0; 17 | } 18 | 19 | p.borderBelow { 20 | margin: 0 0 20px 0; 21 | padding: 0 0 20px 0; 22 | } 23 | 24 | canvas { 25 | background-color: #ccc; 26 | --width: calc(45%); 27 | width: var(--width); 28 | height: calc(var(--width) * 0.75); 29 | margin: 1em; 30 | vertical-align: top; 31 | } 32 | 33 | video { 34 | --width: calc(45%); 35 | width: var(--width); 36 | height: calc(var(--width) * 0.75); 37 | margin: 1em; 38 | object-fit: cover; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/content/capture/canvas-video/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | canvas { 10 | background-color: #ccc; 11 | --width: calc(45%); 12 | width: var(--width); 13 | height: calc(var(--width) * 0.75); 14 | margin: 1em; 15 | vertical-align: top; 16 | } 17 | 18 | video { 19 | --width: calc(45%); 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | margin: 1em; 23 | object-fit: cover; 24 | } 25 | -------------------------------------------------------------------------------- /src/content/capture/canvas-video/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | /* global main */ 10 | 11 | 'use strict'; 12 | 13 | // Call main() in demo.js 14 | main(); 15 | 16 | const canvas = document.querySelector('canvas'); 17 | const video = document.querySelector('video'); 18 | 19 | const stream = canvas.captureStream(); 20 | video.srcObject = stream; 21 | -------------------------------------------------------------------------------- /src/content/capture/video-contenthint/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | video { 10 | --width: calc(45%); 11 | width: var(--width); 12 | height: calc(var(--width) * 9 / 16); 13 | margin: 1em; 14 | object-fit: cover; 15 | } 16 | 17 | .video-container { 18 | border-bottom: 1px solid grey; 19 | font-style: italic; 20 | margin: 20px; 21 | } 22 | 23 | #videos { 24 | text-align: center; 25 | width: 100%; 26 | } 27 | -------------------------------------------------------------------------------- /src/content/capture/video-pc/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | video { 10 | margin: 0 10px 0 0; 11 | width: calc(50% - 7px); 12 | } 13 | 14 | video:last-of-type { 15 | margin-right: 0; 16 | } 17 | 18 | @media screen and (max-width: 400px) { 19 | video { 20 | margin: 0 5px 20px 0; 21 | width: calc(50% - 5px); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/content/capture/video-video/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | video { 10 | margin: 0 10px 0 0; 11 | width: calc(50% - 7px); 12 | } 13 | 14 | video:last-of-type { 15 | margin-right: 0; 16 | } 17 | 18 | @media screen and (max-width: 400px) { 19 | video { 20 | margin: 0 5px 20px 0; 21 | width: calc(50% - 5px); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/content/capture/video-video/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | captureStream(): video to video 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples captureStream(): video to video 37 |

38 | 39 | 44 | 45 | 46 | 47 |

Press play on the left video to start the demo.

48 | 49 |

A stream is captured from the video element on the left using its captureStream() method and set as 50 | the srcObject of the video element on the right.

51 | 52 |

The stream variable are in global scope, so you can inspect them from the browser console.

53 | 54 | View source on GitHub 56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/content/capture/video-video/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 'use strict'; 9 | 10 | const leftVideo = document.getElementById('leftVideo'); 11 | const rightVideo = document.getElementById('rightVideo'); 12 | 13 | leftVideo.addEventListener('canplay', () => { 14 | let stream; 15 | const fps = 0; 16 | if (leftVideo.captureStream) { 17 | stream = leftVideo.captureStream(fps); 18 | } else if (leftVideo.mozCaptureStream) { 19 | stream = leftVideo.mozCaptureStream(fps); 20 | } else { 21 | console.error('Stream capture is not supported'); 22 | stream = null; 23 | } 24 | rightVideo.srcObject = stream; 25 | }); 26 | -------------------------------------------------------------------------------- /src/content/capture/worker-process/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | canvas { 10 | background-color: #ccc; 11 | --width: calc(45%); 12 | width: var(--width); 13 | height: calc(var(--width) * 0.75); 14 | margin: 1em; 15 | vertical-align: top; 16 | } 17 | 18 | video { 19 | --width: calc(45%); 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | margin: 1em; 23 | object-fit: cover; 24 | } 25 | -------------------------------------------------------------------------------- /src/content/capture/worker-process/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Video processing in a Worker 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Processing video data with a Worker 37 |

38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |

The camera is captured to a MediaStreamTrack, which is turned into a 46 | WHATWG Stream of ImageData objects by means of a canvas, and a red 47 | square is added.

48 |

The stream is sent to a Worker, which returns a new stream containing 49 | the same video data.

50 |

This is then mapped back to a MediaStream using another canvas.

51 | 52 |

The chief purpose of the demo is to demonstrate that this is doable, 53 | but that performance can be improved significantly.

54 |

NOTE: This works only on Chrome 76 and above with experimental Web 55 | features enabled, since it depends on transferable Streams.

56 |

A similar demo, without the worker process, is on the canvas filter demo.

57 | View source on GitHub 59 | 60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/content/capture/worker-process/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const source = document.querySelector('#source'); 12 | // TODO(hta): Use OffscreenCanvas for the intermediate canvases. 13 | const canvasIn = document.querySelector('#canvas-source'); 14 | const canvasOut = document.querySelector('#canvas-result'); 15 | const result = document.querySelector('#result'); 16 | 17 | const stream = canvasOut.captureStream(); 18 | let inputStream = null; 19 | let imageData = null; 20 | let transformStream = null; 21 | let writer = null; 22 | let reader = null; 23 | 24 | result.srcObject = stream; 25 | 26 | function loop() { 27 | if (source.videoWidth > 0 && source.videoHeight > 0) { 28 | canvasIn.width = source.videoWidth; 29 | canvasIn.height = source.videoHeight; 30 | const ctx = canvasIn.getContext('2d'); 31 | ctx.drawImage(source, 0, 0); 32 | // Put a red square into the image, to mark it as "processed". 33 | ctx.fillStyle = '#FF0000'; 34 | ctx.fillRect(10, 10, 80, 80); 35 | imageData = ctx.getImageData(0, 0, canvasIn.width, canvasIn.height); 36 | // At this point, we have data that can be transferred. 37 | writer.write(imageData); 38 | } 39 | window.requestAnimationFrame(loop); 40 | } 41 | 42 | // The read function paints the incoming data on the second canvas. 43 | const readData = async () => { 44 | const result = await reader.read(); 45 | if (!result.done) { 46 | canvasOut.width = source.videoWidth; 47 | canvasOut.height = source.videoHeight; 48 | const outCtx = canvasOut.getContext('2d'); 49 | outCtx.putImageData(result.value, 0, 0); 50 | readData(); 51 | } 52 | }; 53 | 54 | (async () => { 55 | inputStream = await navigator.mediaDevices.getUserMedia({video: true}); 56 | source.srcObject = inputStream; 57 | transformStream = new TransformStream(); 58 | writer = transformStream.writable.getWriter(); 59 | 60 | const myWorker = new Worker('js/worker.js'); 61 | myWorker.onmessage = function(e) { 62 | reader = e.data[1].getReader(); 63 | // Start the flow of data. 64 | readData(); 65 | window.requestAnimationFrame(loop); 66 | }; 67 | myWorker.postMessage(['stream', transformStream.readable], 68 | [transformStream.readable]); 69 | source.play(); 70 | result.play(); 71 | })(); 72 | -------------------------------------------------------------------------------- /src/content/capture/worker-process/js/worker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | onmessage = function(e) { 12 | const command = e.data[0]; 13 | if (command == 'stream') { 14 | const inputStream = e.data[1]; 15 | const transformStream = new TransformStream(); 16 | inputStream.pipeTo(transformStream.writable); 17 | postMessage(['response', transformStream.readable], 18 | [transformStream.readable]); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/content/datachannel/basic/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 1em 1em 0; 10 | width: 90px; 11 | } 12 | div#buttons { 13 | margin: 0 0 1em 0; 14 | } 15 | div#send { 16 | margin: 0 20px 1em 0; 17 | } 18 | div#sendReceive { 19 | border-bottom: 1px solid #eee; 20 | margin: 0; 21 | padding: 0 0 10px 0; 22 | } 23 | div#sendReceive > div { 24 | display: inline-block; 25 | width: calc(50% - 20px); 26 | } 27 | form { 28 | margin: 0 0 1em 0; 29 | white-space: nowrap; 30 | } 31 | form span { 32 | font-weight: 300; 33 | margin: 0 1em 0 0; 34 | white-space: normal; 35 | } 36 | textarea { 37 | color: #444; 38 | font-size: 0.9em; 39 | font-weight: 300; 40 | height: 7.0em; 41 | padding: 5px; 42 | width: calc(100% - 10px); 43 | } 44 | -------------------------------------------------------------------------------- /src/content/datachannel/basic/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | const webdriver = require('selenium-webdriver'); 9 | const seleniumHelpers = require('../../../../../test/webdriver'); 10 | 11 | let driver; 12 | const path = '/src/content/datachannel/basic/index.html'; 13 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 14 | 15 | describe('datachannel basic', () => { 16 | beforeAll(async () => { 17 | driver = await seleniumHelpers.buildDriver(); 18 | }); 19 | afterAll(() => { 20 | return driver.quit(); 21 | }); 22 | 23 | beforeEach(() => { 24 | return driver.get(url); 25 | }); 26 | 27 | it('transfers text', async () => { 28 | const text = 'Hello world'; 29 | await driver.findElement(webdriver.By.id('startButton')).click(); 30 | 31 | await Promise.all([ 32 | driver.wait(() => driver.executeScript(() => { 33 | return localConnection && localConnection.connectionState === 'connected'; // eslint-disable-line no-undef 34 | })), 35 | await driver.wait(() => driver.executeScript(() => { 36 | return remoteConnection && remoteConnection.connectionState === 'connected'; // eslint-disable-line no-undef 37 | })), 38 | ]); 39 | await driver.wait(() => driver.findElement(webdriver.By.id('sendButton')).isEnabled()); 40 | 41 | await driver.findElement(webdriver.By.id('dataChannelSend')) 42 | .sendKeys(text); 43 | await driver.findElement(webdriver.By.id('sendButton')).click(); 44 | await driver.wait(() => driver.executeScript(() => { 45 | return document.getElementById('dataChannelReceive').value.length > 0; 46 | })); 47 | 48 | const value = await driver.findElement(webdriver.By.id('dataChannelReceive')).getAttribute('value'); 49 | expect(value).toBe(text); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /src/content/datachannel/channel/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 1em 1em 0; 10 | width: 90px; 11 | } 12 | div#buttons { 13 | margin: 0 0 1em 0; 14 | } 15 | div#send { 16 | margin: 0 20px 1em 0; 17 | } 18 | div#sendReceive { 19 | border-bottom: 1px solid #eee; 20 | margin: 0; 21 | padding: 0 0 10px 0; 22 | } 23 | div#sendReceive > div { 24 | display: inline-block; 25 | width: calc(50% - 20px); 26 | } 27 | form { 28 | margin: 0 0 1em 0; 29 | white-space: nowrap; 30 | } 31 | form span { 32 | font-weight: 300; 33 | margin: 0 1em 0 0; 34 | white-space: normal; 35 | } 36 | textarea { 37 | color: #444; 38 | font-size: 0.9em; 39 | font-weight: 300; 40 | height: 7.0em; 41 | padding: 5px; 42 | width: calc(100% - 10px); 43 | } 44 | -------------------------------------------------------------------------------- /src/content/datachannel/datatransfer/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | div.progress { 9 | margin: 0 0 1em 0; 10 | } 11 | 12 | div.progress div.label { 13 | display: inline-block; 14 | font-weight: 400; 15 | width: 8.2em; 16 | } 17 | 18 | div.input { 19 | margin: 0 0 1em 0; 20 | } 21 | 22 | progress { 23 | width: calc(100% - 8.5em); 24 | } 25 | -------------------------------------------------------------------------------- /src/content/datachannel/datatransfer/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/datachannel/datatransfer/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('datachannel datatransfer', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('transfers data', async () => { 31 | const megsToSend = 4; 32 | await driver.findElement(webdriver.By.id('megsToSend')) 33 | .clear(); 34 | await driver.findElement(webdriver.By.id('megsToSend')) 35 | .sendKeys(megsToSend + '\n'); 36 | 37 | await driver.findElement(webdriver.By.id('sendTheData')).click(); 38 | 39 | await Promise.all([ 40 | driver.wait(() => driver.executeScript(() => { 41 | return localConnection && localConnection.connectionState === 'connected'; // eslint-disable-line no-undef 42 | })), 43 | await driver.wait(() => driver.executeScript(() => { 44 | return remoteConnection && remoteConnection.connectionState === 'connected'; // eslint-disable-line no-undef 45 | })), 46 | ]); 47 | 48 | // the remote connection gets closed when it is done. 49 | await driver.wait(() => driver.executeScript(() => { 50 | return remoteConnection === null; // eslint-disable-line no-undef 51 | })); 52 | 53 | const transferred = await driver.findElement(webdriver.By.id('receiveProgress')).getAttribute('value'); 54 | expect(transferred >>> 0).toBe(megsToSend * 1024 * 1024); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /src/content/datachannel/filetransfer/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | div.progress, div#bitrate { 9 | margin: 0 0 1em 0; 10 | } 11 | 12 | div.progress div.label { 13 | display: inline-block; 14 | font-weight: 400; 15 | width: 8.2em; 16 | } 17 | 18 | form { 19 | margin: 0 0 1em 0; 20 | white-space: nowrap; 21 | } 22 | 23 | progress { 24 | width: calc(100% - 8.5em); 25 | } 26 | -------------------------------------------------------------------------------- /src/content/datachannel/filetransfer/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/datachannel/filetransfer/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('datachannel filetransfer', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('transfers a file', async () => { 31 | await driver.findElement(webdriver.By.id('fileInput')) 32 | .sendKeys(process.cwd() + '/src/content/devices/multi/images/poster.jpg'); 33 | await driver.wait(() => driver.findElement(webdriver.By.id('sendFile')).isEnabled()); 34 | await driver.findElement(webdriver.By.id('sendFile')).click(); 35 | 36 | // the remote connection gets closed when it is done. 37 | await driver.wait(() => driver.executeScript(() => { 38 | return remoteConnection === null; // eslint-disable-line no-undef 39 | })); 40 | await driver.wait(() => driver.findElement(webdriver.By.id('download')).isEnabled()); 41 | }); 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /src/content/datachannel/messaging/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Send messages with datachannel 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 | 40 |

WebRTC samples Send messages with datachannel 41 |

42 |
43 | 44 |

This page show how to send text messages via WebRTC datachannels.

45 | 46 |

Enter a message in one text box and press send and it will be transferred to the "remote" peer over a 47 | datachannel.

48 | 49 |
50 | 51 | 52 | 53 |
54 |

View the console to see logging.

55 | 56 |

For more information about RTCDataChannel, see Getting Started With WebRTC.

59 |
60 | 61 | View source on GitHub 63 |
64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/content/datachannel/messaging/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | div.messageBox { 9 | width: 100%; 10 | } 11 | 12 | textarea.message { 13 | width: 100%; 14 | height: 5em; 15 | resize: none; 16 | display: block; 17 | box-sizing: border-box; 18 | margin: 1em; 19 | } 20 | 21 | label { 22 | font-weight: 400; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/content/devices/input-output/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 'use strict'; 10 | 11 | const seleniumHelpers = require('../../../../../test/webdriver'); 12 | 13 | let driver; 14 | const path = '/src/content/devices/input-output/index.html'; 15 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 16 | 17 | describe('input-output', () => { 18 | beforeAll(async () => { 19 | driver = await seleniumHelpers.buildDriver(); 20 | }); 21 | afterAll(() => { 22 | return driver.quit(); 23 | }); 24 | 25 | beforeEach(() => { 26 | return driver.get(url); 27 | }); 28 | 29 | it('shows at least one audio input device', async () => { 30 | await driver.wait(driver.executeScript(() => { 31 | return document.getElementById('audioSource').childElementCount > 0; 32 | })); 33 | }); 34 | 35 | it('shows at least one video input device', async () => { 36 | await driver.wait(driver.executeScript(() => { 37 | return document.getElementById('videoSource').childElementCount > 0; 38 | })); 39 | }); 40 | 41 | it('shows at least one audio output device device', async function() { 42 | if (process.env.BROWSER === 'firefox') { 43 | this.skip(); 44 | } 45 | await driver.wait(driver.executeScript(() => { 46 | return document.getElementById('audioOutput').childElementCount > 0; 47 | })); 48 | }); 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /src/content/devices/multi/audio/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/content/devices/multi/audio/audio.mp3 -------------------------------------------------------------------------------- /src/content/devices/multi/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | audio { 10 | margin: 0 0 1.5em 0; 11 | width: 100%; 12 | } 13 | 14 | div#sources > div { 15 | float: left; 16 | margin: 0 1em 0 0; 17 | width: calc(50% - 0.5em); 18 | } 19 | 20 | div#sources > div:last-of-type { 21 | margin: 0; 22 | } 23 | 24 | select { 25 | margin: 0 0 0.5em 0; 26 | } 27 | 28 | video { 29 | background: black; 30 | height: 234px; 31 | } 32 | -------------------------------------------------------------------------------- /src/content/devices/multi/images/poster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/content/devices/multi/images/poster.jpg -------------------------------------------------------------------------------- /src/content/devices/multi/video/chrome.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/content/devices/multi/video/chrome.mp4 -------------------------------------------------------------------------------- /src/content/devices/multi/video/chrome.ogv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/content/devices/multi/video/chrome.ogv -------------------------------------------------------------------------------- /src/content/devices/multi/video/chrome.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/content/devices/multi/video/chrome.webm -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/img/netli_1280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/content/extensions/multipleroutes/img/netli_1280.png -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/README.md: -------------------------------------------------------------------------------- 1 | ## Chrome WebRTC Network Limiter 2 | Configures the WebRTC traffic routing options in Chrome's privacy settings. 3 | 4 | ★ What it does: 5 | This configures WebRTC to not use certain IP addresses or protocols: 6 | - private IP addresses not visible to the public internet (e.g. addresses like 192.168.1.2) 7 | - any public IP addresses associated with network interfaces that are not used for web traffic (e.g. an ISP-provided address, when browsing through a VPN) 8 | - Require WebRTC traffic to go through proxy servers as configured in Chrome. Since most of the proxy servers don't handle UDP, this effectively turns off UDP until UDP proxy support is available in Chrome and such proxies are widely deployed. 9 | 10 | When the extension is installed on Chrome versions prior to M48, WebRTC will only use the public IP address associated with the interface used for web traffic, typically the same addresses that are already provided to sites in browser HTTP requests. For Chrome version M48 and after, this extension provides one more configuration which allows WebRTC to use both the default public address and, for machines behind a NAT, the default private address which is associated with the public one. Said behavior will be the default after a fresh installation of the extension to Chrome M48. For upgrade scenarios, the previous selected configuration should not be changed. 11 | 12 | The extension may also disable non-proxied UDP, but this is not on by default and must be configured using the extension's Options page. 13 | 14 | ★ Notes: 15 | This extension may affect the performance of applications that use WebRTC for audio/video or real-time data communication. Because it limits the potential network paths and protocols, WebRTC may pick a path which results in significantly longer delay or lower quality (e.g. through a VPN) or use TCP only through proxy servers which is not ideal for real-time communication. We are attempting to determine how common this is. 16 | 17 | By installing this item, you agree to the Google Terms of Service and Privacy Policy at https://www.google.com/intl/en/policies/. 18 | 19 | -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "NETLI_DEFAULT_RADIO": { 3 | "message": "Give me the best media experience: This option allows Chrome to explore all network paths to find the best way to send and receive media, which may be different from normal web traffic." 4 | }, 5 | "NETLI_DEFAULT_PUBLIC_AND_PRIVATE_INTERFACES_RADIO": { 6 | "message": "Use my default public and private IP addresses: This option forces Chrome to use the same network path for media as for normal web traffic, except when a web proxy is present. For machines behind a NAT, Chrome will also use the default private address to enhance connectivity. To prevent degraded performance, Chrome will attempt to send media directly instead of using the proxy." 7 | }, 8 | "NETLI_DEFAULT_PUBLIC_INTERFACE_ONLY_RADIO": { 9 | "message": "Use only my default public IP address: This option is the same as Use my default public and private IP addresses except that Chrome will not use the private default address." 10 | }, 11 | "NETLI_DISABLE_NON_PROXIED_UDP_RADIO": { 12 | "message": "Use my proxy server (if present): This option forces Chrome to use the same network path for media as for normal web traffic, including use of a web proxy. Chrome will always attempt to send media through the proxy, which will typically hurt media performance and increase the load on the proxy; furthermore, this behavior may be incompatible with some applications." 13 | }, 14 | "NETLI_OPTION_NOT_SUPPORTED": { 15 | "message": "Grayed out options require newer verion of Chrome." 16 | }, 17 | "NETLI_APPDESC": { 18 | "message": "Configures how WebRTC's network traffic is routed by changing Chrome's privacy settings." 19 | }, 20 | "NETLI_APPNAME": { 21 | "message": "WebRTC Network Limiter" 22 | }, 23 | "NETLI_OPTIONS": { 24 | "message": "WebRTC Network Limiter Options" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/img/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/content/extensions/multipleroutes/src/img/icon_128.png -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/img/icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/content/extensions/multipleroutes/src/img/icon_16.png -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_locale": "en", 3 | "description": "__MSG_NETLI_APPDESC__", 4 | "icons": { 5 | "16": "img/icon_16.png", 6 | "128": "img/icon_128.png" 7 | }, 8 | "manifest_version": 3, 9 | "minimum_chrome_version": "42.0.2311.135", 10 | "name": "__MSG_NETLI_APPNAME__", 11 | "options_ui": { 12 | "open_in_tab": false, 13 | "page": "options.html" 14 | }, 15 | "permissions": [ "privacy" ], 16 | "version": "0.2.1.4" 17 | } 18 | -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/options.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 |

12 | 13 | 16 |

17 |

18 | 21 | 24 |

25 |

26 | 29 | 32 |

33 |

34 | 36 | 39 |

40 |

41 | 44 |

45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/options.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const pn = chrome.privacy.network; 12 | const pi = chrome.privacy.IPHandlingPolicy; 13 | 14 | const mapPolicyToRadioId = {}; 15 | mapPolicyToRadioId[pi.DEFAULT] = 0; 16 | mapPolicyToRadioId[pi.DEFAULT_PUBLIC_AND_PRIVATE_INTERFACES] = 1; 17 | mapPolicyToRadioId[pi.DEFAULT_PUBLIC_INTERFACE_ONLY] = 2; 18 | mapPolicyToRadioId[pi.DISABLE_NON_PROXIED_UDP] = 3; 19 | 20 | const mapRadioIdToPolicy = {}; 21 | mapRadioIdToPolicy[0] = pi.DEFAULT; 22 | mapRadioIdToPolicy[1] = pi.DEFAULT_PUBLIC_AND_PRIVATE_INTERFACES; 23 | mapRadioIdToPolicy[2] = pi.DEFAULT_PUBLIC_INTERFACE_ONLY; 24 | mapRadioIdToPolicy[3] = pi.DISABLE_NON_PROXIED_UDP; 25 | 26 | // Saves options. 27 | function saveOptions() { 28 | const radios = document.getElementsByName('ip_policy_selection'); 29 | let i; 30 | for (i = 0; i < radios.length; i++) { 31 | if (radios[i].checked) { 32 | break; 33 | } 34 | } 35 | 36 | pn.webRTCIPHandlingPolicy.set({ 37 | value: mapRadioIdToPolicy[i] 38 | }); 39 | } 40 | 41 | function restoreRadios(policy) { 42 | const radios = document.getElementsByName('ip_policy_selection'); 43 | radios[mapPolicyToRadioId[policy]].checked = true; 44 | } 45 | 46 | function restoreOption() { 47 | pn.webRTCIPHandlingPolicy.get({}, function(details) { 48 | restoreRadios(details.value); 49 | }); 50 | } 51 | 52 | document.addEventListener('DOMContentLoaded', restoreOption); 53 | document.getElementById('default'). 54 | addEventListener('click', saveOptions); 55 | document.getElementById('default_public_and_private_interfaces'). 56 | addEventListener('click', saveOptions); 57 | document.getElementById('default_public_interface_only'). 58 | addEventListener('click', saveOptions); 59 | document.getElementById('disable_non_proxied_udp'). 60 | addEventListener('click', saveOptions); 61 | 62 | document.title = chrome.i18n.getMessage('netli_options'); 63 | const i18nElements = document.querySelectorAll('*[i18n-content]'); 64 | for (let i = 0; i < i18nElements.length; i++) { 65 | const elem = i18nElements[i]; 66 | const msg = elem.getAttribute('i18n-content'); 67 | elem.innerHTML = chrome.i18n.getMessage(msg); 68 | } 69 | 70 | function browserSupportsIPHandlingPolicy() { 71 | return pn.webRTCIPHandlingPolicy !== undefined; 72 | } 73 | 74 | if (browserSupportsIPHandlingPolicy()) { 75 | // Hide the 'not supported' banner. 76 | document.getElementById('not_supported').innerHTML = ''; 77 | } else { 78 | // Disable all options. 79 | for (let i = 0; i < 4; i++) { 80 | const key = 'Mode' + i; 81 | const section = document.getElementById(key); 82 | section.style.color = 'gray'; 83 | section.querySelector('input').disabled = true; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/content/extensions/svc/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | 50 | 51 | div.graph-container { 52 | float: left; 53 | margin: 0.5em; 54 | width: calc(50% - 1em); 55 | } 56 | -------------------------------------------------------------------------------- /src/content/getusermedia/audio/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | gUM audio 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 |

WebRTC samples getUserMedia, audio only 36 |

37 | 38 | 39 | 40 |

Warning: if you're not using headphones, pressing play will cause feedback.

41 | 42 |

Render the audio stream from an audio-only getUserMedia() call with an audio element.

43 | 44 |

The MediaStream object stream passed to the getUserMedia() 45 | callback is in global scope, so you can inspect it from the console.

46 |
47 | 48 |
49 | 50 | View source on GitHub 52 | 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/content/getusermedia/audio/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // Put variables in global scope to make them available to the browser console. 12 | const audio = document.querySelector('audio'); 13 | 14 | const constraints = window.constraints = { 15 | audio: true, 16 | video: false 17 | }; 18 | 19 | function handleSuccess(stream) { 20 | const audioTracks = stream.getAudioTracks(); 21 | console.log('Got stream with constraints:', constraints); 22 | console.log('Using audio device: ' + audioTracks[0].label); 23 | stream.oninactive = function() { 24 | console.log('Stream ended'); 25 | }; 26 | window.stream = stream; // make variable available to browser console 27 | audio.srcObject = stream; 28 | } 29 | 30 | function handleError(error) { 31 | const errorMessage = 'navigator.MediaDevices.getUserMedia error: ' + error.message + ' ' + error.name; 32 | document.getElementById('errorMsg').innerText = errorMessage; 33 | console.log(errorMessage); 34 | } 35 | 36 | navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError); 37 | -------------------------------------------------------------------------------- /src/content/getusermedia/canvas/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | getUserMedia to canvas 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 |

WebRTC samples getUserMedia ⇒ canvas 36 |

37 | 38 | 39 | 40 | 41 | 42 |

Draw a frame from the video onto the canvas element using the drawImage() method.

43 | 44 |

The variables canvas, video and stream are in global scope, so you can 45 | inspect them from the console.

46 | 47 | View source on GitHub 49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/content/getusermedia/canvas/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // Put variables in global scope to make them available to the browser console. 12 | const video = document.querySelector('video'); 13 | const canvas = window.canvas = document.querySelector('canvas'); 14 | canvas.width = 480; 15 | canvas.height = 360; 16 | 17 | const button = document.querySelector('button'); 18 | button.onclick = function() { 19 | canvas.width = video.videoWidth; 20 | canvas.height = video.videoHeight; 21 | canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height); 22 | }; 23 | 24 | const constraints = { 25 | audio: false, 26 | video: true 27 | }; 28 | 29 | function handleSuccess(stream) { 30 | window.stream = stream; // make stream available to browser console 31 | video.srcObject = stream; 32 | } 33 | 34 | function handleError(error) { 35 | console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name); 36 | } 37 | 38 | navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError); 39 | -------------------------------------------------------------------------------- /src/content/getusermedia/filter/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const snapshotButton = document.querySelector('button#snapshot'); 12 | const filterSelect = document.querySelector('select#filter'); 13 | 14 | // Put variables in global scope to make them available to the browser console. 15 | const video = window.video = document.querySelector('video'); 16 | const canvas = window.canvas = document.querySelector('canvas'); 17 | canvas.width = 480; 18 | canvas.height = 360; 19 | 20 | snapshotButton.onclick = function() { 21 | canvas.className = filterSelect.value; 22 | canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height); 23 | }; 24 | 25 | filterSelect.onchange = function() { 26 | video.className = filterSelect.value; 27 | }; 28 | 29 | const constraints = { 30 | audio: false, 31 | video: true 32 | }; 33 | 34 | function handleSuccess(stream) { 35 | window.stream = stream; // make stream available to browser console 36 | video.srcObject = stream; 37 | } 38 | 39 | function handleError(error) { 40 | console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name); 41 | } 42 | 43 | navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError); 44 | -------------------------------------------------------------------------------- /src/content/getusermedia/getdisplaymedia/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | getDisplayMedia 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |

WebRTC samples getDisplayMedia

35 | 36 | 37 | 38 | 47 |
48 | 49 |

Display the screensharing stream from getDisplayMedia() in a video element.

50 | 51 | View source on GitHub 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/content/getusermedia/getdisplaymedia/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 'use strict'; 9 | 10 | const preferredDisplaySurface = document.getElementById('displaySurface'); 11 | const startButton = document.getElementById('startButton'); 12 | 13 | if (adapter.browserDetails.browser === 'chrome' && 14 | adapter.browserDetails.version >= 107) { 15 | // See https://developer.chrome.com/docs/web-platform/screen-sharing-controls/ 16 | document.getElementById('options').style.display = 'block'; 17 | } else if (adapter.browserDetails.browser === 'firefox') { 18 | // Polyfill in Firefox. 19 | // See https://blog.mozilla.org/webrtc/getdisplaymedia-now-available-in-adapter-js/ 20 | adapter.browserShim.shimGetDisplayMedia(window, 'screen'); 21 | } 22 | 23 | function handleSuccess(stream) { 24 | startButton.disabled = true; 25 | preferredDisplaySurface.disabled = true; 26 | const video = document.querySelector('video'); 27 | video.srcObject = stream; 28 | 29 | // demonstrates how to detect that the user has stopped 30 | // sharing the screen via the browser UI. 31 | stream.getVideoTracks()[0].addEventListener('ended', () => { 32 | errorMsg('The user has ended sharing the screen'); 33 | startButton.disabled = false; 34 | preferredDisplaySurface.disabled = false; 35 | }); 36 | } 37 | 38 | function handleError(error) { 39 | errorMsg(`getDisplayMedia error: ${error.name}`, error); 40 | } 41 | 42 | function errorMsg(msg, error) { 43 | const errorElement = document.querySelector('#errorMsg'); 44 | errorElement.innerHTML += `

${msg}

`; 45 | if (typeof error !== 'undefined') { 46 | console.error(error); 47 | } 48 | } 49 | 50 | 51 | startButton.addEventListener('click', () => { 52 | const options = {audio: true, video: true}; 53 | const displaySurface = preferredDisplaySurface.options[preferredDisplaySurface.selectedIndex].value; 54 | if (displaySurface !== 'default') { 55 | options.video = {displaySurface}; 56 | } 57 | navigator.mediaDevices.getDisplayMedia(options) 58 | .then(handleSuccess, handleError); 59 | }); 60 | 61 | if ((navigator.mediaDevices && 'getDisplayMedia' in navigator.mediaDevices)) { 62 | startButton.disabled = false; 63 | } else { 64 | errorMsg('getDisplayMedia is not supported'); 65 | } 66 | -------------------------------------------------------------------------------- /src/content/getusermedia/gum/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | getUserMedia 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |

WebRTC samples 35 | getUserMedia

36 | 37 | 38 | 39 | 40 |
41 | 42 |

Warning: if you're not using headphones, pressing play will cause feedback.

43 | 44 |

Display the video stream from getUserMedia() in a video element.

45 | 46 |

The MediaStream object stream passed to the getUserMedia() callback is in 47 | global scope, so you can inspect it from the console.

48 | 49 | View source on GitHub 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/content/getusermedia/gum/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 'use strict'; 9 | 10 | // Put variables in global scope to make them available to the browser console. 11 | const constraints = window.constraints = { 12 | audio: false, 13 | video: true 14 | }; 15 | 16 | function handleSuccess(stream) { 17 | const video = document.querySelector('video'); 18 | const videoTracks = stream.getVideoTracks(); 19 | console.log('Got stream with constraints:', constraints); 20 | console.log(`Using video device: ${videoTracks[0].label}`); 21 | window.stream = stream; // make variable available to browser console 22 | video.srcObject = stream; 23 | } 24 | 25 | function handleError(error) { 26 | if (error.name === 'OverconstrainedError') { 27 | errorMsg(`OverconstrainedError: The constraints could not be satisfied by the available devices. Constraints: ${JSON.stringify(constraints)}`); 28 | } else if (error.name === 'NotAllowedError') { 29 | errorMsg('NotAllowedError: Permissions have not been granted to use your camera and ' + 30 | 'microphone, you need to allow the page access to your devices in ' + 31 | 'order for the demo to work.'); 32 | } 33 | errorMsg(`getUserMedia error: ${error.name}`, error); 34 | } 35 | 36 | function errorMsg(msg, error) { 37 | const errorElement = document.querySelector('#errorMsg'); 38 | errorElement.innerHTML += `

${msg}

`; 39 | if (typeof error !== 'undefined') { 40 | console.error(error); 41 | } 42 | } 43 | 44 | async function init(e) { 45 | try { 46 | const stream = await navigator.mediaDevices.getUserMedia(constraints); 47 | handleSuccess(stream); 48 | e.target.disabled = true; 49 | } catch (e) { 50 | handleError(e); 51 | } 52 | } 53 | 54 | document.querySelector('#showVideo').addEventListener('click', e => init(e)); 55 | -------------------------------------------------------------------------------- /src/content/getusermedia/gum/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 'use strict'; 10 | 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/getusermedia/gum/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('getUserMedia', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('opens a camera', async () => { 31 | await driver.findElement(webdriver.By.css('button')).click(); 32 | await driver.wait(() => driver.executeScript(() => 33 | document.querySelector('video').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA) 34 | ); 35 | const width = await driver.findElement(webdriver.By.css('video')).getAttribute('videoWidth'); 36 | expect(width >>> 0).toBeGreaterThan(320); 37 | }); 38 | }); 39 | 40 | -------------------------------------------------------------------------------- /src/content/getusermedia/pan-tilt-zoom/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Control camera pan, tilt, and zoom 24 | 25 | 26 | 27 | 28 | 29 | 37 | 38 | 39 | 40 | 41 |
42 |

WebRTC samples 43 | Control Pan-Tilt-Zoom Camera

44 | 45 | 46 | 47 | 48 |
49 |
Pan:
50 | 51 |
52 |
53 |
Tilt:
54 | 55 |
56 |
57 |
Zoom:
58 | 59 |
60 | 61 |
62 | 63 |

Display the video stream from getUserMedia() in a video 64 | element and control pan, tilt, and zoom if camera supports Pan-Tilt-Zoom.

65 | 66 |

The MediaStreamTrack object track is in 67 | global scope, so you can inspect it from the console.

68 | 69 | View source on GitHub 71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/content/getusermedia/pan-tilt-zoom/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 'use strict'; 9 | 10 | // Put variables in global scope to make them available to the browser console. 11 | const constraints = window.constraints = { 12 | video: { 13 | pan: true, tilt: true, zoom: true 14 | } 15 | }; 16 | 17 | function handleSuccess(stream) { 18 | const video = document.querySelector('video'); 19 | const videoTracks = stream.getVideoTracks(); 20 | console.log('Got stream with constraints:', constraints); 21 | console.log(`Using video device: ${videoTracks[0].label}`); 22 | video.srcObject = stream; 23 | 24 | // make track variable available to browser console. 25 | const [track] = [window.track] = stream.getVideoTracks(); 26 | const capabilities = track.getCapabilities(); 27 | const settings = track.getSettings(); 28 | 29 | for (const ptz of ['pan', 'tilt', 'zoom']) { 30 | // Check whether camera supports pan/tilt/zoom. 31 | if (!(ptz in settings)) { 32 | errorMsg(`Camera does not support ${ptz}.`); 33 | continue; 34 | } 35 | 36 | // Map it to a slider element. 37 | const input = document.querySelector(`input[name=${ptz}]`); 38 | input.min = capabilities[ptz].min; 39 | input.max = capabilities[ptz].max; 40 | input.step = capabilities[ptz].step; 41 | input.value = settings[ptz]; 42 | input.disabled = false; 43 | input.oninput = async event => { 44 | try { 45 | const constraints = {advanced: [{[ptz]: input.value}]}; 46 | await track.applyConstraints(constraints); 47 | } catch (err) { 48 | console.error('applyConstraints() failed: ', err); 49 | } 50 | }; 51 | } 52 | } 53 | 54 | function handleError(error) { 55 | if (error.name === 'NotAllowedError') { 56 | errorMsg('Permissions have not been granted to use your camera, ' + 57 | 'you need to allow the page access to your devices in ' + 58 | 'order for the demo to work.'); 59 | } 60 | errorMsg(`getUserMedia error: ${error.name}`, error); 61 | } 62 | 63 | function errorMsg(msg, error) { 64 | const errorElement = document.querySelector('#errorMsg'); 65 | errorElement.innerHTML += `

${msg}

`; 66 | if (typeof error !== 'undefined') { 67 | console.error(error); 68 | } 69 | } 70 | 71 | async function init(e) { 72 | try { 73 | const stream = await navigator.mediaDevices.getUserMedia(constraints); 74 | handleSuccess(stream); 75 | e.target.disabled = true; 76 | } catch (e) { 77 | handleError(e); 78 | } 79 | } 80 | 81 | document.querySelector('#showVideo').addEventListener('click', e => init(e)); 82 | -------------------------------------------------------------------------------- /src/content/getusermedia/record/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 3px 10px 0; 10 | padding-left: 2px; 11 | padding-right: 2px; 12 | width: 120px; 13 | } 14 | 15 | /* copied from main button style */ 16 | .file-upload { 17 | background-color: #d84a38; 18 | border: none; 19 | border-radius: 2px; 20 | color: white; 21 | font-family: 'Roboto', sans-serif; 22 | font-size: 0.8em; 23 | margin: 0 0 1em 0; 24 | padding: 0.5em 0.7em 0.6em 0.7em; 25 | } 26 | 27 | .file-upload:hover { 28 | background-color: #cf402f; 29 | } 30 | 31 | button:last-of-type { 32 | margin: 0; 33 | } 34 | 35 | p.borderBelow { 36 | margin: 0 0 20px 0; 37 | padding: 0 0 20px 0; 38 | } 39 | 40 | video { 41 | vertical-align: top; 42 | --width: 25vw; 43 | width: var(--width); 44 | height: calc(var(--width) * 0.5625); 45 | } 46 | 47 | video:last-of-type { 48 | margin: 0 0 20px 0; 49 | } 50 | 51 | video#gumVideo { 52 | margin: 0 20px 20px 0; 53 | } 54 | 55 | input[type="file"] { 56 | display: none; 57 | } 58 | -------------------------------------------------------------------------------- /src/content/getusermedia/resolution/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* 9 | /* eslint-env node */ 10 | 11 | 'use strict'; 12 | const webdriver = require('selenium-webdriver'); 13 | const seleniumHelpers = require('../../../../../test/webdriver'); 14 | 15 | let driver; 16 | const path = '/src/content/getusermedia/resolution/index.html'; 17 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 18 | 19 | describe('getUserMedia resolutions', () => { 20 | beforeAll(async () => { 21 | driver = await seleniumHelpers.buildDriver(); 22 | }); 23 | afterAll(() => { 24 | return driver.quit(); 25 | }); 26 | 27 | beforeEach(() => { 28 | return driver.get(url); 29 | }); 30 | 31 | const buttonToResolution = { 32 | 'p180': 320, 33 | 'p360': 640, 34 | 'qvga': 320, 35 | 'vga': 640, 36 | 'hd': 1280, 37 | 'full-hd': 1920, 38 | 'televisionFourK': 3840, 39 | /* TODO: unsupported by fake device? Or is fake device limited to physical device resolution? 40 | 'cinemaFourK': 4096, 41 | 'eightK': 8192, 42 | */ 43 | }; 44 | Object.keys(buttonToResolution).forEach(buttonId => { 45 | const resolution = buttonToResolution[buttonId]; 46 | 47 | it(`opens a camera with width ${resolution}px`, async () => { 48 | await driver.findElement(webdriver.By.id(buttonId)).click(); 49 | await driver.wait(() => driver.executeScript(() => 50 | document.querySelector('video').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA) 51 | ); 52 | const width = await driver.findElement(webdriver.By.css('video')).getAttribute('videoWidth'); 53 | expect(width >>> 0).toBe(resolution); 54 | }); 55 | }); 56 | }); 57 | 58 | -------------------------------------------------------------------------------- /src/content/getusermedia/source/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | Page move 14 | 15 | 16 |

The page has moved to: 17 | this page

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/getusermedia/volume/css/main.css: -------------------------------------------------------------------------------- 1 | div#meters > div { 2 | margin: 0 0 1em 0; 3 | } 4 | 5 | div#meters div.label { 6 | display: inline-block; 7 | font-weight: 400; 8 | margin: 0 0.5em 0 0; 9 | width: 3.5em; 10 | } 11 | 12 | div#meters div.value { 13 | display: inline-block; 14 | } 15 | 16 | meter { 17 | width: 50%; 18 | } 19 | 20 | meter#clip { 21 | color: #db4437; 22 | } 23 | 24 | meter#slow { 25 | color: #f4b400; 26 | } 27 | 28 | meter#instant { 29 | color: #0f9d58; 30 | } 31 | -------------------------------------------------------------------------------- /src/content/getusermedia/volume/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | /* global AudioContext, SoundMeter */ 10 | 11 | 'use strict'; 12 | 13 | const startButton = document.getElementById('startButton'); 14 | const stopButton = document.getElementById('stopButton'); 15 | startButton.onclick = start; 16 | stopButton.onclick = stop; 17 | 18 | const instantMeter = document.querySelector('#instant meter'); 19 | const slowMeter = document.querySelector('#slow meter'); 20 | const clipMeter = document.querySelector('#clip meter'); 21 | 22 | const instantValueDisplay = document.querySelector('#instant .value'); 23 | const slowValueDisplay = document.querySelector('#slow .value'); 24 | const clipValueDisplay = document.querySelector('#clip .value'); 25 | 26 | // Put variables in global scope to make them available to the browser console. 27 | const constraints = window.constraints = { 28 | audio: true, 29 | video: false 30 | }; 31 | 32 | let meterRefresh = null; 33 | 34 | function handleSuccess(stream) { 35 | // Put variables in global scope to make them available to the 36 | // browser console. 37 | window.stream = stream; 38 | const soundMeter = window.soundMeter = new SoundMeter(window.audioContext); 39 | soundMeter.connectToSource(stream, function(e) { 40 | if (e) { 41 | alert(e); 42 | return; 43 | } 44 | meterRefresh = setInterval(() => { 45 | instantMeter.value = instantValueDisplay.innerText = 46 | soundMeter.instant.toFixed(2); 47 | slowMeter.value = slowValueDisplay.innerText = 48 | soundMeter.slow.toFixed(2); 49 | clipMeter.value = clipValueDisplay.innerText = 50 | soundMeter.clip; 51 | }, 200); 52 | }); 53 | } 54 | 55 | function handleError(error) { 56 | console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name); 57 | } 58 | 59 | 60 | function start() { 61 | console.log('Requesting local stream'); 62 | startButton.disabled = true; 63 | stopButton.disabled = false; 64 | 65 | try { 66 | window.AudioContext = window.AudioContext || window.webkitAudioContext; 67 | window.audioContext = new AudioContext(); 68 | } catch (e) { 69 | alert('Web Audio API not supported.'); 70 | } 71 | 72 | navigator.mediaDevices 73 | .getUserMedia(constraints) 74 | .then(handleSuccess) 75 | .catch(handleError); 76 | } 77 | 78 | function stop() { 79 | console.log('Stopping local stream'); 80 | startButton.disabled = false; 81 | stopButton.disabled = true; 82 | 83 | window.stream.getTracks().forEach(track => track.stop()); 84 | window.soundMeter.stop(); 85 | window.audioContext.close(); 86 | clearInterval(meterRefresh); 87 | instantMeter.value = instantValueDisplay.innerText = ''; 88 | slowMeter.value = slowValueDisplay.innerText = ''; 89 | clipMeter.value = clipValueDisplay.innerText = ''; 90 | } 91 | -------------------------------------------------------------------------------- /src/content/getusermedia/volume/js/soundmeter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // Meter class that generates a number correlated to audio volume. 12 | // The meter class itself displays nothing, but it makes the 13 | // instantaneous and time-decaying volumes available for inspection. 14 | // It also reports on the fraction of samples that were at or near 15 | // the top of the measurement range. 16 | function SoundMeter(context) { 17 | this.context = context; 18 | this.instant = 0.0; 19 | this.slow = 0.0; 20 | this.clip = 0.0; 21 | this.script = context.createScriptProcessor(2048, 1, 1); 22 | const that = this; 23 | this.script.onaudioprocess = function(event) { 24 | const input = event.inputBuffer.getChannelData(0); 25 | let i; 26 | let sum = 0.0; 27 | let clipcount = 0; 28 | for (i = 0; i < input.length; ++i) { 29 | sum += input[i] * input[i]; 30 | if (Math.abs(input[i]) > 0.99) { 31 | clipcount += 1; 32 | } 33 | } 34 | that.instant = Math.sqrt(sum / input.length); 35 | that.slow = 0.95 * that.slow + 0.05 * that.instant; 36 | that.clip = clipcount / input.length; 37 | }; 38 | } 39 | 40 | SoundMeter.prototype.connectToSource = function(stream, callback) { 41 | console.log('SoundMeter connecting'); 42 | try { 43 | this.mic = this.context.createMediaStreamSource(stream); 44 | this.mic.connect(this.script); 45 | // necessary to make sample run, but should not be. 46 | this.script.connect(this.context.destination); 47 | if (typeof callback !== 'undefined') { 48 | callback(null); 49 | } 50 | } catch (e) { 51 | console.error(e); 52 | if (typeof callback !== 'undefined') { 53 | callback(e); 54 | } 55 | } 56 | }; 57 | 58 | SoundMeter.prototype.stop = function() { 59 | console.log('SoundMeter stopping'); 60 | this.mic.disconnect(); 61 | this.script.disconnect(); 62 | }; 63 | -------------------------------------------------------------------------------- /src/content/insertable-streams/audio-processing/js/worker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | // Adjust this value to increase/decrease the amount of filtering. 10 | // eslint-disable-next-line prefer-const 11 | let cutoff = 100; 12 | 13 | // Returns a low-pass transform function for use with TransformStream. 14 | function lowPassFilter() { 15 | const format = 'f32-planar'; 16 | let lastValuePerChannel = undefined; 17 | return (data, controller) => { 18 | const rc = 1.0 / (cutoff * 2 * Math.PI); 19 | const dt = 1.0 / data.sampleRate; 20 | const alpha = dt / (rc + dt); 21 | const nChannels = data.numberOfChannels; 22 | if (!lastValuePerChannel) { 23 | console.log(`Audio stream has ${nChannels} channels.`); 24 | lastValuePerChannel = Array(nChannels).fill(0); 25 | } 26 | const buffer = new Float32Array(data.numberOfFrames * nChannels); 27 | for (let c = 0; c < nChannels; c++) { 28 | const offset = data.numberOfFrames * c; 29 | const samples = buffer.subarray(offset, offset + data.numberOfFrames); 30 | data.copyTo(samples, {planeIndex: c, format}); 31 | let lastValue = lastValuePerChannel[c]; 32 | 33 | // Apply low-pass filter to samples. 34 | for (let i = 0; i < samples.length; ++i) { 35 | lastValue = lastValue + alpha * (samples[i] - lastValue); 36 | samples[i] = lastValue; 37 | } 38 | 39 | lastValuePerChannel[c] = lastValue; 40 | } 41 | controller.enqueue(new AudioData({ 42 | format, 43 | sampleRate: data.sampleRate, 44 | numberOfFrames: data.numberOfFrames, 45 | numberOfChannels: nChannels, 46 | timestamp: data.timestamp, 47 | data: buffer 48 | })); 49 | }; 50 | } 51 | 52 | let abortController; 53 | 54 | onmessage = async (event) => { 55 | if (event.data.command == 'abort') { 56 | abortController.abort(); 57 | abortController = null; 58 | } else { 59 | const source = event.data.source; 60 | const sink = event.data.sink; 61 | const transformer = new TransformStream({transform: lowPassFilter()}); 62 | abortController = new AbortController(); 63 | const signal = abortController.signal; 64 | const promise = source.pipeThrough(transformer, {signal}).pipeTo(sink); 65 | promise.catch((e) => { 66 | if (signal.aborted) { 67 | console.log('Shutting down streams after abort.'); 68 | } else { 69 | console.error('Error from stream transform:', e); 70 | } 71 | source.cancel(e); 72 | sink.abort(e); 73 | }); 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /src/content/insertable-streams/endtoend-encryption/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 20px 10px 0 0; 11 | width: 100px; 12 | } 13 | 14 | div#buttons { 15 | margin: 0 0 20px 0; 16 | } 17 | 18 | div#status { 19 | height: 2em; 20 | margin: 1em 0 0 0; 21 | } 22 | 23 | input#audio { 24 | margin: 0 0.5em 0 0; 25 | position: relative; 26 | top: -1px; 27 | } 28 | 29 | video { 30 | --width: 45%; 31 | width: var(--width); 32 | height: calc(var(--width) * 0.75); 33 | } 34 | -------------------------------------------------------------------------------- /src/content/insertable-streams/endtoend-encryption/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection end to end encryption 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Peer connection end to end encryption 37 |

38 | 39 |

40 |

Sender and receiver

41 |
42 | Sender and receiver
43 | 44 | 45 |
46 |
47 | Crypto key: 48 | Encrypt first bytes: 49 |

50 |

51 |

Middlebox

52 |
53 | 54 | Switch audio to middlebox: 55 |
56 |

57 | 58 |
59 | 60 | 61 | 62 |
63 |
64 | 65 | View source on GitHub 67 | 68 |
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/content/insertable-streams/endtoend-encryption/js/videopipe.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | // 9 | // A "videopipe" abstraction on top of WebRTC. 10 | // 11 | // The usage of this abstraction: 12 | // var pipe = new VideoPipe(mediastream, handlerFunction); 13 | // handlerFunction = function(MediaStreamTrackEvent) { 14 | // do_something 15 | // } 16 | // pipe.close(); 17 | // 18 | // The VideoPipe will set up 2 PeerConnections, connect them to each 19 | // other, and call HandlerFunction when the stream's track is available 20 | // in the second PeerConnection. 21 | // 22 | 'use strict'; 23 | 24 | function VideoPipe(stream, forceSend, forceReceive, handler) { 25 | this.pc1 = new RTCPeerConnection({ 26 | encodedInsertableStreams: forceSend, 27 | }); 28 | this.pc2 = new RTCPeerConnection({ 29 | encodedInsertableStreams: forceReceive, 30 | }); 31 | this.pc2.ontrack = handler; 32 | stream.getTracks().forEach((track) => this.pc1.addTrack(track, stream)); 33 | } 34 | 35 | VideoPipe.prototype.negotiate = async function() { 36 | this.pc1.onicecandidate = e => this.pc2.addIceCandidate(e.candidate); 37 | this.pc2.onicecandidate = e => this.pc1.addIceCandidate(e.candidate); 38 | 39 | const offer = await this.pc1.createOffer(); 40 | // Disable video/red to allow for easier inspection in Wireshark. 41 | await this.pc2.setRemoteDescription({type: 'offer', sdp: offer.sdp.replace('red/90000', 'green/90000')}); 42 | await this.pc1.setLocalDescription(offer); 43 | 44 | const answer = await this.pc2.createAnswer(); 45 | await this.pc1.setRemoteDescription(answer); 46 | await this.pc2.setLocalDescription(answer); 47 | }; 48 | 49 | VideoPipe.prototype.close = function() { 50 | this.pc1.close(); 51 | this.pc2.close(); 52 | }; 53 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-analyzer/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-crop/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 20px 10px 0 0; 11 | width: 100px; 12 | } 13 | 14 | div#buttons { 15 | margin: 0 0 20px 0; 16 | } 17 | 18 | div#status { 19 | height: 2em; 20 | margin: 1em 0 0 0; 21 | } 22 | 23 | video { 24 | --width: 45%; 25 | width: var(--width); 26 | height: calc(var(--width) * 0.75); 27 | } 28 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-crop/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Insertable Streams - Crop in a worker 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |

WebRTC samples 36 | Breakout Box crop

37 | 38 |

This sample shows how to perform cropping on a video stream using the experimental 39 | mediacapture-transform API 40 | in a Worker. 41 |

42 | 43 | 44 | 45 | 46 |
47 | 48 |
49 | 50 |

51 | Note: This sample is using an experimental API that has not yet been standardized. As 52 | of 2022-11-21, this API is available in the latest version of Chrome based browsers. 53 |

54 | View source on GitHub 56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-crop/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /* global MediaStreamTrackProcessor, MediaStreamTrackGenerator */ 12 | if (typeof MediaStreamTrackProcessor === 'undefined' || 13 | typeof MediaStreamTrackGenerator === 'undefined') { 14 | alert( 15 | 'Your browser does not support the experimental MediaStreamTrack API ' + 16 | 'for Insertable Streams of Media. See the note at the bottom of the ' + 17 | 'page.'); 18 | } 19 | 20 | const startButton = document.getElementById('startButton'); 21 | const localVideo = document.getElementById('localVideo'); 22 | const croppedVideo = document.getElementById('croppedVideo'); 23 | 24 | const worker = new Worker('./js/worker.js', {name: 'Crop worker'}); 25 | startButton.addEventListener('click', async () => { 26 | const stream = await navigator.mediaDevices.getUserMedia({video: {width: 1280, height: 720}}); 27 | localVideo.srcObject = stream; 28 | 29 | const [track] = stream.getTracks(); 30 | const processor = new MediaStreamTrackProcessor({track}); 31 | const {readable} = processor; 32 | 33 | const generator = new MediaStreamTrackGenerator({kind: 'video'}); 34 | const {writable} = generator; 35 | croppedVideo.srcObject = new MediaStream([generator]); 36 | 37 | worker.postMessage({ 38 | operation: 'crop', 39 | readable, 40 | writable, 41 | }, [readable, writable]); 42 | }); 43 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-crop/js/worker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | function transform(frame, controller) { 12 | // Cropping from an existing video frame is supported by the API in Chrome 94+. 13 | const newFrame = new VideoFrame(frame, { 14 | visibleRect: { 15 | x: 320, 16 | width: 640, 17 | y: 180, 18 | height: 360, 19 | } 20 | }); 21 | controller.enqueue(newFrame); 22 | frame.close(); 23 | } 24 | 25 | onmessage = async (event) => { 26 | const {operation} = event.data; 27 | if (operation === 'crop') { 28 | const {readable, writable} = event.data; 29 | readable 30 | .pipeThrough(new TransformStream({transform})) 31 | .pipeTo(writable); 32 | } else { 33 | console.error('Unknown operation', operation); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | .video { 10 | --width: 45%; 11 | width: var(--width); 12 | height: calc(var(--width) * 0.75); 13 | vertical-align: top; 14 | } 15 | 16 | .sourceVideo { 17 | margin: 0 20px 20px 0; 18 | } 19 | 20 | .sinkVideo { 21 | margin: 0 0 20px 0; 22 | } 23 | 24 | div.box { 25 | margin: 1em; 26 | } 27 | 28 | @media screen and (max-width: 400px) { 29 | .video { 30 | height: 90px; 31 | width: calc(50% - 7px); 32 | } 33 | .sourceVideo { 34 | margin: 0 10px 20px 0; 35 | } 36 | .sinkVideo { 37 | margin: 0 0 10px 0; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/camera-source.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /* global VideoMirrorHelper */ // defined in video-mirror-helper.js 12 | 13 | /** 14 | * Opens the device's camera with getUserMedia. 15 | * @implements {MediaStreamSource} in pipeline.js 16 | */ 17 | class CameraSource { // eslint-disable-line no-unused-vars 18 | constructor() { 19 | /** 20 | * @private @const {!VideoMirrorHelper} manages displaying the video stream 21 | * in the page 22 | */ 23 | this.videoMirrorHelper_ = new VideoMirrorHelper(); 24 | /** @private {?MediaStream} camera stream, initialized in getMediaStream */ 25 | this.stream_ = null; 26 | /** @private {string} */ 27 | this.debugPath_ = ''; 28 | } 29 | /** @override */ 30 | setDebugPath(path) { 31 | this.debugPath_ = path; 32 | this.videoMirrorHelper_.setDebugPath(`${path}.videoMirrorHelper_`); 33 | } 34 | /** @override */ 35 | setVisibility(visible) { 36 | this.videoMirrorHelper_.setVisibility(visible); 37 | } 38 | /** @override */ 39 | async getMediaStream() { 40 | if (this.stream_) return this.stream_; 41 | console.log('[CameraSource] Requesting camera.'); 42 | this.stream_ = 43 | await navigator.mediaDevices.getUserMedia({audio: false, video: true}); 44 | console.log( 45 | '[CameraSource] Received camera stream.', 46 | `${this.debugPath_}.stream_ =`, this.stream_); 47 | this.videoMirrorHelper_.setStream(this.stream_); 48 | return this.stream_; 49 | } 50 | /** @override */ 51 | destroy() { 52 | console.log('[CameraSource] Stopping camera'); 53 | this.videoMirrorHelper_.destroy(); 54 | if (this.stream_) { 55 | this.stream_.getTracks().forEach(t => t.stop()); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/canvas-transform.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Applies a picture-frame effect using CanvasRenderingContext2D. 13 | * @implements {FrameTransform} in pipeline.js 14 | */ 15 | class CanvasTransform { // eslint-disable-line no-unused-vars 16 | constructor() { 17 | /** 18 | * @private {?OffscreenCanvas} canvas used to create the 2D context. 19 | * Initialized in init. 20 | */ 21 | this.canvas_ = null; 22 | /** 23 | * @private {?CanvasRenderingContext2D} the 2D context used to draw the 24 | * effect. Initialized in init. 25 | */ 26 | this.ctx_ = null; 27 | /** @private {string} */ 28 | this.debugPath_ = 'debug.pipeline.frameTransform_'; 29 | } 30 | /** @override */ 31 | async init() { 32 | console.log('[CanvasTransform] Initializing 2D context for transform'); 33 | this.canvas_ = new OffscreenCanvas(1, 1); 34 | this.ctx_ = /** @type {?CanvasRenderingContext2D} */ ( 35 | this.canvas_.getContext('2d', {alpha: false, desynchronized: true})); 36 | if (!this.ctx_) { 37 | throw new Error('Unable to create CanvasRenderingContext2D'); 38 | } 39 | console.log( 40 | '[CanvasTransform] CanvasRenderingContext2D initialized.', 41 | `${this.debugPath_}.canvas_ =`, this.canvas_, 42 | `${this.debugPath_}.ctx_ =`, this.ctx_); 43 | } 44 | 45 | /** @override */ 46 | async transform(frame, controller) { 47 | const ctx = this.ctx_; 48 | if (!this.canvas_ || !ctx) { 49 | frame.close(); 50 | return; 51 | } 52 | const width = frame.displayWidth; 53 | const height = frame.displayHeight; 54 | this.canvas_.width = width; 55 | this.canvas_.height = height; 56 | const timestamp = frame.timestamp; 57 | 58 | ctx.drawImage(frame, 0, 0); 59 | frame.close(); 60 | 61 | ctx.shadowColor = '#000'; 62 | ctx.shadowBlur = 20; 63 | ctx.lineWidth = 50; 64 | ctx.strokeStyle = '#000'; 65 | ctx.strokeRect(0, 0, width, height); 66 | 67 | // alpha: 'discard' is needed in order to send frames to a PeerConnection. 68 | controller.enqueue(new VideoFrame(this.canvas_, {timestamp, alpha: 'discard'})); 69 | } 70 | 71 | /** @override */ 72 | destroy() {} 73 | } 74 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/peer-connection-sink.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /* global PeerConnectionPipe */ // defined in peer-connection-pipe.js 12 | /* global VideoSink */ // defined in video-sink.js 13 | 14 | /** 15 | * Sends the transformed video to one end of an RTCPeerConnection and displays 16 | * the remote end in a video element. In this sample, a PeerConnectionSink 17 | * represents processing the local user's camera input using a 18 | * MediaStreamTrackProcessor before sending it to a remote video call 19 | * participant. Contrast with a PeerConnectionSource. 20 | * @implements {MediaStreamSink} in pipeline.js 21 | */ 22 | class PeerConnectionSink { // eslint-disable-line no-unused-vars 23 | constructor() { 24 | /** 25 | * @private @const {!VideoSink} manages displaying the video stream in the 26 | * page 27 | */ 28 | this.videoSink_ = new VideoSink(); 29 | /** 30 | * @private {?PeerConnectionPipe} handles piping the MediaStream through an 31 | * RTCPeerConnection 32 | */ 33 | this.pipe_ = null; 34 | /** @private {string} */ 35 | this.debugPath_ = 'debug.pipeline.sink_'; 36 | this.videoSink_.setDebugPath(`${this.debugPath_}.videoSink_`); 37 | } 38 | 39 | /** @override */ 40 | async setMediaStream(stream) { 41 | console.log( 42 | '[PeerConnectionSink] Setting peer connection sink stream.', stream); 43 | if (this.pipe_) this.pipe_.destroy(); 44 | this.pipe_ = new PeerConnectionPipe(stream, `${this.debugPath_}.pipe_`); 45 | const pipedStream = await this.pipe_.getOutputStream(); 46 | console.log( 47 | '[PeerConnectionSink] Received callee peer connection stream.', 48 | pipedStream); 49 | await this.videoSink_.setMediaStream(pipedStream); 50 | } 51 | 52 | /** @override */ 53 | destroy() { 54 | this.videoSink_.destroy(); 55 | if (this.pipe_) this.pipe_.destroy(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/simple-transforms.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Does nothing. 13 | * @implements {FrameTransform} in pipeline.js 14 | */ 15 | class NullTransform { // eslint-disable-line no-unused-vars 16 | /** @override */ 17 | async init() {} 18 | /** @override */ 19 | async transform(frame, controller) { 20 | controller.enqueue(frame); 21 | } 22 | /** @override */ 23 | destroy() {} 24 | } 25 | 26 | /** 27 | * Drops frames at random. 28 | * @implements {FrameTransform} in pipeline.js 29 | */ 30 | class DropTransform { // eslint-disable-line no-unused-vars 31 | /** @override */ 32 | async init() {} 33 | /** @override */ 34 | async transform(frame, controller) { 35 | if (Math.random() < 0.5) { 36 | controller.enqueue(frame); 37 | } else { 38 | frame.close(); 39 | } 40 | } 41 | /** @override */ 42 | destroy() {} 43 | } 44 | 45 | /** 46 | * Delays all frames by 100ms. 47 | * @implements {FrameTransform} in pipeline.js 48 | */ 49 | class DelayTransform { // eslint-disable-line no-unused-vars 50 | /** @override */ 51 | async init() {} 52 | /** @override */ 53 | async transform(frame, controller) { 54 | await new Promise(resolve => setTimeout(resolve, 100)); 55 | controller.enqueue(frame); 56 | } 57 | /** @override */ 58 | destroy() {} 59 | } 60 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/video-mirror-helper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Helper to display a MediaStream in an HTMLVideoElement, based on the 13 | * visibility setting. 14 | */ 15 | class VideoMirrorHelper { // eslint-disable-line no-unused-vars 16 | constructor() { 17 | /** @private {boolean} */ 18 | this.visibility_ = false; 19 | /** @private {?MediaStream} the stream to display */ 20 | this.stream_ = null; 21 | /** 22 | * @private {?HTMLVideoElement} video element mirroring the camera stream. 23 | * Set if visibility_ is true and stream_ is set. 24 | */ 25 | this.video_ = null; 26 | /** @private {string} */ 27 | this.debugPath_ = ''; 28 | } 29 | /** 30 | * Sets the path to this object from the debug global var. 31 | * @param {string} path 32 | */ 33 | setDebugPath(path) { 34 | this.debugPath_ = path; 35 | } 36 | /** 37 | * Indicates if the video should be mirrored/displayed on the page. 38 | * @param {boolean} visible whether to add the video from the source stream to 39 | * the page 40 | */ 41 | setVisibility(visible) { 42 | this.visibility_ = visible; 43 | if (this.video_ && !this.visibility_) { 44 | this.video_.parentNode.removeChild(this.video_); 45 | this.video_ = null; 46 | } 47 | this.maybeAddVideoElement_(); 48 | } 49 | 50 | /** 51 | * @param {!MediaStream} stream 52 | */ 53 | setStream(stream) { 54 | this.stream_ = stream; 55 | this.maybeAddVideoElement_(); 56 | } 57 | 58 | /** @private */ 59 | maybeAddVideoElement_() { 60 | if (!this.video_ && this.visibility_ && this.stream_) { 61 | this.video_ = 62 | /** @type {!HTMLVideoElement} */ (document.createElement('video')); 63 | console.log( 64 | '[VideoMirrorHelper] Adding source video mirror.', 65 | `${this.debugPath_}.video_ =`, this.video_); 66 | this.video_.classList.add('video', 'sourceVideo'); 67 | this.video_.srcObject = this.stream_; 68 | const outputVideoContainer = 69 | document.getElementById('outputVideoContainer'); 70 | outputVideoContainer.parentNode.insertBefore( 71 | this.video_, outputVideoContainer); 72 | this.video_.play(); 73 | } 74 | } 75 | 76 | /** Frees any resources used by this object. */ 77 | destroy() { 78 | if (this.video_) { 79 | this.video_.pause(); 80 | this.video_.srcObject = null; 81 | this.video_.parentNode.removeChild(this.video_); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/video-sink.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Displays the output stream in a video element. 13 | * @implements {MediaStreamSink} in pipeline.js 14 | */ 15 | class VideoSink { // eslint-disable-line no-unused-vars 16 | constructor() { 17 | /** 18 | * @private {?HTMLVideoElement} output video element 19 | */ 20 | this.video_ = null; 21 | /** @private {string} */ 22 | this.debugPath_ = 'debug.pipeline.sink_'; 23 | } 24 | /** 25 | * Sets the path to this object from the debug global var. 26 | * @param {string} path 27 | */ 28 | setDebugPath(path) { 29 | this.debugPath_ = path; 30 | } 31 | /** @override */ 32 | async setMediaStream(stream) { 33 | console.log('[VideoSink] Setting sink stream.', stream); 34 | if (!this.video_) { 35 | this.video_ = 36 | /** @type {!HTMLVideoElement} */ (document.createElement('video')); 37 | this.video_.classList.add('video', 'sinkVideo'); 38 | document.getElementById('outputVideoContainer').appendChild(this.video_); 39 | console.log( 40 | '[VideoSink] Added video element to page.', 41 | `${this.debugPath_}.video_ =`, this.video_); 42 | } 43 | this.video_.srcObject = stream; 44 | this.video_.play(); 45 | } 46 | /** @override */ 47 | destroy() { 48 | if (this.video_) { 49 | console.log('[VideoSink] Stopping sink video'); 50 | this.video_.pause(); 51 | this.video_.srcObject = null; 52 | this.video_.parentNode.removeChild(this.video_); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/webcodec-transform.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Encodes and decodes frames using the WebCodec API. 13 | * @implements {FrameTransform} in pipeline.js 14 | */ 15 | class WebCodecTransform { // eslint-disable-line no-unused-vars 16 | constructor() { 17 | // Encoder and decoder are initialized in init() 18 | this.decoder_ = null; 19 | this.encoder_ = null; 20 | this.controller_ = null; 21 | } 22 | /** @override */ 23 | async init() { 24 | console.log('[WebCodecTransform] Initializing encoder and decoder'); 25 | this.decoder_ = new VideoDecoder({ 26 | output: frame => this.handleDecodedFrame(frame), 27 | error: this.error 28 | }); 29 | this.encoder_ = new VideoEncoder({ 30 | output: frame => this.handleEncodedFrame(frame), 31 | error: this.error 32 | }); 33 | this.encoder_.configure({codec: 'vp8', width: 640, height: 480}); 34 | this.decoder_.configure({codec: 'vp8', width: 640, height: 480}); 35 | } 36 | 37 | /** @override */ 38 | async transform(frame, controller) { 39 | if (!this.encoder_) { 40 | frame.close(); 41 | return; 42 | } 43 | try { 44 | this.controller_ = controller; 45 | this.encoder_.encode(frame); 46 | } finally { 47 | frame.close(); 48 | } 49 | } 50 | 51 | /** @override */ 52 | destroy() {} 53 | 54 | /* Helper functions */ 55 | handleEncodedFrame(encodedFrame) { 56 | this.decoder_.decode(encodedFrame); 57 | } 58 | 59 | handleDecodedFrame(videoFrame) { 60 | if (!this.controller_) { 61 | videoFrame.close(); 62 | return; 63 | } 64 | this.controller_.enqueue(videoFrame); 65 | } 66 | 67 | error(e) { 68 | console.log('[WebCodecTransform] Bad stuff happened: ' + e); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | Page move 14 | 15 | 16 |

The page has moved to: 17 | this page

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/insertable-streams/webgpu/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | video { 10 | width: 480px; 11 | height: 270px; 12 | } 13 | 14 | .output { 15 | width: 960px; 16 | height: 540px; 17 | margin: 0px 0px 0px 0px; 18 | } 19 | 20 | .error { 21 | font-size: 20px; 22 | color:red; 23 | } 24 | -------------------------------------------------------------------------------- /src/content/insertable-streams/webgpu/js/multi_video_worker.js: -------------------------------------------------------------------------------- 1 | importScripts('./multi_video_main.js'); 2 | 'use strict'; 3 | 4 | let mainTransform = null; 5 | 6 | /* global WebGPUTransform */ // defined in multi_video_main.js 7 | 8 | onmessage = async (event) => { 9 | const {operation} = event.data; 10 | if (operation === 'init') { 11 | mainTransform = new WebGPUTransform(); 12 | const {canvas} = event.data; 13 | const msg = await mainTransform.init(canvas); 14 | if (msg) { 15 | postMessage({error: msg}); 16 | } else { 17 | postMessage({result: 'Done'}); 18 | } 19 | } else if (operation === 'transform') { 20 | const {videoStream, gumStream} = event.data; 21 | mainTransform.transform(videoStream, gumStream); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/content/insertable-streams/webgpu/js/multi_video_worker_manager.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | let worker; 5 | let screenCanvas; 6 | 7 | // eslint-disable-next-line no-unused-vars 8 | class WebGPUWorker { 9 | async init() { 10 | screenCanvas = document.createElement('canvas'); 11 | document.getElementById('outputVideo').append(screenCanvas); 12 | screenCanvas.width = 960; 13 | screenCanvas.height = 540; 14 | 15 | worker = new Worker('./js/multi_video_worker.js'); 16 | console.log('Created a worker thread.'); 17 | const offScreen = screenCanvas.transferControlToOffscreen(); 18 | 19 | const onMessage = new Promise((resolve, reject) => { 20 | worker.addEventListener('message', function handleMsgFromWorker(msg) { 21 | if (msg.data.error) { 22 | document.getElementById('errorMsg').innerText = msg.data.error; 23 | reject(msg.data.error); 24 | } 25 | if (msg.data.result === 'Done') { 26 | resolve(); 27 | } 28 | }); 29 | }); 30 | worker.postMessage( 31 | { 32 | operation: 'init', 33 | canvas: offScreen, 34 | }, [offScreen]); 35 | 36 | await onMessage; 37 | } 38 | 39 | transform(videoStream, gumStream) { 40 | if (videoStream && gumStream) { 41 | worker.postMessage( 42 | { 43 | operation: 'transform', 44 | videoStream: videoStream, 45 | gumStream: gumStream, 46 | }, [videoStream, gumStream]); 47 | } 48 | } 49 | 50 | destroy() { 51 | if (screenCanvas.parentNode) { 52 | screenCanvas.parentNode.removeChild(screenCanvas); 53 | } 54 | worker.terminate(); 55 | console.log('Worker thread destroyed.'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/content/peerconnection/audio/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | audio { 9 | display: inline-block; 10 | position: relative; 11 | top: 9px; 12 | width: calc(100% - 120px); 13 | } 14 | 15 | button { 16 | margin: 0 20px 0 0; 17 | width: 96px; 18 | } 19 | 20 | table { 21 | border-collapse: collapse; 22 | } 23 | 24 | th, td { 25 | border: 1px solid black; 26 | } 27 | 28 | tr:hover { 29 | background-color: #f5f5f5; 30 | } 31 | 32 | div#audio { 33 | margin: 0 0 29px 0; 34 | } 35 | 36 | div#audio > div { 37 | margin: 0 0 20px 0; 38 | } 39 | 40 | div.label { 41 | display: inline-block; 42 | font-weight: 400; 43 | width: 120px; 44 | } 45 | 46 | div.graph-container { 47 | float: left; 48 | margin: 0.5em; 49 | width: calc(50% - 1em); 50 | } 51 | 52 | a#viewSource { 53 | clear: both; 54 | } 55 | -------------------------------------------------------------------------------- /src/content/peerconnection/audio/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 'use strict'; 10 | const webdriver = require('selenium-webdriver'); 11 | const seleniumHelpers = require('../../../../../test/webdriver'); 12 | 13 | let driver; 14 | const path = '/src/content/peerconnection/audio/index.html'; 15 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 16 | 17 | describe('audio-only peerconnection', () => { 18 | beforeAll(async () => { 19 | driver = await seleniumHelpers.buildDriver(); 20 | }); 21 | afterAll(() => { 22 | return driver.quit(); 23 | }); 24 | 25 | beforeEach(() => { 26 | return driver.get(url); 27 | }); 28 | 29 | it('establishes a connection', async () => { 30 | await driver.findElement(webdriver.By.id('callButton')).click(); 31 | await driver.wait(() => driver.executeScript(() => { 32 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 33 | })); 34 | await driver.wait(() => driver.executeScript(() => { 35 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 36 | })); 37 | await driver.wait(() => driver.executeScript(() => { 38 | return document.getElementById('audio2').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 39 | })); 40 | }); 41 | 42 | // TODO: assert usage of different codecs via getStats. 43 | }); 44 | 45 | -------------------------------------------------------------------------------- /src/content/peerconnection/bandwidth/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | video { 9 | --width: 45%; 10 | width: var(--width); 11 | height: calc(var(--width) * 0.75); 12 | } 13 | 14 | button { 15 | margin: 0 20px 0 0; 16 | width: 96px; 17 | } 18 | 19 | video#localVideo { 20 | margin: 0 20px 20px 0; 21 | } 22 | 23 | div.label { 24 | display: inline-block; 25 | font-weight: 400; 26 | width: 120px; 27 | } 28 | 29 | div.graph-container { 30 | float: left; 31 | margin: 0.5em; 32 | width: calc(50% - 1em); 33 | } 34 | 35 | a#viewSource { 36 | clear: both; 37 | } 38 | -------------------------------------------------------------------------------- /src/content/peerconnection/change-codecs/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/peerconnection/change-codecs/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/change-codecs/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection with setCodecPreferences', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | ['video/VP8'].forEach(codec => { 31 | it('establishes a connection', async () => { 32 | await driver.findElement(webdriver.By.id('startButton')).click(); 33 | 34 | await driver.wait(() => driver.executeScript(() => { 35 | return localStream !== null; // eslint-disable-line no-undef 36 | })); 37 | await driver.wait(() => driver.executeScript(() => { 38 | return codecPreferences.disabled === false; // eslint-disable-line no-undef 39 | })); 40 | await driver.findElement(webdriver.By.id('codecPreferences')).click(); 41 | await driver.findElement(webdriver.By.css('option[value=\'video/VP8\']')).click(); 42 | 43 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 44 | await driver.findElement(webdriver.By.id('callButton')).click(); 45 | 46 | await Promise.all([ 47 | await driver.wait(() => driver.executeScript(() => { 48 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 49 | })), 50 | await driver.wait(() => driver.executeScript(() => { 51 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 52 | })), 53 | ]); 54 | 55 | await driver.wait(() => driver.executeScript(() => { 56 | return document.getElementById('remoteVideo').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 57 | })); 58 | 59 | await driver.wait(() => driver.executeScript(() => { 60 | return document.getElementById('actualCodec').innerText !== ''; 61 | })); 62 | const actualCodec = await driver.findElement(webdriver.By.id('actualCodec')).getAttribute('innerText'); 63 | expect(actualCodec.startsWith('Using ' + codec)).toBe(true); 64 | }); 65 | }); 66 | }); 67 | 68 | -------------------------------------------------------------------------------- /src/content/peerconnection/channel/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/peerconnection/channel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection between two tabs 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |

WebRTC samples 36 | Peer connection

37 | 38 |

This sample shows how to setup a connection between two peers in different tabs using 39 | RTCPeerConnection 40 | and Broadcast Channel 41 |

42 | 43 | 44 | 45 | 46 |
47 | 48 | 49 |
50 | 51 |

Click the start button in two tabs (of the same browser; can be in different windows) to make a call

52 | 53 | View source on GitHub 55 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/content/peerconnection/channel/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/channel/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection and broadcast channels', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(async () => { 27 | await driver.get(url); 28 | }); 29 | 30 | it('establishes a connection and hangs up', async () => { 31 | const firstTab = await driver.getWindowHandle(); 32 | 33 | await driver.switchTo().window(firstTab); 34 | await driver.findElement(webdriver.By.id('startButton')).click(); 35 | await driver.wait(() => driver.executeScript(() => { 36 | return localStream !== null; // eslint-disable-line no-undef 37 | })); 38 | 39 | // Create a second tab, switch to it. 40 | await driver.switchTo().newWindow('tab'); 41 | const secondTab = await driver.getWindowHandle(); 42 | await driver.get(url); 43 | 44 | await driver.switchTo().window(secondTab); 45 | await driver.findElement(webdriver.By.id('startButton')).click(); 46 | await driver.wait(() => driver.executeScript(() => { 47 | return localStream !== null; // eslint-disable-line no-undef 48 | })); 49 | 50 | // Assert state in first tab. 51 | await driver.switchTo().window(firstTab); 52 | await driver.wait(() => driver.executeScript(() => { 53 | return pc && pc.connectionState === 'connected'; // eslint-disable-line no-undef 54 | })); 55 | await driver.wait(() => driver.executeScript(() => { 56 | return document.getElementById('remoteVideo').readyState === 4; 57 | })); 58 | 59 | // Assert state in second tab. 60 | await driver.switchTo().window(secondTab); 61 | await driver.wait(() => driver.executeScript(() => { 62 | return pc && pc.connectionState === 'connected'; // eslint-disable-line no-undef 63 | })); 64 | await driver.wait(() => driver.executeScript(() => { 65 | return document.getElementById('remoteVideo').readyState === 4; 66 | })); 67 | }); 68 | }); 69 | 70 | -------------------------------------------------------------------------------- /src/content/peerconnection/constraints/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 25px 0; 10 | vertical-align: top; 11 | width: 134px; 12 | } 13 | 14 | div#getUserMedia { 15 | padding: 0 0 8px 0; 16 | } 17 | 18 | div.input { 19 | display: inline-block; 20 | margin: 0 4px 0 0; 21 | vertical-align: top; 22 | width: 310px; 23 | } 24 | 25 | div.input > div { 26 | margin: 0 0 20px 0; 27 | vertical-align: top; 28 | } 29 | 30 | div.output { 31 | background-color: #eee; 32 | display: inline-block; 33 | font-family: 'Inconsolata', 'Courier New', monospace; 34 | font-size: 0.9em; 35 | padding: 10px 10px 10px 25px; 36 | position: relative; 37 | top: 10px; 38 | white-space: pre; 39 | width: 270px; 40 | } 41 | 42 | section#statistics div { 43 | display: inline-block; 44 | font-family: 'Inconsolata', 'Courier New', monospace; 45 | vertical-align: top; 46 | width: 308px; 47 | } 48 | 49 | section#statistics div#senderStats { 50 | margin: 0 20px 0 0; 51 | } 52 | 53 | section#constraints > div { 54 | margin: 0 0 20px 0; 55 | } 56 | 57 | section#video > div { 58 | display: inline-block; 59 | margin: 0 20px 0 0; 60 | vertical-align: top; 61 | width: calc(50% - 22px); 62 | } 63 | 64 | section#video > div div { 65 | font-size: 0.9em; 66 | margin: 0 0 0.5em 0; 67 | width: 320px; 68 | } 69 | 70 | h2 { 71 | margin: 0 0 1em 0; 72 | } 73 | 74 | section#constraints label { 75 | display: inline-block; 76 | width: 156px; 77 | } 78 | 79 | section { 80 | margin: 0 0 20px 0; 81 | padding: 0 0 15px 0; 82 | } 83 | 84 | section#video { 85 | width: calc(100% + 20px); 86 | } 87 | 88 | video { 89 | --width: 90%; 90 | width: var(--width); 91 | height: calc(var(--width) * 0.75); 92 | margin: 0 0 10px 0; 93 | } 94 | 95 | @media screen and (max-width: 720px) { 96 | button { 97 | font-weight: 500; 98 | height: 56px; 99 | line-height: 1.3em; 100 | width: 90px; 101 | } 102 | 103 | div#getUserMedia { 104 | padding: 0 0 40px 0; 105 | } 106 | 107 | section#statistics div { 108 | width: calc(50% - 14px); 109 | } 110 | 111 | video { 112 | height: 96px; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/content/peerconnection/create-offer/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 20px 10px 0 0; 11 | width: 100px; 12 | } 13 | 14 | div#constraints { 15 | margin: 0 0 20px 0; 16 | } 17 | 18 | div#numAudioTracks { 19 | margin: 0 0 20px 0; 20 | } 21 | 22 | div#constraints div { 23 | margin: 0 0 10px 0; 24 | } 25 | 26 | div#constraints input { 27 | margin: 0 10px 0 0; 28 | position: relative; 29 | top: -2px; 30 | } 31 | 32 | div#numAudioTracks input { 33 | max-width: 30%; 34 | position: relative; 35 | top: 2px; 36 | width: 200px; 37 | } 38 | 39 | label { 40 | font-weight: 500; 41 | margin: 0 10px 0 0; 42 | } 43 | 44 | textarea { 45 | height: 200px; 46 | width: 100%; 47 | } 48 | -------------------------------------------------------------------------------- /src/content/peerconnection/create-offer/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const audioInput = document.querySelector('input#audio'); 12 | const restartInput = document.querySelector('input#restart'); 13 | const vadInput = document.querySelector('input#vad'); 14 | const videoInput = document.querySelector('input#video'); 15 | 16 | const numAudioTracksInput = document.querySelector('div#numAudioTracks input'); 17 | const numAudioTracksDisplay = document.querySelector('span#numAudioTracksDisplay'); 18 | const outputTextarea = document.querySelector('textarea#output'); 19 | const createOfferButton = document.querySelector('button#createOffer'); 20 | 21 | createOfferButton.addEventListener('click', createOffer); 22 | numAudioTracksInput.addEventListener('change', e => numAudioTracksDisplay.innerText = e.target.value); 23 | 24 | async function createOffer() { 25 | outputTextarea.value = ''; 26 | const peerConnection = window.peerConnection = new RTCPeerConnection(null); 27 | const numRequestedAudioTracks = parseInt(numAudioTracksInput.value); 28 | 29 | for (let i = 0; i < numRequestedAudioTracks; i++) { 30 | const acx = new AudioContext(); 31 | const dst = acx.createMediaStreamDestination(); 32 | 33 | // Fill up the peer connection with numRequestedAudioTracks number of tracks. 34 | const track = dst.stream.getTracks()[0]; 35 | peerConnection.addTrack(track, dst.stream); 36 | } 37 | 38 | const offerOptions = { 39 | // New spec states offerToReceiveAudio/Video are of type long (due to 40 | // having to tell how many "m" lines to generate). 41 | // http://w3c.github.io/webrtc-pc/#idl-def-RTCOfferAnswerOptions. 42 | offerToReceiveAudio: (audioInput.checked) ? 1 : 0, 43 | offerToReceiveVideo: (videoInput.checked) ? 1 : 0, 44 | iceRestart: restartInput.checked, 45 | voiceActivityDetection: vadInput.checked 46 | }; 47 | 48 | try { 49 | const offer = await peerConnection.createOffer(offerOptions); 50 | await peerConnection.setLocalDescription(offer); 51 | outputTextarea.value = offer.sdp; 52 | } catch (e) { 53 | outputTextarea.value = `Failed to create offer: ${e}`; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/content/peerconnection/dtmf/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 20px 0; 10 | width: 96px; 11 | } 12 | 13 | div#dialPad button { 14 | background-color: #ddd; 15 | border: 1px solid #ccc; 16 | color: black; 17 | font-size: 1em; 18 | font-weight: 400; 19 | height: 40px; 20 | margin: 0 10px 10px 0; 21 | width: 40px; 22 | } 23 | 24 | div#dialPad button:hover { 25 | background-color: #aaa; 26 | } 27 | 28 | div#dialPad button:active { 29 | background-color: #888; 30 | } 31 | 32 | div#dialPad { 33 | display: inline-block; 34 | margin: 0 20px 20px 0; 35 | vertical-align: top; 36 | } 37 | 38 | div#parameters { 39 | margin: 0 0 25px 0; 40 | } 41 | 42 | div#parameters > div { 43 | height: 28px; 44 | margin: 0 0 10px 0; 45 | } 46 | 47 | div#dtmf { 48 | background-color: #eee; 49 | display: inline-block; 50 | height: 180px; 51 | margin: 0 0 20px 0; 52 | padding: 5px 5px 5px 10px; 53 | width: calc(100% - 239px); 54 | } 55 | 56 | div#dtmf div { 57 | font-family: 'Inconsolata', 'Courier New', monospace; 58 | } 59 | 60 | div#sentTones { 61 | display: inline-block; 62 | line-height: 1.2em; 63 | } 64 | 65 | div#dtmfStatus { 66 | margin: 0 0 10px 0; 67 | } 68 | 69 | div#parameters input[type = range] { 70 | font-size: 1em; 71 | width: 85px; 72 | } 73 | 74 | div#parameters input#tones { 75 | width: calc(100% - 78px); 76 | } 77 | 78 | div#parameters label { 79 | display: inline-block; 80 | font-weight: 400; 81 | height: 28px; 82 | position: relative; 83 | top: 4px; 84 | vertical-align: top; 85 | width: 68px; 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/content/peerconnection/endtoend-encryption/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | Page move 14 | 15 | 16 |

The page has moved to: 17 | this page

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple-relay/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 20px 10px 0 0; 11 | width: 100px; 12 | } 13 | 14 | div#buttons { 15 | margin: 0 0 20px 0; 16 | } 17 | 18 | div#status { 19 | height: 2em; 20 | margin: 1em 0 0 0; 21 | } 22 | 23 | input#audio { 24 | margin: 0 0.5em 0 0; 25 | position: relative; 26 | top: -1px; 27 | } 28 | 29 | video { 30 | --width: 45%; 31 | width: var(--width); 32 | height: calc(var(--width) * 0.75); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple-relay/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection relay 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Peer connection relay 37 |

38 | 39 |
40 | 41 | 42 |
43 | 44 |
45 | 46 |
47 | 48 | 49 | 50 | 51 |
52 | 53 |
54 | 55 | View source on GitHub 57 | 58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple-relay/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /* global VideoPipe */ 12 | 13 | const video1 = document.querySelector('video#video1'); 14 | const video2 = document.querySelector('video#video2'); 15 | 16 | const statusDiv = document.querySelector('div#status'); 17 | 18 | const audioCheckbox = document.querySelector('input#audio'); 19 | 20 | const startButton = document.querySelector('button#start'); 21 | const callButton = document.querySelector('button#call'); 22 | const insertRelayButton = document.querySelector('button#insertRelay'); 23 | const hangupButton = document.querySelector('button#hangup'); 24 | 25 | startButton.onclick = start; 26 | callButton.onclick = call; 27 | insertRelayButton.onclick = insertRelay; 28 | hangupButton.onclick = hangup; 29 | 30 | const pipes = []; 31 | 32 | let localStream; 33 | let remoteStream; 34 | 35 | function gotStream(stream) { 36 | console.log('Received local stream'); 37 | video1.srcObject = stream; 38 | localStream = stream; 39 | callButton.disabled = false; 40 | } 41 | 42 | function gotremoteStream(stream) { 43 | remoteStream = stream; 44 | video2.srcObject = stream; 45 | console.log('Received remote stream'); 46 | console.log(`${pipes.length} element(s) in chain`); 47 | statusDiv.textContent = `${pipes.length} element(s) in chain`; 48 | insertRelayButton.disabled = false; 49 | } 50 | 51 | function start() { 52 | console.log('Requesting local stream'); 53 | startButton.disabled = true; 54 | const options = audioCheckbox.checked ? {audio: true, video: true} : {audio: false, video: true}; 55 | navigator.mediaDevices 56 | .getUserMedia(options) 57 | .then(gotStream) 58 | .catch(function(e) { 59 | alert('getUserMedia() failed'); 60 | console.log('getUserMedia() error: ', e); 61 | }); 62 | } 63 | 64 | function call() { 65 | callButton.disabled = true; 66 | insertRelayButton.disabled = false; 67 | hangupButton.disabled = false; 68 | console.log('Starting call'); 69 | pipes.push(new VideoPipe(localStream, gotremoteStream)); 70 | } 71 | 72 | function insertRelay() { 73 | pipes.push(new VideoPipe(remoteStream, gotremoteStream)); 74 | insertRelayButton.disabled = true; 75 | } 76 | 77 | function hangup() { 78 | console.log('Ending call'); 79 | while (pipes.length > 0) { 80 | const pipe = pipes.pop(); 81 | pipe.close(); 82 | } 83 | insertRelayButton.disabled = true; 84 | hangupButton.disabled = true; 85 | callButton.disabled = false; 86 | } 87 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | margin: 0 0 20px 0; 19 | --width: 40%; 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | } 23 | 24 | video#video1 { 25 | margin: 0 20px 20px 0; 26 | } 27 | 28 | @media screen and (max-width: 400px) { 29 | button { 30 | margin: 0 11px 10px 0; 31 | } 32 | 33 | video { 34 | height: 90px; 35 | margin: 0 0 10px 0; 36 | width: calc(50% - 8px); 37 | } 38 | 39 | video#video1 { 40 | margin: 0 10px 10px 0; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Multiple peer connections 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Multiple peer connections 37 |

38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 47 |
48 | 49 |

View the console to see logging and to inspect the MediaStream object localStream.

50 | 51 |

For more information about RTCPeerConnection, see Getting 53 | Started With WebRTC.

54 | 55 | 56 | View source on GitHub 58 | 59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/multiple/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('multiple peerconnections', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes multiple connections and hangs up', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | 33 | await driver.wait(() => driver.executeScript(() => { 34 | return localStream !== null; // eslint-disable-line no-undef 35 | })); 36 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 37 | await driver.findElement(webdriver.By.id('callButton')).click(); 38 | 39 | await Promise.all([ 40 | driver.wait(() => driver.executeScript(() => { 41 | return pc1Remote && pc1Remote.connectionState === 'connected'; // eslint-disable-line no-undef 42 | })), 43 | await driver.wait(() => driver.executeScript(() => { 44 | return pc2Remote && pc2Remote.connectionState === 'connected'; // eslint-disable-line no-undef 45 | })), 46 | ]); 47 | 48 | await Promise.all([ 49 | await driver.wait(() => driver.executeScript(() => { 50 | return document.getElementById('video2').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 51 | })), 52 | await driver.wait(() => driver.executeScript(() => { 53 | return document.getElementById('video3').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 54 | })), 55 | ]); 56 | 57 | await driver.findElement(webdriver.By.id('hangupButton')).click(); 58 | 59 | await Promise.all([ 60 | await driver.wait(() => driver.executeScript(() => { 61 | return pc1Remote === null; // eslint-disable-line no-undef 62 | })), 63 | await driver.wait(() => driver.executeScript(() => { 64 | return pc2Remote === null; // eslint-disable-line no-undef 65 | })), 66 | ]); 67 | }); 68 | }); 69 | 70 | -------------------------------------------------------------------------------- /src/content/peerconnection/munge-sdp/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 20px 0; 10 | vertical-align: top; 11 | width: 155px; 12 | } 13 | div#buttons { 14 | border-bottom: 1px solid #eee; 15 | margin: 1em 0 1em 0; 16 | padding: 0 0 1em 0; 17 | } 18 | div#local { 19 | margin: 0 20px 0 0; 20 | } 21 | div#preview { 22 | border-bottom: 1px solid #eee; 23 | margin: 0 0 1em 0; 24 | padding: 0 0 0.5em 0; 25 | } 26 | div#preview > div { 27 | display: inline-block; 28 | vertical-align: top; 29 | width: calc(50% - 12px); 30 | } 31 | div#select { 32 | margin: 0 0 1em 0; 33 | } 34 | div#selectSource { 35 | margin: 0 0 1em 0; 36 | } 37 | div.source { 38 | display: inline-block; 39 | margin: 0 0 1em 0; 40 | } 41 | form { 42 | margin: 0 0 1em 0; 43 | white-space: nowrap; 44 | } 45 | h2 { 46 | margin: 0 0 0.5em 0; 47 | } 48 | label { 49 | margin: 0 0.4em 0 0; 50 | } 51 | textarea { 52 | color: #444; 53 | font-size: 0.9em; 54 | font-weight: 300; 55 | height: 7.0em; 56 | padding: 5px; 57 | width: calc(100% - 10px); 58 | } 59 | video { 60 | height: 225px; 61 | } 62 | 63 | @media screen and (max-width: 550px) { 64 | button { 65 | font-weight: 500; 66 | height: 56px; 67 | line-height: 1.3em; 68 | margin: 0 7px 15px 0; 69 | width: 86px; 70 | } 71 | button:nth-child(3n+0) { 72 | margin: 0 0 15px 0; 73 | } 74 | video { 75 | height: 96px; 76 | } 77 | } 78 | 79 | @media screen and (max-width: 800px) { 80 | button { 81 | margin: 0 15px 15px 0; 82 | width: 155px; 83 | } 84 | div#selectSource { 85 | margin: 0 0 0.5em 0; 86 | } 87 | div.source { 88 | margin: 0 0 .2em 0; 89 | } 90 | select { 91 | margin: 0 1.5em 0 0; 92 | } 93 | textarea { 94 | font-size: 0.7em; 95 | } 96 | } 97 | 98 | @media screen and (max-width: 500px) { 99 | textarea { 100 | font-size: 0.5em; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/content/peerconnection/munge-sdp/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/munge-sdp/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection sdp munging', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes a connection and allows hangup and new offer', async () => { 31 | await driver.findElement(webdriver.By.id('getMedia')).click(); 32 | 33 | await driver.wait(() => driver.findElement(webdriver.By.id('createPeerConnection')).isEnabled()); 34 | await driver.findElement(webdriver.By.id('createPeerConnection')).click(); 35 | 36 | await driver.wait(() => driver.findElement(webdriver.By.id('createOffer')).isEnabled()); 37 | await driver.findElement(webdriver.By.id('createOffer')).click(); 38 | 39 | await driver.wait(() => driver.findElement(webdriver.By.id('setOffer')).isEnabled()); 40 | await driver.findElement(webdriver.By.id('setOffer')).click(); 41 | 42 | await driver.wait(() => driver.findElement(webdriver.By.id('createAnswer')).isEnabled()); 43 | await driver.findElement(webdriver.By.id('createAnswer')).click(); 44 | 45 | await driver.wait(() => driver.findElement(webdriver.By.id('setAnswer')).isEnabled()); 46 | await driver.findElement(webdriver.By.id('setAnswer')).click(); 47 | 48 | await driver.wait(() => driver.findElement(webdriver.By.id('hangup')).isEnabled()); 49 | await driver.wait(() => driver.findElement(webdriver.By.id('createOffer')).isEnabled()); 50 | 51 | await Promise.all([ 52 | await driver.wait(() => driver.executeScript(() => { 53 | return localPeerConnection && localPeerConnection.connectionState === 'connected'; // eslint-disable-line no-undef 54 | })), 55 | await driver.wait(() => driver.executeScript(() => { 56 | return remotePeerConnection && remotePeerConnection.connectionState === 'connected'; // eslint-disable-line no-undef 57 | })), 58 | ]); 59 | }); 60 | 61 | // TODO: add test to ensure the text fields are properly filled 62 | }); 63 | -------------------------------------------------------------------------------- /src/content/peerconnection/negotiate-timing/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | @media screen and (max-width: 400px) { 30 | button { 31 | width: 83px; 32 | margin: 0 11px 10px 0; 33 | } 34 | 35 | video { 36 | height: 90px; 37 | margin: 0 0 10px 0; 38 | width: calc(50% - 7px); 39 | } 40 | video#localVideo { 41 | margin: 0 10px 20px 0; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/content/peerconnection/negotiate-timing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection - Renegotiate 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |

WebRTC samples 36 | Peer connection negotiation timing

37 | 38 | 39 | 40 | 41 |
42 | 43 | 44 | 45 | 46 |
47 |
48 |

49 | Video sections after renegotiating: 50 |

51 |
52 |

View the console to see logging. The MediaStream object localStream, and the RTCPeerConnection 53 | objects pc1 and pc2 are in global scope, so you can inspect them in the console as 54 | well.

55 |

56 |

57 | Log goes here 58 |
59 |

60 | View source on GitHub 62 | 63 |
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/content/peerconnection/negotiate-timing/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/negotiate-timing/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection with negotiation timing', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes a connection, renegotiates and hangs up', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | 33 | await driver.wait(() => driver.executeScript(() => { 34 | return localStream !== null; // eslint-disable-line no-undef 35 | })); 36 | 37 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 38 | await driver.findElement(webdriver.By.id('callButton')).click(); 39 | 40 | await Promise.all([ 41 | await driver.wait(() => driver.executeScript(() => { 42 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 43 | })), 44 | await driver.wait(() => driver.executeScript(() => { 45 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 46 | })), 47 | ]); 48 | 49 | await driver.wait(() => driver.executeScript(() => { 50 | return document.getElementById('remoteVideo').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 51 | })); 52 | 53 | await driver.wait(() => driver.findElement(webdriver.By.id('renegotiateButton')).isEnabled()); 54 | await driver.findElement(webdriver.By.id('renegotiateButton')).click(); 55 | 56 | await driver.wait(() => driver.executeScript(() => { 57 | return document.getElementById('log').innerText !== 'Log goes here'; 58 | })); 59 | const logText = await driver.findElement(webdriver.By.id('log')).getAttribute('innerText'); 60 | expect(logText.split('\n').length).toBe(2); 61 | 62 | await driver.findElement(webdriver.By.id('hangupButton')).click(); 63 | 64 | await driver.wait(() => driver.executeScript(() => { 65 | return pc1 === null; // eslint-disable-line no-undef 66 | })); 67 | }); 68 | }); 69 | 70 | -------------------------------------------------------------------------------- /src/content/peerconnection/pc1/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/peerconnection/pc1/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/pc1/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('simple peerconnection', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes a connection and hangs up', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | 33 | await driver.wait(() => driver.executeScript(() => { 34 | return localStream !== null; // eslint-disable-line no-undef 35 | })); 36 | 37 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 38 | await driver.findElement(webdriver.By.id('callButton')).click(); 39 | 40 | await Promise.all([ 41 | await driver.wait(() => driver.executeScript(() => { 42 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 43 | })), 44 | await driver.wait(() => driver.executeScript(() => { 45 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 46 | })), 47 | ]); 48 | 49 | await driver.wait(() => driver.executeScript(() => { 50 | return document.getElementById('remoteVideo').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 51 | })); 52 | 53 | await driver.findElement(webdriver.By.id('hangupButton')).click(); 54 | 55 | await driver.wait(() => driver.executeScript(() => { 56 | return pc1 === null; // eslint-disable-line no-undef 57 | })); 58 | }); 59 | }); 60 | 61 | -------------------------------------------------------------------------------- /src/content/peerconnection/per-frame-callback/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | video { 9 | --width: 45%; 10 | width: var(--width); 11 | height: calc(var(--width) * 0.75); 12 | } 13 | 14 | button { 15 | margin: 0 20px 0 0; 16 | width: 96px; 17 | } 18 | 19 | video#localVideo { 20 | margin: 0 20px 20px 0; 21 | } 22 | 23 | div.label { 24 | display: inline-block; 25 | font-weight: 400; 26 | width: 120px; 27 | } 28 | 29 | div.graph-container { 30 | float: left; 31 | margin: 0.5em; 32 | width: calc(50% - 1em); 33 | } 34 | 35 | a#viewSource { 36 | clear: both; 37 | } 38 | -------------------------------------------------------------------------------- /src/content/peerconnection/perfect-negotiation/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/peerconnection/pr-answer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | PeerConnection PRANSWER Demo 24 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | 38 |
39 | 40 |

WebRTC samples Use pranswer when setting up a peer connection 41 |

42 | 43 | 44 | 45 |
46 | 47 | 48 | 49 |

View the console to see logging and to inspect the MediaStream object localStream.

50 | View source on GitHub 51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/content/peerconnection/restart-ice/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 100%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0; 22 | } 23 | 24 | div#video > div { 25 | display: inline-block; 26 | margin: 0 5px 0 0; 27 | vertical-align: top; 28 | width: calc(50% - 22px); 29 | } 30 | 31 | @media screen and (max-width: 400px) { 32 | button { 33 | width: 83px; 34 | margin: 0 11px 10px 0; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/content/peerconnection/restart-ice/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/restart-ice/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection ice restart', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes a connection and changes candidates on restart', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | 33 | await driver.wait(() => driver.executeScript(() => { 34 | return localStream !== null; // eslint-disable-line no-undef 35 | })); 36 | 37 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 38 | await driver.findElement(webdriver.By.id('callButton')).click(); 39 | 40 | await driver.wait(() => driver.executeScript(() => { 41 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 42 | })); 43 | await driver.wait(() => driver.executeScript(() => { 44 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 45 | })); 46 | 47 | await driver.wait(() => driver.executeScript(() => { 48 | return document.getElementById('remoteVideo').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 49 | })); 50 | await driver.wait(() => driver.findElement(webdriver.By.id('restartButton')).isEnabled()); 51 | 52 | const firstCandidateIds = await Promise.all([ 53 | await driver.findElement(webdriver.By.id('localCandidateId')).getAttribute('innerText'), 54 | await driver.findElement(webdriver.By.id('remoteCandidateId')).getAttribute('innerText'), 55 | ]); 56 | 57 | await driver.wait(() => driver.findElement(webdriver.By.id('restartButton')).isEnabled()); 58 | await driver.findElement(webdriver.By.id('restartButton')).click(); 59 | 60 | await driver.wait(() => driver.findElement(webdriver.By.id('restartButton')).isEnabled()); 61 | 62 | const secondCandidateIds = await Promise.all([ 63 | await driver.findElement(webdriver.By.id('localCandidateId')).getAttribute('innerText'), 64 | await driver.findElement(webdriver.By.id('remoteCandidateId')).getAttribute('innerText'), 65 | ]); 66 | 67 | expect(secondCandidateIds[0]).not.toBe(firstCandidateIds[0]); 68 | expect(secondCandidateIds[1]).not.toBe(firstCandidateIds[1]); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/content/peerconnection/states/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 20px 0; 10 | width: 86.5px; 11 | } 12 | div#buttons { 13 | border-bottom: 1px solid #eee; 14 | margin: 0 0 20px 0; 15 | } 16 | div#states { 17 | border-bottom: 1px solid #eee; 18 | } 19 | div#states > div { 20 | margin: 0 0 20px 0; 21 | min-height: 24px; /* to cope with Unicode character size :^| */ 22 | } 23 | div.label { 24 | display: inline-block; 25 | font-weight: 400; 26 | width: 111px; 27 | } 28 | div.value { 29 | display: inline-block; 30 | } 31 | video { 32 | margin: 0 0 20px 0; 33 | --width: 45%; 34 | width: var(--width); 35 | height: calc(var(--width) * 0.75); 36 | } 37 | video#video1 { 38 | margin: 0 20px 20px 0; 39 | } 40 | 41 | @media screen and (min-width: 730px) { 42 | video { 43 | height: 231px; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/content/peerconnection/trickle-ice/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 20px 10px 0 0; 10 | width: 130px; 11 | } 12 | button#gather { 13 | display: block; 14 | } 15 | section#iceServers input { 16 | margin: 0 0 10px; 17 | width: 260px; 18 | } 19 | section#iceConstraints label { 20 | margin: 0 1em 0 0; 21 | } 22 | section#iceServers label { 23 | display: inline-block; 24 | width: 150px; 25 | } 26 | 27 | section#iceOptions label { 28 | display: inline-block; 29 | width: 200px; 30 | } 31 | 32 | select#servers { 33 | font-size: 1em; 34 | padding: 5px; 35 | width: 420px; 36 | } 37 | div#iceTransports span { 38 | margin: 0 1em 0 0; 39 | } 40 | table#candidates { 41 | font-size: 0.7em; 42 | overflow-y: auto; 43 | text-align: right; 44 | width: 100%; 45 | } 46 | th { 47 | font-weight: bold; 48 | } 49 | th:nth-child(3),td:nth-child(3) { 50 | text-align: left 51 | } 52 | th:nth-child(6),td:nth-child(6) { 53 | text-align: left 54 | } 55 | 56 | .gray { 57 | color: gray 58 | } 59 | 60 | #poolValue { 61 | display: inline-block; 62 | width: 30px 63 | } 64 | 65 | #getUserMediaPermissions { 66 | display: none; 67 | } 68 | 69 | #error-note { 70 | display: none; 71 | } 72 | -------------------------------------------------------------------------------- /src/content/peerconnection/trickle-ice/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/trickle-ice/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('Trickle-Ice', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | afterEach(() => { 30 | return driver.executeScript(() => localStorage.clear()); 31 | }); 32 | 33 | it('gathers a candidate', async () => { 34 | await driver.findElement(webdriver.By.id('gather')).click(); 35 | await driver.wait(() => driver.executeScript(() => pc === null && candidates.length > 0), 30 * 1000); // eslint-disable-line no-undef 36 | }); 37 | 38 | it('loads server data on double click', async () => { 39 | const element = await driver.findElement(webdriver.By.css('#servers>option')); 40 | const actions = driver.actions({async: true}); 41 | await actions.doubleClick(element).perform(); 42 | const value = await driver.findElement(webdriver.By.id('url')).getAttribute('value'); 43 | expect(value).not.toBe(''); 44 | }); 45 | 46 | it('adding a server', async () => { 47 | await driver.findElement(webdriver.By.id('url')) 48 | .sendKeys('stun:stun.l.google.com:19302'); 49 | await driver.findElement(webdriver.By.id('add')).click(); 50 | const length = await driver.findElement(webdriver.By.css('#servers')) 51 | .getAttribute('length'); 52 | expect(length >>> 0).toBe(2); 53 | }); 54 | 55 | it('removing a server', async () => { 56 | await driver.findElement(webdriver.By.css('#servers>option')).click(); 57 | await driver.findElement(webdriver.By.id('remove')).click(); 58 | const length = await driver.findElement(webdriver.By.css('#servers')) 59 | .getAttribute('length'); 60 | expect(length >>> 0).toBe(0); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /src/content/peerconnection/upgrade/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | @media screen and (max-width: 400px) { 30 | button { 31 | width: 83px; 32 | margin: 0 11px 10px 0; 33 | } 34 | 35 | video { 36 | height: 90px; 37 | margin: 0 0 10px 0; 38 | width: calc(50% - 7px); 39 | } 40 | video#localVideo { 41 | margin: 0 10px 20px 0; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/content/peerconnection/upgrade/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection - upgrade 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |

WebRTC samples 36 | Peer connection

37 | 38 | 39 | 40 | 41 |
42 | 43 | 44 | 45 | 46 |
47 | 48 |

View the console to see logging. The MediaStream object localStream, and the RTCPeerConnection 49 | objects pc1 and pc2 are in global scope, so you can inspect them in the console as 50 | well.

51 | 52 |

For more information about RTCPeerConnection, see Getting 54 | Started With WebRTC.

55 | 56 | 57 | View source on GitHub 59 | 60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/content/peerconnection/upgrade/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 'use strict'; 10 | 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/upgrade/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection upgrade from audio-only to audio-video', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('upgrades to video', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | await driver.wait(() => driver.executeScript(() => { 33 | return localStream !== null; // eslint-disable-line no-undef 34 | })); 35 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 36 | await driver.findElement(webdriver.By.id('callButton')).click(); 37 | 38 | await driver.wait(() => driver.executeScript(() => { 39 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 40 | })); 41 | 42 | await driver.wait(() => driver.findElement(webdriver.By.id('upgradeButton')).isEnabled()); 43 | await driver.findElement(webdriver.By.id('upgradeButton')).click(); 44 | await driver.wait(() => driver.executeScript(() => { 45 | return remoteVideo.videoWidth > 0; // eslint-disable-line no-undef 46 | })); 47 | 48 | await driver.wait(() => driver.findElement(webdriver.By.id('hangupButton')).isEnabled()); 49 | await driver.findElement(webdriver.By.id('hangupButton')).click(); 50 | await driver.wait(() => driver.executeScript(() => { 51 | return pc1 === null; // eslint-disable-line no-undef 52 | })); 53 | }); 54 | }); 55 | 56 | -------------------------------------------------------------------------------- /src/content/peerconnection/video-analyzer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | Page move 14 | 15 | 16 |

The page has moved to: 17 | this page

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/peerconnection/webaudio-input/audio/Shamisen-C4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/content/peerconnection/webaudio-input/audio/Shamisen-C4.wav -------------------------------------------------------------------------------- /src/content/peerconnection/webaudio-input/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | audio { 10 | margin: 0 0 20px 0; 11 | width: 50%; 12 | } 13 | 14 | button { 15 | margin: 0 20px 20px 0; 16 | width: 89px; 17 | } 18 | 19 | div#options { 20 | margin: 0 0 20px 0; 21 | } 22 | 23 | div#status { 24 | background-color: #eee; 25 | margin: 0 0 20px 0; 26 | min-height: 140px; 27 | overflow-y: scroll; 28 | padding: 0 0 0 10px; 29 | width: 50%; 30 | } 31 | 32 | input[type='checkbox'] { 33 | margin: 0 10px 0 0; 34 | position: relative; 35 | top: -2px; 36 | } 37 | 38 | label { 39 | font-weight: 400; 40 | } 41 | 42 | li { 43 | margin: 0 0 10px 0; 44 | } 45 | 46 | ul { 47 | list-style-type: square; 48 | padding: 0 0 0 18px; 49 | } 50 | -------------------------------------------------------------------------------- /src/content/peerconnection/webaudio-input/js/webaudioextended.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // WebAudioExtended helper class which takes care of the WebAudio related parts. 12 | 13 | function WebAudioExtended() { 14 | window.AudioContext = window.AudioContext || window.webkitAudioContext; 15 | /* global AudioContext */ 16 | this.context = new AudioContext(); 17 | this.soundBuffer = null; 18 | } 19 | 20 | WebAudioExtended.prototype.start = function() { 21 | this.filter = this.context.createBiquadFilter(); 22 | this.filter.type = 'highpass'; 23 | this.filter.frequency.setValueAtTime(1500, this.context.currentTime + 1); 24 | }; 25 | 26 | WebAudioExtended.prototype.applyFilter = function(stream) { 27 | this.mic = this.context.createMediaStreamSource(stream); 28 | this.mic.connect(this.filter); 29 | this.peer = this.context.createMediaStreamDestination(); 30 | this.filter.connect(this.peer); 31 | return this.peer.stream; 32 | }; 33 | 34 | WebAudioExtended.prototype.renderLocally = function(enabled) { 35 | if (enabled) { 36 | this.mic.connect(this.context.destination); 37 | } else { 38 | this.mic.disconnect(0); 39 | this.mic.connect(this.filter); 40 | } 41 | }; 42 | 43 | WebAudioExtended.prototype.stop = function() { 44 | this.mic.disconnect(0); 45 | this.filter.disconnect(0); 46 | this.mic = null; 47 | this.peer = null; 48 | }; 49 | 50 | WebAudioExtended.prototype.addEffect = function() { 51 | const effect = this.context.createBufferSource(); 52 | effect.buffer = this.soundBuffer; 53 | if (this.peer) { 54 | effect.connect(this.peer); 55 | effect.start(0); 56 | } 57 | }; 58 | 59 | WebAudioExtended.prototype.loadCompleted = function() { 60 | this.context.decodeAudioData(this.request.response, function(buffer) { 61 | this.soundBuffer = buffer; 62 | }.bind(this)); 63 | }; 64 | 65 | WebAudioExtended.prototype.loadSound = function(url) { 66 | this.request = new XMLHttpRequest(); 67 | this.request.open('GET', url, true); 68 | this.request.responseType = 'arraybuffer'; 69 | this.request.onload = this.loadCompleted.bind(this); 70 | this.request.send(); 71 | }; 72 | -------------------------------------------------------------------------------- /src/content/peerconnection/webaudio-output/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 0 20px 0 0; 11 | width: 83px; 12 | } 13 | 14 | button#hangupButton { 15 | margin: 0; 16 | } 17 | 18 | canvas { 19 | background-color: #666; 20 | vertical-align: top; 21 | --width: 45%; 22 | width: var(--width); 23 | height: calc(var(--width) * 0.75); 24 | } 25 | 26 | video { 27 | --width: 45%; 28 | width: var(--width); 29 | height: calc(var(--width) * 0.75); 30 | margin: 0 20px 20px 0; 31 | } 32 | 33 | video#remoteVideo { 34 | display: none; 35 | } 36 | 37 | @media screen and (max-width: 400px) { 38 | button { 39 | margin: 0 11px 10px 0; 40 | width: 83px; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/content/peerconnection/webaudio-output/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection as input to Web Audio 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Peer connection as input to Web Audio 37 |

38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 | 47 | 48 |
49 | 50 |

View the console to see logging. The MediaStream object localStream, and the RTCPeerConnection 51 | objects localPeerConnection and remotePeerConnection are in global scope, so you can 52 | inspect them in the console as well.

53 | 54 |

For more information about RTCPeerConnection, see Getting 56 | Started With WebRTC.

57 | 58 | 59 | View source on GitHub 61 | 62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/images/webrtc-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/images/webrtc-icon-192x192.png -------------------------------------------------------------------------------- /src/js/lib/ga.js: -------------------------------------------------------------------------------- 1 | (function(i, s, o, g, r, a, m) { 2 | i['GoogleAnalyticsObject']=r; i[r]=i[r]||function() { 3 | (i[r].q=i[r].q||[]).push(arguments); 4 | }, i[r].l=1*new Date(); a=s.createElement(o), 5 | m=s.getElementsByTagName(o)[0]; a.async=1; a.src=g; m.parentNode.insertBefore(a, m); 6 | })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); 7 | 8 | ga('create', 'UA-48530561-1', 'auto'); 9 | ga('send', 'pageview'); 10 | -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/bump.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/js/third_party/webgl_teapot/images/bump.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-negx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/js/third_party/webgl_teapot/images/skybox-negx.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-negy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/js/third_party/webgl_teapot/images/skybox-negy.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-negz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/js/third_party/webgl_teapot/images/skybox-negz.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-posx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/js/third_party/webgl_teapot/images/skybox-posx.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-posy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/js/third_party/webgl_teapot/images/skybox-posy.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-posz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/js/third_party/webgl_teapot/images/skybox-posz.jpg -------------------------------------------------------------------------------- /src/js/videopipe.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | // 9 | // A "videopipe" abstraction on top of WebRTC. 10 | // 11 | // The usage of this abstraction: 12 | // var pipe = new VideoPipe(mediastream, handlerFunction); 13 | // handlerFunction = function(mediastream) { 14 | // do_something 15 | // } 16 | // pipe.close(); 17 | // 18 | // The VideoPipe will set up 2 PeerConnections, connect them to each 19 | // other, and call HandlerFunction when the stream is available in the 20 | // second PeerConnection. 21 | // 22 | 23 | function errorHandler(context) { 24 | return function(error) { 25 | trace('Failure in ' + context + ': ' + error.toString); 26 | }; 27 | } 28 | 29 | // eslint-disable-next-line no-unused-vars 30 | function successHandler(context) { 31 | return function() { 32 | trace('Success in ' + context); 33 | }; 34 | } 35 | 36 | function noAction() { 37 | } 38 | 39 | 40 | function VideoPipe(stream, handler) { 41 | let servers = null; 42 | let pc1 = new RTCPeerConnection(servers); 43 | let pc2 = new RTCPeerConnection(servers); 44 | 45 | pc1.addStream(stream); 46 | pc1.onicecandidate = function(event) { 47 | if (event.candidate) { 48 | pc2.addIceCandidate(new RTCIceCandidate(event.candidate), 49 | noAction, errorHandler('AddIceCandidate')); 50 | } 51 | }; 52 | pc2.onicecandidate = function(event) { 53 | if (event.candidate) { 54 | pc1.addIceCandidate(new RTCIceCandidate(event.candidate), 55 | noAction, errorHandler('AddIceCandidate')); 56 | } 57 | }; 58 | pc2.onaddstream = function(e) { 59 | handler(e.stream); 60 | }; 61 | pc1.createOffer(function(desc) { 62 | pc1.setLocalDescription(desc); 63 | pc2.setRemoteDescription(desc); 64 | pc2.createAnswer(function(desc2) { 65 | pc2.setLocalDescription(desc2); 66 | pc1.setRemoteDescription(desc2); 67 | }, errorHandler('pc2.createAnswer')); 68 | }, errorHandler('pc1.createOffer')); 69 | this.pc1 = pc1; 70 | this.pc2 = pc2; 71 | } 72 | 73 | VideoPipe.prototype.close = function() { 74 | this.pc1.close(); 75 | this.pc2.close(); 76 | }; 77 | -------------------------------------------------------------------------------- /src/video/chrome.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/video/chrome.mp4 -------------------------------------------------------------------------------- /src/video/chrome.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/video/chrome.webm -------------------------------------------------------------------------------- /src/video/mixed-content.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/28f28227bd38bb05aabceae5c73b857083f75423/src/video/mixed-content.webm -------------------------------------------------------------------------------- /test/download-browsers.js: -------------------------------------------------------------------------------- 1 | const {buildDriver} = require('./webdriver'); 2 | // Download the browser(s). 3 | async function download() { 4 | if (process.env.BROWSER_A && process.env.BROWSER_B) { 5 | (await buildDriver(process.env.BROWSER_A)).quit(); 6 | (await buildDriver(process.env.BROWSER_B)).quit(); 7 | } else { 8 | (await buildDriver()).quit(); 9 | } 10 | } 11 | download(); 12 | -------------------------------------------------------------------------------- /test/interop/connection.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | const {buildDriver} = require('../webdriver'); 9 | const {PeerConnection, MediaDevices} = require('../webrtcclient'); 10 | const steps = require('../steps'); 11 | 12 | const browserA = process.env.BROWSER_A || 'chrome'; 13 | const browserB = process.env.BROWSER_B || 'chrome'; 14 | 15 | describe(`basic interop test ${browserA} => ${browserB}`, function() { 16 | let drivers; 17 | let clients; 18 | beforeAll(async () => { 19 | const options = { 20 | version: process.env.BVER || 'stable', 21 | browserLogging: true, 22 | } 23 | drivers = [ 24 | await buildDriver(browserA, options), 25 | await buildDriver(browserB, options), 26 | ]; 27 | clients = drivers.map(driver => { 28 | return { 29 | connection: new PeerConnection(driver), 30 | mediaDevices: new MediaDevices(driver), 31 | }; 32 | }); 33 | }); 34 | afterAll(async () => { 35 | await drivers.map(driver => driver.close()); 36 | }); 37 | 38 | it('establishes a connection', async () => { 39 | await Promise.all(drivers); // timeouts in before(Each)? 40 | await steps.step(drivers, (d) => d.get('https://webrtc.github.io/samples/emptypage.html'), 'Empty page loaded'); 41 | await steps.step(clients, (client) => client.connection.create(), 'Created RTCPeerConnection'); 42 | await steps.step(clients, async (client) => { 43 | const stream = await client.mediaDevices.getUserMedia({audio: true, video: true}); 44 | return Promise.all(stream.getTracks().map(async track => { 45 | return client.connection.addTrack(track, stream); 46 | })); 47 | }, 'Acquired and added audio/video stream'); 48 | const offerWithCandidates = await clients[0].connection.setLocalDescription(); 49 | await clients[1].connection.setRemoteDescription(offerWithCandidates); 50 | const answerWithCandidates = await clients[1].connection.setLocalDescription(); 51 | await clients[0].connection.setRemoteDescription(answerWithCandidates); 52 | 53 | await steps.step(drivers, (d) => steps.waitNVideosExist(d, 1), 'Video elements exist'); 54 | await steps.step(drivers, steps.waitAllVideosHaveEnoughData, 'Video elements have enough data'); 55 | }, 30000); 56 | }, 90000); 57 | -------------------------------------------------------------------------------- /test/steps.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | const TIMEOUT = 10000; 9 | 10 | function step(drivers, cb, logMessage) { 11 | return Promise.all(drivers.map(driver => { 12 | return cb(driver); 13 | })).then(() => { 14 | if (logMessage) { 15 | console.log(logMessage); 16 | } 17 | }); 18 | } 19 | function waitNVideosExist(driver, n) { 20 | return driver.wait(() => { 21 | return driver.executeScript(n => document.querySelectorAll('video').length === n, n); 22 | }, TIMEOUT); 23 | } 24 | 25 | function waitAllVideosHaveEnoughData(driver) { 26 | return driver.wait(() => { 27 | return driver.executeScript(() => { 28 | const videos = document.querySelectorAll('video'); 29 | let ready = 0; 30 | for (let i = 0; i < videos.length; i++) { 31 | if (videos[i].readyState >= videos[i].HAVE_ENOUGH_DATA) { 32 | ready++; 33 | } 34 | } 35 | return ready === videos.length; 36 | }); 37 | }, TIMEOUT); 38 | } 39 | 40 | module.exports = { 41 | step, 42 | waitNVideosExist, 43 | waitAllVideosHaveEnoughData, 44 | }; 45 | --------------------------------------------------------------------------------