Some checks failed
REUSE Compliance Check / lint (push) Failing after 0s
Browser E2E / Test (push) Failing after 3s
Test / test (1.24) (push) Failing after 0s
Test / test-i386 (1.25) (push) Failing after 0s
Test / test-i386 (1.24) (push) Failing after 0s
Test / test-windows (1.25) (push) Failing after 0s
Test / test-windows (1.24) (push) Failing after 0s
Test / test (1.25) (push) Failing after 0s
Examples Tests / Test (push) Failing after 3s
Test / test-macos (1.25) (push) Failing after 0s
Test / test-macos (1.24) (push) Failing after 0s
Go mod tidy / tidy (push) Failing after 0s
Test / test-wasm (push) Failing after 0s
CodeQL / analyze (push) Failing after 1s
Fuzz / fuzz (push) Failing after 0s
93 lines
2.8 KiB
JavaScript
93 lines
2.8 KiB
JavaScript
/* eslint-env browser */
|
|
|
|
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] })
|
|
|
|
function log (value) {
|
|
console.log(value)
|
|
const line = document.createElement('pre')
|
|
line.innerText = value.toString()
|
|
document.getElementById('logs').appendChild(line)
|
|
}
|
|
|
|
// Reorders available codecs so that the selected codec is first on the list
|
|
function codecs () {
|
|
const option = document.getElementById('codec').value.toLowerCase()
|
|
const caps = RTCRtpSender.getCapabilities('video')?.codecs ?? []
|
|
|
|
const primary = caps.filter((c) => c.mimeType.toLowerCase() === `video/${option}`)
|
|
if (primary.length === 0) {
|
|
alert('Unsupported codec selected')
|
|
throw new DOMException('Unsupported codec')
|
|
}
|
|
const primaryPayloads = new Set(primary.map((c) => c.preferredPayloadType))
|
|
const pairedRtx = caps.filter(
|
|
(c) => c.mimeType.toLowerCase() === 'video/rtx' && c.sdpFmtpLine?.includes('apt=') && primaryPayloads.has(Number(c.sdpFmtpLine.split('apt=')[1]))
|
|
)
|
|
|
|
const rest = caps.filter((c) => !primary.includes(c) && !pairedRtx.includes(c))
|
|
const ordered = [...primary, ...pairedRtx, ...rest]
|
|
console.log('Codec preferences', ordered)
|
|
return ordered
|
|
}
|
|
|
|
async function start () {
|
|
log('Starting...')
|
|
const screenSrc = await navigator.mediaDevices.getDisplayMedia({ video: { frameRate: 30, height: 360 } })
|
|
const video = document.getElementById('screen')
|
|
video.hidden = false
|
|
video.srcObject = screenSrc
|
|
|
|
const trans = pc.addTransceiver(screenSrc.getVideoTracks()[0], { direction: 'sendrecv' })
|
|
trans.setCodecPreferences(codecs())
|
|
|
|
const offer = await pc.createOffer()
|
|
await pc.setLocalDescription(offer)
|
|
|
|
log('Gathering ICE candidates')
|
|
|
|
await new Promise((resolve) => {
|
|
pc.onicecandidate = (ev) => {
|
|
if (ev.candidate == null) {
|
|
resolve()
|
|
}
|
|
}
|
|
if (pc.iceGatheringState === 'complete') {
|
|
resolve()
|
|
}
|
|
})
|
|
|
|
log('Done gathering ICE candidates')
|
|
|
|
log('Sending SDP')
|
|
const resp = await fetch('/sdp', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(pc.localDescription)
|
|
})
|
|
const sdp = await resp.json()
|
|
|
|
pc.setRemoteDescription(sdp)
|
|
}
|
|
|
|
pc.addEventListener('iceconnectionstatechange', () => {
|
|
console.log('ICE connection state', pc.iceConnectionState)
|
|
})
|
|
|
|
pc.ontrack = (t) => {
|
|
log('New track received')
|
|
const received = document.getElementById('received')
|
|
received.hidden = false
|
|
received.srcObject = t.streams[0]
|
|
pc.getStats().then((stats) => {
|
|
const codec = Array.from(stats.values()).find((e) => e.type === 'codec')
|
|
log(`New track codec: ${codec.mimeType}`)
|
|
})
|
|
}
|
|
|
|
document.getElementById('start').onclick = start
|