我正在使用 React-Native,在我的项目中使用 WEBRTC 时我不断收到此错误,谁能告诉我哪里出错了
代码:
import React, {useEffect, useState, useCallback} from 'react';
import {View, StyleSheet, Alert, PermissionsAndroid} from 'react-native';
import {Text} from 'react-native-paper';
import {Button} from 'react-native-paper';
import AsyncStorage from '@react-native-community/async-storage';
import {TextInput} from 'react-native-paper';
import {useFocusEffect} from '@react-navigation/native';
import InCallManager from 'react-native-incall-manager';
import {
RTCPeerConnection,
RTCIceCandidate,
RTCSessionDescription,
RTCView,
MediaStream,
MediaStreamTrack,
mediaDevices,
registerGlobals,
} from 'react-native-webrtc';
async function requestCameraPermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Cool Photo App Camera Permission',
message:
'Cool Photo App needs access to your camera ' +
'so you can take awesome pictures.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('You can use the camera');
} else {
console.log('Camera permission denied');
}
} catch (err) {
console.warn(err);
}
}
export default function CallScreen({navigation, ...props}) {
requestCameraPermission();
let name;
let connectedUser;
const [userId, setUserId] = useState('');
const [socketActive, setSocketActive] = useState(false);
const [calling, setCalling] = useState(false);
// Video Scrs
const [localStream, setLocalStream] = useState({toURL: () => null});
const [remoteStream, setRemoteStream] = useState({toURL: () => null});
const [conn, setConn] = useState(new WebSocket('ws://localhost:8080'));
const [yourConn, setYourConn] = useState(
//change the config as you need
new RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302',
}, {
urls: 'stun:stun1.l.google.com:19302',
}, {
urls: 'stun:stun2.l.google.com:19302',
}
],
}),
);
const [offer, setOffer] = useState(null);
const [callToUsername, setCallToUsername] = useState(null);
useFocusEffect(
useCallback(() => {
AsyncStorage.getItem('userId').then(id => {
console.log(id);
if (id) {
setUserId(id);
} else {
setUserId('');
navigation.push('Login');
}
});
}, [userId]),
);
useEffect(() => {
navigation.setOptions({
title: 'Your ID - ' + userId,
headerRight: () => (
<Button mode="text" onPress={onLogout} style={{paddingRight: 10}}>
Logout
</Button>
),
});
}, [userId]);
/**
* Calling Stuff
*/
useEffect(() => {
if (socketActive && userId.length > 0) {
try {
InCallManager.start({media: 'audio'});
InCallManager.setForceSpeakerphoneOn(true);
InCallManager.setSpeakerphoneOn(true);
InCallManager.requestRecordPermission();
InCallManager.requestCameraPermission();
} catch (err) {
console.log('InApp Caller ---------------------->', err);
}
console.log(InCallManager);
send({
type: 'login',
name: userId,
});
}
}, [socketActive, userId]);
const onLogin = () => {};
useEffect(() => {
/**
*
* Sockets Signalling
*/
conn.onopen = () => {
console.log('Connected to the signaling server');
setSocketActive(true);
};
//when we got a message from a signaling server
conn.onmessage = msg => {
let data;
if (msg.data === 'Hello world') {
data = {};
} else {
data = JSON.parse(msg.data);
console.log('Data --------------------->', data);
switch (data.type) {
case 'login':
console.log('Login');
break;
//when somebody wants to call us
case 'offer':
handleOffer(data.offer, data.name);
console.log('Offer');
break;
case 'answer':
handleAnswer(data.answer);
console.log('Answer');
break;
//when a remote peer sends an ice candidate to us
case 'candidate':
handleCandidate(data.candidate);
console.log('Candidate');
break;
case 'leave':
handleLeave();
console.log('Leave');
break;
default:
break;
}
}
};
conn.onerror = function(err) {
console.log('Got error', err);
};
/**
* Socjket Signalling Ends
*/
let isFront = false;
mediaDevices.enumerateDevices().then(sourceInfos => {
let videoSourceId;
for (let i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if (
sourceInfo.kind == 'videoinput' &&
sourceInfo.facing == (isFront ? 'front' : 'environment')
) {
videoSourceId = sourceInfo.deviceId;
}
}
mediaDevices
.getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 500, // Provide your own width, height and frame rate here
minHeight: 300,
minFrameRate: 30,
},
facingMode: isFront ? 'user' : 'environment',
optional: videoSourceId ? [{sourceId: videoSourceId}] : [],
},
})
.then(stream => {
// Got stream!
setLocalStream(stream);
// setup stream listening
yourConn.addStream(stream);
})
.catch(error => {
// Log error
});
});
yourConn.onaddstream = event => {
console.log('On Add Stream', event);
setRemoteStream(event.stream);
};
// Setup ice handling
yourConn.onicecandidate = event => {
if (event.candidate) {
send({
type: 'candidate',
candidate: event.candidate,
});
}
};
}, []);
const send = message => {
//attach the other peer username to our messages
if (connectedUser) {
message.name = connectedUser;
console.log('Connected iser in end----------', message);
}
conn.send(JSON.stringify(message));
};
const onCall = () => {
setCalling(true);
connectedUser = callToUsername;
console.log('Caling to', callToUsername);
// create an offer
yourConn.createOffer().then(offer => {
yourConn.setLocalDescription(offer).then(() => {
console.log('Sending Ofer');
console.log(offer);
send({
type: 'offer',
offer: offer,
});
// Send pc.localDescription to peer
});
});
};
//when somebody sends us an offer
const handleOffer = async (offer, name) => {
console.log(name + ' is calling you.');
console.log('Accepting Call===========>', offer);
connectedUser = name;
try {
await yourConn.setRemoteDescription(new RTCSessionDescription(offer));
const answer = await yourConn.createAnswer();
await yourConn.setLocalDescription(answer);
send({
type: 'answer',
answer: answer,
});
} catch (err) {
console.log('Offerr Error', err);
}
};
//when we got an answer from a remote user
const handleAnswer = answer => {
yourConn.setRemoteDescription(new RTCSessionDescription(answer));
};
//when we got an ice candidate from a remote user
const handleCandidate = candidate => {
setCalling(false);
console.log('Candidate ----------------->', candidate);
yourConn.addIceCandidate(new RTCIceCandidate(candidate));
};
//hang up
const hangUp = () => {
send({
type: 'leave',
});
handleLeave();
};
const handleLeave = () => {
connectedUser = null;
setRemoteStream({toURL: () => null});
yourConn.close();
// yourConn.onicecandidate = null;
// yourConn.onaddstream = null;
};
const onLogout = () => {
// hangUp();
AsyncStorage.removeItem('userId').then(res => {
navigation.push('Login');
});
};
const acceptCall = async () => {
console.log('Accepting Call===========>', offer);
connectedUser = offer.name;
try {
await yourConn.setRemoteDescription(new RTCSessionDescription(offer));
const answer = await yourConn.createAnswer();
await yourConn.setLocalDescription(answer);
send({
type: 'answer',
answer: answer,
});
} catch (err) {
console.log('Offerr Error', err);
}
};
const rejectCall = async () => {
send({
type: 'leave',
});
``;
setOffer(null);
handleLeave();
};
/**
* Calling Stuff Ends
*/
return (
<View style={styles.root}>
<View style={styles.inputField}>
<TextInput
label="Enter Friends Id"
mode="outlined"
style={{marginBottom: 7}}
onChangeText={text => setCallToUsername(text)}
/>
<Button
mode="contained"
onPress={onCall}
loading={calling}
// style={styles.btn}
contentStyle={styles.btnContent}
disabled={!(socketActive && userId.length > 0)}>
Call
</Button>
</View>
<View style={styles.videoContainer}>
<View style={[styles.videos, styles.localVideos]}>
<Text>Your Video</Text>
<RTCView streamURL={localStream.toURL()} style={styles.localVideo} />
</View>
<View style={[styles.videos, styles.remoteVideos]}>
<Text>Friends Video</Text>
<RTCView
streamURL={remoteStream.toURL()}
style={styles.remoteVideo}
/>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
root: {
backgroundColor: '#fff',
flex: 1,
padding: 20,
},
inputField: {
marginBottom: 10,
flexDirection: 'column',
},
videoContainer: {
flex: 1,
minHeight: 450,
},
videos: {
width: '100%',
flex: 1,
position: 'relative',
overflow: 'hidden',
borderRadius: 6,
},
localVideos: {
height: 100,
marginBottom: 10,
},
remoteVideos: {
height: 400,
},
localVideo: {
backgroundColor: '#f2f2f2',
height: '100%',
width: '100%',
},
remoteVideo: {
backgroundColor: '#f2f2f2',
height: '100%',
width: '100%',
},
});
错误:
[Unhandled promise rejection: TypeError: null is not an object (evaluating '_InCallManager.checkCameraPermission')]
at node_modules\react-native-incall-manager\index.js:137:4 in InCallManager#checkCameraPermission
at node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
at node_modules\regenerator-runtime\runtime.js:293:29 in invoke
at node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
at node_modules\regenerator-runtime\runtime.js:154:27 in invoke
at node_modules\regenerator-runtime\runtime.js:189:16 in PromiseImpl$argument_0
at node_modules\react-native\node_modules\promise\setimmediate\core.js:45:6 in tryCallTwo
at node_modules\react-native\node_modules\promise\setimmediate\core.js:200:22 in doResolve
at node_modules\react-native\node_modules\promise\setimmediate\core.js:66:11 in Promise
at node_modules\regenerator-runtime\runtime.js:188:15 in callInvokeWithMethodAndArg
at node_modules\regenerator-runtime\runtime.js:211:38 in enqueue
at node_modules\regenerator-runtime\runtime.js:238:8 in exports.async
at [native code]:null in checkCameraPermission
at node_modules\react-native-incall-manager\index.js:22:8 in InCallManager#constructor
at http://192.168.1.102:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&minify=false:168267:34 in <unknown>
at node_modules\metro\src\lib\polyfills\require.js:321:11 in loadModuleImplementation
at http://192.168.1.102:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&minify=false:167304:68 in <unknown>
at node_modules\metro\src\lib\polyfills\require.js:321:11 in loadModuleImplementation
at http://192.168.1.102:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&minify=false:134166:54 in <unknown>
at node_modules\metro\src\lib\polyfills\require.js:321:11 in loadModuleImplementation
at node_modules\expo\AppEntry.js:3:0 in <global>
at node_modules\metro\src\lib\polyfills\require.js:321:11 in loadModuleImplementation
at node_modules\metro\src\lib\polyfills\require.js:201:44 in guardedLoadModule
at http://192.168.1.102:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&minify=false:170463:3 in global code
[Unhandled promise rejection: TypeError: null is not an object (evaluating '_InCallManager.checkRecordPermission')]
at node_modules\react-native-incall-manager\index.js:123:4 in InCallManager#checkRecordPermission
at node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
at node_modules\regenerator-runtime\runtime.js:293:29 in invoke
at node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
at node_modules\regenerator-runtime\runtime.js:154:27 in invoke
at node_modules\regenerator-runtime\runtime.js:189:16 in PromiseImpl$argument_0
at node_modules\react-native\node_modules\promise\setimmediate\core.js:45:6 in tryCallTwo
at node_modules\react-native\node_modules\promise\setimmediate\core.js:200:22 in doResolve
at node_modules\react-native\node_modules\promise\setimmediate\core.js:66:11 in Promise
at node_modules\regenerator-runtime\runtime.js:188:15 in callInvokeWithMethodAndArg
at node_modules\regenerator-runtime\runtime.js:211:38 in enqueue
at node_modules\regenerator-runtime\runtime.js:238:8 in exports.async
at [native code]:null in checkRecordPermission
at node_modules\react-native-incall-manager\index.js:21:13 in InCallManager#constructor
at http://192.168.1.102:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&minify=false:168267:34 in <unknown>
at node_modules\metro\src\lib\polyfills\require.js:321:11 in loadModuleImplementation
at http://192.168.1.102:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&minify=false:167304:68 in <unknown>
at node_modules\metro\src\lib\polyfills\require.js:321:11 in loadModuleImplementation
at http://192.168.1.102:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&minify=false:134166:54 in <unknown>
at node_modules\metro\src\lib\polyfills\require.js:321:11 in loadModuleImplementation
at node_modules\expo\AppEntry.js:3:0 in <global>
at node_modules\metro\src\lib\polyfills\require.js:321:11 in loadModuleImplementation
at node_modules\metro\src\lib\polyfills\require.js:201:44 in guardedLoadModule
at http://192.168.1.102:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&minify=false:170463:3 in global code
上面的错误是一个警告,但是一旦我输入登录值并点击按钮,我就会收到这个错误:
You can use the camera
Error [TypeError: null is not an object (evaluating 'WebRTCModule.peerConnectionInit')]
TypeError: null is not an object (evaluating 'WebRTCModule.peerConnectionInit')
This error is located at:
in CallScreen (at SceneView.tsx:122)
in StaticContainer
in StaticContainer (at SceneView.tsx:115)
in EnsureSingleNavigator (at SceneView.tsx:114)
in SceneView (at useDescriptors.tsx:153)
in RCTView (at View.js:34)
in View (at CardContainer.tsx:245)
in RCTView (at View.js:34)
in View (at CardContainer.tsx:244)
in RCTView (at View.js:34)
in View (at CardSheet.tsx:33)
in ForwardRef(CardSheet) (at Card.tsx:573)
in RCTView (at View.js:34)
in View (at createAnimatedComponent.js:165)
in AnimatedComponent (at createAnimatedComponent.js:215)
in ForwardRef(AnimatedComponentWrapper) (at Card.tsx:555)
in PanGestureHandler (at GestureHandlerNative.tsx:13)
in PanGestureHandler (at Card.tsx:549)
in RCTView (at View.js:34)
in View (at createAnimatedComponent.js:165)
in AnimatedComponent (at createAnimatedComponent.js:215)
in ForwardRef(AnimatedComponentWrapper) (at Card.tsx:544)
in RCTView (at View.js:34)
in View (at Card.tsx:538)
in Card (at CardContainer.tsx:206)
in CardContainer (at CardStack.tsx:620)
in RCTView (at View.js:34)
in View (at Screens.tsx:84)
in MaybeScreen (at CardStack.tsx:613)
in RCTView (at View.js:34)
in View (at Screens.tsx:54)
in MaybeScreenContainer (at CardStack.tsx:495)
in CardStack (at StackView.tsx:462)
in KeyboardManager (at StackView.tsx:458)
in RNCSafeAreaProvider (at SafeAreaContext.tsx:76)
in SafeAreaProvider (at SafeAreaProviderCompat.tsx:42)
in SafeAreaProviderCompat (at StackView.tsx:455)
in GestureHandlerRootView (at GestureHandlerRootView.android.tsx:26)
in GestureHandlerRootView (at StackView.tsx:454)
in StackView (at createStackNavigator.tsx:87)
in StackNavigator (at App.js:39)
in EnsureSingleNavigator (at BaseNavigationContainer.tsx:409)
in ForwardRef(BaseNavigationContainer) (at NavigationContainer.tsx:91)
in ThemeProvider (at NavigationContainer.tsx:90)
in ForwardRef(NavigationContainer) (at App.js:38)
in App (created by ExpoRoot)
in ExpoRoot (at renderApplication.js:45)
in RCTView (at View.js:34)
in View (at AppContainer.js:106)
in RCTView (at View.js:34)
in View (at AppContainer.js:132)
in AppContainer (at renderApplication.js:39)
at node_modules\react-
at [native code]:null in performSyncWorkOnRoot
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5321:31 in runWithPriority$argument_1
at node_modules\scheduler\cjs\scheduler.development.js:653:23 in unstable_runWithPriority
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5316:21 in flushSyncCallbackQueueImpl
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5304:28 in flushSyncCallbackQueue
at node_modules\react-
at [native code]:null in dispatchAction
at node_modules\@react-navigation\core\src\useSyncState.tsx:38:9 in React.useCallback$argument_0
at http://192.168.1.102:19000/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&hot=false&minify=false:138144:20 in <unknown>
at node_modules\@react-navigation\core\src\useNavigationHelpers.tsx:43:21 in dispatch
at node_modules\@react-navigation\core\src\useNavigationCache.tsx:77:12 in dispatch
at node_modules\@react-navigation\core\src\useNavigationCache.tsx:89:24 in acc.name
at screens\LoginScreen.js:19:6 in onLogin
at node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
at node_modules\regenerator-runtime\runtime.js:293:29 in invoke
at node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
at node_modules\regenerator-runtime\runtime.js:154:27 in invoke
at node_modules\regenerator-runtime\runtime.js:164:18 in PromiseImpl.resolve.then$argument_0
at node_modules\react-native\node_modules\promise\setimmediate\core.js:37:13 in tryCallOne
at node_modules\react-native\node_modules\promise\setimmediate\core.js:123:24 in setImmediate$argument_0
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:130:14 in _callTimer
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:181:14 in _callImmediatesPass
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:441:30 in callImmediates
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:387:6 in __callImmediates
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:135:6 in __guard$argument_0
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:364:10 in __guard
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:134:4 in flushedQueue
at [native code]:null in flushedQueue
at [native code]:null in invokeCallbackAndReturnFlushedQueue
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
at node_modules\react-native\Libraries\LogBox\LogBox.js:173:8 in registerError
at node_modules\react-native\Libraries\LogBox\LogBox.js:59:8 in errorImpl
at node_modules\react-native\Libraries\LogBox\LogBox.js:33:4 in console.error
at node_modules\expo\build\environment\react-native-logs.fx.js:27:4 in error
setImmediate$argument_0
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:130:14 in _callTimer
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:181:14 in _callImmediatesPass
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:441:30 in callImmediates
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:387:6 in __callImmediates
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:135:6 in __guard$argument_0
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:364:10 in __guard
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:134:4 in flushedQueue
at [native code]:null in flushedQueue
at [native code]:null in invokeCallbackAndReturnFlushedQueue
我已经完成了资源所说的一切,所以我添加了这个:
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import {View, StyleSheet, Alert, PermissionsAndroid} from 'react-native';
import LoginScreen from './screens/LoginScreen';
import CallScreen from './screens/CallScreen';
import {SafeAreaView} from 'react-native-safe-area-context';
const Stack = createStackNavigator();
const requestCameraPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: "Cool Photo App Camera Permission",
message:
"Cool Photo App needs access to your camera " +
"so you can take awesome pictures.",
buttonNeutral: "Ask Me Later",
buttonNegative: "Cancel",
buttonPositive: "OK"
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log("You can use the camera");
} else {
console.log("Camera permission denied");
}
} catch (err) {
console.warn(err);
}
};
const App = () => {
requestCameraPermission();
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{headerShown: false}}
/>
<Stack.Screen name="Call" component={CallScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
我关注的资源在这里:https://www.npmjs.com/package/react-native-incall-manager