GithubHelp home page GithubHelp logo

react-native-webrtc-web-shim's Introduction

React Native WebRTC

React-Native-WebRTC

npm version npm downloads Discourse topics

A WebRTC module for React Native.

Feature Overview

Android iOS tvOS macOS* Windows* Web* Expo*
Audio/Video ✔️ ✔️ ✔️ - - ✔️ ✔️
Data Channels ✔️ ✔️ - - - ✔️ ✔️
Screen Capture ✔️ ✔️ - - - ✔️ ✔️
Plan B - - - - - - -
Unified Plan* ✔️ ✔️ - - - ✔️ ✔️
Simulcast* ✔️ ✔️ - - - ✔️ ✔️

macOS - We don't currently actively support macOS at this time.
Support might return in the future.

Windows - We don't currently support the react-native-windows platform at this time.
Anyone interested in getting the ball rolling? We're open to contributions.

Web - The react-native-webrtc-web-shim project provides a shim for react-native-web support.
Which will allow you to use (almost) the exact same code in your react-native-web project as you would with react-native directly.

Expo - As this module includes native code it is not available in the Expo Go app by default.
However you can get things working via the expo-dev-client library and out-of-tree config-plugins/react-native-webrtc package.

Unified Plan - As of version 106.0.0 Unified Plan is the only supported mode.
Those still in need of Plan B will need to use an older release.

Simulcast - As of version 111.0.0 Simulcast is now possible with ease.
Software encode/decode factories have been enabled by default.

WebRTC Revision

  • Currently used revision: M118
  • Supported architectures
    • Android: armeabi-v7a, arm64-v8a, x86, x86_64
    • iOS: arm64, x86_64
    • tvOS: arm64
    • macOS: (temporarily disabled)

Getting Started

Use one of the following preferred package install methods to immediately get going.
Don't forget to follow platform guides below to cover any extra required steps.

npm: npm install react-native-webrtc --save
yarn: yarn add react-native-webrtc
pnpm: pnpm install react-native-webrtc

Guides

Example Projects

We have some very basic example projects included in the examples directory.
Don't worry, there are plans to include a much more broader example with backend included.

Community

Come join our Discourse Community if you want to discuss any React Native and WebRTC related topics.
Everyone is welcome and every little helps.

Related Projects

Looking for extra functionality coverage?
The react-native-webrtc organization provides a number of packages which are more than useful when developing Real Time Communication applications.

react-native-webrtc-web-shim's People

Contributors

8ballbombom avatar dependabot[bot] avatar ruddell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

react-native-webrtc-web-shim's Issues

got error while installation

Describe the bug

npm resolution error report

2023-09-05T06:34:09.724Z

While resolving: undefined@undefined
Found: [email protected]
node_modules/react
  peer react@"16.x" from [email protected]
  node_modules/react-native-webrtc-web-shim
    react-native-webrtc-web-shim@"*" from the root project

Could not resolve dependency:
peer react@"18.2.0" from [email protected]
node_modules/react-native
  peer react-native@"0.x" from [email protected]
  node_modules/react-native-webrtc-web-shim
    react-native-webrtc-web-shim@"*" from the root project

Fix the upstream dependency conflict, or retry
this command with --force, or --legacy-peer-deps
to accept an incorrect (and potentially broken) dependency resolution.

Raw JSON explanation object:

{
  "code": "ERESOLVE",
  "current": {
    "name": "react",
    "version": "16.14.0",
    "whileInstalling": {
      "path": "C:\\Users\\Jigar\\Desktop\\College Project"
    },
    "location": "node_modules/react",
    "isWorkspace": false,
    "dependents": [
      {
        "type": "peer",
        "name": "react",
        "spec": "16.x",
        "from": {
          "name": "react-native-webrtc-web-shim",
          "version": "1.0.3",
          "whileInstalling": {
            "path": "C:\\Users\\Jigar\\Desktop\\College Project"
          },
          "location": "node_modules/react-native-webrtc-web-shim",
          "isWorkspace": false,
          "dependents": [
            {
              "type": "prod",
              "name": "react-native-webrtc-web-shim",
              "spec": "*",
              "from": {
                "location": "C:\\Users\\Jigar\\Desktop\\College Project"
              }
            }
          ]
        }
      }
    ]
  },
  "currentEdge": null,
  "edge": {
    "type": "peer",
    "name": "react",
    "spec": "18.2.0",
    "error": "INVALID",
    "from": {
      "name": "react-native",
      "version": "0.72.4",
      "whileInstalling": {
        "path": "C:\\Users\\Jigar\\Desktop\\College Project"
      },
      "location": "node_modules/react-native",
      "isWorkspace": false,
      "dependents": [
        {
          "type": "peer",
          "name": "react-native",
          "spec": "0.x",
          "from": {
            "name": "react-native-webrtc-web-shim",
            "version": "1.0.3",
            "whileInstalling": {
              "path": "C:\\Users\\Jigar\\Desktop\\College Project"
            },
            "location": "node_modules/react-native-webrtc-web-shim",
            "isWorkspace": false,
            "dependents": [
              {
                "type": "prod",
                "name": "react-native-webrtc-web-shim",
                "spec": "*",
                "from": {
                  "location": "C:\\Users\\Jigar\\Desktop\\College Project"
                }
              }
            ]
          }
        }
      ]
    }
  },
  "strictPeerDeps": false,
  "force": false
}

Instructions To reproduce

This Issue with [email protected]

Versions (please complete the following information):

  • React Native Version:0.72.4
  • React Native Web Version:~0.19.6
  • React Native WebRTC Version:^111.0.3

SSR rendering in NextJS causes build error

Describe the bug

react-native-webrtc-web-shim uses window, which is undefined on Server. This is causing error in NextJS:

ReferenceError: window is not defined

In this minified code snipet

window.MediaStream.prototype.release=function(){this.getTracks().forEach(function(e){return e.stop()})}

Versions:

React Native Version: 0.73.2
React Native Web Version: 0.19.10
React Native WebRTC Version: 118.0.0

Data channel support

Investigate differences for data channels between React Native WebRTC and browser WebRTC.

Currently untested. Work as expected.

  • Add a KITE test

RTCView with a mute method

Hi @8BallBomBom ,

Is there a method to mute local stream for ONLY myself?

If I mute it as below, then the whole stream is muted and my audio is not sent to the other party.
If I don't mute myself, in my own video view I hear myself.

useEffect(() => {
	if (localStream && localStream.getAudioTracks().length > 0) {
		localStream.getAudioTracks()[0].enabled = false;
	}
}, [localStream]);

Hence I imagine if there can be a method for RTCView so that I can mute the voice only for myself.

...
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
	{
		Platform.OS === "web"
			? <RTCView key={`local-${callRenderValue}`} stream={localStream} style={{ flex: 1 }} />
			: <RTCView key={`local-${callRenderValue}`} streamURL={localStream ? localStream.toURL() : undefined} style={{ flex: 1 }} />
	}
</View>
<View style={{ flex: 1 }}>
	{
		Platform.OS === "web"
			? <RTCView key={`remote-${callRenderValue}`} stream={remoteStream ? (remoteStream.toURL ? remoteStream.toURL() : remoteStream) : undefined} style={{ flex: 1 }} />
			: <RTCView key={`remote-${callRenderValue}`} streamURL={remoteStream ? (remoteStream.toURL ? remoteStream.toURL() : remoteStream) : undefined} style={{ flex: 1 }} />
	}
</View>
<View style={{ flexDirection: "row", justifyContent: "space-around" }}>
	<Button title="End Call" onPress={endCall} />
</View>
...

The video is functioning correctly, but there is an issue with the audio.

Hi, I encountered an issue while creating a group video call. The video is functioning as expected, but unfortunately, there seems to be a problem with the audio. Below is the code I've implemented. Can you spot any mistakes?

export function MultipleWebRTCVoiceChat() {
  const navigation = useNavigation();
  let peerConnections = React.useRef<Record<string, RTCPeerConnection | null>>(
    {}
  ).current;
  const [localStream, setLocalStream, getLocalStream] =
    useAdvancedState<MediaStream | null>(null);
  const [remoteStreams, setRemoteStreams] = React.useState<
    Record<string, MediaStream>
  >({});
  const [socket, setSocket, getSocket] = useAdvancedState<any>(null);
  const mySocketId = React.useRef<string>("");

  const localStreamRef = React.useRef<MediaStream | null>(null);

  const [room, setRoom] = React.useState<string>("123");
  const [isMuted, setIsMuted] = React.useState<boolean>(false);
  const [isCamera, setIsCamera] = React.useState<boolean>(true);
  const [isConnected, setIsConnected] = React.useState<boolean>(false);
  const [isJoined, setIsJoined] = React.useState<boolean>(false);
  const [isConnectError, setIsConnectError] = React.useState<boolean>(false);

  const getPeerConnection = async (socketId: string) => {
    if (peerConnections[socketId]) {
      return peerConnections[socketId];
    }
    const localStream = getLocalStream();
    const configuration = {
      iceServers: [
        {
          urls: "stun:stun.l.google.com:19302",
        },
        {
          urls: "stun:stun1.l.google.com:19302",
        },
      ],
    };
    const pc = new RTCPeerConnection(configuration);

    // localStream?.getAudioTracks().forEach((track: any) => {
    //     pc.addTrack(track, localStream as any);
    // });

    // localStream?.getVideoTracks().forEach((track: any) => {
    //     pc.addTrack(track, localStream as any);
    // });

    localStream?.getTracks().forEach((track: any) => {
      pc.addTrack(track, localStream as any);
    });

    pc.onsignalingstatechange = (event: any) => {
      console.log("onsignalingstatechange", event);
    };

    pc.onaddstream = (event: any) => {
      console.log("onaddstream", event);
      setRemoteStreams((old) => ({
        ...old,
        [socketId]: event.stream,
      }));
    };

    pc.onicecandidate = (event: any) => {
      const socket = getSocket();
      if (event.candidate) {
        socket.emit("ice-candidate", {
          room: room,
          candidate: event.candidate,
        });
      }
    };

    pc.ontrack = (event: any) => {
      if (mySocketId.current !== socketId) {
        console.log("ontrack", mySocketId.current, socketId, event.track.kind);
      }
      setRemoteStreams((old) => ({
        ...old,
        [socketId]: event.streams[0],
      }));
    };

    peerConnections[socketId] = pc;

    return pc;
  };

  const init = async () => {
    localStreamRef.current = await mediaDevices.getUserMedia({
      audio: true,
      video: isCamera,
    });

    setLocalStream(localStreamRef.current as any);
    const socket = io(SOCKET_STREAM_URL, {
      transports: ["websocket"],
    });
    socket.on("connect_error", async (err: any) => {
      console.log("connect_error", err, err.message);
      setIsConnectError(true);
      setIsConnected(false);
      setIsJoined(false);
    });
    socket.on("connect", () => {
      setIsConnectError(false);
      setIsConnected(true);
      const socketId = socket.id;
      setSocket(socket);
      mySocketId.current = socketId;
      handleJoinRoom();
    });

    socket.on("offer", handleOffer);
    socket.on("answer", handleAnswer);
    socket.on("ice-candidate", handleIceCandidate);
    socket.on("user-connected", handleUserConnected);
    socket.on("user-disconnected", handleUserDisconnected);
  };

  const handleUserConnected = async (socketId: string) => {
    console.log("handleUserConnected", socketId);
    const socket = getSocket();
    const pc = await getPeerConnection(socketId);
    const offer = await pc.createOffer({
      offerToReceiveAudio: true,
      offerToReceiveVideo: true,
    });
    await pc.setLocalDescription(new RTCSessionDescription(offer));
    socket.emit("offer", {
      target: socketId,
      offer: offer,
    });
  };

  const handleUserDisconnected = (socketId: string) => {
    peerConnections[socketId]?.close();
    peerConnections[socketId] = null;
    setRemoteStreams((old) => {
      delete old[socketId];
      return old;
    });
  };

  const handleOffer = async (event: { source: string; offer: any }) => {
    const { source, offer } = event;
    console.log("handleOffer", event);
    const socket = getSocket();
    const pc = await getPeerConnection(source);
    await pc.setRemoteDescription(new RTCSessionDescription(offer));
    const answer = await pc.createAnswer();
    await pc.setLocalDescription(new RTCSessionDescription(answer));
    socket.emit("answer", {
      target: source,
      answer: answer,
    });
  };

  const handleAnswer = async (payload: { source: string; answer: any }) => {
    console.log("handleAnswer", payload);
    const { source, answer } = payload;
    const pc = await getPeerConnection(source);
    await pc.setRemoteDescription(new RTCSessionDescription(answer));
  };

  const handleIceCandidate = async (event: {
    source: string;
    candidate: any;
  }) => {
    console.log("handleIceCandidate", event);
    const { source, candidate } = event;
    const pc = await getPeerConnection(source);
    if (!pc) {
      return;
    }
    await pc.addIceCandidate(new RTCIceCandidate(candidate));
  };

  useEffect(() => {
    init();
  }, []);

  const handleJoinRoom = async () => {
    const socket = getSocket();
    socket.emit("join-room", room);
    setIsJoined(true);
  };

  const handleLeaveRoom = () => {
    const socket = getSocket();
    socket.emit("leave-room", room);
    setIsJoined(false);
  };

  const handleShareScreen = async () => {
    const stream = await mediaDevices.getDisplayMedia({
      audio: true,
      video: true,
    });
    setLocalStream(stream as any);
    stream.getTracks().forEach((track: any) => {
      peerConnections[room]?.addTrack(track, stream);
    });
  };

  const handleShareCamera = async () => {
    localStream?.getVideoTracks().forEach((track: any) => {
      track.enabled = true;
    });
  };

  const handleShareCameraOff = async () => {
    localStream?.getVideoTracks().forEach((track: any) => {
      track.enabled = false;
    });
  };

  const handleShareAudio = async () => {
    const stream = await mediaDevices.getUserMedia({
      audio: true,
      video: false,
    });
    setLocalStream(stream as any);
    stream.getTracks().forEach((track: any) => {
      peerConnections[room]?.addTrack(track, stream);
    });
  };

  const handleMute = () => {
    const localStream = getLocalStream();
    localStream?.getAudioTracks().forEach((track: any) => {
      track.enabled = !track.enabled;
    });
    setIsMuted(true);
  };

  const handleUnmute = () => {
    const localStream = getLocalStream();
    localStream?.getAudioTracks().forEach((track: any) => {
      track.enabled = !track.enabled;
    });
    setIsMuted(false);
  };

  useEffect(() => {
    if (isMuted) {
      handleMute();
    } else {
      handleUnmute();
    }
  }, [isMuted]);

  useEffect(() => {
    if (isCamera) {
      handleShareCamera();
    } else {
      handleShareCameraOff();
    }
  }, [isCamera]);

  console.log("remoteStreams", remoteStreams);

  if (isConnectError) {
    return (
      <View
        style={{
          backgroundColor: "transparent",
          flex: 1,
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        <Text
          style={{
            color: "white",
            fontSize: 20,
            textAlign: "center",
          }}
        >
          Cannot connect to video call
        </Text>
      </View>
    );
  }

  return (
    <View
      style={{
        backgroundColor: "transparent",
      }}
    >
      {isConnected ? (
        <View
          style={{
            backgroundColor: "transparent",
          }}
        >
          <View
            style={{
              flexDirection: "row",
              flexWrap: "wrap",
              backgroundColor: "transparent",
              padding: 16,
            }}
          >
            <StreamMedia
              isMuted={isMuted}
              fullName="You"
              isVideoOff={!isCamera}
              stream={localStream}
            />

            {Object.keys(remoteStreams).map((id) => (
              <StreamMedia stream={remoteStreams[id]} />
            ))}
          </View>
        </View>
      ) : (
        <View></View>
      )}

      <Row
        style={{
          marginLeft: 26,
        }}
      >
        <Col>
          <SwitchButton
            value={isMuted}
            onChange={setIsMuted}
            views={[
              {
                value: false,
                view: <Octicons name="unmute" size={14} color="gray" />,
              },
              {
                value: true,
                view: <Octicons name="mute" size={14} color="gray" />,
              },
            ]}
          />
        </Col>

        <Col
          style={{
            marginLeft: 6,
          }}
        >
          <SwitchButton
            value={isCamera}
            onChange={setIsCamera}
            views={[
              {
                value: false,
                view: <Feather name="video-off" size={14} color="gray" />,
              },
              {
                value: true,
                view: <Feather name="video" size={14} color="gray" />,
              },
            ]}
          />
        </Col>

        {isConnected && (
          <Col
            style={{
              marginLeft: 6,
            }}
          >
            <SwitchButton
              value={isJoined}
              onChange={!isJoined ? handleJoinRoom : handleLeaveRoom}
              views={[
                {
                  value: false,
                  view: <Text>Join</Text>,
                },
                {
                  value: true,
                  view: <Text>End Calling</Text>,
                },
              ]}
            />
          </Col>
        )}
      </Row>
    </View>
  );
}

install issue

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/react-native-webrtc
npm ERR! react-native-webrtc@"^111.0.6" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react-native-webrtc@"~1" from [email protected]
npm ERR! node_modules/react-native-webrtc-web-shim
npm ERR! react-native-webrtc-web-shim@"https://github.com/react-native-webrtc/react-native-webrtc-web-shim" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR!
npm ERR! For a full report see:
npm ERR! /home/bmatusiak/.npm/_logs/2023-11-01T01_01_24_996Z-eresolve-report.txt

npm ERR! A complete log of this run can be found in: /home/bmatusiak/.npm/_logs/2023-11-01T01_01_24_996Z-debug-0.log

crash at expo metro build

running: npx expo export -p web
as described on https://docs.expo.dev/distribution/publishing-websites/

╰─>$ npx expo export -p web
Static rendering is enabled. Learn more
Starting Metro Bundler
λ Bundled 6747ms (node_modules/expo-router/node/render.js)
Web Bundled 6871ms (node_modules/expo-router/entry.js)

Metro error: window is not defined

1 | import RTCView from './RTCView';
2 |

3 | window.MediaStream.prototype.release = function release() {
| ^
4 | this.getTracks().forEach((track) => track.stop());
5 | };
6 |

Call Stack
factory (node_modules/react-native-webrtc-web-shim/src/react-native-webrtc-web-shim.web.js:3)
loadModuleImplementation (node_modules/metro-runtime/src/polyfills/require.js:342:5)
guardedLoadModule (node_modules/metro-runtime/src/polyfills/require.js:240:12)
r (node_modules/metro-runtime/src/polyfills/require.js:127:7)
factory (node_modules/react-native-webrtc-web-shim/index.js:1)
loadModuleImplementation (node_modules/metro-runtime/src/polyfills/require.js:342:5)
guardedLoadModule (node_modules/metro-runtime/src/polyfills/require.js:240:12)
r (node_modules/metro-runtime/src/polyfills/require.js:127:7)
factory (app/calls/[id].js:8)
h (node_modules/metro-runtime/src/polyfills/require.js:342:5)

npm registry and ts update

@ruddell Any chance you could change the npm package here to be this package rather than one you have forked?
But also can you give someone from the WebRTC org access on npm to publish releases?

I'll be looking to make several releases to get things working again and converted over to TypeScript.
Not entirely sure about the testing workflow but will look at the possibility of replacing/updating it.

Props are not handled as expected

Describe the bug
If you use mirror prop, then you would get:

Warning: Received `true` for a non-boolean attribute `mirror`.

If you want to write it to the DOM, pass a string instead: mirror="true" or mirror={value.toString()}.
    at video
    at Video
    at RTCView (webpack://mobile/./node_modules/react-native-webrtc-web-shim/src/RTCView.web.js?:1:1135)

Versions (please complete the following information):

  • React Native Version: 0.73.2
  • React Native Web Version: 0.19.10
  • React Native WebRTC Version: 118.0.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.