updated Tile & Video to nullify src object
This commit is contained in:
parent
ef545d0480
commit
c9596f14c9
|
|
@ -1,5 +1,11 @@
|
||||||
/**
|
/**
|
||||||
* Audio
|
* Audio
|
||||||
|
* ---
|
||||||
|
* When working with audio elements it's very important to avoid mutating
|
||||||
|
* the DOM elements as much as possible to avoid audio pops and crackles.
|
||||||
|
* This component addresses to known browser quirks; Safari autoplay
|
||||||
|
* and Chrome's maximum media elements. On Chrome we add all audio tracks
|
||||||
|
* into into a single audio node using the CombinedAudioTrack component
|
||||||
*/
|
*/
|
||||||
import React, { useEffect, useMemo } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import { useTracks } from '@dailyjs/shared/contexts/TracksProvider';
|
import { useTracks } from '@dailyjs/shared/contexts/TracksProvider';
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import useVideoTrack from '@dailyjs/shared/hooks/useVideoTrack';
|
import useVideoTrack from '@dailyjs/shared/hooks/useVideoTrack';
|
||||||
import { ReactComponent as IconMicMute } from '@dailyjs/shared/icons/mic-off-sm.svg';
|
import { ReactComponent as IconMicMute } from '@dailyjs/shared/icons/mic-off-sm.svg';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
@ -14,11 +14,36 @@ export const Tile = React.memo(
|
||||||
showName = true,
|
showName = true,
|
||||||
showAvatar = true,
|
showAvatar = true,
|
||||||
aspectRatio = DEFAULT_ASPECT_RATIO,
|
aspectRatio = DEFAULT_ASPECT_RATIO,
|
||||||
|
onVideoResize,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const videoTrack = useVideoTrack(participant);
|
const videoTrack = useVideoTrack(participant);
|
||||||
const videoEl = useRef(null);
|
const videoEl = useRef(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add optional event listener for resize event so the parent component
|
||||||
|
* can know the video's native aspect ratio.
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
const video = videoEl.current;
|
||||||
|
if (!onVideoResize || !video) return false;
|
||||||
|
|
||||||
|
const handleResize = () => {
|
||||||
|
if (!video) return;
|
||||||
|
const width = video?.videoWidth;
|
||||||
|
const height = video?.videoHeight;
|
||||||
|
if (width && height) {
|
||||||
|
// Return the video's aspect ratio to the parent's handler
|
||||||
|
onVideoResize(width / height);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleResize();
|
||||||
|
video?.addEventListener('resize', handleResize);
|
||||||
|
|
||||||
|
return () => video?.removeEventListener('resize', handleResize);
|
||||||
|
}, [onVideoResize, videoEl, participant]);
|
||||||
|
|
||||||
const cx = classNames('tile', {
|
const cx = classNames('tile', {
|
||||||
mirrored,
|
mirrored,
|
||||||
avatar: showAvatar && !videoTrack,
|
avatar: showAvatar && !videoTrack,
|
||||||
|
|
@ -35,7 +60,11 @@ export const Tile = React.memo(
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{videoTrack ? (
|
{videoTrack ? (
|
||||||
<Video ref={videoEl} videoTrack={videoTrack} />
|
<Video
|
||||||
|
ref={videoEl}
|
||||||
|
participantId={participant?.id}
|
||||||
|
videoTrack={videoTrack}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
showAvatar && (
|
showAvatar && (
|
||||||
<div className="avatar">
|
<div className="avatar">
|
||||||
|
|
@ -122,6 +151,7 @@ Tile.propTypes = {
|
||||||
showName: PropTypes.bool,
|
showName: PropTypes.bool,
|
||||||
showAvatar: PropTypes.bool,
|
showAvatar: PropTypes.bool,
|
||||||
aspectRatio: PropTypes.number,
|
aspectRatio: PropTypes.number,
|
||||||
|
onVideoResize: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Tile;
|
export default Tile;
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,46 @@
|
||||||
import React, { forwardRef, memo, useEffect } from 'react';
|
import React, { useMemo, forwardRef, memo, useEffect } from 'react';
|
||||||
|
import Bowser from 'bowser';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { shallowEqualObjects } from 'shallow-equal';
|
import { shallowEqualObjects } from 'shallow-equal';
|
||||||
|
|
||||||
export const Video = memo(
|
export const Video = memo(
|
||||||
forwardRef(({ videoTrack, ...rest }, videoEl) => {
|
forwardRef(({ participantId, videoTrack, ...rest }, videoEl) => {
|
||||||
|
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=1232649
|
||||||
|
const isChrome92 = useMemo(() => {
|
||||||
|
const { browser, platform, os } = Bowser.parse(navigator.userAgent);
|
||||||
|
return (
|
||||||
|
browser.name === 'Chrome' &&
|
||||||
|
parseInt(browser.version, 10) >= 92 &&
|
||||||
|
(platform.type === 'desktop' || os.name === 'Android')
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effect: Umount
|
||||||
|
* Note: nullify src to ensure media object is not counted
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
const video = videoEl.current;
|
||||||
|
if (!video) return false;
|
||||||
|
// clean up when video renders for different participant
|
||||||
|
video.srcObject = null;
|
||||||
|
if (isChrome92) video.load();
|
||||||
|
return () => {
|
||||||
|
// clean up when unmounted
|
||||||
|
video.srcObject = null;
|
||||||
|
if (isChrome92) video.load();
|
||||||
|
};
|
||||||
|
}, [videoEl, isChrome92, participantId]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Effect: mount source
|
* Effect: mount source
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!videoEl?.current) return;
|
const video = videoEl.current;
|
||||||
// eslint-disable-next-line no-param-reassign
|
if (!video || !videoTrack) return;
|
||||||
videoEl.current.srcObject = new MediaStream([videoTrack]);
|
video.srcObject = new MediaStream([videoTrack]);
|
||||||
}, [videoEl, videoTrack]);
|
if (isChrome92) video.load();
|
||||||
|
}, [videoEl, isChrome92, videoTrack]);
|
||||||
/**
|
|
||||||
* Effect: unmount
|
|
||||||
*/
|
|
||||||
useEffect(
|
|
||||||
() => () => {
|
|
||||||
if (videoEl?.current?.srcObject) {
|
|
||||||
videoEl.current.srcObject.getVideoTracks().forEach((t) => t.stop());
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
videoEl.current.srcObject = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[videoEl]
|
|
||||||
);
|
|
||||||
|
|
||||||
return <video autoPlay muted playsInline ref={videoEl} {...rest} />;
|
return <video autoPlay muted playsInline ref={videoEl} {...rest} />;
|
||||||
}),
|
}),
|
||||||
|
|
@ -35,6 +50,7 @@ export const Video = memo(
|
||||||
Video.propTypes = {
|
Video.propTypes = {
|
||||||
videoTrack: PropTypes.any,
|
videoTrack: PropTypes.any,
|
||||||
mirrored: PropTypes.bool,
|
mirrored: PropTypes.bool,
|
||||||
|
participantId: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Video;
|
export default Video;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue