question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Video Streams show Black Screen sometimes (Android)

See original GitHub issue

Bug report

  • Reproduced on:
  • iOS

Description

This only happens sometimes, and I can’t find how to make it consistent. But it is a critical bug for myself. When call comes on Android device, sometimes both Remote and Local Stream show Black screen and I can’t see any live video feed.

I use React Native CallKeep for handling calls.

Steps to Reproduce

  • Call Android Device
  • Display Incoming Call UI
  • Accept Call and App is sent back to foreground
  • See buttons and black screen instead of video stream (IE RTCView is displaying Black Video screen)

Versions

- ConnectyCube: Latest
- CallKeep: Latest
- React Native: 0.63.2
- Phone model: Android 7 Samsung S6

Code

const CallControl = ({ route }: { route: any }) => {
  const initSession = {
    ID: '',
    initiatorID: 0,
    currentUserID: 0,
    opponentsIDs: [],
    getUserMedia: () => {},
    accept: () => {},
    call: () => {},
    stop: () => {},
    mute: () => {},
    unmute: () => {},
    reject: () => {},
  };
  const initExtension = { busy: false };

  const sess = useRef<Session>(initSession);
  const ext = useRef<Extension>(initExtension);
  const callId = useRef<string>('');
  const currentUser = route?.params?.currentUser || {};

  const [localStream, setLocalStreams] = useState<any>([]);
  const [remoteStreams, setRemoteStreams] = useStateCallback<
    [] | Array<Stream>
  >([]);

  const [selectedUserId, setSelectedUser] = useState<null | number>(null);

  const [isAudioMuted, setIsAudioMuted] = useState(false);
  const [isFrontCamera, setIsFrontCamera] = useState(true);

  const cam = true;

  const reset = async () => {
    const id = oneTimeId();
    RNCallKeep.endCall(id);
    await ConnectyCube.videochat.clearSession(sess.current.ID);
    RNCallKeep.endAllCalls();

    InCallManager.stop();
    setRemoteStreams([]);
    setLocalStreams([]);
    setSelectedUser(null);
    setIsAudioMuted(false);
    setIsFrontCamera(true);
    sess.current = initSession;
    ext.current = initExtension;
    callId.current = '';
  };

  const oneTimeId = () => {
    callId.current = callId.current || uuid();
    return callId.current;
  };

  // Start/Stop onPress call handlers
  const startCall = async () => {
    if (!selectedUserId) {
      return;
    }

    const t = ConnectyCube.videochat.CallType.VIDEO; // AUDIO is also possible
    sess.current = ConnectyCube.videochat.createNewSession([selectedUserId], t);

    const stream = await sess.current?.getUserMedia?.(MEDIA_OPTIONS);
    await sess.current?.call({});

    setLocalStreams([{ userId: 'localStream', stream }]);
    setRemoteStreams([{ userId: selectedUserId, stream: null }]);

    if (isIOS) {
      const id = oneTimeId();
      const name = users.find(u => u.id === selectedUserId)?.name || '';
      RNCallKeep.startCall(id, name, name, 'generic', cam);
    }
  };

  const stopCall = async () => {
    await sess.current?.stop?.({});
    await reset();
  };

  // CallKeep button actions
  const acceptCall = async () => {
    const id = oneTimeId();
    RNCallKeep.setCurrentCallActive(id);

    const stream = await sess.current?.getUserMedia?.(MEDIA_OPTIONS);
    await sess.current?.accept?.({});

    setLocalStreams([{ userId: 'localStream', stream }]);
    setRemoteStreams([{ userId: selectedUserId, stream: null }]);
  };

  const rejectCall = async () => {
    await sess.current?.reject?.(ext.current);
    await reset();
  };

  // ConnectyCall Listeners
  const onCallListener: Listener = async (session, _userId, extension) => {
    ext.current = extension || initExtension;
    sess.current = session || initSession;

    const name =
      users.find(u => u.id === sess?.current?.initiatorID)?.name || '';
    const id = oneTimeId();
    RNCallKeep.displayIncomingCall(id, name, name, 'generic', cam);
  };

  const onAcceptCallListener: Listener = async (session, _, extension) => {
    ext.current = extension || initExtension;
    sess.current = session || initSession;
  };

  const onRemoteStreamListener = async (
    _session: Session,
    userId: number,
    stream: Stream
  ) =>
    setRemoteStreams([...remoteStreams, { userId, stream }], () => {
      InCallManager.start({ media: 'video', auto: false });
      InCallManager.setSpeakerphoneOn(true);
      RNCallKeep.backToForeground();
    });

  const onRejectCallListener = async () => {
    await reset();
  };

  const onStopCallListener = async () => {
    await reset();
  };

  const onUserNotAnswerListener = async () => {
    if (!remoteStreams[0]) {
      await reset();
    }
  };

  // Init
  const initializeConnectyCube = () => {
    ConnectyCube.videochat.onCallListener = onCallListener;
    ConnectyCube.videochat.onAcceptCallListener = onAcceptCallListener;
    ConnectyCube.videochat.onRemoteStreamListener = onRemoteStreamListener;
    ConnectyCube.videochat.onRejectCallListener = onRejectCallListener;
    ConnectyCube.videochat.onStopCallListener = onStopCallListener;
    ConnectyCube.videochat.onUserNotAnswerListener = onUserNotAnswerListener;
  };

  const initializeCallKeep = async () => {
    await RNCallKeep.setup(SETUP_OPTIONS);
    RNCallKeep.setAvailable(true);
    RNCallKeep.addEventListener('answerCall', acceptCall);
    RNCallKeep.addEventListener('endCall', rejectCall);
  };

  const cleanup = () => {
    RNCallKeep.removeEventListener('answerCall');
    RNCallKeep.removeEventListener('endCall');
    stopCall();
  };

  useEffect(() => {
    initializeConnectyCube();
    initializeCallKeep();
    return cleanup;
  }, []);

  // Actions
  const toggleMute = () => {
    const mute = !isAudioMuted;
    setIsAudioMuted(mute);
    mute ? sess.current?.mute?.('audio') : sess.current?.unmute?.('audio');
  };

  const toggleCamera = () => {
    setIsFrontCamera(!isFrontCamera);
    localStream[0]?.stream
      ?.getVideoTracks?.()
      .forEach((track: Track) => track._switchCamera());
  };

  // Buttons
  const renderCallStartStopButton = () => (
    <TouchableOpacity
      style={[
        styles.buttonContainer,
        remoteStreams[0] ? styles.buttonCallEnd : styles.buttonCall,
      ]}
      onPress={remoteStreams[0] ? stopCall : startCall}>
      <MaterialIcon
        name={remoteStreams[0] ? 'call-end' : 'call'}
        size={32}
        color='white'
      />
    </TouchableOpacity>
  );

  const renderMuteButton = () => (
    <TouchableOpacity
      style={[styles.buttonContainer, styles.buttonMute]}
      onPress={toggleMute}>
      <MaterialIcon
        name={isAudioMuted ? 'mic-off' : 'mic'}
        size={32}
        color='white'
      />
    </TouchableOpacity>
  );

  const renderSwitchVideoSourceButton = () => (
    <TouchableOpacity
      style={[styles.buttonContainer, styles.buttonSwitch]}
      onPress={toggleCamera}>
      <MaterialIcon
        name={isFrontCamera ? 'camera-rear' : 'camera-front'}
        size={32}
        color='white'
      />
    </TouchableOpacity>
  );

  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: 'black' }}>
      <StatusBar backgroundColor='black' barStyle='light-content' />
      {remoteStreams[0] ? (
        <View style={styles.inColumn}>
          <VideoScreen
            mirror={false}
            stream={remoteStreams?.[0]?.stream}
            userId={remoteStreams?.[0]?.userId}
          />
          <VideoScreen
            mirror={isFrontCamera}
            stream={localStream?.[0]?.stream}
            userId={localStream?.[0]?.userId}
          />
        </View>
      ) : (
        <UsersSelect
          currentUser={currentUser}
          selectedUserId={selectedUserId}
          setSelectedUser={setSelectedUser}
        />
      )}
      <SafeAreaView style={styles.container}>
        <View style={styles.toolBarItem}>
          {remoteStreams[0] && renderMuteButton()}
        </View>
        {(selectedUserId || remoteStreams[0]) && (
          <View style={styles.toolBarItem}>{renderCallStartStopButton()}</View>
        )}
        <View style={styles.toolBarItem}>
          {remoteStreams[0] && localStream && renderSwitchVideoSourceButton()}
        </View>
      </SafeAreaView>
    </SafeAreaView>
  );
};

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:33 (12 by maintainers)

github_iconTop GitHub Comments

1reaction
PRRugebregtcommented, Feb 3, 2021

Hi @ccvlad,

Thanks for the reply. I’m sorry I didn’t see this was for android specifically.

However I managed to get it working eventually. For me the key was to configure and initialize the audiosession of the videosession. When I didn’t set this specifically, I ended up with the occasional black screen for one user… When I set the audiosession and configured it for the videochat, everything just runs smoothly! I don’t know if this is any help, but just thought I’d put it out here. Maybe it’s not the video itself that it causing the problem, but setting the correct audio device for the localstream. Good luck in solving this problem! And thanks again for the reply.

0reactions
ccvladcommented, Jan 25, 2022

Closed due to inactivity. Please create a new issue if needed.

Read more comments on GitHub >

github_iconTop Results From Across the Web

[SOLVED] Screen Goes Black When Watching Videos Android
If the screen goes black when watching videos Android”, possible causes include ad-blocking plugins, browser, network and app problems.
Read more >
Top 8 Ways to Fix YouTube Black Screen Issue on Android TV
Is the YouTube app showing a black screen on your Android TV? Check out these top 8 ways to get rid of the...
Read more >
Resolve Black Screen Issue When Play Videos
Sometimes, while watching a video on a media player or YouTube, you tend to have a black screen, but at the back, the...
Read more >
Black screen when returning to video playback activity in ...
A black screen is shown instead of the video. Does anyone have a workaround or solution for this issue.
Read more >
How can I fix black screen issues? - Agora Documentation
Check the camera hardware. Start the built-in video camera to test the recording function. Check if the camera access permission is enabled. Both...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found