Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/client/src/Call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ export class Call {
setup = async () => {
await withoutConcurrency(this.joinLeaveConcurrencyTag, async () => {
if (this.initialized) return;
globalThis.streamRNVideoSDK?.callManager.setup();

this.leaveCallHooks.add(
this.on('all', (event) => {
Expand Down Expand Up @@ -659,6 +660,8 @@ export class Call {
this.cancelAutoDrop();
this.clientStore.unregisterCall(this);

globalThis.streamRNVideoSDK?.callManager.stop();

this.camera.dispose();
this.microphone.dispose();
this.screenShare.dispose();
Expand Down Expand Up @@ -884,6 +887,8 @@ export class Call {
throw new Error(`Illegal State: call.join() shall be called only once`);
}

globalThis.streamRNVideoSDK?.callManager.start();

// we will count the number of join failures per SFU.
// once the number of failures reaches 2, we will piggyback on the `migrating_from`
// field to force the coordinator to provide us another SFU
Expand Down
23 changes: 23 additions & 0 deletions packages/client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,29 @@ export type CallConstructor = {
clientStore: StreamVideoWriteableStateStore;
};

export type StreamRNVideoSDKGlobals = {
callManager: {
/**
* Sets up the in call manager.
*/
setup(): void;

/**
* Starts the in call manager.
*/
start(): void;

/**
* Stops the in call manager.
*/
stop(): void;
};
};

declare global {
var streamRNVideoSDK: StreamRNVideoSDKGlobals | undefined;
}

/**
* The options to pass to {@link Call.join} method.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import com.streamvideo.reactnative.audio.utils.AudioDeviceEndpointUtils
import com.streamvideo.reactnative.audio.utils.AudioFocusUtil
import com.streamvideo.reactnative.audio.utils.AudioManagerUtil
import com.streamvideo.reactnative.audio.utils.AudioManagerUtil.Companion.getAvailableAudioDevices
import com.streamvideo.reactnative.audio.utils.AudioSetupStoreUtil
import com.streamvideo.reactnative.audio.utils.CallAudioRole
import com.streamvideo.reactnative.callmanager.ProximityManager
import com.streamvideo.reactnative.callmanager.StreamInCallManagerModule
Expand Down Expand Up @@ -90,6 +89,8 @@ class AudioDeviceManager(
@EndpointType
private var userSelectedAudioDevice: Int? = null

var enableStereo: Boolean = false

private val mAudioManager =
mReactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager

Expand All @@ -99,7 +100,6 @@ class AudioDeviceManager(
private var audioFocusLost = false

private var audioFocusUtil = AudioFocusUtil(mAudioManager, this)
private var audioSetupStoreUtil = AudioSetupStoreUtil(mReactContext, mAudioManager, this)

var callAudioRole: CallAudioRole = CallAudioRole.Communicator

Expand All @@ -113,33 +113,38 @@ class AudioDeviceManager(
mAudioManager.registerAudioDeviceCallback(this, null)
}

fun setup() {
if (callAudioRole == CallAudioRole.Communicator) {
mAudioManager.mode = AudioManager.MODE_IN_COMMUNICATION
} else {
// Audio routing is handled automatically by the system in normal media mode
// and bluetooth microphones may not work on some devices.
mAudioManager.mode = AudioManager.MODE_NORMAL
}
audioFocusUtil.setup(callAudioRole, mReactContext)
}

fun start(activity: Activity) {
runInAudioThread {
setup()
userSelectedAudioDevice = null
selectedAudioDeviceEndpoint = null
audioSetupStoreUtil.storeOriginalAudioSetup()
if (callAudioRole == CallAudioRole.Communicator) {
// Audio routing is manually controlled by the SDK in communication media mode
// and local microphone can be published
mAudioManager.mode = AudioManager.MODE_IN_COMMUNICATION
activity.volumeControlStream = AudioManager.STREAM_VOICE_CALL
bluetoothManager.start()
mAudioManager.registerAudioDeviceCallback(this, null)
updateAudioDeviceState()
proximityManager.start()
} else {
// Audio routing is handled automatically by the system in normal media mode
// and bluetooth microphones may not work on some devices.
mAudioManager.mode = AudioManager.MODE_NORMAL
activity.volumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE
}

audioSetupStoreUtil.storeOriginalAudioSetup()
audioFocusUtil.requestFocus(callAudioRole, mReactContext)
}
}

fun stop() {
fun stop(activity: Activity) {
runInAudioThread {
if (callAudioRole == CallAudioRole.Communicator) {
if (Build.VERSION.SDK_INT >= 31) {
Expand All @@ -150,7 +155,7 @@ class AudioDeviceManager(
bluetoothManager.stop()
proximityManager.stop()
}
audioSetupStoreUtil.restoreOriginalAudioSetup()
activity.volumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE
audioFocusUtil.abandonFocus()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.os.Build
import androidx.annotation.RequiresApi
import com.facebook.react.bridge.ReactContext
import com.oney.WebRTCModule.WebRTCModule
import org.webrtc.audio.JavaAudioDeviceModule
Expand All @@ -25,18 +26,31 @@ class AudioFocusUtil(
private lateinit var request: AudioFocusRequest


@RequiresApi(26)
private fun getAudioAttributes(mode: CallAudioRole): AudioAttributes {
return AudioAttributes.Builder()
.setUsage(if (mode == CallAudioRole.Communicator) AudioAttributes.USAGE_VOICE_COMMUNICATION else AudioAttributes.USAGE_MEDIA)
.setContentType(if (mode == CallAudioRole.Communicator) AudioAttributes.CONTENT_TYPE_SPEECH else AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
}

private fun setup(audioAttributes: AudioAttributes, reactContext: ReactContext) {
val webRTCModule = reactContext.getNativeModule(WebRTCModule::class.java)!!
val adm = webRTCModule.audioDeviceModule as JavaAudioDeviceModule
WebRtcAudioTrackHelper.setAudioOutputAttributes(adm, audioAttributes)
}

fun setup(mode: CallAudioRole, reactContext: ReactContext) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val audioAttributes = getAudioAttributes(mode)
setup(audioAttributes, reactContext)
}
}

fun requestFocus(mode: CallAudioRole, reactContext: ReactContext) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val audioAttributes = AudioAttributes.Builder()
.setUsage(if (mode == CallAudioRole.Communicator) AudioAttributes.USAGE_VOICE_COMMUNICATION else AudioAttributes.USAGE_MEDIA)
.setContentType(if (mode == CallAudioRole.Communicator) AudioAttributes.CONTENT_TYPE_SPEECH else AudioAttributes.CONTENT_TYPE_MUSIC)
.build()

// 1. set audio attributes to webrtc
val webRTCModule = reactContext.getNativeModule(WebRTCModule::class.java)!!
val adm = webRTCModule.audioDeviceModule as JavaAudioDeviceModule
WebRtcAudioTrackHelper.setAudioOutputAttributes(adm, audioAttributes)
val audioAttributes = getAudioAttributes(mode)
setup(audioAttributes, reactContext)

// 2. request the audio focus with the audio attributes
request = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@ class StreamInCallManagerModule(reactContext: ReactApplicationContext) :
}
}

@ReactMethod
fun setEnableStereoAudioOutput(enabled: Boolean) {
AudioDeviceManager.runInAudioThread {
if (audioManagerActivated) {
Log.e(TAG, "setAudioRole(): AudioManager is already activated and so enabling stereo audio output cannot be changed")
return@runInAudioThread
}
mAudioDeviceManager.enableStereo = enabled
}
}

@ReactMethod
fun setup() {
mAudioDeviceManager.setup()
}

@ReactMethod
fun start() {
AudioDeviceManager.runInAudioThread {
Expand All @@ -99,7 +115,9 @@ class StreamInCallManagerModule(reactContext: ReactApplicationContext) :
AudioDeviceManager.runInAudioThread {
if (audioManagerActivated) {
Log.d(TAG, "stop() mAudioDeviceManager")
mAudioDeviceManager.stop()
reactApplicationContext.currentActivity?.let {
mAudioDeviceManager.stop(it)
}
setMicrophoneMute(false)
setKeepScreenOn(false)
audioManagerActivated = false
Expand Down
4 changes: 4 additions & 0 deletions packages/react-native-sdk/ios/StreamInCallManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ @interface RCT_EXTERN_MODULE(StreamInCallManager, RCTEventEmitter)

RCT_EXTERN_METHOD(setDefaultAudioDeviceEndpointType:(NSString *)endpointType)

RCT_EXTERN_METHOD(setEnableStereoAudioOutput:(BOOL)enable)

RCT_EXTERN_METHOD(setup)

RCT_EXTERN_METHOD(start)

RCT_EXTERN_METHOD(stop)
Expand Down
Loading
Loading