diff --git a/app/products/calls/connection.ts b/app/products/calls/connection.ts index 94d3dfcf1..1c70e3900 100644 --- a/app/products/calls/connection.ts +++ b/app/products/calls/connection.ts @@ -21,7 +21,7 @@ export let client: any = null; const websocketConnectTimeout = 3000; export async function newClient(channelID: string, closeCb: () => void, setScreenShareURL: (url: string) => void) { - let peer: any = null; + let peer: Peer | null = null; let stream: MediaStream; let voiceTrackAdded = false; let voiceTrack: MediaStreamTrack | null = null; @@ -50,13 +50,15 @@ export async function newClient(channelID: string, closeCb: () => void, setScree streams.forEach((s) => { s.getTracks().forEach((track: MediaStreamTrack) => { track.stop(); + track.release(); }); }); - if (peer) { - peer.destroy(); - } - InCallManager.stop(); + peer?.destroy(undefined, undefined, () => { + // Wait until the peer connection is closed, which avoids the following racy error that can cause problems with accessing the audio system in the future: + // AVAudioSession_iOS.mm:1243 Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session. + InCallManager.stop(); + }); if (closeCb) { closeCb(); @@ -67,7 +69,7 @@ export async function newClient(channelID: string, closeCb: () => void, setScree if (!peer) { return; } - if (voiceTrackAdded) { + if (voiceTrackAdded && voiceTrack) { peer.replaceTrack(voiceTrack, null, stream); } if (voiceTrack) { @@ -160,7 +162,7 @@ export async function newClient(channelID: string, closeCb: () => void, setScree ws.on('message', ({data}) => { const msg = JSON.parse(data); if (msg.type === 'answer' || msg.type === 'offer') { - peer.signal(data); + peer?.signal(data); } }); diff --git a/app/products/calls/simple-peer.ts b/app/products/calls/simple-peer.ts index 42b97641e..b1e5a07ce 100644 --- a/app/products/calls/simple-peer.ts +++ b/app/products/calls/simple-peer.ts @@ -421,7 +421,12 @@ export default class Peer extends stream.Duplex { this.isNegotiating = true; } - _destroy(err: Error | null, cb: (error: Error | null) => void) { + destroy(err?: Error, cb?: (error: Error | null) => void, cbPCClose?: () => void): this { + this._destroy(err, cb, cbPCClose); + return this; + } + + _destroy(err?: Error | null, cb?: (error: Error | null) => void, cbPcClose?: () => void) { if (this.destroyed || this.destroying) { return; } @@ -468,14 +473,14 @@ export default class Peer extends stream.Duplex { } catch (err) {} // eslint-disable-line // allow events concurrent with destruction to be handled - this.channel.onmessage = null; - this.channel.onopen = null; - this.channel.onclose = null; - this.channel.onerror = null; + this.channel.onmessage = undefined; + this.channel.onopen = undefined; + this.channel.onclose = undefined; + this.channel.onerror = undefined; } if (this.pc) { try { - this.pc.close(); + this.pc.close(cbPcClose); } catch (err) {} // eslint-disable-line // allow events concurrent with destruction to be handled @@ -491,7 +496,7 @@ export default class Peer extends stream.Duplex { this.emit('error', err); } this.emit('close'); - cb(null); + cb?.(null); }, 0); } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 54397f90b..62d40f085 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -348,7 +348,7 @@ PODS: - ReactNativeExceptionHandler (2.10.10): - React-Core - ReactNativeIncallManager (3.3.0): - - React + - React-Core - ReactNativeKeyboardTrackingView (5.7.0): - React - ReactNativeNavigation (7.25.1): @@ -781,7 +781,7 @@ SPEC CHECKSUMS: React-runtimeexecutor: 2450b43df7ffe8e805a0b3dcb2abd4282f1f1836 ReactCommon: d98c6c96b567f9b3a15f9fd4cc302c1eda8e3cf2 ReactNativeExceptionHandler: b11ff67c78802b2f62eed0e10e75cb1ef7947c60 - ReactNativeIncallManager: a840ef75640518065742a893e6c6ff98ee820039 + ReactNativeIncallManager: 642c22630caadff0a0619413aff4a9da08d63df9 ReactNativeKeyboardTrackingView: 02137fac3b2ebd330d74fa54ead48b14750a2306 ReactNativeNavigation: 6e747bdf88f138088a105285274170b3fc0404ed rn-fetch-blob: 17961aec08caae68bb8fc0e5b40f93b3acfa6932 diff --git a/package-lock.json b/package-lock.json index 5c8321e44..5b79d1e1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,7 +57,7 @@ "react-native-haptic-feedback": "1.13.0", "react-native-hw-keyboard-event": "0.0.4", "react-native-image-picker": "4.7.3", - "react-native-incall-manager": "3.3.0", + "react-native-incall-manager": "github:cpoile/react-native-incall-manager", "react-native-keyboard-aware-scrollview": "2.1.0", "react-native-keyboard-tracking-view": "5.7.0", "react-native-keychain": "8.0.0", @@ -81,7 +81,7 @@ "react-native-svg": "12.1.1", "react-native-vector-icons": "9.0.0", "react-native-video": "5.2.0", - "react-native-webrtc": "github:streamer45/react-native-webrtc", + "react-native-webrtc": "github:mattermost/react-native-webrtc", "react-native-webview": "11.17.1", "react-native-youtube": "2.0.2", "react-redux": "7.2.6", @@ -19400,8 +19400,8 @@ }, "node_modules/react-native-incall-manager": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/react-native-incall-manager/-/react-native-incall-manager-3.3.0.tgz", - "integrity": "sha512-SvomgHUoKqVso/BGv02b4ndBqenlqYqW3pptZz5qHwteidKFNWI6ny+PPw5X55MdK0lyTbzBoiT3DwS06Qu0lg==", + "resolved": "git+ssh://git@github.com/cpoile/react-native-incall-manager.git#8c55b9dac0a2ab25d651fb54b504d384f9989b36", + "license": "ISC", "peerDependencies": { "react-native": ">=0.40.0" } @@ -19746,7 +19746,7 @@ }, "node_modules/react-native-webrtc": { "version": "1.75.3", - "resolved": "git+ssh://git@github.com/streamer45/react-native-webrtc.git#7fe7d434892e6b29c5a6086d30b10f482db1e592", + "resolved": "git+ssh://git@github.com/mattermost/react-native-webrtc.git#7f765758f2f67e467ebd224c1c4d00a475e400d3", "dependencies": { "base64-js": "^1.1.2", "event-target-shim": "^1.0.5", @@ -38798,9 +38798,8 @@ "requires": {} }, "react-native-incall-manager": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/react-native-incall-manager/-/react-native-incall-manager-3.3.0.tgz", - "integrity": "sha512-SvomgHUoKqVso/BGv02b4ndBqenlqYqW3pptZz5qHwteidKFNWI6ny+PPw5X55MdK0lyTbzBoiT3DwS06Qu0lg==", + "version": "git+ssh://git@github.com/cpoile/react-native-incall-manager.git#8c55b9dac0a2ab25d651fb54b504d384f9989b36", + "from": "react-native-incall-manager@github:cpoile/react-native-incall-manager", "requires": {} }, "react-native-keyboard-aware-scrollview": { @@ -39060,8 +39059,8 @@ } }, "react-native-webrtc": { - "version": "git+ssh://git@github.com/streamer45/react-native-webrtc.git#7fe7d434892e6b29c5a6086d30b10f482db1e592", - "from": "react-native-webrtc@github:streamer45/react-native-webrtc", + "version": "git+ssh://git@github.com/mattermost/react-native-webrtc.git#7f765758f2f67e467ebd224c1c4d00a475e400d3", + "from": "react-native-webrtc@github:mattermost/react-native-webrtc", "requires": { "base64-js": "^1.1.2", "event-target-shim": "^1.0.5", diff --git a/package.json b/package.json index 53baa1426..74a250b5d 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "react-native-haptic-feedback": "1.13.0", "react-native-hw-keyboard-event": "0.0.4", "react-native-image-picker": "4.7.3", - "react-native-incall-manager": "3.3.0", + "react-native-incall-manager": "github:cpoile/react-native-incall-manager", "react-native-keyboard-aware-scrollview": "2.1.0", "react-native-keyboard-tracking-view": "5.7.0", "react-native-keychain": "8.0.0", @@ -79,7 +79,7 @@ "react-native-svg": "12.1.1", "react-native-vector-icons": "9.0.0", "react-native-video": "5.2.0", - "react-native-webrtc": "github:streamer45/react-native-webrtc", + "react-native-webrtc": "github:mattermost/react-native-webrtc", "react-native-webview": "11.17.1", "react-native-youtube": "2.0.2", "react-redux": "7.2.6", diff --git a/types/modules/react-native-webrtc.d.ts b/types/modules/react-native-webrtc.d.ts index 60f3314e5..1e0f68827 100644 --- a/types/modules/react-native-webrtc.d.ts +++ b/types/modules/react-native-webrtc.d.ts @@ -50,11 +50,17 @@ declare module 'react-native-webrtc' { constructor(); stop(): void; + applyConstraints(): void; + clone(): void; + getCapabilities(): void; + getConstraints(): void; + getSettings(): void; + release(): void; private _switchCamera(): void; @@ -74,13 +80,21 @@ declare module 'react-native-webrtc' { constructor(arg: any); addTrack(track: MediaStreamTrack): void; + removeTrack(track: MediaStreamTrack): void; + getTracks(): MediaStreamTrack[]; + getTrackById(trackId: string): MediaStreamTrack | undefined; + getAudioTracks(): MediaStreamTrack[]; + getVideoTracks(): MediaStreamTrack[]; + clone(): void; + toURL(): string; + release(): void; } @@ -92,23 +106,27 @@ declare module 'react-native-webrtc' { bufferedAmountLowThreshold: number; id: number; label: string; - maxPacketLifeTime: ?number; - maxRetransmits: ?number; + maxPacketLifeTime?: number; + maxRetransmits?: number; negotiated: boolean; ordered: boolean; protocol: string; readyState: 'connecting' | 'open' | 'closing' | 'closed'; - onopen: ?Function; - onmessage: ?Function; - onbufferedamountlow: ?Function; - onerror: ?Function; - onclose: ?Function; + onopen?: Function; + onmessage?: Function; + onbufferedamountlow?: Function; + onerror?: Function; + onclose?: Function; constructor(peerConnectionId: number, label: string, dataChannelDict: RTCDataChannelInit) + send(data: string | ArrayBuffer | ArrayBufferView): void + close(): void + _unregisterEvents(): void + _registerEvents(): void } @@ -116,6 +134,7 @@ declare module 'react-native-webrtc' { type: string; data: string | ArrayBuffer | Blob; origin: string; + constructor(type: any, eventInitDict: any) } @@ -129,6 +148,33 @@ declare module 'react-native-webrtc' { track?: MediaStreamTrack; } + export interface EventOnConnectionStateChange { + target: { + iceConnectionState: RTCIceConnectionState; + }; + } + + export interface ConfigurationParam { + username?: string | undefined; + credential?: string | undefined; + } + + export interface ConfigurationParamWithUrls extends ConfigurationParam { + urls: string[]; + } + + export interface ConfigurationParamWithUrl extends ConfigurationParam { + url: string; + } + + export interface RTCPeerConnectionConfiguration { + iceServers: ConfigurationParamWithUrls[] | ConfigurationParamWithUrl[]; + iceTransportPolicy?: 'all' | 'relay' | 'nohost' | 'none' | undefined; + bundlePolicy?: 'balanced' | 'max-compat' | 'max-bundle' | undefined; + rtcpMuxPolicy?: 'negotiate' | 'require' | undefined; + iceCandidatePoolSize?: number | undefined; + } + export class RTCPeerConnection { localDescription: RTCSessionDescriptionType; remoteDescription: RTCSessionDescriptionType; @@ -164,11 +210,11 @@ declare module 'react-native-webrtc' { addTrack(track: MediaStreamTrack): void; - addTransceiver(kind: 'audio'|'video'|MediaStreamTrack, init: any): void; + addTransceiver(kind: 'audio' | 'video' | MediaStreamTrack, init: any): void; removeStream(stream: MediaStream): void; - removeTrack(sender: RtpSender): Promise + removeTrack(sender: RTCRtpSender): Promise createOffer(options?: RTCOfferOptions): Promise; @@ -188,7 +234,7 @@ declare module 'react-native-webrtc' { getRemoteStreams(): MediaStream[]; - close(): void; + close(cb?: () => void): void; private _getTrack(streamReactTag: string, trackId: string): MediaStreamTrack; @@ -218,6 +264,7 @@ declare module 'react-native-webrtc' { export class RTCSessionDescription extends RTCSessionDescriptionType { constructor(info: RTCSessionDescriptionType); + toJSON(): RTCSessionDescriptionType; }