@stream-io/video-react-native-sdk 1.21.1 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/android/gradle.properties +3 -3
- package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt +10 -0
- package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativePackage.kt +2 -1
- package/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt +592 -0
- package/android/src/main/java/com/streamvideo/reactnative/audio/BluetoothManager.kt +755 -0
- package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioDeviceEndpointUtils.kt +192 -0
- package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt +62 -0
- package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioManagerUtil.kt +159 -0
- package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioSetupStoreUtil.kt +41 -0
- package/android/src/main/java/com/streamvideo/reactnative/audio/utils/WebRtcAudioUtils.kt +250 -0
- package/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt +191 -0
- package/android/src/main/java/com/streamvideo/reactnative/model/AudioDeviceEndpoint.kt +136 -0
- package/android/src/main/java/org/webrtc/audio/WebRtcAudioTrackHelper.kt +12 -0
- package/dist/commonjs/components/Call/CallContent/CallContent.js +13 -9
- package/dist/commonjs/components/Call/CallContent/CallContent.js.map +1 -1
- package/dist/commonjs/components/Call/CallContent/RTCViewPipIOS.js +9 -2
- package/dist/commonjs/components/Call/CallContent/RTCViewPipIOS.js.map +1 -1
- package/dist/commonjs/components/Call/CallContent/RTCViewPipNative.js +3 -0
- package/dist/commonjs/components/Call/CallContent/RTCViewPipNative.js.map +1 -1
- package/dist/commonjs/components/Livestream/HostLivestream/HostLivestream.js +10 -6
- package/dist/commonjs/components/Livestream/HostLivestream/HostLivestream.js.map +1 -1
- package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLivestreamControls.js +8 -4
- package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLivestreamControls.js.map +1 -1
- package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js +26 -27
- package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js.map +1 -1
- package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js +10 -6
- package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js.map +1 -1
- package/dist/commonjs/hooks/useIsInPiPMode.js +3 -3
- package/dist/commonjs/hooks/useIsInPiPMode.js.map +1 -1
- package/dist/commonjs/hooks/usePermissionNotification.js +5 -5
- package/dist/commonjs/hooks/usePermissionNotification.js.map +1 -1
- package/dist/commonjs/index.js +12 -0
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/modules/call-manager/CallManager.js +113 -0
- package/dist/commonjs/modules/call-manager/CallManager.js.map +1 -0
- package/dist/commonjs/modules/call-manager/PrevLibDetection.js +18 -0
- package/dist/commonjs/modules/call-manager/PrevLibDetection.js.map +1 -0
- package/dist/commonjs/modules/call-manager/index.js +24 -0
- package/dist/commonjs/modules/call-manager/index.js.map +1 -0
- package/dist/commonjs/modules/call-manager/native-module.d.js +4 -0
- package/dist/commonjs/modules/call-manager/native-module.d.js.map +1 -0
- package/dist/commonjs/modules/call-manager/types.js +2 -0
- package/dist/commonjs/modules/call-manager/types.js.map +1 -0
- package/dist/commonjs/providers/StreamCall/AppStateListener.js +5 -5
- package/dist/commonjs/providers/StreamCall/AppStateListener.js.map +1 -1
- package/dist/commonjs/theme/theme.js.map +1 -1
- package/dist/commonjs/utils/internal/rxSubjects.js +2 -2
- package/dist/commonjs/utils/internal/rxSubjects.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/module/components/Call/CallContent/CallContent.js +14 -9
- package/dist/module/components/Call/CallContent/CallContent.js.map +1 -1
- package/dist/module/components/Call/CallContent/RTCViewPipIOS.js +9 -2
- package/dist/module/components/Call/CallContent/RTCViewPipIOS.js.map +1 -1
- package/dist/module/components/Call/CallContent/RTCViewPipNative.js +3 -0
- package/dist/module/components/Call/CallContent/RTCViewPipNative.js.map +1 -1
- package/dist/module/components/Livestream/HostLivestream/HostLivestream.js +10 -5
- package/dist/module/components/Livestream/HostLivestream/HostLivestream.js.map +1 -1
- package/dist/module/components/Livestream/LivestreamControls/ViewerLivestreamControls.js +9 -4
- package/dist/module/components/Livestream/LivestreamControls/ViewerLivestreamControls.js.map +1 -1
- package/dist/module/components/Livestream/LivestreamLayout/LivestreamLayout.js +26 -27
- package/dist/module/components/Livestream/LivestreamLayout/LivestreamLayout.js.map +1 -1
- package/dist/module/components/Livestream/ViewerLivestream/ViewerLivestream.js +10 -5
- package/dist/module/components/Livestream/ViewerLivestream/ViewerLivestream.js.map +1 -1
- package/dist/module/hooks/useIsInPiPMode.js +4 -4
- package/dist/module/hooks/useIsInPiPMode.js.map +1 -1
- package/dist/module/hooks/usePermissionNotification.js +7 -6
- package/dist/module/hooks/usePermissionNotification.js.map +1 -1
- package/dist/module/icons/Back.js +1 -1
- package/dist/module/icons/Back.js.map +1 -1
- package/dist/module/icons/CameraSwitch.js +1 -1
- package/dist/module/icons/CameraSwitch.js.map +1 -1
- package/dist/module/icons/Mic.js +1 -1
- package/dist/module/icons/Mic.js.map +1 -1
- package/dist/module/icons/MicOff.js +1 -1
- package/dist/module/icons/MicOff.js.map +1 -1
- package/dist/module/icons/Phone.js +1 -1
- package/dist/module/icons/Phone.js.map +1 -1
- package/dist/module/icons/PinVertical.js +1 -1
- package/dist/module/icons/PinVertical.js.map +1 -1
- package/dist/module/icons/Reaction.js +1 -1
- package/dist/module/icons/Reaction.js.map +1 -1
- package/dist/module/icons/Spotlight.js +1 -1
- package/dist/module/icons/Spotlight.js.map +1 -1
- package/dist/module/icons/Video.js +1 -1
- package/dist/module/icons/Video.js.map +1 -1
- package/dist/module/icons/VideoSlash.js +1 -1
- package/dist/module/icons/VideoSlash.js.map +1 -1
- package/dist/module/index.js +1 -0
- package/dist/module/index.js.map +1 -1
- package/dist/module/modules/call-manager/CallManager.js +106 -0
- package/dist/module/modules/call-manager/CallManager.js.map +1 -0
- package/dist/module/modules/call-manager/PrevLibDetection.js +12 -0
- package/dist/module/modules/call-manager/PrevLibDetection.js.map +1 -0
- package/dist/module/modules/call-manager/index.js +4 -0
- package/dist/module/modules/call-manager/index.js.map +1 -0
- package/dist/module/modules/call-manager/native-module.d.js +2 -0
- package/dist/module/modules/call-manager/native-module.d.js.map +1 -0
- package/dist/module/modules/call-manager/types.js +2 -0
- package/dist/module/modules/call-manager/types.js.map +1 -0
- package/dist/module/providers/StreamCall/AppStateListener.js +6 -6
- package/dist/module/providers/StreamCall/AppStateListener.js.map +1 -1
- package/dist/module/theme/theme.js.map +1 -1
- package/dist/module/utils/internal/rxSubjects.js +1 -1
- package/dist/module/utils/internal/rxSubjects.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/typescript/components/Call/CallContent/CallContent.d.ts +3 -2
- package/dist/typescript/components/Call/CallContent/CallContent.d.ts.map +1 -1
- package/dist/typescript/components/Call/CallContent/RTCViewPipIOS.d.ts +5 -0
- package/dist/typescript/components/Call/CallContent/RTCViewPipIOS.d.ts.map +1 -1
- package/dist/typescript/components/Call/CallContent/RTCViewPipNative.d.ts +6 -0
- package/dist/typescript/components/Call/CallContent/RTCViewPipNative.d.ts.map +1 -1
- package/dist/typescript/components/Livestream/HostLivestream/HostLivestream.d.ts.map +1 -1
- package/dist/typescript/components/Livestream/LivestreamControls/ViewerLivestreamControls.d.ts.map +1 -1
- package/dist/typescript/components/Livestream/LivestreamLayout/LivestreamLayout.d.ts.map +1 -1
- package/dist/typescript/components/Livestream/ViewerLivestream/ViewerLivestream.d.ts.map +1 -1
- package/dist/typescript/hooks/usePermissionNotification.d.ts.map +1 -1
- package/dist/typescript/icons/Back.d.ts +1 -1
- package/dist/typescript/icons/Back.d.ts.map +1 -1
- package/dist/typescript/icons/BadNetwork.d.ts +1 -1
- package/dist/typescript/icons/BadNetwork.d.ts.map +1 -1
- package/dist/typescript/icons/CameraSwitch.d.ts +1 -1
- package/dist/typescript/icons/CameraSwitch.d.ts.map +1 -1
- package/dist/typescript/icons/LivestreamControls.d.ts +1 -1
- package/dist/typescript/icons/LivestreamControls.d.ts.map +1 -1
- package/dist/typescript/icons/Lock.d.ts +1 -1
- package/dist/typescript/icons/Lock.d.ts.map +1 -1
- package/dist/typescript/icons/Maximize.d.ts +1 -1
- package/dist/typescript/icons/Maximize.d.ts.map +1 -1
- package/dist/typescript/icons/Mic.d.ts +1 -1
- package/dist/typescript/icons/Mic.d.ts.map +1 -1
- package/dist/typescript/icons/MicOff.d.ts +1 -1
- package/dist/typescript/icons/MicOff.d.ts.map +1 -1
- package/dist/typescript/icons/Phone.d.ts +1 -1
- package/dist/typescript/icons/Phone.d.ts.map +1 -1
- package/dist/typescript/icons/PhoneDown.d.ts +1 -1
- package/dist/typescript/icons/PhoneDown.d.ts.map +1 -1
- package/dist/typescript/icons/PinVertical.d.ts +1 -1
- package/dist/typescript/icons/PinVertical.d.ts.map +1 -1
- package/dist/typescript/icons/Reaction.d.ts +1 -1
- package/dist/typescript/icons/Reaction.d.ts.map +1 -1
- package/dist/typescript/icons/ScreenShare.d.ts +1 -1
- package/dist/typescript/icons/ScreenShare.d.ts.map +1 -1
- package/dist/typescript/icons/ScreenShareIndicator.d.ts +1 -1
- package/dist/typescript/icons/ScreenShareIndicator.d.ts.map +1 -1
- package/dist/typescript/icons/Spotlight.d.ts +1 -1
- package/dist/typescript/icons/Spotlight.d.ts.map +1 -1
- package/dist/typescript/icons/StopScreenShare.d.ts +1 -1
- package/dist/typescript/icons/StopScreenShare.d.ts.map +1 -1
- package/dist/typescript/icons/Video.d.ts +1 -1
- package/dist/typescript/icons/Video.d.ts.map +1 -1
- package/dist/typescript/icons/VideoSlash.d.ts +1 -1
- package/dist/typescript/icons/VideoSlash.d.ts.map +1 -1
- package/dist/typescript/index.d.ts +1 -0
- package/dist/typescript/index.d.ts.map +1 -1
- package/dist/typescript/modules/call-manager/CallManager.d.ts +67 -0
- package/dist/typescript/modules/call-manager/CallManager.d.ts.map +1 -0
- package/dist/typescript/modules/call-manager/PrevLibDetection.d.ts +13 -0
- package/dist/typescript/modules/call-manager/PrevLibDetection.d.ts.map +1 -0
- package/dist/typescript/modules/call-manager/index.d.ts +4 -0
- package/dist/typescript/modules/call-manager/index.d.ts.map +1 -0
- package/dist/typescript/modules/call-manager/types.d.ts +15 -0
- package/dist/typescript/modules/call-manager/types.d.ts.map +1 -0
- package/dist/typescript/providers/StreamCall/AppStateListener.d.ts.map +1 -1
- package/dist/typescript/theme/theme.d.ts +1 -2
- package/dist/typescript/theme/theme.d.ts.map +1 -1
- package/dist/typescript/utils/internal/rxSubjects.d.ts +1 -1
- package/dist/typescript/utils/internal/rxSubjects.d.ts.map +1 -1
- package/dist/typescript/version.d.ts +1 -1
- package/ios/PictureInPicture/StreamPictureInPictureController.swift +5 -0
- package/ios/RTCViewPip.swift +15 -0
- package/ios/RTCViewPipManager.mm +1 -0
- package/ios/StreamInCallManager.m +26 -0
- package/ios/StreamInCallManager.swift +303 -0
- package/ios/StreamVideoReactNative-Bridging-Header.h +1 -0
- package/ios/StreamVideoReactNative.m +6 -5
- package/package.json +32 -34
- package/src/components/Call/CallContent/CallContent.tsx +14 -10
- package/src/components/Call/CallContent/RTCViewPipIOS.tsx +17 -2
- package/src/components/Call/CallContent/RTCViewPipNative.tsx +8 -0
- package/src/components/Livestream/HostLivestream/HostLivestream.tsx +8 -3
- package/src/components/Livestream/LivestreamControls/ViewerLivestreamControls.tsx +11 -5
- package/src/components/Livestream/LivestreamLayout/LivestreamLayout.tsx +38 -29
- package/src/components/Livestream/ViewerLivestream/ViewerLivestream.tsx +8 -3
- package/src/hooks/useIsInPiPMode.tsx +4 -4
- package/src/hooks/usePermissionNotification.tsx +7 -12
- package/src/icons/Back.tsx +2 -2
- package/src/icons/BadNetwork.tsx +1 -1
- package/src/icons/CameraSwitch.tsx +2 -2
- package/src/icons/LivestreamControls.tsx +1 -1
- package/src/icons/Lock.tsx +1 -1
- package/src/icons/Maximize.tsx +1 -1
- package/src/icons/Mic.tsx +2 -2
- package/src/icons/MicOff.tsx +2 -2
- package/src/icons/Phone.tsx +2 -2
- package/src/icons/PhoneDown.tsx +1 -1
- package/src/icons/PinVertical.tsx +2 -2
- package/src/icons/Reaction.tsx +2 -2
- package/src/icons/ScreenShare.tsx +1 -1
- package/src/icons/ScreenShareIndicator.tsx +1 -1
- package/src/icons/Spotlight.tsx +2 -2
- package/src/icons/StopScreenShare.tsx +1 -1
- package/src/icons/Video.tsx +2 -2
- package/src/icons/VideoSlash.tsx +2 -2
- package/src/index.ts +1 -0
- package/src/modules/call-manager/CallManager.ts +116 -0
- package/src/modules/call-manager/PrevLibDetection.ts +27 -0
- package/src/modules/call-manager/index.ts +5 -0
- package/src/modules/call-manager/native-module.d.ts +80 -0
- package/src/modules/call-manager/types.ts +25 -0
- package/src/providers/StreamCall/AppStateListener.tsx +6 -9
- package/src/theme/theme.ts +2 -2
- package/src/utils/internal/rxSubjects.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import React
|
|
3
|
+
import UIKit
|
|
4
|
+
import AVFoundation
|
|
5
|
+
import stream_react_native_webrtc
|
|
6
|
+
import AVKit
|
|
7
|
+
import MediaPlayer
|
|
8
|
+
|
|
9
|
+
enum CallAudioRole {
|
|
10
|
+
case listener
|
|
11
|
+
case communicator
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
enum DefaultAudioDevice {
|
|
15
|
+
case speaker
|
|
16
|
+
case earpiece
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@objc(StreamInCallManager)
|
|
20
|
+
class StreamInCallManager: RCTEventEmitter {
|
|
21
|
+
|
|
22
|
+
private let audioSessionQueue = DispatchQueue(label: "io.getstream.rn.audioSessionQueue")
|
|
23
|
+
|
|
24
|
+
private var audioManagerActivated = false
|
|
25
|
+
private var callAudioRole: CallAudioRole = .communicator
|
|
26
|
+
private var defaultAudioDevice: DefaultAudioDevice = .speaker
|
|
27
|
+
private var previousVolume: Float = 0.75
|
|
28
|
+
|
|
29
|
+
private struct AudioSessionState {
|
|
30
|
+
let category: AVAudioSession.Category
|
|
31
|
+
let mode: AVAudioSession.Mode
|
|
32
|
+
let options: AVAudioSession.CategoryOptions
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private var previousAudioSessionState: AudioSessionState?
|
|
36
|
+
|
|
37
|
+
override func invalidate() {
|
|
38
|
+
stop()
|
|
39
|
+
super.invalidate()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@objc(setAudioRole:)
|
|
48
|
+
func setAudioRole(audioRole: String) {
|
|
49
|
+
audioSessionQueue.async { [self] in
|
|
50
|
+
if audioManagerActivated {
|
|
51
|
+
log("AudioManager is already activated, audio role cannot be changed.")
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
self.callAudioRole = audioRole.lowercased() == "listener" ? .listener : .communicator
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@objc(setDefaultAudioDeviceEndpointType:)
|
|
59
|
+
func setDefaultAudioDeviceEndpointType(endpointType: String) {
|
|
60
|
+
audioSessionQueue.async { [self] in
|
|
61
|
+
if audioManagerActivated {
|
|
62
|
+
log("AudioManager is already activated, default audio device cannot be changed.")
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
self.defaultAudioDevice = endpointType.lowercased() == "earpiece" ? .earpiece : .speaker
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@objc
|
|
70
|
+
func start() {
|
|
71
|
+
audioSessionQueue.async { [self] in
|
|
72
|
+
if audioManagerActivated {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
let session = AVAudioSession.sharedInstance()
|
|
76
|
+
previousAudioSessionState = AudioSessionState(
|
|
77
|
+
category: session.category,
|
|
78
|
+
mode: session.mode,
|
|
79
|
+
options: session.categoryOptions
|
|
80
|
+
)
|
|
81
|
+
configureAudioSession()
|
|
82
|
+
audioManagerActivated = true
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@objc
|
|
87
|
+
func stop() {
|
|
88
|
+
audioSessionQueue.async { [self] in
|
|
89
|
+
if !audioManagerActivated {
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
if let prev = previousAudioSessionState {
|
|
93
|
+
let session = AVAudioSession.sharedInstance()
|
|
94
|
+
do {
|
|
95
|
+
try session.setCategory(prev.category, mode: prev.mode, options: prev.options)
|
|
96
|
+
} catch {
|
|
97
|
+
log("Error restoring previous audio session: \(error.localizedDescription)")
|
|
98
|
+
}
|
|
99
|
+
previousAudioSessionState = nil
|
|
100
|
+
}
|
|
101
|
+
audioManagerActivated = false
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private func configureAudioSession() {
|
|
106
|
+
let intendedCategory: AVAudioSession.Category!
|
|
107
|
+
let intendedMode: AVAudioSession.Mode!
|
|
108
|
+
let intendedOptions: AVAudioSession.CategoryOptions!
|
|
109
|
+
|
|
110
|
+
if (callAudioRole == .listener) {
|
|
111
|
+
// enables high quality audio playback but disables microphone
|
|
112
|
+
intendedCategory = .playback
|
|
113
|
+
intendedMode = .default
|
|
114
|
+
intendedOptions = []
|
|
115
|
+
} else {
|
|
116
|
+
intendedCategory = .playAndRecord
|
|
117
|
+
intendedMode = .voiceChat
|
|
118
|
+
|
|
119
|
+
if (defaultAudioDevice == .speaker) {
|
|
120
|
+
// defaultToSpeaker will route to speaker if nothing else is connected
|
|
121
|
+
intendedOptions = [.allowBluetooth, .defaultToSpeaker]
|
|
122
|
+
} else {
|
|
123
|
+
// having no defaultToSpeaker makes sure audio goes to earpiece if nothing is connected
|
|
124
|
+
intendedOptions = [.allowBluetooth]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// START: set the config that webrtc must use when it takes control
|
|
129
|
+
let rtcConfig = RTCAudioSessionConfiguration.webRTC()
|
|
130
|
+
rtcConfig.category = intendedCategory.rawValue
|
|
131
|
+
rtcConfig.mode = intendedMode.rawValue
|
|
132
|
+
rtcConfig.categoryOptions = intendedOptions
|
|
133
|
+
RTCAudioSessionConfiguration.setWebRTC(rtcConfig)
|
|
134
|
+
// END
|
|
135
|
+
|
|
136
|
+
// START: compare current audio session with intended, and update if different
|
|
137
|
+
let session = RTCAudioSession.sharedInstance()
|
|
138
|
+
let currentCategory = session.category
|
|
139
|
+
let currentMode = session.mode
|
|
140
|
+
let currentOptions = session.categoryOptions
|
|
141
|
+
let currentIsActive = session.isActive
|
|
142
|
+
|
|
143
|
+
if currentCategory != intendedCategory.rawValue || currentMode != intendedMode.rawValue || currentOptions != intendedOptions || !currentIsActive {
|
|
144
|
+
session.lockForConfiguration()
|
|
145
|
+
do {
|
|
146
|
+
try session.setCategory(intendedCategory, mode: intendedMode, options: intendedOptions)
|
|
147
|
+
try session.setActive(true)
|
|
148
|
+
log("configureAudioSession: setCategory success \(intendedCategory.rawValue) \(intendedMode.rawValue) \(intendedOptions.rawValue)")
|
|
149
|
+
} catch {
|
|
150
|
+
log("configureAudioSession: setCategory failed due to: \(error.localizedDescription)")
|
|
151
|
+
do {
|
|
152
|
+
try session.setMode(intendedMode)
|
|
153
|
+
try session.setActive(true)
|
|
154
|
+
log("configureAudioSession: setMode success \(intendedMode.rawValue)")
|
|
155
|
+
} catch {
|
|
156
|
+
log("configureAudioSession: Error setting mode: \(error.localizedDescription)")
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
session.unlockForConfiguration()
|
|
160
|
+
} else {
|
|
161
|
+
log("configureAudioSession: no change needed")
|
|
162
|
+
}
|
|
163
|
+
// END
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@objc(showAudioRoutePicker)
|
|
167
|
+
public func showAudioRoutePicker() {
|
|
168
|
+
guard #available(iOS 11.0, tvOS 11.0, macOS 10.15, *) else {
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
DispatchQueue.main.async {
|
|
172
|
+
// AVRoutePickerView is the default UI with a
|
|
173
|
+
// button that users tap to stream audio/video content to a media receiver
|
|
174
|
+
let routePicker = AVRoutePickerView()
|
|
175
|
+
// Send a touch up inside event to the button to trigger the audio route picker
|
|
176
|
+
(routePicker.subviews.first { $0 is UIButton } as? UIButton)?
|
|
177
|
+
.sendActions(for: .touchUpInside)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@objc(setForceSpeakerphoneOn:)
|
|
182
|
+
func setForceSpeakerphoneOn(enable: Bool) {
|
|
183
|
+
let session = AVAudioSession.sharedInstance()
|
|
184
|
+
do {
|
|
185
|
+
try session.overrideOutputAudioPort(enable ? .speaker : .none)
|
|
186
|
+
try session.setActive(true)
|
|
187
|
+
} catch {
|
|
188
|
+
log("Error setting speakerphone: \(error)")
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@objc(setMicrophoneMute:)
|
|
193
|
+
func setMicrophoneMute(enable: Bool) {
|
|
194
|
+
log("iOS does not support setMicrophoneMute()")
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@objc
|
|
198
|
+
func logAudioState() {
|
|
199
|
+
let session = AVAudioSession.sharedInstance()
|
|
200
|
+
let logString = """
|
|
201
|
+
Audio State:
|
|
202
|
+
Category: \(session.category.rawValue)
|
|
203
|
+
Mode: \(session.mode.rawValue)
|
|
204
|
+
Output Port: \(session.currentRoute.outputs.first?.portName ?? "N/A")
|
|
205
|
+
Input Port: \(session.currentRoute.inputs.first?.portName ?? "N/A")
|
|
206
|
+
Category Options: \(session.categoryOptions)
|
|
207
|
+
InputNumberOfChannels: \(session.inputNumberOfChannels)
|
|
208
|
+
OutputNumberOfChannels: \(session.outputNumberOfChannels)
|
|
209
|
+
"""
|
|
210
|
+
log(logString)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@objc(muteAudioOutput)
|
|
214
|
+
func muteAudioOutput() {
|
|
215
|
+
DispatchQueue.main.async { [self] in
|
|
216
|
+
let volumeView = MPVolumeView()
|
|
217
|
+
|
|
218
|
+
// Add to a temporary view hierarchy to make it functional
|
|
219
|
+
if let window = getCurrentWindow() {
|
|
220
|
+
volumeView.frame = CGRect(x: -1000, y: -1000, width: 1, height: 1)
|
|
221
|
+
window.addSubview(volumeView)
|
|
222
|
+
|
|
223
|
+
// Give it a moment to initialize
|
|
224
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
225
|
+
if let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider {
|
|
226
|
+
self.previousVolume = slider.value
|
|
227
|
+
slider.setValue(0.0, animated: false)
|
|
228
|
+
slider.sendActions(for: .valueChanged)
|
|
229
|
+
self.log("Audio output muted via slider event")
|
|
230
|
+
} else {
|
|
231
|
+
self.log("Could not find volume slider")
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Remove from view hierarchy after use
|
|
235
|
+
volumeView.removeFromSuperview()
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@objc(unmuteAudioOutput)
|
|
242
|
+
func unmuteAudioOutput() {
|
|
243
|
+
DispatchQueue.main.async { [self] in
|
|
244
|
+
let volumeView = MPVolumeView()
|
|
245
|
+
|
|
246
|
+
// Add to a temporary view hierarchy to make it functional
|
|
247
|
+
if let window = getCurrentWindow() {
|
|
248
|
+
volumeView.frame = CGRect(x: -1000, y: -1000, width: 1, height: 1)
|
|
249
|
+
window.addSubview(volumeView)
|
|
250
|
+
|
|
251
|
+
// Give it a moment to initialize
|
|
252
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
253
|
+
if let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider {
|
|
254
|
+
let targetVolume = self.previousVolume > 0 ? self.previousVolume : 0.75
|
|
255
|
+
slider.setValue(targetVolume, animated: false)
|
|
256
|
+
slider.sendActions(for: .valueChanged)
|
|
257
|
+
self.log("Audio output unmuted via slider event")
|
|
258
|
+
} else {
|
|
259
|
+
self.log("Could not find volume slider")
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Remove from view hierarchy after use
|
|
263
|
+
volumeView.removeFromSuperview()
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// MARK: - RCTEventEmitter
|
|
270
|
+
|
|
271
|
+
override func supportedEvents() -> [String]! {
|
|
272
|
+
// TODO: list events that can be sent to JS
|
|
273
|
+
return []
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@objc
|
|
277
|
+
override func addListener(_ eventName: String!) {
|
|
278
|
+
super.addListener(eventName)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@objc
|
|
282
|
+
override func removeListeners(_ count: Double) {
|
|
283
|
+
super.removeListeners(count)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// MARK: - Helper Methods
|
|
287
|
+
private func getCurrentWindow() -> UIWindow? {
|
|
288
|
+
if #available(iOS 13.0, *) {
|
|
289
|
+
return UIApplication.shared.connectedScenes
|
|
290
|
+
.compactMap({ $0 as? UIWindowScene })
|
|
291
|
+
.first?.windows
|
|
292
|
+
.first(where: { $0.isKeyWindow })
|
|
293
|
+
} else {
|
|
294
|
+
return UIApplication.shared.keyWindow
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// MARK: - Logging Helper
|
|
299
|
+
private func log(_ message: String) {
|
|
300
|
+
NSLog("InCallManager: %@", message)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
}
|
|
@@ -69,7 +69,7 @@ RCT_EXPORT_MODULE();
|
|
|
69
69
|
if ((self = [super init])) {
|
|
70
70
|
_notificationCenter = CFNotificationCenterGetDarwinNotifyCenter();
|
|
71
71
|
[UIDevice currentDevice].batteryMonitoringEnabled = YES;
|
|
72
|
-
[self
|
|
72
|
+
[self setupScreenshareEventObserver];
|
|
73
73
|
[StreamVideoReactNative initializeSharedDictionaries];
|
|
74
74
|
}
|
|
75
75
|
return self;
|
|
@@ -85,7 +85,7 @@ RCT_EXPORT_METHOD(currentThermalState:(RCTPromiseResolveBlock)resolve rejecter:(
|
|
|
85
85
|
resolve(thermalStateString);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
-(void)
|
|
88
|
+
-(void)invalidate {
|
|
89
89
|
if (_busyTonePlayer) {
|
|
90
90
|
if (_busyTonePlayer.isPlaying) {
|
|
91
91
|
[_busyTonePlayer stop];
|
|
@@ -93,11 +93,12 @@ RCT_EXPORT_METHOD(currentThermalState:(RCTPromiseResolveBlock)resolve rejecter:(
|
|
|
93
93
|
_busyTonePlayer = nil;
|
|
94
94
|
[self removeAudioInterruptionHandling];
|
|
95
95
|
}
|
|
96
|
-
[self
|
|
96
|
+
[self clearScreenshareEventObserver];
|
|
97
|
+
[super invalidate];
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
|
|
100
|
-
-(void)
|
|
101
|
+
-(void)setupScreenshareEventObserver {
|
|
101
102
|
CFNotificationCenterAddObserver(_notificationCenter,
|
|
102
103
|
(__bridge const void *)(self),
|
|
103
104
|
broadcastNotificationCallback,
|
|
@@ -112,7 +113,7 @@ RCT_EXPORT_METHOD(currentThermalState:(RCTPromiseResolveBlock)resolve rejecter:(
|
|
|
112
113
|
CFNotificationSuspensionBehaviorDeliverImmediately);
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
-(void)
|
|
116
|
+
-(void)clearScreenshareEventObserver {
|
|
116
117
|
CFNotificationCenterRemoveObserver(_notificationCenter,
|
|
117
118
|
(__bridge const void *)(self),
|
|
118
119
|
(__bridge CFStringRef)kBroadcastStartedNotification,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-react-native-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"description": "Stream Video SDK for React Native",
|
|
5
5
|
"author": "https://getstream.io",
|
|
6
6
|
"homepage": "https://getstream.io/video/docs/react-native/",
|
|
@@ -45,12 +45,12 @@
|
|
|
45
45
|
"!**/.*"
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@stream-io/video-client": "1.
|
|
49
|
-
"@stream-io/video-react-bindings": "1.
|
|
48
|
+
"@stream-io/video-client": "1.34.0",
|
|
49
|
+
"@stream-io/video-react-bindings": "1.10.0",
|
|
50
50
|
"intl-pluralrules": "2.0.1",
|
|
51
51
|
"lodash.merge": "^4.6.2",
|
|
52
|
-
"react-native-url-polyfill": "
|
|
53
|
-
"rxjs": "~7.8.
|
|
52
|
+
"react-native-url-polyfill": "^3.0.0",
|
|
53
|
+
"rxjs": "~7.8.2",
|
|
54
54
|
"text-encoding-polyfill": "0.6.7"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
@@ -60,16 +60,15 @@
|
|
|
60
60
|
"@react-native-firebase/app": ">=17.5.0",
|
|
61
61
|
"@react-native-firebase/messaging": ">=17.5.0",
|
|
62
62
|
"@stream-io/noise-cancellation-react-native": ">=0.1.0",
|
|
63
|
-
"@stream-io/react-native-webrtc": ">=125.4.
|
|
63
|
+
"@stream-io/react-native-webrtc": ">=125.4.4",
|
|
64
64
|
"@stream-io/video-filters-react-native": ">=0.1.0",
|
|
65
65
|
"expo": ">=47.0.0",
|
|
66
66
|
"expo-build-properties": "*",
|
|
67
67
|
"expo-notifications": "*",
|
|
68
68
|
"react": ">=17.0.0",
|
|
69
|
-
"react-native": ">=0.
|
|
69
|
+
"react-native": ">=0.73.0",
|
|
70
70
|
"react-native-callkeep": ">=4.3.11",
|
|
71
71
|
"react-native-gesture-handler": ">=2.8.0",
|
|
72
|
-
"react-native-incall-manager": ">=4.2.0",
|
|
73
72
|
"react-native-reanimated": ">=2.7.0",
|
|
74
73
|
"react-native-svg": ">=13.6.0",
|
|
75
74
|
"react-native-voip-push-notification": ">=3.3.1"
|
|
@@ -116,44 +115,43 @@
|
|
|
116
115
|
}
|
|
117
116
|
},
|
|
118
117
|
"devDependencies": {
|
|
119
|
-
"@
|
|
120
|
-
"@expo/config-
|
|
121
|
-
"@expo/
|
|
118
|
+
"@babel/core": "^7.28.4",
|
|
119
|
+
"@expo/config-plugins": "54.0.2",
|
|
120
|
+
"@expo/config-types": "^54.0.8",
|
|
121
|
+
"@expo/plist": "^0.4.7",
|
|
122
122
|
"@notifee/react-native": "9.1.8",
|
|
123
123
|
"@react-native-community/netinfo": "11.4.1",
|
|
124
124
|
"@react-native-community/push-notification-ios": "1.11.0",
|
|
125
|
-
"@react-native-firebase/app": "^
|
|
126
|
-
"@react-native-firebase/messaging": "^
|
|
127
|
-
"@react-native/babel-preset": "^0.
|
|
128
|
-
"@stream-io/noise-cancellation-react-native": "^0.
|
|
129
|
-
"@stream-io/react-native-webrtc": "125.4.
|
|
130
|
-
"@stream-io/video-filters-react-native": "^0.
|
|
125
|
+
"@react-native-firebase/app": "^23.4.0",
|
|
126
|
+
"@react-native-firebase/messaging": "^23.4.0",
|
|
127
|
+
"@react-native/babel-preset": "^0.81.4",
|
|
128
|
+
"@stream-io/noise-cancellation-react-native": "^0.4.0",
|
|
129
|
+
"@stream-io/react-native-webrtc": "125.4.4",
|
|
130
|
+
"@stream-io/video-filters-react-native": "^0.8.0",
|
|
131
131
|
"@testing-library/jest-native": "^5.4.3",
|
|
132
|
-
"@testing-library/react-native": "13.
|
|
133
|
-
"@tsconfig/
|
|
132
|
+
"@testing-library/react-native": "13.3.3",
|
|
133
|
+
"@tsconfig/node18": "^18.2.4",
|
|
134
134
|
"@types/jest": "^29.5.14",
|
|
135
135
|
"@types/lodash.merge": "^4.6.9",
|
|
136
|
-
"@types/react": "
|
|
137
|
-
"@types/react-native-incall-manager": "^4.0.3",
|
|
136
|
+
"@types/react": "~19.1.17",
|
|
138
137
|
"@types/react-test-renderer": "^19.1.0",
|
|
139
|
-
"expo": "~
|
|
140
|
-
"expo-build-properties": "^0.
|
|
141
|
-
"expo-module-scripts": "^
|
|
142
|
-
"expo-
|
|
143
|
-
"expo-notifications": "~0.29.14",
|
|
138
|
+
"expo": "~54.0.12",
|
|
139
|
+
"expo-build-properties": "^1.0.9",
|
|
140
|
+
"expo-module-scripts": "^5.0.7",
|
|
141
|
+
"expo-notifications": "~0.32.12",
|
|
144
142
|
"jest": "^29.7.0",
|
|
145
|
-
"react": "19.
|
|
146
|
-
"react-native": "0.
|
|
143
|
+
"react": "19.1.0",
|
|
144
|
+
"react-native": "^0.81.4",
|
|
147
145
|
"react-native-builder-bob": "~0.23",
|
|
148
146
|
"react-native-callkeep": "^4.3.16",
|
|
149
|
-
"react-native-gesture-handler": "^2.
|
|
150
|
-
"react-native-
|
|
151
|
-
"react-native-
|
|
152
|
-
"react-native-svg": "15.11.2",
|
|
147
|
+
"react-native-gesture-handler": "^2.28.0",
|
|
148
|
+
"react-native-reanimated": "~4.1.2",
|
|
149
|
+
"react-native-svg": "^15.14.0",
|
|
153
150
|
"react-native-voip-push-notification": "3.3.3",
|
|
154
|
-
"react-
|
|
151
|
+
"react-native-worklets": "^0.5.0",
|
|
152
|
+
"react-test-renderer": "19.1.0",
|
|
155
153
|
"rimraf": "^6.0.1",
|
|
156
|
-
"typescript": "^5.
|
|
154
|
+
"typescript": "^5.9.3"
|
|
157
155
|
},
|
|
158
156
|
"react-native-builder-bob": {
|
|
159
157
|
"source": "src",
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
NativeModules,
|
|
4
|
+
Platform,
|
|
3
5
|
StyleSheet,
|
|
4
6
|
View,
|
|
5
|
-
NativeModules,
|
|
6
7
|
type ViewStyle,
|
|
7
|
-
Platform,
|
|
8
8
|
} from 'react-native';
|
|
9
|
-
import InCallManager from 'react-native-incall-manager';
|
|
10
9
|
import {
|
|
11
10
|
CallParticipantsGrid,
|
|
12
11
|
type CallParticipantsGridProps,
|
|
@@ -43,6 +42,7 @@ import {
|
|
|
43
42
|
type ScreenShareOverlayProps,
|
|
44
43
|
} from '../../utility/ScreenShareOverlay';
|
|
45
44
|
import { RTCViewPipIOS } from './RTCViewPipIOS';
|
|
45
|
+
import { getRNInCallManagerLibNoThrow } from '../../../modules/call-manager/PrevLibDetection';
|
|
46
46
|
|
|
47
47
|
export type StreamReactionType = StreamReaction & {
|
|
48
48
|
icon: string;
|
|
@@ -95,7 +95,8 @@ export type CallContentProps = Pick<
|
|
|
95
95
|
*/
|
|
96
96
|
disablePictureInPicture?: boolean;
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
98
|
+
* @deprecated This prop is deprecated and will be removed in the future. Use `StreamInCallManager` instead.
|
|
99
|
+
* Props to set the audio mode for the react-native-incall-manager library
|
|
99
100
|
* If media type is video, audio is routed by default to speaker, otherwise it is routed to earpiece.
|
|
100
101
|
* Changing the mode on the fly is not supported.
|
|
101
102
|
* Manually invoke `InCallManager.start({ media })` to achieve this.
|
|
@@ -119,9 +120,9 @@ export const CallContent = ({
|
|
|
119
120
|
layout = 'grid',
|
|
120
121
|
landscape = false,
|
|
121
122
|
supportedReactions,
|
|
123
|
+
initialInCallManagerAudioMode = 'video',
|
|
122
124
|
iOSPiPIncludeLocalParticipantVideo,
|
|
123
125
|
disablePictureInPicture,
|
|
124
|
-
initialInCallManagerAudioMode = 'video',
|
|
125
126
|
}: CallContentProps) => {
|
|
126
127
|
const [
|
|
127
128
|
showRemoteParticipantInFloatingView,
|
|
@@ -140,8 +141,6 @@ export const CallContent = ({
|
|
|
140
141
|
|
|
141
142
|
useAutoEnterPiPEffect(disablePictureInPicture);
|
|
142
143
|
|
|
143
|
-
const incallManagerModeRef = useRef(initialInCallManagerAudioMode);
|
|
144
|
-
|
|
145
144
|
const _remoteParticipants = useRemoteParticipants();
|
|
146
145
|
const remoteParticipants = useDebouncedValue(_remoteParticipants, 300); // we debounce the remote participants to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously
|
|
147
146
|
const localParticipant = useLocalParticipant();
|
|
@@ -188,10 +187,15 @@ export const CallContent = ({
|
|
|
188
187
|
/**
|
|
189
188
|
* This hook is used to handle IncallManager specs of the application.
|
|
190
189
|
*/
|
|
190
|
+
const incallManagerModeRef = useRef(initialInCallManagerAudioMode);
|
|
191
191
|
useEffect(() => {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
const prevInCallManager = getRNInCallManagerLibNoThrow();
|
|
193
|
+
if (prevInCallManager) {
|
|
194
|
+
prevInCallManager.start({ media: incallManagerModeRef.current });
|
|
195
|
+
return () => {
|
|
196
|
+
prevInCallManager.stop();
|
|
197
|
+
};
|
|
198
|
+
}
|
|
195
199
|
}, []);
|
|
196
200
|
|
|
197
201
|
const handleFloatingViewParticipantSwitch = () => {
|
|
@@ -18,13 +18,19 @@ import {
|
|
|
18
18
|
import { useDebouncedValue } from '../../../utils/hooks';
|
|
19
19
|
import { shouldDisableIOSLocalVideoOnBackgroundRef } from '../../../utils/internal/shouldDisableIOSLocalVideoOnBackground';
|
|
20
20
|
import { useTrackDimensions } from '../../../hooks/useTrackDimensions';
|
|
21
|
+
import { isInPiPMode$ } from '../../../utils/internal/rxSubjects';
|
|
21
22
|
|
|
22
23
|
type Props = {
|
|
23
24
|
includeLocalParticipantVideo?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Callback that is called when the PiP mode state changes.
|
|
27
|
+
* @param active - true when PiP started, false when PiP stopped
|
|
28
|
+
*/
|
|
29
|
+
onPiPChange?: (active: boolean) => void;
|
|
24
30
|
};
|
|
25
31
|
|
|
26
32
|
export const RTCViewPipIOS = React.memo((props: Props) => {
|
|
27
|
-
const { includeLocalParticipantVideo } = props;
|
|
33
|
+
const { includeLocalParticipantVideo, onPiPChange } = props;
|
|
28
34
|
const call = useCall();
|
|
29
35
|
const { useParticipants } = useCallStateHooks();
|
|
30
36
|
const _allParticipants = useParticipants({
|
|
@@ -112,9 +118,18 @@ export const RTCViewPipIOS = React.memo((props: Props) => {
|
|
|
112
118
|
return videoStreamToRender?.toURL();
|
|
113
119
|
}, [videoStreamToRender]);
|
|
114
120
|
|
|
121
|
+
const handlePiPChange = (event: { nativeEvent: { active: boolean } }) => {
|
|
122
|
+
isInPiPMode$.next(event.nativeEvent.active);
|
|
123
|
+
onPiPChange?.(event.nativeEvent.active);
|
|
124
|
+
};
|
|
125
|
+
|
|
115
126
|
return (
|
|
116
127
|
<>
|
|
117
|
-
<RTCViewPipNative
|
|
128
|
+
<RTCViewPipNative
|
|
129
|
+
streamURL={streamURL}
|
|
130
|
+
ref={nativeRef}
|
|
131
|
+
onPiPChange={handlePiPChange}
|
|
132
|
+
/>
|
|
118
133
|
{participantInSpotlight && (
|
|
119
134
|
<DimensionsUpdatedRenderless
|
|
120
135
|
participant={participantInSpotlight}
|
|
@@ -10,8 +10,13 @@ import {
|
|
|
10
10
|
|
|
11
11
|
const COMPONENT_NAME = 'RTCViewPip';
|
|
12
12
|
|
|
13
|
+
export type PiPChangeEvent = {
|
|
14
|
+
active: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
13
17
|
type RTCViewPipNativeProps = {
|
|
14
18
|
streamURL?: string;
|
|
19
|
+
onPiPChange?: (event: { nativeEvent: PiPChangeEvent }) => void;
|
|
15
20
|
};
|
|
16
21
|
|
|
17
22
|
const NativeComponent: HostComponent<RTCViewPipNativeProps> =
|
|
@@ -48,6 +53,7 @@ export const RTCViewPipNative = React.memo(
|
|
|
48
53
|
React.Ref<any>,
|
|
49
54
|
{
|
|
50
55
|
streamURL?: string;
|
|
56
|
+
onPiPChange?: (event: { nativeEvent: PiPChangeEvent }) => void;
|
|
51
57
|
}
|
|
52
58
|
>((props, ref) => {
|
|
53
59
|
if (Platform.OS !== 'ios') return null;
|
|
@@ -58,6 +64,8 @@ export const RTCViewPipNative = React.memo(
|
|
|
58
64
|
pointerEvents={'none'}
|
|
59
65
|
// eslint-disable-next-line react/prop-types
|
|
60
66
|
streamURL={props.streamURL}
|
|
67
|
+
// eslint-disable-next-line react/prop-types
|
|
68
|
+
onPiPChange={props.onPiPChange}
|
|
61
69
|
// @ts-expect-error - types issue
|
|
62
70
|
ref={ref}
|
|
63
71
|
/>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useMemo } from 'react';
|
|
2
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
|
-
import
|
|
3
|
+
import { getRNInCallManagerLibNoThrow } from '../../../modules/call-manager/PrevLibDetection';
|
|
4
4
|
|
|
5
5
|
import { useTheme } from '../../../contexts';
|
|
6
6
|
import {
|
|
@@ -89,8 +89,13 @@ export const HostLivestream = ({
|
|
|
89
89
|
|
|
90
90
|
// Automatically route audio to speaker devices as relevant for watching videos.
|
|
91
91
|
useEffect(() => {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
const prevInCallManager = getRNInCallManagerLibNoThrow();
|
|
93
|
+
if (prevInCallManager) {
|
|
94
|
+
prevInCallManager.start({ media: 'video' });
|
|
95
|
+
return () => {
|
|
96
|
+
prevInCallManager.stop();
|
|
97
|
+
};
|
|
98
|
+
}
|
|
94
99
|
}, []);
|
|
95
100
|
|
|
96
101
|
const [topViewHeight, setTopViewHeight] = React.useState<number>();
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ViewerLeaveStreamButton as DefaultViewerLeaveStreamButton,
|
|
11
11
|
type ViewerLeaveStreamButtonProps,
|
|
12
12
|
} from './ViewerLeaveStreamButton';
|
|
13
|
+
import { callManager } from '../../../modules/call-manager';
|
|
13
14
|
import { useTheme } from '../../../contexts';
|
|
14
15
|
import { Z_INDEX } from '../../../constants';
|
|
15
16
|
import {
|
|
@@ -18,12 +19,11 @@ import {
|
|
|
18
19
|
LiveIndicator,
|
|
19
20
|
} from '../LivestreamTopView';
|
|
20
21
|
import { IconWrapper, Maximize } from '../../../icons';
|
|
21
|
-
import InCallManager from 'react-native-incall-manager';
|
|
22
22
|
import {
|
|
23
|
-
VolumeOff,
|
|
24
|
-
VolumeOn,
|
|
25
23
|
PauseIcon,
|
|
26
24
|
PlayIcon,
|
|
25
|
+
VolumeOff,
|
|
26
|
+
VolumeOn,
|
|
27
27
|
} from '../../../icons/LivestreamControls';
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -104,10 +104,16 @@ export const ViewerLivestreamControls = ({
|
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
const toggleAudio = () => {
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
const shouldMute = !isMuted;
|
|
108
|
+
callManager.speaker.setMute(shouldMute);
|
|
109
|
+
setIsMuted(shouldMute);
|
|
109
110
|
};
|
|
110
111
|
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
// always unmute audio output on mount for consistency
|
|
114
|
+
callManager.speaker.setMute(false);
|
|
115
|
+
}, []);
|
|
116
|
+
|
|
111
117
|
const togglePlayPause = () => {
|
|
112
118
|
setIsPlaying(!isPlaying);
|
|
113
119
|
showPlayPauseButtonWithTimeout();
|