import React, { useState, useRef, useEffect } from "react"; import { Text, IconButton, Box, Flex } from "theme-ui"; import StreamMuteIcon from "../../icons/StreamMuteIcon"; import Banner from "../banner/Banner"; import Slider from "../Slider"; function Stream({ stream, nickname }) { const [streamVolume, setStreamVolume] = useState(1); const [showStreamInteractBanner, setShowStreamInteractBanner] = useState( false ); const [streamMuted, setStreamMuted] = useState(false); const audioRef = useRef(); useEffect(() => { if (audioRef.current) { audioRef.current.srcObject = stream; // Try and auto play the audio audioRef.current .play() .then(() => { // Played fine }) .catch(() => { // Unable to autoplay setStreamMuted(true); setShowStreamInteractBanner(true); }); } }, [stream]); function toggleMute() { if (audioRef.current) { if (streamMuted) { audioRef.current.play().then(() => { setStreamMuted(false); setShowStreamInteractBanner(false); }); } else { setStreamMuted(true); } } } function handleVolumeChange(event) { const volume = parseFloat(event.target.value); setStreamVolume(volume); } // Platforms like iOS don't allow you to control audio volume // Detect this by trying to change the audio volume const [isVolumeControlAvailable, setIsVolumeControlAvailable] = useState( true ); useEffect(() => { let audio = audioRef.current; function checkVolumeControlAvailable() { const prevVolume = audio.volume; // Set volume to 0.5, then check if the value actually stuck 100ms later audio.volume = 0.5; setTimeout(() => { setIsVolumeControlAvailable(audio.volume === 0.5); audio.volume = prevVolume; }, [100]); } audio.addEventListener("playing", checkVolumeControlAvailable); return () => { audio.removeEventListener("playing", checkVolumeControlAvailable); }; }, []); // Use an audio context gain node to control volume to go past 100% const audioGainRef = useRef(); useEffect(() => { let audioContext; if (stream && !streamMuted && isVolumeControlAvailable) { audioContext = new AudioContext(); let source = audioContext.createMediaStreamSource(stream); let gainNode = audioContext.createGain(); gainNode.gain.value = 0; source.connect(gainNode); gainNode.connect(audioContext.destination); audioGainRef.current = gainNode; } return () => { audioContext && audioContext.close(); }; }, [stream, streamMuted, isVolumeControlAvailable]); useEffect(() => { if (audioGainRef.current && audioRef.current) { if (streamVolume <= 1) { audioGainRef.current.gain.value = 0; audioRef.current.volume = streamVolume; } else { audioRef.current.volume = 1; audioGainRef.current.gain.value = (streamVolume - 1) * 2; } } }, [streamVolume]); return ( <> { if (stream) { toggleMute(); } }} > {stream && setShowStreamInteractBanner(false)} > {nickname} has started streaming. Click { } to listen. ); } export default Stream;