├── .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 |
40 |
41 |
42 | This browser does not support the video element.
43 |
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 |
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 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
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 |
37 |
38 |
39 |
Take snapshot
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 |
35 |
36 |
37 |
Start
38 |
39 | Advanced options
40 |
41 | Show default sharing options
42 | Prefer to share a browser tab
43 | Prefer to share a window
44 | Prefer to share an entire screen
45 |
46 |
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 |
36 |
37 |
38 |
Open camera
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 |
Open camera
47 |
48 |
52 |
56 |
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 | Start
60 | Call
61 | Hang Up
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 |
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 | Start
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 |
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 | Start
48 | Hang Up
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 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Start
48 | Call
49 | Insert relay
50 | Hang Up
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 | Start
45 | Call
46 | Hang Up
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 | Start
43 | Call
44 | Renegotiate
45 | Hang Up
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 |
Call
47 |
Accept
48 |
Hang Up
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 |
37 |
38 |
39 |
40 |
41 |
42 | Start
43 | Call
44 | Turn on video
45 | Hang Up
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 | Start
46 | Call
47 | Hang Up
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 |
--------------------------------------------------------------------------------