1

I've looked through countless guides and articles about react-native-webrtc and, surprisingly, I was not able to find a solution to my problem. Everything else works just fine (audio calls and video calls), however for some reason, during video calls specifically, the audio is played back through the built-in earpiece, not the speaker.

I've found some other solutions for 3rd party libraries, such as setSpeakerPhoneOn or setForceSpeakerPhoneOn from react-native-incall-manager, but I don't want to install a whole separate library for just one feature. I've tried looking into their source code, but there was little that I could gather that would help me.

How can I toggle the speaker with only the react-native-webrtc library?

Here is how the caller's call is initiated:

const CallerVideoScreen = React.memo(() => {
    const room = useUnit($room);
    const iceCandidates = useUnit($iceCandidates) // empty array until target user gathers all candidates from their side;
    const turnCredentials = useUnit($turnCredentials) // generate on my own ICE server;

    const [localStream, setLocalStream] = React.useState(null);
    const [remoteStream, setRemoteStream] = React.useState(null);
    const [cachedLocalPC, setCachedLocalPC] = React.useState(null);

    React.useEffect(() => {
        setScreenHeaderComponent({
            payload: {
                screenHeaderConfig: {
                    component: null,
                    props: null,
                    previous: {
                        component: EScreenHeaderComponentName.ChatHeader,
                        props: {},
                    },
                },
            },
        });

        startLocalStream();
    }, []);

    React.useEffect(() => {
        if (localStream && room?.id && turnCredentials) {
            startCall();

            return () => {
                setLocalStream(null);
                setRemoteStream(null);
                setCachedLocalPC(null);
            };
        }
    }, [localStream, room?.id, turnCredentials]);

    const startLocalStream = async () => {
        const devices = await mediaDevices.enumerateDevices();

        const videoSourceId = devices.find(
            (device) => device.kind === 'videoinput' && device.facing === 'front',
        );

        if (videoSourceId) {
            const newStream = await mediaDevices.getUserMedia({
                audio: true,
                video: {
                    mandatory: {
                        minWidth: 500,
                        minHeight: 300,
                        minFrameRate: 30,
                    },
                    facingMode: 'user',
                    optional: videoSourceId ? [{ sourceId: videoSourceId }] : [],
                },
            });

            setLocalStream(newStream);
        }
    };

    const startCall = async () => {
        if (localStream) {
            const localPC = new RTCPeerConnection(
                iceServerConfig({ turnCredentials }),
            );

            localStream.getTracks().forEach((track) => {
                localPC.addTrack(track, localStream);
            });

            localPC.addEventListener('icecandidate', (e) => {
                if (!e.candidate) {
                    return;
                }

                // store candidate
                storeIceCandidate({ payload: e.candidate.toJSON() });
            });

            localPC.ontrack = (e) => {
                const newStream = new MediaStream();
                e.streams[0].getTracks().forEach((track) => {
                    newStream.addTrack(track);
                });

                setRemoteStream(newStream);
            };

            const offer = await localPC.createOffer({});
            await localPC.setLocalDescription(offer);

            // signal offer to target user
            updateCallRoomOffer({ payload: { offer } });

            setCachedLocalPC(localPC);
        }
    };

    React.useEffect(() => {
        if (cachedLocalPC && room?.answer) {
            if (!cachedLocalPC.currentRemoteDescription) {
                // intercept answer to offer and set description
                const rtcSessionDescription = new RTCSessionDescription(room.answer);
                cachedLocalPC.setRemoteDescription(rtcSessionDescription);
            } else {
                // log error
            }
        }
    }, [cachedLocalPC, room?.answer]);

    React.useEffect(() => {
        if (cachedLocalPC && iceCandidates.length) {
            iceCandidates.forEach(({ candidate, sdpMLineIndex, sdpMid }) => {
                cachedLocalPC.addIceCandidate(
                    new RTCIceCandidate({ candidate, sdpMLineIndex, sdpMid }),
                );
            });
        }
    }, [cachedLocalPC, iceCandidates]);

    return (
        <RTCView
            style={styles.rctView}
            streamURL={remoteStream?.toURL() ?? ''}
            objectFit={'cover'}
        />
    );
});

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.