@@ -2,21 +2,20 @@ import React, { useEffect, useRef } from 'react';
22import { Platform , StyleSheet , View } from 'react-native' ;
33import type { MediaStream } from '@stream-io/react-native-webrtc' ;
44import { RTCView } from '@stream-io/react-native-webrtc' ;
5- import type { ParticipantViewProps } from './ParticipantView' ;
5+ import type { ParticipantViewProps } from '.. /ParticipantView' ;
66import {
7- CallingState ,
87 hasPausedTrack ,
98 hasScreenShare ,
109 hasVideo ,
11- SfuModels ,
1210 type VideoTrackType ,
1311 VisibilityState ,
1412} from '@stream-io/video-client' ;
1513import { useCall , useCallStateHooks } from '@stream-io/video-react-bindings' ;
16- import { ParticipantVideoFallback as DefaultParticipantVideoFallback } from './ParticipantVideoFallback' ;
17- import { useTheme } from '../../../contexts/ThemeContext' ;
18- import { useTrackDimensions } from '../../../hooks/useTrackDimensions' ;
19- import { useScreenshotIosContext } from '../../../contexts/internal/ScreenshotIosContext' ;
14+ import { ParticipantVideoFallback as DefaultParticipantVideoFallback } from '../ParticipantVideoFallback' ;
15+ import { useTheme } from '../../../../contexts/ThemeContext' ;
16+ import { useTrackDimensions } from '../../../../hooks/useTrackDimensions' ;
17+ import { useScreenshotIosContext } from '../../../../contexts/internal/ScreenshotIosContext' ;
18+ import TrackSubscriber , { TrackSubscriberHandle } from './TrackSubscriber' ;
2019
2120const DEFAULT_VIEWPORT_VISIBILITY_STATE : Record <
2221 VideoTrackType ,
@@ -56,16 +55,9 @@ export const VideoRenderer = ({
5655 theme : { videoRenderer } ,
5756 } = useTheme ( ) ;
5857 const call = useCall ( ) ;
59- const { useCallCallingState , useCameraState, useIncomingVideoSettings } =
60- useCallStateHooks ( ) ;
58+ const { useCameraState, useIncomingVideoSettings } = useCallStateHooks ( ) ;
59+ const trackSubscriberRef = useRef < TrackSubscriberHandle > ( null ) ;
6160 const { isParticipantVideoEnabled } = useIncomingVideoSettings ( ) ;
62- const callingState = useCallCallingState ( ) ;
63- const pendingVideoLayoutRef = useRef < SfuModels . VideoDimension | undefined > (
64- undefined ,
65- ) ;
66- const subscribedVideoLayoutRef = useRef < SfuModels . VideoDimension | undefined > (
67- undefined ,
68- ) ;
6961 const { direction } = useCameraState ( ) ;
7062 const viewRef = useRef ( null ) ;
7163 const {
@@ -91,7 +83,6 @@ export const VideoRenderer = ({
9183 ? hasScreenShare ( participant )
9284 : hasVideo ( participant ) ;
9385
94- const hasJoinedCall = callingState === CallingState . JOINED ;
9586 const videoStreamToRender = ( isScreenSharing
9687 ? screenShareStream
9788 : videoStream ) as unknown as MediaStream | undefined ;
@@ -179,11 +170,6 @@ export const VideoRenderer = ({
179170 } ,
180171 } ) ) ;
181172 }
182- if ( subscribedVideoLayoutRef . current ) {
183- // when video is enabled again, we want to use the last subscribed dimension to resubscribe
184- pendingVideoLayoutRef . current = subscribedVideoLayoutRef . current ;
185- subscribedVideoLayoutRef . current = undefined ;
186- }
187173 }
188174 } , [
189175 sessionId ,
@@ -194,101 +180,26 @@ export const VideoRenderer = ({
194180 isLocalParticipant ,
195181 ] ) ;
196182
197- useEffect ( ( ) => {
198- if ( ! hasJoinedCall && subscribedVideoLayoutRef . current ) {
199- // when call is joined again, we want to use the last subscribed dimension to resubscribe
200- pendingVideoLayoutRef . current = subscribedVideoLayoutRef . current ;
201- subscribedVideoLayoutRef . current = undefined ;
202- }
203- } , [ hasJoinedCall ] ) ;
204-
205- /**
206- * This effect updates the subscription either
207- * 1. when video tracks are published and was unpublished before
208- * 2. when the view's visibility changes
209- * 3. when call was rejoined
210- */
211- useEffect ( ( ) => {
212- if ( ! call || isLocalParticipant ) {
213- return ;
214- }
215- // NOTE: We only want to update the subscription if the pendingVideoLayoutRef is set
216- const updateIsNeeded = pendingVideoLayoutRef . current ;
217-
218- if ( ! updateIsNeeded || ! isPublishingVideoTrack || ! hasJoinedCall ) {
219- return ;
220- }
221-
222- // NOTE: When the view is not visible, we want to subscribe to audio only.
223- // We unsubscribe their video by setting the dimension to undefined
224- const dimension = isVisible ? pendingVideoLayoutRef . current : undefined ;
225- call . state . updateParticipantTracks ( trackType , {
226- [ sessionId ] : { dimension } ,
227- } ) ;
228- call . dynascaleManager . applyTrackSubscriptions ( ) ;
229-
230- if ( dimension ) {
231- subscribedVideoLayoutRef . current = pendingVideoLayoutRef . current ;
232- pendingVideoLayoutRef . current = undefined ;
233- }
234- } , [
235- call ,
236- isPublishingVideoTrack ,
237- trackType ,
238- isVisible ,
239- sessionId ,
240- hasJoinedCall ,
241- isLocalParticipant ,
242- ] ) ;
243-
244- useEffect ( ( ) => {
245- return ( ) => {
246- subscribedVideoLayoutRef . current = undefined ;
247- pendingVideoLayoutRef . current = undefined ;
248- } ;
249- } , [ trackType , sessionId ] ) ;
250-
251183 const onLayout : React . ComponentProps < typeof RTCView > [ 'onLayout' ] = (
252184 event ,
253185 ) => {
254- if ( ! call || isLocalParticipant ) {
255- return ;
256- }
257- const dimension = {
258- width : Math . trunc ( event . nativeEvent . layout . width ) ,
259- height : Math . trunc ( event . nativeEvent . layout . height ) ,
260- } ;
261-
262- // NOTE: If the participant hasn't published a video track yet,
263- // or the view is not viewable, we store the dimensions and handle it
264- // when the track is published or the video is enabled.
265- if ( ! isPublishingVideoTrack || ! isVisible || ! hasJoinedCall ) {
266- pendingVideoLayoutRef . current = dimension ;
267- return ;
268- }
269-
270- // NOTE: We don't want to update the subscription if the dimension hasn't changed
271- if (
272- subscribedVideoLayoutRef . current ?. width === dimension . width &&
273- subscribedVideoLayoutRef . current ?. height === dimension . height
274- ) {
275- return ;
276- }
277- call . state . updateParticipantTracks ( trackType , {
278- [ sessionId ] : {
279- dimension,
280- } ,
281- } ) ;
282- call . dynascaleManager . applyTrackSubscriptions ( ) ;
283- subscribedVideoLayoutRef . current = dimension ;
284- pendingVideoLayoutRef . current = undefined ;
186+ trackSubscriberRef . current ?. onLayoutUpdate ( event ) ;
285187 } ;
286188
287189 return (
288190 < View
289191 onLayout = { onLayout }
290192 style = { [ styles . container , videoRenderer . container ] }
291193 >
194+ { call && ! isLocalParticipant && (
195+ < TrackSubscriber
196+ ref = { trackSubscriberRef }
197+ call = { call }
198+ participantSessionId = { sessionId }
199+ trackType = { trackType }
200+ isVisible = { isVisible }
201+ />
202+ ) }
292203 { canShowVideo &&
293204 videoStreamToRender &&
294205 ( objectFit || isVideoDimensionsValid ) ? (
0 commit comments