├── .npmrc ├── AUTHORS ├── .stylelintrc ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── config.yml │ └── bug_report.md └── workflows │ ├── test.yml │ └── interop-tests.yml ├── google1b7eb21c5b594ba0.html ├── src ├── video │ ├── chrome.mp4 │ ├── chrome.webm │ └── mixed-content.webm ├── images │ └── webrtc-icon-192x192.png ├── content │ ├── devices │ │ ├── multi │ │ │ ├── audio │ │ │ │ └── audio.mp3 │ │ │ ├── images │ │ │ │ └── poster.jpg │ │ │ ├── video │ │ │ │ ├── chrome.mp4 │ │ │ │ ├── chrome.ogv │ │ │ │ └── chrome.webm │ │ │ └── css │ │ │ │ └── main.css │ │ └── input-output │ │ │ └── js │ │ │ └── test.js │ ├── extensions │ │ ├── multipleroutes │ │ │ ├── img │ │ │ │ └── netli_1280.png │ │ │ └── src │ │ │ │ ├── img │ │ │ │ ├── icon_128.png │ │ │ │ └── icon_16.png │ │ │ │ ├── manifest.json │ │ │ │ ├── options.html │ │ │ │ ├── _locales │ │ │ │ └── en │ │ │ │ │ └── messages.json │ │ │ │ ├── README.md │ │ │ │ └── options.js │ │ └── svc │ │ │ └── css │ │ │ └── main.css │ ├── peerconnection │ │ ├── webaudio-input │ │ │ ├── audio │ │ │ │ └── Shamisen-C4.wav │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── webaudioextended.js │ │ ├── video-analyzer │ │ │ └── index.html │ │ ├── endtoend-encryption │ │ │ └── index.html │ │ ├── multiple-relay │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.html │ │ │ └── js │ │ │ │ └── main.js │ │ ├── bandwidth │ │ │ └── css │ │ │ │ └── main.css │ │ ├── per-frame-callback │ │ │ └── css │ │ │ │ └── main.css │ │ ├── restart-ice │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── test.js │ │ ├── multiple │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── js │ │ │ │ └── test.js │ │ │ └── index.html │ │ ├── webaudio-output │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── index.html │ │ ├── create-offer │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── main.js │ │ ├── upgrade │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── js │ │ │ │ └── test.js │ │ │ └── index.html │ │ ├── negotiate-timing │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── js │ │ │ │ └── test.js │ │ │ └── index.html │ │ ├── pc1 │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── test.js │ │ ├── channel │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── js │ │ │ │ └── test.js │ │ │ └── index.html │ │ ├── change-codecs │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── test.js │ │ ├── perfect-negotiation │ │ │ └── css │ │ │ │ └── main.css │ │ ├── audio │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── test.js │ │ ├── states │ │ │ └── css │ │ │ │ └── main.css │ │ ├── trickle-ice │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── test.js │ │ ├── dtmf │ │ │ └── css │ │ │ │ └── main.css │ │ ├── munge-sdp │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ └── test.js │ │ ├── constraints │ │ │ └── css │ │ │ │ └── main.css │ │ └── pr-answer │ │ │ └── index.html │ ├── getusermedia │ │ ├── volume │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ ├── volume-meter-processor.js │ │ │ │ ├── soundmeter.js │ │ │ │ └── main.js │ │ ├── source │ │ │ └── index.html │ │ ├── record │ │ │ └── css │ │ │ │ └── main.css │ │ ├── canvas │ │ │ ├── js │ │ │ │ └── main.js │ │ │ └── index.html │ │ ├── audio │ │ │ ├── js │ │ │ │ └── main.js │ │ │ └── index.html │ │ ├── gum │ │ │ ├── js │ │ │ │ ├── test.js │ │ │ │ └── main.js │ │ │ └── index.html │ │ ├── filter │ │ │ └── js │ │ │ │ └── main.js │ │ ├── resolution │ │ │ └── js │ │ │ │ └── test.js │ │ ├── getdisplaymedia │ │ │ ├── js │ │ │ │ └── main.js │ │ │ └── index.html │ │ └── pan-tilt-zoom │ │ │ ├── js │ │ │ └── main.js │ │ │ └── index.html │ ├── insertable-streams │ │ ├── webgpu │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ ├── multi_video_worker.js │ │ │ │ └── multi_video_worker_manager.js │ │ ├── video-crop │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── js │ │ │ │ ├── worker.js │ │ │ │ └── main.js │ │ │ └── index.html │ │ ├── video │ │ │ └── index.html │ │ ├── endtoend-encryption │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── js │ │ │ │ └── videopipe.js │ │ │ └── index.html │ │ ├── video-processing │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── js │ │ │ │ ├── simple-transforms.js │ │ │ │ ├── video-sink.js │ │ │ │ ├── camera-source.js │ │ │ │ ├── webcodec-transform.js │ │ │ │ ├── peer-connection-sink.js │ │ │ │ ├── canvas-transform.js │ │ │ │ └── video-mirror-helper.js │ │ ├── video-analyzer │ │ │ └── css │ │ │ │ └── main.css │ │ └── audio-processing │ │ │ └── js │ │ │ └── worker.js │ ├── capture │ │ ├── video-pc │ │ │ └── css │ │ │ │ └── main.css │ │ ├── canvas-video │ │ │ ├── js │ │ │ │ └── main.js │ │ │ └── css │ │ │ │ └── main.css │ │ ├── video-video │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── js │ │ │ │ └── main.js │ │ │ └── index.html │ │ ├── video-contenthint │ │ │ └── css │ │ │ │ └── main.css │ │ ├── canvas-filter │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── js │ │ │ │ └── main.js │ │ │ └── index.html │ │ ├── worker-process │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── js │ │ │ │ ├── worker.js │ │ │ │ └── main.js │ │ │ └── index.html │ │ ├── canvas-pc │ │ │ └── css │ │ │ │ └── main.css │ │ └── canvas-record │ │ │ └── css │ │ │ └── main.css │ └── datachannel │ │ ├── messaging │ │ ├── main.css │ │ └── index.html │ │ ├── datatransfer │ │ ├── css │ │ │ └── main.css │ │ └── js │ │ │ └── test.js │ │ ├── filetransfer │ │ ├── css │ │ │ └── main.css │ │ └── js │ │ │ └── test.js │ │ ├── basic │ │ ├── css │ │ │ └── main.css │ │ └── js │ │ │ └── test.js │ │ └── channel │ │ └── css │ │ └── main.css └── js │ ├── third_party │ └── webgl_teapot │ │ └── images │ │ ├── bump.jpg │ │ ├── skybox-negx.jpg │ │ ├── skybox-negy.jpg │ │ ├── skybox-negz.jpg │ │ ├── skybox-posx.jpg │ │ ├── skybox-posy.jpg │ │ └── skybox-posz.jpg │ ├── lib │ └── ga.js │ └── videopipe.js ├── release ├── desktopCaptureExtension.zip ├── ChromeWebRTCNetworkLimiterExtension_0.1.zip └── ChromeWebRTCNetworkLimiterExtension_0.2.1.1.zip ├── .gitignore ├── test ├── download-browsers.js ├── steps.js └── interop │ └── connection.test.js ├── CONTRIBUTING.md ├── README.md ├── .eslintrc.js ├── package.json └── LICENSE.md /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The WebRTC Project Authors 2 | The Chromium Authors 3 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-recommended" 3 | } -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Description** 2 | 3 | 4 | **Purpose** 5 | -------------------------------------------------------------------------------- /google1b7eb21c5b594ba0.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google1b7eb21c5b594ba0.html -------------------------------------------------------------------------------- /src/video/chrome.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/video/chrome.mp4 -------------------------------------------------------------------------------- /src/video/chrome.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/video/chrome.webm -------------------------------------------------------------------------------- /src/video/mixed-content.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/video/mixed-content.webm -------------------------------------------------------------------------------- /src/images/webrtc-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/images/webrtc-icon-192x192.png -------------------------------------------------------------------------------- /release/desktopCaptureExtension.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/release/desktopCaptureExtension.zip -------------------------------------------------------------------------------- /src/content/devices/multi/audio/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/content/devices/multi/audio/audio.mp3 -------------------------------------------------------------------------------- /src/content/devices/multi/images/poster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/content/devices/multi/images/poster.jpg -------------------------------------------------------------------------------- /src/content/devices/multi/video/chrome.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/content/devices/multi/video/chrome.mp4 -------------------------------------------------------------------------------- /src/content/devices/multi/video/chrome.ogv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/content/devices/multi/video/chrome.ogv -------------------------------------------------------------------------------- /src/content/devices/multi/video/chrome.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/content/devices/multi/video/chrome.webm -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/bump.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/js/third_party/webgl_teapot/images/bump.jpg -------------------------------------------------------------------------------- /release/ChromeWebRTCNetworkLimiterExtension_0.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/release/ChromeWebRTCNetworkLimiterExtension_0.1.zip -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-negx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/js/third_party/webgl_teapot/images/skybox-negx.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-negy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/js/third_party/webgl_teapot/images/skybox-negy.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-negz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/js/third_party/webgl_teapot/images/skybox-negz.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-posx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/js/third_party/webgl_teapot/images/skybox-posx.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-posy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/js/third_party/webgl_teapot/images/skybox-posy.jpg -------------------------------------------------------------------------------- /src/js/third_party/webgl_teapot/images/skybox-posz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/js/third_party/webgl_teapot/images/skybox-posz.jpg -------------------------------------------------------------------------------- /release/ChromeWebRTCNetworkLimiterExtension_0.2.1.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/release/ChromeWebRTCNetworkLimiterExtension_0.2.1.1.zip -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/img/netli_1280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/content/extensions/multipleroutes/img/netli_1280.png -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/img/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/content/extensions/multipleroutes/src/img/icon_128.png -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/img/icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/content/extensions/multipleroutes/src/img/icon_16.png -------------------------------------------------------------------------------- /src/content/peerconnection/webaudio-input/audio/Shamisen-C4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webrtc/samples/HEAD/src/content/peerconnection/webaudio-input/audio/Shamisen-C4.wav -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Please use the discuss-webrtc mailing list for general questions 4 | url: https://groups.google.com/g/discuss-webrtc 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | browsers* 2 | .eslintcache 3 | firefox-*.tar.bz2 4 | node_modules 5 | .DS_Store 6 | validation-report.json 7 | validation-status.json 8 | .idea 9 | firefox_profile/* 10 | tests_output 11 | *.log 12 | *~ 13 | \#*# -------------------------------------------------------------------------------- /test/download-browsers.js: -------------------------------------------------------------------------------- 1 | const {buildDriver} = require('./webdriver'); 2 | // Download the browser(s). 3 | async function download() { 4 | if (process.env.BROWSER_A && process.env.BROWSER_B) { 5 | (await buildDriver(process.env.BROWSER_A)).quit(); 6 | (await buildDriver(process.env.BROWSER_B)).quit(); 7 | } else { 8 | (await buildDriver()).quit(); 9 | } 10 | } 11 | download(); 12 | -------------------------------------------------------------------------------- /src/js/lib/ga.js: -------------------------------------------------------------------------------- 1 | (function(i, s, o, g, r, a, m) { 2 | i['GoogleAnalyticsObject']=r; i[r]=i[r]||function() { 3 | (i[r].q=i[r].q||[]).push(arguments); 4 | }, i[r].l=1*new Date(); a=s.createElement(o), 5 | m=s.getElementsByTagName(o)[0]; a.async=1; a.src=g; m.parentNode.insertBefore(a, m); 6 | })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); 7 | 8 | ga('create', 'UA-48530561-1', 'auto'); 9 | ga('send', 'pageview'); 10 | -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_locale": "en", 3 | "description": "__MSG_NETLI_APPDESC__", 4 | "icons": { 5 | "16": "img/icon_16.png", 6 | "128": "img/icon_128.png" 7 | }, 8 | "manifest_version": 3, 9 | "minimum_chrome_version": "42.0.2311.135", 10 | "name": "__MSG_NETLI_APPNAME__", 11 | "options_ui": { 12 | "open_in_tab": false, 13 | "page": "options.html" 14 | }, 15 | "permissions": [ "privacy" ], 16 | "version": "0.2.1.4" 17 | } 18 | -------------------------------------------------------------------------------- /src/content/getusermedia/volume/css/main.css: -------------------------------------------------------------------------------- 1 | div#meters > div { 2 | margin: 0 0 1em 0; 3 | } 4 | 5 | div#meters div.label { 6 | display: inline-block; 7 | font-weight: 400; 8 | margin: 0 0.5em 0 0; 9 | width: 3.5em; 10 | } 11 | 12 | div#meters div.value { 13 | display: inline-block; 14 | } 15 | 16 | meter { 17 | width: 50%; 18 | } 19 | 20 | meter#clip { 21 | color: #db4437; 22 | } 23 | 24 | meter#slow { 25 | color: #f4b400; 26 | } 27 | 28 | meter#instant { 29 | color: #0f9d58; 30 | } 31 | -------------------------------------------------------------------------------- /src/content/insertable-streams/webgpu/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | video { 10 | width: 480px; 11 | height: 270px; 12 | } 13 | 14 | .output { 15 | width: 960px; 16 | height: 540px; 17 | margin: 0px 0px 0px 0px; 18 | } 19 | 20 | .error { 21 | font-size: 20px; 22 | color:red; 23 | } 24 | -------------------------------------------------------------------------------- /src/content/capture/video-pc/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | video { 10 | margin: 0 10px 0 0; 11 | width: calc(50% - 7px); 12 | } 13 | 14 | video:last-of-type { 15 | margin-right: 0; 16 | } 17 | 18 | @media screen and (max-width: 400px) { 19 | video { 20 | margin: 0 5px 20px 0; 21 | width: calc(50% - 5px); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/content/capture/canvas-video/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | /* global main */ 10 | 11 | 'use strict'; 12 | 13 | // Call main() in demo.js 14 | main(); 15 | 16 | const canvas = document.querySelector('canvas'); 17 | const video = document.querySelector('video'); 18 | 19 | const stream = canvas.captureStream(); 20 | video.srcObject = stream; 21 | -------------------------------------------------------------------------------- /src/content/capture/video-video/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | video { 10 | margin: 0 10px 0 0; 11 | width: calc(50% - 7px); 12 | } 13 | 14 | video:last-of-type { 15 | margin-right: 0; 16 | } 17 | 18 | @media screen and (max-width: 400px) { 19 | video { 20 | margin: 0 5px 20px 0; 21 | width: calc(50% - 5px); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/content/datachannel/messaging/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | div.messageBox { 9 | width: 100%; 10 | } 11 | 12 | textarea.message { 13 | width: 100%; 14 | height: 5em; 15 | resize: none; 16 | display: block; 17 | box-sizing: border-box; 18 | margin: 1em; 19 | } 20 | 21 | label { 22 | font-weight: 400; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/content/datachannel/datatransfer/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | div.progress { 9 | margin: 0 0 1em 0; 10 | } 11 | 12 | div.progress div.label { 13 | display: inline-block; 14 | font-weight: 400; 15 | width: 8.2em; 16 | } 17 | 18 | div.input { 19 | margin: 0 0 1em 0; 20 | } 21 | 22 | progress { 23 | width: calc(100% - 8.5em); 24 | } 25 | -------------------------------------------------------------------------------- /src/content/datachannel/filetransfer/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | div.progress, div#bitrate { 9 | margin: 0 0 1em 0; 10 | } 11 | 12 | div.progress div.label { 13 | display: inline-block; 14 | font-weight: 400; 15 | width: 8.2em; 16 | } 17 | 18 | form { 19 | margin: 0 0 1em 0; 20 | white-space: nowrap; 21 | } 22 | 23 | progress { 24 | width: calc(100% - 8.5em); 25 | } 26 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-crop/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 20px 10px 0 0; 11 | width: 100px; 12 | } 13 | 14 | div#buttons { 15 | margin: 0 0 20px 0; 16 | } 17 | 18 | div#status { 19 | height: 2em; 20 | margin: 1em 0 0 0; 21 | } 22 | 23 | video { 24 | --width: 45%; 25 | width: var(--width); 26 | height: calc(var(--width) * 0.75); 27 | } 28 | -------------------------------------------------------------------------------- /src/content/capture/video-contenthint/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | video { 10 | --width: calc(45%); 11 | width: var(--width); 12 | height: calc(var(--width) * 9 / 16); 13 | margin: 1em; 14 | object-fit: cover; 15 | } 16 | 17 | .video-container { 18 | border-bottom: 1px solid grey; 19 | font-style: italic; 20 | margin: 20px; 21 | } 22 | 23 | #videos { 24 | text-align: center; 25 | width: 100%; 26 | } 27 | -------------------------------------------------------------------------------- /src/content/devices/multi/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | audio { 10 | margin: 0 0 1.5em 0; 11 | width: 100%; 12 | } 13 | 14 | div#sources > div { 15 | float: left; 16 | margin: 0 1em 0 0; 17 | width: calc(50% - 0.5em); 18 | } 19 | 20 | div#sources > div:last-of-type { 21 | margin: 0; 22 | } 23 | 24 | select { 25 | margin: 0 0 0.5em 0; 26 | } 27 | 28 | video { 29 | background: black; 30 | height: 234px; 31 | } 32 | -------------------------------------------------------------------------------- /src/content/getusermedia/source/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | Page move 14 | 15 | 16 |

The page has moved to: 17 | this page

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/capture/canvas-filter/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | canvas { 10 | background-color: #ccc; 11 | --width: calc(45%); 12 | width: var(--width); 13 | height: calc(var(--width) * 0.75); 14 | margin: 1em; 15 | vertical-align: top; 16 | } 17 | 18 | video { 19 | --width: calc(45%); 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | margin: 1em; 23 | object-fit: cover; 24 | } 25 | -------------------------------------------------------------------------------- /src/content/capture/canvas-video/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | canvas { 10 | background-color: #ccc; 11 | --width: calc(45%); 12 | width: var(--width); 13 | height: calc(var(--width) * 0.75); 14 | margin: 1em; 15 | vertical-align: top; 16 | } 17 | 18 | video { 19 | --width: calc(45%); 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | margin: 1em; 23 | object-fit: cover; 24 | } 25 | -------------------------------------------------------------------------------- /src/content/capture/worker-process/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | canvas { 10 | background-color: #ccc; 11 | --width: calc(45%); 12 | width: var(--width); 13 | height: calc(var(--width) * 0.75); 14 | margin: 1em; 15 | vertical-align: top; 16 | } 17 | 18 | video { 19 | --width: calc(45%); 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | margin: 1em; 23 | object-fit: cover; 24 | } 25 | -------------------------------------------------------------------------------- /src/content/capture/worker-process/js/worker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | onmessage = function(e) { 12 | const command = e.data[0]; 13 | if (command == 'stream') { 14 | const inputStream = e.data[1]; 15 | const transformStream = new TransformStream(); 16 | inputStream.pipeTo(transformStream.writable); 17 | postMessage(['response', transformStream.readable], 18 | [transformStream.readable]); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/content/capture/canvas-pc/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | canvas { 10 | background-color: #ccc; 11 | --width: calc(45%); 12 | width: var(--width); 13 | height: calc(var(--width) * 0.75); 14 | margin: 1em; 15 | vertical-align: top; 16 | } 17 | 18 | video { 19 | --width: calc(45%); 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | margin: 1em; 23 | object-fit: cover; 24 | } 25 | 26 | #autoplay { 27 | display: none; 28 | } 29 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | Page move 14 | 15 | 16 |

The page has moved to: 17 | this page

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/peerconnection/video-analyzer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | Page move 14 | 15 | 16 |

The page has moved to: 17 | this page

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/peerconnection/endtoend-encryption/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | Page move 14 | 15 | 16 |

The page has moved to: 17 | this page

18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/insertable-streams/endtoend-encryption/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 20px 10px 0 0; 11 | width: 100px; 12 | } 13 | 14 | div#buttons { 15 | margin: 0 0 20px 0; 16 | } 17 | 18 | div#status { 19 | height: 2em; 20 | margin: 1em 0 0 0; 21 | } 22 | 23 | input#audio { 24 | margin: 0 0.5em 0 0; 25 | position: relative; 26 | top: -1px; 27 | } 28 | 29 | video { 30 | --width: 45%; 31 | width: var(--width); 32 | height: calc(var(--width) * 0.75); 33 | } 34 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple-relay/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 20px 10px 0 0; 11 | width: 100px; 12 | } 13 | 14 | div#buttons { 15 | margin: 0 0 20px 0; 16 | } 17 | 18 | div#status { 19 | height: 2em; 20 | margin: 1em 0 0 0; 21 | } 22 | 23 | input#audio { 24 | margin: 0 0.5em 0 0; 25 | position: relative; 26 | top: -1px; 27 | } 28 | 29 | video { 30 | --width: 45%; 31 | width: var(--width); 32 | height: calc(var(--width) * 0.75); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/content/insertable-streams/webgpu/js/multi_video_worker.js: -------------------------------------------------------------------------------- 1 | importScripts('./multi_video_main.js'); 2 | 'use strict'; 3 | 4 | let mainTransform = null; 5 | 6 | /* global WebGPUTransform */ // defined in multi_video_main.js 7 | 8 | onmessage = async (event) => { 9 | const {operation} = event.data; 10 | if (operation === 'init') { 11 | mainTransform = new WebGPUTransform(); 12 | const {canvas} = event.data; 13 | const msg = await mainTransform.init(canvas); 14 | if (msg) { 15 | postMessage({error: msg}); 16 | } else { 17 | postMessage({result: 'Done'}); 18 | } 19 | } else if (operation === 'transform') { 20 | const {videoStream, gumStream} = event.data; 21 | mainTransform.transform(videoStream, gumStream); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/content/peerconnection/bandwidth/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | video { 9 | --width: 45%; 10 | width: var(--width); 11 | height: calc(var(--width) * 0.75); 12 | } 13 | 14 | button { 15 | margin: 0 20px 0 0; 16 | width: 96px; 17 | } 18 | 19 | video#localVideo { 20 | margin: 0 20px 20px 0; 21 | } 22 | 23 | div.label { 24 | display: inline-block; 25 | font-weight: 400; 26 | width: 120px; 27 | } 28 | 29 | div.graph-container { 30 | float: left; 31 | margin: 0.5em; 32 | width: calc(50% - 1em); 33 | } 34 | 35 | a#viewSource { 36 | clear: both; 37 | } 38 | -------------------------------------------------------------------------------- /src/content/peerconnection/per-frame-callback/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | video { 9 | --width: 45%; 10 | width: var(--width); 11 | height: calc(var(--width) * 0.75); 12 | } 13 | 14 | button { 15 | margin: 0 20px 0 0; 16 | width: 96px; 17 | } 18 | 19 | video#localVideo { 20 | margin: 0 20px 20px 0; 21 | } 22 | 23 | div.label { 24 | display: inline-block; 25 | font-weight: 400; 26 | width: 120px; 27 | } 28 | 29 | div.graph-container { 30 | float: left; 31 | margin: 0.5em; 32 | width: calc(50% - 1em); 33 | } 34 | 35 | a#viewSource { 36 | clear: both; 37 | } 38 | -------------------------------------------------------------------------------- /src/content/peerconnection/restart-ice/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 100%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0; 22 | } 23 | 24 | div#video > div { 25 | display: inline-block; 26 | margin: 0 5px 0 0; 27 | vertical-align: top; 28 | width: calc(50% - 22px); 29 | } 30 | 31 | @media screen and (max-width: 400px) { 32 | button { 33 | width: 83px; 34 | margin: 0 11px 10px 0; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: lint-and-test 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-node@v4 11 | - run: npm install 12 | - run: npm run eslint 13 | - run: npm run stylelint 14 | test: 15 | needs: lint 16 | runs-on: ubuntu-22.04 17 | timeout-minutes: 5 18 | strategy: 19 | matrix: 20 | browser: [chrome] 21 | version: [stable] 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: actions/setup-node@v4 25 | - run: npm install 26 | - run: sudo rm /usr/bin/chromedriver # remove preinstalled github chromedriver from $PATH 27 | - run: Xvfb :99 & 28 | - run: BROWSER=${{matrix.browser}} BVER=${{matrix.version}} DISPLAY=:99.0 npm run jest -- --retries=3 29 | -------------------------------------------------------------------------------- /src/content/capture/video-video/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 'use strict'; 9 | 10 | const leftVideo = document.getElementById('leftVideo'); 11 | const rightVideo = document.getElementById('rightVideo'); 12 | 13 | leftVideo.addEventListener('canplay', () => { 14 | let stream; 15 | const fps = 0; 16 | if (leftVideo.captureStream) { 17 | stream = leftVideo.captureStream(fps); 18 | } else if (leftVideo.mozCaptureStream) { 19 | stream = leftVideo.mozCaptureStream(fps); 20 | } else { 21 | console.error('Stream capture is not supported'); 22 | stream = null; 23 | } 24 | rightVideo.srcObject = stream; 25 | }); 26 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | .video { 10 | --width: 45%; 11 | width: var(--width); 12 | height: calc(var(--width) * 0.75); 13 | vertical-align: top; 14 | } 15 | 16 | .sourceVideo { 17 | margin: 0 20px 20px 0; 18 | } 19 | 20 | .sinkVideo { 21 | margin: 0 0 20px 0; 22 | } 23 | 24 | div.box { 25 | margin: 1em; 26 | } 27 | 28 | @media screen and (max-width: 400px) { 29 | .video { 30 | height: 90px; 31 | width: calc(50% - 7px); 32 | } 33 | .sourceVideo { 34 | margin: 0 10px 20px 0; 35 | } 36 | .sinkVideo { 37 | margin: 0 0 10px 0; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | margin: 0 0 20px 0; 19 | --width: 40%; 20 | width: var(--width); 21 | height: calc(var(--width) * 0.75); 22 | } 23 | 24 | video#video1 { 25 | margin: 0 20px 20px 0; 26 | } 27 | 28 | @media screen and (max-width: 400px) { 29 | button { 30 | margin: 0 11px 10px 0; 31 | } 32 | 33 | video { 34 | height: 90px; 35 | margin: 0 0 10px 0; 36 | width: calc(50% - 8px); 37 | } 38 | 39 | video#video1 { 40 | margin: 0 10px 10px 0; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/content/peerconnection/webaudio-output/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 0 20px 0 0; 11 | width: 83px; 12 | } 13 | 14 | button#hangupButton { 15 | margin: 0; 16 | } 17 | 18 | canvas { 19 | background-color: #666; 20 | vertical-align: top; 21 | --width: 45%; 22 | width: var(--width); 23 | height: calc(var(--width) * 0.75); 24 | } 25 | 26 | video { 27 | --width: 45%; 28 | width: var(--width); 29 | height: calc(var(--width) * 0.75); 30 | margin: 0 20px 20px 0; 31 | } 32 | 33 | video#remoteVideo { 34 | display: none; 35 | } 36 | 37 | @media screen and (max-width: 400px) { 38 | button { 39 | margin: 0 11px 10px 0; 40 | width: 83px; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/interop-tests.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | - cron: "30 5 * * *" 4 | 5 | jobs: 6 | interop: 7 | runs-on: ubuntu-22.04 8 | timeout-minutes: 5 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | browserA: [chrome, firefox] 13 | browserB: [firefox, chrome] 14 | bver: [unstable] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-node@v4 18 | - run: npm install 19 | - run: sudo rm /usr/bin/chromedriver /usr/bin/geckodriver # remove preinstalled github chromedriver/geckodriver from $PATH 20 | - run: Xvfb :99 & 21 | - run: BROWSER_A=${{matrix.browserA}} BROWSER_B=${{matrix.browserB}} BVER=${{matrix.bver}} DISPLAY=:99.0 node test/download-browsers.js 22 | - run: BROWSER_A=${{matrix.browserA}} BROWSER_B=${{matrix.browserB}} BVER=${{matrix.bver}} DISPLAY=:99.0 node_modules/.bin/jest --retries=3 test/interop/ 23 | -------------------------------------------------------------------------------- /src/content/peerconnection/create-offer/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | button { 10 | margin: 20px 10px 0 0; 11 | width: 100px; 12 | } 13 | 14 | div#constraints { 15 | margin: 0 0 20px 0; 16 | } 17 | 18 | div#numAudioTracks { 19 | margin: 0 0 20px 0; 20 | } 21 | 22 | div#constraints div { 23 | margin: 0 0 10px 0; 24 | } 25 | 26 | div#constraints input { 27 | margin: 0 10px 0 0; 28 | position: relative; 29 | top: -2px; 30 | } 31 | 32 | div#numAudioTracks input { 33 | max-width: 30%; 34 | position: relative; 35 | top: 2px; 36 | width: 200px; 37 | } 38 | 39 | label { 40 | font-weight: 500; 41 | margin: 0 10px 0 0; 42 | } 43 | 44 | textarea { 45 | height: 200px; 46 | width: 100%; 47 | } 48 | -------------------------------------------------------------------------------- /src/content/capture/canvas-record/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 3px 10px 0; 10 | padding-left: 2px; 11 | padding-right: 2px; 12 | width: 99px; 13 | } 14 | 15 | button:last-of-type { 16 | margin: 0; 17 | } 18 | 19 | p.borderBelow { 20 | margin: 0 0 20px 0; 21 | padding: 0 0 20px 0; 22 | } 23 | 24 | canvas { 25 | background-color: #ccc; 26 | --width: calc(45%); 27 | width: var(--width); 28 | height: calc(var(--width) * 0.75); 29 | margin: 1em; 30 | vertical-align: top; 31 | } 32 | 33 | video { 34 | --width: calc(45%); 35 | width: var(--width); 36 | height: calc(var(--width) * 0.75); 37 | margin: 1em; 38 | object-fit: cover; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/content/peerconnection/upgrade/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | @media screen and (max-width: 400px) { 30 | button { 31 | width: 83px; 32 | margin: 0 11px 10px 0; 33 | } 34 | 35 | video { 36 | height: 90px; 37 | margin: 0 0 10px 0; 38 | width: calc(50% - 7px); 39 | } 40 | video#localVideo { 41 | margin: 0 10px 20px 0; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/content/peerconnection/negotiate-timing/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | @media screen and (max-width: 400px) { 30 | button { 31 | width: 83px; 32 | margin: 0 11px 10px 0; 33 | } 34 | 35 | video { 36 | height: 90px; 37 | margin: 0 0 10px 0; 38 | width: calc(50% - 7px); 39 | } 40 | video#localVideo { 41 | margin: 0 10px 20px 0; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/content/peerconnection/webaudio-input/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | audio { 10 | margin: 0 0 20px 0; 11 | width: 50%; 12 | } 13 | 14 | button { 15 | margin: 0 20px 20px 0; 16 | width: 89px; 17 | } 18 | 19 | div#options { 20 | margin: 0 0 20px 0; 21 | } 22 | 23 | div#status { 24 | background-color: #eee; 25 | margin: 0 0 20px 0; 26 | min-height: 140px; 27 | overflow-y: scroll; 28 | padding: 0 0 0 10px; 29 | width: 50%; 30 | } 31 | 32 | input[type='checkbox'] { 33 | margin: 0 10px 0 0; 34 | position: relative; 35 | top: -2px; 36 | } 37 | 38 | label { 39 | font-weight: 400; 40 | } 41 | 42 | li { 43 | margin: 0 0 10px 0; 44 | } 45 | 46 | ul { 47 | list-style-type: square; 48 | padding: 0 0 0 18px; 49 | } 50 | -------------------------------------------------------------------------------- /src/content/datachannel/basic/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 1em 1em 0; 10 | width: 90px; 11 | } 12 | div#buttons { 13 | margin: 0 0 1em 0; 14 | } 15 | div#send { 16 | margin: 0 20px 1em 0; 17 | } 18 | div#sendReceive { 19 | border-bottom: 1px solid #eee; 20 | margin: 0; 21 | padding: 0 0 10px 0; 22 | } 23 | div#sendReceive > div { 24 | display: inline-block; 25 | width: calc(50% - 20px); 26 | } 27 | form { 28 | margin: 0 0 1em 0; 29 | white-space: nowrap; 30 | } 31 | form span { 32 | font-weight: 300; 33 | margin: 0 1em 0 0; 34 | white-space: normal; 35 | } 36 | textarea { 37 | color: #444; 38 | font-size: 0.9em; 39 | font-weight: 300; 40 | height: 7.0em; 41 | padding: 5px; 42 | width: calc(100% - 10px); 43 | } 44 | -------------------------------------------------------------------------------- /src/content/datachannel/channel/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 1em 1em 0; 10 | width: 90px; 11 | } 12 | div#buttons { 13 | margin: 0 0 1em 0; 14 | } 15 | div#send { 16 | margin: 0 20px 1em 0; 17 | } 18 | div#sendReceive { 19 | border-bottom: 1px solid #eee; 20 | margin: 0; 21 | padding: 0 0 10px 0; 22 | } 23 | div#sendReceive > div { 24 | display: inline-block; 25 | width: calc(50% - 20px); 26 | } 27 | form { 28 | margin: 0 0 1em 0; 29 | white-space: nowrap; 30 | } 31 | form span { 32 | font-weight: 300; 33 | margin: 0 1em 0 0; 34 | white-space: normal; 35 | } 36 | textarea { 37 | color: #444; 38 | font-size: 0.9em; 39 | font-weight: 300; 40 | height: 7.0em; 41 | padding: 5px; 42 | width: calc(100% - 10px); 43 | } 44 | -------------------------------------------------------------------------------- /src/content/peerconnection/pc1/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/peerconnection/channel/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/peerconnection/change-codecs/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-analyzer/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/peerconnection/perfect-negotiation/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/content/peerconnection/audio/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | audio { 9 | display: inline-block; 10 | position: relative; 11 | top: 9px; 12 | width: calc(100% - 120px); 13 | } 14 | 15 | button { 16 | margin: 0 20px 0 0; 17 | width: 96px; 18 | } 19 | 20 | table { 21 | border-collapse: collapse; 22 | } 23 | 24 | th, td { 25 | border: 1px solid black; 26 | } 27 | 28 | tr:hover { 29 | background-color: #f5f5f5; 30 | } 31 | 32 | div#audio { 33 | margin: 0 0 29px 0; 34 | } 35 | 36 | div#audio > div { 37 | margin: 0 0 20px 0; 38 | } 39 | 40 | div.label { 41 | display: inline-block; 42 | font-weight: 400; 43 | width: 120px; 44 | } 45 | 46 | div.graph-container { 47 | float: left; 48 | margin: 0.5em; 49 | width: calc(50% - 1em); 50 | } 51 | 52 | a#viewSource { 53 | clear: both; 54 | } 55 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-crop/js/worker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | function transform(frame, controller) { 12 | // Cropping from an existing video frame is supported by the API in Chrome 94+. 13 | const newFrame = new VideoFrame(frame, { 14 | visibleRect: { 15 | x: 320, 16 | width: 640, 17 | y: 180, 18 | height: 360, 19 | } 20 | }); 21 | controller.enqueue(newFrame); 22 | frame.close(); 23 | } 24 | 25 | onmessage = async (event) => { 26 | const {operation} = event.data; 27 | if (operation === 'crop') { 28 | const {readable, writable} = event.data; 29 | readable 30 | .pipeThrough(new TransformStream({transform})) 31 | .pipeTo(writable); 32 | } else { 33 | console.error('Unknown operation', operation); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/content/peerconnection/states/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 20px 0; 10 | width: 86.5px; 11 | } 12 | div#buttons { 13 | border-bottom: 1px solid #eee; 14 | margin: 0 0 20px 0; 15 | } 16 | div#states { 17 | border-bottom: 1px solid #eee; 18 | } 19 | div#states > div { 20 | margin: 0 0 20px 0; 21 | min-height: 24px; /* to cope with Unicode character size :^| */ 22 | } 23 | div.label { 24 | display: inline-block; 25 | font-weight: 400; 26 | width: 111px; 27 | } 28 | div.value { 29 | display: inline-block; 30 | } 31 | video { 32 | margin: 0 0 20px 0; 33 | --width: 45%; 34 | width: var(--width); 35 | height: calc(var(--width) * 0.75); 36 | } 37 | video#video1 { 38 | margin: 0 20px 20px 0; 39 | } 40 | 41 | @media screen and (min-width: 730px) { 42 | video { 43 | height: 231px; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/content/extensions/svc/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 0 0; 10 | width: 83px; 11 | } 12 | 13 | button#hangupButton { 14 | margin: 0; 15 | } 16 | 17 | video { 18 | --width: 45%; 19 | width: var(--width); 20 | height: calc(var(--width) * 0.75); 21 | margin: 0 0 20px 0; 22 | vertical-align: top; 23 | } 24 | 25 | video#localVideo { 26 | margin: 0 20px 20px 0; 27 | } 28 | 29 | div.box { 30 | margin: 1em; 31 | } 32 | 33 | @media screen and (max-width: 400px) { 34 | button { 35 | width: 83px; 36 | margin: 0 11px 10px 0; 37 | } 38 | 39 | video { 40 | height: 90px; 41 | margin: 0 0 10px 0; 42 | width: calc(50% - 7px); 43 | } 44 | video#localVideo { 45 | margin: 0 10px 20px 0; 46 | } 47 | 48 | } 49 | 50 | 51 | div.graph-container { 52 | float: left; 53 | margin: 0.5em; 54 | width: calc(50% - 1em); 55 | } 56 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # WebRTC welcomes patches/pulls for features and bug fixes! 2 | 3 | For contributors external to Google, follow the instructions given in the 4 | [Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual). 5 | In all cases, contributors must sign a contributor license agreement before a contribution can be accepted. 6 | Please complete the agreement for an [individual](https://developers.google.com/open-source/cla/individual) or 7 | a [corporation](https://developers.google.com/open-source/cla/corporate) as appropriate. 8 | 9 | If you plan to add a new sample or make significant changes to an existing sample, we recommend that you start by creating 10 | a [new issue](https://github.com/webrtc/samples/issues/new) where we can discuss the details. 11 | 12 | # How to start developing a patch, new feature or bug fix 13 | ## Clone the repo in desired folder 14 | ```bash 15 | git clone https://github.com/webrtc/samples.git 16 | ``` 17 | 18 | ## Install npm dependencies 19 | ```bash 20 | npm install 21 | ``` 22 | 23 | ## Start web server for development 24 | ```bash 25 | npm start 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebRTC Code Samples 2 | 3 | This is a repository for the WebRTC JavaScript code samples. All of the samples can be tested from [webrtc.github.io/samples](https://webrtc.github.io/samples). 4 | 5 | To run the samples locally 6 | ``` 7 | npm install && npm start 8 | ``` 9 | and open your browser on the page indicated. 10 | 11 | ## Contributing 12 | We welcome contributions and bugfixes. Please see [CONTRIBUTING.md](https://github.com/webrtc/samples/blob/gh-pages/CONTRIBUTING.md) for details. 13 | 14 | ## Bugs 15 | 16 | If you encounter a bug or problem with one of the samples, please submit a 17 | [new issue](https://github.com/webrtc/samples/issues/new) so we know about it and can fix it. 18 | 19 | Please avoid submitting issues on this repository for general problems you have with WebRTC. If you have found a bug in 20 | the WebRTC APIs, please see [webrtc.org/bugs](https://webrtc.org/support/bug-reporting) for how to submit bugs on the affected browsers. 21 | If you need support on how to implement your own WebRTC-based application, please see the 22 | [discuss-webrtc](https://groups.google.com/forum/#!forum/discuss-webrtc) Google Group. 23 | 24 | -------------------------------------------------------------------------------- /src/content/getusermedia/volume/js/volume-meter-processor.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // This class is used to compute the volume of the input audio stream. 12 | class VolumeMeterProcessor extends AudioWorkletProcessor { 13 | constructor() { 14 | super(); 15 | this._lastUpdate = Date.now(); 16 | } 17 | process(inputs) { 18 | // This example only supports mono channel. 19 | const input = inputs[0][0]; 20 | if (!input) { 21 | return true; 22 | } 23 | let sum = 0.0; 24 | let clipcount = 0; 25 | for (let i = 0; i < input.length; ++i) { 26 | sum += input[i] * input[i]; 27 | if (Math.abs(input[i]) > 0.99) { 28 | clipcount += 1; 29 | } 30 | } 31 | const instant = Math.sqrt(sum / input.length); 32 | this.port.postMessage({ 33 | instant: instant, 34 | clip: clipcount / input.length, 35 | }); 36 | return true; 37 | } 38 | } 39 | 40 | registerProcessor('volume-meter-processor', VolumeMeterProcessor); 41 | -------------------------------------------------------------------------------- /src/content/getusermedia/record/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 3px 10px 0; 10 | padding-left: 2px; 11 | padding-right: 2px; 12 | width: 120px; 13 | } 14 | 15 | /* copied from main button style */ 16 | .file-upload { 17 | background-color: #d84a38; 18 | border: none; 19 | border-radius: 2px; 20 | color: white; 21 | font-family: 'Roboto', sans-serif; 22 | font-size: 0.8em; 23 | margin: 0 0 1em 0; 24 | padding: 0.5em 0.7em 0.6em 0.7em; 25 | } 26 | 27 | .file-upload:hover { 28 | background-color: #cf402f; 29 | } 30 | 31 | button:last-of-type { 32 | margin: 0; 33 | } 34 | 35 | p.borderBelow { 36 | margin: 0 0 20px 0; 37 | padding: 0 0 20px 0; 38 | } 39 | 40 | video { 41 | vertical-align: top; 42 | --width: 25vw; 43 | width: var(--width); 44 | height: calc(var(--width) * 0.5625); 45 | } 46 | 47 | video:last-of-type { 48 | margin: 0 0 20px 0; 49 | } 50 | 51 | video#gumVideo { 52 | margin: 0 20px 20px 0; 53 | } 54 | 55 | input[type="file"] { 56 | display: none; 57 | } 58 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'extends': 'google', 3 | 'parserOptions': { 4 | 'ecmaVersion': 2017, 5 | 'sourceType': 'module', 6 | }, 7 | 'env': { 8 | 'browser': true, 9 | 'es6': true, 10 | 'node': true, 11 | 'jest': true 12 | }, 13 | 'rules': { 14 | 'max-len': 'off', 15 | 'require-jsdoc': 'off', 16 | 'arrow-parens': 'off', 17 | 'comma-dangle': 'off', 18 | 'no-throw-literal': 'off', 19 | 'camelcase': 'off', 20 | 'prefer-rest-params': 'off', 21 | 'no-invalid-this': 'off', 22 | 'eol-last': 'off', 23 | 'no-undef': 2, 24 | }, 25 | "globals": { 26 | "adapter": true, 27 | "browserSupportsIPHandlingPolicy": true, 28 | "browserSupportsNonProxiedUdpBoolean": true, 29 | "chrome": true, 30 | "ga": true, 31 | "getPolicyFromBooleans": true, 32 | "importScripts": true, 33 | // From WebGPU specification 34 | "GPUBufferUsage": true, 35 | "GPUTextureUsage": true, 36 | // From Streams specification 37 | "TransformStream": true, 38 | // From WebCodec specification 39 | "AudioData": true, 40 | "AudioEncoder": true, 41 | "AudioDecoder": true, 42 | "VideoFrame": true, 43 | "VideoEncoder": true, 44 | "VideoDecoder": true, 45 | }, 46 | "plugins": ["jest"] 47 | }; 48 | -------------------------------------------------------------------------------- /src/content/getusermedia/canvas/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // Put variables in global scope to make them available to the browser console. 12 | const video = document.querySelector('video'); 13 | const canvas = window.canvas = document.querySelector('canvas'); 14 | canvas.width = 480; 15 | canvas.height = 360; 16 | 17 | const button = document.querySelector('button'); 18 | button.onclick = function() { 19 | canvas.width = video.videoWidth; 20 | canvas.height = video.videoHeight; 21 | canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height); 22 | }; 23 | 24 | const constraints = { 25 | audio: false, 26 | video: true 27 | }; 28 | 29 | function handleSuccess(stream) { 30 | window.stream = stream; // make stream available to browser console 31 | video.srcObject = stream; 32 | } 33 | 34 | function handleError(error) { 35 | console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name); 36 | } 37 | 38 | navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError); 39 | -------------------------------------------------------------------------------- /test/steps.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | const TIMEOUT = 10000; 9 | 10 | function step(drivers, cb, logMessage) { 11 | return Promise.all(drivers.map(driver => { 12 | return cb(driver); 13 | })).then(() => { 14 | if (logMessage) { 15 | console.log(logMessage); 16 | } 17 | }); 18 | } 19 | function waitNVideosExist(driver, n) { 20 | return driver.wait(() => { 21 | return driver.executeScript(n => document.querySelectorAll('video').length === n, n); 22 | }, TIMEOUT); 23 | } 24 | 25 | function waitAllVideosHaveEnoughData(driver) { 26 | return driver.wait(() => { 27 | return driver.executeScript(() => { 28 | const videos = document.querySelectorAll('video'); 29 | let ready = 0; 30 | for (let i = 0; i < videos.length; i++) { 31 | if (videos[i].readyState >= videos[i].HAVE_ENOUGH_DATA) { 32 | ready++; 33 | } 34 | } 35 | return ready === videos.length; 36 | }); 37 | }, TIMEOUT); 38 | } 39 | 40 | module.exports = { 41 | step, 42 | waitNVideosExist, 43 | waitAllVideosHaveEnoughData, 44 | }; 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | # Please read first! 10 | Please use [discuss-webrtc](https://groups.google.com/forum/#!forum/discuss-webrtc) for general technical discussions and questions. 11 | If you have found an issue/bug with the native `libwebrtc` SDK or a browser's behaviour around WebRTC please create an issue in the relevant bug tracker. You can find more information on how to submit a bug and do so in the right place [here](https://webrtc.googlesource.com/src/+/refs/heads/main/docs/bug-reporting.md) 12 | 13 | - [ ] I understand that issues created here are _only_ relevant to the samples in this repo - not browser or SDK bugs 14 | - [ ] I have provided steps to reproduce 15 | - [ ] I have provided browser name and version 16 | - [ ] I have provided a link to the sample here or a modified version thereof 17 | 18 | **Note: If the checkboxes above are not checked (which you do after the issue is posted), the issue will be closed.** 19 | 20 | ## Browser affected 21 | 22 | **Browser name including version (e.g. Chrome 64.0.3282.119)** 23 | 24 | 25 | ## Description 26 | 27 | 28 | ## Steps to reproduce 29 | 30 | 31 | ## Expected results 32 | 33 | 34 | ## Actual results 35 | 36 | -------------------------------------------------------------------------------- /src/content/getusermedia/audio/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // Put variables in global scope to make them available to the browser console. 12 | const audio = document.querySelector('audio'); 13 | 14 | const constraints = window.constraints = { 15 | audio: true, 16 | video: false 17 | }; 18 | 19 | function handleSuccess(stream) { 20 | const audioTracks = stream.getAudioTracks(); 21 | console.log('Got stream with constraints:', constraints); 22 | console.log('Using audio device: ' + audioTracks[0].label); 23 | stream.oninactive = function() { 24 | console.log('Stream ended'); 25 | }; 26 | window.stream = stream; // make variable available to browser console 27 | audio.srcObject = stream; 28 | } 29 | 30 | function handleError(error) { 31 | const errorMessage = 'navigator.MediaDevices.getUserMedia error: ' + error.message + ' ' + error.name; 32 | document.getElementById('errorMsg').innerText = errorMessage; 33 | console.log(errorMessage); 34 | } 35 | 36 | navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError); 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webrtc-samples", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Project checking for WebRTC GitHub samples repo", 6 | "keywords": [ 7 | "webrtc" 8 | ], 9 | "homepage": "https://webrtc.github.io/samples/", 10 | "bugs": { 11 | "url": "https://github.com/webrtc/samples/issues" 12 | }, 13 | "license": "BSD-3-Clause", 14 | "author": "The WebRTC project authors", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/webrtc/samples.git" 18 | }, 19 | "scripts": { 20 | "start": "http-server . -c-1 -a 127.0.0.1", 21 | "test": "npm run eslint && npm run stylelint", 22 | "eslint": "eslint 'test/**.js' 'src/content/**/*.js'", 23 | "jest": "node test/download-browsers.js && jest --testTimeout 5000 --maxWorkers=1 src/content/", 24 | "stylelint": "stylelint 'src/**/*.css'" 25 | }, 26 | "eslintIgnore": [ 27 | "'**/third_party/*.js'" 28 | ], 29 | "devDependencies": { 30 | "@puppeteer/browsers": "^2.2.0", 31 | "eslint": "^8.9.0", 32 | "eslint-config-google": "^0.14.0", 33 | "eslint-plugin-jest": "^27.4.0", 34 | "http-server": "^14.1.0", 35 | "jest": "^29.7.0", 36 | "selenium-webdriver": "^4.19.0", 37 | "stylelint": "^14.5.3", 38 | "stylelint-config-recommended": "^7.0.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/content/getusermedia/gum/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 'use strict'; 10 | 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/getusermedia/gum/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('getUserMedia', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('opens a camera', async () => { 31 | await driver.findElement(webdriver.By.css('button')).click(); 32 | await driver.wait(() => driver.executeScript(() => 33 | document.querySelector('video').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA) 34 | ); 35 | const width = await driver.findElement(webdriver.By.css('video')).getAttribute('videoWidth'); 36 | expect(width >>> 0).toBeGreaterThan(320); 37 | }); 38 | }); 39 | 40 | -------------------------------------------------------------------------------- /src/content/peerconnection/trickle-ice/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 20px 10px 0 0; 10 | width: 130px; 11 | } 12 | button#gather { 13 | display: block; 14 | } 15 | section#iceServers input { 16 | margin: 0 0 10px; 17 | width: 260px; 18 | } 19 | section#iceConstraints label { 20 | margin: 0 1em 0 0; 21 | } 22 | section#iceServers label { 23 | display: inline-block; 24 | width: 150px; 25 | } 26 | 27 | section#iceOptions label { 28 | display: inline-block; 29 | width: 200px; 30 | } 31 | 32 | select#servers { 33 | font-size: 1em; 34 | padding: 5px; 35 | width: 420px; 36 | } 37 | div#iceTransports span { 38 | margin: 0 1em 0 0; 39 | } 40 | table#candidates { 41 | font-size: 0.7em; 42 | overflow-y: auto; 43 | text-align: right; 44 | width: 100%; 45 | } 46 | th { 47 | font-weight: bold; 48 | } 49 | th:nth-child(3),td:nth-child(3) { 50 | text-align: left 51 | } 52 | th:nth-child(6),td:nth-child(6) { 53 | text-align: left 54 | } 55 | 56 | .gray { 57 | color: gray 58 | } 59 | 60 | #poolValue { 61 | display: inline-block; 62 | width: 30px 63 | } 64 | 65 | #getUserMediaPermissions { 66 | display: none; 67 | } 68 | 69 | #error-note { 70 | display: none; 71 | } 72 | -------------------------------------------------------------------------------- /src/content/getusermedia/filter/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const snapshotButton = document.querySelector('button#snapshot'); 12 | const filterSelect = document.querySelector('select#filter'); 13 | 14 | // Put variables in global scope to make them available to the browser console. 15 | const video = window.video = document.querySelector('video'); 16 | const canvas = window.canvas = document.querySelector('canvas'); 17 | canvas.width = 480; 18 | canvas.height = 360; 19 | 20 | snapshotButton.onclick = function() { 21 | canvas.className = filterSelect.value; 22 | canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height); 23 | }; 24 | 25 | filterSelect.onchange = function() { 26 | video.className = filterSelect.value; 27 | }; 28 | 29 | const constraints = { 30 | audio: false, 31 | video: true 32 | }; 33 | 34 | function handleSuccess(stream) { 35 | window.stream = stream; // make stream available to browser console 36 | video.srcObject = stream; 37 | } 38 | 39 | function handleError(error) { 40 | console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name); 41 | } 42 | 43 | navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError); 44 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/simple-transforms.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Does nothing. 13 | * @implements {FrameTransform} in pipeline.js 14 | */ 15 | class NullTransform { // eslint-disable-line no-unused-vars 16 | /** @override */ 17 | async init() {} 18 | /** @override */ 19 | async transform(frame, controller) { 20 | controller.enqueue(frame); 21 | } 22 | /** @override */ 23 | destroy() {} 24 | } 25 | 26 | /** 27 | * Drops frames at random. 28 | * @implements {FrameTransform} in pipeline.js 29 | */ 30 | class DropTransform { // eslint-disable-line no-unused-vars 31 | /** @override */ 32 | async init() {} 33 | /** @override */ 34 | async transform(frame, controller) { 35 | if (Math.random() < 0.5) { 36 | controller.enqueue(frame); 37 | } else { 38 | frame.close(); 39 | } 40 | } 41 | /** @override */ 42 | destroy() {} 43 | } 44 | 45 | /** 46 | * Delays all frames by 100ms. 47 | * @implements {FrameTransform} in pipeline.js 48 | */ 49 | class DelayTransform { // eslint-disable-line no-unused-vars 50 | /** @override */ 51 | async init() {} 52 | /** @override */ 53 | async transform(frame, controller) { 54 | await new Promise(resolve => setTimeout(resolve, 100)); 55 | controller.enqueue(frame); 56 | } 57 | /** @override */ 58 | destroy() {} 59 | } 60 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, The WebRTC project authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the 13 | distribution. 14 | 15 | * Neither the name of Google nor the names of its contributors may 16 | be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/content/datachannel/filetransfer/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/datachannel/filetransfer/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('datachannel filetransfer', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('transfers a file', async () => { 31 | await driver.findElement(webdriver.By.id('fileInput')) 32 | .sendKeys(process.cwd() + '/src/content/devices/multi/images/poster.jpg'); 33 | await driver.wait(() => driver.findElement(webdriver.By.id('sendFile')).isEnabled()); 34 | await driver.findElement(webdriver.By.id('sendFile')).click(); 35 | 36 | // the remote connection gets closed when it is done. 37 | await driver.wait(() => driver.executeScript(() => { 38 | return pc2 === null; // eslint-disable-line no-undef 39 | })); 40 | await driver.wait(() => driver.findElement(webdriver.By.id('download')).isEnabled()); 41 | }); 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /src/content/insertable-streams/endtoend-encryption/js/videopipe.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | // 9 | // A "videopipe" abstraction on top of WebRTC. 10 | // 11 | // The usage of this abstraction: 12 | // var pipe = new VideoPipe(mediastream, handlerFunction); 13 | // handlerFunction = function(MediaStreamTrackEvent) { 14 | // do_something 15 | // } 16 | // pipe.close(); 17 | // 18 | // The VideoPipe will set up 2 PeerConnections, connect them to each 19 | // other, and call HandlerFunction when the stream's track is available 20 | // in the second PeerConnection. 21 | // 22 | 'use strict'; 23 | 24 | function VideoPipe(stream, forceSend, forceReceive, handler) { 25 | this.pc1 = new RTCPeerConnection(); 26 | this.pc2 = new RTCPeerConnection(); 27 | this.pc2.ontrack = handler; 28 | stream.getTracks().forEach((track) => this.pc1.addTrack(track, stream)); 29 | } 30 | 31 | VideoPipe.prototype.negotiate = async function() { 32 | this.pc1.onicecandidate = e => this.pc2.addIceCandidate(e.candidate); 33 | this.pc2.onicecandidate = e => this.pc1.addIceCandidate(e.candidate); 34 | 35 | await this.pc1.setLocalDescription(); 36 | await this.pc2.setRemoteDescription(this.pc1.localDescription); 37 | await this.pc2.setLocalDescription(); 38 | await this.pc1.setRemoteDescription(this.pc2.localDescription); 39 | }; 40 | 41 | VideoPipe.prototype.close = function() { 42 | this.pc1.close(); 43 | this.pc2.close(); 44 | }; 45 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-crop/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /* global MediaStreamTrackProcessor, MediaStreamTrackGenerator */ 12 | if (typeof MediaStreamTrackProcessor === 'undefined' || 13 | typeof MediaStreamTrackGenerator === 'undefined') { 14 | alert( 15 | 'Your browser does not support the experimental MediaStreamTrack API ' + 16 | 'for Insertable Streams of Media. See the note at the bottom of the ' + 17 | 'page.'); 18 | } 19 | 20 | const startButton = document.getElementById('startButton'); 21 | const localVideo = document.getElementById('localVideo'); 22 | const croppedVideo = document.getElementById('croppedVideo'); 23 | 24 | const worker = new Worker('./js/worker.js', {name: 'Crop worker'}); 25 | startButton.addEventListener('click', async () => { 26 | const stream = await navigator.mediaDevices.getUserMedia({video: {width: 1280, height: 720}}); 27 | localVideo.srcObject = stream; 28 | 29 | const [track] = stream.getTracks(); 30 | const processor = new MediaStreamTrackProcessor({track}); 31 | const {readable} = processor; 32 | 33 | const generator = new MediaStreamTrackGenerator({kind: 'video'}); 34 | const {writable} = generator; 35 | croppedVideo.srcObject = new MediaStream([generator]); 36 | 37 | worker.postMessage({ 38 | operation: 'crop', 39 | readable, 40 | writable, 41 | }, [readable, writable]); 42 | }); 43 | -------------------------------------------------------------------------------- /src/content/devices/input-output/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 'use strict'; 10 | 11 | const seleniumHelpers = require('../../../../../test/webdriver'); 12 | 13 | let driver; 14 | const path = '/src/content/devices/input-output/index.html'; 15 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 16 | 17 | describe('input-output', () => { 18 | beforeAll(async () => { 19 | driver = await seleniumHelpers.buildDriver(); 20 | }); 21 | afterAll(() => { 22 | return driver.quit(); 23 | }); 24 | 25 | beforeEach(() => { 26 | return driver.get(url); 27 | }); 28 | 29 | it('shows at least one audio input device', async () => { 30 | await driver.wait(driver.executeScript(() => { 31 | return document.getElementById('audioSource').childElementCount > 0; 32 | })); 33 | }); 34 | 35 | it('shows at least one video input device', async () => { 36 | await driver.wait(driver.executeScript(() => { 37 | return document.getElementById('videoSource').childElementCount > 0; 38 | })); 39 | }); 40 | 41 | it('shows at least one audio output device device', async function() { 42 | if (process.env.BROWSER === 'firefox') { 43 | this.skip(); 44 | } 45 | await driver.wait(driver.executeScript(() => { 46 | return document.getElementById('audioOutput').childElementCount > 0; 47 | })); 48 | }); 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /src/content/peerconnection/audio/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 'use strict'; 10 | const webdriver = require('selenium-webdriver'); 11 | const seleniumHelpers = require('../../../../../test/webdriver'); 12 | 13 | let driver; 14 | const path = '/src/content/peerconnection/audio/index.html'; 15 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 16 | 17 | describe('audio-only peerconnection', () => { 18 | beforeAll(async () => { 19 | driver = await seleniumHelpers.buildDriver(); 20 | }); 21 | afterAll(() => { 22 | return driver.quit(); 23 | }); 24 | 25 | beforeEach(() => { 26 | return driver.get(url); 27 | }); 28 | 29 | it('establishes a connection', async () => { 30 | await driver.findElement(webdriver.By.id('callButton')).click(); 31 | await driver.wait(() => driver.executeScript(() => { 32 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 33 | })); 34 | await driver.wait(() => driver.executeScript(() => { 35 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 36 | })); 37 | await driver.wait(() => driver.executeScript(() => { 38 | return document.getElementById('audio2').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 39 | })); 40 | }); 41 | 42 | // TODO: assert usage of different codecs via getStats. 43 | }); 44 | 45 | -------------------------------------------------------------------------------- /src/content/insertable-streams/webgpu/js/multi_video_worker_manager.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | let worker; 5 | let screenCanvas; 6 | 7 | // eslint-disable-next-line no-unused-vars 8 | class WebGPUWorker { 9 | async init() { 10 | screenCanvas = document.createElement('canvas'); 11 | document.getElementById('outputVideo').append(screenCanvas); 12 | screenCanvas.width = 960; 13 | screenCanvas.height = 540; 14 | 15 | worker = new Worker('./js/multi_video_worker.js'); 16 | console.log('Created a worker thread.'); 17 | const offScreen = screenCanvas.transferControlToOffscreen(); 18 | 19 | const onMessage = new Promise((resolve, reject) => { 20 | worker.addEventListener('message', function handleMsgFromWorker(msg) { 21 | if (msg.data.error) { 22 | document.getElementById('errorMsg').innerText = msg.data.error; 23 | reject(msg.data.error); 24 | } 25 | if (msg.data.result === 'Done') { 26 | resolve(); 27 | } 28 | }); 29 | }); 30 | worker.postMessage( 31 | { 32 | operation: 'init', 33 | canvas: offScreen, 34 | }, [offScreen]); 35 | 36 | await onMessage; 37 | } 38 | 39 | transform(videoStream, gumStream) { 40 | if (videoStream && gumStream) { 41 | worker.postMessage( 42 | { 43 | operation: 'transform', 44 | videoStream: videoStream, 45 | gumStream: gumStream, 46 | }, [videoStream, gumStream]); 47 | } 48 | } 49 | 50 | destroy() { 51 | if (screenCanvas.parentNode) { 52 | screenCanvas.parentNode.removeChild(screenCanvas); 53 | } 54 | worker.terminate(); 55 | console.log('Worker thread destroyed.'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/content/capture/canvas-filter/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const source = document.querySelector('#source'); 12 | // TODO(hta): Use OffscreenCanvas for the intermediate canvases. 13 | const canvasIn = document.querySelector('#canvas-source'); 14 | const canvasOut = document.querySelector('#canvas-result'); 15 | const result = document.querySelector('#result'); 16 | 17 | const stream = canvasOut.captureStream(); 18 | let inputStream = null; 19 | let imageData = null; 20 | 21 | result.srcObject = stream; 22 | 23 | function loop() { 24 | if (source.videoWidth > 0 && source.videoHeight > 0) { 25 | canvasIn.width = source.videoWidth; 26 | canvasIn.height = source.videoHeight; 27 | const ctx = canvasIn.getContext('2d'); 28 | ctx.drawImage(source, 0, 0); 29 | // Put a red square into the image, to mark it as "processed". 30 | ctx.fillStyle = '#FF0000'; 31 | ctx.fillRect(10, 10, 80, 80); 32 | imageData = ctx.getImageData(0, 0, canvasIn.width, canvasIn.height); 33 | // At this point, we have data that can be transferred. 34 | // We paint it on the second canvas. 35 | canvasOut.width = source.videoWidth; 36 | canvasOut.height = source.videoHeight; 37 | const outCtx = canvasOut.getContext('2d'); 38 | outCtx.putImageData(imageData, 0, 0); 39 | } 40 | window.requestAnimationFrame(loop); 41 | } 42 | 43 | (async () => { 44 | inputStream = await navigator.mediaDevices.getUserMedia({video: true}); 45 | source.srcObject = inputStream; 46 | source.play(); 47 | result.play(); 48 | window.requestAnimationFrame(loop); 49 | })(); 50 | -------------------------------------------------------------------------------- /src/content/getusermedia/volume/js/soundmeter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // Meter class that generates a number correlated to audio volume. 12 | // The meter class itself displays nothing, but it makes the 13 | // instantaneous and time-decaying volumes available for inspection. 14 | // It also reports on the fraction of samples that were at or near 15 | // the top of the measurement range. 16 | function SoundMeter(context) { 17 | this.context = context; 18 | this.instant = 0.0; 19 | this.slow = 0.0; 20 | this.clip = 0.0; 21 | this.node = null; 22 | } 23 | 24 | SoundMeter.prototype.connectToSource = async function(stream, callback) { 25 | console.log('SoundMeter connecting'); 26 | try { 27 | await this.context.audioWorklet.addModule('js/volume-meter-processor.js'); 28 | this.mic = this.context.createMediaStreamSource(stream); 29 | this.node = new AudioWorkletNode(this.context, 'volume-meter-processor'); 30 | this.node.port.onmessage = (event) => { 31 | const {instant, clip} = event.data; 32 | this.instant = instant; 33 | this.clip = clip; 34 | this.slow = 0.95 * this.slow + 0.05 * this.instant; 35 | }; 36 | this.mic.connect(this.node); 37 | if (typeof callback !== 'undefined') { 38 | callback(null); 39 | } 40 | } catch (e) { 41 | console.error(e); 42 | if (typeof callback !== 'undefined') { 43 | callback(e); 44 | } 45 | } 46 | }; 47 | 48 | SoundMeter.prototype.stop = function() { 49 | console.log('SoundMeter stopping'); 50 | this.mic.disconnect(); 51 | this.node.disconnect(); 52 | }; 53 | -------------------------------------------------------------------------------- /src/content/peerconnection/dtmf/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 20px 0; 10 | width: 96px; 11 | } 12 | 13 | div#dialPad button { 14 | background-color: #ddd; 15 | border: 1px solid #ccc; 16 | color: black; 17 | font-size: 1em; 18 | font-weight: 400; 19 | height: 40px; 20 | margin: 0 10px 10px 0; 21 | width: 40px; 22 | } 23 | 24 | div#dialPad button:hover { 25 | background-color: #aaa; 26 | } 27 | 28 | div#dialPad button:active { 29 | background-color: #888; 30 | } 31 | 32 | div#dialPad { 33 | display: inline-block; 34 | margin: 0 20px 20px 0; 35 | vertical-align: top; 36 | } 37 | 38 | div#parameters { 39 | margin: 0 0 25px 0; 40 | } 41 | 42 | div#parameters > div { 43 | height: 28px; 44 | margin: 0 0 10px 0; 45 | } 46 | 47 | div#dtmf { 48 | background-color: #eee; 49 | display: inline-block; 50 | height: 180px; 51 | margin: 0 0 20px 0; 52 | padding: 5px 5px 5px 10px; 53 | width: calc(100% - 239px); 54 | } 55 | 56 | div#dtmf div { 57 | font-family: 'Inconsolata', 'Courier New', monospace; 58 | } 59 | 60 | div#sentTones { 61 | display: inline-block; 62 | line-height: 1.2em; 63 | } 64 | 65 | div#dtmfStatus { 66 | margin: 0 0 10px 0; 67 | } 68 | 69 | div#parameters input[type = range] { 70 | font-size: 1em; 71 | width: 85px; 72 | } 73 | 74 | div#parameters input#tones { 75 | width: calc(100% - 78px); 76 | } 77 | 78 | div#parameters label { 79 | display: inline-block; 80 | font-weight: 400; 81 | height: 28px; 82 | position: relative; 83 | top: 4px; 84 | vertical-align: top; 85 | width: 68px; 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/video-sink.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Displays the output stream in a video element. 13 | * @implements {MediaStreamSink} in pipeline.js 14 | */ 15 | class VideoSink { // eslint-disable-line no-unused-vars 16 | constructor() { 17 | /** 18 | * @private {?HTMLVideoElement} output video element 19 | */ 20 | this.video_ = null; 21 | /** @private {string} */ 22 | this.debugPath_ = 'debug.pipeline.sink_'; 23 | } 24 | /** 25 | * Sets the path to this object from the debug global var. 26 | * @param {string} path 27 | */ 28 | setDebugPath(path) { 29 | this.debugPath_ = path; 30 | } 31 | /** @override */ 32 | async setMediaStream(stream) { 33 | console.log('[VideoSink] Setting sink stream.', stream); 34 | if (!this.video_) { 35 | this.video_ = 36 | /** @type {!HTMLVideoElement} */ (document.createElement('video')); 37 | this.video_.classList.add('video', 'sinkVideo'); 38 | document.getElementById('outputVideoContainer').appendChild(this.video_); 39 | console.log( 40 | '[VideoSink] Added video element to page.', 41 | `${this.debugPath_}.video_ =`, this.video_); 42 | } 43 | this.video_.srcObject = stream; 44 | this.video_.play(); 45 | } 46 | /** @override */ 47 | destroy() { 48 | if (this.video_) { 49 | console.log('[VideoSink] Stopping sink video'); 50 | this.video_.pause(); 51 | this.video_.srcObject = null; 52 | this.video_.parentNode.removeChild(this.video_); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/options.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 |

12 | 13 | 16 |

17 |

18 | 21 | 24 |

25 |

26 | 29 | 32 |

33 |

34 | 36 | 39 |

40 |

41 | 44 |

45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/content/getusermedia/resolution/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* 9 | /* eslint-env node */ 10 | 11 | 'use strict'; 12 | const webdriver = require('selenium-webdriver'); 13 | const seleniumHelpers = require('../../../../../test/webdriver'); 14 | 15 | let driver; 16 | const path = '/src/content/getusermedia/resolution/index.html'; 17 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 18 | 19 | describe('getUserMedia resolutions', () => { 20 | beforeAll(async () => { 21 | driver = await seleniumHelpers.buildDriver(); 22 | }); 23 | afterAll(() => { 24 | return driver.quit(); 25 | }); 26 | 27 | beforeEach(() => { 28 | return driver.get(url); 29 | }); 30 | 31 | const buttonToResolution = { 32 | 'p180': 320, 33 | 'p360': 640, 34 | 'qvga': 320, 35 | 'vga': 640, 36 | 'hd': 1280, 37 | 'full-hd': 1920, 38 | 'televisionFourK': 3840, 39 | /* TODO: unsupported by fake device? Or is fake device limited to physical device resolution? 40 | 'cinemaFourK': 4096, 41 | 'eightK': 8192, 42 | */ 43 | }; 44 | Object.keys(buttonToResolution).forEach(buttonId => { 45 | const resolution = buttonToResolution[buttonId]; 46 | 47 | it(`opens a camera with width ${resolution}px`, async () => { 48 | await driver.findElement(webdriver.By.id(buttonId)).click(); 49 | await driver.wait(() => driver.executeScript(() => 50 | document.querySelector('video').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA) 51 | ); 52 | const width = await driver.findElement(webdriver.By.css('video')).getAttribute('videoWidth'); 53 | expect(width >>> 0).toBe(resolution); 54 | }); 55 | }); 56 | }); 57 | 58 | -------------------------------------------------------------------------------- /src/content/datachannel/basic/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | const webdriver = require('selenium-webdriver'); 9 | const seleniumHelpers = require('../../../../../test/webdriver'); 10 | 11 | let driver; 12 | const path = '/src/content/datachannel/basic/index.html'; 13 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 14 | 15 | describe('datachannel basic', () => { 16 | beforeAll(async () => { 17 | driver = await seleniumHelpers.buildDriver(); 18 | }); 19 | afterAll(() => { 20 | return driver.quit(); 21 | }); 22 | 23 | beforeEach(() => { 24 | return driver.get(url); 25 | }); 26 | 27 | it('transfers text', async () => { 28 | const text = 'Hello world'; 29 | await driver.findElement(webdriver.By.id('startButton')).click(); 30 | 31 | await Promise.all([ 32 | driver.wait(() => driver.executeScript(() => { 33 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 34 | })), 35 | await driver.wait(() => driver.executeScript(() => { 36 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 37 | })), 38 | ]); 39 | await driver.wait(() => driver.findElement(webdriver.By.id('sendButton')).isEnabled()); 40 | 41 | await driver.findElement(webdriver.By.id('dataChannelSend')) 42 | .sendKeys(text); 43 | await driver.findElement(webdriver.By.id('sendButton')).click(); 44 | await driver.wait(() => driver.executeScript(() => { 45 | return document.getElementById('dataChannelReceive').value.length > 0; 46 | })); 47 | 48 | const value = await driver.findElement(webdriver.By.id('dataChannelReceive')).getAttribute('value'); 49 | expect(value).toBe(text); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "NETLI_DEFAULT_RADIO": { 3 | "message": "Give me the best media experience: This option allows Chrome to explore all network paths to find the best way to send and receive media, which may be different from normal web traffic." 4 | }, 5 | "NETLI_DEFAULT_PUBLIC_AND_PRIVATE_INTERFACES_RADIO": { 6 | "message": "Use my default public and private IP addresses: This option forces Chrome to use the same network path for media as for normal web traffic, except when a web proxy is present. For machines behind a NAT, Chrome will also use the default private address to enhance connectivity. To prevent degraded performance, Chrome will attempt to send media directly instead of using the proxy." 7 | }, 8 | "NETLI_DEFAULT_PUBLIC_INTERFACE_ONLY_RADIO": { 9 | "message": "Use only my default public IP address: This option is the same as Use my default public and private IP addresses except that Chrome will not use the private default address." 10 | }, 11 | "NETLI_DISABLE_NON_PROXIED_UDP_RADIO": { 12 | "message": "Use my proxy server (if present): This option forces Chrome to use the same network path for media as for normal web traffic, including use of a web proxy. Chrome will always attempt to send media through the proxy, which will typically hurt media performance and increase the load on the proxy; furthermore, this behavior may be incompatible with some applications." 13 | }, 14 | "NETLI_OPTION_NOT_SUPPORTED": { 15 | "message": "Grayed out options require newer verion of Chrome." 16 | }, 17 | "NETLI_APPDESC": { 18 | "message": "Configures how WebRTC's network traffic is routed by changing Chrome's privacy settings." 19 | }, 20 | "NETLI_APPNAME": { 21 | "message": "WebRTC Network Limiter" 22 | }, 23 | "NETLI_OPTIONS": { 24 | "message": "WebRTC Network Limiter Options" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/content/getusermedia/gum/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 'use strict'; 9 | 10 | // Put variables in global scope to make them available to the browser console. 11 | const constraints = window.constraints = { 12 | audio: false, 13 | video: true 14 | }; 15 | 16 | function handleSuccess(stream) { 17 | const video = document.querySelector('video'); 18 | const videoTracks = stream.getVideoTracks(); 19 | console.log('Got stream with constraints:', constraints); 20 | console.log(`Using video device: ${videoTracks[0].label}`); 21 | window.stream = stream; // make variable available to browser console 22 | video.srcObject = stream; 23 | } 24 | 25 | function handleError(error) { 26 | if (error.name === 'OverconstrainedError') { 27 | errorMsg(`OverconstrainedError: The constraints could not be satisfied by the available devices. Constraints: ${JSON.stringify(constraints)}`); 28 | } else if (error.name === 'NotAllowedError') { 29 | errorMsg('NotAllowedError: Permissions have not been granted to use your camera and ' + 30 | 'microphone, you need to allow the page access to your devices in ' + 31 | 'order for the demo to work.'); 32 | } 33 | errorMsg(`getUserMedia error: ${error.name}`, error); 34 | } 35 | 36 | function errorMsg(msg, error) { 37 | const errorElement = document.querySelector('#errorMsg'); 38 | errorElement.innerHTML += `

${msg}

`; 39 | if (typeof error !== 'undefined') { 40 | console.error(error); 41 | } 42 | } 43 | 44 | async function init(e) { 45 | try { 46 | const stream = await navigator.mediaDevices.getUserMedia(constraints); 47 | handleSuccess(stream); 48 | e.target.disabled = true; 49 | } catch (e) { 50 | handleError(e); 51 | } 52 | } 53 | 54 | document.querySelector('#showVideo').addEventListener('click', e => init(e)); 55 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/camera-source.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /* global VideoMirrorHelper */ // defined in video-mirror-helper.js 12 | 13 | /** 14 | * Opens the device's camera with getUserMedia. 15 | * @implements {MediaStreamSource} in pipeline.js 16 | */ 17 | class CameraSource { // eslint-disable-line no-unused-vars 18 | constructor() { 19 | /** 20 | * @private @const {!VideoMirrorHelper} manages displaying the video stream 21 | * in the page 22 | */ 23 | this.videoMirrorHelper_ = new VideoMirrorHelper(); 24 | /** @private {?MediaStream} camera stream, initialized in getMediaStream */ 25 | this.stream_ = null; 26 | /** @private {string} */ 27 | this.debugPath_ = ''; 28 | } 29 | /** @override */ 30 | setDebugPath(path) { 31 | this.debugPath_ = path; 32 | this.videoMirrorHelper_.setDebugPath(`${path}.videoMirrorHelper_`); 33 | } 34 | /** @override */ 35 | setVisibility(visible) { 36 | this.videoMirrorHelper_.setVisibility(visible); 37 | } 38 | /** @override */ 39 | async getMediaStream() { 40 | if (this.stream_) return this.stream_; 41 | console.log('[CameraSource] Requesting camera.'); 42 | this.stream_ = 43 | await navigator.mediaDevices.getUserMedia({audio: false, video: true}); 44 | console.log( 45 | '[CameraSource] Received camera stream.', 46 | `${this.debugPath_}.stream_ =`, this.stream_); 47 | this.videoMirrorHelper_.setStream(this.stream_); 48 | return this.stream_; 49 | } 50 | /** @override */ 51 | destroy() { 52 | console.log('[CameraSource] Stopping camera'); 53 | this.videoMirrorHelper_.destroy(); 54 | if (this.stream_) { 55 | this.stream_.getTracks().forEach(t => t.stop()); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/content/datachannel/datatransfer/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/datachannel/datatransfer/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('datachannel datatransfer', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('transfers data', async () => { 31 | const megsToSend = 4; 32 | await driver.findElement(webdriver.By.id('megsToSend')) 33 | .clear(); 34 | await driver.findElement(webdriver.By.id('megsToSend')) 35 | .sendKeys(megsToSend + '\n'); 36 | 37 | await driver.findElement(webdriver.By.id('sendTheData')).click(); 38 | 39 | await Promise.all([ 40 | driver.wait(() => driver.executeScript(() => { 41 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 42 | })), 43 | await driver.wait(() => driver.executeScript(() => { 44 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 45 | })), 46 | ]); 47 | 48 | // the remote connection gets closed when it is done. 49 | await driver.wait(() => driver.executeScript(() => { 50 | return pc2 === null; // eslint-disable-line no-undef 51 | })); 52 | 53 | const transferred = await driver.findElement(webdriver.By.id('receiveProgress')).getAttribute('value'); 54 | expect(transferred >>> 0).toBe(megsToSend * 1024 * 1024); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/webcodec-transform.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Encodes and decodes frames using the WebCodec API. 13 | * @implements {FrameTransform} in pipeline.js 14 | */ 15 | class WebCodecTransform { // eslint-disable-line no-unused-vars 16 | constructor() { 17 | // Encoder and decoder are initialized in init() 18 | this.decoder_ = null; 19 | this.encoder_ = null; 20 | this.controller_ = null; 21 | } 22 | /** @override */ 23 | async init() { 24 | console.log('[WebCodecTransform] Initializing encoder and decoder'); 25 | this.decoder_ = new VideoDecoder({ 26 | output: frame => this.handleDecodedFrame(frame), 27 | error: this.error 28 | }); 29 | this.encoder_ = new VideoEncoder({ 30 | output: frame => this.handleEncodedFrame(frame), 31 | error: this.error 32 | }); 33 | this.encoder_.configure({codec: 'vp8', width: 640, height: 480}); 34 | this.decoder_.configure({codec: 'vp8', width: 640, height: 480}); 35 | } 36 | 37 | /** @override */ 38 | async transform(frame, controller) { 39 | if (!this.encoder_) { 40 | frame.close(); 41 | return; 42 | } 43 | try { 44 | this.controller_ = controller; 45 | this.encoder_.encode(frame); 46 | } finally { 47 | frame.close(); 48 | } 49 | } 50 | 51 | /** @override */ 52 | destroy() {} 53 | 54 | /* Helper functions */ 55 | handleEncodedFrame(encodedFrame) { 56 | this.decoder_.decode(encodedFrame); 57 | } 58 | 59 | handleDecodedFrame(videoFrame) { 60 | if (!this.controller_) { 61 | videoFrame.close(); 62 | return; 63 | } 64 | this.controller_.enqueue(videoFrame); 65 | } 66 | 67 | error(e) { 68 | console.log('[WebCodecTransform] Bad stuff happened: ' + e); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/content/extensions/multipleroutes/src/README.md: -------------------------------------------------------------------------------- 1 | ## Chrome WebRTC Network Limiter 2 | Configures the WebRTC traffic routing options in Chrome's privacy settings. 3 | 4 | ★ What it does: 5 | This configures WebRTC to not use certain IP addresses or protocols: 6 | - private IP addresses not visible to the public internet (e.g. addresses like 192.168.1.2) 7 | - any public IP addresses associated with network interfaces that are not used for web traffic (e.g. an ISP-provided address, when browsing through a VPN) 8 | - Require WebRTC traffic to go through proxy servers as configured in Chrome. Since most of the proxy servers don't handle UDP, this effectively turns off UDP until UDP proxy support is available in Chrome and such proxies are widely deployed. 9 | 10 | When the extension is installed on Chrome versions prior to M48, WebRTC will only use the public IP address associated with the interface used for web traffic, typically the same addresses that are already provided to sites in browser HTTP requests. For Chrome version M48 and after, this extension provides one more configuration which allows WebRTC to use both the default public address and, for machines behind a NAT, the default private address which is associated with the public one. Said behavior will be the default after a fresh installation of the extension to Chrome M48. For upgrade scenarios, the previous selected configuration should not be changed. 11 | 12 | The extension may also disable non-proxied UDP, but this is not on by default and must be configured using the extension's Options page. 13 | 14 | ★ Notes: 15 | This extension may affect the performance of applications that use WebRTC for audio/video or real-time data communication. Because it limits the potential network paths and protocols, WebRTC may pick a path which results in significantly longer delay or lower quality (e.g. through a VPN) or use TCP only through proxy servers which is not ideal for real-time communication. We are attempting to determine how common this is. 16 | 17 | By installing this item, you agree to the Google Terms of Service and Privacy Policy at https://www.google.com/intl/en/policies/. 18 | 19 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/peer-connection-sink.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /* global PeerConnectionPipe */ // defined in peer-connection-pipe.js 12 | /* global VideoSink */ // defined in video-sink.js 13 | 14 | /** 15 | * Sends the transformed video to one end of an RTCPeerConnection and displays 16 | * the remote end in a video element. In this sample, a PeerConnectionSink 17 | * represents processing the local user's camera input using a 18 | * MediaStreamTrackProcessor before sending it to a remote video call 19 | * participant. Contrast with a PeerConnectionSource. 20 | * @implements {MediaStreamSink} in pipeline.js 21 | */ 22 | class PeerConnectionSink { // eslint-disable-line no-unused-vars 23 | constructor() { 24 | /** 25 | * @private @const {!VideoSink} manages displaying the video stream in the 26 | * page 27 | */ 28 | this.videoSink_ = new VideoSink(); 29 | /** 30 | * @private {?PeerConnectionPipe} handles piping the MediaStream through an 31 | * RTCPeerConnection 32 | */ 33 | this.pipe_ = null; 34 | /** @private {string} */ 35 | this.debugPath_ = 'debug.pipeline.sink_'; 36 | this.videoSink_.setDebugPath(`${this.debugPath_}.videoSink_`); 37 | } 38 | 39 | /** @override */ 40 | async setMediaStream(stream) { 41 | console.log( 42 | '[PeerConnectionSink] Setting peer connection sink stream.', stream); 43 | if (this.pipe_) this.pipe_.destroy(); 44 | this.pipe_ = new PeerConnectionPipe(stream, `${this.debugPath_}.pipe_`); 45 | const pipedStream = await this.pipe_.getOutputStream(); 46 | console.log( 47 | '[PeerConnectionSink] Received callee peer connection stream.', 48 | pipedStream); 49 | await this.videoSink_.setMediaStream(pipedStream); 50 | } 51 | 52 | /** @override */ 53 | destroy() { 54 | this.videoSink_.destroy(); 55 | if (this.pipe_) this.pipe_.destroy(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/content/peerconnection/pc1/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/pc1/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('simple peerconnection', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes a connection and hangs up', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | 33 | await driver.wait(() => driver.executeScript(() => { 34 | return localStream !== null; // eslint-disable-line no-undef 35 | })); 36 | 37 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 38 | await driver.findElement(webdriver.By.id('callButton')).click(); 39 | 40 | await Promise.all([ 41 | await driver.wait(() => driver.executeScript(() => { 42 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 43 | })), 44 | await driver.wait(() => driver.executeScript(() => { 45 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 46 | })), 47 | ]); 48 | 49 | await driver.wait(() => driver.executeScript(() => { 50 | return document.getElementById('remoteVideo').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 51 | })); 52 | 53 | await driver.findElement(webdriver.By.id('hangupButton')).click(); 54 | 55 | await driver.wait(() => driver.executeScript(() => { 56 | return pc1 === null; // eslint-disable-line no-undef 57 | })); 58 | }); 59 | }); 60 | 61 | -------------------------------------------------------------------------------- /src/content/peerconnection/munge-sdp/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 20px 0; 10 | vertical-align: top; 11 | width: 155px; 12 | } 13 | div#buttons { 14 | border-bottom: 1px solid #eee; 15 | margin: 1em 0 1em 0; 16 | padding: 0 0 1em 0; 17 | } 18 | div#local { 19 | margin: 0 20px 0 0; 20 | } 21 | div#preview { 22 | border-bottom: 1px solid #eee; 23 | margin: 0 0 1em 0; 24 | padding: 0 0 0.5em 0; 25 | } 26 | div#preview > div { 27 | display: inline-block; 28 | vertical-align: top; 29 | width: calc(50% - 12px); 30 | } 31 | div#select { 32 | margin: 0 0 1em 0; 33 | } 34 | div#selectSource { 35 | margin: 0 0 1em 0; 36 | } 37 | div.source { 38 | display: inline-block; 39 | margin: 0 0 1em 0; 40 | } 41 | form { 42 | margin: 0 0 1em 0; 43 | white-space: nowrap; 44 | } 45 | h2 { 46 | margin: 0 0 0.5em 0; 47 | } 48 | label { 49 | margin: 0 0.4em 0 0; 50 | } 51 | textarea { 52 | color: #444; 53 | font-size: 0.9em; 54 | font-weight: 300; 55 | height: 7.0em; 56 | padding: 5px; 57 | width: calc(100% - 10px); 58 | } 59 | video { 60 | height: 225px; 61 | } 62 | 63 | @media screen and (max-width: 550px) { 64 | button { 65 | font-weight: 500; 66 | height: 56px; 67 | line-height: 1.3em; 68 | margin: 0 7px 15px 0; 69 | width: 86px; 70 | } 71 | button:nth-child(3n+0) { 72 | margin: 0 0 15px 0; 73 | } 74 | video { 75 | height: 96px; 76 | } 77 | } 78 | 79 | @media screen and (max-width: 800px) { 80 | button { 81 | margin: 0 15px 15px 0; 82 | width: 155px; 83 | } 84 | div#selectSource { 85 | margin: 0 0 0.5em 0; 86 | } 87 | div.source { 88 | margin: 0 0 .2em 0; 89 | } 90 | select { 91 | margin: 0 1.5em 0 0; 92 | } 93 | textarea { 94 | font-size: 0.7em; 95 | } 96 | } 97 | 98 | @media screen and (max-width: 500px) { 99 | textarea { 100 | font-size: 0.5em; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/content/peerconnection/upgrade/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 'use strict'; 10 | 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/upgrade/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection upgrade from audio-only to audio-video', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('upgrades to video', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | await driver.wait(() => driver.executeScript(() => { 33 | return localStream !== null; // eslint-disable-line no-undef 34 | })); 35 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 36 | await driver.findElement(webdriver.By.id('callButton')).click(); 37 | 38 | await driver.wait(() => driver.executeScript(() => { 39 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 40 | })); 41 | 42 | await driver.wait(() => driver.findElement(webdriver.By.id('upgradeButton')).isEnabled()); 43 | await driver.findElement(webdriver.By.id('upgradeButton')).click(); 44 | await driver.wait(() => driver.executeScript(() => { 45 | return remoteVideo.videoWidth > 0; // eslint-disable-line no-undef 46 | })); 47 | 48 | await driver.wait(() => driver.findElement(webdriver.By.id('hangupButton')).isEnabled()); 49 | await driver.findElement(webdriver.By.id('hangupButton')).click(); 50 | await driver.wait(() => driver.executeScript(() => { 51 | return pc1 === null; // eslint-disable-line no-undef 52 | })); 53 | }); 54 | }); 55 | 56 | -------------------------------------------------------------------------------- /src/content/getusermedia/canvas/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | getUserMedia to canvas 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 |

WebRTC samples getUserMedia ⇒ canvas 36 |

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

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

43 | 44 |

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

46 | 47 | View source on GitHub 49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/js/videopipe.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | // 9 | // A "videopipe" abstraction on top of WebRTC. 10 | // 11 | // The usage of this abstraction: 12 | // var pipe = new VideoPipe(mediastream, handlerFunction); 13 | // handlerFunction = function(mediastream) { 14 | // do_something 15 | // } 16 | // pipe.close(); 17 | // 18 | // The VideoPipe will set up 2 PeerConnections, connect them to each 19 | // other, and call HandlerFunction when the stream is available in the 20 | // second PeerConnection. 21 | // 22 | 23 | function errorHandler(context) { 24 | return function(error) { 25 | trace('Failure in ' + context + ': ' + error.toString); 26 | }; 27 | } 28 | 29 | // eslint-disable-next-line no-unused-vars 30 | function successHandler(context) { 31 | return function() { 32 | trace('Success in ' + context); 33 | }; 34 | } 35 | 36 | function noAction() { 37 | } 38 | 39 | 40 | function VideoPipe(stream, handler) { 41 | let servers = null; 42 | let pc1 = new RTCPeerConnection(servers); 43 | let pc2 = new RTCPeerConnection(servers); 44 | 45 | pc1.addStream(stream); 46 | pc1.onicecandidate = function(event) { 47 | if (event.candidate) { 48 | pc2.addIceCandidate(new RTCIceCandidate(event.candidate), 49 | noAction, errorHandler('AddIceCandidate')); 50 | } 51 | }; 52 | pc2.onicecandidate = function(event) { 53 | if (event.candidate) { 54 | pc1.addIceCandidate(new RTCIceCandidate(event.candidate), 55 | noAction, errorHandler('AddIceCandidate')); 56 | } 57 | }; 58 | pc2.onaddstream = function(e) { 59 | handler(e.stream); 60 | }; 61 | pc1.createOffer(function(desc) { 62 | pc1.setLocalDescription(desc); 63 | pc2.setRemoteDescription(desc); 64 | pc2.createAnswer(function(desc2) { 65 | pc2.setLocalDescription(desc2); 66 | pc1.setRemoteDescription(desc2); 67 | }, errorHandler('pc2.createAnswer')); 68 | }, errorHandler('pc1.createOffer')); 69 | this.pc1 = pc1; 70 | this.pc2 = pc2; 71 | } 72 | 73 | VideoPipe.prototype.close = function() { 74 | this.pc1.close(); 75 | this.pc2.close(); 76 | }; 77 | -------------------------------------------------------------------------------- /src/content/peerconnection/webaudio-input/js/webaudioextended.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // WebAudioExtended helper class which takes care of the WebAudio related parts. 12 | 13 | function WebAudioExtended() { 14 | window.AudioContext = window.AudioContext || window.webkitAudioContext; 15 | /* global AudioContext */ 16 | this.context = new AudioContext(); 17 | this.soundBuffer = null; 18 | } 19 | 20 | WebAudioExtended.prototype.start = function() { 21 | this.filter = this.context.createBiquadFilter(); 22 | this.filter.type = 'highpass'; 23 | this.filter.frequency.setValueAtTime(1500, this.context.currentTime + 1); 24 | }; 25 | 26 | WebAudioExtended.prototype.applyFilter = function(stream) { 27 | this.mic = this.context.createMediaStreamSource(stream); 28 | this.mic.connect(this.filter); 29 | this.peer = this.context.createMediaStreamDestination(); 30 | this.filter.connect(this.peer); 31 | return this.peer.stream; 32 | }; 33 | 34 | WebAudioExtended.prototype.renderLocally = function(enabled) { 35 | if (enabled) { 36 | this.mic.connect(this.context.destination); 37 | } else { 38 | this.mic.disconnect(0); 39 | this.mic.connect(this.filter); 40 | } 41 | }; 42 | 43 | WebAudioExtended.prototype.stop = function() { 44 | this.mic.disconnect(0); 45 | this.filter.disconnect(0); 46 | this.mic = null; 47 | this.peer = null; 48 | }; 49 | 50 | WebAudioExtended.prototype.addEffect = function() { 51 | const effect = this.context.createBufferSource(); 52 | effect.buffer = this.soundBuffer; 53 | if (this.peer) { 54 | effect.connect(this.peer); 55 | effect.start(0); 56 | } 57 | }; 58 | 59 | WebAudioExtended.prototype.loadCompleted = function() { 60 | this.context.decodeAudioData(this.request.response, function(buffer) { 61 | this.soundBuffer = buffer; 62 | }.bind(this)); 63 | }; 64 | 65 | WebAudioExtended.prototype.loadSound = function(url) { 66 | this.request = new XMLHttpRequest(); 67 | this.request.open('GET', url, true); 68 | this.request.responseType = 'arraybuffer'; 69 | this.request.onload = this.loadCompleted.bind(this); 70 | this.request.send(); 71 | }; 72 | -------------------------------------------------------------------------------- /src/content/peerconnection/create-offer/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const audioInput = document.querySelector('input#audio'); 12 | const restartInput = document.querySelector('input#restart'); 13 | const vadInput = document.querySelector('input#vad'); 14 | const videoInput = document.querySelector('input#video'); 15 | 16 | const numAudioTracksInput = document.querySelector('div#numAudioTracks input'); 17 | const numAudioTracksDisplay = document.querySelector('span#numAudioTracksDisplay'); 18 | const outputTextarea = document.querySelector('textarea#output'); 19 | const createOfferButton = document.querySelector('button#createOffer'); 20 | 21 | createOfferButton.addEventListener('click', createOffer); 22 | numAudioTracksInput.addEventListener('change', e => numAudioTracksDisplay.innerText = e.target.value); 23 | 24 | async function createOffer() { 25 | outputTextarea.value = ''; 26 | const peerConnection = window.peerConnection = new RTCPeerConnection(null); 27 | const numRequestedAudioTracks = parseInt(numAudioTracksInput.value); 28 | 29 | for (let i = 0; i < numRequestedAudioTracks; i++) { 30 | const acx = new AudioContext(); 31 | const dst = acx.createMediaStreamDestination(); 32 | 33 | // Fill up the peer connection with numRequestedAudioTracks number of tracks. 34 | const track = dst.stream.getTracks()[0]; 35 | peerConnection.addTrack(track, dst.stream); 36 | } 37 | 38 | const offerOptions = { 39 | // New spec states offerToReceiveAudio/Video are of type long (due to 40 | // having to tell how many "m" lines to generate). 41 | // http://w3c.github.io/webrtc-pc/#idl-def-RTCOfferAnswerOptions. 42 | offerToReceiveAudio: (audioInput.checked) ? 1 : 0, 43 | offerToReceiveVideo: (videoInput.checked) ? 1 : 0, 44 | iceRestart: restartInput.checked, 45 | voiceActivityDetection: vadInput.checked 46 | }; 47 | 48 | try { 49 | const offer = await peerConnection.createOffer(offerOptions); 50 | await peerConnection.setLocalDescription(offer); 51 | outputTextarea.value = offer.sdp; 52 | } catch (e) { 53 | outputTextarea.value = `Failed to create offer: ${e}`; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/content/getusermedia/gum/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | getUserMedia 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |

WebRTC samples 35 | getUserMedia

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

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

43 | 44 |

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

45 | 46 |

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

48 | 49 | View source on GitHub 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/content/getusermedia/audio/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | gUM audio 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 |

WebRTC samples getUserMedia, audio only 36 |

37 | 38 | 39 | 40 |

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

41 | 42 |

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

43 | 44 |

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

46 |
47 | 48 |
49 | 50 | View source on GitHub 52 | 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/content/peerconnection/trickle-ice/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/trickle-ice/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('Trickle-Ice', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | afterEach(() => { 30 | return driver.executeScript(() => localStorage.clear()); 31 | }); 32 | 33 | it('gathers a candidate', async () => { 34 | await driver.findElement(webdriver.By.id('gather')).click(); 35 | await driver.wait(() => driver.executeScript(() => pc === null && candidates.length > 0), 30 * 1000); // eslint-disable-line no-undef 36 | }); 37 | 38 | it('loads server data on double click', async () => { 39 | const element = await driver.findElement(webdriver.By.css('#servers>option')); 40 | const actions = driver.actions({async: true}); 41 | await actions.doubleClick(element).perform(); 42 | const value = await driver.findElement(webdriver.By.id('url')).getAttribute('value'); 43 | expect(value).not.toBe(''); 44 | }); 45 | 46 | it('adding a server', async () => { 47 | await driver.findElement(webdriver.By.id('url')) 48 | .sendKeys('stun:stun.l.google.com:19302'); 49 | await driver.findElement(webdriver.By.id('add')).click(); 50 | const length = await driver.findElement(webdriver.By.css('#servers')) 51 | .getAttribute('length'); 52 | expect(length >>> 0).toBe(2); 53 | }); 54 | 55 | it('removing a server', async () => { 56 | await driver.findElement(webdriver.By.css('#servers>option')).click(); 57 | await driver.findElement(webdriver.By.id('remove')).click(); 58 | const length = await driver.findElement(webdriver.By.css('#servers')) 59 | .getAttribute('length'); 60 | expect(length >>> 0).toBe(0); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /src/content/getusermedia/getdisplaymedia/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 'use strict'; 9 | 10 | const preferredDisplaySurface = document.getElementById('displaySurface'); 11 | const startButton = document.getElementById('startButton'); 12 | 13 | if (adapter.browserDetails.browser === 'chrome' && 14 | adapter.browserDetails.version >= 107) { 15 | // See https://developer.chrome.com/docs/web-platform/screen-sharing-controls/ 16 | document.getElementById('options').style.display = 'block'; 17 | } else if (adapter.browserDetails.browser === 'firefox') { 18 | // Polyfill in Firefox. 19 | // See https://blog.mozilla.org/webrtc/getdisplaymedia-now-available-in-adapter-js/ 20 | adapter.browserShim.shimGetDisplayMedia(window, 'screen'); 21 | } 22 | 23 | function handleSuccess(stream) { 24 | startButton.disabled = true; 25 | preferredDisplaySurface.disabled = true; 26 | const video = document.querySelector('video'); 27 | video.srcObject = stream; 28 | 29 | // demonstrates how to detect that the user has stopped 30 | // sharing the screen via the browser UI. 31 | stream.getVideoTracks()[0].addEventListener('ended', () => { 32 | errorMsg('The user has ended sharing the screen'); 33 | startButton.disabled = false; 34 | preferredDisplaySurface.disabled = false; 35 | }); 36 | } 37 | 38 | function handleError(error) { 39 | errorMsg(`getDisplayMedia error: ${error.name}`, error); 40 | } 41 | 42 | function errorMsg(msg, error) { 43 | const errorElement = document.querySelector('#errorMsg'); 44 | errorElement.innerHTML += `

${msg}

`; 45 | if (typeof error !== 'undefined') { 46 | console.error(error); 47 | } 48 | } 49 | 50 | 51 | startButton.addEventListener('click', () => { 52 | const options = {audio: true, video: true}; 53 | const displaySurface = preferredDisplaySurface.options[preferredDisplaySurface.selectedIndex].value; 54 | if (displaySurface !== 'default') { 55 | options.video = {displaySurface}; 56 | } 57 | navigator.mediaDevices.getDisplayMedia(options) 58 | .then(handleSuccess, handleError); 59 | }); 60 | 61 | if ((navigator.mediaDevices && 'getDisplayMedia' in navigator.mediaDevices)) { 62 | startButton.disabled = false; 63 | } else { 64 | errorMsg('getDisplayMedia is not supported'); 65 | } 66 | -------------------------------------------------------------------------------- /src/content/peerconnection/constraints/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | button { 9 | margin: 0 20px 25px 0; 10 | vertical-align: top; 11 | width: 134px; 12 | } 13 | 14 | div#getUserMedia { 15 | padding: 0 0 8px 0; 16 | } 17 | 18 | div.input { 19 | display: inline-block; 20 | margin: 0 4px 0 0; 21 | vertical-align: top; 22 | width: 310px; 23 | } 24 | 25 | div.input > div { 26 | margin: 0 0 20px 0; 27 | vertical-align: top; 28 | } 29 | 30 | div.output { 31 | background-color: #eee; 32 | display: inline-block; 33 | font-family: 'Inconsolata', 'Courier New', monospace; 34 | font-size: 0.9em; 35 | padding: 10px 10px 10px 25px; 36 | position: relative; 37 | top: 10px; 38 | white-space: pre; 39 | width: 270px; 40 | } 41 | 42 | section#statistics div { 43 | display: inline-block; 44 | font-family: 'Inconsolata', 'Courier New', monospace; 45 | vertical-align: top; 46 | width: 308px; 47 | } 48 | 49 | section#statistics div#senderStats { 50 | margin: 0 20px 0 0; 51 | } 52 | 53 | section#constraints > div { 54 | margin: 0 0 20px 0; 55 | } 56 | 57 | section#video > div { 58 | display: inline-block; 59 | margin: 0 20px 0 0; 60 | vertical-align: top; 61 | width: calc(50% - 22px); 62 | } 63 | 64 | section#video > div div { 65 | font-size: 0.9em; 66 | margin: 0 0 0.5em 0; 67 | width: 320px; 68 | } 69 | 70 | h2 { 71 | margin: 0 0 1em 0; 72 | } 73 | 74 | section#constraints label { 75 | display: inline-block; 76 | width: 156px; 77 | } 78 | 79 | section { 80 | margin: 0 0 20px 0; 81 | padding: 0 0 15px 0; 82 | } 83 | 84 | section#video { 85 | width: calc(100% + 20px); 86 | } 87 | 88 | video { 89 | --width: 90%; 90 | width: var(--width); 91 | height: calc(var(--width) * 0.75); 92 | margin: 0 0 10px 0; 93 | } 94 | 95 | @media screen and (max-width: 720px) { 96 | button { 97 | font-weight: 500; 98 | height: 56px; 99 | line-height: 1.3em; 100 | width: 90px; 101 | } 102 | 103 | div#getUserMedia { 104 | padding: 0 0 40px 0; 105 | } 106 | 107 | section#statistics div { 108 | width: calc(50% - 14px); 109 | } 110 | 111 | video { 112 | height: 96px; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/content/capture/worker-process/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const source = document.querySelector('#source'); 12 | // TODO(hta): Use OffscreenCanvas for the intermediate canvases. 13 | const canvasIn = document.querySelector('#canvas-source'); 14 | const canvasOut = document.querySelector('#canvas-result'); 15 | const result = document.querySelector('#result'); 16 | 17 | const stream = canvasOut.captureStream(); 18 | let inputStream = null; 19 | let imageData = null; 20 | let transformStream = null; 21 | let writer = null; 22 | let reader = null; 23 | 24 | result.srcObject = stream; 25 | 26 | function loop() { 27 | if (source.videoWidth > 0 && source.videoHeight > 0) { 28 | canvasIn.width = source.videoWidth; 29 | canvasIn.height = source.videoHeight; 30 | const ctx = canvasIn.getContext('2d'); 31 | ctx.drawImage(source, 0, 0); 32 | // Put a red square into the image, to mark it as "processed". 33 | ctx.fillStyle = '#FF0000'; 34 | ctx.fillRect(10, 10, 80, 80); 35 | imageData = ctx.getImageData(0, 0, canvasIn.width, canvasIn.height); 36 | // At this point, we have data that can be transferred. 37 | writer.write(imageData); 38 | } 39 | window.requestAnimationFrame(loop); 40 | } 41 | 42 | // The read function paints the incoming data on the second canvas. 43 | const readData = async () => { 44 | const result = await reader.read(); 45 | if (!result.done) { 46 | canvasOut.width = source.videoWidth; 47 | canvasOut.height = source.videoHeight; 48 | const outCtx = canvasOut.getContext('2d'); 49 | outCtx.putImageData(result.value, 0, 0); 50 | readData(); 51 | } 52 | }; 53 | 54 | (async () => { 55 | inputStream = await navigator.mediaDevices.getUserMedia({video: true}); 56 | source.srcObject = inputStream; 57 | transformStream = new TransformStream(); 58 | writer = transformStream.writable.getWriter(); 59 | 60 | const myWorker = new Worker('js/worker.js'); 61 | myWorker.onmessage = function(e) { 62 | reader = e.data[1].getReader(); 63 | // Start the flow of data. 64 | readData(); 65 | window.requestAnimationFrame(loop); 66 | }; 67 | myWorker.postMessage(['stream', transformStream.readable], 68 | [transformStream.readable]); 69 | source.play(); 70 | result.play(); 71 | })(); 72 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/canvas-transform.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Applies a picture-frame effect using CanvasRenderingContext2D. 13 | * @implements {FrameTransform} in pipeline.js 14 | */ 15 | class CanvasTransform { // eslint-disable-line no-unused-vars 16 | constructor() { 17 | /** 18 | * @private {?OffscreenCanvas} canvas used to create the 2D context. 19 | * Initialized in init. 20 | */ 21 | this.canvas_ = null; 22 | /** 23 | * @private {?CanvasRenderingContext2D} the 2D context used to draw the 24 | * effect. Initialized in init. 25 | */ 26 | this.ctx_ = null; 27 | /** @private {string} */ 28 | this.debugPath_ = 'debug.pipeline.frameTransform_'; 29 | } 30 | /** @override */ 31 | async init() { 32 | console.log('[CanvasTransform] Initializing 2D context for transform'); 33 | this.canvas_ = new OffscreenCanvas(1, 1); 34 | this.ctx_ = /** @type {?CanvasRenderingContext2D} */ ( 35 | this.canvas_.getContext('2d', {alpha: false, desynchronized: true})); 36 | if (!this.ctx_) { 37 | throw new Error('Unable to create CanvasRenderingContext2D'); 38 | } 39 | console.log( 40 | '[CanvasTransform] CanvasRenderingContext2D initialized.', 41 | `${this.debugPath_}.canvas_ =`, this.canvas_, 42 | `${this.debugPath_}.ctx_ =`, this.ctx_); 43 | } 44 | 45 | /** @override */ 46 | async transform(frame, controller) { 47 | const ctx = this.ctx_; 48 | if (!this.canvas_ || !ctx) { 49 | frame.close(); 50 | return; 51 | } 52 | const width = frame.displayWidth; 53 | const height = frame.displayHeight; 54 | this.canvas_.width = width; 55 | this.canvas_.height = height; 56 | const timestamp = frame.timestamp; 57 | 58 | ctx.drawImage(frame, 0, 0); 59 | frame.close(); 60 | 61 | ctx.shadowColor = '#000'; 62 | ctx.shadowBlur = 20; 63 | ctx.lineWidth = 50; 64 | ctx.strokeStyle = '#000'; 65 | ctx.strokeRect(0, 0, width, height); 66 | 67 | // alpha: 'discard' is needed in order to send frames to a PeerConnection. 68 | controller.enqueue(new VideoFrame(this.canvas_, {timestamp, alpha: 'discard'})); 69 | } 70 | 71 | /** @override */ 72 | destroy() {} 73 | } 74 | -------------------------------------------------------------------------------- /src/content/peerconnection/pr-answer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | PeerConnection PRANSWER Demo 24 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | 38 |
39 | 40 |

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

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

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

50 | View source on GitHub 51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/content/getusermedia/getdisplaymedia/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | getDisplayMedia 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |

WebRTC samples getDisplayMedia

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

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

50 | 51 | View source on GitHub 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple-relay/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection relay 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Peer connection relay 37 |

38 | 39 |
40 | 41 | 42 |
43 | 44 |
45 | 46 |
47 | 48 | 49 | 50 | 51 |
52 | 53 |
54 | 55 | View source on GitHub 57 | 58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/content/capture/video-video/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | captureStream(): video to video 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples captureStream(): video to video 37 |

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

Press play on the left video to start the demo.

48 | 49 |

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

51 | 52 |

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

53 | 54 | View source on GitHub 56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/content/peerconnection/channel/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/channel/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection and broadcast channels', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(async () => { 27 | await driver.get(url); 28 | }); 29 | 30 | it('establishes a connection and hangs up', async () => { 31 | const firstTab = await driver.getWindowHandle(); 32 | 33 | await driver.switchTo().window(firstTab); 34 | await driver.findElement(webdriver.By.id('startButton')).click(); 35 | await driver.wait(() => driver.executeScript(() => { 36 | return localStream !== null; // eslint-disable-line no-undef 37 | })); 38 | 39 | // Create a second tab, switch to it. 40 | await driver.switchTo().newWindow('tab'); 41 | const secondTab = await driver.getWindowHandle(); 42 | await driver.get(url); 43 | 44 | await driver.switchTo().window(secondTab); 45 | await driver.findElement(webdriver.By.id('startButton')).click(); 46 | await driver.wait(() => driver.executeScript(() => { 47 | return localStream !== null; // eslint-disable-line no-undef 48 | })); 49 | 50 | // Assert state in first tab. 51 | await driver.switchTo().window(firstTab); 52 | await driver.wait(() => driver.executeScript(() => { 53 | return pc && pc.connectionState === 'connected'; // eslint-disable-line no-undef 54 | })); 55 | await driver.wait(() => driver.executeScript(() => { 56 | return document.getElementById('remoteVideo').readyState === 4; 57 | })); 58 | 59 | // Assert state in second tab. 60 | await driver.switchTo().window(secondTab); 61 | await driver.wait(() => driver.executeScript(() => { 62 | return pc && pc.connectionState === 'connected'; // eslint-disable-line no-undef 63 | })); 64 | await driver.wait(() => driver.executeScript(() => { 65 | return document.getElementById('remoteVideo').readyState === 4; 66 | })); 67 | }); 68 | }); 69 | 70 | -------------------------------------------------------------------------------- /test/interop/connection.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | const {buildDriver} = require('../webdriver'); 9 | const {PeerConnection, MediaDevices} = require('../webrtcclient'); 10 | const steps = require('../steps'); 11 | 12 | const browserA = process.env.BROWSER_A || 'chrome'; 13 | const browserB = process.env.BROWSER_B || 'chrome'; 14 | 15 | describe(`basic interop test ${browserA} => ${browserB}`, function() { 16 | let drivers; 17 | let clients; 18 | beforeAll(async () => { 19 | const options = { 20 | version: process.env.BVER || 'stable', 21 | browserLogging: true, 22 | } 23 | drivers = [ 24 | await buildDriver(browserA, options), 25 | await buildDriver(browserB, options), 26 | ]; 27 | clients = drivers.map(driver => { 28 | return { 29 | connection: new PeerConnection(driver), 30 | mediaDevices: new MediaDevices(driver), 31 | }; 32 | }); 33 | }); 34 | afterAll(async () => { 35 | await drivers.map(driver => driver.close()); 36 | }); 37 | 38 | it('establishes a connection', async () => { 39 | await Promise.all(drivers); // timeouts in before(Each)? 40 | await steps.step(drivers, (d) => d.get('https://webrtc.github.io/samples/emptypage.html'), 'Empty page loaded'); 41 | await steps.step(clients, (client) => client.connection.create(), 'Created RTCPeerConnection'); 42 | await steps.step(clients, async (client) => { 43 | const stream = await client.mediaDevices.getUserMedia({audio: true, video: true}); 44 | return Promise.all(stream.getTracks().map(async track => { 45 | return client.connection.addTrack(track, stream); 46 | })); 47 | }, 'Acquired and added audio/video stream'); 48 | const offerWithCandidates = await clients[0].connection.setLocalDescription(); 49 | await clients[1].connection.setRemoteDescription(offerWithCandidates); 50 | const answerWithCandidates = await clients[1].connection.setLocalDescription(); 51 | await clients[0].connection.setRemoteDescription(answerWithCandidates); 52 | 53 | await steps.step(drivers, (d) => steps.waitNVideosExist(d, 1), 'Video elements exist'); 54 | await steps.step(drivers, steps.waitAllVideosHaveEnoughData, 'Video elements have enough data'); 55 | }, 30000); 56 | }, 90000); 57 | -------------------------------------------------------------------------------- /src/content/peerconnection/munge-sdp/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/munge-sdp/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection sdp munging', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes a connection and allows hangup and new offer', async () => { 31 | await driver.findElement(webdriver.By.id('getMedia')).click(); 32 | 33 | await driver.wait(() => driver.findElement(webdriver.By.id('createPeerConnection')).isEnabled()); 34 | await driver.findElement(webdriver.By.id('createPeerConnection')).click(); 35 | 36 | await driver.wait(() => driver.findElement(webdriver.By.id('createOffer')).isEnabled()); 37 | await driver.findElement(webdriver.By.id('createOffer')).click(); 38 | 39 | await driver.wait(() => driver.findElement(webdriver.By.id('setOffer')).isEnabled()); 40 | await driver.findElement(webdriver.By.id('setOffer')).click(); 41 | 42 | await driver.wait(() => driver.findElement(webdriver.By.id('createAnswer')).isEnabled()); 43 | await driver.findElement(webdriver.By.id('createAnswer')).click(); 44 | 45 | await driver.wait(() => driver.findElement(webdriver.By.id('setAnswer')).isEnabled()); 46 | await driver.findElement(webdriver.By.id('setAnswer')).click(); 47 | 48 | await driver.wait(() => driver.findElement(webdriver.By.id('hangup')).isEnabled()); 49 | await driver.wait(() => driver.findElement(webdriver.By.id('createOffer')).isEnabled()); 50 | 51 | await Promise.all([ 52 | await driver.wait(() => driver.executeScript(() => { 53 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 54 | })), 55 | await driver.wait(() => driver.executeScript(() => { 56 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 57 | })), 58 | ]); 59 | }); 60 | 61 | // TODO: add test to ensure the text fields are properly filled 62 | }); 63 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/multiple/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('multiple peerconnections', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes multiple connections and hangs up', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | 33 | await driver.wait(() => driver.executeScript(() => { 34 | return localStream !== null; // eslint-disable-line no-undef 35 | })); 36 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 37 | await driver.findElement(webdriver.By.id('callButton')).click(); 38 | 39 | await Promise.all([ 40 | driver.wait(() => driver.executeScript(() => { 41 | return pc1Remote && pc1Remote.connectionState === 'connected'; // eslint-disable-line no-undef 42 | })), 43 | await driver.wait(() => driver.executeScript(() => { 44 | return pc2Remote && pc2Remote.connectionState === 'connected'; // eslint-disable-line no-undef 45 | })), 46 | ]); 47 | 48 | await Promise.all([ 49 | await driver.wait(() => driver.executeScript(() => { 50 | return document.getElementById('video2').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 51 | })), 52 | await driver.wait(() => driver.executeScript(() => { 53 | return document.getElementById('video3').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 54 | })), 55 | ]); 56 | 57 | await driver.findElement(webdriver.By.id('hangupButton')).click(); 58 | 59 | await Promise.all([ 60 | await driver.wait(() => driver.executeScript(() => { 61 | return pc1Remote === null; // eslint-disable-line no-undef 62 | })), 63 | await driver.wait(() => driver.executeScript(() => { 64 | return pc2Remote === null; // eslint-disable-line no-undef 65 | })), 66 | ]); 67 | }); 68 | }); 69 | 70 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-crop/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Insertable Streams - Crop in a worker 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |

WebRTC samples 36 | Breakout Box crop

37 | 38 |

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

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

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

54 | View source on GitHub 56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/content/peerconnection/channel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection between two tabs 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |

WebRTC samples 36 | Peer connection

37 | 38 |

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

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

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

52 | 53 | View source on GitHub 55 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple-relay/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /* global VideoPipe */ 12 | 13 | const video1 = document.querySelector('video#video1'); 14 | const video2 = document.querySelector('video#video2'); 15 | 16 | const statusDiv = document.querySelector('div#status'); 17 | 18 | const audioCheckbox = document.querySelector('input#audio'); 19 | 20 | const startButton = document.querySelector('button#start'); 21 | const callButton = document.querySelector('button#call'); 22 | const insertRelayButton = document.querySelector('button#insertRelay'); 23 | const hangupButton = document.querySelector('button#hangup'); 24 | 25 | startButton.onclick = start; 26 | callButton.onclick = call; 27 | insertRelayButton.onclick = insertRelay; 28 | hangupButton.onclick = hangup; 29 | 30 | const pipes = []; 31 | 32 | let localStream; 33 | let remoteStream; 34 | 35 | function gotStream(stream) { 36 | console.log('Received local stream'); 37 | video1.srcObject = stream; 38 | localStream = stream; 39 | callButton.disabled = false; 40 | } 41 | 42 | function gotremoteStream(stream) { 43 | remoteStream = stream; 44 | video2.srcObject = stream; 45 | console.log('Received remote stream'); 46 | console.log(`${pipes.length} element(s) in chain`); 47 | statusDiv.textContent = `${pipes.length} element(s) in chain`; 48 | insertRelayButton.disabled = false; 49 | } 50 | 51 | function start() { 52 | console.log('Requesting local stream'); 53 | startButton.disabled = true; 54 | const options = audioCheckbox.checked ? {audio: true, video: true} : {audio: false, video: true}; 55 | navigator.mediaDevices 56 | .getUserMedia(options) 57 | .then(gotStream) 58 | .catch(function(e) { 59 | alert('getUserMedia() failed'); 60 | console.log('getUserMedia() error: ', e); 61 | }); 62 | } 63 | 64 | function call() { 65 | callButton.disabled = true; 66 | insertRelayButton.disabled = false; 67 | hangupButton.disabled = false; 68 | console.log('Starting call'); 69 | pipes.push(new VideoPipe(localStream, gotremoteStream)); 70 | } 71 | 72 | function insertRelay() { 73 | pipes.push(new VideoPipe(remoteStream, gotremoteStream)); 74 | insertRelayButton.disabled = true; 75 | } 76 | 77 | function hangup() { 78 | console.log('Ending call'); 79 | while (pipes.length > 0) { 80 | const pipe = pipes.pop(); 81 | pipe.close(); 82 | } 83 | insertRelayButton.disabled = true; 84 | hangupButton.disabled = true; 85 | callButton.disabled = false; 86 | } 87 | -------------------------------------------------------------------------------- /src/content/peerconnection/negotiate-timing/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/negotiate-timing/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection with negotiation timing', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes a connection, renegotiates and hangs up', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | 33 | await driver.wait(() => driver.executeScript(() => { 34 | return localStream !== null; // eslint-disable-line no-undef 35 | })); 36 | 37 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 38 | await driver.findElement(webdriver.By.id('callButton')).click(); 39 | 40 | await Promise.all([ 41 | await driver.wait(() => driver.executeScript(() => { 42 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 43 | })), 44 | await driver.wait(() => driver.executeScript(() => { 45 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 46 | })), 47 | ]); 48 | 49 | await driver.wait(() => driver.executeScript(() => { 50 | return document.getElementById('remoteVideo').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 51 | })); 52 | 53 | await driver.wait(() => driver.findElement(webdriver.By.id('renegotiateButton')).isEnabled()); 54 | await driver.findElement(webdriver.By.id('renegotiateButton')).click(); 55 | 56 | await driver.wait(() => driver.executeScript(() => { 57 | return document.getElementById('log').innerText !== 'Log goes here'; 58 | })); 59 | const logText = await driver.findElement(webdriver.By.id('log')).getAttribute('innerText'); 60 | expect(logText.split('\n').length).toBe(2); 61 | 62 | await driver.findElement(webdriver.By.id('hangupButton')).click(); 63 | 64 | await driver.wait(() => driver.executeScript(() => { 65 | return pc1 === null; // eslint-disable-line no-undef 66 | })); 67 | }); 68 | }); 69 | 70 | -------------------------------------------------------------------------------- /src/content/insertable-streams/audio-processing/js/worker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | // Adjust this value to increase/decrease the amount of filtering. 10 | // eslint-disable-next-line prefer-const 11 | let cutoff = 100; 12 | 13 | // Returns a low-pass transform function for use with TransformStream. 14 | function lowPassFilter() { 15 | const format = 'f32-planar'; 16 | let lastValuePerChannel = undefined; 17 | return (data, controller) => { 18 | const rc = 1.0 / (cutoff * 2 * Math.PI); 19 | const dt = 1.0 / data.sampleRate; 20 | const alpha = dt / (rc + dt); 21 | const nChannels = data.numberOfChannels; 22 | if (!lastValuePerChannel) { 23 | console.log(`Audio stream has ${nChannels} channels.`); 24 | lastValuePerChannel = Array(nChannels).fill(0); 25 | } 26 | const buffer = new Float32Array(data.numberOfFrames * nChannels); 27 | for (let c = 0; c < nChannels; c++) { 28 | const offset = data.numberOfFrames * c; 29 | const samples = buffer.subarray(offset, offset + data.numberOfFrames); 30 | data.copyTo(samples, {planeIndex: c, format}); 31 | let lastValue = lastValuePerChannel[c]; 32 | 33 | // Apply low-pass filter to samples. 34 | for (let i = 0; i < samples.length; ++i) { 35 | lastValue = lastValue + alpha * (samples[i] - lastValue); 36 | samples[i] = lastValue; 37 | } 38 | 39 | lastValuePerChannel[c] = lastValue; 40 | } 41 | controller.enqueue(new AudioData({ 42 | format, 43 | sampleRate: data.sampleRate, 44 | numberOfFrames: data.numberOfFrames, 45 | numberOfChannels: nChannels, 46 | timestamp: data.timestamp, 47 | data: buffer 48 | })); 49 | }; 50 | } 51 | 52 | let abortController; 53 | 54 | onmessage = async (event) => { 55 | if (event.data.command == 'abort') { 56 | abortController.abort(); 57 | abortController = null; 58 | } else { 59 | const source = event.data.source; 60 | const sink = event.data.sink; 61 | const transformer = new TransformStream({transform: lowPassFilter()}); 62 | abortController = new AbortController(); 63 | const signal = abortController.signal; 64 | const promise = source.pipeThrough(transformer, {signal}).pipeTo(sink); 65 | promise.catch((e) => { 66 | if (signal.aborted) { 67 | console.log('Shutting down streams after abort.'); 68 | } else { 69 | console.error('Error from stream transform:', e); 70 | } 71 | source.cancel(e); 72 | sink.abort(e); 73 | }); 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /src/content/peerconnection/multiple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Multiple peer connections 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Multiple peer connections 37 |

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

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

50 | 51 |

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

54 | 55 | 56 | View source on GitHub 58 | 59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/content/peerconnection/change-codecs/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/change-codecs/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection with setCodecPreferences', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | ['video/VP8'].forEach(codec => { 31 | it('establishes a connection', async () => { 32 | await driver.findElement(webdriver.By.id('startButton')).click(); 33 | 34 | await driver.wait(() => driver.executeScript(() => { 35 | return localStream !== null; // eslint-disable-line no-undef 36 | })); 37 | await driver.wait(() => driver.executeScript(() => { 38 | return codecPreferences.disabled === false; // eslint-disable-line no-undef 39 | })); 40 | await driver.findElement(webdriver.By.id('codecPreferences')).click(); 41 | await driver.findElement(webdriver.By.css('option[value=\'video/VP8\']')).click(); 42 | 43 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 44 | await driver.findElement(webdriver.By.id('callButton')).click(); 45 | 46 | await Promise.all([ 47 | await driver.wait(() => driver.executeScript(() => { 48 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 49 | })), 50 | await driver.wait(() => driver.executeScript(() => { 51 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 52 | })), 53 | ]); 54 | 55 | await driver.wait(() => driver.executeScript(() => { 56 | return document.getElementById('remoteVideo').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 57 | })); 58 | 59 | await driver.wait(() => driver.executeScript(() => { 60 | return document.getElementById('actualCodec').innerText !== ''; 61 | })); 62 | const actualCodec = await driver.findElement(webdriver.By.id('actualCodec')).getAttribute('innerText'); 63 | expect(actualCodec.startsWith('Using ' + codec)).toBe(true); 64 | }); 65 | }); 66 | }); 67 | 68 | -------------------------------------------------------------------------------- /src/content/insertable-streams/video-processing/js/video-mirror-helper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Helper to display a MediaStream in an HTMLVideoElement, based on the 13 | * visibility setting. 14 | */ 15 | class VideoMirrorHelper { // eslint-disable-line no-unused-vars 16 | constructor() { 17 | /** @private {boolean} */ 18 | this.visibility_ = false; 19 | /** @private {?MediaStream} the stream to display */ 20 | this.stream_ = null; 21 | /** 22 | * @private {?HTMLVideoElement} video element mirroring the camera stream. 23 | * Set if visibility_ is true and stream_ is set. 24 | */ 25 | this.video_ = null; 26 | /** @private {string} */ 27 | this.debugPath_ = ''; 28 | } 29 | /** 30 | * Sets the path to this object from the debug global var. 31 | * @param {string} path 32 | */ 33 | setDebugPath(path) { 34 | this.debugPath_ = path; 35 | } 36 | /** 37 | * Indicates if the video should be mirrored/displayed on the page. 38 | * @param {boolean} visible whether to add the video from the source stream to 39 | * the page 40 | */ 41 | setVisibility(visible) { 42 | this.visibility_ = visible; 43 | if (this.video_ && !this.visibility_) { 44 | this.video_.parentNode.removeChild(this.video_); 45 | this.video_ = null; 46 | } 47 | this.maybeAddVideoElement_(); 48 | } 49 | 50 | /** 51 | * @param {!MediaStream} stream 52 | */ 53 | setStream(stream) { 54 | this.stream_ = stream; 55 | this.maybeAddVideoElement_(); 56 | } 57 | 58 | /** @private */ 59 | maybeAddVideoElement_() { 60 | if (!this.video_ && this.visibility_ && this.stream_) { 61 | this.video_ = 62 | /** @type {!HTMLVideoElement} */ (document.createElement('video')); 63 | console.log( 64 | '[VideoMirrorHelper] Adding source video mirror.', 65 | `${this.debugPath_}.video_ =`, this.video_); 66 | this.video_.classList.add('video', 'sourceVideo'); 67 | this.video_.srcObject = this.stream_; 68 | const outputVideoContainer = 69 | document.getElementById('outputVideoContainer'); 70 | outputVideoContainer.parentNode.insertBefore( 71 | this.video_, outputVideoContainer); 72 | this.video_.play(); 73 | } 74 | } 75 | 76 | /** Frees any resources used by this object. */ 77 | destroy() { 78 | if (this.video_) { 79 | this.video_.pause(); 80 | this.video_.srcObject = null; 81 | this.video_.parentNode.removeChild(this.video_); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/content/datachannel/messaging/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Send messages with datachannel 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 | 40 |

WebRTC samples Send messages with datachannel 41 |

42 |
43 | 44 |

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

45 | 46 |

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

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

View the console to see logging.

55 | 56 |

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

59 |
60 | 61 | View source on GitHub 63 |
64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/content/capture/canvas-filter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Video to Canvas to video 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

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

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

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

47 |

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

49 |

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

51 | 52 |

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

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

${msg}

`; 66 | if (typeof error !== 'undefined') { 67 | console.error(error); 68 | } 69 | } 70 | 71 | async function init(e) { 72 | try { 73 | const stream = await navigator.mediaDevices.getUserMedia(constraints); 74 | handleSuccess(stream); 75 | e.target.disabled = true; 76 | } catch (e) { 77 | handleError(e); 78 | } 79 | } 80 | 81 | document.querySelector('#showVideo').addEventListener('click', e => init(e)); 82 | -------------------------------------------------------------------------------- /src/content/peerconnection/negotiate-timing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection - Renegotiate 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |

WebRTC samples 36 | Peer connection negotiation timing

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

49 | Video sections after renegotiating: 50 |

51 |
52 |

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

55 |

56 |

57 | Log goes here 58 |
59 |

60 | View source on GitHub 62 | 63 |
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/content/capture/worker-process/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Video processing in a Worker 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Processing video data with a Worker 37 |

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

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

48 |

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

50 |

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

51 | 52 |

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

54 |

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

56 |

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

57 | View source on GitHub 59 | 60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/content/peerconnection/upgrade/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection - upgrade 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |

WebRTC samples 36 | Peer connection

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

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

51 | 52 |

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

55 | 56 | 57 | View source on GitHub 59 | 60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/content/getusermedia/pan-tilt-zoom/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Control camera pan, tilt, and zoom 24 | 25 | 26 | 27 | 28 | 29 | 37 | 38 | 39 | 40 | 41 |
42 |

WebRTC samples 43 | Control Pan-Tilt-Zoom Camera

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

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

65 | 66 |

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

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

WebRTC samples Peer connection as input to Web Audio 37 |

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

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

53 | 54 |

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

57 | 58 | 59 | View source on GitHub 61 | 62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/content/insertable-streams/endtoend-encryption/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Peer connection end to end encryption 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |

WebRTC samples Peer connection end to end encryption 37 |

38 | 39 |

40 |

Sender and receiver

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

50 |

51 |

Middlebox

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

57 | 58 |
59 | 60 | 61 | 62 |
63 |
64 | 65 | View source on GitHub 67 | 68 |
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/content/peerconnection/restart-ice/js/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | /* eslint-env node */ 9 | 10 | 'use strict'; 11 | const webdriver = require('selenium-webdriver'); 12 | const seleniumHelpers = require('../../../../../test/webdriver'); 13 | 14 | let driver; 15 | const path = '/src/content/peerconnection/restart-ice/index.html'; 16 | const url = `${process.env.BASEURL ? process.env.BASEURL : ('file://' + process.cwd())}${path}`; 17 | 18 | describe('peerconnection ice restart', () => { 19 | beforeAll(async () => { 20 | driver = await seleniumHelpers.buildDriver(); 21 | }); 22 | afterAll(() => { 23 | return driver.quit(); 24 | }); 25 | 26 | beforeEach(() => { 27 | return driver.get(url); 28 | }); 29 | 30 | it('establishes a connection and changes candidates on restart', async () => { 31 | await driver.findElement(webdriver.By.id('startButton')).click(); 32 | 33 | await driver.wait(() => driver.executeScript(() => { 34 | return localStream !== null; // eslint-disable-line no-undef 35 | })); 36 | 37 | await driver.wait(() => driver.findElement(webdriver.By.id('callButton')).isEnabled()); 38 | await driver.findElement(webdriver.By.id('callButton')).click(); 39 | 40 | await driver.wait(() => driver.executeScript(() => { 41 | return pc1 && pc1.connectionState === 'connected'; // eslint-disable-line no-undef 42 | })); 43 | await driver.wait(() => driver.executeScript(() => { 44 | return pc2 && pc2.connectionState === 'connected'; // eslint-disable-line no-undef 45 | })); 46 | 47 | await driver.wait(() => driver.executeScript(() => { 48 | return document.getElementById('remoteVideo').readyState === HTMLMediaElement.HAVE_ENOUGH_DATA; 49 | })); 50 | await driver.wait(() => driver.findElement(webdriver.By.id('restartButton')).isEnabled()); 51 | 52 | const firstCandidateIds = await Promise.all([ 53 | await driver.findElement(webdriver.By.id('localCandidateId')).getAttribute('innerText'), 54 | await driver.findElement(webdriver.By.id('remoteCandidateId')).getAttribute('innerText'), 55 | ]); 56 | 57 | await driver.wait(() => driver.findElement(webdriver.By.id('restartButton')).isEnabled()); 58 | await driver.findElement(webdriver.By.id('restartButton')).click(); 59 | 60 | await driver.wait(() => driver.findElement(webdriver.By.id('restartButton')).isEnabled()); 61 | 62 | const secondCandidateIds = await Promise.all([ 63 | await driver.findElement(webdriver.By.id('localCandidateId')).getAttribute('innerText'), 64 | await driver.findElement(webdriver.By.id('remoteCandidateId')).getAttribute('innerText'), 65 | ]); 66 | 67 | expect(secondCandidateIds[0]).not.toBe(firstCandidateIds[0]); 68 | expect(secondCandidateIds[1]).not.toBe(firstCandidateIds[1]); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/content/getusermedia/volume/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a BSD-style license 5 | * that can be found in the LICENSE file in the root of the source 6 | * tree. 7 | */ 8 | 9 | /* global AudioContext, SoundMeter */ 10 | 11 | 'use strict'; 12 | 13 | const startButton = document.getElementById('startButton'); 14 | const stopButton = document.getElementById('stopButton'); 15 | startButton.onclick = start; 16 | stopButton.onclick = stop; 17 | 18 | const instantMeter = document.querySelector('#instant meter'); 19 | const slowMeter = document.querySelector('#slow meter'); 20 | const clipMeter = document.querySelector('#clip meter'); 21 | 22 | const instantValueDisplay = document.querySelector('#instant .value'); 23 | const slowValueDisplay = document.querySelector('#slow .value'); 24 | const clipValueDisplay = document.querySelector('#clip .value'); 25 | 26 | // Put variables in global scope to make them available to the browser console. 27 | const constraints = window.constraints = { 28 | audio: true, 29 | video: false 30 | }; 31 | 32 | let meterRefresh = null; 33 | 34 | function handleSuccess(stream) { 35 | // Put variables in global scope to make them available to the 36 | // browser console. 37 | window.stream = stream; 38 | const soundMeter = window.soundMeter = new SoundMeter(window.audioContext); 39 | soundMeter.connectToSource(stream, function(e) { 40 | if (e) { 41 | alert(e); 42 | return; 43 | } 44 | meterRefresh = setInterval(() => { 45 | instantMeter.value = instantValueDisplay.innerText = 46 | soundMeter.instant.toFixed(2); 47 | slowMeter.value = slowValueDisplay.innerText = 48 | soundMeter.slow.toFixed(2); 49 | clipMeter.value = clipValueDisplay.innerText = 50 | soundMeter.clip; 51 | }, 200); 52 | }); 53 | } 54 | 55 | function handleError(error) { 56 | console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name); 57 | } 58 | 59 | 60 | function start() { 61 | console.log('Requesting local stream'); 62 | startButton.disabled = true; 63 | stopButton.disabled = false; 64 | 65 | try { 66 | window.AudioContext = window.AudioContext || window.webkitAudioContext; 67 | window.audioContext = new AudioContext(); 68 | } catch (e) { 69 | alert('Web Audio API not supported.'); 70 | } 71 | 72 | navigator.mediaDevices 73 | .getUserMedia(constraints) 74 | .then(handleSuccess) 75 | .catch(handleError); 76 | } 77 | 78 | function stop() { 79 | console.log('Stopping local stream'); 80 | startButton.disabled = false; 81 | stopButton.disabled = true; 82 | 83 | window.stream.getTracks().forEach(track => track.stop()); 84 | window.soundMeter.stop(); 85 | window.audioContext.close(); 86 | clearInterval(meterRefresh); 87 | instantMeter.value = instantValueDisplay.innerText = ''; 88 | slowMeter.value = slowValueDisplay.innerText = ''; 89 | clipMeter.value = clipValueDisplay.innerText = ''; 90 | } 91 | --------------------------------------------------------------------------------