Files
archived-vdo.ninja/whip.html
2024-10-17 04:34:18 -04:00

998 lines
37 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>WHIP/WHEP Client - WebRTC Streaming Tool</title>
<meta name="description" content="A powerful WHIP/WHEP client for WebRTC streaming, leveraging VDO.Ninja for seamless audio and video transmission.">
<meta name="keywords" content="WHIP, WHEP, WebRTC, streaming, VDO.Ninja, MediaMTX">
<meta name="author" content="Your Name or Company">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://vdo.ninja/">
<meta property="og:title" content="WHIP/WHEP Client - WebRTC Streaming Tool">
<meta property="og:description" content="A powerful WHIP/WHEP client for WebRTC streaming, leveraging VDO.Ninja for seamless audio and video transmission.">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://vdo.ninja/">
<meta property="twitter:title" content="WHIP/WHEP Client - WebRTC Streaming Tool">
<meta property="twitter:description" content="A powerful WHIP/WHEP client for WebRTC streaming, leveraging VDO.Ninja for seamless audio and video transmission.">
<!-- Favicon -->
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
<link id="favicon1" rel="icon" type="image/png" sizes="32x32" href="./media/favicon-32x32.png" />
<link id="favicon2" rel="icon" type="image/png" sizes="16x16" href="./media/favicon-16x16.png" />
<link id="favicon3" rel="icon" href="./media/favicon.ico" />
<!-- Styles -->
<link rel="stylesheet" href="./lineawesome/css/line-awesome.min.css" />
<style>
html {
border: 0;
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
body {
padding: 0;
min-height: 100vh;
width: 100%;
background: linear-gradient(to top, #363644, 50%, #151b29) fixed;
font-size: 2em;
font-family: Helvetica, Arial, sans-serif;
display: flex;
flex-flow: column;
margin: 0;
overflow-y: auto;
overflow-x: hidden;
}
video {
margin: 0;
padding: 0;
overflow: hidden;
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=), none;
user-select: none;
}
#moreinfo {
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
margin: 2em;
color:white;
transform:scale(120%);
}
a {
color:white;
}
button.glyphicon-button:focus,
button.glyphicon-button:active:focus,
button.glyphicon-button.active:focus,
button.glyphicon-button.focus,
button.glyphicon-button:active.focus,
button.glyphicon-button.active.focus {
outline: none !important;
}
.gobutton {
font-size:14px;
font-weight: bold;
border: none;
background: #6aab23;
display: flex;
border-radius: 0px;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
box-shadow: 0 12px 15px -10px #5ca70b, 0 2px 0px #6aab23;
color: white;
cursor: pointer;
box-sizing: border-box;
align-items: center;
padding: 0 1em;
min-width: 50px;
}
.details{
font-size: 14px;
font-weight: bold;
border: none;
background: #555;
display: flex;
border-radius: 0px;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
box-shadow: 0 12px 15px -10px #444, 0 2px 0px #555;
color: white;
box-sizing: border-box;
align-items: center;
padding: 0 1em;
min-width: 50px;
}
#header{
width:100%;
background-color: #101520;
}
.changeText {
font-size: 1em;
align-self: center;
width: 100%;
padding: 1em;
font-weight: bold;
background: white;
border: 4px solid white;
box-shadow: 0px 30px 40px -32px #6aab23, 0 2px 0px #6aab23;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
transition: all 0.2s linear;
box-sizing: border-box;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
.changeText:focus {
outline: none;
}
select.changetext{
padding: .1em;
}
.container{
font-size: 16px;
align-self:center;
max-width: 100%;
width: 720px;
margin: auto auto;
}
label {
font: white;
font-size: 1em;
color: white;
}
input[type='checkbox'] {
-webkit-appearance:none;
width:30px;
height:30px;
background:white;
border-radius:5px;
border:2px solid #555;
cursor: pointer;
}
input[type='checkbox']:checked {
background: #1A1;
}
#audioOutput, #lastUrls {
font-size: calc(16px + 0.3vw);
width: 730px;
height: 100%;
flex: 20;
border-radius: 10px;
padding: 1em;
background: #eaeaea;
cursor:pointer;
}
label[for="audioOutput"] {
font-size: 3em;
color: #FE53BB;
text-shadow: 0px 0px 30px #fe53bb;
padding-right: 10px;
}
label[for="changeText"] {
font-size: 3em;
color: #00F6FF;
text-shadow: 0px 0px 30px #00f6ff;
padding-top: 5px;
padding-right: 10px;
}
label[for="lastUrls"] {
font-size: 3em;
color: #1a1;
text-shadow: 0px 0px 30px #1a1;
padding-right: 10px;
cursor: pointer;
}
div#audioOutputContainer, #history {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
margin: 2em;
}
@media only screen and (max-width: 1030px) {
body{
zoom: 0.9;
-moz-transform: scale(0.9);
-moz-transform-origin: 0 0;
}
}
#messageDiv {
font-size: .7em;
color: #DDD;
transition: all 0.5s linear;
font-style: italic;
opacity: 0;
text-align: center;
margin: 10px 0;
}
div.urlInput {
padding: 0 0 1vh 0;
}
@media only screen and (max-height: 639px) {
div.urlInput {
}
div#audioOutputContainer, #history {
margin: 1em;
}
}
@media only screen and (max-width: 767px) {
div.urlInput {
}
div#audioOutputContainer, #history {
margin: 2em 1em;
}
}
@media only screen and (max-height: 380px) {
div.urlInput {
}
div#audioOutputContainer, #history {
margin: 1em;
}
}
label[for="audioOutput"], label[for="lastUrls"] {
font-size: 3em;
}
#warning4mac, #electronVersion {
background: #8500f7;
box-shadow: 0px 0px 50px 10px #8500f7ab, inset 0px 0px 10px 2px #8d08ffba;
border: 2px solid #8500f7;
border-radius: 10px;
width: 90%;
padding:1em;
margin:0 auto;
color:white;
font-size:1.3em;
margin-bottom: 20px;
}
#warning4mac a, #electronVersion a {
color:white;
}
ul#lastUrls {
list-style: none;
background: #101520;
color: white;
padding: 1em;
}
ul#lastUrls li {
padding: 5px 0px;
}
ul#lastUrls li:nth-child(even) {
background-color: #182031;
}
.inputComboGrid,.inputCombo {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
flex-grow: 1;
}
@media only screen and (max-width: 699px) {
.inputComboGrid {
display: grid;
padding: 0px 5px;
}
.inputComboGrid > * {
margin: 2px 0;
}
}
#version{
margin: 0 auto;
font-size: 30%;
display: inline-block;
color: #000A;
}
h3 {
color: #b0e3ff;
}
.hidden{
display:none;
opacity:0;
visibility:none;
width:0;
height:0
}
</style>
</head>
<body>
<div id="header" style="-webkit-app-region: drag; color:#6f6f6f;font-size:20px; line-height: 20px; padding: 5px 10px; letter-spacing: 3; font-weight: bold;">WHIP / WHEP simple sample setup</div>
<div class="container">
<div id="urlInput1" class="urlInput" title="Put the link you want to load here">
<h3>Publish a video from VDO.Ninja to a WHIP ingestion end-point</h3>
<div class="inputCombo" id="inputCombo1">
<label for="changeText">
<i class="las la-upload"></i>
</label>
<input type="text" id="changeText1" class="inputfield changeText" placeholder="WHIP Publishing URL" />
<button onclick="gohere1();" class="gobutton" id="gobutton1">GO</button>
</div>
<div >
<div class="inputCombo" style="margin: 10px 0px 10px 10px;">
<input type="password" id="changeText1a" class="inputfield changeText" placeholder="🗝️ Authentication Bearer Token (optional)" />
<div class="details">⚙️</div>
</div>
<div class="inputComboGrid" id="advanced" style="margin: 10px 0px 10px 10px;">
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="whipoutaudiobitrate" title="Which audio bitrate target would you prefer? 128-kbps is fine for music." >
<option value="0" selected>🎙Default Audio Bitrate</option>
<option value="32">🎙32-kbps</option>
<option value="64">🎙64-kbps</option>
<option value="128">🎙128-kbps</option>
<option value="256">🎙256-kbps</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="vbrcbr" title="Whether the audio bitrate with be constant or variable" >
<option value="cbr" selected>🎙CBR</option>
<option value="vbr">🎙VBR</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="denoise" onchange="checkStereo()" title="Turn off to improve clarity, but you'll hear any background noise" >
<option value="0" selected>🎙Denoise Off</option>
<option value="1">🎙Denoise On</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="autogain" onchange="checkStereo()" title="Auto-controls the input volume; turn off to manage that yourself." >
<option value="0" selected>🎙Auto Gain Off</option>
<option value="1">🎙Auto Gain On</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="stereo" title="Stereo is available only if auto-gain and noise-reduction is off." >
<option value="1" selected>🎙Stereo</option>
<option value="0">🎙Mono</option>
</select >
</div>
<div class="inputComboGrid" id="advanced2" style="margin: 10px 0px 10px 10px;">
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="bitrateGroupFlag" title="Which video bitrate target would you prefer?" >
<option value="0" selected>🎦Default Video Bitrate</option>
<option value="500">🎦500-kbps</option>
<option value="2500">🎦2500-kbps</option>
<option value="6000">🎦6000-kbps</option>
<option value="20000">🎦20000-kbps</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="codecGroupFlag" onchange="updateSVC();" title="Which video codec would you prefer to be used if available?" >
<option value="default" selected>🎦OpenH264</option>
<option id="av1codec" value="av1">🎦AV1</option>
<option value="vp9">🎦VP9</option>
<option value="vp8">🎦VP8</option>
<option value="h264">🎦H264</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="svcGroupFlag" title="Which scalable video coding do you want to use?" >
<option value="0" selected>🎦 SVC Off</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="e2eeGroupFlag1" title="E2EE uses insertable streams; not everthing supports this" >
<option value="0" selected>🔑 E2EE Off</option>
<option value="1">🔑 E2EE On</option>
</select >
</div>
<div class="inputComboGrid" id="advanced2a" style="margin: 10px 0px 10px 10px;">
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="keyFrameRateFlag" title="Tries to force a minimum keyframe rate internal. This may hurt quality as the method of triggering a keyframe from the browser may cause a flicker or a few blurry frames. Without it though, viewers may not be able to load the video promptly." >
<option value="0" selected>🎦 Force Keyframes Interval: Off</option>
<option value="2000">🎦 Force Keyframes Interval: 2s</option>
<option value="6000">🎦 Force Keyframes Interval: 6s</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="whipwaitFlag" title="Time to wait for ICE candidates before sending the offer. Needed if you whip server is behind a firewall.">
<option value="0">⌛Do not wait for ICE candidates</option>
<option selected value="200">⌛Wait 200-ms for ICE candidates</option>
<option value="500">⌛Wait 500-ms for ICE candidates</option>
<option value="1000">⌛Wait 1000-ms for ICE candidates</option>
<option value="5000">⌛Wait 5000-ms for ICE candidates</option>
</select>
</div>
</div>
</div>
<br />
<div id="urlInput1a" class="urlInput" title="Put the link you want to load here">
<h3>Publish a video from VDO.Ninja to your Twitch channel</h3>
<div class="inputCombo" id="inputCombo1t">
<label for="changeText">
<i class="las la-upload"></i>
</label>
<input type="password" id="changeText1t" autocomplete="changeText1twitcha" class="inputfield changeText" placeholder="Enter your Twitch stream token here" />
<button onclick="gohere1t();" class="gobutton" id="gobutton1t">GO</button>
</div>
<div class="hidden">
<div class="inputCombo" style="margin: 10px 0px 10px 10px;" autocomplete="changeText1twitchb" title="If you want to have viewers of this VDO.Ninja link auto-load your Twitch stream">
<input type="text" class="inputfield changeText" placeholder="Twitch user name (optional)" />
<div class="details">📻</div>
</div>
</div>
</div>
<br /><br /><br />
<div id="urlInput2" class="urlInput"title="Put the WHIP token you want to listen for">
<h3>Setup VDO.Ninja to be a WHIP-ingestion end-point (ie: OBS -> VDO)</h3>
<div class="inputCombo" id="inputCombo2">
<label for="changeText">
<i class="las la-play"></i>
</label>
<input type="text" id="changeText2" class="inputfield changeText" placeholder="Ingest WHIP token" />
<button onclick="gohere2();" class="gobutton" id="gobutton2">GO</button>
</div>
<h3 style="text-align: center;color:#ccc;"><i>The WHIP endpoint for VDO.Ninja is <b>https://whip.vdo.ninja</b></i></h3>
</div>
<div id="urlInput3" class="urlInput"title="Put the link you want to play here">
<h3>Play a remote video stream available via WHEP</h3>
<div class="inputCombo" id="inputCombo3">
<label for="changeText">
<i class="las la-play"></i>
</label>
<input type="text" id="changeText3" class="inputfield changeText" placeholder="WHEP Play URL" />
<button onclick="gohere3();" class="gobutton" id="gobutton3">GO</button>
</div>
<div class="inputCombo" style="margin: 10px 0px 10px 10px;">
<input type="password" id="changeText3a" class="inputfield changeText" placeholder="🗝️ Authentication Bearer Token (optional)" />
<div class="details">⚙️</div>
</div>
<div class="inputComboGrid" id="advancedwhep" style="margin: 10px 0px 10px 10px;">
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="whepbuffer" title="Adding a playback buffer can help reduce frame loss or jitter" >
<option value="0" selected>⌛No added playback buffer</option>
<option value="500">⌛500-ms added</option>
<option value="1000">⌛1000-ms added</option>
<option value="2000">⌛2000-ms added</option>
<option value="3000">⌛3000-ms added</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="whepicewait" title="Adding a playback buffer can help reduce frame loss or jitter" >
<option value="0">⌛Do not wait for ICE candidates</option>
<option value="500">⌛Wait 500-ms for ICE candidates</option>
<option value="1000">⌛Wait 1000-ms for ICE candidates</option>
<option value="2000" selected>⌛Wait 2000-ms for ICE candidates</option>
<option value="5000">⌛Wait 5000-ms for ICE candidates</option>
</select >
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="e2eeGroupFlag2" title="E2EE uses insertable streams; not everthing supports this" >
<option value="0" selected>🔑 E2EE Off</option>
<option value="1">🔑 E2EE On</option>
</select >
</div>
<div class="inputComboGrid" id="advanced3" style="margin: 10px 0px 10px 10px;">
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="stereowhep" title="Stereo is available only if auto-gain and noise-reduction is off." >
<option value="1" selected>🎙Stereo</option>
<option value="0">🎙Mono</option>
</select>
</div>
</div>
<br /><br />
<div id="urlInput4" class="urlInput" title="Start a VDO.Ninja stream, and then after, access it remotely via WHEP">
<h3>Host a VDO.Ninja stream as a WHEP source</h3>
<div class="inputCombo" id="inputCombo4">
<label for="changeText">
<i class="las la-broadcast-tower"></i>
</label>
<input type="text" id="changeText4" class="inputfield changeText" oninput="change4()" onchange="change4()" placeholder="The WHEP Token you wish to use goes here" />
<button onclick="gohere4();" class="gobutton" id="gobutton4" onclick="gohere4();" >GO</button>
</div>
<h3 style="text-align: center;color:#ccc;"><i>The WHEP endpoint for this is <a href='' id="whepoutsrc" target="_blank">https://whep.vdo.ninja/<span id='whepoutid'>WHEP_TOKEN_HERE</span></a></i></h3>
</div>
<div id="history" title="History of past links used. You can clear this history using the button to the left">
<h3 style='cursor:pointer;' onclick="resetHistory()">Clear History</h3>
<label for="lastUrls" onclick="resetHistory()">
<i class="las la-history"></i>
</label>
</div>
<br /><br /><br /><br /><br />
<div id="moreinfo">
<h3 style='cursor:pointer;'>More information and options</h3>
For more WHIP/WHEP options, tools, services, and documentation, please see: <a href='https://docs.vdo.ninja/steves-helper-apps/whip-and-whep-tooling' target="_blank"><br /><br />
<b>https://docs.vdo.ninja/steves-helper-apps/whip-and-whep-tooling</b></a>
<br />
<h3 style='cursor:pointer;'>For community support</h3>
For support, join our <a href='https://discord.vdo.ninja' target="_blank">Discord server here</a>.
<br /><br />
<div id="additional-info">
<h2>About WHIP/WHEP and MediaMTX Integration</h2>
<p>WHIP (WebRTC-HTTP ingestion protocol) and WHEP (WebRTC-HTTP egress protocol) are standardized protocols for WebRTC streaming. This client utilizes VDO.Ninja as the WebRTC layer to provide a powerful and flexible streaming solution.</p>
<h3>Using MediaMTX</h3>
<p>To easily configure this client for use with MediaMTX, you can use the <code>&mediamtx</code> parameter in the URL. Here's what you need to know:</p>
<ul>
<li>Simply add <code>&mediamtx=domainname</code> to your URL to configure the client for MediaMTX.</li>
<li>The default port for MediaMTX is 8889. If you're using the default port, you don't need to specify it.</li>
<li>If you're using a non-standard port, include it like this: <code>&mediamtx=domainname.com:443</code></li>
<li>MediaMTX doesn't support <code>stereo=0</code>, so this setting is automatically removed if detected.</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li>Ensure your MediaMTX server is properly configured and accessible.</li>
<li>For optimal performance, use a wired internet connection when streaming.</li>
<li>Be aware of your bandwidth limitations when configuring video and audio bitrates.</li>
<li>Some browsers may have limitations with certain codecs. If you experience issues, try switching to a different codec.</li>
</ul>
<h3>Troubleshooting</h3>
<p>If you encounter issues:</p>
<ul>
<li>Check your network connection and firewall settings.</li>
<li>Ensure your MediaMTX server is running and accessible.</li>
<li>Try using different browsers or updating your current browser.</li>
<li>Verify that your camera and microphone are properly connected and permitted in your browser settings.</li>
</ul>
<p>For more detailed information on WHIP and WHEP, visit the <a href="https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html" target="_blank">IETF WHIP draft</a> and <a href="https://www.ietf.org/archive/id/draft-murillo-whep-00.html" target="_blank">IETF WHEP draft</a>.</p>
</div>
<br />
<div id="about-vdo-ninja">
<h2>About VDO.Ninja</h2>
<p>VDO.Ninja is a powerful, free, and open-source platform for live video production. It uses peer-to-peer technology to bring remote cameras into OBS or other studio software, allowing content creators to produce real-time live shows using remote media streams.</p>
<p>Key features of VDO.Ninja include:</p>
<ul>
<li>High-quality, low-latency video streaming</li>
<li>Peer-to-peer technology for direct video transfer</li>
<li>Ability to turn smartphones into wireless webcams</li>
<li>Compatible with OBS and other studio software</li>
<li>Free to use with no account required</li>
</ul>
<p>VDO.Ninja is designed to be serverless, ensuring it can be offered for free while providing increased levels of security and privacy.</p>
<h3>Open Source</h3>
<p>Both VDO.Ninja and this WHIP/WHEP client are open-source projects. You can find their source code and contribute to their development on GitHub:</p>
<ul>
<li>VDO.Ninja: <a href="https://github.com/steveseguin/vdoninja" target="_blank">https://github.com/steveseguin/vdoninja</a></li>
<li>WHIP/WHEP Client: <a href="https://github.com/steveseguin/vdo.ninja/blob/develop/whip.html" target="_blank">https://github.com/steveseguin/vdo.ninja/blob/develop/whip.html</a></li>
</ul>
<p>We encourage the community to contribute, report issues, and suggest improvements to help make these tools even better for everyone.</p>
</div>
<br /><br /><br /><br />
</div>
<br /><br /><br /><br />
</div>
<script>
var domain = "./";
document.querySelector("#changeText1").value = localStorage.getItem('changeText1') || "";
document.querySelector("#changeText1t").value = localStorage.getItem('changeText1t') || "";
document.querySelector("#changeText1a").value = localStorage.getItem('changeText1a') || "";
document.querySelector("#changeText2").value = localStorage.getItem('changeText2') || "";
if (localStorage.getItem('changeText3')!==null){
document.getElementById('changeText3').value = localStorage.getItem('changeText3');
}
if (localStorage.getItem('changeText3a')!==null){
document.getElementById('changeText3a').value = localStorage.getItem('changeText3a');
}
if (localStorage.getItem('whepbuffer')!==null){
document.getElementById('whepbuffer').value = localStorage.getItem('whepbuffer');
}
if (localStorage.getItem('whepicewait')!==null){
document.getElementById('whepicewait').value = localStorage.getItem('whepicewait');
}
if (localStorage.getItem('bitrateGroupFlag')!==null){
document.getElementById('bitrateGroupFlag').value = localStorage.getItem('bitrateGroupFlag');
}
if (localStorage.getItem('codecGroupFlag')!==null){
document.getElementById('codecGroupFlag').value = localStorage.getItem('codecGroupFlag');
}
if (localStorage.getItem('keyFrameRateFlag')!==null){
document.getElementById('keyFrameRateFlag').value = localStorage.getItem('keyFrameRateFlag');
}
if (localStorage.getItem('svcGroupFlag')!==null){
document.getElementById('svcGroupFlag').value = localStorage.getItem('svcGroupFlag');
}
if (localStorage.getItem('whipoutaudiobitrate')!==null){
document.getElementById('whipoutaudiobitrate').value = localStorage.getItem('whipoutaudiobitrate');
}
if (localStorage.getItem('vbrcbr')!==null){
document.getElementById('vbrcbr').value = localStorage.getItem('vbrcbr');
}
if (localStorage.getItem('autogain')!==null){
document.getElementById('autogain').value = localStorage.getItem('autogain');
}
if (localStorage.getItem('stereo')!==null){
document.getElementById('stereo').value = localStorage.getItem('stereo');
}
if (localStorage.getItem('denoise')!==null){
document.getElementById('denoise').value = localStorage.getItem('denoise');
}
if (localStorage.getItem('e2eeGroupFlag1')!==null){
document.getElementById('e2eeGroupFlag1').value = localStorage.getItem('e2eeGroupFlag1');
}
if (localStorage.getItem('e2eeGroupFlag2')!==null){
document.getElementById('e2eeGroupFlag2').value = localStorage.getItem('e2eeGroupFlag2');
}
if (localStorage.getItem('stereowhep')!==null){
document.getElementById('stereowhep').value = localStorage.getItem('stereowhep');
}
if (localStorage.getItem('whipwaitFlag')!==null){
document.getElementById('whipwaitFlag').value = localStorage.getItem('whipwaitFlag');
}
const scalabilityModes = [
'L1T1',
'L1T2',
'L1T3',
'L2T1',
'L2T2',
'L2T3',
'L3T1',
'L3T2',
'L3T3',
'L2T1h',
'L2T2h',
'L2T3h',
'S2T1',
'S2T2',
'S2T3',
'S2T1h',
'S2T2h',
'S2T3h',
'S3T1',
'S3T2',
'S3T3',
'S3T1h',
'S3T2h',
'S3T3h',
'L2T2_KEY',
'L2T3_KEY',
'L3T2_KEY',
'L3T3_KEY'
];
function gohere1(){
if (document.getElementById('changeText1').value){
localStorage.setItem('changeText1', document.getElementById('changeText1').value);
localStorage.setItem('changeText1a', document.getElementById('changeText1a').value || "");
localStorage.setItem('bitrateGroupFlag', document.getElementById('bitrateGroupFlag').value);
localStorage.setItem('codecGroupFlag', document.getElementById('codecGroupFlag').value);
localStorage.setItem('keyFrameRateFlag', document.getElementById('keyFrameRateFlag').value);
localStorage.setItem('svcGroupFlag', document.getElementById('svcGroupFlag').value);
localStorage.setItem('whipwaitFlag', document.getElementById('whipwaitFlag').value);
localStorage.setItem('whipoutaudiobitrate', document.getElementById('whipoutaudiobitrate').value);
localStorage.setItem('vbrcbr', document.getElementById('vbrcbr').value);
localStorage.setItem('autogain', document.getElementById('autogain').value);
localStorage.setItem('stereo', document.getElementById('stereo').value);
localStorage.setItem('denoise', document.getElementById('denoise').value);
localStorage.setItem('e2eeGroupFlag1', document.getElementById('e2eeGroupFlag1').value);
var whipoutaudiobitrate = "";
if (parseInt(document.getElementById('whipoutaudiobitrate').value)){
whipoutaudiobitrate = "&whipoutaudiobitrate="+document.getElementById('whipoutaudiobitrate').value;
}
var vbrcbr = "&"+document.getElementById('vbrcbr').value;
var autogain = "&autogain="+document.getElementById('autogain').value;
var stereo = "&stereo="+document.getElementById('stereo').value;
var denoise = "&denoise="+document.getElementById('denoise').value;
var bitrate = "";
if (parseInt(document.getElementById('bitrateGroupFlag').value)){
bitrate = "&whipoutvideobitrate="+document.getElementById('bitrateGroupFlag').value;
}
var codec = "";
if (document.getElementById('codecGroupFlag').value!=="default"){
codec = "&whipoutcodec="+document.getElementById('codecGroupFlag').value;
}
var keyFrameRateFlag = "";
if (document.getElementById('keyFrameRateFlag').value!=="0"){
keyFrameRateFlag = "&whipoutkeyframe="+document.getElementById('keyFrameRateFlag').value;
}
var svc = "";
if (document.getElementById('svcGroupFlag').value!=="0"){
svc = "&svc="+document.getElementById('svcGroupFlag').value;
}
var e2ee = "";
if (document.getElementById('e2eeGroupFlag1').value!=="0"){
e2ee = "&e2ee&password";
}
var whipwait = "&whipwait=" + document.getElementById('whipwaitFlag').value;
if (document.getElementById('changeText1a').value){
window.location = domain + "?push&whippush=" + encodeURIComponent(document.getElementById('changeText1').value) + "&whippushtoken=" + document.getElementById('changeText1a').value + codec + bitrate+whipoutaudiobitrate+vbrcbr+autogain+stereo+denoise+svc+e2ee+keyFrameRateFlag + whipwait;
} else {
window.location = domain + "?push&whippush=" + encodeURIComponent(document.getElementById('changeText1').value) + codec + bitrate+whipoutaudiobitrate+vbrcbr+autogain+stereo+denoise+svc+e2ee+keyFrameRateFlag + whipwait;
}
}
}
function checkStereo(){
if (parseInt(document.getElementById('autogain').value) || parseInt(document.getElementById('denoise').value)){
document.getElementById('stereo').disabled = true;
document.getElementById('stereo').title = "Noise reduction and auto-gain will prevent stereo audio from working";
} else {
document.getElementById('stereo').disabled = false;
delete document.getElementById('stereo').disabled;
document.getElementById('stereo').title = "Enable stereo 2.0 audio if available. Must be enabled on the viewer's end as well.";
}
}
function updateSVC(){
var codecName = document.getElementById('codecGroupFlag').value;
var select = document.getElementById("svcGroupFlag");
var selectedValue = "0";
if (select.options && select.selectedIndex && select.options[select.selectedIndex]){
selectedValue = select.options[select.selectedIndex].value;
}
select.innerHTML = "";
var option = document.createElement("option");
option.text = "🎦 SVC Off";
option.value = "0";
select.add(option);
select.selectedIndex = 0;
if (svcLUT[codecName]){
svcLUT[codecName].forEach(opt=>{
option = document.createElement("option");
option.text = "🎦 "+opt;
option.value = opt;
select.add(option);
if (opt == selectedValue){
select.value = opt;
}
});
}
}
function gohere1t(){
if (document.getElementById('changeText1t').value){
localStorage.setItem('changeText1t', document.getElementById('changeText1t').value);
window.location = domain + "?whipoutvideobitrate=5800&stereo&push&whippush=https%3A%2F%2Fg.webrtc.live-video.net%3A4443%2Fv2%2Foffer&whippushtoken="+ document.getElementById('changeText1t').value;
}
}
function gohere2(){
if (document.getElementById('changeText2').value){
localStorage.setItem('changeText2', document.getElementById('changeText2').value);
window.location = domain + "?hidemenu&whip=" + document.getElementById('changeText2').value;
}
}
function gohere3(){
if (document.getElementById('changeText3').value){
if (document.getElementById('changeText3').value.startsWith("http://vdo.ninja/")){
document.getElementById('changeText3').value = document.getElementById('changeText3').value.replace("http://vdo.ninja/","http://insecure.vdo.ninja/"); // a special exception for WHEP developers
} else if (document.getElementById('changeText3').value.startsWith("http://")){
if (window.location.protocol+window.location.hostname == "https:vdo.ninja"){
var tmp = window.location.pathname.split("/");
tmp.pop();
domain = "http://insecure.vdo.ninja"+tmp.join("/")+"/";
}
}
localStorage.setItem('changeText3', document.getElementById('changeText3').value);
localStorage.setItem('changeText3a', document.getElementById('changeText3a').value);
localStorage.setItem('whepbuffer', document.getElementById('whepbuffer').value);
localStorage.setItem('whepicewait', document.getElementById('whepicewait').value);
localStorage.setItem('e2eeGroupFlag2', document.getElementById('e2eeGroupFlag2').value);
localStorage.setItem('stereowhep', document.getElementById('stereowhep').value);
var addedon = "";
if (parseInt(document.getElementById('whepbuffer').value)){
addedon += "&buffer="+document.getElementById('whepbuffer').value;
}
if (parseInt(document.getElementById('e2eeGroupFlag2').value)){
addedon += "&e2ee&password";
}
if (parseInt(document.getElementById('stereowhep').value)){
addedon += "&stereo=2"; // viewer side only; stereo=1 will do both ways
} else {
addedon += "&mono";
}
if (document.getElementById('changeText3a').value){
addedon += "&whepplaytoken="+document.getElementById('changeText3a').value;
}
addedon += "&whepwait="+document.getElementById('whepicewait').value;
window.location = domain + "?&hidemenu&whepplay=" + encodeURIComponent(document.getElementById('changeText3').value)+addedon;
}
}
function change4(){
document.getElementById('whepoutid').innerText = document.getElementById('changeText4').value;
document.getElementById('whepoutsrc').href = "https://whep.vdo.ninja/"+document.getElementById('changeText4').value;
}
function gohere4(){
if (document.getElementById('changeText4').value){ // document.getElementById('changeText4').value
localStorage.setItem('changeText4', document.getElementById('changeText4').value);
document.getElementById('whepoutid').innerText = document.getElementById('changeText4').value;
document.getElementById('whepoutsrc').href = "https://whep.vdo.ninja/"+document.getElementById('changeText4').value;
var addedon = "";
window.location = domain + "?push=" + encodeURIComponent(document.getElementById('changeText4').value)+"&whepout=" + encodeURIComponent(document.getElementById('changeText4').value)+addedon;
}
}
function resetHistory(){
localStorage.clear();
document.querySelector("#changeText1").value = "";
document.querySelector("#changeText1a").value = "";
document.querySelector("#changeText2").value = "";
document.querySelector("#changeText3").value = "";
document.querySelector("#changeText1t").value = "";
checkStereo();
}
(function (w) {
w.URLSearchParams = w.URLSearchParams || function (searchString) {
var self = this;
self.searchString = searchString;
self.get = function (name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
if (results == null) {
return null;
}
else {
return decodeURI(results[1]) || 0;
}
};
}
})(window)
var urlParams = new URLSearchParams(window.location.search);
function enterPressed(event, callback){
if (event.keyCode === 13){ // Number 13 is the "Enter" key on the keyboard
event.preventDefault(); // Cancel the default action, if needed
callback();
}
}
checkStereo();
var isMobile = false;
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)){ // does not detect iPad Pros.
isMobile=true; // if iOS, default to H264? meh. let's not.
}
var Firefox = navigator.userAgent.indexOf("Firefox")>=0;
if (Firefox){
Firefox = parseInt(navigator.userAgent.split("irefox/").pop()) || true;
}
var capabilityType = Firefox ? "transmission" : "webrtc";
var codecs = RTCRtpSender.getCapabilities('video').codecs;
var svcLUT = {};
var svcDefault = {};
function getCommonValues(obj) {
if (obj.default){
delete obj.default;
}
let commonValues = [];
let firstKey = Object.keys(obj)[0];
let firstArray = obj[firstKey];
for (let i = 0; i < firstArray.length; i++) {
let currentValue = firstArray[i];
let isCommonValue = true;
for (let key in obj) {
if (!obj[key].includes(currentValue)) {
isCommonValue = false;
break;
}
}
if (isCommonValue) {
commonValues.push(currentValue);
}
}
return commonValues
}
async function processCodecs(){
await codecs.forEach(async codec => {
try {
var codecName = codec.mimeType.replace("video/","").toLowerCase();
if (['video/red', 'video/ulpfec', 'video/rtx'].includes(codec.mimeType)) {
return;
} else if (svcLUT[codecName]){ // already done
return;
}
svcLUT[codecName] = [];
var capabilityPromises = [];
for (const mode of scalabilityModes) {
capabilityPromises.push(navigator.mediaCapabilities.encodingInfo({
type: capabilityType,
video: {
contentType: codec.mimeType,
width: 1920,
height: 1080,
bitrate: 10000,
framerate: 29.97,
scalabilityMode: mode
}
}));
}
var capabilityResults = await Promise.all(capabilityPromises);
for (var i = 0;i<capabilityResults.length;i++){
if (capabilityResults[i].supported){
svcLUT[codecName].push(scalabilityModes[i]);
}
}
svcLUT['default'] = getCommonValues(svcLUT);
updateSVC();
} catch(e){
console.error(e);
}
});
console.log("available codecs");
console.log(svcLUT);
}
if (codecs){
var av1found = false;
codecs.forEach(c =>{
if (c.mimeType.toLowerCase().includes("av1")){
av1found = true;
}
})
if (!av1found){
document.getElementById("av1codec").disabled = true;
document.getElementById("av1codec").title = "Not found on your system";
} else if (localStorage.getItem('codecGroupFlag')===null){
document.getElementById('codecGroupFlag').value = "av1";
}
processCodecs();
}
</script>
</body>
</html>