Showing preview only (1,012K chars total). Download the full file or copy to clipboard to get everything.
Repository: webrtc/samples
Branch: gh-pages
Commit: 96ded9705722
Files: 241
Total size: 940.7 KB
Directory structure:
gitextract_t5d8f4yo/
├── .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
├── 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/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ ├── js/
│ │ │ │ └── main.js
│ │ │ └── video/
│ │ │ ├── chrome.ogv
│ │ │ └── chrome.webm
│ │ ├── extensions/
│ │ │ ├── multipleroutes/
│ │ │ │ └── src/
│ │ │ │ ├── README.md
│ │ │ │ ├── _locales/
│ │ │ │ │ └── en/
│ │ │ │ │ └── messages.json
│ │ │ │ ├── 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
│ │ │ └── volume-meter-processor.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/
│ │ │ │ └── index.html
│ │ │ ├── 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
│ │ │ └── webgpu/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ ├── main.js
│ │ │ ├── multi_video_main.js
│ │ │ ├── multi_video_worker.js
│ │ │ └── multi_video_worker_manager.js
│ │ └── peerconnection/
│ │ ├── always-negotiate-datachannels/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ └── main.js
│ │ ├── 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/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ ├── main.js
│ │ │ └── test.js
│ │ ├── multiple-relay/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ └── main.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/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ ├── main.js
│ │ │ └── webaudioextended.js
│ │ └── webaudio-output/
│ │ ├── css/
│ │ │ └── main.css
│ │ ├── index.html
│ │ └── js/
│ │ └── main.js
│ ├── css/
│ │ └── main.css
│ ├── js/
│ │ ├── lib/
│ │ │ └── ga.js
│ │ ├── third_party/
│ │ │ ├── graph.js
│ │ │ ├── streamvisualizer.js
│ │ │ └── webgl_teapot/
│ │ │ ├── cameracontroller.js
│ │ │ ├── demo.js
│ │ │ ├── matrix4x4.js
│ │ │ ├── teapot-streams.js
│ │ │ ├── webgl-debug.js
│ │ │ └── webgl-utils.js
│ │ └── videopipe.js
│ └── video/
│ ├── chrome.webm
│ └── mixed-content.webm
└── test/
├── download-browsers.js
├── interop/
│ └── connection.test.js
├── steps.js
├── webdriver.js
└── webrtcclient.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc.js
================================================
module.exports = {
'extends': 'google',
'parserOptions': {
'ecmaVersion': 2017,
'sourceType': 'module',
},
'env': {
'browser': true,
'es6': true,
'node': true,
'jest': true
},
'rules': {
'max-len': 'off',
'require-jsdoc': 'off',
'arrow-parens': 'off',
'comma-dangle': 'off',
'no-throw-literal': 'off',
'camelcase': 'off',
'prefer-rest-params': 'off',
'no-invalid-this': 'off',
'eol-last': 'off',
'no-undef': 2,
},
"globals": {
"adapter": true,
"browserSupportsIPHandlingPolicy": true,
"browserSupportsNonProxiedUdpBoolean": true,
"chrome": true,
"ga": true,
"getPolicyFromBooleans": true,
"importScripts": true,
// From WebGPU specification
"GPUBufferUsage": true,
"GPUTextureUsage": true,
// From Streams specification
"TransformStream": true,
// From WebCodec specification
"AudioData": true,
"AudioEncoder": true,
"AudioDecoder": true,
"VideoFrame": true,
"VideoEncoder": true,
"VideoDecoder": true,
},
"plugins": ["jest"]
};
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
# Please read first!
Please use [discuss-webrtc](https://groups.google.com/forum/#!forum/discuss-webrtc) for general technical discussions and questions.
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)
- [ ] I understand that issues created here are _only_ relevant to the samples in this repo - not browser or SDK bugs
- [ ] I have provided steps to reproduce
- [ ] I have provided browser name and version
- [ ] I have provided a link to the sample here or a modified version thereof
**Note: If the checkboxes above are not checked (which you do after the issue is posted), the issue will be closed.**
## Browser affected
**Browser name including version (e.g. Chrome 64.0.3282.119)**
## Description
## Steps to reproduce
## Expected results
## Actual results
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Please use the discuss-webrtc mailing list for general questions
url: https://groups.google.com/g/discuss-webrtc
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
**Description**
**Purpose**
================================================
FILE: .github/workflows/interop-tests.yml
================================================
on:
schedule:
- cron: "30 5 * * *"
jobs:
interop:
runs-on: ubuntu-22.04
timeout-minutes: 5
strategy:
fail-fast: false
matrix:
browserA: [chrome, firefox]
browserB: [firefox, chrome]
bver: [unstable]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm install
- run: sudo rm /usr/bin/chromedriver /usr/bin/geckodriver # remove preinstalled github chromedriver/geckodriver from $PATH
- run: Xvfb :99 &
- run: BROWSER_A=${{matrix.browserA}} BROWSER_B=${{matrix.browserB}} BVER=${{matrix.bver}} DISPLAY=:99.0 node test/download-browsers.js
- run: BROWSER_A=${{matrix.browserA}} BROWSER_B=${{matrix.browserB}} BVER=${{matrix.bver}} DISPLAY=:99.0 node_modules/.bin/jest --retries=3 test/interop/
================================================
FILE: .github/workflows/test.yml
================================================
name: lint-and-test
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm install
- run: npm run eslint
- run: npm run stylelint
test:
needs: lint
runs-on: ubuntu-22.04
timeout-minutes: 5
strategy:
matrix:
browser: [chrome]
version: [stable]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm install
- run: sudo rm /usr/bin/chromedriver # remove preinstalled github chromedriver from $PATH
- run: Xvfb :99 &
- run: BROWSER=${{matrix.browser}} BVER=${{matrix.version}} DISPLAY=:99.0 npm run jest -- --retries=3
================================================
FILE: .gitignore
================================================
browsers*
.eslintcache
firefox-*.tar.bz2
node_modules
.DS_Store
validation-report.json
validation-status.json
.idea
firefox_profile/*
tests_output
*.log
*~
\#*#
================================================
FILE: .npmrc
================================================
package-lock=false
================================================
FILE: .stylelintrc
================================================
{
"extends": "stylelint-config-recommended"
}
================================================
FILE: AUTHORS
================================================
The WebRTC Project Authors
The Chromium Authors
================================================
FILE: CONTRIBUTING.md
================================================
# WebRTC welcomes patches/pulls for features and bug fixes!
For contributors external to Google, follow the instructions given in the
[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual).
In all cases, contributors must sign a contributor license agreement before a contribution can be accepted.
Please complete the agreement for an [individual](https://developers.google.com/open-source/cla/individual) or
a [corporation](https://developers.google.com/open-source/cla/corporate) as appropriate.
If you plan to add a new sample or make significant changes to an existing sample, we recommend that you start by creating
a [new issue](https://github.com/webrtc/samples/issues/new) where we can discuss the details.
# How to start developing a patch, new feature or bug fix
## Clone the repo in desired folder
```bash
git clone https://github.com/webrtc/samples.git
```
## Install npm dependencies
```bash
npm install
```
## Start web server for development
```bash
npm start
```
================================================
FILE: LICENSE.md
================================================
Copyright (c) 2014, The WebRTC project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Google nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# WebRTC Code Samples
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).
To run the samples locally
```
npm install && npm start
```
and open your browser on the page indicated.
## Contributing
We welcome contributions and bugfixes. Please see [CONTRIBUTING.md](https://github.com/webrtc/samples/blob/gh-pages/CONTRIBUTING.md) for details.
## Bugs
If you encounter a bug or problem with one of the samples, please submit a
[new issue](https://github.com/webrtc/samples/issues/new) so we know about it and can fix it.
Please avoid submitting issues on this repository for general problems you have with WebRTC. If you have found a bug in
the WebRTC APIs, please see [webrtc.org/bugs](https://webrtc.org/support/bug-reporting) for how to submit bugs on the affected browsers.
If you need support on how to implement your own WebRTC-based application, please see the
[discuss-webrtc](https://groups.google.com/forum/#!forum/discuss-webrtc) Google Group.
================================================
FILE: google1b7eb21c5b594ba0.html
================================================
google-site-verification: google1b7eb21c5b594ba0.html
================================================
FILE: index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC Javascript code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="src/images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>WebRTC samples</title>
<link rel="icon" sizes="192x192" href="src/images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="src/css/main.css"/>
<style>
h2 {
font-size: 1.5em;
font-weight: 500;
}
h3 {
border-top: none;
}
section {
border-bottom: 1px solid #eee;
margin: 0 0 1.5em 0;
padding: 0 0 1.5em 0;
}
section:last-child {
border-bottom: none;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="container">
<h1>WebRTC samples</h1>
<section>
<p>
This is a collection of small samples demonstrating various parts of the <a
href="https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API">WebRTC APIs</a>. The code for all
samples are available in the <a href="https://github.com/webrtc/samples">GitHub repository</a>.
</p>
<p>Most of the samples use <a href="https://github.com/webrtc/adapter">adapter.js</a>, a shim to insulate apps
from spec changes and prefix differences.</p>
<p><a href="https://webrtc.org/getting-started/testing" title="Command-line flags for WebRTC testing">https://webrtc.org/getting-started/testing</a>
lists command line flags useful for development and testing with Chrome.</p>
<p>Patches and issues welcome! See <a href="https://github.com/webrtc/samples/blob/gh-pages/CONTRIBUTING.md">CONTRIBUTING.md</a>
for instructions.</p>
<p class="warning"><strong>Warning:</strong> It is highly recommended to use headphones when testing these
samples, as it will otherwise risk loud audio feedback on your system.</p>
</section>
<section>
<h2 id="getusermedia"><a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia">getUserMedia():</a>
</h2>
<p class="description">Access media devices</p>
<ul>
<li><a href="src/content/getusermedia/gum/">Basic getUserMedia demo</a></li>
<li><a href="src/content/getusermedia/canvas/">Use getUserMedia with canvas</a></li>
<li><a href="src/content/getusermedia/filter/">Use getUserMedia with canvas and CSS filters</a></li>
<li><a href="src/content/getusermedia/resolution/">Choose camera resolution</a></li>
<li><a href="src/content/getusermedia/audio/">Audio-only getUserMedia() output to local audio element</a>
</li>
<li><a href="src/content/getusermedia/volume/">Audio-only getUserMedia() displaying volume</a></li>
<li><a href="src/content/getusermedia/record/">Record stream</a></li>
<li><a href="src/content/getusermedia/getdisplaymedia/">Screensharing with getDisplayMedia</a></li>
<li><a href="src/content/getusermedia/pan-tilt-zoom/">Control camera pan, tilt, and zoom</a></li>
<li><a href="src/content/getusermedia/exposure/">Control exposure</a></li>
</ul>
<h2 id="devices">Devices:</h2>
<p class="description">Query media devices</p>
<ul>
<li><a href="src/content/devices/input-output/">Choose camera, microphone and speaker</a></li>
<li><a href="src/content/devices/multi/">Choose media source and audio output</a></li>
</ul>
<h2 id="capture">Stream capture:</h2>
<p class="description">Stream from canvas or video elements</p>
<ul>
<li><a href="src/content/capture/video-video/">Stream from a video element to a video element</a></li>
<li><a href="src/content/capture/video-pc/">Stream from a video element to a peer connection</a></li>
<li><a href="src/content/capture/canvas-video/">Stream from a canvas element to a video element</a></li>
<li><a href="src/content/capture/canvas-pc/">Stream from a canvas element to a peer connection</a></li>
<li><a href="src/content/capture/canvas-record/">Record a stream from a canvas element</a></li>
<li><a href="src/content/capture/video-contenthint/">Guiding video encoding with content hints</a></li>
</ul>
<h2 id="peerconnection"><a href="https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection">RTCPeerConnection:</a>
</h2>
<p class="description">Controlling peer connectivity</p>
<ul>
<li><a href="src/content/peerconnection/pc1/">Basic peer connection demo in a single tab</a></li>
<li><a href="src/content/peerconnection/channel/">Basic peer connection demo between two tabs</a></li>
<li><a href="src/content/peerconnection/always-negotiate-datachannels/">Always negotiate datachannels</a></li>
<li><a href="src/content/peerconnection/perfect-negotiation/">Peer connection using Perfect Negotiation</a></li>
<li><a href="src/content/peerconnection/audio/">Audio-only peer connection demo</a></li>
<li><a href="src/content/peerconnection/bandwidth/">Change bandwidth on the fly</a></li>
<li><a href="src/content/peerconnection/change-codecs/">Change codecs before the call</a></li>
<li><a href="src/content/peerconnection/upgrade/">Upgrade a call and turn video on</a></li>
<li><a href="src/content/peerconnection/multiple/">Multiple peer connections at once</a></li>
<li><a href="src/content/peerconnection/multiple-relay/">Forward the output of one PC into another</a></li>
<li><a href="src/content/peerconnection/munge-sdp/">Munge SDP parameters</a></li>
<li><a href="src/content/peerconnection/pr-answer/">Use pranswer when setting up a peer connection</a></li>
<li><a href="src/content/peerconnection/constraints/">Constraints and stats</a></li>
<li><a href="src/content/peerconnection/per-frame-callback/">RTCPeerConnection and requestVideoFrameCallback()</a></li>
<li><a href="src/content/peerconnection/create-offer/">Display createOffer output for various scenarios</a>
</li>
<li><a href="src/content/peerconnection/dtmf/">Use RTCDTMFSender</a></li>
<li><a href="src/content/peerconnection/states/">Display peer connection states</a></li>
<li><a href="src/content/peerconnection/trickle-ice/">ICE candidate gathering from STUN/TURN servers</a>
</li>
<li><a href="src/content/peerconnection/restart-ice/">Do an ICE restart</a></li>
<li><a href="src/content/peerconnection/webaudio-input/">Web Audio output as input to peer connection</a>
</li>
<li><a href="src/content/peerconnection/webaudio-output/">Peer connection as input to Web Audio</a></li>
<li><a href="src/content/peerconnection/negotiate-timing/">Measure how long renegotiation takes</a></li>
<li><a href="src/content/extensions/svc/">Choose scalablilityMode before call - Scalable Video Coding (SVC) Extension </a></li>
</ul>
<h2 id="datachannel"><a
href="https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel">RTCDataChannel:</a></h2>
<p class="description">Send arbitrary data over peer connections</p>
<ul>
<li><a href="src/content/datachannel/basic/">Transmit text</a></li>
<li><a href="src/content/datachannel/filetransfer/">Transfer a file</a></li>
<li><a href="src/content/datachannel/datatransfer/">Transfer data</a></li>
<li><a href="src/content/datachannel/channel/">Basic datachannel demo between two tabs</a></li>
<li><a href="src/content/datachannel/messaging/">Messaging</a></li>
</ul>
<h2 id="videoChat">Video chat:</h2>
<p class="description">Full featured WebRTC application</p>
<ul>
<li><a href="https://github.com/webrtc/apprtc/">AppRTC video chat client</a> that you can run out of a Docker image</li>
</ul>
<h2 id="capture">Insertable Streams:</h2>
<p class="description">API for processing media</p>
<ul>
<li><a href="src/content/insertable-streams/endtoend-encryption">End to end encryption using WebRTC Insertable Streams</a></li> (Experimental)
<li><a href="src/content/insertable-streams/video-analyzer">Video analyzer using WebRTC Insertable Streams</a></li> (Experimental)
<li><a href="src/content/insertable-streams/video-processing">Video processing using MediaStream Insertable Streams</a></li> (Experimental)
<li><a href="src/content/insertable-streams/audio-processing">Audio processing using MediaStream Insertable Streams</a></li> (Experimental)
<li><a href="src/content/insertable-streams/video-crop">Video cropping using MediaStream Insertable Streams in a Worker</a></li> (Experimental)
<li><a href="src/content/insertable-streams/webgpu">Integrations with WebGPU for custom video rendering:</a></li> (Experimental)
</ul>
</section>
</div>
<script src="src/js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: package.json
================================================
{
"name": "webrtc-samples",
"private": true,
"version": "1.0.0",
"description": "Project checking for WebRTC GitHub samples repo",
"keywords": [
"webrtc"
],
"homepage": "https://webrtc.github.io/samples/",
"bugs": {
"url": "https://github.com/webrtc/samples/issues"
},
"license": "BSD-3-Clause",
"author": "The WebRTC project authors",
"repository": {
"type": "git",
"url": "https://github.com/webrtc/samples.git"
},
"scripts": {
"start": "http-server . -c-1 -a 127.0.0.1",
"test": "npm run eslint && npm run stylelint",
"eslint": "eslint 'test/**.js' 'src/content/**/*.js'",
"jest": "node test/download-browsers.js && jest --testTimeout 5000 --maxWorkers=1 src/content/",
"stylelint": "stylelint 'src/**/*.css'"
},
"eslintIgnore": [
"'**/third_party/*.js'"
],
"devDependencies": {
"@puppeteer/browsers": "^2.2.0",
"eslint": "^8.9.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-jest": "^27.4.0",
"http-server": "^14.1.0",
"jest": "^29.7.0",
"selenium-webdriver": "^4.19.0",
"stylelint": "^14.5.3",
"stylelint-config-recommended": "^7.0.0"
}
}
================================================
FILE: src/content/capture/canvas-filter/css/main.css
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
canvas {
background-color: #ccc;
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
vertical-align: top;
}
video {
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
object-fit: cover;
}
================================================
FILE: src/content/capture/canvas-filter/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Video to Canvas to video</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Stream camera via a canvas to a video element</span>
</h1>
<video id="source" autoplay></video>
<canvas id="canvas-source" style="display:none"></canvas>
<canvas id="canvas-result" style="display:none"></canvas>
<video id="result" autoplay></video>
<p>The camera is captured to a video element, which is mapped onto a
canvas, and a red square is added.</p>
<p>The canvas is then captured to an ImageData object, and painted
onto a second canvas.</p>
<p>A stream is captured from the second canvas element using its
<code>captureStream()</code> method and set as the <code>srcObject</code> of the video element.</p>
<p>The <code>inputStream</code>, <code>source</code>,
<code>canvasIn</code>, <code>canvasOut</code>,
<code>result</code>, and <code>stream</code> variables are in global
scope, so you can
inspect them from the browser console.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/capture/canvas-filter"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="js/main.js"></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/capture/canvas-filter/js/main.js
================================================
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const source = document.querySelector('#source');
// TODO(hta): Use OffscreenCanvas for the intermediate canvases.
const canvasIn = document.querySelector('#canvas-source');
const canvasOut = document.querySelector('#canvas-result');
const result = document.querySelector('#result');
const stream = canvasOut.captureStream();
let inputStream = null;
let imageData = null;
result.srcObject = stream;
function loop() {
if (source.videoWidth > 0 && source.videoHeight > 0) {
canvasIn.width = source.videoWidth;
canvasIn.height = source.videoHeight;
const ctx = canvasIn.getContext('2d');
ctx.drawImage(source, 0, 0);
// Put a red square into the image, to mark it as "processed".
ctx.fillStyle = '#FF0000';
ctx.fillRect(10, 10, 80, 80);
imageData = ctx.getImageData(0, 0, canvasIn.width, canvasIn.height);
// At this point, we have data that can be transferred.
// We paint it on the second canvas.
canvasOut.width = source.videoWidth;
canvasOut.height = source.videoHeight;
const outCtx = canvasOut.getContext('2d');
outCtx.putImageData(imageData, 0, 0);
}
window.requestAnimationFrame(loop);
}
(async () => {
inputStream = await navigator.mediaDevices.getUserMedia({video: true});
source.srcObject = inputStream;
source.play();
result.play();
window.requestAnimationFrame(loop);
})();
================================================
FILE: src/content/capture/canvas-pc/css/main.css
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
canvas {
background-color: #ccc;
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
vertical-align: top;
}
video {
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
object-fit: cover;
}
#autoplay {
display: none;
}
================================================
FILE: src/content/capture/canvas-pc/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Canvas to peer connection</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1>
<a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Stream from canvas to peer connection</span>
</h1>
<canvas></canvas>
<video playsinline autoplay muted></video>
<div id="autoplay">Due to autoplay policy the video seems not to be playing. Clicking the left teapot usually resolves this.</div>
<p>Click and drag on the canvas element (on the left) to move the teapot.</p>
<p>This demo requires Firefox 47 or Chrome 52 (or later).</p>
<p>The teapot is drawn on the canvas element using WebGL. A stream is captured from the canvas using its <code>captureStream()</code>
method and streamed via a peer connection to the video element on the right.</p>
<p>View the browser console to see logging.</p>
<p>Several variables are in global scope, so you can inspect them from the console: <code>canvas</code>,
<code>video</code>, <code>pc1</code>, <code>pc2</code> and <code>stream</code>.
</p>
<p>For more demos and information about <code>captureStream()</code>, see <a
href="https://docs.google.com/document/d/1JmWfOtUP6ZqsYJ--U8y0OtHkBt-VyjX4N-JqIjb1t78"
title="Implementation overview doc">Media Capture from Canvas Implementation</a>.</p>
<p>For more information about RTCPeerConnection, see <a href="http://www.html5rocks.com/en/tutorials/webrtc/basics/"
title="HTML5 Rocks article about WebRTC by Sam Dutton">Getting
Started With WebRTC</a>.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/capture/canvas-pc"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<!-- Teapot code -->
<script src="../../../js/third_party/webgl_teapot/webgl-utils.js"></script>
<script src="../../../js/third_party/webgl_teapot/webgl-debug.js"></script>
<script src="../../../js/third_party/webgl_teapot/matrix4x4.js"></script>
<script src="../../../js/third_party/webgl_teapot/cameracontroller.js"></script>
<script src="../../../js/third_party/webgl_teapot/teapot-streams.js"></script>
<script src="../../../js/third_party/webgl_teapot/demo.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/capture/canvas-pc/js/main.js
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* global main */
'use strict';
const canvas = document.querySelector('canvas');
const video = document.querySelector('video');
let pc1;
let pc2;
let startTime;
video.addEventListener('loadedmetadata', function() {
document.getElementById('autoplay').style.display = 'none';
console.log(`Remote video videoWidth: ${this.videoWidth}px, videoHeight: ${this.videoHeight}px`);
});
video.onresize = () => {
console.log(`Remote video size changed to ${video.videoWidth}x${video.videoHeight}`);
// We'll use the first onsize callback as an indication that video has started
// playing out.
if (startTime) {
const elapsedTime = window.performance.now() - startTime;
console.log(`Setup time: ${elapsedTime.toFixed(3)}ms`);
startTime = null;
}
};
// Call main() in demo.js
main();
const stream = canvas.captureStream();
console.log('Got stream from canvas');
call();
function call() {
console.log('Starting call');
startTime = window.performance.now();
const videoTracks = stream.getVideoTracks();
const audioTracks = stream.getAudioTracks();
if (videoTracks.length > 0) {
console.log(`Using video device: ${videoTracks[0].label}`);
}
if (audioTracks.length > 0) {
console.log(`Using audio device: ${audioTracks[0].label}`);
}
const servers = null;
pc1 = new RTCPeerConnection(servers);
console.log('Created local peer connection object pc1');
pc1.onicecandidate = e => onIceCandidate(pc1, e);
pc2 = new RTCPeerConnection(servers);
console.log('Created remote peer connection object pc2');
pc2.onicecandidate = e => onIceCandidate(pc2, e);
pc1.oniceconnectionstatechange = e => onIceStateChange(pc1, e);
pc2.oniceconnectionstatechange = e => onIceStateChange(pc2, e);
pc2.ontrack = gotRemoteStream;
stream.getTracks().forEach(
track => {
pc1.addTrack(
track,
stream
);
}
);
console.log('Added local stream to pc1');
console.log('pc1 createOffer start');
pc1.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError);
}
function onCreateSessionDescriptionError(error) {
console.log(`Failed to create session description: ${error.toString()}`);
}
function onCreateOfferSuccess(desc) {
console.log(`Offer from pc1\n${desc.sdp}`);
console.log('pc1 setLocalDescription start');
pc1.setLocalDescription(desc, () => onSetLocalSuccess(pc1), onSetSessionDescriptionError);
console.log('pc2 setRemoteDescription start');
pc2.setRemoteDescription(desc, () => onSetRemoteSuccess(pc2), onSetSessionDescriptionError);
console.log('pc2 createAnswer start');
// Since the 'remote' side has no media stream we need
// to pass in the right constraints in order for it to
// accept the incoming offer of audio and video.
pc2.createAnswer(onCreateAnswerSuccess, onCreateSessionDescriptionError);
}
function onSetLocalSuccess(pc) {
console.log(`${getName(pc)} setLocalDescription complete`);
}
function onSetRemoteSuccess(pc) {
console.log(`${getName(pc)} setRemoteDescription complete`);
}
function onSetSessionDescriptionError(error) {
console.log(`Failed to set session description: ${error.toString()}`);
}
function gotRemoteStream(e) {
if (video.srcObject !== e.streams[0]) {
video.srcObject = e.streams[0];
console.log('pc2 received remote stream');
if (video.paused) {
document.getElementById('autoplay').style.display = 'block';
}
}
}
function onCreateAnswerSuccess(desc) {
console.log(`Answer from pc2:\n${desc.sdp}`);
console.log('pc2 setLocalDescription start');
pc2.setLocalDescription(desc, () => onSetLocalSuccess(pc2), onSetSessionDescriptionError);
console.log('pc1 setRemoteDescription start');
pc1.setRemoteDescription(desc, () => onSetRemoteSuccess(pc1), onSetSessionDescriptionError);
}
function onIceCandidate(pc, event) {
getOtherPc(pc).addIceCandidate(event.candidate)
.then(
() => onAddIceCandidateSuccess(pc),
err => onAddIceCandidateError(pc, err)
);
console.log(`${getName(pc)} ICE candidate: ${event.candidate ? event.candidate.candidate : '(null)'}`);
}
function onAddIceCandidateSuccess(pc) {
console.log(`${getName(pc)} addIceCandidate success`);
}
function onAddIceCandidateError(pc, error) {
console.log(`${getName(pc)} failed to add ICE Candidate: ${error.toString()}`);
}
function onIceStateChange(pc, event) {
if (pc) {
console.log(`${getName(pc)} ICE state: ${pc.iceConnectionState}`);
console.log('ICE state change event: ', event);
}
}
function getName(pc) {
return (pc === pc1) ? 'pc1' : 'pc2';
}
function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}
================================================
FILE: src/content/capture/canvas-record/css/main.css
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
button {
margin: 0 3px 10px 0;
padding-left: 2px;
padding-right: 2px;
width: 99px;
}
button:last-of-type {
margin: 0;
}
p.borderBelow {
margin: 0 0 20px 0;
padding: 0 0 20px 0;
}
canvas {
background-color: #ccc;
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
vertical-align: top;
}
video {
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
object-fit: cover;
}
================================================
FILE: src/content/capture/canvas-record/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Record canvas stream</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Record stream from a canvas</span>
</h1>
<canvas></canvas>
<video id="recorded" playsinline loop></video>
<div>
<button id="record">Start Recording</button>
<button id="play" disabled>Play</button>
<button id="download" disabled>Download</button>
</div>
<p>Click and drag on the canvas (on the left) to move the teapot.</p>
<p>This demo requires Firefox 43 or above, Chrome 51 or above, or Chrome 50 with <strong>Experimental Web Platform
features</strong> enabled.</p>
<p>The teapot is drawn on the canvas element using WebGL. A stream is captured from the canvas element using its
<code>captureStream()</code> method then recorded using the <code>MediaRecorder</code> API.</p>
<p>The <code>canvas</code>, <code>video</code>, and <code>stream</code> variables are in global scope, so you can
inspect them from the browser console.</p>
<p>For more demos and information about <code>captureStream()</code>, see <a
href="https://docs.google.com/document/d/1JmWfOtUP6ZqsYJ--U8y0OtHkBt-VyjX4N-JqIjb1t78"
title="Implementation overview doc">Media Capture from Canvas Implementation</a>.</p>
<p>For more information see the MediaStream Recording API <a
href="http://w3c.github.io/mediacapture-record/MediaRecorder.html"
title="W3C MediaStream Recording API Editor's Draft">Editor's Draft</a>.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/capture/canvas-record"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<!-- Teapot code -->
<script src="../../../js/third_party/webgl_teapot/webgl-utils.js"></script>
<script src="../../../js/third_party/webgl_teapot/webgl-debug.js"></script>
<script src="../../../js/third_party/webgl_teapot/matrix4x4.js"></script>
<script src="../../../js/third_party/webgl_teapot/cameracontroller.js"></script>
<script src="../../../js/third_party/webgl_teapot/teapot-streams.js"></script>
<script src="../../../js/third_party/webgl_teapot/demo.js"></script>
<!-- include adapter for srcObject shim -->
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/capture/canvas-record/js/main.js
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
/* globals main */
// This code is adapted from
// https://rawgit.com/Miguelao/demos/master/mediarecorder.html
/* globals main, MediaRecorder */
const mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', handleSourceOpen, false);
let mediaRecorder;
let recordedBlobs;
let sourceBuffer;
const canvas = document.querySelector('canvas');
const video = document.querySelector('video');
const recordButton = document.querySelector('button#record');
const playButton = document.querySelector('button#play');
const downloadButton = document.querySelector('button#download');
recordButton.onclick = toggleRecording;
playButton.onclick = play;
downloadButton.onclick = download;
// Start the GL teapot on the canvas
main();
const stream = canvas.captureStream(); // frames per second
console.log('Started stream capture from canvas element: ', stream);
function handleSourceOpen(event) {
console.log('MediaSource opened');
sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
console.log('Source buffer: ', sourceBuffer);
}
function handleDataAvailable(event) {
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);
}
}
function handleStop(event) {
console.log('Recorder stopped: ', event);
const superBuffer = new Blob(recordedBlobs, {type: 'video/webm'});
video.src = window.URL.createObjectURL(superBuffer);
}
function toggleRecording() {
if (recordButton.textContent === 'Start Recording') {
startRecording();
} else {
stopRecording();
recordButton.textContent = 'Start Recording';
playButton.disabled = false;
downloadButton.disabled = false;
}
}
// The nested try blocks will be simplified when Chrome 47 moves to Stable
function startRecording() {
let options = {mimeType: 'video/webm'};
recordedBlobs = [];
try {
mediaRecorder = new MediaRecorder(stream, options);
} catch (e0) {
console.log('Unable to create MediaRecorder with options Object: ', e0);
try {
options = {mimeType: 'video/webm,codecs=vp9'};
mediaRecorder = new MediaRecorder(stream, options);
} catch (e1) {
console.log('Unable to create MediaRecorder with options Object: ', e1);
try {
options = 'video/vp8'; // Chrome 47
mediaRecorder = new MediaRecorder(stream, options);
} catch (e2) {
alert('MediaRecorder is not supported by this browser.\n\n' +
'Try Firefox 29 or later, or Chrome 47 or later, ' +
'with Enable experimental Web Platform features enabled from chrome://flags.');
console.error('Exception while creating MediaRecorder:', e2);
return;
}
}
}
console.log('Created MediaRecorder', mediaRecorder, 'with options', options);
recordButton.textContent = 'Stop Recording';
playButton.disabled = true;
downloadButton.disabled = true;
mediaRecorder.onstop = handleStop;
mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.start(100); // collect 100ms of data
console.log('MediaRecorder started', mediaRecorder);
}
function stopRecording() {
mediaRecorder.stop();
console.log('Recorded Blobs: ', recordedBlobs);
video.controls = true;
}
function play() {
video.play();
}
function download() {
const blob = new Blob(recordedBlobs, {type: 'video/webm'});
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'test.webm';
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
}
================================================
FILE: src/content/capture/canvas-video/css/main.css
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
canvas {
background-color: #ccc;
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
vertical-align: top;
}
video {
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
object-fit: cover;
}
================================================
FILE: src/content/capture/canvas-video/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Canvas to video</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Stream from canvas to video element</span>
</h1>
<canvas></canvas>
<video playsinline autoplay muted></video>
<p>Click and drag on the canvas (on the left) to move the teapot.</p>
<p>This demo requires Firefox 47 or Chrome 52 (or later).</p>
<p>The teapot is drawn on the canvas element using WebGL. A stream is captured from the canvas element using its
<code>captureStream()</code> method and set as the <code>srcObject</code> of the video element.</p>
<p>The <code>canvas</code>, <code>video</code>, and <code>stream</code> variables are in global scope, so you can
inspect them from the browser console.</p>
<p>For more demos and information about <code>captureStream()</code>, see <a
href="https://docs.google.com/document/d/1JmWfOtUP6ZqsYJ--U8y0OtHkBt-VyjX4N-JqIjb1t78"
title="Implementation overview doc">Media Capture from Canvas Implementation</a>.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/capture/canvas-video"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<!-- Teapot code -->
<script src="../../../js/third_party/webgl_teapot/webgl-utils.js"></script>
<script src="../../../js/third_party/webgl_teapot/webgl-debug.js"></script>
<script src="../../../js/third_party/webgl_teapot/matrix4x4.js"></script>
<script src="../../../js/third_party/webgl_teapot/cameracontroller.js"></script>
<script src="../../../js/third_party/webgl_teapot/teapot-streams.js"></script>
<script src="../../../js/third_party/webgl_teapot/demo.js"></script>
<script src="js/main.js"></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/capture/canvas-video/js/main.js
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* global main */
'use strict';
// Call main() in demo.js
main();
const canvas = document.querySelector('canvas');
const video = document.querySelector('video');
const stream = canvas.captureStream();
video.srcObject = stream;
================================================
FILE: src/content/capture/video-contenthint/css/main.css
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
video {
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 9 / 16);
margin: 1em;
object-fit: cover;
}
.video-container {
border-bottom: 1px solid grey;
font-style: italic;
margin: 20px;
}
#videos {
text-align: center;
width: 100%;
}
================================================
FILE: src/content/capture/video-contenthint/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>MediaStreamTrack Content Hints</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Guiding video encoding with content hints</span>
</h1>
<div id="videos">
<div class="video-container">
<h2>Source video file (high bitrate)</h2>
<video id="srcVideo" playsinline controls muted loop>
<source src="../../../video/mixed-content.webm" type="video/webm"/>
<p>This browser does not support the video element.</p>
</video>
</div>
<div class="video-container">
<h2>"motion" video @ 50kbps</h2>
<video id="motionVideo" playsinline autoplay muted></video>
</div>
<div class="video-container">
<h2>"detail" video @ 50kbps</h2>
<video id="detailVideo" playsinline autoplay muted></video>
</div>
</div>
<p>This demo requires Chrome 57.0.2957.0 or later with <strong>Experimental Web Platform features</strong> enabled
from <tt>chrome://flags</tt>.</p>
<p>A stream is captured from the source video using the <code>captureStream()</code> method. The stream is cloned
and transmitted via two separate PeerConnections using 50kbps of video bandwidth. This is insufficient to
generate good quality in the encoded bitstream, so trade-offs have to be made.</p>
<p>The transmitted stream tracks are using <a href="https://www.w3.org/TR/mst-content-hint/">MediaStreamTrack
Content Hints</a> to indicate characteristics in the video stream, which informs PeerConnection on how to encode
the track (to prefer motion or individual frame detail).</p>
<p>The text part of the clip shows a clear case for when <tt>'detail'</tt> is better, and the fighting scene shows a
clear case for when <tt>'motion'</tt> is better. The spinning model however shows a case where <tt>'motion'</tt>
or <tt>'detail'</tt> are not clear-cut decisions and even with good content detection what's preferred depends
on what the user prefers.</p>
<p>Other MediaStreamTrack consumers such as MediaStreamRecorder can also make use of this information to guide
encoding parameters for the stream without additional extensions to the MediaStreamRecorder specification, but
this is currently not implemented in Chromium.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/capture/video-contenthint"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/capture/video-contenthint/js/main.js
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const srcVideo = document.getElementById('srcVideo');
const motionVideo = document.getElementById('motionVideo');
const detailVideo = document.getElementById('detailVideo');
let srcStream;
let motionStream;
let detailStream;
const offerOptions = {
offerToReceiveAudio: 0,
offerToReceiveVideo: 1
};
function maybeCreateStream() {
if (srcStream) {
return;
}
if (srcVideo.captureStream) {
srcStream = srcVideo.captureStream();
call();
} else {
console.log('captureStream() not supported');
}
}
// Video tag capture must be set up after video tracks are enumerated.
srcVideo.oncanplay = maybeCreateStream;
if (srcVideo.readyState >= 3) { // HAVE_FUTURE_DATA
// Video is already ready to play, call maybeCreateStream in case oncanplay
// fired before we registered the event handler.
maybeCreateStream();
}
srcVideo.play();
function setVideoTrackContentHints(stream, hint) {
const tracks = stream.getVideoTracks();
tracks.forEach(track => {
if ('contentHint' in track) {
track.contentHint = hint;
if (track.contentHint !== hint) {
console.log('Invalid video track contentHint: \'' + hint + '\'');
}
} else {
console.log('MediaStreamTrack contentHint attribute not supported');
}
});
}
function call() {
// This creates multiple independent PeerConnections instead of multiple
// streams on a single PeerConnection object so that b=AS (the bitrate
// constraints) can be applied independently.
motionStream = srcStream.clone();
// TODO(pbos): Remove fluid when no clients use it, motion is the newer name.
setVideoTrackContentHints(motionStream, 'fluid');
setVideoTrackContentHints(motionStream, 'motion');
establishPC(motionVideo, motionStream);
detailStream = srcStream.clone();
// TODO(pbos): Remove detailed when no clients use it, detail is the newer
// name.
setVideoTrackContentHints(detailStream, 'detailed');
setVideoTrackContentHints(detailStream, 'detail');
establishPC(detailVideo, detailStream);
}
function establishPC(videoTag, stream) {
const pc1 = new RTCPeerConnection(null);
const pc2 = new RTCPeerConnection(null);
pc1.onicecandidate = e => {
onIceCandidate(pc1, pc2, e);
};
pc2.onicecandidate = e => {
onIceCandidate(pc2, pc1, e);
};
pc2.ontrack = event => {
if (videoTag.srcObject !== event.streams[0]) {
videoTag.srcObject = event.streams[0];
}
};
stream.getTracks().forEach(track => pc1.addTrack(track, stream));
pc1.createOffer(offerOptions)
.then(desc => {
pc1.setLocalDescription(desc)
.then(() => pc2.setRemoteDescription(desc))
.then(() => pc2.createAnswer())
.then(answerDesc => onCreateAnswerSuccess(pc1, pc2, answerDesc))
.catch(onSetSessionDescriptionError);
})
.catch(e => console.log('Failed to create session description: ' + e.toString()));
}
function onSetSessionDescriptionError(error) {
console.log('Failed to set session description: ' + error.toString());
}
function onCreateAnswerSuccess(pc1, pc2, desc) {
// Hard-code video bitrate to 50kbps.
desc.sdp = desc.sdp.replace(/a=mid:(.*)\r\n/g, 'a=mid:$1\r\nb=AS:' + 50 + '\r\n');
pc2.setLocalDescription(desc)
.then(() => pc1.setRemoteDescription(desc))
.catch(onSetSessionDescriptionError);
}
function onIceCandidate(pc, otherPc, event) {
otherPc.addIceCandidate(event.candidate);
}
================================================
FILE: src/content/capture/video-pc/css/main.css
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
video {
margin: 0 10px 0 0;
width: calc(50% - 7px);
}
video:last-of-type {
margin-right: 0;
}
@media screen and (max-width: 400px) {
video {
margin: 0 5px 20px 0;
width: calc(50% - 5px);
}
}
================================================
FILE: src/content/capture/video-pc/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Video to peer connection</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Stream from a video to a peer connection</span>
</h1>
<video id="leftVideo" playsinline controls muted>
<!-- NB CORS: https://bugzilla.mozilla.org/show_bug.cgi?id=1177793 -->
<source src="../../../video/chrome.webm" type="video/webm"/>
<source src="../../../video/chrome.mp4" type="video/mp4"/>
<p>This browser does not support the video element.</p>
</video>
<video id="rightVideo" playsinline autoplay controls></video>
<p>This demo requires Firefox 47, Chrome 53 with <strong>Experimental Web Platform features</strong> enabled from
<tt>chrome://flags</tt>.</p>
<p>A stream is captured from the video on the left using the <code>captureStream()</code> method, and streamed via a
peer connection to the video element on the right.</p>
<p>View the browser console to see logging.</p>
<p>Several variables are in global scope, so you can inspect them from the console: <code>pc1</code>,
<code>pc2</code> and <code>stream</code>.</p>
<p>For more information about RTCPeerConnection, see <a href="http://www.html5rocks.com/en/tutorials/webrtc/basics/"
title="HTML5 Rocks article about WebRTC by Sam Dutton">Getting
Started With WebRTC</a>.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/capture/video-pc"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/capture/video-pc/js/main.js
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const leftVideo = document.getElementById('leftVideo');
const rightVideo = document.getElementById('rightVideo');
let stream;
let pc1;
let pc2;
const offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
let startTime;
function maybeCreateStream() {
if (stream) {
return;
}
if (leftVideo.captureStream) {
stream = leftVideo.captureStream();
console.log('Captured stream from leftVideo with captureStream',
stream);
call();
} else if (leftVideo.mozCaptureStream) {
stream = leftVideo.mozCaptureStream();
console.log('Captured stream from leftVideo with mozCaptureStream()',
stream);
call();
} else {
console.log('captureStream() not supported');
}
}
// Video tag capture must be set up after video tracks are enumerated.
leftVideo.oncanplay = maybeCreateStream;
if (leftVideo.readyState >= 3) { // HAVE_FUTURE_DATA
// Video is already ready to play, call maybeCreateStream in case oncanplay
// fired before we registered the event handler.
maybeCreateStream();
}
leftVideo.play();
rightVideo.onloadedmetadata = () => {
console.log(`Remote video videoWidth: ${rightVideo.videoWidth}px, videoHeight: ${rightVideo.videoHeight}px`);
};
rightVideo.onresize = () => {
console.log(`Remote video size changed to ${rightVideo.videoWidth}x${rightVideo.videoHeight}`);
// We'll use the first onresize callback as an indication that
// video has started playing out.
if (startTime) {
const elapsedTime = window.performance.now() - startTime;
console.log('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
startTime = null;
}
};
function call() {
console.log('Starting call');
startTime = window.performance.now();
const videoTracks = stream.getVideoTracks();
const audioTracks = stream.getAudioTracks();
if (videoTracks.length > 0) {
console.log(`Using video device: ${videoTracks[0].label}`);
}
if (audioTracks.length > 0) {
console.log(`Using audio device: ${audioTracks[0].label}`);
}
const servers = null;
pc1 = new RTCPeerConnection(servers);
console.log('Created local peer connection object pc1');
pc1.onicecandidate = e => onIceCandidate(pc1, e);
pc2 = new RTCPeerConnection(servers);
console.log('Created remote peer connection object pc2');
pc2.onicecandidate = e => onIceCandidate(pc2, e);
pc1.oniceconnectionstatechange = e => onIceStateChange(pc1, e);
pc2.oniceconnectionstatechange = e => onIceStateChange(pc2, e);
pc2.ontrack = gotRemoteStream;
stream.getTracks().forEach(track => pc1.addTrack(track, stream));
console.log('Added local stream to pc1');
console.log('pc1 createOffer start');
pc1.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError, offerOptions);
}
function onCreateSessionDescriptionError(error) {
console.log(`Failed to create session description: ${error.toString()}`);
}
function onCreateOfferSuccess(desc) {
console.log(`Offer from pc1
${desc.sdp}`);
console.log('pc1 setLocalDescription start');
pc1.setLocalDescription(desc, () => onSetLocalSuccess(pc1), onSetSessionDescriptionError);
console.log('pc2 setRemoteDescription start');
pc2.setRemoteDescription(desc, () => onSetRemoteSuccess(pc2), onSetSessionDescriptionError);
console.log('pc2 createAnswer start');
// Since the 'remote' side has no media stream we need
// to pass in the right constraints in order for it to
// accept the incoming offer of audio and video.
pc2.createAnswer(onCreateAnswerSuccess, onCreateSessionDescriptionError);
}
function onSetLocalSuccess(pc) {
console.log(`${getName(pc)} setLocalDescription complete`);
}
function onSetRemoteSuccess(pc) {
console.log(`${getName(pc)} setRemoteDescription complete`);
}
function onSetSessionDescriptionError(error) {
console.log(`Failed to set session description: ${error.toString()}`);
}
function gotRemoteStream(event) {
if (rightVideo.srcObject !== event.streams[0]) {
rightVideo.srcObject = event.streams[0];
console.log('pc2 received remote stream', event);
}
}
function onCreateAnswerSuccess(desc) {
console.log(`Answer from pc2:
${desc.sdp}`);
console.log('pc2 setLocalDescription start');
pc2.setLocalDescription(desc, () => onSetLocalSuccess(pc2), onSetSessionDescriptionError);
console.log('pc1 setRemoteDescription start');
pc1.setRemoteDescription(desc, () => onSetRemoteSuccess(pc1), onSetSessionDescriptionError);
}
function onIceCandidate(pc, event) {
getOtherPc(pc).addIceCandidate(event.candidate)
.then(
() => onAddIceCandidateSuccess(pc),
err => onAddIceCandidateError(pc, err)
);
console.log(`${getName(pc)} ICE candidate:
${event.candidate ?
event.candidate.candidate : '(null)'}`);
}
function onAddIceCandidateSuccess(pc) {
console.log(`${getName(pc)} addIceCandidate success`);
}
function onAddIceCandidateError(pc, error) {
console.log(`${getName(pc)} failed to add ICE Candidate: ${error.toString()}`);
}
function onIceStateChange(pc, event) {
if (pc) {
console.log(`${getName(pc)} ICE state: ${pc.iceConnectionState}`);
console.log('ICE state change event: ', event);
}
}
function getName(pc) {
return (pc === pc1) ? 'pc1' : 'pc2';
}
function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}
================================================
FILE: src/content/capture/video-video/css/main.css
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
video {
margin: 0 10px 0 0;
width: calc(50% - 7px);
}
video:last-of-type {
margin-right: 0;
}
@media screen and (max-width: 400px) {
video {
margin: 0 5px 20px 0;
width: calc(50% - 5px);
}
}
================================================
FILE: src/content/capture/video-video/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>captureStream(): video to video</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>captureStream(): video to video</span>
</h1>
<video id="leftVideo" playsinline controls loop muted>
<source src="../../../video/chrome.webm" type="video/webm"/>
<source src="../../../video/chrome.mp4" type="video/mp4"/>
<p>This browser does not support the video element.</p>
</video>
<video id="rightVideo" playsinline autoplay muted></video>
<p>Press play on the left video to start the demo.</p>
<p>A stream is captured from the video element on the left using its <code>captureStream()</code> method and set as
the <code>srcObject</code> of the video element on the right.</p>
<p>The <code>stream</code> variable are in global scope, so you can inspect them from the browser console.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/capture/video-video"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/capture/video-video/js/main.js
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const leftVideo = document.getElementById('leftVideo');
const rightVideo = document.getElementById('rightVideo');
leftVideo.addEventListener('canplay', () => {
let stream;
const fps = 0;
if (leftVideo.captureStream) {
stream = leftVideo.captureStream(fps);
} else if (leftVideo.mozCaptureStream) {
stream = leftVideo.mozCaptureStream(fps);
} else {
console.error('Stream capture is not supported');
stream = null;
}
rightVideo.srcObject = stream;
});
================================================
FILE: src/content/capture/worker-process/css/main.css
================================================
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
canvas {
background-color: #ccc;
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
vertical-align: top;
}
video {
--width: calc(45%);
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 1em;
object-fit: cover;
}
================================================
FILE: src/content/capture/worker-process/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Video processing in a Worker</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Processing video data with a Worker</span>
</h1>
<video id="source" autoplay></video>
<canvas id="canvas-source" style="display:none"></canvas>
<canvas id="canvas-result" style="display:none"></canvas>
<video id="result" autoplay></video>
<p>The camera is captured to a MediaStreamTrack, which is turned into a
WHATWG Stream of ImageData objects by means of a canvas, and a red
square is added.</p>
<p>The stream is sent to a Worker, which returns a new stream containing
the same video data.</p>
<p>This is then mapped back to a MediaStream using another canvas.</p>
<p>The chief purpose of the demo is to demonstrate that this is doable,
but that performance can be improved significantly.</p>
<p>NOTE: This works only on Chrome 76 and above with experimental Web
features enabled, since it depends on transferable Streams.</p>
<p>A similar demo, without the worker process, is on the <a href="../canvas-filter/">canvas filter</a> demo.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/capture/worker-process"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="js/main.js"></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/capture/worker-process/js/main.js
================================================
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const source = document.querySelector('#source');
// TODO(hta): Use OffscreenCanvas for the intermediate canvases.
const canvasIn = document.querySelector('#canvas-source');
const canvasOut = document.querySelector('#canvas-result');
const result = document.querySelector('#result');
const stream = canvasOut.captureStream();
let inputStream = null;
let imageData = null;
let transformStream = null;
let writer = null;
let reader = null;
result.srcObject = stream;
function loop() {
if (source.videoWidth > 0 && source.videoHeight > 0) {
canvasIn.width = source.videoWidth;
canvasIn.height = source.videoHeight;
const ctx = canvasIn.getContext('2d');
ctx.drawImage(source, 0, 0);
// Put a red square into the image, to mark it as "processed".
ctx.fillStyle = '#FF0000';
ctx.fillRect(10, 10, 80, 80);
imageData = ctx.getImageData(0, 0, canvasIn.width, canvasIn.height);
// At this point, we have data that can be transferred.
writer.write(imageData);
}
window.requestAnimationFrame(loop);
}
// The read function paints the incoming data on the second canvas.
const readData = async () => {
const result = await reader.read();
if (!result.done) {
canvasOut.width = source.videoWidth;
canvasOut.height = source.videoHeight;
const outCtx = canvasOut.getContext('2d');
outCtx.putImageData(result.value, 0, 0);
readData();
}
};
(async () => {
inputStream = await navigator.mediaDevices.getUserMedia({video: true});
source.srcObject = inputStream;
transformStream = new TransformStream();
writer = transformStream.writable.getWriter();
const myWorker = new Worker('js/worker.js');
myWorker.onmessage = function(e) {
reader = e.data[1].getReader();
// Start the flow of data.
readData();
window.requestAnimationFrame(loop);
};
myWorker.postMessage(['stream', transformStream.readable],
[transformStream.readable]);
source.play();
result.play();
})();
================================================
FILE: src/content/capture/worker-process/js/worker.js
================================================
/*
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
onmessage = function(e) {
const command = e.data[0];
if (command == 'stream') {
const inputStream = e.data[1];
const transformStream = new TransformStream();
inputStream.pipeTo(transformStream.writable);
postMessage(['response', transformStream.readable],
[transformStream.readable]);
}
};
================================================
FILE: src/content/datachannel/basic/css/main.css
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
button {
margin: 0 1em 1em 0;
width: 90px;
}
div#buttons {
margin: 0 0 1em 0;
}
div#send {
margin: 0 20px 1em 0;
}
div#sendReceive {
border-bottom: 1px solid #eee;
margin: 0;
padding: 0 0 10px 0;
}
div#sendReceive > div {
display: inline-block;
width: calc(50% - 20px);
}
form {
margin: 0 0 1em 0;
white-space: nowrap;
}
form span {
font-weight: 300;
margin: 0 1em 0 0;
white-space: normal;
}
textarea {
color: #444;
font-size: 0.9em;
font-weight: 300;
height: 7.0em;
padding: 5px;
width: calc(100% - 10px);
}
================================================
FILE: src/content/datachannel/basic/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Transmit text</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a>
<span>Transmit text</span></h1>
<div id="buttons">
<button id="startButton">Start</button>
<button id="sendButton" disabled>Send</button>
<button id="closeButton" disabled>Stop</button>
</div>
<div id="sendReceive">
<div id="send">
<h2>Send</h2>
<textarea id="dataChannelSend" disabled
placeholder="Press Start, enter some text, then press Send."></textarea>
</div>
<div id="receive">
<h2>Receive</h2>
<textarea id="dataChannelReceive" disabled></textarea>
</div>
</div>
<p>View the console to see logging.</p>
<p>The <code>RTCPeerConnection</code> objects <code>pc1</code> and <code>pc2</code> are in
global scope, so you can inspect them in the console as well.</p>
<p>For more information about RTCDataChannel, see <a
href="http://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-rtcdatachannel"
title="RTCDataChannel section of HTML5 Rocks article about WebRTC">Getting Started With WebRTC</a>.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/datachannel/basic"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/datachannel/basic/js/main.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
let pc1;
let pc2;
let sendChannel;
let receiveChannel;
const dataChannelSend = document.querySelector('textarea#dataChannelSend');
const dataChannelReceive = document.querySelector('textarea#dataChannelReceive');
const startButton = document.querySelector('button#startButton');
const sendButton = document.querySelector('button#sendButton');
const closeButton = document.querySelector('button#closeButton');
startButton.onclick = createConnection;
sendButton.onclick = sendData;
closeButton.onclick = closeDataChannels;
function enableStartButton() {
startButton.disabled = false;
}
function disableSendButton() {
sendButton.disabled = true;
}
function createConnection() {
dataChannelSend.placeholder = '';
const servers = null;
pc1 = new RTCPeerConnection(servers);
console.log('Created local peer connection object pc1');
sendChannel = pc1.createDataChannel('sendDataChannel');
console.log('Created send data channel');
pc1.onicecandidate = e => {
onIceCandidate(pc1, e);
};
sendChannel.onopen = onSendChannelStateChange;
sendChannel.onclose = onSendChannelStateChange;
pc2 = new RTCPeerConnection(servers);
console.log('Created remote peer connection object pc2');
pc2.onicecandidate = e => {
onIceCandidate(pc2, e);
};
pc2.ondatachannel = receiveChannelCallback;
pc1.createOffer().then(
gotDescription1,
onCreateSessionDescriptionError
);
startButton.disabled = true;
closeButton.disabled = false;
}
function onCreateSessionDescriptionError(error) {
console.log('Failed to create session description: ' + error.toString());
}
function sendData() {
const data = dataChannelSend.value;
sendChannel.send(data);
console.log('Sent Data: ' + data);
}
function closeDataChannels() {
console.log('Closing data channels');
sendChannel.close();
console.log('Closed data channel with label: ' + sendChannel.label);
receiveChannel.close();
console.log('Closed data channel with label: ' + receiveChannel.label);
pc1.close();
pc2.close();
pc1 = null;
pc2 = null;
console.log('Closed peer connections');
startButton.disabled = false;
sendButton.disabled = true;
closeButton.disabled = true;
dataChannelSend.value = '';
dataChannelReceive.value = '';
dataChannelSend.disabled = true;
disableSendButton();
enableStartButton();
}
function gotDescription1(desc) {
pc1.setLocalDescription(desc);
console.log(`Offer from pc1\n${desc.sdp}`);
pc2.setRemoteDescription(desc);
pc2.createAnswer().then(
gotDescription2,
onCreateSessionDescriptionError
);
}
function gotDescription2(desc) {
pc2.setLocalDescription(desc);
console.log(`Answer from pc2\n${desc.sdp}`);
pc1.setRemoteDescription(desc);
}
function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}
function getName(pc) {
return (pc === pc1) ? 'pc1' : 'pc2';
}
function onIceCandidate(pc, event) {
getOtherPc(pc)
.addIceCandidate(event.candidate)
.then(
onAddIceCandidateSuccess,
onAddIceCandidateError
);
console.log(`${getName(pc)} ICE candidate: ${event.candidate ? event.candidate.candidate : '(null)'}`);
}
function onAddIceCandidateSuccess() {
console.log('AddIceCandidate success.');
}
function onAddIceCandidateError(error) {
console.log(`Failed to add Ice Candidate: ${error.toString()}`);
}
function receiveChannelCallback(event) {
console.log('Receive Channel Callback');
receiveChannel = event.channel;
receiveChannel.onmessage = onReceiveMessageCallback;
receiveChannel.onopen = onReceiveChannelStateChange;
receiveChannel.onclose = onReceiveChannelStateChange;
}
function onReceiveMessageCallback(event) {
console.log('Received Message');
dataChannelReceive.value = event.data;
}
function onSendChannelStateChange() {
const readyState = sendChannel.readyState;
console.log('Send channel state is: ' + readyState);
if (readyState === 'open') {
dataChannelSend.disabled = false;
dataChannelSend.focus();
sendButton.disabled = false;
closeButton.disabled = false;
} else {
dataChannelSend.disabled = true;
sendButton.disabled = true;
closeButton.disabled = true;
}
}
function onReceiveChannelStateChange() {
const readyState = receiveChannel.readyState;
console.log(`Receive channel state is: ${readyState}`);
}
================================================
FILE: src/content/datachannel/basic/js/test.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
const webdriver = require('selenium-webdriver');
const seleniumHelpers = require('../../../../../test/webdriver');
let driver;
const path = '/src/content/datachannel/basic/index.html';
const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`;
describe('datachannel basic', () => {
beforeAll(async () => {
driver = await seleniumHelpers.buildDriver();
});
afterAll(() => {
return driver.quit();
});
beforeEach(() => {
return driver.get(url);
});
it('transfers text', async () => {
const text = 'Hello world';
await driver.findElement(webdriver.By.id('startButton')).click();
await Promise.all([
driver.wait(() => driver.executeScript(() => {
return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef
})),
await driver.wait(() => driver.executeScript(() => {
return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef
})),
]);
await driver.wait(() => driver.findElement(webdriver.By.id('sendButton')).isEnabled());
await driver.findElement(webdriver.By.id('dataChannelSend'))
.sendKeys(text);
await driver.findElement(webdriver.By.id('sendButton')).click();
await driver.wait(() => driver.executeScript(() => {
return document.getElementById('dataChannelReceive').value.length > 0;
}));
const value = await driver.findElement(webdriver.By.id('dataChannelReceive')).getAttribute('value');
expect(value).toBe(text);
});
});
================================================
FILE: src/content/datachannel/channel/css/main.css
================================================
/*
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
button {
margin: 0 1em 1em 0;
width: 90px;
}
div#buttons {
margin: 0 0 1em 0;
}
div#send {
margin: 0 20px 1em 0;
}
div#sendReceive {
border-bottom: 1px solid #eee;
margin: 0;
padding: 0 0 10px 0;
}
div#sendReceive > div {
display: inline-block;
width: calc(50% - 20px);
}
form {
margin: 0 0 1em 0;
white-space: nowrap;
}
form span {
font-weight: 300;
margin: 0 1em 0 0;
white-space: normal;
}
textarea {
color: #444;
font-size: 0.9em;
font-weight: 300;
height: 7.0em;
padding: 5px;
width: calc(100% - 10px);
}
================================================
FILE: src/content/datachannel/channel/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Transmit text (between two tabs)</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a>
<span>Transmit text</span></h1>
<div id="buttons">
<button id="startButton" disabled>Start</button>
<button id="sendButton" disabled>Send</button>
<button id="closeButton" disabled>Stop</button>
</div>
<div id="sendReceive">
<div id="send">
<h2>Send</h2>
<textarea id="dataChannelSend" disabled
placeholder="Press Start, enter some text, then press Send."></textarea>
</div>
<div id="receive">
<h2>Receive</h2>
<textarea id="dataChannelReceive" disabled></textarea>
</div>
</div>
<p>This sample shows how to setup a datachannel connection between two peers in different tabs using
<a href="https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection">RTCPeerConnection</a>
and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API">Broadcast Channel</a>
</p>
<p>Open the sample in two tabs (of the same browser), then click start in the first tab and
send messages back and forth.</p>
<p>For more information about RTCDataChannel, see <a
href="http://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-rtcdatachannel"
title="RTCDataChannel section of HTML5 Rocks article about WebRTC">Getting Started With WebRTC</a>.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/datachannel/channel"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/datachannel/channel/js/main.js
================================================
/*
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const startButton = document.getElementById('startButton');
const closeButton = document.getElementById('closeButton');
const sendButton = document.getElementById('sendButton');
sendButton.onclick = sendData;
const dataChannelSend = document.querySelector('textarea#dataChannelSend');
const dataChannelReceive = document.querySelector('textarea#dataChannelReceive');
let pc;
let sendChannel;
let receiveChannel;
const signaling = new BroadcastChannel('webrtc');
signaling.onmessage = e => {
switch (e.data.type) {
case 'offer':
handleOffer(e.data);
break;
case 'answer':
handleAnswer(e.data);
break;
case 'candidate':
handleCandidate(e.data);
break;
case 'ready':
// A second tab joined. This tab will enable the start button unless in a call already.
if (pc) {
console.log('already in call, ignoring');
return;
}
startButton.disabled = false;
break;
case 'bye':
if (pc) {
hangup();
}
break;
default:
console.log('unhandled', e);
break;
}
};
signaling.postMessage({type: 'ready'});
startButton.onclick = async () => {
startButton.disabled = true;
closeButton.disabled = false;
await createPeerConnection();
sendChannel = pc.createDataChannel('sendDataChannel');
sendChannel.onopen = onSendChannelStateChange;
sendChannel.onmessage = onSendChannelMessageCallback;
sendChannel.onclose = onSendChannelStateChange;
const offer = await pc.createOffer();
signaling.postMessage({type: 'offer', sdp: offer.sdp});
await pc.setLocalDescription(offer);
};
closeButton.onclick = async () => {
hangup();
signaling.postMessage({type: 'bye'});
};
async function hangup() {
if (pc) {
pc.close();
pc = null;
}
sendChannel = null;
receiveChannel = null;
console.log('Closed peer connections');
startButton.disabled = false;
sendButton.disabled = true;
closeButton.disabled = true;
dataChannelSend.value = '';
dataChannelReceive.value = '';
dataChannelSend.disabled = true;
};
function createPeerConnection() {
pc = new RTCPeerConnection();
pc.onicecandidate = e => {
const message = {
type: 'candidate',
candidate: null,
};
if (e.candidate) {
message.candidate = e.candidate.candidate;
message.sdpMid = e.candidate.sdpMid;
message.sdpMLineIndex = e.candidate.sdpMLineIndex;
}
signaling.postMessage(message);
};
}
async function handleOffer(offer) {
if (pc) {
console.error('existing peerconnection');
return;
}
await createPeerConnection();
pc.ondatachannel = receiveChannelCallback;
await pc.setRemoteDescription(offer);
const answer = await pc.createAnswer();
signaling.postMessage({type: 'answer', sdp: answer.sdp});
await pc.setLocalDescription(answer);
}
async function handleAnswer(answer) {
if (!pc) {
console.error('no peerconnection');
return;
}
await pc.setRemoteDescription(answer);
}
async function handleCandidate(candidate) {
if (!pc) {
console.error('no peerconnection');
return;
}
if (!candidate.candidate) {
await pc.addIceCandidate(null);
} else {
await pc.addIceCandidate(candidate);
}
}
function sendData() {
const data = dataChannelSend.value;
if (sendChannel) {
sendChannel.send(data);
} else {
receiveChannel.send(data);
}
console.log('Sent Data: ' + data);
}
function receiveChannelCallback(event) {
console.log('Receive Channel Callback');
receiveChannel = event.channel;
receiveChannel.onmessage = onReceiveChannelMessageCallback;
receiveChannel.onopen = onReceiveChannelStateChange;
receiveChannel.onclose = onReceiveChannelStateChange;
}
function onReceiveChannelMessageCallback(event) {
console.log('Received Message');
dataChannelReceive.value = event.data;
}
function onSendChannelMessageCallback(event) {
console.log('Received Message');
dataChannelReceive.value = event.data;
}
function onSendChannelStateChange() {
const readyState = sendChannel.readyState;
console.log('Send channel state is: ' + readyState);
if (readyState === 'open') {
dataChannelSend.disabled = false;
dataChannelSend.focus();
sendButton.disabled = false;
closeButton.disabled = false;
} else {
dataChannelSend.disabled = true;
sendButton.disabled = true;
closeButton.disabled = true;
}
}
function onReceiveChannelStateChange() {
const readyState = receiveChannel.readyState;
console.log(`Receive channel state is: ${readyState}`);
if (readyState === 'open') {
dataChannelSend.disabled = false;
sendButton.disabled = false;
closeButton.disabled = false;
} else {
dataChannelSend.disabled = true;
sendButton.disabled = true;
closeButton.disabled = true;
}
}
================================================
FILE: src/content/datachannel/channel/js/test.js
================================================
/*
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
const webdriver = require('selenium-webdriver');
const seleniumHelpers = require('../../../../../test/webdriver');
let driver;
const path = '/src/content/datachannel/channel/index.html';
const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`;
describe('datachannel and broadcast channels', () => {
beforeAll(async () => {
driver = await seleniumHelpers.buildDriver();
});
afterAll(() => {
return driver.quit();
});
beforeEach(async () => {
await driver.get(url);
});
it('establishes a connection and sends a message back and forth', async () => {
const firstHello = 'First tab to second tab';
const secondHello = 'Second tab to first tab';
const firstTab = await driver.getWindowHandle();
// Create a second tab, switch to it.
await driver.switchTo().newWindow('tab');
const secondTab = await driver.getWindowHandle();
await driver.get(url);
await driver.switchTo().window(firstTab);
await driver.findElement(webdriver.By.id('startButton')).click();
// Assert state in first tab.
await driver.switchTo().window(firstTab);
await driver.wait(() => driver.executeScript(() => {
return pc && pc.connectionState === 'connected'; // eslint-disable-line no-undef
}));
// Assert state in second tab.
await driver.switchTo().window(secondTab);
await driver.wait(() => driver.executeScript(() => {
return pc && pc.connectionState === 'connected'; // eslint-disable-line no-undef
}));
// Send a message from the first tab to the second tab.
await driver.switchTo().window(firstTab);
await driver.wait(() => driver.findElement(webdriver.By.id('sendButton')).isEnabled());
await driver.findElement(webdriver.By.id('dataChannelSend'))
.sendKeys(firstHello);
await driver.findElement(webdriver.By.id('sendButton')).click();
// Assert it was received.
await driver.switchTo().window(secondTab);
await driver.wait(() => driver.executeScript(() => {
return document.getElementById('dataChannelReceive').value.length > 0;
}));
const fromFirst= await driver.findElement(webdriver.By.id('dataChannelReceive')).getAttribute('value');
expect(fromFirst).toBe(firstHello);
// Send a message from the second tab to the first tab.
await driver.switchTo().window(secondTab);
await driver.wait(() => driver.findElement(webdriver.By.id('sendButton')).isEnabled());
await driver.findElement(webdriver.By.id('dataChannelSend'))
.sendKeys(secondHello);
await driver.findElement(webdriver.By.id('sendButton')).click();
// Assert it was received.
await driver.switchTo().window(firstTab);
await driver.wait(() => driver.executeScript(() => {
return document.getElementById('dataChannelReceive').value.length > 0;
}));
const fromSecond = await driver.findElement(webdriver.By.id('dataChannelReceive')).getAttribute('value');
expect(fromSecond).toBe(secondHello);
});
});
================================================
FILE: src/content/datachannel/datatransfer/css/main.css
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
div.progress {
margin: 0 0 1em 0;
}
div.progress div.label {
display: inline-block;
font-weight: 400;
width: 8.2em;
}
div.input {
margin: 0 0 1em 0;
}
progress {
width: calc(100% - 8.5em);
}
================================================
FILE: src/content/datachannel/datatransfer/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Generate and transfer data</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="https://webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Generate and transfer data</span>
</h1>
<section>
<p>This page generates and sends the specified amount of data via WebRTC datachannels.</p>
<p>To accomplish this in an interoperable way, the data is split into chunks which are then transferred via the
datachannel. The datachannel is reliable and ordered by default which is well-suited to filetransfers.</p>
<p>Send and receive progress is monitored using HTML5 <i>progress</i> elements.</p>
</section>
<section>
<div id="button">
<button id="sendTheData" type="button">Generate and send data</button>
</div>
<div class="input">
<input type="number" id="megsToSend" min="1" name="megs" value="16"/>
<label for="megsToSend">MB <b>(warning: very large values will potentially cause memory problems)</b></label>
<div id="errorMsg"></div>
</div>
<div class="input">
<input type="checkbox" id="ordered" checked>
<label for="ordered">Ordered mode</label>
</div>
<div class="progress">
<div class="label">Send progress:</div>
<progress id="sendProgress" max="0" value="0"></progress>
</div>
<div class="progress">
<div class="label">Receive progress:</div>
<progress id="receiveProgress" max="0" value="0"></progress>
</div>
<div>
<span id="transferStatus"></span>
</div>
</section>
<section>
<p>View the console to see logging.</p>
<p>The <code>RTCPeerConnection</code> objects <code>pc1</code> and <code>pc2</code> are
in global scope, so you can inspect them in the console as well.</p>
<p>For more information about RTCDataChannel, see <a
href="http://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-rtcdatachannel"
title="RTCDataChannel section of HTML5 Rocks article about WebRTC">Getting Started With WebRTC</a>.</p>
</section>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/datachannel/datatransfer"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/datachannel/datatransfer/js/main.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const MAX_CHUNK_SIZE = 262144;
let pc1;
let pc2;
let sendChannel;
let receiveChannel;
let chunkSize;
let lowWaterMark;
let highWaterMark;
let dataString;
let timeoutHandle = null;
const megsToSend = document.querySelector('input#megsToSend');
const sendButton = document.querySelector('button#sendTheData');
const orderedCheckbox = document.querySelector('input#ordered');
const sendProgress = document.querySelector('progress#sendProgress');
const receiveProgress = document.querySelector('progress#receiveProgress');
const errorMessage = document.querySelector('div#errorMsg');
const transferStatus = document.querySelector('span#transferStatus');
let bytesToSend = 0;
let totalTimeUsedInSend = 0;
let numberOfSendCalls = 0;
let maxTimeUsedInSend = 0;
let sendStartTime = 0;
let currentThroughput = 0;
sendButton.addEventListener('click', createConnection);
// Prevent data sent to be set to 0.
megsToSend.addEventListener('change', function() {
const number = this.value;
if (Number.isNaN(number)) {
errorMessage.innerHTML = `Invalid value for MB to send: ${number}`;
} else if (number <= 0) {
sendButton.disabled = true;
errorMessage.innerHTML = '<p>Please enter a number greater than zero.</p>';
} else if (number > 64) {
sendButton.disabled = true;
errorMessage.innerHTML = '<p>Please enter a number lower or equal than 64.</p>';
} else {
errorMessage.innerHTML = '';
sendButton.disabled = false;
}
});
async function createConnection() {
sendButton.disabled = true;
megsToSend.disabled = true;
const servers = null;
const number = Number.parseInt(megsToSend.value);
bytesToSend = number * 1024 * 1024;
pc1 = new RTCPeerConnection(servers);
// Let's make a data channel!
const dataChannelParams = {ordered: false};
if (orderedCheckbox.checked) {
dataChannelParams.ordered = true;
}
sendChannel = pc1.createDataChannel('sendDataChannel', dataChannelParams);
sendChannel.addEventListener('open', onSendChannelOpen);
sendChannel.addEventListener('close', onSendChannelClosed);
console.log('Created send data channel: ', sendChannel);
console.log('Created local peer connection object pc1: ', pc1);
pc1.addEventListener('icecandidate', e => onIceCandidate(pc1, e));
pc2 = new RTCPeerConnection(servers);
pc2.addEventListener('icecandidate', e => onIceCandidate(pc2, e));
pc2.addEventListener('datachannel', receiveChannelCallback);
try {
const localOffer = await pc1.createOffer();
await handleLocalDescription(localOffer);
} catch (e) {
console.error('Failed to create session description: ', e);
}
transferStatus.innerHTML = 'Peer connection setup complete.';
}
function sendData() {
// Stop scheduled timer if any (part of the workaround introduced below)
if (timeoutHandle !== null) {
clearTimeout(timeoutHandle);
timeoutHandle = null;
}
let bufferedAmount = sendChannel.bufferedAmount;
while (sendProgress.value < sendProgress.max) {
transferStatus.innerText = 'Sending data...';
const timeBefore = performance.now();
sendChannel.send(dataString);
const timeUsed = performance.now() - timeBefore;
if (timeUsed > maxTimeUsedInSend) {
maxTimeUsedInSend = timeUsed;
totalTimeUsedInSend += timeUsed;
}
numberOfSendCalls += 1;
bufferedAmount += chunkSize;
sendProgress.value += chunkSize;
// Pause sending if we reach the high water mark
if (bufferedAmount >= highWaterMark) {
// This is a workaround due to the bug that all browsers are incorrectly calculating the
// amount of buffered data. Therefore, the 'bufferedamountlow' event would not fire.
if (sendChannel.bufferedAmount < lowWaterMark) {
timeoutHandle = setTimeout(() => sendData(), 0);
}
console.log(`Paused sending, buffered amount: ${bufferedAmount} (announced: ${sendChannel.bufferedAmount})`);
break;
}
}
if (sendProgress.value === sendProgress.max) {
transferStatus.innerHTML = 'Data transfer completed successfully!';
}
}
function startSendingData() {
transferStatus.innerHTML = 'Start sending data.';
sendProgress.max = bytesToSend;
receiveProgress.max = sendProgress.max;
sendProgress.value = 0;
receiveProgress.value = 0;
sendStartTime = performance.now();
maxTimeUsedInSend = 0;
totalTimeUsedInSend = 0;
numberOfSendCalls = 0;
sendData();
}
function maybeReset() {
if (pc1 === null && pc2 === null) {
sendButton.disabled = false;
megsToSend.disabled = false;
}
}
async function handleLocalDescription(desc) {
pc1.setLocalDescription(desc);
console.log('Offer from pc1:\n', desc.sdp);
pc2.setRemoteDescription(desc);
try {
const remoteAnswer = await pc2.createAnswer();
handleRemoteAnswer(remoteAnswer);
} catch (e) {
console.error('Error when creating remote answer: ', e);
}
}
function handleRemoteAnswer(desc) {
pc2.setLocalDescription(desc);
console.log('Answer from pc2:\n', desc.sdp);
pc1.setRemoteDescription(desc);
}
function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}
async function onIceCandidate(pc, event) {
const candidate = event.candidate;
if (candidate === null) {
return;
} // Ignore null candidates
try {
await getOtherPc(pc).addIceCandidate(candidate);
console.log('AddIceCandidate successful: ', candidate);
} catch (e) {
console.error('Failed to add Ice Candidate: ', e);
}
}
function receiveChannelCallback(event) {
console.log('Receive Channel Callback');
receiveChannel = event.channel;
receiveChannel.binaryType = 'arraybuffer';
receiveChannel.addEventListener('close', onReceiveChannelClosed);
receiveChannel.addEventListener('message', onReceiveMessageCallback);
}
function onReceiveMessageCallback(event) {
receiveProgress.value += event.data.length;
currentThroughput = receiveProgress.value / (performance.now() - sendStartTime);
console.log('Current Throughput is:', currentThroughput, 'bytes/sec');
// Workaround for a bug in Chrome which prevents the closing event from being raised by the
// remote side. Also a workaround for Firefox which does not send all pending data when closing
// the channel.
if (receiveProgress.value === receiveProgress.max) {
sendChannel.close();
receiveChannel.close();
}
}
function onSendChannelOpen() {
console.log('Send channel is open');
chunkSize = Math.min(pc1.sctp.maxMessageSize, MAX_CHUNK_SIZE);
console.log('Determined chunk size: ', chunkSize);
dataString = new Array(chunkSize).fill('X').join('');
lowWaterMark = chunkSize; // A single chunk
highWaterMark = Math.max(chunkSize * 8, 1048576); // 8 chunks or at least 1 MiB
console.log('Send buffer low water threshold: ', lowWaterMark);
console.log('Send buffer high water threshold: ', highWaterMark);
sendChannel.bufferedAmountLowThreshold = lowWaterMark;
sendChannel.addEventListener('bufferedamountlow', (e) => {
console.log('BufferedAmountLow event:', e);
sendData();
});
startSendingData();
}
function onSendChannelClosed() {
console.log('Send channel is closed');
pc1.close();
pc1 = null;
console.log('Closed local peer connection');
maybeReset();
console.log('Average time spent in send() (ms): ' +
totalTimeUsedInSend / numberOfSendCalls);
console.log('Max time spent in send() (ms): ' + maxTimeUsedInSend);
const spentTime = performance.now() - sendStartTime;
console.log('Total time spent: ' + spentTime);
console.log('MBytes/Sec: ' + (bytesToSend / 1000) / spentTime);
}
function onReceiveChannelClosed() {
console.log('Receive channel is closed');
pc2.close();
pc2 = null;
console.log('Closed remote peer connection');
maybeReset();
}
================================================
FILE: src/content/datachannel/datatransfer/js/test.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
const webdriver = require('selenium-webdriver');
const seleniumHelpers = require('../../../../../test/webdriver');
let driver;
const path = '/src/content/datachannel/datatransfer/index.html';
const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`;
describe('datachannel datatransfer', () => {
beforeAll(async () => {
driver = await seleniumHelpers.buildDriver();
});
afterAll(() => {
return driver.quit();
});
beforeEach(() => {
return driver.get(url);
});
it('transfers data', async () => {
const megsToSend = 4;
await driver.findElement(webdriver.By.id('megsToSend'))
.clear();
await driver.findElement(webdriver.By.id('megsToSend'))
.sendKeys(megsToSend + '\n');
await driver.findElement(webdriver.By.id('sendTheData')).click();
await Promise.all([
driver.wait(() => driver.executeScript(() => {
return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef
})),
await driver.wait(() => driver.executeScript(() => {
return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef
})),
]);
// the remote connection gets closed when it is done.
await driver.wait(() => driver.executeScript(() => {
return pc2 === null; // eslint-disable-line no-undef
}));
const transferred = await driver.findElement(webdriver.By.id('receiveProgress')).getAttribute('value');
expect(transferred >>> 0).toBe(megsToSend * 1024 * 1024);
});
});
================================================
FILE: src/content/datachannel/filetransfer/css/main.css
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
div.progress, div#bitrate {
margin: 0 0 1em 0;
}
div.progress div.label {
display: inline-block;
font-weight: 400;
width: 8.2em;
}
form {
margin: 0 0 1em 0;
white-space: nowrap;
}
progress {
width: calc(100% - 8.5em);
}
================================================
FILE: src/content/datachannel/filetransfer/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Transfer a file</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<div id="container">
<h1><a href="https://webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Transfer a file</span></h1>
<section>
<p>This page shows how to transfer a file via WebRTC datachannels.</p>
<p>To accomplish this in an interoperable way, the file is split into chunks which are then transferred via the datachannel. The datachannel is reliable and ordered by default which is well-suited to filetransfers.</p>
<p>Send and receive progress is monitored using HTML5 <i>progress</i> elements.</p>
<p>At the receiver, the file is reassembled using the Blob API and made available for download.</p>
<p>Note: real-world applications require a file transfer protocol to send metadata about the file (such as the filename, type, size, last modification date, hash, ...).This information can be conveyed either via the signaling channel or in-band. The demo elides this by assuming knowledge of the file size at the receiver and closes both the datachannel and the peerconnection when the correct amount of bytes has been received.</p>
</section>
<section>
<div >
<form id="fileInfo">
<input type="file" id="fileInput" name="files"/>
</form>
<button disabled id="sendFile">Send</button>
<button disabled id="abortButton">Abort</button>
</div>
<div class="progress">
<div class="label">Send progress: </div>
<progress id="sendProgress" max="0" value="0"></progress>
</div>
<div class="progress">
<div class="label">Receive progress: </div>
<progress id="receiveProgress" max="0" value="0"></progress>
</div>
<div id="bitrate"></div>
<a id="download"></a>
<span id="status"></span>
</section>
<section>
<p>View the console to see logging.</p>
<p>The <code>RTCPeerConnection</code> objects <code>pc1</code> and <code>pc2</code> are in global scope, so you can inspect them in the console as well.</p>
<p>For more information about RTCDataChannel, see <a href="http://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-rtcdatachannel" title="RTCDataChannel section of HTML5 Rocks article about WebRTC">Getting Started With WebRTC</a>.</p>
</section>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/datachannel/filetransfer" title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/datachannel/filetransfer/js/main.js
================================================
/* eslint no-unused-expressions: 0 */
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
let pc1;
let pc2;
let sendChannel;
let receiveChannel;
let fileReader;
const bitrateDiv = document.querySelector('div#bitrate');
const fileInput = document.querySelector('input#fileInput');
const abortButton = document.querySelector('button#abortButton');
const downloadAnchor = document.querySelector('a#download');
const sendProgress = document.querySelector('progress#sendProgress');
const receiveProgress = document.querySelector('progress#receiveProgress');
const statusMessage = document.querySelector('span#status');
const sendFileButton = document.querySelector('button#sendFile');
let receiveBuffer = [];
let receivedSize = 0;
let bytesPrev = 0;
let timestampPrev = 0;
let timestampStart;
let statsInterval = null;
let bitrateMax = 0;
sendFileButton.addEventListener('click', () => createConnection());
fileInput.addEventListener('change', handleFileInputChange, false);
abortButton.addEventListener('click', () => {
if (fileReader && fileReader.readyState === 1) {
console.log('Abort read!');
fileReader.abort();
}
});
async function handleFileInputChange() {
const file = fileInput.files[0];
if (!file) {
console.log('No file chosen');
} else {
sendFileButton.disabled = false;
}
}
async function createConnection() {
abortButton.disabled = false;
sendFileButton.disabled = true;
pc1 = new RTCPeerConnection();
console.log('Created local peer connection object pc1');
sendChannel = pc1.createDataChannel('sendDataChannel');
sendChannel.binaryType = 'arraybuffer';
console.log('Created send data channel');
sendChannel.addEventListener('open', onSendChannelStateChange);
sendChannel.addEventListener('close', onSendChannelStateChange);
sendChannel.addEventListener('error', onError);
pc1.addEventListener('icecandidate', async event => {
console.log('Local ICE candidate: ', event.candidate);
await pc2.addIceCandidate(event.candidate);
});
pc2 = new RTCPeerConnection();
console.log('Created remote peer connection object pc2');
pc2.addEventListener('icecandidate', async event => {
console.log('Remote ICE candidate: ', event.candidate);
await pc1.addIceCandidate(event.candidate);
});
pc2.addEventListener('datachannel', receiveChannelCallback);
try {
const offer = await pc1.createOffer();
await gotLocalDescription(offer);
} catch (e) {
console.log('Failed to create session description: ', e);
}
fileInput.disabled = true;
}
function sendData() {
const file = fileInput.files[0];
console.log(`File is ${[file.name, file.size, file.type, file.lastModified].join(' ')}`);
// Handle 0 size files.
statusMessage.textContent = '';
downloadAnchor.textContent = '';
if (file.size === 0) {
bitrateDiv.innerHTML = '';
statusMessage.textContent = 'File is empty, please select a non-empty file';
closeDataChannels();
return;
}
sendProgress.max = file.size;
receiveProgress.max = file.size;
const chunkSize = 16384;
fileReader = new FileReader();
let offset = 0;
fileReader.addEventListener('error', error => console.error('Error reading file:', error));
fileReader.addEventListener('abort', event => console.log('File reading aborted:', event));
fileReader.addEventListener('load', e => {
console.log('FileRead.onload ', e);
sendChannel.send(e.target.result);
offset += e.target.result.byteLength;
sendProgress.value = offset;
if (offset < file.size) {
readSlice(offset);
}
});
const readSlice = o => {
console.log('readSlice ', o);
const slice = file.slice(offset, o + chunkSize);
fileReader.readAsArrayBuffer(slice);
};
readSlice(0);
}
function closeDataChannels() {
console.log('Closing data channels');
sendChannel.close();
console.log(`Closed data channel with label: ${sendChannel.label}`);
sendChannel = null;
if (receiveChannel) {
receiveChannel.close();
console.log(`Closed data channel with label: ${receiveChannel.label}`);
receiveChannel = null;
}
pc1.close();
pc2.close();
pc1 = null;
pc2 = null;
console.log('Closed peer connections');
// re-enable the file select
fileInput.disabled = false;
abortButton.disabled = true;
sendFileButton.disabled = false;
}
async function gotLocalDescription(desc) {
await pc1.setLocalDescription(desc);
console.log(`Offer from pc1\n ${desc.sdp}`);
await pc2.setRemoteDescription(desc);
try {
const answer = await pc2.createAnswer();
await gotRemoteDescription(answer);
} catch (e) {
console.log('Failed to create session description: ', e);
}
}
async function gotRemoteDescription(desc) {
await pc2.setLocalDescription(desc);
console.log(`Answer from pc2\n ${desc.sdp}`);
await pc1.setRemoteDescription(desc);
}
function receiveChannelCallback(event) {
console.log('Receive Channel Callback');
receiveChannel = event.channel;
receiveChannel.binaryType = 'arraybuffer';
receiveChannel.onmessage = onReceiveMessageCallback;
receiveChannel.onopen = onReceiveChannelStateChange;
receiveChannel.onclose = onReceiveChannelStateChange;
receivedSize = 0;
bitrateMax = 0;
downloadAnchor.textContent = '';
downloadAnchor.removeAttribute('download');
if (downloadAnchor.href) {
URL.revokeObjectURL(downloadAnchor.href);
downloadAnchor.removeAttribute('href');
}
}
function onReceiveMessageCallback(event) {
console.log(`Received Message ${event.data.byteLength}`);
receiveBuffer.push(event.data);
receivedSize += event.data.byteLength;
receiveProgress.value = receivedSize;
// we are assuming that our signaling protocol told
// about the expected file size (and name, hash, etc).
const file = fileInput.files[0];
if (receivedSize === file.size) {
const received = new Blob(receiveBuffer);
receiveBuffer = [];
downloadAnchor.href = URL.createObjectURL(received);
downloadAnchor.download = file.name;
downloadAnchor.textContent =
`Click to download '${file.name}' (${file.size} bytes)`;
downloadAnchor.style.display = 'block';
const bitrate = Math.round(receivedSize * 8 /
((new Date()).getTime() - timestampStart));
bitrateDiv.innerHTML =
`<strong>Average Bitrate:</strong> ${bitrate} kbits/sec (max: ${bitrateMax} kbits/sec)`;
if (statsInterval) {
clearInterval(statsInterval);
statsInterval = null;
}
closeDataChannels();
}
}
function onSendChannelStateChange() {
if (sendChannel) {
const {readyState} = sendChannel;
console.log(`Send channel state is: ${readyState}`);
if (readyState === 'open') {
sendData();
}
}
}
function onError(error) {
if (sendChannel) {
console.error('Error in sendChannel:', error);
return;
}
console.log('Error in sendChannel which is already closed:', error);
}
async function onReceiveChannelStateChange() {
if (receiveChannel) {
const readyState = receiveChannel.readyState;
console.log(`Receive channel state is: ${readyState}`);
if (readyState === 'open') {
timestampStart = (new Date()).getTime();
timestampPrev = timestampStart;
statsInterval = setInterval(displayStats, 500);
await displayStats();
}
}
}
// display bitrate statistics.
async function displayStats() {
if (pc2 && pc2.iceConnectionState === 'connected') {
const stats = await pc2.getStats();
let activeCandidatePair;
stats.forEach(report => {
if (report.type === 'transport') {
activeCandidatePair = stats.get(report.selectedCandidatePairId);
}
});
if (activeCandidatePair) {
if (timestampPrev === activeCandidatePair.timestamp) {
return;
}
// calculate current bitrate
const bytesNow = activeCandidatePair.bytesReceived;
const bitrate = Math.round((bytesNow - bytesPrev) * 8 /
(activeCandidatePair.timestamp - timestampPrev));
bitrateDiv.innerHTML = `<strong>Current Bitrate:</strong> ${bitrate} kbits/sec`;
timestampPrev = activeCandidatePair.timestamp;
bytesPrev = bytesNow;
if (bitrate > bitrateMax) {
bitrateMax = bitrate;
}
}
}
}
================================================
FILE: src/content/datachannel/filetransfer/js/test.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
const webdriver = require('selenium-webdriver');
const seleniumHelpers = require('../../../../../test/webdriver');
let driver;
const path = '/src/content/datachannel/filetransfer/index.html';
const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`;
describe('datachannel filetransfer', () => {
beforeAll(async () => {
driver = await seleniumHelpers.buildDriver();
});
afterAll(() => {
return driver.quit();
});
beforeEach(() => {
return driver.get(url);
});
it('transfers a file', async () => {
await driver.findElement(webdriver.By.id('fileInput'))
.sendKeys(process.cwd() + '/src/content/devices/multi/images/poster.jpg');
await driver.wait(() => driver.findElement(webdriver.By.id('sendFile')).isEnabled());
await driver.findElement(webdriver.By.id('sendFile')).click();
// the remote connection gets closed when it is done.
await driver.wait(() => driver.executeScript(() => {
return pc2 === null; // eslint-disable-line no-undef
}));
await driver.wait(() => driver.findElement(webdriver.By.id('download')).isEnabled());
});
});
================================================
FILE: src/content/datachannel/messaging/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Send messages with datachannel</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<link rel="stylesheet" href="main.css"/>
<script src="https://unpkg.com/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="main.js" type="module"></script>
</head>
<body>
<div id="container">
<h1><a href="https://webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Send messages with datachannel</span>
</h1>
<section>
<p>This page show how to send text messages via WebRTC datachannels.</p>
<p>Enter a message in one text box and press send and it will be transferred to the "remote" peer over a
datachannel.</p>
</section>
<messaging-sample></messaging-sample>
<section>
<p>View the console to see logging.</p>
<p>For more information about RTCDataChannel, see <a
href="http://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-rtcdatachannel"
title="RTCDataChannel section of HTML5 Rocks article about WebRTC">Getting Started With WebRTC</a>.</p>
</section>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/datachannel/messaging"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/datachannel/messaging/main.css
================================================
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
div.messageBox {
width: 100%;
}
textarea.message {
width: 100%;
height: 5em;
resize: none;
display: block;
box-sizing: border-box;
margin: 1em;
}
label {
font-weight: 400;
}
================================================
FILE: src/content/datachannel/messaging/main.js
================================================
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint no-unused-expressions: 0 */
'use strict';
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@0.6.2?module';
class MessagingSample extends LitElement {
constructor() {
super();
this.connected = false;
this.localMessages = '';
this.remoteMessages = '';
}
disconnect() {
this._pc1.close();
this._pc2.close();
}
async connect() {
console.log('connect!');
try {
const dataChannelParams = {ordered: true};
this._pc1 = new RTCPeerConnection();
this._pc1.addEventListener('icecandidate', async e => {
console.log('local connection ICE candidate: ', e.candidate);
await this._pc2.addIceCandidate(e.candidate);
});
this._pc2 = new RTCPeerConnection();
this._pc2.addEventListener('icecandidate', async e => {
console.log('remote connection ICE candidate: ', e.candidate);
await this._pc1.addIceCandidate(e.candidate);
});
window.localChannel = this._localChannel = this._pc1
.createDataChannel('messaging-channel', dataChannelParams);
this._localChannel.binaryType = 'arraybuffer';
this._localChannel.addEventListener('open', () => {
console.log('Local channel open!');
this.connected = true;
});
this._localChannel.addEventListener('close', () => {
console.log('Local channel closed!');
this.connected = false;
});
this._localChannel.addEventListener('message', this._onLocalMessageReceived.bind(this));
this._pc2.addEventListener('datachannel', this._onRemoteDataChannel.bind(this));
const initLocalOffer = async () => {
const localOffer = await this._pc1.createOffer();
console.log(`Got local offer ${JSON.stringify(localOffer)}`);
const localDesc = this._pc1.setLocalDescription(localOffer);
const remoteDesc = this._pc2.setRemoteDescription(localOffer);
return Promise.all([localDesc, remoteDesc]);
};
const initRemoteAnswer = async () => {
const remoteAnswer = await this._pc2.createAnswer();
console.log(`Got remote answer ${JSON.stringify(remoteAnswer)}`);
const localDesc = this._pc2.setLocalDescription(remoteAnswer);
const remoteDesc = this._pc1.setRemoteDescription(remoteAnswer);
return Promise.all([localDesc, remoteDesc]);
};
await initLocalOffer();
await initRemoteAnswer();
} catch (e) {
console.log(e);
}
}
_onLocalMessageReceived(event) {
console.log(`Remote message received by local: ${event.data}`);
this.localMessages += event.data + '\n';
}
_onRemoteDataChannel(event) {
console.log(`onRemoteDataChannel: ${JSON.stringify(event)}`);
window.remoteChannel = this._remoteChannel = event.channel;
this._remoteChannel.binaryType = 'arraybuffer';
this._remoteChannel.addEventListener('message', this._onRemoteMessageReceived.bind(this));
this._remoteChannel.addEventListener('close', () => {
console.log('Remote channel closed!');
this.connected = false;
});
}
_onRemoteMessageReceived(event) {
console.log(`Local message received by remote: ${event.data}`);
this.remoteMessages += event.data + '\n';
}
static get properties() {
return {
connected: {type: Boolean},
localMessages: {type: String},
remoteMessages: {type: String}
};
}
render() {
return html`<section>
<style>
@import "../../../css/main.css";
@import "main.css";
</style>
<div>
<button ?disabled="${this.connected}" @click="${this.connect.bind(this)}">Connect</button>
<button ?disabled="${!this.connected}" @click="${this.disconnect.bind(this)}">Disconnect</button>
</div>
<div class="messageBox">
<label for="localOutgoing">Local outgoing message:</label>
<textarea class="message" id="localOutgoing"
placeholder="Local outgoing message goes here."></textarea>
<button ?disabled="${!this.connected}" @click="${e => this._sendMessage('#localOutgoing', this._localChannel)}
id="sendLocal">Send message from local</button>
</div>
<div class="messageBox">
<label for="localIncoming">Local incoming messages:</label>
<textarea class="message" id="localIncoming" disabled
placeholder="Local incoming messages arrive here.">${this.localMessages}</textarea>
</div>
<div class="messageBox">
<label for="remoteOutgoing">Remote outgoing message:</label>
<textarea class="message" id="remoteOutgoing"
placeholder="Remote outgoing message goes here."></textarea>
<button ?disabled="${!this.connected}" @click="${e => this._sendMessage('#remoteOutgoing', this._remoteChannel)}"
id="sendRemote">Send message from remote</button>
</div>
<div class="messageBox">
<label for="remoteIncoming">Remote incoming messages:</label>
<textarea class="message" id="remoteIncoming" disabled
placeholder="Remote incoming messages arrive here.">${this.remoteMessages}</textarea>
</div>
</section>`;
}
_sendMessage(selector, channel) {
const textarea = this.shadowRoot.querySelector(selector);
const value = textarea.value;
if (value === '') {
console.log('Not sending empty message!');
return;
}
console.log('Sending remote message: ', value);
channel.send(value);
textarea.value = '';
}
}
customElements.define('messaging-sample', MessagingSample);
================================================
FILE: src/content/devices/input-output/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Select audio and video sources</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<style>
div.select {
display: inline-block;
margin: 0 0 1em 0;
}
p.small {
font-size: 0.7em;
}
label {
width: 12em;
display: inline-block;
}
</style>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a><span>Select sources & outputs</span>
</h1>
<p>Get available audio, video sources and audio output devices from <code>mediaDevices.enumerateDevices()</code>
then set the source for <code>getUserMedia()</code> using a <code>deviceId</code> constraint.</p>
<p><b>Note:</b> without permission, the browser will restrict the available devices to at most one per type.</p>
<div class="select">
<label for="audioSource">Audio input source: </label><select id="audioSource"></select>
</div>
<div class="select">
<label for="audioOutput">Audio output destination: </label><select id="audioOutput"></select>
</div>
<div class="select">
<label for="videoSource">Video source: </label><select id="videoSource"></select>
</div>
<video id="video" playsinline autoplay></video>
<p><b>Note:</b> If you hear a reverb sound your microphone is picking up the output of your
speakers/headset, lower the volume and/or move the microphone further away from your speakers/headset.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/devices/input-output"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/devices/input-output/js/main.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const videoElement = document.querySelector('video');
const audioInputSelect = document.querySelector('select#audioSource');
const audioOutputSelect = document.querySelector('select#audioOutput');
const videoSelect = document.querySelector('select#videoSource');
const selectors = [audioInputSelect, audioOutputSelect, videoSelect];
let hasMic = false;
let hasCamera = false;
let openMic = undefined;
let openCamera = undefined;
let hasPermission = false;
audioOutputSelect.disabled = !('sinkId' in HTMLMediaElement.prototype);
function getDevices() {
navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
}
function gotDevices(deviceInfos) {
console.log('gotDevices', deviceInfos);
hasMic = false;
hasCamera = false;
hasPermission = false;
// Handles being called several times to update labels. Preserve values.
const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
if (deviceInfo.deviceId == '') {
continue;
}
// If we get at least one deviceId, that means user has granted user
// media permissions.
hasPermission = true;
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'audioinput') {
hasMic = true;
option.text = deviceInfo.label || `microphone ${audioInputSelect.length + 1}`;
audioInputSelect.appendChild(option);
} else if (deviceInfo.kind === 'audiooutput') {
option.text = deviceInfo.label || `speaker ${audioOutputSelect.length + 1}`;
audioOutputSelect.appendChild(option);
} else if (deviceInfo.kind === 'videoinput') {
hasCamera = true;
option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
videoSelect.appendChild(option);
} else {
console.log('Some other kind of source/device: ', deviceInfo);
}
}
selectors.forEach((select, selectorIndex) => {
if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
select.value = values[selectorIndex];
}
});
start();
}
// Attach audio output device to video element using device/sink ID.
function attachSinkId(element, sinkId) {
if (typeof element.sinkId !== 'undefined') {
element.setSinkId(sinkId)
.then(() => {
console.log(`Success, audio output device attached: ${sinkId}`);
})
.catch(error => {
let errorMessage = error;
if (error.name === 'SecurityError') {
errorMessage = `You need to use HTTPS for selecting audio output device: ${error}`;
}
console.error(errorMessage);
// Jump back to first output device in the list as it's the default.
audioOutputSelect.selectedIndex = 0;
});
} else {
console.warn('Browser does not support output device selection.');
}
}
function changeAudioDestination() {
const audioDestination = audioOutputSelect.value;
attachSinkId(videoElement, audioDestination);
}
function gotStream(stream) {
window.stream = stream; // make stream available to console
videoElement.srcObject = stream;
if (stream.getVideoTracks()[0]) {
openCamera = stream.getVideoTracks()[0].getSettings().deviceId;
}
if (stream.getAudioTracks()[0]) {
openMic = stream.getAudioTracks()[0].getSettings().deviceId;
}
// Refresh list in case labels have become available
return getDevices();
}
function handleError(error) {
console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
}
function start() {
const audioSource = audioInputSelect.value || undefined;
const videoSource = videoSelect.value || undefined;
// Don't open the same devices again.
if (hasPermission && openMic == audioSource && openCamera == videoSource) {
return;
}
// Close existng streams.
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
openCamera = undefined;
openMic = undefined;
}
const constraints = {
audio: true,
video: true
};
if (hasMic) {
constraints['audio'] = {deviceId: audioSource ? {exact: audioSource} : undefined};
}
if (hasCamera) {
constraints['video'] = {deviceId: videoSource ? {exact: videoSource} : undefined};
}
console.log('start', constraints);
if (!hasPermission || hasCamera || hasMic) {
navigator.mediaDevices.getUserMedia(constraints).then(gotStream).catch(handleError);
}
}
audioInputSelect.onchange = start;
audioOutputSelect.onchange = changeAudioDestination;
videoSelect.onchange = start;
navigator.mediaDevices.ondevicechange = getDevices;
getDevices();
================================================
FILE: src/content/devices/input-output/js/test.js
================================================
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
const seleniumHelpers = require('../../../../../test/webdriver');
let driver;
const path = '/src/content/devices/input-output/index.html';
const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`;
describe('input-output', () => {
beforeAll(async () => {
driver = await seleniumHelpers.buildDriver();
});
afterAll(() => {
return driver.quit();
});
beforeEach(() => {
return driver.get(url);
});
it('shows at least one audio input device', async () => {
await driver.wait(driver.executeScript(() => {
return document.getElementById('audioSource').childElementCount > 0;
}));
});
it('shows at least one video input device', async () => {
await driver.wait(driver.executeScript(() => {
return document.getElementById('videoSource').childElementCount > 0;
}));
});
it('shows at least one audio output device device', async function() {
if (process.env.BROWSER === 'firefox') {
this.skip();
}
await driver.wait(driver.executeScript(() => {
return document.getElementById('audioOutput').childElementCount > 0;
}));
});
});
================================================
FILE: src/content/devices/multi/css/main.css
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
audio {
margin: 0 0 1.5em 0;
width: 100%;
}
div#sources > div {
float: left;
margin: 0 1em 0 0;
width: calc(50% - 0.5em);
}
div#sources > div:last-of-type {
margin: 0;
}
select {
margin: 0 0 0.5em 0;
}
video {
background: black;
height: 234px;
}
================================================
FILE: src/content/devices/multi/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>getUserMedia: output device selection</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>getUserMedia: output device selection</span>
</h1>
<div id="sources">
<div>
<h2>getUserMedia():</h2>
<div>
<video class="gum" title="audio and video stream from getUserMedia()" playsinline autoplay controls></video>
<div class="outputSelector">
<label>Select audio output: </label>
<select></select>
</div>
</div>
<div>
<audio class="gum" title="audio stream from getUserMedia()" controls></audio>
<div class="outputSelector">
<label>Select audio output: </label>
<select></select>
</div>
</div>
</div>
<div>
<h2>Local media files:</h2>
<div>
<video title="local video file" playsinline controls loop poster="images/poster.jpg" preload="metadata">
<source src="video/chrome.webm" type="video/webm"/>
<source src="video/chrome.mp4" type="video/mp4"/>
<p>This browser does not support the video element.</p>
</video>
<div class="outputSelector">
<label>Select audio output: </label>
<select></select>
</div>
</div>
<div>
<audio title="local audio file" controls loop>
<source src="audio/audio.mp3" type="audio/mp3"/>
This browser does not support the audio element.
</audio>
<div class="outputSelector">
<label>Select audio output: </label>
<select></select>
</div>
</div>
</div>
</div>
<p>This demo must be run from localhost or over HTTPS Chrome 49 or later, Firefox is not supported yet.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/devices/multi"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/devices/multi/js/main.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const gumAudio = document.querySelector('audio.gum');
gumAudio.addEventListener('play', () => {
gumAudio.volume = 0.1;
console.log('Audio lowered to reduce feedback from local gUM stream');
});
const gumVideo = document.querySelector('video.gum');
gumVideo.addEventListener('play', () => {
gumVideo.volume = 0.1;
console.log('Audio lowered to reduce feedback from local gUM stream');
});
function gotDevices(deviceInfos) {
const masterOutputSelector = document.createElement('select');
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'audiooutput') {
console.info('Found audio output device: ', deviceInfo.label);
option.text = deviceInfo.label || `speaker ${masterOutputSelector.length + 1}`;
masterOutputSelector.appendChild(option);
} else {
console.log('Found non audio output device: ', deviceInfo.label);
}
}
// Clone the master outputSelector and replace outputSelector placeholders.
const allOutputSelectors = document.querySelectorAll('select');
for (let selector = 0; selector < allOutputSelectors.length; selector++) {
const newOutputSelector = masterOutputSelector.cloneNode(true);
newOutputSelector.addEventListener('change', changeAudioDestination);
allOutputSelectors[selector].parentNode.replaceChild(newOutputSelector,
allOutputSelectors[selector]);
}
}
navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
// Attach audio output device to the provided media element using the deviceId.
function attachSinkId(element, sinkId, outputSelector) {
if (typeof element.sinkId !== 'undefined') {
element.setSinkId(sinkId)
.then(() => {
console.log(`Success, audio output device attached: ${sinkId} to element with ${element.title} as source.`);
})
.catch(error => {
let errorMessage = error;
if (error.name === 'SecurityError') {
errorMessage = `You need to use HTTPS for selecting audio output device: ${error}`;
}
console.error(errorMessage);
// Jump back to first output device in the list as it's the default.
outputSelector.selectedIndex = 0;
});
} else {
console.warn('Browser does not support output device selection.');
}
}
function changeAudioDestination(event) {
const deviceId = event.target.value;
const outputSelector = event.target;
// FIXME: Make the media element lookup dynamic.
const element = event.composedPath()[2].childNodes[1];
attachSinkId(element, deviceId, outputSelector);
}
function gotStream(stream) {
window.stream = stream; // make stream available to console
gumAudio.srcObject = stream;
gumVideo.srcObject = stream;
}
function start() {
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
const constraints = {
audio: true,
video: true
};
navigator.mediaDevices.getUserMedia(constraints).then(gotStream).catch(handleError);
}
start();
function handleError(error) {
console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
}
================================================
FILE: src/content/extensions/multipleroutes/src/README.md
================================================
## Chrome WebRTC Network Limiter
Configures the WebRTC traffic routing options in Chrome's privacy settings.
★ What it does:
This configures WebRTC to not use certain IP addresses or protocols:
- private IP addresses not visible to the public internet (e.g. addresses like 192.168.1.2)
- 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)
- 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.
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.
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.
★ Notes:
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.
By installing this item, you agree to the Google Terms of Service and Privacy Policy at https://www.google.com/intl/en/policies/.
================================================
FILE: src/content/extensions/multipleroutes/src/_locales/en/messages.json
================================================
{
"NETLI_DEFAULT_RADIO": {
"message": "<strong>Give me the best media experience:</strong> 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."
},
"NETLI_DEFAULT_PUBLIC_AND_PRIVATE_INTERFACES_RADIO": {
"message": "<strong>Use my default public and private IP addresses:</strong> 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."
},
"NETLI_DEFAULT_PUBLIC_INTERFACE_ONLY_RADIO": {
"message": "<strong>Use only my default public IP address:</strong> This option is the same as <em>Use my default public and private IP addresses</em> except that Chrome will not use the private default address."
},
"NETLI_DISABLE_NON_PROXIED_UDP_RADIO": {
"message": "<strong>Use my proxy server (if present):</strong> 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."
},
"NETLI_OPTION_NOT_SUPPORTED": {
"message": "Grayed out options require newer verion of Chrome."
},
"NETLI_APPDESC": {
"message": "Configures how WebRTC's network traffic is routed by changing Chrome's privacy settings."
},
"NETLI_APPNAME": {
"message": "WebRTC Network Limiter"
},
"NETLI_OPTIONS": {
"message": "WebRTC Network Limiter Options"
}
}
================================================
FILE: src/content/extensions/multipleroutes/src/manifest.json
================================================
{
"default_locale": "en",
"description": "__MSG_NETLI_APPDESC__",
"icons": {
"16": "img/icon_16.png",
"128": "img/icon_128.png"
},
"manifest_version": 3,
"minimum_chrome_version": "42.0.2311.135",
"name": "__MSG_NETLI_APPNAME__",
"options_ui": {
"open_in_tab": false,
"page": "options.html"
},
"permissions": [ "privacy" ],
"version": "0.2.1.4"
}
================================================
FILE: src/content/extensions/multipleroutes/src/options.html
================================================
<!doctype html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<body>
<p id="Mode0">
<input type="radio" name="ip_policy_selection" id="default" value="default">
<label for="default">
<span i18n-content="netli_default_radio"></span>
</label>
</p>
<p id="Mode1">
<input type="radio" name="ip_policy_selection"
id="default_public_and_private_interfaces"
value="default_public_and_private_interfaces">
<label for="default_public_and_private_interfaces">
<span i18n-content="netli_default_public_and_private_interfaces_radio"></span>
</label>
</p>
<p id="Mode2">
<input type="radio" name="ip_policy_selection"
id="default_public_interface_only"
value="default_public_interface_only">
<label for="default_public_interface_only">
<span i18n-content="netli_default_public_interface_only_radio"></span>
</label>
</p>
<p id="Mode3">
<input type="radio" name="ip_policy_selection"
id="disable_non_proxied_udp" value="disable_non_proxied_udp">
<label for="disable_non_proxied_udp" id="for_disable_non_proxied_udp">
<span i18n-content="netli_disable_non_proxied_udp_radio"></span>
</label>
</p>
<p>
<label id="not_supported">
<span i18n-content="netli_option_not_supported"></span>
</label>
</p>
<script src="utils.js"></script>
<script src="options.js"></script>
</body>
</html>
================================================
FILE: src/content/extensions/multipleroutes/src/options.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const pn = chrome.privacy.network;
const pi = chrome.privacy.IPHandlingPolicy;
const mapPolicyToRadioId = {};
mapPolicyToRadioId[pi.DEFAULT] = 0;
mapPolicyToRadioId[pi.DEFAULT_PUBLIC_AND_PRIVATE_INTERFACES] = 1;
mapPolicyToRadioId[pi.DEFAULT_PUBLIC_INTERFACE_ONLY] = 2;
mapPolicyToRadioId[pi.DISABLE_NON_PROXIED_UDP] = 3;
const mapRadioIdToPolicy = {};
mapRadioIdToPolicy[0] = pi.DEFAULT;
mapRadioIdToPolicy[1] = pi.DEFAULT_PUBLIC_AND_PRIVATE_INTERFACES;
mapRadioIdToPolicy[2] = pi.DEFAULT_PUBLIC_INTERFACE_ONLY;
mapRadioIdToPolicy[3] = pi.DISABLE_NON_PROXIED_UDP;
// Saves options.
function saveOptions() {
const radios = document.getElementsByName('ip_policy_selection');
let i;
for (i = 0; i < radios.length; i++) {
if (radios[i].checked) {
break;
}
}
pn.webRTCIPHandlingPolicy.set({
value: mapRadioIdToPolicy[i]
});
}
function restoreRadios(policy) {
const radios = document.getElementsByName('ip_policy_selection');
radios[mapPolicyToRadioId[policy]].checked = true;
}
function restoreOption() {
pn.webRTCIPHandlingPolicy.get({}, function(details) {
restoreRadios(details.value);
});
}
document.addEventListener('DOMContentLoaded', restoreOption);
document.getElementById('default').
addEventListener('click', saveOptions);
document.getElementById('default_public_and_private_interfaces').
addEventListener('click', saveOptions);
document.getElementById('default_public_interface_only').
addEventListener('click', saveOptions);
document.getElementById('disable_non_proxied_udp').
addEventListener('click', saveOptions);
document.title = chrome.i18n.getMessage('netli_options');
const i18nElements = document.querySelectorAll('*[i18n-content]');
for (let i = 0; i < i18nElements.length; i++) {
const elem = i18nElements[i];
const msg = elem.getAttribute('i18n-content');
elem.innerHTML = chrome.i18n.getMessage(msg);
}
function browserSupportsIPHandlingPolicy() {
return pn.webRTCIPHandlingPolicy !== undefined;
}
if (browserSupportsIPHandlingPolicy()) {
// Hide the 'not supported' banner.
document.getElementById('not_supported').innerHTML = '';
} else {
// Disable all options.
for (let i = 0; i < 4; i++) {
const key = 'Mode' + i;
const section = document.getElementById(key);
section.style.color = 'gray';
section.querySelector('input').disabled = true;
}
}
================================================
FILE: src/content/extensions/svc/css/main.css
================================================
/*
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
button {
margin: 0 20px 0 0;
width: 83px;
}
button#hangupButton {
margin: 0;
}
video {
--width: 45%;
width: var(--width);
height: calc(var(--width) * 0.75);
margin: 0 0 20px 0;
vertical-align: top;
}
video#localVideo {
margin: 0 20px 20px 0;
}
div.box {
margin: 1em;
}
@media screen and (max-width: 400px) {
button {
width: 83px;
margin: 0 11px 10px 0;
}
video {
height: 90px;
margin: 0 0 10px 0;
width: calc(50% - 7px);
}
video#localVideo {
margin: 0 10px 20px 0;
}
}
div.graph-container {
float: left;
margin: 0.5em;
width: calc(50% - 1em);
}
================================================
FILE: src/content/extensions/svc/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Scalable Video Coding (SVC) Extension</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css"/>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a>
<span>Peer connection</span></h1>
<p>This sample shows how to setup a connection between two peers using
<a href="https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection">RTCPeerConnection</a> and
choose the preferred video codec to use and scalability mode when the <a href="https://www.w3.org/TR/webrtc-svc/">Scalable Video Coding (SVC) Extension</a> is available.
</p>
<video id="localVideo" playsinline autoplay muted></video>
<video id="remoteVideo" playsinline autoplay></video>
<div class="box">
<button id="startButton">Start</button>
<button id="callButton">Call</button>
<button id="hangupButton">Hang Up</button>
</div>
<div class="box">
<span>Codec preferences:</span>
<select id="codecPreferences" disabled>
<option selected value="">Default</option>
</select>
<br/>
<span>Scalability Mode:</span>
<select id="scalabilityMode" disabled>
<option selected value="">NONE</option>
</select>
<div id="actualCodec"></div>
</div>
<div class="graph-container" id="bitrateGraph">
<div>Bitrate</div>
<canvas id="bitrateCanvas"></canvas>
</div>
<div class="graph-container" id="packetGraph">
<div>Packets sent per second</div>
<canvas id="packetCanvas"></canvas>
</div>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/extensions/svc"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/third_party/graph.js"></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/extensions/svc/js/main.js
================================================
/*
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* global TimelineDataSeries, TimelineGraphView */
'use strict';
const startButton = document.getElementById('startButton');
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');
callButton.disabled = true;
hangupButton.disabled = true;
startButton.addEventListener('click', start);
callButton.addEventListener('click', call);
hangupButton.addEventListener('click', hangup);
let startTime;
const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
localVideo.addEventListener('loadedmetadata', function() {
console.log(`Local video videoWidth: ${this.videoWidth}px, videoHeight: ${this.videoHeight}px`);
});
remoteVideo.addEventListener('loadedmetadata', function() {
console.log(`Remote video videoWidth: ${this.videoWidth}px, videoHeight: ${this.videoHeight}px`);
});
remoteVideo.addEventListener('resize', () => {
console.log(`Remote video size changed to ${remoteVideo.videoWidth}x${remoteVideo.videoHeight}`);
// We'll use the first onsize callback as an indication that video has started
// playing out.
if (startTime) {
const elapsedTime = window.performance.now() - startTime;
console.log('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
startTime = null;
}
});
const codecPreferences = document.querySelector('#codecPreferences');
const scalabilityMode = document.querySelector('#scalabilityMode');
const supportsSetCodecPreferences = window.RTCRtpTransceiver &&
'setCodecPreferences' in window.RTCRtpTransceiver.prototype;
const scalabilityModes = [
'L1T1',
'L1T2',
'L1T3',
'L2T1',
'L2T2',
'L2T3',
'L3T1',
'L3T2',
'L3T3',
'L2T1h',
'L2T2h',
'L2T3h',
'S2T1',
'S2T2',
'S2T3',
'S2T1h',
'S2T2h',
'S2T3h',
'S3T1',
'S3T2',
'S3T3',
'S3T1h',
'S3T2h',
'S3T3h',
'L2T2_KEY',
'L2T3_KEY',
'L3T2_KEY',
'L3T3_KEY'
];
let localStream;
let pc1;
let pc2;
let bitrateGraph;
let bitrateSeries;
let headerrateSeries;
let packetGraph;
let packetSeries;
let lastResult;
const offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
function getName(pc) {
return (pc === pc1) ? 'pc1' : 'pc2';
}
function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}
async function start() {
console.log('Requesting local stream');
startButton.disabled = true;
try {
const stream = await navigator.mediaDevices.getUserMedia({audio: false, video: true});
console.log('Received local stream');
localVideo.srcObject = stream;
localStream = stream;
callButton.disabled = false;
} catch (e) {
alert(`getUserMedia() error: ${e.name}`);
}
if (supportsSetCodecPreferences) {
const {codecs} = RTCRtpReceiver.getCapabilities('video');
codecs.forEach(codec => {
if (['video/red', 'video/ulpfec', 'video/rtx', 'video/flexfec-03'].includes(codec.mimeType)) {
return;
}
const option = document.createElement('option');
option.value = (codec.mimeType + ' ' + (codec.sdpFmtpLine || '')).trim();
option.innerText = option.value;
codecPreferences.appendChild(option);
});
codecPreferences.addEventListener('change', async (event) => {
const [mimeType] = event.target.value.split(' ');
while (scalabilityMode.firstChild) {
scalabilityMode.firstChild.remove();
}
const option = document.createElement('option');
option.value = '';
option.innerText = 'NONE';
scalabilityMode.appendChild(option);
const capabilityPromises = [];
for (const mode of scalabilityModes) {
capabilityPromises.push(navigator.mediaCapabilities.encodingInfo({
type: 'webrtc',
video: {
contentType: mimeType,
width: 640,
height: 480,
bitrate: 10000,
framerate: 29.97,
scalabilityMode: mode
}}));
}
const capabilityResults = await Promise.all(capabilityPromises);
for (let i = 0; i < scalabilityModes.length; ++i) {
if (capabilityResults[i].supported) {
const option = document.createElement('option');
option.value = scalabilityModes[i];
option.innerText = scalabilityModes[i];
scalabilityMode.appendChild(option);
}
}
if (scalabilityMode.childElementCount > 1) {
scalabilityMode.disabled = false;
} else {
scalabilityMode.disabled = true;
}
});
codecPreferences.disabled = false;
}
bitrateSeries = new TimelineDataSeries();
bitrateGraph = new TimelineGraphView('bitrateGraph', 'bitrateCanvas');
bitrateGraph.updateEndDate();
headerrateSeries = new TimelineDataSeries();
headerrateSeries.setColor('green');
packetSeries = new TimelineDataSeries();
packetGraph = new TimelineGraphView('packetGraph', 'packetCanvas');
packetGraph.updateEndDate();
}
async function call() {
callButton.disabled = true;
hangupButton.disabled = false;
console.log('Starting call');
startTime = window.performance.now();
const videoTracks = localStream.getVideoTracks();
const audioTracks = localStream.getAudioTracks();
if (videoTracks.length > 0) {
console.log(`Using video device: ${videoTracks[0].label}`);
}
if (audioTracks.length > 0) {
console.log(`Using audio device: ${audioTracks[0].label}`);
}
const configuration = {};
console.log('RTCPeerConnection configuration:', configuration);
pc1 = new RTCPeerConnection(configuration);
console.log('Created local peer connection object pc1');
pc1.addEventListener('icecandidate', e => onIceCandidate(pc1, e));
pc2 = new RTCPeerConnection(configuration);
console.log('Created remote peer connection object pc2');
pc2.addEventListener('icecandidate', e => onIceCandidate(pc2, e));
pc2.addEventListener('track', gotRemoteStream);
const mode = scalabilityMode.value;
localStream.getTracks().forEach((track) =>{
if (track.kind == 'video' && mode) {
pc1.addTransceiver(track, {
streams: [localStream],
sendEncodings: [
{scalabilityMode: mode}
]
});
} else {
pc1.addTrack(track, localStream);
}
});
console.log('Added local stream to pc1');
codecPreferences.disabled = true;
scalabilityMode.disabled = true;
try {
console.log('pc1 createOffer start');
const offer = await pc1.createOffer(offerOptions);
await onCreateOfferSuccess(offer);
} catch (e) {
onCreateSessionDescriptionError(e);
}
}
function onCreateSessionDescriptionError(error) {
console.log(`Failed to create session description: ${error.toString()}`);
}
async function onCreateOfferSuccess(desc) {
console.log(`Offer from pc1\n${desc.sdp}`);
console.log('pc1 setLocalDescription start');
try {
await pc1.setLocalDescription(desc);
onSetLocalSuccess(pc1);
} catch (e) {
onSetSessionDescriptionError();
}
console.log('pc2 setRemoteDescription start');
try {
await pc2.setRemoteDescription(desc);
onSetRemoteSuccess(pc2);
} catch (e) {
onSetSessionDescriptionError();
}
console.log('pc2 createAnswer start');
// Since the 'remote' side has no media stream we need
// to pass in the right constraints in order for it to
// accept the incoming offer of audio and video.
try {
const answer = await pc2.createAnswer();
await onCreateAnswerSuccess(answer);
} catch (e) {
onCreateSessionDescriptionError(e);
}
}
function onSetLocalSuccess(pc) {
console.log(`${getName(pc)} setLocalDescription complete`);
}
function onSetRemoteSuccess(pc) {
console.log(`${getName(pc)} setRemoteDescription complete`);
}
function onSetSessionDescriptionError(error) {
console.log(`Failed to set session description: ${error.toString()}`);
}
function gotRemoteStream(e) {
if (remoteVideo.srcObject !== e.streams[0]) {
remoteVideo.srcObject = e.streams[0];
console.log('pc2 received remote stream');
}
if (supportsSetCodecPreferences) {
const preferredCodec = codecPreferences.options[codecPreferences.selectedIndex];
if (preferredCodec.value !== '') {
const [mimeType, sdpFmtpLine] = preferredCodec.value.split(' ');
const {codecs} = RTCRtpReceiver.getCapabilities('video');
const selectedCodecIndex = codecs.findIndex(c => c.mimeType === mimeType && c.sdpFmtpLine === sdpFmtpLine);
const selectedCodec = codecs[selectedCodecIndex];
codecs.splice(selectedCodecIndex, 1);
codecs.unshift(selectedCodec);
e.transceiver.setCodecPreferences(codecs);
console.log('Preferred video codec', selectedCodec);
}
}
}
async function onCreateAnswerSuccess(desc) {
console.log(`Answer from pc2:\n${desc.sdp}`);
console.log('pc2 setLocalDescription start');
try {
await pc2.setLocalDescription(desc);
onSetLocalSuccess(pc2);
} catch (e) {
onSetSessionDescriptionError(e);
}
console.log('pc1 setRemoteDescription start');
try {
await pc1.setRemoteDescription(desc);
onSetRemoteSuccess(pc1);
// Display the video codec that is actually used.
setTimeout(async () => {
const stats = await pc1.getStats();
stats.forEach(stat => {
if (!(stat.type === 'outbound-rtp' && stat.kind === 'video')) {
return;
}
const codec = stats.get(stat.codecId);
document.getElementById('actualCodec').innerText = 'Using ' + codec.mimeType +
' ' + (codec.sdpFmtpLine ? codec.sdpFmtpLine + ' ' : '') +
', payloadType=' + codec.payloadType + '.';
});
}, 1000);
} catch (e) {
onSetSessionDescriptionError(e);
}
}
async function onIceCandidate(pc, event) {
try {
await (getOtherPc(pc).addIceCandidate(event.candidate));
onAddIceCandidateSuccess(pc);
} catch (e) {
onAddIceCandidateError(pc, e);
}
console.log(`${getName(pc)} ICE candidate:\n${event.candidate ? event.candidate.candidate : '(null)'}`);
}
function onAddIceCandidateSuccess(pc) {
console.log(`${getName(pc)} addIceCandidate success`);
}
function onAddIceCandidateError(pc, error) {
console.log(`${getName(pc)} failed to add ICE Candidate: ${error.toString()}`);
}
function hangup() {
console.log('Ending call');
pc1.close();
pc2.close();
pc1 = null;
pc2 = null;
hangupButton.disabled = true;
callButton.disabled = false;
codecPreferences.disabled = false;
scalabilityMode.disabled = false;
}
// query getStats every second
window.setInterval(() => {
if (!pc1) {
return;
}
const sender = pc1.getSenders()[0];
if (!sender) {
return;
}
sender.getStats().then(res => {
res.forEach(report => {
let bytes;
let headerBytes;
let packets;
if (report.type === 'outbound-rtp') {
if (report.isRemote) {
return;
}
const now = report.timestamp;
bytes = report.bytesSent;
headerBytes = report.headerBytesSent;
packets = report.packetsSent;
if (lastResult && lastResult.has(report.id)) {
// calculate bitrate
const bitrate = 8 * (bytes - lastResult.get(report.id).bytesSent) /
(now - lastResult.get(report.id).timestamp);
const headerrate = 8 * (headerBytes - lastResult.get(report.id).headerBytesSent) /
(now - lastResult.get(report.id).timestamp);
// append to chart
bitrateSeries.addPoint(now, bitrate);
headerrateSeries.addPoint(now, headerrate);
bitrateGraph.setDataSeries([bitrateSeries, headerrateSeries]);
bitrateGraph.updateEndDate();
// calculate number of packets and append to chart
packetSeries.addPoint(now, packets -
lastResult.get(report.id).packetsSent);
packetGraph.setDataSeries([packetSeries]);
packetGraph.updateEndDate();
}
}
});
lastResult = res;
});
}, 1000);
================================================
FILE: src/content/getusermedia/audio/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>gUM audio</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>getUserMedia, audio only</span>
</h1>
<audio id="gum-local" controls autoplay></audio>
<p class="warning">Warning: if you're not using headphones, pressing play will cause feedback.</p>
<p>Render the audio stream from an audio-only <code>getUserMedia()</code> call with an audio element.</p>
<p>The <code>MediaStream</code> object <code><em>stream</em></code> passed to the <code>getUserMedia()</code>
callback is in global scope, so you can inspect it from the console.</p>
<div>
<span id="errorMsg"></span>
</div>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/audio"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/getusermedia/audio/js/main.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
// Put variables in global scope to make them available to the browser console.
const audio = document.querySelector('audio');
const constraints = window.constraints = {
audio: true,
video: false
};
function handleSuccess(stream) {
const audioTracks = stream.getAudioTracks();
console.log('Got stream with constraints:', constraints);
console.log('Using audio device: ' + audioTracks[0].label);
stream.oninactive = function() {
console.log('Stream ended');
};
window.stream = stream; // make variable available to browser console
audio.srcObject = stream;
}
function handleError(error) {
const errorMessage = 'navigator.MediaDevices.getUserMedia error: ' + error.message + ' ' + error.name;
document.getElementById('errorMsg').innerText = errorMessage;
console.log(errorMessage);
}
navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
================================================
FILE: src/content/getusermedia/canvas/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>getUserMedia to canvas</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>getUserMedia ⇒ canvas</span>
</h1>
<video playsinline autoplay></video>
<button>Take snapshot</button>
<canvas></canvas>
<p>Draw a frame from the video onto the canvas element using the <code>drawImage()</code> method.</p>
<p>The variables <code>canvas</code>, <code>video</code> and <code>stream</code> are in global scope, so you can
inspect them from the console.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/canvas"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/getusermedia/canvas/js/main.js
================================================
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
// Put variables in global scope to make them available to the browser console.
const video = document.querySelector('video');
const canvas = window.canvas = document.querySelector('canvas');
canvas.width = 480;
canvas.height = 360;
const button = document.querySelector('button');
button.onclick = function() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
};
const constraints = {
audio: false,
video: true
};
function handleSuccess(stream) {
window.stream = stream; // make stream available to browser console
video.srcObject = stream;
}
function handleError(error) {
console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
}
navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
================================================
FILE: src/content/getusermedia/exposure/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>Control Exposure</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<style>
div.label {
display: inline-block;
font-weight: 400;
margin: 0 0.5em 0 0;
width: 10em;
}
</style>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a>
<span>Control Exposure</span></h1>
<video id="gum-local" autoplay playsinline></video>
<button id="showVideo">Open camera</button>
<div>
<div class="label">Exposure Mode:</div>
<select name="exposureMode" id="exposureMode" disabled>
</select>
</div>
<div>
<div class="label">Exposure Time:</div>
<input name="exposureTime" type="range" disabled>
</div>
<div>
<div class="label">Exposure Compensation:</div>
<input name="exposureCompensation" type="range" disabled>
</div>
<div>
<div class="label">Brightness:</div>
<input name="brightness" type="range" disabled>
</div>
<div>
<div class="label">White Balance Mode:</div>
<select name="whiteBalanceMode" id="whiteBalanceMode" disabled>
</select>
</div>
<button id="refreshControls" onClick="loadProperties(true)" disabled>Refresh Controls</button>
<div id="errorMsg"></div>
<p>Display the video stream from <code>getUserMedia()</code> in a video
element and control exposureMode, exposureTime and exposureCompensation if camera supports it.</p>
<p>The <code>MediaStreamTrack</code> object <code>track</code> is in
global scope, so you can inspect it from the console.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/exposure"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
================================================
FILE: src/content/getusermedia/exposure/js/main.js
================================================
/*
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
// Put variables in global scope to make them available to the browser console.
const constraints = window.constraints = {
audio: false,
video: true
};
function handleSuccess(stream) {
const video = document.querySelector('video');
const videoTracks = stream.getVideoTracks();
console.log('Got stream with constraints:', constraints);
console.log(`Using video device: ${videoTracks[0].label}`);
video.srcObject = stream;
// make track variable available to browser console.
[window.track] = stream.getVideoTracks();
loadProperties();
document.querySelector(`button[id=refreshControls]`).disabled = false;
}
function loadProperties(refreshValuesOnly) {
const track = window.track;
const capabilities = track.getCapabilities();
const settings = track.getSettings();
console.log('Capabilities: ', capabilities);
console.log('Settings: ', settings);
for (const property of ['exposureMode', 'exposureTime', 'exposureCompensation', 'brightness', 'whiteBalanceMode']) {
// Check whether camera supports exposure.
if (!(property in settings)) {
errorMsg(`Camera does not support ${property}.`);
continue;
}
let element;
if (Array.isArray(capabilities[property])) {
// Map it to a select element.
const select = document.querySelector(`select[name=${property}]`);
element = select;
if (capabilities[property] && !refreshValuesOnly) {
for (const mode of capabilities[property]) {
select.insertAdjacentHTML('afterbegin', `<option value="${mode}">${mode}</option>`);
}
}
} else {
// Map it to a slider element.
const input = document.querySelector(`input[name=${property}]`);
element = input;
input.min = capabilities[property].min;
input.max = capabilities[property].max;
input.step = capabilities[property].step;
}
element.value = settings[property];
element.disabled = false;
if (!refreshValuesOnly) {
element.oninput = async event => {
try {
const constraints = {advanced: [{[property]: element.value}]};
await track.applyConstraints(constraints);
console.log('Did successfully apply new constraints: ', constraints);
console.log('New camera settings: ', track.getSettings());
} catch (err) {
console.error('applyConstraints() failed: ', err);
}
};
}
}
}
function handleError(error) {
if (error.name === 'NotAllowedError') {
errorMsg('Permissions have not been granted to use your camera, ' +
'you need to allow the page access to your devices in ' +
'order for the demo to work.');
}
errorMsg(`getUserMedia error: ${error.name}`, error);
}
function errorMsg(msg, error) {
const errorElement = document.querySelector('#errorMsg');
errorElement.innerHTML += `<p>${msg}</p>`;
if (typeof error !== 'undefined') {
console.error(error);
}
}
async function init(e) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
handleSuccess(stream);
e.target.disabled = true;
} catch (e) {
handleError(e);
}
}
document.querySelector('#showVideo').addEventListener('click', e => init(e));
================================================
FILE: src/content/getusermedia/filter/index.html
================================================
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">
<base target="_blank">
<title>getUserMedia + CSS filters</title>
<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css">
<style>
.none {
-webkit-filter: none;
filter: none;
}
.blur {
-webkit-filter: blur(3px);
filter: blur(3px);
}
.grayscale {
-webkit-filter: grayscale(1);
filter: grayscale(1);
}
.invert {
-webkit-filter: invert(1);
filter: invert(1);
}
.sepia {
-webkit-filter: sepia(1);
filter: sepia(1);
}
button#snapshot {
margin: 0 10px 25px 0;
width: 110px;
}
video {
object-fit: cover;
}
</style>
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>getUserMedia + CSS filters</span>
</h1>
<video playsinline autoplay></video>
<label for="filter">Filter: </label>
<select id="filter">
<option value="none">None</option>
<option value="blur">Blur</option>
<option value="grayscale">Grayscale</option>
<option value="invert">Invert</option>
<option value="sepia">Sepia</option>
</select>
<button id="snapshot">Take snapshot</button>
<canvas></canvas>
<p>Draw a frame from the getUserMedia video stream onto the canvas element, then apply CSS filters.</p>
<p>The variables <code>canvas</code>, <code>video</code> and <code>stream</code> are in global scope, so you can
inspect them from the console.</p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/filter"
title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js" async></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
==========================
gitextract_t5d8f4yo/
├── .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
├── 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/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ ├── js/
│ │ │ │ └── main.js
│ │ │ └── video/
│ │ │ ├── chrome.ogv
│ │ │ └── chrome.webm
│ │ ├── extensions/
│ │ │ ├── multipleroutes/
│ │ │ │ └── src/
│ │ │ │ ├── README.md
│ │ │ │ ├── _locales/
│ │ │ │ │ └── en/
│ │ │ │ │ └── messages.json
│ │ │ │ ├── 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
│ │ │ └── volume-meter-processor.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/
│ │ │ │ └── index.html
│ │ │ ├── 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
│ │ │ └── webgpu/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ ├── main.js
│ │ │ ├── multi_video_main.js
│ │ │ ├── multi_video_worker.js
│ │ │ └── multi_video_worker_manager.js
│ │ └── peerconnection/
│ │ ├── always-negotiate-datachannels/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ └── main.js
│ │ ├── 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/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ ├── main.js
│ │ │ └── test.js
│ │ ├── multiple-relay/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ └── main.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/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ ├── index.html
│ │ │ └── js/
│ │ │ ├── main.js
│ │ │ └── webaudioextended.js
│ │ └── webaudio-output/
│ │ ├── css/
│ │ │ └── main.css
│ │ ├── index.html
│ │ └── js/
│ │ └── main.js
│ ├── css/
│ │ └── main.css
│ ├── js/
│ │ ├── lib/
│ │ │ └── ga.js
│ │ ├── third_party/
│ │ │ ├── graph.js
│ │ │ ├── streamvisualizer.js
│ │ │ └── webgl_teapot/
│ │ │ ├── cameracontroller.js
│ │ │ ├── demo.js
│ │ │ ├── matrix4x4.js
│ │ │ ├── teapot-streams.js
│ │ │ ├── webgl-debug.js
│ │ │ └── webgl-utils.js
│ │ └── videopipe.js
│ └── video/
│ ├── chrome.webm
│ └── mixed-content.webm
└── test/
├── download-browsers.js
├── interop/
│ └── connection.test.js
├── steps.js
├── webdriver.js
└── webrtcclient.js
SYMBOL INDEX (718 symbols across 87 files)
FILE: src/content/capture/canvas-filter/js/main.js
function loop (line 23) | function loop() {
FILE: src/content/capture/canvas-pc/js/main.js
function call (line 45) | function call() {
function onCreateSessionDescriptionError (line 81) | function onCreateSessionDescriptionError(error) {
function onCreateOfferSuccess (line 85) | function onCreateOfferSuccess(desc) {
function onSetLocalSuccess (line 98) | function onSetLocalSuccess(pc) {
function onSetRemoteSuccess (line 102) | function onSetRemoteSuccess(pc) {
function onSetSessionDescriptionError (line 106) | function onSetSessionDescriptionError(error) {
function gotRemoteStream (line 110) | function gotRemoteStream(e) {
function onCreateAnswerSuccess (line 120) | function onCreateAnswerSuccess(desc) {
function onIceCandidate (line 128) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 137) | function onAddIceCandidateSuccess(pc) {
function onAddIceCandidateError (line 141) | function onAddIceCandidateError(pc, error) {
function onIceStateChange (line 145) | function onIceStateChange(pc, event) {
function getName (line 152) | function getName(pc) {
function getOtherPc (line 156) | function getOtherPc(pc) {
FILE: src/content/capture/canvas-record/js/main.js
function handleSourceOpen (line 40) | function handleSourceOpen(event) {
function handleDataAvailable (line 46) | function handleDataAvailable(event) {
function handleStop (line 52) | function handleStop(event) {
function toggleRecording (line 58) | function toggleRecording() {
function startRecording (line 70) | function startRecording() {
function stopRecording (line 104) | function stopRecording() {
function play (line 110) | function play() {
function download (line 114) | function download() {
FILE: src/content/capture/video-contenthint/js/main.js
function maybeCreateStream (line 24) | function maybeCreateStream() {
function setVideoTrackContentHints (line 46) | function setVideoTrackContentHints(stream, hint) {
function call (line 60) | function call() {
function establishPC (line 77) | function establishPC(videoTag, stream) {
function onSetSessionDescriptionError (line 105) | function onSetSessionDescriptionError(error) {
function onCreateAnswerSuccess (line 109) | function onCreateAnswerSuccess(pc1, pc2, desc) {
function onIceCandidate (line 117) | function onIceCandidate(pc, otherPc, event) {
FILE: src/content/capture/video-pc/js/main.js
function maybeCreateStream (line 25) | function maybeCreateStream() {
function call (line 69) | function call() {
function onCreateSessionDescriptionError (line 98) | function onCreateSessionDescriptionError(error) {
function onCreateOfferSuccess (line 102) | function onCreateOfferSuccess(desc) {
function onSetLocalSuccess (line 116) | function onSetLocalSuccess(pc) {
function onSetRemoteSuccess (line 120) | function onSetRemoteSuccess(pc) {
function onSetSessionDescriptionError (line 124) | function onSetSessionDescriptionError(error) {
function gotRemoteStream (line 128) | function gotRemoteStream(event) {
function onCreateAnswerSuccess (line 135) | function onCreateAnswerSuccess(desc) {
function onIceCandidate (line 144) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 155) | function onAddIceCandidateSuccess(pc) {
function onAddIceCandidateError (line 159) | function onAddIceCandidateError(pc, error) {
function onIceStateChange (line 163) | function onIceStateChange(pc, event) {
function getName (line 170) | function getName(pc) {
function getOtherPc (line 174) | function getOtherPc(pc) {
FILE: src/content/capture/worker-process/js/main.js
function loop (line 26) | function loop() {
FILE: src/content/datachannel/basic/js/main.js
function enableStartButton (line 25) | function enableStartButton() {
function disableSendButton (line 29) | function disableSendButton() {
function createConnection (line 33) | function createConnection() {
function onCreateSessionDescriptionError (line 64) | function onCreateSessionDescriptionError(error) {
function sendData (line 68) | function sendData() {
function closeDataChannels (line 74) | function closeDataChannels() {
function gotDescription1 (line 95) | function gotDescription1(desc) {
function gotDescription2 (line 105) | function gotDescription2(desc) {
function getOtherPc (line 111) | function getOtherPc(pc) {
function getName (line 115) | function getName(pc) {
function onIceCandidate (line 119) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 129) | function onAddIceCandidateSuccess() {
function onAddIceCandidateError (line 133) | function onAddIceCandidateError(error) {
function receiveChannelCallback (line 137) | function receiveChannelCallback(event) {
function onReceiveMessageCallback (line 145) | function onReceiveMessageCallback(event) {
function onSendChannelStateChange (line 150) | function onSendChannelStateChange() {
function onReceiveChannelStateChange (line 165) | function onReceiveChannelStateChange() {
FILE: src/content/datachannel/channel/js/main.js
function hangup (line 75) | async function hangup() {
function createPeerConnection (line 91) | function createPeerConnection() {
function handleOffer (line 107) | async function handleOffer(offer) {
function handleAnswer (line 121) | async function handleAnswer(answer) {
function handleCandidate (line 129) | async function handleCandidate(candidate) {
function sendData (line 141) | function sendData() {
function receiveChannelCallback (line 151) | function receiveChannelCallback(event) {
function onReceiveChannelMessageCallback (line 159) | function onReceiveChannelMessageCallback(event) {
function onSendChannelMessageCallback (line 164) | function onSendChannelMessageCallback(event) {
function onSendChannelStateChange (line 169) | function onSendChannelStateChange() {
function onReceiveChannelStateChange (line 184) | function onReceiveChannelStateChange() {
FILE: src/content/datachannel/datatransfer/js/main.js
constant MAX_CHUNK_SIZE (line 10) | const MAX_CHUNK_SIZE = 262144;
function createConnection (line 55) | async function createConnection() {
function sendData (line 94) | function sendData() {
function startSendingData (line 132) | function startSendingData() {
function maybeReset (line 145) | function maybeReset() {
function handleLocalDescription (line 152) | async function handleLocalDescription(desc) {
function handleRemoteAnswer (line 164) | function handleRemoteAnswer(desc) {
function getOtherPc (line 170) | function getOtherPc(pc) {
function onIceCandidate (line 174) | async function onIceCandidate(pc, event) {
function receiveChannelCallback (line 187) | function receiveChannelCallback(event) {
function onReceiveMessageCallback (line 195) | function onReceiveMessageCallback(event) {
function onSendChannelOpen (line 209) | function onSendChannelOpen() {
function onSendChannelClosed (line 228) | function onSendChannelClosed() {
function onReceiveChannelClosed (line 242) | function onReceiveChannelClosed() {
FILE: src/content/datachannel/filetransfer/js/main.js
function handleFileInputChange (line 43) | async function handleFileInputChange() {
function createConnection (line 52) | async function createConnection() {
function sendData (line 90) | function sendData() {
function closeDataChannels (line 127) | function closeDataChannels() {
function gotLocalDescription (line 149) | async function gotLocalDescription(desc) {
function gotRemoteDescription (line 161) | async function gotRemoteDescription(desc) {
function receiveChannelCallback (line 167) | function receiveChannelCallback(event) {
function onReceiveMessageCallback (line 185) | function onReceiveMessageCallback(event) {
function onSendChannelStateChange (line 218) | function onSendChannelStateChange() {
function onError (line 228) | function onError(error) {
function onReceiveChannelStateChange (line 236) | async function onReceiveChannelStateChange() {
function displayStats (line 250) | async function displayStats() {
FILE: src/content/datachannel/messaging/main.js
class MessagingSample (line 13) | class MessagingSample extends LitElement {
method constructor (line 14) | constructor() {
method disconnect (line 21) | disconnect() {
method connect (line 26) | async connect() {
method _onLocalMessageReceived (line 79) | _onLocalMessageReceived(event) {
method _onRemoteDataChannel (line 84) | _onRemoteDataChannel(event) {
method _onRemoteMessageReceived (line 95) | _onRemoteMessageReceived(event) {
method properties (line 100) | static get properties() {
method render (line 108) | render() {
method _sendMessage (line 147) | _sendMessage(selector, channel) {
FILE: src/content/devices/input-output/js/main.js
function getDevices (line 24) | function getDevices() {
function gotDevices (line 28) | function gotDevices(deviceInfos) {
function attachSinkId (line 74) | function attachSinkId(element, sinkId) {
function changeAudioDestination (line 94) | function changeAudioDestination() {
function gotStream (line 99) | function gotStream(stream) {
function handleError (line 112) | function handleError(error) {
function start (line 116) | function start() {
FILE: src/content/devices/multi/js/main.js
function gotDevices (line 22) | function gotDevices(deviceInfos) {
function attachSinkId (line 51) | function attachSinkId(element, sinkId, outputSelector) {
function changeAudioDestination (line 71) | function changeAudioDestination(event) {
function gotStream (line 79) | function gotStream(stream) {
function start (line 85) | function start() {
function handleError (line 100) | function handleError(error) {
FILE: src/content/extensions/multipleroutes/src/options.js
function saveOptions (line 27) | function saveOptions() {
function restoreRadios (line 41) | function restoreRadios(policy) {
function restoreOption (line 46) | function restoreOption() {
function browserSupportsIPHandlingPolicy (line 70) | function browserSupportsIPHandlingPolicy() {
FILE: src/content/extensions/svc/js/main.js
function getName (line 97) | function getName(pc) {
function getOtherPc (line 101) | function getOtherPc(pc) {
function start (line 105) | async function start() {
function call (line 182) | async function call() {
function onCreateSessionDescriptionError (line 230) | function onCreateSessionDescriptionError(error) {
function onCreateOfferSuccess (line 234) | async function onCreateOfferSuccess(desc) {
function onSetLocalSuccess (line 264) | function onSetLocalSuccess(pc) {
function onSetRemoteSuccess (line 268) | function onSetRemoteSuccess(pc) {
function onSetSessionDescriptionError (line 272) | function onSetSessionDescriptionError(error) {
function gotRemoteStream (line 276) | function gotRemoteStream(e) {
function onCreateAnswerSuccess (line 296) | async function onCreateAnswerSuccess(desc) {
function onIceCandidate (line 328) | async function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 338) | function onAddIceCandidateSuccess(pc) {
function onAddIceCandidateError (line 342) | function onAddIceCandidateError(pc, error) {
function hangup (line 346) | function hangup() {
FILE: src/content/getusermedia/audio/js/main.js
function handleSuccess (line 19) | function handleSuccess(stream) {
function handleError (line 30) | function handleError(error) {
FILE: src/content/getusermedia/canvas/js/main.js
function handleSuccess (line 29) | function handleSuccess(stream) {
function handleError (line 34) | function handleError(error) {
FILE: src/content/getusermedia/exposure/js/main.js
function handleSuccess (line 16) | function handleSuccess(stream) {
function loadProperties (line 31) | function loadProperties(refreshValuesOnly) {
function handleError (line 82) | function handleError(error) {
function errorMsg (line 91) | function errorMsg(msg, error) {
function init (line 99) | async function init(e) {
FILE: src/content/getusermedia/filter/js/main.js
function handleSuccess (line 34) | function handleSuccess(stream) {
function handleError (line 39) | function handleError(error) {
FILE: src/content/getusermedia/getdisplaymedia/js/main.js
function handleSuccess (line 23) | function handleSuccess(stream) {
function handleError (line 38) | function handleError(error) {
function errorMsg (line 42) | function errorMsg(msg, error) {
FILE: src/content/getusermedia/gum/js/main.js
function handleSuccess (line 16) | function handleSuccess(stream) {
function handleError (line 25) | function handleError(error) {
function errorMsg (line 36) | function errorMsg(msg, error) {
function init (line 44) | async function init(e) {
FILE: src/content/getusermedia/pan-tilt-zoom/js/main.js
function handleSuccess (line 17) | function handleSuccess(stream) {
function handleError (line 54) | function handleError(error) {
function errorMsg (line 63) | function errorMsg(msg, error) {
function init (line 71) | async function init(e) {
FILE: src/content/getusermedia/record/js/main.js
function doPlay (line 36) | function doPlay(blob) {
function mimeTypeToFileExtension (line 73) | function mimeTypeToFileExtension(mimeType) {
function handleDataAvailable (line 86) | function handleDataAvailable(event) {
function getSupportedMimeTypes (line 93) | function getSupportedMimeTypes() {
function startRecording (line 119) | async function startRecording() {
function stopRecording (line 158) | function stopRecording() {
function handleSuccess (line 162) | function handleSuccess(stream) {
function init (line 179) | async function init(constraints, isGetDisplayMedia) {
function onStart (line 191) | async function onStart(isGetDisplayMedia) {
FILE: src/content/getusermedia/resolution/js/main.js
function gotDevices (line 119) | function gotDevices(deviceInfos) {
function handleError (line 135) | function handleError(error) {
function gotStream (line 141) | function gotStream(mediaStream) {
function errorMessage (line 158) | function errorMessage(who, what) {
function clearErrorMessage (line 165) | function clearErrorMessage() {
function displayVideoDimensions (line 169) | function displayVideoDimensions(whereSeen) {
function constraintChange (line 192) | function constraintChange(e) {
function getMedia (line 230) | function getMedia(constraints) {
FILE: src/content/getusermedia/volume/js/main.js
function handleSuccess (line 34) | function handleSuccess(stream) {
function handleError (line 55) | function handleError(error) {
function start (line 60) | function start() {
function stop (line 78) | function stop() {
FILE: src/content/getusermedia/volume/js/soundmeter.js
function SoundMeter (line 16) | function SoundMeter(context) {
FILE: src/content/getusermedia/volume/js/volume-meter-processor.js
class VolumeMeterProcessor (line 12) | class VolumeMeterProcessor extends AudioWorkletProcessor {
method constructor (line 13) | constructor() {
method process (line 17) | process(inputs) {
FILE: src/content/insertable-streams/audio-processing/js/main.js
function init (line 57) | async function init() {
function start (line 71) | async function start() {
function stop (line 102) | async function stop() {
FILE: src/content/insertable-streams/audio-processing/js/worker.js
function lowPassFilter (line 14) | function lowPassFilter() {
FILE: src/content/insertable-streams/endtoend-encryption/js/main.js
function gotStream (line 81) | function gotStream(stream) {
function gotRemoteStream (line 88) | function gotRemoteStream(stream) {
function start (line 94) | function start() {
function setupSenderTransform (line 112) | function setupSenderTransform(sender) {
function setupReceiverTransform (line 140) | function setupReceiverTransform(receiver) {
function maybeSetCodecPreferences (line 155) | function maybeSetCodecPreferences(trackEvent) {
function call (line 174) | function call() {
function hangup (line 201) | function hangup() {
function setCryptoKey (line 209) | function setCryptoKey(event) {
function toggleMute (line 225) | function toggleMute(event) {
FILE: src/content/insertable-streams/endtoend-encryption/js/videopipe.js
function VideoPipe (line 24) | function VideoPipe(stream, forceSend, forceReceive, handler) {
FILE: src/content/insertable-streams/endtoend-encryption/js/worker.js
function dump (line 37) | function dump(encodedFrame, direction, max = 16) {
function encodeFunction (line 55) | function encodeFunction(encodedFrame, controller) {
function decodeFunction (line 85) | function decodeFunction(encodedFrame, controller) {
function handleTransform (line 121) | function handleTransform(operation, readable, writable) {
FILE: src/content/insertable-streams/video-analyzer/js/main.js
function getName (line 70) | function getName(pc) {
function getOtherPc (line 74) | function getOtherPc(pc) {
function start (line 78) | async function start() {
function call (line 95) | async function call() {
function onCreateSessionDescriptionError (line 126) | function onCreateSessionDescriptionError(error) {
function onCreateOfferSuccess (line 130) | async function onCreateOfferSuccess(desc) {
function onSetLocalSuccess (line 157) | function onSetLocalSuccess(pc) {
function onSetRemoteSuccess (line 161) | function onSetRemoteSuccess(pc) {
function onSetSessionDescriptionError (line 165) | function onSetSessionDescriptionError(error) {
function gotRemoteTrack (line 169) | function gotRemoteTrack(e) {
function onCreateAnswerSuccess (line 179) | async function onCreateAnswerSuccess(desc) {
function onIceCandidate (line 197) | async function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 207) | function onAddIceCandidateSuccess(pc) {
function onAddIceCandidateError (line 211) | function onAddIceCandidateError(pc, error) {
function onIceStateChange (line 215) | function onIceStateChange(pc, event) {
function hangup (line 222) | function hangup() {
function videoAnalyzer (line 247) | function videoAnalyzer(encodedFrame, controller) {
FILE: src/content/insertable-streams/video-crop/js/worker.js
function transform (line 11) | function transform(frame, controller) {
FILE: src/content/insertable-streams/video-processing/js/camera-source.js
class CameraSource (line 17) | class CameraSource { // eslint-disable-line no-unused-vars
method constructor (line 18) | constructor() {
method setDebugPath (line 30) | setDebugPath(path) {
method setVisibility (line 35) | setVisibility(visible) {
method getMediaStream (line 39) | async getMediaStream() {
method destroy (line 51) | destroy() {
FILE: src/content/insertable-streams/video-processing/js/canvas-source.js
constant TEXT_SOURCE (line 11) | const TEXT_SOURCE =
constant CANVAS_ASPECT_RATIO (line 13) | const CANVAS_ASPECT_RATIO = 16 / 9;
function roundToEven (line 19) | function roundToEven(x) {
class CanvasSource (line 27) | class CanvasSource { // eslint-disable-line no-unused-vars
method constructor (line 28) | constructor() {
method setDebugPath (line 72) | setDebugPath(path) {
method setVisibility (line 76) | setVisibility(visible) {
method updateCanvasVisibility (line 83) | updateCanvasVisibility() {
method requestAnimationFrame (line 95) | requestAnimationFrame() {
method animate (line 103) | animate(now) {
method getMediaStream (line 174) | async getMediaStream() {
method destroy (line 202) | destroy() {
FILE: src/content/insertable-streams/video-processing/js/canvas-transform.js
class CanvasTransform (line 15) | class CanvasTransform { // eslint-disable-line no-unused-vars
method constructor (line 16) | constructor() {
method init (line 31) | async init() {
method transform (line 46) | async transform(frame, controller) {
method destroy (line 72) | destroy() {}
FILE: src/content/insertable-streams/video-processing/js/main.js
function createProcessedMediaStreamTrack (line 67) | function createProcessedMediaStreamTrack(sourceTrack, transform, signal) {
function initUI (line 135) | function initUI() {
FILE: src/content/insertable-streams/video-processing/js/peer-connection-pipe.js
class PeerConnectionPipe (line 20) | class PeerConnectionPipe { // eslint-disable-line no-unused-vars
method constructor (line 25) | constructor(inputStream, debugPath) {
method setDebugPath (line 48) | setDebugPath(path) {
method init_ (line 56) | async init_(inputStream) {
method getOutputStream (line 96) | getOutputStream() {
method destroy (line 101) | destroy() {
FILE: src/content/insertable-streams/video-processing/js/peer-connection-sink.js
class PeerConnectionSink (line 22) | class PeerConnectionSink { // eslint-disable-line no-unused-vars
method constructor (line 23) | constructor() {
method setMediaStream (line 40) | async setMediaStream(stream) {
method destroy (line 53) | destroy() {
FILE: src/content/insertable-streams/video-processing/js/peer-connection-source.js
class PeerConnectionSource (line 23) | class PeerConnectionSource { // eslint-disable-line no-unused-vars
method constructor (line 28) | constructor(originalSource) {
method setDebugPath (line 51) | setDebugPath(path) {
method setVisibility (line 58) | setVisibility(visible) {
method getMediaStream (line 63) | async getMediaStream() {
method destroy (line 82) | destroy() {
FILE: src/content/insertable-streams/video-processing/js/pipeline.js
function createProcessedMediaStream (line 24) | function createProcessedMediaStream(sourceStream, transform, signal) {
class MediaStreamSource (line 45) | class MediaStreamSource { // eslint-disable-line no-unused-vars
method setDebugPath (line 50) | setDebugPath(path) {}
method setVisibility (line 57) | setVisibility(visible) {}
method getMediaStream (line 62) | async getMediaStream() {}
method destroy (line 64) | destroy() {}
class FrameTransform (line 73) | class FrameTransform { // eslint-disable-line no-unused-vars
method init (line 75) | async init() {}
method transform (line 82) | async transform(frame, controller) {}
method destroy (line 84) | destroy() {}
class MediaStreamSink (line 93) | class MediaStreamSink { // eslint-disable-line no-unused-vars
method setMediaStream (line 97) | async setMediaStream(stream) {}
method destroy (line 99) | destroy() {}
class Pipeline (line 105) | class Pipeline { // eslint-disable-line no-unused-vars
method constructor (line 106) | constructor() {
method getSource (line 123) | getSource() {
method updateSource (line 131) | async updateSource(mediaStreamSource) {
method maybeStartPipeline_ (line 147) | async maybeStartPipeline_() {
method updateTransform (line 175) | async updateTransform(frameTransform) {
method updateSink (line 192) | async updateSink(mediaStreamSink) {
method destroy (line 205) | destroy() {
FILE: src/content/insertable-streams/video-processing/js/simple-transforms.js
class NullTransform (line 15) | class NullTransform { // eslint-disable-line no-unused-vars
method init (line 17) | async init() {}
method transform (line 19) | async transform(frame, controller) {
method destroy (line 23) | destroy() {}
class DropTransform (line 30) | class DropTransform { // eslint-disable-line no-unused-vars
method init (line 32) | async init() {}
method transform (line 34) | async transform(frame, controller) {
method destroy (line 42) | destroy() {}
class DelayTransform (line 49) | class DelayTransform { // eslint-disable-line no-unused-vars
method init (line 51) | async init() {}
method transform (line 53) | async transform(frame, controller) {
method destroy (line 58) | destroy() {}
FILE: src/content/insertable-streams/video-processing/js/video-mirror-helper.js
class VideoMirrorHelper (line 15) | class VideoMirrorHelper { // eslint-disable-line no-unused-vars
method constructor (line 16) | constructor() {
method setDebugPath (line 33) | setDebugPath(path) {
method setVisibility (line 41) | setVisibility(visible) {
method setStream (line 53) | setStream(stream) {
method maybeAddVideoElement_ (line 59) | maybeAddVideoElement_() {
method destroy (line 77) | destroy() {
FILE: src/content/insertable-streams/video-processing/js/video-sink.js
class VideoSink (line 15) | class VideoSink { // eslint-disable-line no-unused-vars
method constructor (line 16) | constructor() {
method setDebugPath (line 28) | setDebugPath(path) {
method setMediaStream (line 32) | async setMediaStream(stream) {
method destroy (line 47) | destroy() {
FILE: src/content/insertable-streams/video-processing/js/video-source.js
class VideoSource (line 15) | class VideoSource { // eslint-disable-line no-unused-vars
method constructor (line 16) | constructor() {
method setDebugPath (line 30) | setDebugPath(path) {
method setVisibility (line 34) | setVisibility(visible) {
method updateVideoVisibility (line 41) | updateVideoVisibility() {
method getMediaStream (line 63) | async getMediaStream() {
method destroy (line 103) | destroy() {
FILE: src/content/insertable-streams/video-processing/js/webcodec-transform.js
class WebCodecTransform (line 15) | class WebCodecTransform { // eslint-disable-line no-unused-vars
method constructor (line 16) | constructor() {
method init (line 23) | async init() {
method transform (line 38) | async transform(frame, controller) {
method destroy (line 52) | destroy() {}
method handleEncodedFrame (line 55) | handleEncodedFrame(encodedFrame) {
method handleDecodedFrame (line 59) | handleDecodedFrame(videoFrame) {
method error (line 67) | error(e) {
FILE: src/content/insertable-streams/video-processing/js/webgl-transform.js
class WebGLTransform (line 15) | class WebGLTransform { // eslint-disable-line no-unused-vars
method constructor (line 16) | constructor() {
method init (line 32) | async init() {
method loadShader_ (line 119) | loadShader_(type, shaderSrc) {
method attributeSetFloats_ (line 143) | attributeSetFloats_(attrName, vsize, arr) {
method transform (line 153) | async transform(frame, controller) {
method destroy (line 182) | destroy() {
FILE: src/content/insertable-streams/webgpu/js/main.js
function getMediaStream (line 17) | async function getMediaStream(src) {
function getUserMediaStream (line 50) | function getUserMediaStream() {
function main (line 63) | async function main(sourceType) {
function destroy_source (line 86) | function destroy_source() {
function updateSource (line 101) | function updateSource() {
FILE: src/content/insertable-streams/webgpu/js/multi_video_main.js
class WebGPUTransform (line 36) | class WebGPUTransform { // eslint-disable-line no-unused-vars
method constructor (line 37) | constructor() {
method init (line 47) | async init(inputCanvas) {
method copyOnTexture (line 159) | async copyOnTexture(device, videoTexture, frame, xcorr, ycorr) {
method renderOnScreen (line 179) | async renderOnScreen(videoSource, gumSource) {
method transform (line 237) | async transform(videoStream, gumStream) {
method destroy (line 250) | destroy() {
FILE: src/content/insertable-streams/webgpu/js/multi_video_worker_manager.js
class WebGPUWorker (line 8) | class WebGPUWorker {
method init (line 9) | async init() {
method transform (line 39) | transform(videoStream, gumStream) {
method destroy (line 50) | destroy() {
FILE: src/content/peerconnection/always-negotiate-datachannels/js/main.js
function log (line 27) | function log(text) {
function getName (line 30) | function getName(pc) {
function getOtherPc (line 34) | function getOtherPc(pc) {
function start (line 38) | async function start() {
function call (line 51) | async function call() {
function gotRemoteStream (line 87) | function gotRemoteStream(e) {
function onIceCandidate (line 91) | function onIceCandidate(pc, event) {
function onAddIceCandidateError (line 97) | function onAddIceCandidateError(pc, error) {
function renegotiate (line 101) | async function renegotiate() {
function hangup (line 124) | function hangup() {
FILE: src/content/peerconnection/audio/js/main.js
function setPtime (line 78) | async function setPtime(ptime) {
function gotStream (line 90) | function gotStream(stream) {
function onCreateSessionDescriptionError (line 123) | function onCreateSessionDescriptionError(error) {
function call (line 127) | function call() {
function gotDescription1 (line 151) | function gotDescription1(desc) {
function gotDescription2 (line 164) | function gotDescription2(desc) {
function hangup (line 180) | function hangup() {
function gotRemoteStream (line 192) | function gotRemoteStream(e) {
function getOtherPc (line 215) | function getOtherPc(pc) {
function getName (line 219) | function getName(pc) {
function onIceCandidate (line 223) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 232) | function onAddIceCandidateSuccess() {
function onAddIceCandidateError (line 236) | function onAddIceCandidateError(error) {
function onSetSessionDescriptionError (line 240) | function onSetSessionDescriptionError(error) {
function forceChosenAudioCodec (line 244) | function forceChosenAudioCodec(sdp) {
function maybePreferCodec (line 252) | function maybePreferCodec(sdp, type, dir, codec) {
function findLine (line 285) | function findLine(sdpLines, prefix, substr) {
function findLineInRange (line 291) | function findLineInRange(sdpLines, startLine, endLine, prefix, substr) {
function getCodecPayloadType (line 305) | function getCodecPayloadType(sdpLine) {
function setDefaultCodec (line 312) | function setDefaultCodec(mLine, payload) {
FILE: src/content/peerconnection/bandwidth/js/main.js
function gotStream (line 63) | function gotStream(stream) {
function onCreateSessionDescriptionError (line 90) | function onCreateSessionDescriptionError(error) {
function call (line 94) | function call() {
function gotDescription1 (line 119) | function gotDescription1(desc) {
function gotDescription2 (line 130) | function gotDescription2(desc) {
function hangup (line 149) | function hangup() {
function gotRemoteStream (line 161) | function gotRemoteStream(e) {
function getOtherPc (line 170) | function getOtherPc(pc) {
function getName (line 174) | function getName(pc) {
function onIceCandidate (line 178) | function onIceCandidate(event) {
function onAddIceCandidateSuccess (line 187) | function onAddIceCandidateSuccess() {
function onAddIceCandidateError (line 191) | function onAddIceCandidateError(error) {
function onSetSessionDescriptionError (line 195) | function onSetSessionDescriptionError(error) {
function setBandwidth (line 209) | function setBandwidth(bandwidthInKbps) {
function updateBandwidthRestriction (line 249) | function updateBandwidthRestriction(sdp, bandwidth) {
function removeBandwidthRestriction (line 264) | function removeBandwidthRestriction(sdp) {
function triangle (line 318) | function triangle(number, maxValue) {
function syntheticVideoStream (line 323) | function syntheticVideoStream({width = 640, height = 480, signal} = {}) {
FILE: src/content/peerconnection/change-codecs/js/main.js
function getName (line 51) | function getName(pc) {
function getOtherPc (line 55) | function getOtherPc(pc) {
function start (line 59) | async function start() {
function call (line 86) | async function call() {
function onCreateSessionDescriptionError (line 118) | function onCreateSessionDescriptionError(error) {
function onCreateOfferSuccess (line 122) | async function onCreateOfferSuccess(desc) {
function onSetLocalSuccess (line 149) | function onSetLocalSuccess(pc) {
function onSetRemoteSuccess (line 153) | function onSetRemoteSuccess(pc) {
function onSetSessionDescriptionError (line 157) | function onSetSessionDescriptionError(error) {
function gotRemoteStream (line 161) | function gotRemoteStream(e) {
function onCreateAnswerSuccess (line 183) | async function onCreateAnswerSuccess(desc) {
function onIceCandidate (line 221) | async function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 231) | function onAddIceCandidateSuccess(pc) {
function onAddIceCandidateError (line 235) | function onAddIceCandidateError(pc, error) {
function hangup (line 239) | function hangup() {
FILE: src/content/peerconnection/channel/js/main.js
function hangup (line 72) | async function hangup() {
function createPeerConnection (line 83) | function createPeerConnection() {
function makeCall (line 101) | async function makeCall() {
function handleOffer (line 109) | async function handleOffer(offer) {
function handleAnswer (line 122) | async function handleAnswer(answer) {
function handleCandidate (line 130) | async function handleCandidate(candidate) {
FILE: src/content/peerconnection/constraints/js/main.js
function main (line 51) | function main() {
function hangup (line 55) | function hangup() {
function getMedia (line 82) | function getMedia() {
function gotStream (line 101) | function gotStream(stream) {
function getUserMediaConstraints (line 108) | function getUserMediaConstraints() {
function displayGetUserMediaConstraints (line 140) | function displayGetUserMediaConstraints() {
function createPeerConnection (line 146) | function createPeerConnection() {
function onAddIceCandidateSuccess (line 194) | function onAddIceCandidateSuccess() {
function onAddIceCandidateError (line 198) | function onAddIceCandidateError(error) {
function showRemoteStats (line 202) | function showRemoteStats(results) {
function showLocalStats (line 258) | function showLocalStats(results) {
function dumpStats (line 293) | function dumpStats(results) {
function displayRangeValue (line 314) | function displayRangeValue(e) {
FILE: src/content/peerconnection/create-offer/js/main.js
function createOffer (line 24) | async function createOffer() {
FILE: src/content/peerconnection/dtmf/js/main.js
function main (line 41) | async function main() {
function gotStream (line 52) | async function gotStream(stream) {
function call (line 69) | async function call() {
function gotLocalOffer (line 93) | async function gotLocalOffer(desc) {
function gotRemoteAnswer (line 105) | function gotRemoteAnswer(desc) {
function hangup (line 111) | function hangup() {
function gotRemoteStream (line 125) | function gotRemoteStream(e) {
function getOtherPc (line 151) | function getOtherPc(pc) {
function getName (line 155) | function getName(pc) {
function onIceCandidate (line 159) | async function onIceCandidate(pc, event) {
function dtmfOnToneChange (line 168) | function dtmfOnToneChange(tone) {
function sendTones (line 175) | function sendTones(tones) {
function handleSendTonesClick (line 186) | function handleSendTonesClick() {
function addDialPadHandlers (line 190) | function addDialPadHandlers() {
FILE: src/content/peerconnection/multiple-relay/js/main.js
function gotStream (line 35) | function gotStream(stream) {
function gotremoteStream (line 42) | function gotremoteStream(stream) {
function start (line 51) | function start() {
function call (line 64) | function call() {
function insertRelay (line 72) | function insertRelay() {
function hangup (line 77) | function hangup() {
FILE: src/content/peerconnection/multiple/js/main.js
function maybeSetCodecPreferences (line 35) | function maybeSetCodecPreferences(trackEvent) {
function start (line 47) | async function start() {
function call (line 58) | async function call() {
function negotiate (line 90) | async function negotiate(localPc, remotePc) {
function hangup (line 100) | function hangup() {
function gotRemoteStream (line 112) | function gotRemoteStream(e, videoObject) {
FILE: src/content/peerconnection/munge-sdp/js/main.js
function getMedia (line 47) | async function getMedia() {
function gotStream (line 63) | function gotStream(stream) {
function createPeerConnection (line 70) | function createPeerConnection() {
function onSetSessionDescriptionSuccess (line 104) | function onSetSessionDescriptionSuccess() {
function onSetSessionDescriptionError (line 108) | function onSetSessionDescriptionError(error) {
function createOffer (line 113) | async function createOffer() {
function onCreateSessionDescriptionError (line 122) | function onCreateSessionDescriptionError(error) {
function setOffer (line 126) | async function setOffer() {
function gotDescription1 (line 159) | function gotDescription1(description) {
function createAnswer (line 166) | async function createAnswer() {
function setAnswer (line 178) | async function setAnswer() {
function gotDescription2 (line 215) | function gotDescription2(description) {
function sendData (line 222) | function sendData() {
function hangup (line 230) | function hangup() {
function gotRemoteStream (line 253) | function gotRemoteStream(e) {
function getOtherPc (line 260) | function getOtherPc(pc) {
function getName (line 264) | function getName(pc) {
function onIceCandidate (line 268) | async function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 280) | function onAddIceCandidateSuccess() {
function onAddIceCandidateError (line 284) | function onAddIceCandidateError(error) {
function receiveChannelCallback (line 288) | function receiveChannelCallback(event) {
function onReceiveMessageCallback (line 296) | function onReceiveMessageCallback(event) {
function onSendChannelStateChange (line 301) | function onSendChannelStateChange() {
function onReceiveChannelStateChange (line 311) | function onReceiveChannelStateChange() {
FILE: src/content/peerconnection/negotiate-timing/js/main.js
function logToScreen (line 67) | function logToScreen(text) {
function getName (line 72) | function getName(pc) {
function getOtherPc (line 76) | function getOtherPc(pc) {
function start (line 80) | async function start() {
function runOfferAnswer (line 94) | async function runOfferAnswer() {
function call (line 119) | async function call() {
function gotRemoteStream (line 158) | function gotRemoteStream(e) {
function onIceCandidate (line 167) | async function onIceCandidate(pc, event) {
function onIceStateChange (line 177) | function onIceStateChange(pc, event) {
function adjustTransceiverCounts (line 184) | function adjustTransceiverCounts(pc, videoCount) {
function getAudioImpairment (line 202) | async function getAudioImpairment(audioTransceiver) {
function baselineAudioImpairment (line 214) | async function baselineAudioImpairment(pc) {
function measureAudioImpairment (line 220) | async function measureAudioImpairment(pc) {
function renegotiate (line 228) | async function renegotiate() {
function hangup (line 242) | function hangup() {
FILE: src/content/peerconnection/pc1/js/main.js
function getName (line 51) | function getName(pc) {
function getOtherPc (line 55) | function getOtherPc(pc) {
function start (line 59) | async function start() {
function call (line 73) | async function call() {
function onCreateSessionDescriptionError (line 110) | function onCreateSessionDescriptionError(error) {
function onCreateOfferSuccess (line 114) | async function onCreateOfferSuccess(desc) {
function onSetLocalSuccess (line 144) | function onSetLocalSuccess(pc) {
function onSetRemoteSuccess (line 148) | function onSetRemoteSuccess(pc) {
function onSetSessionDescriptionError (line 152) | function onSetSessionDescriptionError(error) {
function gotRemoteStream (line 156) | function gotRemoteStream(e) {
function onCreateAnswerSuccess (line 163) | async function onCreateAnswerSuccess(desc) {
function onIceCandidate (line 181) | async function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 191) | function onAddIceCandidateSuccess(pc) {
function onAddIceCandidateError (line 195) | function onAddIceCandidateError(pc, error) {
function onIceStateChange (line 199) | function onIceStateChange(pc, event) {
function hangup (line 206) | function hangup() {
FILE: src/content/peerconnection/per-frame-callback/js/main.js
function gotStream (line 98) | function gotStream(stream) {
function onCreateSessionDescriptionError (line 129) | function onCreateSessionDescriptionError(error) {
function call (line 133) | function call() {
function gotDescription1 (line 151) | function gotDescription1(desc) {
function gotDescription2 (line 162) | function gotDescription2(desc) {
function hangup (line 172) | function hangup() {
function gotRemoteStream (line 184) | function gotRemoteStream(e) {
function getOtherPc (line 191) | function getOtherPc(pc) {
function getName (line 195) | function getName(pc) {
function onIceCandidate (line 199) | function onIceCandidate(event) {
function onAddIceCandidateSuccess (line 208) | function onAddIceCandidateSuccess() {
function onAddIceCandidateError (line 212) | function onAddIceCandidateError(error) {
function onSetSessionDescriptionError (line 216) | function onSetSessionDescriptionError(error) {
FILE: src/content/peerconnection/perfect-negotiation/js/main.js
function run (line 21) | async function run(target, cmd) {
function startLocalVideo (line 39) | function startLocalVideo(r1, g1, b1, r2, g2, b2) {
function setupIframe (line 76) | async function setupIframe(el, polite, r1, g1, b1, r2, g2, b2) {
function swapOnBoth (line 98) | async function swapOnBoth(politeFirst) { // eslint-disable-line no-unuse...
function setupIframes (line 108) | async function setupIframes() {
FILE: src/content/peerconnection/perfect-negotiation/js/peer.js
function peer (line 18) | function peer(other, polite, fail = undefined) { // eslint-disable-line ...
FILE: src/content/peerconnection/pr-answer/js/main.js
function gotStream (line 35) | function gotStream(stream) {
function start (line 50) | function start() {
function onCreateSessionDescriptionError (line 79) | function onCreateSessionDescriptionError(error) {
function onCreateAnswerError (line 84) | function onCreateAnswerError(error) {
function onSetLocalDescriptionError (line 89) | function onSetLocalDescriptionError(error) {
function onSetLocalDescriptionSuccess (line 94) | function onSetLocalDescriptionSuccess() {
function gotDescription1 (line 98) | function gotDescription1(desc) {
function gotDescription2 (line 111) | function gotDescription2(desc) {
function gotDescription3 (line 120) | function gotDescription3(desc) {
function accept (line 129) | function accept() {
function stop (line 135) | function stop() {
function gotRemoteStream (line 146) | function gotRemoteStream(e) {
function getOtherPc (line 151) | function getOtherPc(pc) {
function getName (line 155) | function getName(pc) {
function onIceCandidate (line 159) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 166) | function onAddIceCandidateSuccess() {
function onAddIceCandidateError (line 170) | function onAddIceCandidateError(error) {
FILE: src/content/peerconnection/restart-ice/js/main.js
function getName (line 61) | function getName(pc) {
function getOtherPc (line 65) | function getOtherPc(pc) {
function gotStream (line 69) | function gotStream(stream) {
function start (line 76) | function start() {
function restart (line 89) | function restart() {
function call (line 96) | function call() {
function onCreateSessionDescriptionError (line 133) | function onCreateSessionDescriptionError(error) {
function onCreateOfferSuccess (line 137) | function onCreateOfferSuccess(desc) {
function onSetLocalSuccess (line 150) | function onSetLocalSuccess(pc) {
function onSetRemoteSuccess (line 154) | function onSetRemoteSuccess(pc) {
function onSetSessionDescriptionError (line 158) | function onSetSessionDescriptionError(error) {
function gotRemoteStream (line 162) | function gotRemoteStream(e) {
function onCreateAnswerSuccess (line 169) | function onCreateAnswerSuccess(desc) {
function onIceCandidate (line 189) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 196) | function onAddIceCandidateSuccess(pc) {
function onAddIceCandidateError (line 200) | function onAddIceCandidateError(pc, error) {
function onIceStateChange (line 204) | function onIceStateChange(pc, event) {
function checkStats (line 217) | function checkStats(pc) {
function hangup (line 252) | function hangup() {
FILE: src/content/peerconnection/states/js/main.js
function gotStream (line 40) | function gotStream(stream) {
function start (line 47) | function start() {
function call (line 59) | function call() {
function onCreateSessionDescriptionError (line 101) | function onCreateSessionDescriptionError(error) {
function gotDescription1 (line 105) | function gotDescription1(description) {
function gotDescription2 (line 112) | function gotDescription2(description) {
function hangup (line 118) | function hangup() {
function gotRemoteStream (line 134) | function gotRemoteStream(e) {
function stateCallback1 (line 141) | function stateCallback1() {
function stateCallback2 (line 150) | function stateCallback2() {
function iceStateCallback1 (line 159) | function iceStateCallback1() {
function iceStateCallback2 (line 168) | function iceStateCallback2() {
function connStateCallback1 (line 177) | function connStateCallback1() {
function connStateCallback2 (line 185) | function connStateCallback2() {
function getOtherPc (line 192) | function getOtherPc(pc) {
function getName (line 196) | function getName(pc) {
function onIceCandidate (line 200) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 207) | function onAddIceCandidateSuccess() {
function onAddIceCandidateError (line 211) | function onAddIceCandidateError(error) {
FILE: src/content/peerconnection/trickle-ice/js/main.js
function setDefaultServer (line 39) | function setDefaultServer(serversSelect) {
function writeServersToLocalStorage (line 47) | function writeServersToLocalStorage() {
function readServersFromLocalStorage (line 53) | function readServersFromLocalStorage() {
function selectServer (line 71) | function selectServer(event) {
function addServer (line 79) | function addServer() {
function removeServer (line 105) | function removeServer() {
function start (line 114) | async function start() {
function formatPriority (line 176) | function formatPriority(priority) {
function appendCell (line 184) | function appendCell(row, val) {
function getFinalResult (line 192) | function getFinalResult() {
function iceCallback (line 228) | async function iceCallback(event) {
function gatheringStateChange (line 263) | function gatheringStateChange() {
function iceCandidateError (line 282) | function iceCandidateError(e) {
FILE: src/content/peerconnection/upgrade/js/main.js
function getName (line 55) | function getName(pc) {
function getOtherPc (line 59) | function getOtherPc(pc) {
function gotStream (line 63) | function gotStream(stream) {
function start (line 70) | function start() {
function call (line 82) | function call() {
function onCreateSessionDescriptionError (line 110) | function onCreateSessionDescriptionError(error) {
function onCreateOfferSuccess (line 114) | function onCreateOfferSuccess(desc) {
function onSetLocalSuccess (line 127) | function onSetLocalSuccess(pc) {
function onSetRemoteSuccess (line 131) | function onSetRemoteSuccess(pc) {
function onSetSessionDescriptionError (line 135) | function onSetSessionDescriptionError(error) {
function gotRemoteStream (line 139) | function gotRemoteStream(e) {
function onCreateAnswerSuccess (line 147) | function onCreateAnswerSuccess(desc) {
function onIceCandidate (line 156) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 163) | function onAddIceCandidateSuccess(pc) {
function onAddIceCandidateError (line 167) | function onAddIceCandidateError(pc, error) {
function onIceStateChange (line 171) | function onIceStateChange(pc, event) {
function upgrade (line 178) | function upgrade() {
function hangup (line 200) | function hangup() {
FILE: src/content/peerconnection/webaudio-input/js/main.js
function start (line 32) | function start() {
function stop (line 46) | function stop() {
function handleSuccess (line 58) | function handleSuccess(stream) {
function handleFailure (line 88) | function handleFailure(error) {
function gotDescription1 (line 94) | function gotDescription1(desc) {
function gotDescription2 (line 104) | function gotDescription2(desc) {
function gotRemoteStream (line 110) | function gotRemoteStream(e) {
function getOtherPc (line 116) | function getOtherPc(pc) {
function getName (line 120) | function getName(pc) {
function onIceCandidate (line 124) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 131) | function onAddIceCandidateSuccess() {
function onAddIceCandidateError (line 135) | function onAddIceCandidateError(error) {
function handleKeyDown (line 139) | function handleKeyDown() {
function toggleRenderLocally (line 143) | function toggleRenderLocally() {
function logError (line 148) | function logError(error) {
FILE: src/content/peerconnection/webaudio-input/js/webaudioextended.js
function WebAudioExtended (line 13) | function WebAudioExtended() {
FILE: src/content/peerconnection/webaudio-output/js/main.js
function getName (line 55) | function getName(pc) {
function getOtherPc (line 59) | function getOtherPc(pc) {
function gotStream (line 63) | function gotStream(stream) {
function start (line 70) | function start() {
function call (line 82) | function call() {
function onCreateSessionDescriptionError (line 113) | function onCreateSessionDescriptionError(error) {
function onCreateOfferSuccess (line 117) | function onCreateOfferSuccess(desc) {
function onSetLocalSuccess (line 130) | function onSetLocalSuccess(pc) {
function onSetRemoteSuccess (line 134) | function onSetRemoteSuccess(pc) {
function onSetSessionDescriptionError (line 138) | function onSetSessionDescriptionError(error) {
function gotRemoteStream (line 142) | function gotRemoteStream(e) {
function onCreateAnswerSuccess (line 152) | function onCreateAnswerSuccess(desc) {
function onIceCandidate (line 160) | function onIceCandidate(pc, event) {
function onAddIceCandidateSuccess (line 167) | function onAddIceCandidateSuccess(pc) {
function onAddIceCandidateError (line 171) | function onAddIceCandidateError(pc, error) {
function onIceStateChange (line 175) | function onIceStateChange(pc, event) {
function hangup (line 182) | function hangup() {
FILE: src/js/third_party/graph.js
constant MAX_STATS_DATA_POINT_BUFFER_SIZE (line 15) | const MAX_STATS_DATA_POINT_BUFFER_SIZE = 1000;
function TimelineDataSeries (line 21) | function TimelineDataSeries() {
function DataPoint (line 134) | function DataPoint(time, value) {
function TimelineGraphView (line 168) | function TimelineGraphView(divId, canvasId) {
function Graph (line 414) | function Graph() {
FILE: src/js/third_party/streamvisualizer.js
constant WIDTH (line 21) | const WIDTH = 308;
constant HEIGHT (line 22) | const HEIGHT = 231;
constant SMOOTHING (line 25) | const SMOOTHING = 0.8;
constant FFT_SIZE (line 26) | const FFT_SIZE = 2048;
function StreamVisualizer (line 28) | function StreamVisualizer(remoteStream, canvas) {
FILE: src/js/third_party/webgl_teapot/cameracontroller.js
function CameraController (line 45) | function CameraController(element, opt_canvas, opt_context) {
FILE: src/js/third_party/webgl_teapot/demo.js
function main (line 63) | function main() {
function log (line 92) | function log(msg) {
function handleContextLost (line 98) | function handleContextLost(e) {
function handleContextRestored (line 104) | function handleContextRestored() {
function output (line 110) | function output(str) {
function checkGLError (line 115) | function checkGLError() {
function init (line 124) | function init() {
function initTeapot (line 136) | function initTeapot() {
function loadShader (line 218) | function loadShader(type, shaderSrc) {
function initShaders (line 235) | function initShaders() {
function draw (line 269) | function draw() {
function clearLoadingImages (line 342) | function clearLoadingImages() {
function loadTexture (line 349) | function loadTexture(src) {
function loadCubeMap (line 373) | function loadCubeMap(base, suffix) {
FILE: src/js/third_party/webgl_teapot/matrix4x4.js
function Matrix4x4 (line 45) | function Matrix4x4() {
FILE: src/js/third_party/webgl_teapot/webgl-debug.js
function init (line 176) | function init(ctx) {
function checkInit (line 192) | function checkInit() {
function mightBeEnum (line 203) | function mightBeEnum(value) {
function glEnumToString (line 217) | function glEnumToString(value) {
function glFunctionArgToString (line 233) | function glFunctionArgToString(functionName, numArgs, argumentIndex, val...
function glFunctionArgsToString (line 279) | function glFunctionArgsToString(functionName, args) {
function makePropertyWrapper (line 291) | function makePropertyWrapper(wrapper, original, propertyName) {
function makeFunctionWrapper (line 305) | function makeFunctionWrapper(original, functionName) {
function makeDebugContext (line 332) | function makeDebugContext(ctx, opt_onErrorFunc, opt_onFunc, opt_err_ctx) {
function resetToInitialState (line 402) | function resetToInitialState(ctx) {
function makeLostContextSimulatingCanvas (line 466) | function makeLostContextSimulatingCanvas(canvas) {
FILE: src/js/third_party/webgl_teapot/webgl-utils.js
function showLink (line 105) | function showLink(str) {
FILE: src/js/videopipe.js
function errorHandler (line 23) | function errorHandler(context) {
function successHandler (line 30) | function successHandler(context) {
function noAction (line 36) | function noAction() {
function VideoPipe (line 40) | function VideoPipe(stream, handler) {
FILE: test/download-browsers.js
function download (line 3) | async function download() {
FILE: test/steps.js
constant TIMEOUT (line 8) | const TIMEOUT = 10000;
function step (line 10) | function step(drivers, cb, logMessage) {
function waitNVideosExist (line 19) | function waitNVideosExist(driver, n) {
function waitAllVideosHaveEnoughData (line 25) | function waitAllVideosHaveEnoughData(driver) {
FILE: test/webdriver.js
function download (line 18) | async function download(browser, version, cacheDir, platform) {
function mapVersion (line 38) | function mapVersion(browser, version) {
function buildDriver (line 50) | async function buildDriver(browser = process.env.BROWSER || 'chrome', op...
FILE: test/webrtcclient.js
class MediaStream (line 11) | class MediaStream {
method constructor (line 12) | constructor(tracks = []) {
method getTracks (line 17) | getTracks() {
method getAudioTracks (line 21) | getAudioTracks() {
method getVideoTracks (line 25) | getVideoTracks() {
class MediaDevices (line 30) | class MediaDevices {
method constructor (line 31) | constructor(driver) {
method getUserMedia (line 35) | getUserMedia(constraints) {
class PeerConnection (line 58) | class PeerConnection {
method constructor (line 59) | constructor(driver) {
method create (line 63) | create(rtcConfiguration) {
method addTrack (line 69) | addTrack(track, stream) {
method createOffer (line 77) | createOffer(offerOptions) {
method createAnswer (line 85) | createAnswer() {
method setLocalDescription (line 95) | setLocalDescription(desc) {
method setRemoteDescription (line 112) | setRemoteDescription(desc) {
Condensed preview — 241 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,011K chars).
[
{
"path": ".eslintrc.js",
"chars": 1089,
"preview": "module.exports = {\n 'extends': 'google',\n 'parserOptions': {\n 'ecmaVersion': 2017,\n 'sourceType': 'module',\n },"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 1151,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n# Please read fir"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 170,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Please use the discuss-webrtc mailing list for general questions\n "
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 30,
"preview": "**Description**\n\n\n**Purpose**\n"
},
{
"path": ".github/workflows/interop-tests.yml",
"chars": 825,
"preview": "on:\r\n schedule:\r\n - cron: \"30 5 * * *\"\r\n\r\njobs:\r\n interop:\r\n runs-on: ubuntu-22.04\r\n timeout-minutes: 5\r\n "
},
{
"path": ".github/workflows/test.yml",
"chars": 712,
"preview": "name: lint-and-test\n\non: [pull_request]\n\njobs:\n lint:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkou"
},
{
"path": ".gitignore",
"chars": 160,
"preview": "browsers*\n.eslintcache\nfirefox-*.tar.bz2\nnode_modules\n.DS_Store\nvalidation-report.json\nvalidation-status.json\n.idea\nfire"
},
{
"path": ".npmrc",
"chars": 19,
"preview": "package-lock=false\n"
},
{
"path": ".stylelintrc",
"chars": 47,
"preview": "{\n \"extends\": \"stylelint-config-recommended\"\n}"
},
{
"path": "AUTHORS",
"chars": 48,
"preview": "The WebRTC Project Authors\nThe Chromium Authors\n"
},
{
"path": "CONTRIBUTING.md",
"chars": 1039,
"preview": "# WebRTC welcomes patches/pulls for features and bug fixes!\n\nFor contributors external to Google, follow the instruction"
},
{
"path": "LICENSE.md",
"chars": 1511,
"preview": "Copyright (c) 2014, The WebRTC project authors. All rights reserved.\n\nRedistribution and use in source and binary forms,"
},
{
"path": "README.md",
"chars": 1068,
"preview": "# WebRTC Code Samples\n\nThis is a repository for the WebRTC JavaScript code samples. All of the samples can be tested fro"
},
{
"path": "google1b7eb21c5b594ba0.html",
"chars": 53,
"preview": "google-site-verification: google1b7eb21c5b594ba0.html"
},
{
"path": "index.html",
"chars": 10183,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "package.json",
"chars": 1164,
"preview": "{\n \"name\": \"webrtc-samples\",\n \"private\": true,\n \"version\": \"1.0.0\",\n \"description\": \"Project checking for WebRTC Git"
},
{
"path": "src/content/capture/canvas-filter/css/main.css",
"chars": 526,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/canvas-filter/index.html",
"chars": 2533,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/capture/canvas-filter/js/main.js",
"chars": 1593,
"preview": "/*\n* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/canvas-pc/css/main.css",
"chars": 536,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/canvas-pc/index.html",
"chars": 3626,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/capture/canvas-pc/js/main.js",
"chars": 4878,
"preview": "/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/capture/canvas-record/css/main.css",
"chars": 742,
"preview": "/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/capture/canvas-record/index.html",
"chars": 3636,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/capture/canvas-record/js/main.js",
"chars": 3858,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/canvas-video/css/main.css",
"chars": 526,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/canvas-video/index.html",
"chars": 3011,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/capture/canvas-video/js/main.js",
"chars": 449,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/video-contenthint/css/main.css",
"chars": 491,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/video-contenthint/index.html",
"chars": 3962,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/capture/video-contenthint/js/main.js",
"chars": 3665,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/video-pc/css/main.css",
"chars": 429,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/video-pc/index.html",
"chars": 2988,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/capture/video-pc/js/main.js",
"chars": 5509,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/video-video/css/main.css",
"chars": 429,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/video-video/index.html",
"chars": 2371,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/capture/video-video/js/main.js",
"chars": 714,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/worker-process/css/main.css",
"chars": 526,
"preview": "/*\n* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/worker-process/index.html",
"chars": 2628,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/capture/worker-process/js/main.js",
"chars": 2196,
"preview": "/*\n* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/capture/worker-process/js/worker.js",
"chars": 553,
"preview": "/*\n* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/datachannel/basic/css/main.css",
"chars": 773,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/basic/index.html",
"chars": 2766,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/datachannel/basic/js/main.js",
"chars": 4568,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/basic/js/test.js",
"chars": 1764,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/channel/css/main.css",
"chars": 773,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/channel/index.html",
"chars": 3052,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/datachannel/channel/js/main.js",
"chars": 5033,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/channel/js/test.js",
"chars": 3276,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/datatransfer/css/main.css",
"chars": 429,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/datatransfer/index.html",
"chars": 3747,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/datachannel/datatransfer/js/main.js",
"chars": 7961,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/datatransfer/js/test.js",
"chars": 1810,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/filetransfer/css/main.css",
"chars": 460,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/filetransfer/index.html",
"chars": 3822,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/datachannel/filetransfer/js/main.js",
"chars": 8392,
"preview": "/* eslint no-unused-expressions: 0 */\n/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * "
},
{
"path": "src/content/datachannel/filetransfer/js/test.js",
"chars": 1409,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/messaging/index.html",
"chars": 2507,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/datachannel/messaging/main.css",
"chars": 432,
"preview": "/*\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/datachannel/messaging/main.js",
"chars": 5729,
"preview": "/*\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/devices/input-output/index.html",
"chars": 2956,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/devices/input-output/js/main.js",
"chars": 5063,
"preview": "/*\n* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/devices/input-output/js/test.js",
"chars": 1414,
"preview": "/*\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/devices/multi/css/main.css",
"chars": 492,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/devices/multi/index.html",
"chars": 3637,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/devices/multi/js/main.js",
"chars": 3520,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/extensions/multipleroutes/src/README.md",
"chars": 2060,
"preview": "## Chrome WebRTC Network Limiter\nConfigures the WebRTC traffic routing options in Chrome's privacy settings.\n\n★ What it "
},
{
"path": "src/content/extensions/multipleroutes/src/_locales/en/messages.json",
"chars": 1863,
"preview": "{\n \"NETLI_DEFAULT_RADIO\": {\n \"message\": \"<strong>Give me the best media experience:</strong> This option allows C"
},
{
"path": "src/content/extensions/multipleroutes/src/manifest.json",
"chars": 385,
"preview": "{\n \"default_locale\": \"en\",\n \"description\": \"__MSG_NETLI_APPDESC__\",\n \"icons\": {\n \"16\": \"img/icon_16.png\",\n \"128"
},
{
"path": "src/content/extensions/multipleroutes/src/options.html",
"chars": 1618,
"preview": "<!doctype html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/extensions/multipleroutes/src/options.js",
"chars": 2608,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/extensions/svc/css/main.css",
"chars": 838,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/extensions/svc/index.html",
"chars": 3140,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/extensions/svc/js/main.js",
"chars": 12143,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/audio/index.html",
"chars": 2179,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/audio/js/main.js",
"chars": 1142,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/canvas/index.html",
"chars": 2036,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/canvas/js/main.js",
"chars": 1108,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/exposure/index.html",
"chars": 3093,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/exposure/js/main.js",
"chars": 3468,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/filter/index.html",
"chars": 3041,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/filter/js/main.js",
"chars": 1261,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/getdisplaymedia/index.html",
"chars": 2318,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/getdisplaymedia/js/main.js",
"chars": 2184,
"preview": "/*\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/gum/index.html",
"chars": 2177,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/gum/js/main.js",
"chars": 1786,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/gum/js/test.js",
"chars": 1174,
"preview": "/*\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/pan-tilt-zoom/index.html",
"chars": 2634,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/pan-tilt-zoom/js/main.js",
"chars": 2454,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/record/css/main.css",
"chars": 969,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/record/index.html",
"chars": 2812,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/record/js/main.js",
"chars": 6688,
"preview": "/*\n* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n*\n* Use of this source code is governed by a "
},
{
"path": "src/content/getusermedia/resolution/index.html",
"chars": 4118,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/resolution/js/main.js",
"chars": 6696,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/resolution/js/test.js",
"chars": 1669,
"preview": "/*\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/source/index.html",
"chars": 547,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/volume/css/main.css",
"chars": 329,
"preview": "div#meters > div {\n margin: 0 0 1em 0;\n}\n\ndiv#meters div.label {\n display: inline-block;\n font-weight: 400;\n margin:"
},
{
"path": "src/content/getusermedia/volume/index.html",
"chars": 2988,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/getusermedia/volume/js/main.js",
"chars": 2660,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/volume/js/soundmeter.js",
"chars": 1576,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/getusermedia/volume/js/volume-meter-processor.js",
"chars": 1020,
"preview": "/*\n * Copyright (c) 2025 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/audio-processing/index.html",
"chars": 2932,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/insertable-streams/audio-processing/js/main.js",
"chars": 3031,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/audio-processing/js/worker.js",
"chars": 2411,
"preview": "/*\n * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/endtoend-encryption/css/main.css",
"chars": 529,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/endtoend-encryption/index.html",
"chars": 2671,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/insertable-streams/endtoend-encryption/js/main.js",
"chars": 7368,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/endtoend-encryption/js/test.js",
"chars": 2954,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/endtoend-encryption/js/videopipe.js",
"chars": 1402,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/endtoend-encryption/js/worker.js",
"chars": 5915,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video/index.html",
"chars": 577,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/insertable-streams/video-analyzer/css/main.css",
"chars": 757,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-analyzer/index.html",
"chars": 2851,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/insertable-streams/video-analyzer/js/main.js",
"chars": 9062,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-crop/css/main.css",
"chars": 454,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-crop/index.html",
"chars": 2380,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/insertable-streams/video-crop/js/main.js",
"chars": 1429,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-crop/js/worker.js",
"chars": 856,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/css/main.css",
"chars": 637,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/index.html",
"chars": 4423,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/insertable-streams/video-processing/js/camera-source.js",
"chars": 1746,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/canvas-source.js",
"chars": 7252,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/canvas-transform.js",
"chars": 2161,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/main.js",
"chars": 9884,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/peer-connection-pipe.js",
"chars": 3867,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/peer-connection-sink.js",
"chars": 1895,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/peer-connection-source.js",
"chars": 3129,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/pipeline.js",
"chars": 6704,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/simple-transforms.js",
"chars": 1293,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/video-mirror-helper.js",
"chars": 2392,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/video-sink.js",
"chars": 1555,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/video-source.js",
"chars": 3520,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/webcodec-transform.js",
"chars": 1712,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/video-processing/js/webgl-transform.js",
"chars": 6950,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/webgpu/css/main.css",
"chars": 384,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/insertable-streams/webgpu/index.html",
"chars": 3154,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/insertable-streams/webgpu/js/main.js",
"chars": 3299,
"preview": "'use strict';\n\n/* global MediaStreamTrackProcessor, MediaStreamTrackGenerator */\nif (typeof MediaStreamTrackProcessor =="
},
{
"path": "src/content/insertable-streams/webgpu/js/multi_video_main.js",
"chars": 7332,
"preview": "\n'use strict';\n\nconst wgslShaders = {\n vertex: `\nstruct VertexInput {\n [[location(0)]] position : vec3<f32>;\n [[locat"
},
{
"path": "src/content/insertable-streams/webgpu/js/multi_video_worker.js",
"chars": 622,
"preview": "importScripts('./multi_video_main.js');\n'use strict';\n\nlet mainTransform = null;\n\n/* global WebGPUTransform */ // define"
},
{
"path": "src/content/insertable-streams/webgpu/js/multi_video_worker_manager.js",
"chars": 1449,
"preview": "\n'use strict';\n\nlet worker;\nlet screenCanvas;\n\n// eslint-disable-next-line no-unused-vars\nclass WebGPUWorker {\n async i"
},
{
"path": "src/content/peerconnection/always-negotiate-datachannels/css/main.css",
"chars": 729,
"preview": "/*\n * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/always-negotiate-datachannels/index.html",
"chars": 3219,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/always-negotiate-datachannels/js/main.js",
"chars": 4610,
"preview": "/*\n * Copyright (c) 2026 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/audio/css/main.css",
"chars": 764,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/audio/index.html",
"chars": 4042,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/audio/js/main.js",
"chars": 12718,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/audio/js/test.js",
"chars": 1441,
"preview": "/*\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/bandwidth/css/main.css",
"chars": 597,
"preview": "/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/bandwidth/index.html",
"chars": 2957,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/bandwidth/js/main.js",
"chars": 11475,
"preview": "/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/change-codecs/css/main.css",
"chars": 757,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/change-codecs/index.html",
"chars": 3094,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/change-codecs/js/main.js",
"chars": 8076,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/change-codecs/js/test.js",
"chars": 2472,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/channel/css/main.css",
"chars": 757,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/channel/index.html",
"chars": 2421,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/channel/js/main.js",
"chars": 3297,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/channel/js/test.js",
"chars": 2339,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/constraints/css/main.css",
"chars": 1825,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/constraints/index.html",
"chars": 4722,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/constraints/js/main.js",
"chars": 10096,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/create-offer/css/main.css",
"chars": 693,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/create-offer/index.html",
"chars": 3026,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/create-offer/js/main.js",
"chars": 2139,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/dtmf/css/main.css",
"chars": 1407,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/dtmf/index.html",
"chars": 3476,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/dtmf/js/main.js",
"chars": 5924,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/dtmf/js/test.js",
"chars": 2769,
"preview": "/*\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/endtoend-encryption/index.html",
"chars": 583,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/multiple/css/main.css",
"chars": 682,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/multiple/index.html",
"chars": 2468,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/multiple/js/main.js",
"chars": 3764,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/multiple/js/test.js",
"chars": 2352,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/multiple-relay/css/main.css",
"chars": 530,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/multiple-relay/index.html",
"chars": 2339,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/multiple-relay/js/main.js",
"chars": 2346,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/munge-sdp/css/main.css",
"chars": 1636,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/munge-sdp/index.html",
"chars": 3492,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/munge-sdp/js/main.js",
"chars": 9117,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/munge-sdp/js/test.js",
"chars": 2378,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/negotiate-timing/css/main.css",
"chars": 729,
"preview": "/*\n * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/negotiate-timing/index.html",
"chars": 2587,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/negotiate-timing/js/main.js",
"chars": 9173,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/negotiate-timing/js/test.js",
"chars": 2442,
"preview": "/*\n * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/pc1/css/main.css",
"chars": 757,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/pc1/index.html",
"chars": 2761,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/pc1/js/main.js",
"chars": 6457,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/pc1/js/test.js",
"chars": 1936,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/per-frame-callback/css/main.css",
"chars": 597,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/per-frame-callback/index.html",
"chars": 3386,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/per-frame-callback/js/main.js",
"chars": 6784,
"preview": "/*\n * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/perfect-negotiation/css/main.css",
"chars": 757,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/perfect-negotiation/index.html",
"chars": 3596,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/perfect-negotiation/js/main.js",
"chars": 4108,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/perfect-negotiation/js/peer.js",
"chars": 5736,
"preview": "/*\n * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/pr-answer/index.html",
"chars": 2280,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/pr-answer/js/main.js",
"chars": 5033,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/restart-ice/css/main.css",
"chars": 630,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
},
{
"path": "src/content/peerconnection/restart-ice/index.html",
"chars": 2985,
"preview": "<!DOCTYPE html>\n<!--\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source c"
},
{
"path": "src/content/peerconnection/restart-ice/js/main.js",
"chars": 8509,
"preview": "/*\n * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by"
}
]
// ... and 41 more files (download for full content)
About this extraction
This page contains the full source code of the webrtc/samples GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 241 files (940.7 KB), approximately 308.3k tokens, and a symbol index with 718 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.