diff --git a/modules/UI/videolayout/LargeVideoManager.js b/modules/UI/videolayout/LargeVideoManager.js index b0aadcc197..3e90b563f7 100644 --- a/modules/UI/videolayout/LargeVideoManager.js +++ b/modules/UI/videolayout/LargeVideoManager.js @@ -11,13 +11,15 @@ import { Avatar } from '../../../react/features/base/avatar'; import theme from '../../../react/features/base/components/themes/participantsPaneTheme.json'; import { getSourceNameSignalingFeatureFlag } from '../../../react/features/base/config'; import { i18next } from '../../../react/features/base/i18n'; +import { JitsiTrackEvents } from '../../../react/features/base/lib-jitsi-meet'; import { VIDEO_TYPE } from '../../../react/features/base/media'; import { getParticipantById, getParticipantDisplayName } from '../../../react/features/base/participants'; import { - getVideoTrackByParticipant + getVideoTrackByParticipant, + trackStreamingStatusChanged } from '../../../react/features/base/tracks'; import { CHAT_SIZE } from '../../../react/features/chat'; import { @@ -116,6 +118,14 @@ export default class LargeVideoManager { */ this._videoAspectRatio = 0; + /** + * The video track in effect. + * This is used to add and remove listeners on track streaming status change. + * + * @type {Object} + */ + this.videoTrack = undefined; + this.$container = $('#largeVideoContainer'); this.$container.css({ @@ -242,6 +252,26 @@ export default class LargeVideoManager { const tracks = state['features/base/tracks']; const videoTrack = getVideoTrackByParticipant(tracks, participant); + // Remove track streaming status listener from the old track and add it to the new track, + // in order to stop updating track streaming status for the old track and start it for the new track. + // TODO: when this class is converted to a function react component, + // use a custom hook to update a local track streaming status. + if (this.videoTrack?.jitsiTrack?.getSourceName() !== videoTrack?.jitsiTrack?.getSourceName()) { + if (this.videoTrack) { + this.videoTrack.jitsiTrack.off(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, + this.handleTrackStreamingStatusChanged); + APP.store.dispatch(trackStreamingStatusChanged(this.videoTrack.jitsiTrack, + this.videoTrack.jitsiTrack.getTrackStreamingStatus())); + } + if (videoTrack && !videoTrack.local) { + this.videoTrack = videoTrack; + this.videoTrack.jitsiTrack.on(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, + this.handleTrackStreamingStatusChanged); + APP.store.dispatch(trackStreamingStatusChanged(this.videoTrack.jitsiTrack, + this.videoTrack.jitsiTrack.getTrackStreamingStatus())); + } + } + isVideoRenderable = !isVideoMuted && ( APP.conference.isLocalId(id) || participant?.isLocalScreenShare @@ -340,6 +370,19 @@ export default class LargeVideoManager { }); } + /** + * Handle track streaming status change event by + * by dispatching an action to update track streaming status for the given track in app state. + * + * @param {JitsiTrack} jitsiTrack the track with streaming status updated + * @param {JitsiTrackStreamingStatus} streamingStatus the updated track streaming status + * + * @private + */ + handleTrackStreamingStatusChanged(jitsiTrack, streamingStatus) { + APP.store.dispatch(trackStreamingStatusChanged(jitsiTrack, streamingStatus)); + } + /** * Shows/hides notification about participant's connectivity issues to be * shown on the large video area. diff --git a/react/features/connection-indicator/components/web/ConnectionIndicatorIcon.js b/react/features/connection-indicator/components/web/ConnectionIndicatorIcon.js index 116e4493fd..40ed4cab8a 100644 --- a/react/features/connection-indicator/components/web/ConnectionIndicatorIcon.js +++ b/react/features/connection-indicator/components/web/ConnectionIndicatorIcon.js @@ -52,12 +52,14 @@ export const ConnectionIndicatorIcon = ({ }: Props) => { const sourceNameSignalingEnabled = useSelector(state => getSourceNameSignalingFeatureFlag(state)); const dispatch = useDispatch(); - const sourceName = track?.jitsiTrack?.getSourceName?.(); + const sourceName = track?.jitsiTrack?.getSourceName(); const handleTrackStreamingStatusChanged = streamingStatus => { dispatch(trackStreamingStatusChanged(track.jitsiTrack, streamingStatus)); }; + // TODO: replace this with a custom hook to be reused where track streaming status is needed. + // TODO: In the hood the listener should updates a local track streaming status instead of that in redux store. useEffect(() => { if (track && !track.local && sourceNameSignalingEnabled) { track.jitsiTrack.on(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, handleTrackStreamingStatusChanged); diff --git a/react/features/filmstrip/components/web/Thumbnail.js b/react/features/filmstrip/components/web/Thumbnail.js index 3651fcd75f..2521ad6454 100644 --- a/react/features/filmstrip/components/web/Thumbnail.js +++ b/react/features/filmstrip/components/web/Thumbnail.js @@ -9,6 +9,7 @@ import { createScreenSharingIssueEvent, sendAnalytics } from '../../../analytics import { Avatar } from '../../../base/avatar'; import { getSourceNameSignalingFeatureFlag } from '../../../base/config'; import { isMobileBrowser } from '../../../base/environment/utils'; +import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet'; import { MEDIA_TYPE, VideoTrack } from '../../../base/media'; import { getLocalParticipant, @@ -24,7 +25,8 @@ import { getLocalVideoTrack, getTrackByMediaTypeAndParticipant, getFakeScreenshareParticipantTrack, - updateLastTrackVideoMediaEvent + updateLastTrackVideoMediaEvent, + trackStreamingStatusChanged } from '../../../base/tracks'; import { getVideoObjectPosition } from '../../../face-landmarks/functions'; import { hideGif, showGif } from '../../../gifs/actions'; @@ -54,6 +56,7 @@ import ThumbnailAudioIndicator from './ThumbnailAudioIndicator'; import ThumbnailBottomIndicators from './ThumbnailBottomIndicators'; import ThumbnailTopIndicators from './ThumbnailTopIndicators'; + declare var interfaceConfig: Object; /** @@ -244,7 +247,12 @@ export type Props = {| /** * Styles that will be set to the Thumbnail's main span element. */ - style?: ?Object + style?: ?Object, + + /** + * Whether source name signaling is enabled. + */ + _sourceNameSignalingEnabled: boolean |}; const defaultStyles = theme => { @@ -404,6 +412,7 @@ class Thumbnail extends Component { this._hidePopover = this._hidePopover.bind(this); this._onGifMouseEnter = this._onGifMouseEnter.bind(this); this._onGifMouseLeave = this._onGifMouseLeave.bind(this); + this.handleTrackStreamingStatusChanged = this.handleTrackStreamingStatusChanged.bind(this); } /** @@ -414,6 +423,38 @@ class Thumbnail extends Component { */ componentDidMount() { this._onDisplayModeChanged(); + + + // Listen to track streaming status changed event to keep it updated. + // TODO: after converting this component to a react function component, + // use a custom hook to update local track streaming status. + const { _videoTrack, dispatch, _sourceNameSignalingEnabled } = this.props; + + if (_sourceNameSignalingEnabled && _videoTrack && !_videoTrack.local) { + _videoTrack.jitsiTrack.on(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, + this.handleTrackStreamingStatusChanged); + dispatch(trackStreamingStatusChanged(_videoTrack.jitsiTrack, + _videoTrack.jitsiTrack.getTrackStreamingStatus())); + } + } + + /** + * Remove listeners for track streaming status update. + * + * @inheritdoc + * @returns {void} + */ + componentWillUnmount() { + // TODO: after converting this component to a react function component, + // use a custom hook to update local track streaming status. + const { _videoTrack, dispatch, _sourceNameSignalingEnabled } = this.props; + + if (_sourceNameSignalingEnabled && _videoTrack && !_videoTrack.local) { + _videoTrack.jitsiTrack.off(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, + this.handleTrackStreamingStatusChanged); + dispatch(trackStreamingStatusChanged(_videoTrack.jitsiTrack, + _videoTrack.jitsiTrack.getTrackStreamingStatus())); + } } /** @@ -427,6 +468,38 @@ class Thumbnail extends Component { if (prevState.displayMode !== this.state.displayMode) { this._onDisplayModeChanged(); } + + // TODO: after converting this component to a react function component, + // use a custom hook to update local track streaming status. + const { _videoTrack, dispatch, _sourceNameSignalingEnabled } = this.props; + + if (_sourceNameSignalingEnabled + && prevProps._videoTrack?.jitsiTrack?.getSourceName() !== _videoTrack?.jitsiTrack?.getSourceName()) { + if (prevProps._videoTrack && !prevProps._videoTrack.local) { + prevProps._videoTrack.jitsiTrack.off(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, + this.handleTrackStreamingStatusChanged); + dispatch(trackStreamingStatusChanged(prevProps._videoTrack.jitsiTrack, + prevProps._videoTrack.jitsiTrack.getTrackStreamingStatus())); + } + if (_videoTrack && !_videoTrack.local) { + _videoTrack.jitsiTrack.on(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, + this.handleTrackStreamingStatusChanged); + dispatch(trackStreamingStatusChanged(_videoTrack.jitsiTrack, + _videoTrack.jitsiTrack.getTrackStreamingStatus())); + } + } + } + + /** + * Handle track streaming status change event by + * by dispatching an action to update track streaming status for the given track in app state. + * + * @param {JitsiTrack} jitsiTrack - The track with streaming status updated. + * @param {JitsiTrackStreamingStatus} streamingStatus - The updated track streaming status. + * @returns {void} + */ + handleTrackStreamingStatusChanged(jitsiTrack, streamingStatus) { + this.props.dispatch(trackStreamingStatusChanged(jitsiTrack, streamingStatus)); } /** @@ -1212,7 +1285,8 @@ function _mapStateToProps(state, ownProps): Object { _videoObjectPosition: getVideoObjectPosition(state, participant?.id), _videoTrack, ...size, - _gifSrc: mode === 'chat' ? null : gifSrc + _gifSrc: mode === 'chat' ? null : gifSrc, + _sourceNameSignalingEnabled: sourceNameSignalingEnabled }; }