From fc07dd2af97024efd2aa84ce7dcb206fdfe30d78 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Mon, 13 Jun 2022 17:24:25 -0400 Subject: [PATCH] Convert useMediaStream to TypeScript --- package.json | 1 + .../{useMediaStream.js => useMediaStream.ts} | 65 ++++++++++++------- yarn.lock | 5 ++ 3 files changed, 48 insertions(+), 23 deletions(-) rename src/video-grid/{useMediaStream.js => useMediaStream.ts} (81%) diff --git a/package.json b/package.json index 9c4b8fc..4c58ecf 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@sentry/react": "^6.13.3", "@sentry/tracing": "^6.13.3", "@types/grecaptcha": "^3.0.4", + "@types/sdp-transform": "^2.4.5", "@use-gesture/react": "^10.2.11", "classnames": "^2.3.1", "color-hash": "^2.0.1", diff --git a/src/video-grid/useMediaStream.js b/src/video-grid/useMediaStream.ts similarity index 81% rename from src/video-grid/useMediaStream.js rename to src/video-grid/useMediaStream.ts index 029cc0f..3709504 100644 --- a/src/video-grid/useMediaStream.js +++ b/src/video-grid/useMediaStream.ts @@ -14,13 +14,30 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useRef, useEffect } from "react"; +import { useRef, useEffect, RefObject } from "react"; import { parse as parseSdp, write as writeSdp } from "sdp-transform"; import { useSpatialAudio } from "../settings/useSetting"; -export function useMediaStream(stream, audioOutputDevice, mute = false) { - const mediaRef = useRef(); +declare global { + interface Window { + // For detecting whether this browser is Chrome or not + chrome?: unknown; + } +} + +// TypeScript doesn't know about the experimental setSinkId method, so we +// declare it ourselves +interface MediaElement extends HTMLMediaElement { + setSinkId: (id: string) => void; +} + +export const useMediaStream = ( + stream: MediaStream, + audioOutputDevice: string, + mute = false +): RefObject => { + const mediaRef = useRef(); useEffect(() => { console.log( @@ -76,14 +93,14 @@ export function useMediaStream(stream, audioOutputDevice, mute = false) { }, []); return mediaRef; -} +}; // Loops the given audio stream back through a local peer connection, to make // AEC work with Web Audio streams on Chrome. The resulting stream should be // played through an audio element. // This hack can be removed once the following bug is resolved: // https://bugs.chromium.org/p/chromium/issues/detail?id=687574 -const createLoopback = async (stream) => { +const createLoopback = async (stream: MediaStream): Promise => { // Prepare our local peer connections const conn = new RTCPeerConnection(); const loopbackConn = new RTCPeerConnection(); @@ -102,8 +119,6 @@ const createLoopback = async (stream) => { // Hook the connections together stream.getTracks().forEach((track) => conn.addTrack(track)); const offer = await conn.createOffer({ - offerVideo: false, - offerAudio: true, offerToReceiveAudio: false, offerToReceiveVideo: false, }); @@ -126,10 +141,14 @@ const createLoopback = async (stream) => { return loopbackStream; }; -export const useAudioContext = () => { - const context = useRef(); - const destination = useRef(); - const audioRef = useRef(); +export const useAudioContext = (): [ + AudioContext, + AudioNode, + RefObject +] => { + const context = useRef(); + const destination = useRef(); + const audioRef = useRef(); useEffect(() => { if (audioRef.current && !context.current) { @@ -137,11 +156,12 @@ export const useAudioContext = () => { if (window.chrome) { // We're in Chrome, which needs a loopback hack applied to enable AEC - destination.current = context.current.createMediaStreamDestination(); + const streamDest = context.current.createMediaStreamDestination(); + destination.current = streamDest; const audioEl = audioRef.current; (async () => { - audioEl.srcObject = await createLoopback(destination.current.stream); + audioEl.srcObject = await createLoopback(streamDest.stream); await audioEl.play(); })(); return () => { @@ -157,13 +177,13 @@ export const useAudioContext = () => { }; export const useSpatialMediaStream = ( - stream, - audioOutputDevice, - audioContext, - audioDestination, + stream: MediaStream, + audioOutputDevice: string, + audioContext: AudioContext, + audioDestination: AudioNode, mute = false -) => { - const tileRef = useRef(); +): [RefObject, RefObject] => { + const tileRef = useRef(); const [spatialAudio] = useSpatialAudio(); // If spatial audio is enabled, we handle audio separately from the video element const mediaRef = useMediaStream( @@ -172,8 +192,8 @@ export const useSpatialMediaStream = ( spatialAudio || mute ); - const pannerNodeRef = useRef(); - const sourceRef = useRef(); + const pannerNodeRef = useRef(); + const sourceRef = useRef(); useEffect(() => { if (spatialAudio && tileRef.current && !mute) { @@ -204,8 +224,7 @@ export const useSpatialMediaStream = ( }; updatePosition(); - source.connect(pannerNode); - pannerNode.connect(audioDestination); + source.connect(pannerNode).connect(audioDestination); // HACK: We abuse the CSS transitionrun event to detect when the tile // moves, because useMeasure, IntersectionObserver, etc. all have no // ability to track changes in the CSS transform property diff --git a/yarn.lock b/yarn.lock index fa83a1f..68339b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3024,6 +3024,11 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== +"@types/sdp-transform@^2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@types/sdp-transform/-/sdp-transform-2.4.5.tgz#3167961e0a1a5265545e278627aa37c606003f53" + integrity sha512-GVO0gnmbyO3Oxm2HdPsYUNcyihZE3GyCY8ysMYHuQGfLhGZq89Nm4lSzULWTzZoyHtg+VO/IdrnxZHPnPSGnAg== + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"