import React, { Dispatch, SetStateAction, useEffect, useRef } from 'react' import type { CustomHTMLVideoElement, SoraDevtoolsState } from '@/types' import { getVideoSizeByResolution } from '@/utils' type VideoProps = { localVideo?: boolean displayResolution: SoraDevtoolsState['displayResolution'] stream: MediaStream | null mute: boolean audioOutput: string setHeight: Dispatch> } const VideoElement: React.FC = (props) => { const { displayResolution, stream, mute, audioOutput, setHeight } = props const videoRef = useRef(null) const videoSize = getVideoSizeByResolution(displayResolution) useEffect(() => { const resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => { entries.forEach((entry) => { setHeight(entry.contentRect.height) }) }) if (videoRef.current) { if (audioOutput && stream && stream.getAudioTracks().length > 0) { videoRef.current.setSinkId(audioOutput) } resizeObserver.observe(videoRef.current) } return () => { resizeObserver.disconnect() } // eslint-disable-next-line react-hooks/exhaustive-deps }, []) useEffect(() => { if (videoRef.current && mute) { videoRef.current.muted = true } }, [mute]) useEffect(() => { if (videoRef.current && stream) { // Chrome で first video frame まで音声が出力されない現象のワークアラウンド // 一旦 video tracks を disabled にしておき、 loadedmetadata イベントで有効にする // c.f. https://bugs.chromium.org/p/chromium/issues/detail?id=403710 let originalEnabled: boolean | undefined stream.getVideoTracks().forEach((track) => { originalEnabled = track.enabled track.enabled = false }) videoRef.current.onloadedmetadata = (_) => { stream.getVideoTracks().forEach((track) => { if (originalEnabled !== undefined) { track.enabled = originalEnabled } }) } videoRef.current.srcObject = stream if (audioOutput && stream.getAudioTracks().length > 0) { videoRef.current.setSinkId(audioOutput) } } else if (videoRef.current && stream === null) { videoRef.current.srcObject = null } // eslint-disable-next-line react-hooks/exhaustive-deps }, [stream]) if (audioOutput && videoRef.current?.setSinkId && stream && stream.getAudioTracks().length > 0) { videoRef.current.setSinkId(audioOutput) } return (