diff --git a/web/shared/components/dialog-preview.tsx b/web/shared/components/dialog-preview.tsx index ec1e573d..9b099922 100644 --- a/web/shared/components/dialog-preview.tsx +++ b/web/shared/components/dialog-preview.tsx @@ -17,7 +17,7 @@ export const PreviewDialog = forwardRef((props, ref) => { const [streamId, setStreamId] = useState(''); const refPeerConnection = useRef(null); const refWhepClient = useRef(null); - const refVideoTrack = useRef(null); + const refMediaStream = useRef(); const [connState, setConnState] = useState(''); const [videoResolution, setVideoResolution] = useState(''); const logger = useLogger(); @@ -47,8 +47,8 @@ export const PreviewDialog = forwardRef((props, ref) => { if (refVideo.current) { refVideo.current.srcObject = null; } - if (refVideoTrack.current) { - refVideoTrack.current = null; + if (refMediaStream.current) { + refMediaStream.current = null; } if (refPeerConnection.current) { refPeerConnection.current = null; @@ -88,14 +88,14 @@ export const PreviewDialog = forwardRef((props, ref) => { const pc = new RTCPeerConnection(); pc.addTransceiver('video', { direction: 'recvonly' }); pc.addTransceiver('audio', { direction: 'recvonly' }); + const ms = new MediaStream(); + refMediaStream.current = ms; + if (refVideo.current) { + refVideo.current.srcObject = ms; + } pc.addEventListener('track', ev => { logger.log(`track: ${ev.track.kind}`); - if (ev.track.kind === 'video' && ev.streams.length > 0) { - refVideoTrack.current = ev.track; - if (refVideo.current) { - refVideo.current.srcObject = ev.streams[0]; - } - } + ms.addTrack(ev.track); }); pc.addEventListener('iceconnectionstatechange', () => { const state = pc.iceConnectionState; @@ -136,8 +136,11 @@ export const PreviewDialog = forwardRef((props, ref) => { }; const handleVideoResize = (_: TargetedEvent) => { - if (refVideoTrack.current) { - setVideoResolution(formatVideoTrackResolution(refVideoTrack.current)); + if (refMediaStream.current) { + const videoTrack = refMediaStream.current.getVideoTracks()[0]; + if (videoTrack) { + setVideoResolution(formatVideoTrackResolution(videoTrack)); + } } }; diff --git a/web/shared/components/dialog-web-stream.tsx b/web/shared/components/dialog-web-stream.tsx index f9eca5d5..f574b280 100644 --- a/web/shared/components/dialog-web-stream.tsx +++ b/web/shared/components/dialog-web-stream.tsx @@ -52,15 +52,18 @@ export const WebStreamDialog = forwardRef((props, ref) if (refVideo.current) { refVideo.current.srcObject = stream; } - const videoTrack = stream.getVideoTracks()[0]; - setVideoResolution(formatVideoTrackResolution(videoTrack)); updateConnState('Started'); const pc = new RTCPeerConnection(); pc.addEventListener('iceconnectionstatechange', () => { updateConnState(pc.iceConnectionState); }); - pc.addTransceiver(videoTrack, { direction: 'sendonly' }); - stream.getAudioTracks().forEach(track => pc.addTrack(track)); + stream.getVideoTracks().forEach(vt => { + pc.addTransceiver(vt, { direction: 'sendonly' }); + setVideoResolution(formatVideoTrackResolution(vt)); + }); + stream.getAudioTracks().forEach(at => { + pc.addTransceiver(at, { direction: 'sendonly' }); + }); const whip = new WHIPClient(); const url = `${location.origin}/whip/${streamId}`; const token = ''; diff --git a/web/shared/tools/debugger/compat.tsx b/web/shared/tools/debugger/compat.tsx index 146063d0..24519385 100644 --- a/web/shared/tools/debugger/compat.tsx +++ b/web/shared/tools/debugger/compat.tsx @@ -16,7 +16,6 @@ declare module 'preact' { declare global { interface Window { - refreshDevice(): Promise; startWhip(): Promise; startWhep(): Promise; } @@ -60,11 +59,34 @@ const WhepLayerSelect = [ { value: 'f', text: 'HIGH' }, ]; +const NoneDevice = { value: '', text: 'none' }; + export default function DebuggerCompat() { const streamIdInput = useUrlParamsInput('id'); const idBearerTokenInput = useUrlParamsInput('token'); - const refreshDevice = useCallback(() => window.refreshDevice(), []); + const [refreshDisabled, setRefreshDisabled] = useState(false); + const [audioDevices, setAudioDevices] = useState([NoneDevice]); + const [videoDevices, setVideoDevices] = useState([NoneDevice]); + + const refreshDevice = useCallback(async () => { + try { + const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); + mediaStream.getTracks().map(track => track.stop()); + } catch (e) { + console.error('Failed to getUserMedia:', e); + } + const devices = (await navigator.mediaDevices.enumerateDevices()).filter(i => !!i.deviceId); + const audio = devices.filter(i => i.kind === 'audioinput').map(i => ({ value: i.deviceId, text: i.label })); + if (audio.length > 0) { + setAudioDevices(audio); + } + const video = devices.filter(i => i.kind === 'videoinput').map(i => ({ value: i.deviceId, text: i.label })); + if (video.length > 0) { + setVideoDevices(video); + } + setRefreshDisabled(true); + }, []); const startWhip = useCallback(() => window.startWhip(), []); const startWhep = useCallback(() => window.startWhep(), []); @@ -83,9 +105,17 @@ export default function DebuggerCompat() { WHIP
- -
Audio Device:
-
Video Device:
+ +
Audio Device: + +
+
Video Device: + +
diff --git a/web/shared/tools/debugger/debugger.js b/web/shared/tools/debugger/debugger.js index bb10fb65..0c19fcba 100644 --- a/web/shared/tools/debugger/debugger.js +++ b/web/shared/tools/debugger/debugger.js @@ -64,16 +64,6 @@ const layers = [ {rid: 'f', scalabilityMode: 'L1T3'} ] -function initLayerSelect(elementId, opts) { - const selectLayer = document.getElementById(elementId) - if (selectLayer) opts.map(i => { - const opt = document.createElement("option") - opt.value = i.value - opt.text = i.text - selectLayer.appendChild(opt) - }) -} - // WHIP let whipNum = 0 @@ -87,30 +77,6 @@ const idWhipButtonStop = "whip-button-stop" const idWhipPseudoAudio = "whip-pseudo-audio" const idWhipDataChannel = "whip-datachannel" -// initLayerSelect(idWhipLayerSelect, [ -// {value: "f", text: "Base"}, -// {value: "h", text: "Base + 1/2"}, -// {value: "q", text: "Base + 1/2 + 1/4"}, -// ]) - -async function refreshDevice() { - const mediaStream = await navigator.mediaDevices.getUserMedia({audio: true, video: true}) - mediaStream.getTracks().map(track => track.stop()) - - const devices = (await navigator.mediaDevices.enumerateDevices()).filter(i => !!i.deviceId) - initLayerSelect(idWhipAudioDevice, devices.filter(i => i.kind === 'audioinput').map(i => { - return {value: i.deviceId, text: i.label} - })) - initLayerSelect(idWhipVideoDevice, devices.filter(i => i.kind === 'videoinput').map(i => { - return {value: i.deviceId, text: i.label} - })) -} - -window.refreshDevice = () => { - refreshDevice() - document.getElementById("whip-device-button").disabled = true -} - async function startWhip() { const streamId = getElementValue(idStreamId) if (!streamId) { @@ -130,7 +96,10 @@ async function startWhip() { let stream if (!audioDevice && !videoDevice) { - stream = await navigator.mediaDevices.getDisplayMedia({audio: false, video: videoSize}) + stream = await navigator.mediaDevices.getDisplayMedia({ + audio: true, + video: videoSize + }) } else { stream = await navigator.mediaDevices.getUserMedia({ audio: {deviceId: audioDevice}, @@ -161,7 +130,9 @@ async function startWhip() { if (document.getElementById(idWhipPseudoAudio).checked) { pc.addTransceiver('audio', { 'direction': 'sendonly' }) } else { - stream.getAudioTracks().map(track => pc.addTrack(track)) + stream.getAudioTracks().forEach(track => pc.addTransceiver(track, { + direction: 'sendonly' + })) } const audioCodec = getElementValue(idWhipAudioCodec) @@ -232,13 +203,16 @@ async function startWhep() { document.getElementById(idWhepDataChannel).dataChannel = pc.createDataChannel("") pc.oniceconnectionstatechange = e => logWhep(num, pc.iceConnectionState) - pc.addTransceiver('video', {'direction': 'recvonly'}) - pc.addTransceiver('audio', {'direction': 'recvonly'}) + pc.addTransceiver("video", { "direction": "recvonly" }) + pc.addTransceiver("audio", { "direction": "recvonly" }) + + const ms = new MediaStream(); pc.ontrack = ev => { logWhep(num, `track: ${ev.track.kind}`) - if (ev.track.kind === "video") { - if (ev.streams.length !== 0) document.getElementById("whep-video-player").srcObject = ev.streams[0] - } + ms.addTrack(ev.track); + // addtrack removetrack events won't fire when calling addTrack/removeTrack in javascript + // https://github.com/w3c/mediacapture-main/issues/517 + document.getElementById("whep-video-player").srcObject = ms; } const whep = new WHEPClient() const url = location.origin + "/whep/" + streamId