import React, { useEffect, useRef, useMemo, useCallback } from 'react'; import { AudioLevelDisplayType } from '../../types'; import { AudioLevelIndicator } from '../AudioLevelIndicators'; import { useInView } from 'react-intersection-observer'; import HMSLogger from '../../utils/ui-logger'; import { hmsUiClassParserGenerator } from '../../utils/classes'; import { useHMSActions } from '../../hooks/HMSRoomProvider'; import { HMSTrack } from '../../store/schema'; import { useHMSTheme } from '../../hooks/HMSThemeProvider'; export type DisplayShapes = 'circle' | 'rectangle'; export interface VideoClasses { /** The actual video element * */ video?: string; /** * Extra styles added when video is circular */ videoCircle?: string; /** * Extra styles added when video is local stream */ videoLocal?: string; /** * Extra styles added when objectFit is set to cover */ videoCover?: string; /** * Extra styles added when objectFit is contain */ videoContain?: string; /** * Extra styles added when to audio level border */ borderAudioRoot?: string; } export interface VideoProps { //TODO make one of audioTrack and videoTrack mandatory instead of both /** * Native Video Track to be displayed. This is auto inferred and not required if you * pass in peer and hmsVideoTrack */ videoTrack?: MediaStreamTrack; /** * HMS Video Track is for track related metadata */ hmsVideoTrack?: HMSTrack; /** * Audio Track to be displayed. */ audioTrack?: MediaStreamTrack; /** * Indicates if the stream belongs to the user viewing it. Used in labelling and styling. */ isLocal?: boolean; peerId?: string; /** * Indicates whether to show the volume of the stream's audio. Needs audioLevel to display the audio level. */ showAudioLevel?: boolean; /** * Indicates the volume of the stream as a number. * Ignored when showAudioLevel is false. */ audioLevel?: number; /** * Indicates the fit type of the video inside the container. * 'contain' - Video is fit inside the container with it's original aspect-ratio. * 'cover' - Video is scaled to cover the largest area within the container while maintaining aspectRatio. */ objectFit: 'contain' | 'cover'; /** * Shape of the video tile. * Use 'rectangle' and 'aspect ratio' 1:1 for square. */ displayShape?: DisplayShapes; /** * Indicates how to display the volume of the stream's audio. * Supported types are 'border', 'inline-wave' and 'inline-circle'. * Ignored if showAudioLevel is set to false. */ audioLevelDisplayType?: AudioLevelDisplayType; /** * The color of the audio display */ audioLevelDisplayColor?: string; /** * extra classes added by user */ classes?: VideoClasses; } const defaultClasses: VideoClasses = { video: 'h-full w-full rounded-lg absolute left-0 top-0', videoCircle: 'rounded-full', videoLocal: 'transform -scale-x-100', videoCover: 'object-cover', videoContain: 'object-contain', borderAudioRoot: 'w-full h-full absolute left-0 top-0 rounded-lg', }; export const Video = ({ peerId, videoTrack, hmsVideoTrack, objectFit, isLocal, showAudioLevel, audioLevel = 0, audioLevelDisplayType, audioLevelDisplayColor, displayShape, classes, }: VideoProps) => { const { tw } = useHMSTheme(); const styler = useMemo( () => hmsUiClassParserGenerator({ tw, classes, defaultClasses, tag: 'hmsui-video', }), [], ); const videoRef = useRef(null); const hmsActions = useHMSActions(); const { ref: inViewRef, inView } = useInView({ threshold: 0.5 }); const setRefs = useCallback( node => { videoRef.current = node; inViewRef(node); }, [inViewRef], ); useEffect(() => { (async () => { if (videoRef.current && hmsVideoTrack) { HMSLogger.d('Video InView', videoTrack, inView); if (inView && hmsVideoTrack.enabled) { await hmsActions.attachVideo(hmsVideoTrack.id, videoRef.current); } else { await hmsActions.detachVideo(hmsVideoTrack.id, videoRef.current); } } })(); }, [inView, videoRef, videoTrack, hmsVideoTrack]); useEffect(() => { if (videoRef && videoRef.current && videoTrack && !hmsVideoTrack) { videoRef.current.srcObject = new MediaStream([videoTrack]); } }, [videoRef, videoTrack, isLocal]); return ( <> {showAudioLevel && audioLevelDisplayType === 'border' && ( )} ); };