Created
January 17, 2021 23:55
-
-
Save kwindla/05efd2e39689322698f6dde6f51a7568 to your computer and use it in GitHub Desktop.
Daily API switching between 720p and 360p during a call
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>test quality and cpu difference for 720p vs 360p video</title> | |
<script src="https://unpkg.com/@daily-co/daily-js"></script> | |
</head> | |
<body onload="main()"> | |
<div id="local-controls"> | |
<div> | |
<input | |
type="radio" | |
id="720p" | |
name="video-properties" | |
value="720p" | |
checked | |
onchange="buttonChangeVideoSendProperties(this)" | |
/> | |
<label for="720p">720p (Daily defaults)</label> | |
</div> | |
<div> | |
<input | |
type="radio" | |
id="360p" | |
name="video-properties" | |
value="360p" | |
onchange="buttonChangeVideoSendProperties(this)" | |
/> | |
<label for="360p">360p</label> | |
</div> | |
</div> | |
<hr /> | |
<div>Video send bitrate: <span id="video-send-kbps">0</span></div> | |
<div>Video recv bitrate: <span id="video-recv-kbps">0</span></div> | |
<div>Active video resolution: <span id="active-res">-</span></div> | |
<div> | |
Video receive resolutions: | |
<div id="receive-res"></div> | |
</div> | |
<hr /> | |
<div id="local-video"></div> | |
<div id="videos"></div> | |
<script> | |
async function main() { | |
const ROOM_URL = "ROOM ... URL ... HERE"; | |
window.callObject = DailyIframe.createCallObject(); | |
callObject.on("track-started", displayVideo); | |
callObject.on("track-stopped", destroyVideo); | |
callObject.on("app-message", handleMessage); | |
await callObject.join({ url: ROOM_URL }); | |
await callObject.setNetworkTopology({ topology: "sfu" }); | |
console.log("setting interval"); | |
setInterval(updateStatsDisplay, 3000); | |
} | |
async function handleMessage(msg) { | |
console.log("app-message", msg); | |
if (msg.data.newMode) { | |
document.getElementById(msg.data.newMode).checked = true; | |
changeVideoSendProperties(msg.data.newMode); | |
return; | |
} | |
} | |
async function buttonChangeVideoSendProperties(el) { | |
const newMode = el.value; | |
// tell everyone else about the new mode | |
callObject.sendAppMessage({ newMode }); | |
// now change locally | |
changeVideoSendProperties(newMode); | |
} | |
async function changeVideoSendProperties(newMode) { | |
console.log("switching to", newMode); | |
// get rtp parameters so we can modify the simulcast layers | |
const producer = window.rtcpeers.sfu.getProducerByTag("cam-video"); | |
if (!(producer && producer.track && producer.rtpSender)) { | |
console.log("UNEXPECTED: no rtpSender"); | |
return; | |
} | |
const params = producer.rtpSender.getParameters(); | |
if (newMode === "720p") { | |
// current Daily defaults | |
await callObject.setBandwidth({ | |
trackConstraints: { width: 1280, height: 720 }, | |
}); | |
params.encodings[0].maxBitrate = 80 * 1000; | |
params.encodings[0].maxFramerate = 10; | |
params.encodings[0].scaleResolutionDownBy = 4; | |
params.encodings[1].maxBitrate = 200 * 1000; | |
params.encodings[1].maxFramerate = 15; | |
params.encodings[1].scaleResolutionDownBy = 2; | |
params.encodings[2].maxBitrate = 680 * 1000; | |
params.encodings[2].maxFramerate = 30; | |
params.encodings[2].scaleResolutionDownBy = 1; | |
params.encodings[2].active = true; | |
await producer.rtpSender.setParameters(params); | |
return; | |
} | |
if (newMode === "360p") { | |
await callObject.setBandwidth({ | |
trackConstraints: { width: 640, height: 360 }, | |
}); | |
// change to two layers, by setting the top simulcast layer to inactive. | |
// Chrome will do this anyway, but let's make it explicit, as well | |
// set our new top layer's bitrate and framerate higher than the | |
// Daily default for the middle layer | |
params.encodings[0].maxBitrate = 80 * 1000; | |
params.encodings[0].maxFramerate = 10; | |
params.encodings[0].scaleResolutionDownBy = 2; | |
params.encodings[1].maxBitrate = 500 * 1000; | |
params.encodings[1].maxFramerate = 30; | |
params.encodings[1].scaleResolutionDownBy = 1; | |
params.encodings[2].active = false; | |
await producer.rtpSender.setParameters(params); | |
return; | |
} | |
} | |
async function subscribeAll() { | |
let updateList = {}; | |
for (let id in callObject.participants()) { | |
if (id === "local") { | |
continue; | |
} | |
updateList[id] = { setSubscribedTracks: true }; | |
} | |
callObject.updateParticipants(updateList); | |
} | |
function displayVideo(evt) { | |
console.log(evt); | |
if (!(evt.track.kind === "video")) { | |
return; | |
} | |
let videosDiv = document.getElementById( | |
evt.participant.local ? "local-video" : "videos" | |
); | |
let videoEl = document.createElement("video"); | |
videoEl._participant = evt.participant; | |
videosDiv.appendChild(videoEl); | |
videoEl.style.width = "100%"; | |
videoEl.srcObject = new MediaStream([evt.track]); | |
videoEl.play(); | |
doLayout(); | |
} | |
function destroyVideo(evt) { | |
let vids = document.getElementsByTagName("video"); | |
for (let vid of vids) { | |
if ( | |
vid.srcObject && | |
vid.srcObject.getVideoTracks()[0] === evt.track | |
) { | |
vid.remove(); | |
} | |
} | |
doLayout(); | |
} | |
const SMALL_VIDEO_WIDTH = 220; | |
function doLayout() { | |
console.log("laying out"); | |
const vids = Array.from(document.getElementsByTagName("video")); | |
// local | |
const localVid = vids.find((v) => v._participant.local); | |
if (localVid) { | |
localVid.style.width = SMALL_VIDEO_WIDTH; | |
localVid.style.padding = "10px"; | |
localVid.style.display = "inline"; | |
} | |
// lon-local | |
vids | |
.filter((v) => !v._participant.local) | |
.forEach((v) => { | |
v.style.width = "50%"; | |
v.style.display = "inline"; | |
}); | |
} | |
async function updateStatsDisplay() { | |
console.log("stats"); | |
const simpleStats = (await callObject.getNetworkStats()).stats.latest; | |
const producerStats = Array.from( | |
await rtcpeers.sfu.producers[1].getStats() | |
); | |
const trackStats = producerStats.find( | |
(s) => | |
s[0].match(/RTCMediaStreamTrack_sender/) && s[1].kind === "video" | |
); | |
document.getElementById("video-send-kbps").innerText = Math.round( | |
simpleStats.videoSendBitsPerSecond / 1000 | |
); | |
document.getElementById("video-recv-kbps").innerText = Math.round( | |
simpleStats.videoRecvBitsPerSecond / 1000 | |
); | |
if (trackStats) { | |
document.getElementById("active-res").innerText = | |
trackStats[1].frameWidth + " x " + trackStats[1].frameHeight; | |
} | |
const inboundVids = document | |
.getElementById("videos") | |
.getElementsByTagName("video"); | |
document.getElementById("receive-res").innerHTML = Array.from( | |
inboundVids | |
).reduce( | |
(a, c) => a + c.videoWidth + " x " + c.videoHeight + "<br />", | |
"" | |
); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment