diff --git a/Project/src/MakeCall/CallCard.js b/Project/src/MakeCall/CallCard.js
index df0e757..fb9dcdf 100644
--- a/Project/src/MakeCall/CallCard.js
+++ b/Project/src/MakeCall/CallCard.js
@@ -1,6 +1,6 @@
import React from "react";
import { MessageBar, MessageBarType } from '@fluentui/react'
-import { FunctionalStreamRenderer as StreamRenderer } from "./FunctionalStreamRenderer";
+import { StreamRenderer } from "./StreamRenderer";
import AddParticipantPopover from "./AddParticipantPopover";
import RemoteParticipantCard from "./RemoteParticipantCard";
import { Panel, PanelType } from '@fluentui/react/lib/Panel';
@@ -517,7 +517,6 @@ export default class CallCard extends React.Component {
}
updateListOfParticipantsToRender(reason) {
-
const ovcFeature = this.call.feature(Features.OptimalVideoCount);
const optimalVideoCount = ovcFeature.optimalVideoCount;
console.log(`updateListOfParticipantsToRender because ${reason}, ovc is ${optimalVideoCount}`);
@@ -559,7 +558,7 @@ export default class CallCard extends React.Component {
});
streamsToAdd = streamsToAdd.slice(0, optimalVideoCount - streamsToKeep.length);
console.log(`updateListOfParticipantsToRender identified ${streamsToAdd.length} streams to add`);
- streamsToKeep = streamsToKeep.concat(streamsToAdd.filter(e => !!e));
+ streamsToKeep = streamsToKeep.concat(streamsToAdd.filter(stream => !!stream.participant));
}
console.log(`updateListOfParticipantsToRender final number of streams to render ${streamsToKeep.length}}`);
this.setState(prevState => ({
diff --git a/Project/src/MakeCall/FunctionalStreamRenderer.js b/Project/src/MakeCall/FunctionalStreamRenderer.js
deleted file mode 100644
index 40b1473..0000000
--- a/Project/src/MakeCall/FunctionalStreamRenderer.js
+++ /dev/null
@@ -1,186 +0,0 @@
-import React, { useEffect, useState, useRef, useImperativeHandle, forwardRef } from "react";
-import { utils } from '../Utils/Utils';
-import { VideoStreamRenderer } from "@azure/communication-calling";
-import CustomVideoEffects from "./RawVideoAccess/CustomVideoEffects";
-import VideoReceiveStats from './VideoReceiveStats';
-
-export const FunctionalStreamRenderer = forwardRef(({
- remoteParticipant,
- stream,
- isPinningActive,
- isPinned,
- dominantRemoteParticipant,
- dominantSpeakerMode,
- call,
- streamCount,
- showMediaStats
-}, ref) => {
- const componentId = `${utils.getIdentifierText(remoteParticipant.identifier)}-${stream.mediaStreamType}-${stream.id}`;
- const videoContainerId = componentId + '-videoContainer';
- const componentContainer = useRef(null);
- const videoContainer = useRef(null);
- const renderer = useRef(null);
- const view = useRef(null);
- const [isLoading, setIsLoading] = useState(false);
- const [isSpeaking, setIsSpeaking] = useState(!!remoteParticipant?.isSpeaking);
- const [isMuted, setIsMuted] = useState(!!remoteParticipant?.isMuted);
- const [displayName, setDisplayName] = useState(remoteParticipant?.displayName?.trim() ?? '');
- const [videoStats, setVideoStats] = useState();
- const [transportStats, setTransportStats] = useState();
-
- useEffect(() => {
- initializeComponent();
- return () => {
- stream.off('isReceivingChanged', isReceivingChanged);
- remoteParticipant.off('isSpeakingChanged', isSpeakingChanged);
- remoteParticipant.off('isMutedChanged', isMutedChanged);
- remoteParticipant.off('displayNameChanged', isDisplayNameChanged);
- disposeRenderer();
- }
- }, []);
-
- const getRenderer = () => {
- return view.current;
- }
-
- const createRenderer = async () => {
- if (!renderer.current) {
- renderer.current = new VideoStreamRenderer(stream);
- view.current = await renderer.current.createView();
- return view.current;
- } else {
- throw new Error(`[App][StreamMedia][id=${stream.id}][createRenderer] stream already has a renderer`);
- }
- }
-
- const attachRenderer = (v) => {
- try {
- if (v) {
- view.current = v;
- }
-
- if (!view.current.target) {
- throw new Error(`[App][StreamMedia][id=${stream.id}][attachRenderer] target is undefined. Must create renderer first`);
- } else {
- componentContainer.current.style.display = 'block';
- videoContainer.current.appendChild(view.current?.target);
- }
- } catch (e) {
- console.error(e);
- }
- }
-
- const disposeRenderer = () => {
- if (videoContainer.current && componentContainer.current) {
- videoContainer.current.innerHTML = '';
- componentContainer.current.style.display = 'none';
- }
- if (renderer.current) {
- renderer.current.dispose();
- } else {
- console.warn(`[App][StreamMedia][id=${stream.id}][disposeRender] no renderer to dispose`);
- }
- }
- const isReceivingChanged = () => {
- try {
- if (stream?.isAvailable) {
- setIsLoading(!stream.isReceiving);
- } else {
- setIsLoading(false);
- }
-
- } catch (e) {
- console.error(e);
- }
- };
-
- const isMutedChanged = () => {
- setIsMuted(remoteParticipant && remoteParticipant?.isMuted);
- };
-
- const isSpeakingChanged = () => {
- setIsSpeaking(remoteParticipant && remoteParticipant.isSpeaking);
- }
-
- const isDisplayNameChanged = () => {
- setDisplayName(remoteParticipant.displayName.trim());
- }
- /**
- * Start stream after DOM has rendered
- */
- const initializeComponent = async () => {
- stream.on('isReceivingChanged', isReceivingChanged);
- remoteParticipant.on('isMutedChanged', isMutedChanged);
- remoteParticipant.on('isSpeakingChanged', isSpeakingChanged);
- if (dominantSpeakerMode && dominantRemoteParticipant !== remoteParticipant) {
- return;
- }
-
- try {
- if (stream.isAvailable && !renderer.current) {
- await createRenderer();
- attachRenderer();
- }
- } catch (e) {
- console.error(e);
- }
- }
-
- useImperativeHandle(ref, () => ({
- updateReceiveStats(videoStatsReceived, transportStatsReceived) {
- if (videoStatsReceived) {
- if (videoStatsReceived !== videoStats && stream.isAvailable) {
- setVideoStats(videoStatsReceived);
- setTransportStats(transportStatsReceived);
- }
- }
- },
- getRenderer,
- createRenderer,
- attachRenderer,
- disposeRenderer
- }));
-
- if (stream.isAvailable) {
- return (
-
-
-
- {displayName ? displayName : remoteParticipant.displayName ? remoteParticipant.displayName : utils.getIdentifierText(remoteParticipant.identifier)}
-
-
- {
- isLoading &&
- }
-
- {
- videoStats && showMediaStats &&
-
-
-
- }
-
- );
- }
- return <>>;
-});
diff --git a/Project/src/MakeCall/StreamRenderer.js b/Project/src/MakeCall/StreamRenderer.js
index 06b3a7c..1d87f36 100644
--- a/Project/src/MakeCall/StreamRenderer.js
+++ b/Project/src/MakeCall/StreamRenderer.js
@@ -1,187 +1,186 @@
-import React from "react";
+import React, { useEffect, useState, useRef, useImperativeHandle, forwardRef } from "react";
import { utils } from '../Utils/Utils';
import { VideoStreamRenderer } from "@azure/communication-calling";
import CustomVideoEffects from "./RawVideoAccess/CustomVideoEffects";
import VideoReceiveStats from './VideoReceiveStats';
-export default class StreamRenderer extends React.Component {
- constructor(props) {
- super(props);
- this.stream = props.stream;
- this.remoteParticipant = props.remoteParticipant;
- this.componentId = `${utils.getIdentifierText(this.remoteParticipant.identifier)}-${this.stream.mediaStreamType}-${this.stream.id}`;
- this.videoContainerId = this.componentId + '-videoContainer';
- this.videoContainer = undefined;
- this.renderer = undefined;
- this.view = undefined;
- this.dominantSpeakerMode = props.dominantSpeakerMode;
- this.dominantRemoteParticipant = props.dominantRemoteParticipant;
- this.loadingSpinner = document.createElement('div');
- this.loadingSpinner.className = 'remote-video-loading-spinner';
- this.state = {
- isSpeaking: false,
- displayName: this.remoteParticipant.displayName?.trim(),
- videoStats: undefined,
- transportStats: undefined
- };
- this.call = props.call;
- }
-
- componentDidUpdate(prevProps, prevState, snapshot) {
- if (this.dominantSpeakerMode !== prevProps.dominantSpeakerMode) {
- this.dominantSpeakerMode = prevProps.dominantSpeakerMode
+export const StreamRenderer = forwardRef(({
+ remoteParticipant,
+ stream,
+ isPinningActive,
+ isPinned,
+ dominantRemoteParticipant,
+ dominantSpeakerMode,
+ call,
+ streamCount,
+ showMediaStats
+}, ref) => {
+ const componentId = `${utils.getIdentifierText(remoteParticipant.identifier)}-${stream.mediaStreamType}-${stream.id}`;
+ const videoContainerId = componentId + '-videoContainer';
+ const componentContainer = useRef(null);
+ const videoContainer = useRef(null);
+ const renderer = useRef(null);
+ const view = useRef(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [isSpeaking, setIsSpeaking] = useState(!!remoteParticipant?.isSpeaking);
+ const [isMuted, setIsMuted] = useState(!!remoteParticipant?.isMuted);
+ const [displayName, setDisplayName] = useState(remoteParticipant?.displayName?.trim() ?? '');
+ const [videoStats, setVideoStats] = useState();
+ const [transportStats, setTransportStats] = useState();
+
+ useEffect(() => {
+ initializeComponent();
+ return () => {
+ stream.off('isReceivingChanged', isReceivingChanged);
+ remoteParticipant.off('isSpeakingChanged', isSpeakingChanged);
+ remoteParticipant.off('isMutedChanged', isMutedChanged);
+ remoteParticipant.off('displayNameChanged', isDisplayNameChanged);
+ disposeRenderer();
}
+ }, []);
- if (this.dominantRemoteParticipant !== prevProps.dominantRemoteParticipant) {
- this.dominantRemoteParticipant = prevProps.dominantRemoteParticipant
- }
+ const getRenderer = () => {
+ return view.current;
}
- /**
- * Start stream after DOM has rendered
- */
- async componentDidMount() {
- document.getElementById(this.componentId).hidden = true;
- this.videoContainer = document.getElementById(this.videoContainerId);
-
- this.remoteParticipant.on('isSpeakingChanged', () => {
- this.setState({ isSpeaking: this.remoteParticipant.isSpeaking });
- });
-
- this.remoteParticipant.on('isMutedChanged', () => {
- if (this.remoteParticipant.isMuted) {
- this.setState({ isSpeaking: false });
- }
- });
- this.remoteParticipant.on('displayNameChanged', () => {
- this.setState({ displayName: this.remoteParticipant.displayName?.trim() });
- })
-
- console.log(`[App][StreamMedia][id=${this.stream.id}] handle new stream`);
- console.log(`[App][StreamMedia][id=${this.stream.id}] stream info - ` +
- `streamId=${this.stream.id}, streamType=${this.stream.mediaStreamType}, ` +
- `isAvailable=${this.stream.isAvailable}, isReceiving=${this.stream.isReceiving}`);
+ const createRenderer = async () => {
+ if (!renderer.current) {
+ renderer.current = new VideoStreamRenderer(stream);
+ view.current = await renderer.current.createView();
+ return view.current;
+ } else {
+ throw new Error(`[App][StreamMedia][id=${stream.id}][createRenderer] stream already has a renderer`);
+ }
+ }
- /**
- * This feature is alpha
- * @beta
- */
- console.log(`[App][StreamMedia][id=${this.stream.id}] subscribing to isRenderingChanged`);
- this.stream.on('isReceivingChanged', () => {
- try {
- if (this.stream.isAvailable) {
- const isReceiving = this.stream.isReceiving;
- const isLoadingSpinnerActive = this.videoContainer.contains(this.loadingSpinner);
- if (!isReceiving && !isLoadingSpinnerActive) {
- this.videoContainer.appendChild(this.loadingSpinner);
- } else if (isReceiving && isLoadingSpinnerActive) {
- this.videoContainer.removeChild(this.loadingSpinner);
- }
- }
- } catch (e) {
- console.error(e);
+ const attachRenderer = (v) => {
+ try {
+ if (v) {
+ view.current = v;
}
- });
-
- console.log(`[App][StreamMedia][id=${this.stream.id}] subscribing to isAvailableChanged`);
- this.stream.on('isAvailableChanged', async () => {
- try {
- if(this.dominantSpeakerMode && this.dominantRemoteParticipant !== this.remoteParticipant) {
- return;
- }
- console.log(`[App][StreamMedia][id=${this.stream.id}][isAvailableChanged] triggered`);
- if (this.stream.isAvailable && !this.renderer) {
- console.log(`[App][StreamMedia][id=${this.stream.id}][isAvailableChanged] isAvailable=${this.stream.isAvailable}`);
- await this.createRenderer();
- this.attachRenderer();
- } else {
- console.log(`[App][StreamMedia][id=${this.stream.id}][isAvailableChanged] isAvailable=${this.stream.isAvailable}`);
- this.disposeRenderer();
- }
- } catch (e) {
- console.error(e);
+ if (!view.current.target) {
+ throw new Error(`[App][StreamMedia][id=${stream.id}][attachRenderer] target is undefined. Must create renderer first`);
+ } else {
+ componentContainer.current.style.display = 'block';
+ videoContainer.current.appendChild(view.current?.target);
}
- });
-
- if(this.dominantSpeakerMode && this.dominantRemoteParticipant !== this.remoteParticipant) {
- return;
+ } catch (e) {
+ console.error(e);
}
+ }
+ const disposeRenderer = () => {
+ if (videoContainer.current && componentContainer.current) {
+ videoContainer.current.innerHTML = '';
+ componentContainer.current.style.display = 'none';
+ }
+ if (renderer.current) {
+ renderer.current.dispose();
+ } else {
+ console.warn(`[App][StreamMedia][id=${stream.id}][disposeRender] no renderer to dispose`);
+ }
+ }
+ const isReceivingChanged = () => {
try {
- console.log(`[App][StreamMedia][id=${this.stream.id}] checking initial value - isAvailable=${this.stream.isAvailable}`);
- if (this.stream.isAvailable && !this.renderer) {
- await this.createRenderer();
- this.attachRenderer();
+ if (stream?.isAvailable) {
+ setIsLoading(!stream.isReceiving);
+ } else {
+ setIsLoading(false);
}
+
} catch (e) {
console.error(e);
}
- }
+ };
- getRenderer() {
- return this.renderer;
- }
+ const isMutedChanged = () => {
+ setIsMuted(remoteParticipant && remoteParticipant?.isMuted);
+ };
- updateReceiveStats(videoStats, transportStats) {
- if (this.state.videoStats !== videoStats) {
- this.setState({ videoStats, transportStats });
- }
+ const isSpeakingChanged = () => {
+ setIsSpeaking(remoteParticipant && remoteParticipant.isSpeaking);
}
- async createRenderer() {
- console.info(`[App][StreamMedia][id=${this.stream.id}][renderStream] attempt to render stream type=${this.stream.mediaStreamType}, id=${this.stream.id}`);
- if (!this.renderer) {
- this.renderer = new VideoStreamRenderer(this.stream);
- this.view = await this.renderer.createView();
- console.info(`[App][StreamMedia][id=${this.stream.id}][renderStream] createView resolved, appending view`);
- } else {
- throw new Error(`[App][StreamMedia][id=${this.stream.id}][createRenderer] stream already has a renderer`);
- }
+ const isDisplayNameChanged = () => {
+ setDisplayName(remoteParticipant.displayName.trim());
}
+ /**
+ * Start stream after DOM has rendered
+ */
+ const initializeComponent = async () => {
+ stream.on('isReceivingChanged', isReceivingChanged);
+ remoteParticipant.on('isMutedChanged', isMutedChanged);
+ remoteParticipant.on('isSpeakingChanged', isSpeakingChanged);
+ if (dominantSpeakerMode && dominantRemoteParticipant !== remoteParticipant) {
+ return;
+ }
- async attachRenderer() {
- console.info(`[App][StreamMedia][id=${this.stream.id}][attachRenderer] attempt to attach view=${this.view.target}, id=${this.stream.id} to DOM, under container id=${this.videoContainerId}`);
try {
- if(!this.view.target) {
- throw new Error(`[App][StreamMedia][id=${this.stream.id}][attachRenderer] target is undefined. Must create renderer first`);
+ if (stream.isAvailable && !renderer.current) {
+ await createRenderer();
+ attachRenderer();
}
- document.getElementById(this.componentId).hidden = false;
- document.getElementById(this.videoContainerId).appendChild(this.view.target);
} catch (e) {
console.error(e);
}
}
- disposeRenderer() {
- if (this.renderer) {
- this.renderer.dispose();
- this.renderer = undefined;
- document.getElementById(this.componentId).hidden = true;
- } else {
- console.warn(`[App][StreamMedia][id=${this.stream.id}][disposeRender] no renderer to dispose`);
- }
- }
-
- render() {
+ useImperativeHandle(ref, () => ({
+ updateReceiveStats(videoStatsReceived, transportStatsReceived) {
+ if (videoStatsReceived) {
+ if (videoStatsReceived !== videoStats && stream.isAvailable) {
+ setVideoStats(videoStatsReceived);
+ setTransportStats(transportStatsReceived);
+ }
+ }
+ },
+ getRenderer,
+ createRenderer,
+ attachRenderer,
+ disposeRenderer
+ }));
+
+ if (stream.isAvailable) {
return (
-
-
-
- {this.state.displayName ? this.state.displayName : utils.getIdentifierText(this.remoteParticipant.identifier)}
-
+
+
+
+ {displayName ? displayName : remoteParticipant.displayName ? remoteParticipant.displayName : utils.getIdentifierText(remoteParticipant.identifier)}
+
+
+ {
+ isLoading &&
+ }
+
{
- this.state.videoStats &&
+ videoStats && showMediaStats &&
-
+
}
-
-
);
}
-}
-
-
-
+ return <>>;
+});