├── .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 |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 |The page has moved to: 17 | this page
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/peerconnection/video-analyzer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 |The page has moved to: 17 | this page
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/content/peerconnection/endtoend-encryption/index.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 |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_ = 'Draw a frame from the video onto the canvas element using the drawImage() method.
The variables canvas, video and stream are in global scope, so you can
45 | inspect them from the console.
Warning: if you're not using headphones, pressing play will cause feedback.
43 | 44 |Display the video stream from getUserMedia() in a video element.
The MediaStream object stream passed to the getUserMedia() callback is in
47 | global scope, so you can inspect it from the console.
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.
The MediaStream object stream passed to the getUserMedia()
45 | callback is in global scope, so you can inspect it from the console.
${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 |View the console to see logging and to inspect the MediaStream object localStream.
Display the screensharing stream from getDisplayMedia() in a video element.
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.
The stream variable are in global scope, so you can inspect them from the browser console.
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 |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 |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 |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 |View the console to see logging and to inspect the MediaStream object localStream.
For more information about RTCPeerConnection, see Getting 53 | Started With WebRTC.
54 | 55 | 56 | View source on GitHub 58 | 59 |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 |View the console to see logging.
55 | 56 |For more information about RTCDataChannel, see Getting Started With WebRTC.
59 |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.
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.
${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 |49 | Video sections after renegotiating: 50 |
51 |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.
56 |
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 |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.
For more information about RTCPeerConnection, see Getting 54 | Started With WebRTC.
55 | 56 | 57 | View source on GitHub 59 | 60 |Display the video stream from getUserMedia() in a video
64 | element and control pan, tilt, and zoom if camera supports Pan-Tilt-Zoom.
The MediaStreamTrack object track is in
67 | global scope, so you can inspect it from the console.
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.
For more information about RTCPeerConnection, see Getting 56 | Started With WebRTC.
57 | 58 | 59 | View source on GitHub 61 | 62 |40 |
51 |