philnash/twilio-video-react-hooks

Problem black screen when reconnecting

Closed this issue · 13 comments

Hello all !

I'am using this stack for my project, and i have some random error when a user is reconnecting on group room.

In fact, i have 3 users :

  • Tianna
  • Kellen
  • Me

When Tianna is reloading her page, we have a black square but for other users this black square does not appear.
So i don't know where is the problem ? Maybe the cause of the problem is the track ?

You can see more with the following image.

https://ibb.co/SKjY1HD

Regards.

Hi @Chateux, when users refresh the page, they should be disconnected from the room using this code which should mean they trigger participantDisconnected events in the browsers of the other participants.

Is there something up with that code in your application?

True I have implemented this piece of code, but it does not works....

Are you able to investigate whether it is being triggered properly? I haven't experienced an issue with it myself, so I need you to tell me as much as you can about this if I am to help.

Hello,

I have implemented the solution like this :

The file is Lobby.jsx :

  useEffect(() => {
    const participantConnected = participant => {
      setLoggin(false);
      setParticipants(prevParticipants => [...prevParticipants, participant]);
    };

    const participantDisconnected = participant => {
      setLoggin(true);
      setParticipants(prevParticipants =>
        prevParticipants.filter(p => p !== participant)
      );
    };
    setLoading(true);

    const VideoConnectCallbackSingle = {
      name: roomName,
      audio: true,
      video: isSmallScreen
        ? { height: 480, frameRate: 24, width: 640 }
        : { height: 720, frameRate: 24, width: 1280 },
      networkQuality: { local: 2, remote: 1 }
    };

    const VideoConnectCallbackGroups = {
      name: roomName,
      audio: true,
      debug: true,
      video: isSmallScreen
        ? { height: 480, frameRate: 24, width: 640 }
        : { height: 720, frameRate: 24, width: 1280 },
      bandwidthProfile: {
        video: {
          mode: "presentation",
          renderDimensions: {
            high: { height: 1080, width: 1920 },
            standard: { height: 720, width: 1280 },
            low: { height: 176, width: 144 }
          },
          maxTracks: isSmallScreen ? 5 : 10,
          maxSubscriptionBitrate: isSmallScreen ? 2500000 : 0
        }
      },
      maxAudioBitrate: 16000,
      preferredVideoCodecs: [{ codec: "VP8", simulcast: true }],
      networkQuality: { local: 1, remote: 1 }
    };

    Video.connect(
      token,
      appointment.appointment.type === "masterclass"
        ? VideoConnectCallbackGroups
        : VideoConnectCallbackSingle
    ).then(room => {
      setRoom(room);
      room.on("participantConnected", participantConnected);
      room.on("participantDisconnected", participantDisconnected);
      room.participants.forEach(participantConnected);
      setLoading(false);
      return () => {
        room.off("participantConnected", participantConnected);
        room.off("participantDisconnected", participantDisconnected);
      };
    });

    return () => {
      setRoom(currentRoom => {
        if (currentRoom && currentRoom.localParticipant.state === "connected") {
          currentRoom.localParticipant.tracks.forEach(trackPublication => {
            trackPublication.track.stop();
          });
          currentRoom.disconnect();
          return null;
        }
        return currentRoom;
      });
    };

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [token, roomName]);

  const displayCamera = () => {
    room.localParticipant.videoTracks.forEach(track => {
      if (track.track.isEnabled) {
        track.track.disable();
      } else {
        track.track.enable();
      }
      setCameraChecked(track.track.isEnabled);
    });
  };

  const displayAudio = () => {
    room.localParticipant.audioTracks.forEach(track => {
      if (track.track.isEnabled) {
        track.track.disable();
      } else {
        track.track.enable();
      }
      setAudioChecked(track.track.isEnabled);
    });
  };

  const zoomOnParticipant = participant => {
    if (room.localParticipant === participant) {
      setIsZoomParticipant(room.localParticipant);
    } else {
      setIsZoomParticipant(participant);
    }
  };
  const clickRoomParticipant = () => {
    setIsZoomParticipant(null);
  };

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const handleLogout = useCallback(() => {
    setRoom(prevRoom => {
      if (prevRoom) {
        prevRoom.localParticipant.tracks.forEach(trackPub => {
          trackPub.track.stop();
        });
        prevRoom.disconnect();
      }
      return null;
    });
    handleLogoutFinal();
  });

  // eslint-disable-next-line consistent-return
  useEffect(() => {
    if (room) {
      const tidyUp = event => {
        if (event.persisted) {
          return;
        }
        if (room) {
          room.disconnect();
          handleLogout();
        }
      };
      window.addEventListener("pagehide", tidyUp);
      window.addEventListener("beforeunload", tidyUp);
      return () => {
        window.removeEventListener("pagehide", tidyUp);
        window.removeEventListener("beforeunload", tidyUp);
      };
    }
  }, [room, handleLogout]);

isSmallScreen is a variable that contains the width of the page (For mobile)
setLogin is a useState to check if a user is connected
VideoConnectCallbackGroups is a variable contains group
VideoConnectCallbackSingle is a viable that contains pee-to-peer room
setIsZoomParticipant is for a user that i would to pin

Then, the code I put in place is inspired by this: https://github.com/philnash/twilio-video-react-hooks/tree/917b232264a7d457f969f43ea7c8a0857db04219

Regards.

Are you able to log whether tidyUp is getting called when your participant refreshes the page?

Also, why do you call setLoggin(false) for each participant that connects and setLoggin(true) for each participant that disconnects?

I will try to send you the log.

Because we have a waiting room, and if a new user is connecting it redirect to twilio.

I guess my question about the setLoggin function is that it happens for every new participant that joins the room and for every participant that leaves the call. Is it doing something that affects removing the participant when they disconnect? Because that could cause the black screen.

In fact, in the hook lobby, I have a waiting page that watches if a person has logged in or not.

If a person has arrived, the cameras will be displayed.

Like that :

if (
    login !== true &&
    room &&
    room.participants.size === 0
  ) {
    return (
      <StyledLobbyWait>
        <StyledWaitContainer>
          <Title type="title2Bold">Please wait your participant.</Title>
     )
}

and then i have a return return who contain participant

<Participant
       
                          key={room.localParticipant.sid}
                          participant={room.localParticipant}
                        />

Here the logs :

EVENT TIDY UP
beforeunload

bubbles: false

cancelBubble: false

cancelable: true

composed: false

currentTarget: null

defaultPrevented: false

eventPhase: 0

explicitOriginalTarget: HTMLDocument

isTrusted: true

originalTarget: HTMLDocument

returnValue: ""

srcElement: HTMLDocument

target: HTMLDocument

timeStamp: 36204

type: "beforeunload"

<get isTrusted()>: function isTrusted()

: BeforeUnloadEventPrototype { returnValue: Getter & Setter, … }
Lobby.jsx:247
ROOM IF 3 :
{…}

_events: Object { participantConnected: participantConnected(), participantDisconnected: participantDisconnected() }

_eventsCount: 2

_instanceId: 1

_log: Object { … }

_maxListeners: undefined

_options: Object { wsServer: "wss://global.vss.twilio.com/signaling", automaticSubscription: true, dominantSpeaker: false, … }

_participants: Map { PAde699680ea74467dae1e3312e5997d3c → {…}, PAa83c92dc3ecddb887baf0faea5f3611f → {…} }

_signaling: Object { _events: {…}, _eventsCount: 6, name: "e15ae334-5c01-498c-a20d-93c057416a5a", … }

dominantSpeaker:

isRecording:

localParticipant: Object { audioTracks: Map(0), dataTracks: Map(0), identity: Getter, … }

mediaRegion: "de1"

name: "e15ae334-5c01-498c-a20d-93c057416a5a"

participants: Map { PAde699680ea74467dae1e3312e5997d3c → {…}, PAa83c92dc3ecddb887baf0faea5f3611f → {…} }

sid: "RM729ce63356e1c0ab07d3e917e5bf8a49"

state:

<get dominantSpeaker()>: function get()

<get isRecording()>: function get()

<get state()>: function get()

: Object { … }

I am going to need a bit more context for where all those logs came from. Which participant are these for?

I found the solution, thank you very much!
In fact, it was the cameras that were badly disconnected that were displayed, you just had to add a condition.

Oh, ok! Is that something I should add back into this repo, or was it part of your implementation?

It's on my side haha 👍