diff --git a/android/build.gradle b/android/build.gradle index acdc704f5..5e66c96b0 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -127,7 +127,8 @@ def kotlin_version = getExtOrDefault('kotlinVersion') dependencies { // noinspection GradleDynamicVersion api 'com.facebook.react:react-native:+' - api 'io.agora.rtc:agora-special-full:3.6.2.70-2' + api 'io.agora.rtc:full-sdk:3.7.0' + implementation 'io.agora.rtc:full-screen-sharing:3.7.0' implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" } diff --git a/android/src/main/java/io/agora/rtc/base/BeanCovertor.kt b/android/src/main/java/io/agora/rtc/base/BeanCovertor.kt index 9b78df6a8..a0d5cc3cb 100644 --- a/android/src/main/java/io/agora/rtc/base/BeanCovertor.kt +++ b/android/src/main/java/io/agora/rtc/base/BeanCovertor.kt @@ -2,8 +2,10 @@ package io.agora.rtc.base import io.agora.rtc.AgoraMediaRecorder import io.agora.rtc.RtcEngineConfig +import io.agora.rtc.ScreenCaptureParameters import io.agora.rtc.audio.AgoraRhythmPlayerConfig import io.agora.rtc.audio.AudioRecordingConfiguration +import io.agora.rtc.audio.SpatialAudioParams import io.agora.rtc.internal.EncryptionConfig import io.agora.rtc.internal.LastmileProbeConfig import io.agora.rtc.live.LiveInjectStreamConfig @@ -156,7 +158,7 @@ fun mapToLastmileProbeConfig(map: Map<*, *>): LastmileProbeConfig { (map["probeUplink"] as? Boolean)?.let { probeUplink = it } (map["probeDownlink"] as? Boolean)?.let { probeDownlink = it } (map["expectedUplinkBitrate"] as? Number)?.let { expectedUplinkBitrate = it.toInt() } - (map["expectedDownlinkBitrate"] as? Number)?.let { expectedUplinkBitrate = it.toInt() } + (map["expectedDownlinkBitrate"] as? Number)?.let { expectedDownlinkBitrate = it.toInt() } } } @@ -367,3 +369,50 @@ fun mapToColorEnhanceOptions(map: Map<*, *>): ColorEnhanceOptions { (map["skinProtectLevel"] as? Number)?.let { skinProtectLevel = it.toFloat() } } } + +fun mapToScreenCaptureParameters(map: Map<*, *>): ScreenCaptureParameters { + return ScreenCaptureParameters().apply { + (map["captureAudio"] as? Boolean)?.let { captureAudio = it } + (map["audioParams"] as? Map<*, *>)?.let { + audioCaptureParameters = mapToAudioCaptureParameters(it) + } + (map["captureVideo"] as? Boolean)?.let { captureVideo = it } + (map["videoParams"] as? Map<*, *>)?.let { + videoCaptureParameters = mapToVideoCaptureParameters(it) + } + } +} + +fun mapToVideoCaptureParameters(map: Map<*, *>): ScreenCaptureParameters.VideoCaptureParameters { + return ScreenCaptureParameters.VideoCaptureParameters().apply { + (map["bitrate"] as? Number)?.let { bitrate = it.toInt() } + (map["frameRate"] as? Number)?.let { framerate = it.toInt() } + (map["dimensions"] as? Map<*, *>)?.let { it -> + mapToVideoDimensions(it).let { + width = it.width + height = it.height + } + } + (map["contentHint"] as? Number)?.let { contentHint = it.toInt() } + } +} + +fun mapToAudioCaptureParameters(map: Map<*, *>): ScreenCaptureParameters.AudioCaptureParameters { + return ScreenCaptureParameters.AudioCaptureParameters().apply { + (map["sampleRate"] as? Number)?.let { sampleRate = it.toInt() } + (map["channels"] as? Number)?.let { channels = it.toInt() } + (map["captureSignalVolume"] as? Number)?.let { captureSignalVolume = it.toInt() } + (map["allowCaptureCurrentApp"] as? Boolean)?.let { allowCaptureCurrentApp = it } + } +} + +fun mapToSpatialAudioParams(map: Map<*, *>): SpatialAudioParams { + return SpatialAudioParams().apply { + (map["speaker_azimuth"] as? Number)?.let { spk_azimuth = it.toDouble() } + (map["speaker_elevation"] as? Number)?.let { spk_elevation = it.toDouble() } + (map["speaker_distance"] as? Number)?.let { spk_distance = it.toDouble() } + (map["speaker_orientation"] as? Number)?.let { spk_orientation = it.toInt() } + (map["enable_blur"] as? Boolean)?.let { enable_blur = it } + (map["enable_air_absorb"] as? Boolean)?.let { enable_air_absorb = it } + } +} diff --git a/android/src/main/java/io/agora/rtc/base/RtcChannel.kt b/android/src/main/java/io/agora/rtc/base/RtcChannel.kt index d75d37731..3e3cbc48b 100644 --- a/android/src/main/java/io/agora/rtc/base/RtcChannel.kt +++ b/android/src/main/java/io/agora/rtc/base/RtcChannel.kt @@ -1,6 +1,5 @@ package io.agora.rtc.base -import io.agora.rtc.Constants import io.agora.rtc.IMetadataObserver import io.agora.rtc.RtcChannel import io.agora.rtc.RtcEngine @@ -143,8 +142,15 @@ class IRtcChannel { class RtcChannelManager( private val emit: (methodName: String, data: Map?) -> Unit ) : IRtcChannel.RtcChannelInterface { - private val rtcChannelMap = Collections.synchronizedMap(mutableMapOf()) - private val mediaObserverMap = Collections.synchronizedMap(mutableMapOf()) + companion object { + private val rtcChannelMap = Collections.synchronizedMap(mutableMapOf()) + private val mediaObserverMap = + Collections.synchronizedMap(mutableMapOf()) + + operator fun get(channelId: String): RtcChannel? { + return rtcChannelMap[channelId] + } + } fun release() { rtcChannelMap.forEach { it.value.destroy() } @@ -152,10 +158,6 @@ class RtcChannelManager( mediaObserverMap.clear() } - operator fun get(channelId: String): RtcChannel? { - return rtcChannelMap[channelId] - } - override fun create(params: Map, callback: Callback) { callback.resolve(params["engine"] as RtcEngine) { e -> e.createRtcChannel(params["channelId"] as String)?.let { @@ -179,19 +181,19 @@ class RtcChannelManager( val role = (params["role"] as Number).toInt() (params["options"] as? Map<*, *>)?.let { callback.code( - this[params["channelId"] as String]?.setClientRole( + RtcChannelManager[params["channelId"] as String]?.setClientRole( role, mapToClientRoleOptions(it) ) ) return@setClientRole } - callback.code(this[params["channelId"] as String]?.setClientRole(role)) + callback.code(RtcChannelManager[params["channelId"] as String]?.setClientRole(role)) } override fun joinChannel(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.joinChannel( + RtcChannelManager[params["channelId"] as String]?.joinChannel( params["token"] as? String, params["optionalInfo"] as? String, (params["optionalUid"] as Number).toNativeUInt(), @@ -202,7 +204,7 @@ class RtcChannelManager( override fun joinChannelWithUserAccount(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.joinChannelWithUserAccount( + RtcChannelManager[params["channelId"] as String]?.joinChannelWithUserAccount( params["token"] as? String, params["userAccount"] as String, mapToChannelMediaOptions(params["options"] as Map<*, *>) @@ -211,32 +213,32 @@ class RtcChannelManager( } override fun leaveChannel(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.leaveChannel()) + callback.code(RtcChannelManager[params["channelId"] as String]?.leaveChannel()) } override fun renewToken(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.renewToken(params["token"] as String)) + callback.code(RtcChannelManager[params["channelId"] as String]?.renewToken(params["token"] as String)) } override fun getConnectionState(params: Map, callback: Callback) { - callback.resolve(this[params["channelId"] as String]) { it.connectionState } + callback.resolve(RtcChannelManager[params["channelId"] as String]) { it.connectionState } } override fun publish(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.publish()) + callback.code(RtcChannelManager[params["channelId"] as String]?.publish()) } override fun unpublish(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.unpublish()) + callback.code(RtcChannelManager[params["channelId"] as String]?.unpublish()) } override fun getCallId(params: Map, callback: Callback) { - callback.resolve(this[params["channelId"] as String]) { it.callId } + callback.resolve(RtcChannelManager[params["channelId"] as String]) { it.callId } } override fun setAVSyncSource(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.setAVSyncSource( + RtcChannelManager[params["channelId"] as String]?.setAVSyncSource( params["channelId"] as String, (params["uid"] as Number).toNativeUInt() ) @@ -245,7 +247,7 @@ class RtcChannelManager( override fun adjustUserPlaybackSignalVolume(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.adjustUserPlaybackSignalVolume( + RtcChannelManager[params["channelId"] as String]?.adjustUserPlaybackSignalVolume( (params["uid"] as Number).toNativeUInt(), (params["volume"] as Number).toInt() ) @@ -254,7 +256,7 @@ class RtcChannelManager( override fun muteLocalAudioStream(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.muteLocalAudioStream( + RtcChannelManager[params["channelId"] as String]?.muteLocalAudioStream( params["muted"] as Boolean ) ) @@ -262,7 +264,7 @@ class RtcChannelManager( override fun muteRemoteAudioStream(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.muteRemoteAudioStream( + RtcChannelManager[params["channelId"] as String]?.muteRemoteAudioStream( (params["uid"] as Number).toNativeUInt(), params["muted"] as Boolean ) @@ -270,16 +272,20 @@ class RtcChannelManager( } override fun muteAllRemoteAudioStreams(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.muteAllRemoteAudioStreams(params["muted"] as Boolean)) + callback.code(RtcChannelManager[params["channelId"] as String]?.muteAllRemoteAudioStreams(params["muted"] as Boolean)) } override fun setDefaultMuteAllRemoteAudioStreams(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.setDefaultMuteAllRemoteAudioStreams(params["muted"] as Boolean)) + callback.code( + RtcChannelManager[params["channelId"] as String]?.setDefaultMuteAllRemoteAudioStreams( + params["muted"] as Boolean + ) + ) } override fun muteLocalVideoStream(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.muteLocalVideoStream( + RtcChannelManager[params["channelId"] as String]?.muteLocalVideoStream( params["muted"] as Boolean ) ) @@ -287,7 +293,7 @@ class RtcChannelManager( override fun muteRemoteVideoStream(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.muteRemoteVideoStream( + RtcChannelManager[params["channelId"] as String]?.muteRemoteVideoStream( (params["uid"] as Number).toNativeUInt(), params["muted"] as Boolean ) @@ -295,16 +301,20 @@ class RtcChannelManager( } override fun muteAllRemoteVideoStreams(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.muteAllRemoteVideoStreams(params["muted"] as Boolean)) + callback.code(RtcChannelManager[params["channelId"] as String]?.muteAllRemoteVideoStreams(params["muted"] as Boolean)) } override fun setDefaultMuteAllRemoteVideoStreams(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.setDefaultMuteAllRemoteVideoStreams(params["muted"] as Boolean)) + callback.code( + RtcChannelManager[params["channelId"] as String]?.setDefaultMuteAllRemoteVideoStreams( + params["muted"] as Boolean + ) + ) } override fun enableRemoteSuperResolution(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.enableRemoteSuperResolution( + RtcChannelManager[params["channelId"] as String]?.enableRemoteSuperResolution( (params["uid"] as Number).toNativeUInt(), params["enabled"] as Boolean ) @@ -313,7 +323,7 @@ class RtcChannelManager( override fun setRemoteVoicePosition(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.setRemoteVoicePosition( + RtcChannelManager[params["channelId"] as String]?.setRemoteVoicePosition( (params["uid"] as Number).toNativeUInt(), (params["pan"] as Number).toDouble(), (params["gain"] as Number).toDouble() @@ -323,7 +333,7 @@ class RtcChannelManager( override fun setLiveTranscoding(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.setLiveTranscoding( + RtcChannelManager[params["channelId"] as String]?.setLiveTranscoding( mapToLiveTranscoding( params["transcoding"] as Map<*, *> ) @@ -333,7 +343,7 @@ class RtcChannelManager( override fun addPublishStreamUrl(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.addPublishStreamUrl( + RtcChannelManager[params["channelId"] as String]?.addPublishStreamUrl( params["url"] as String, params["transcodingEnabled"] as Boolean ) @@ -341,16 +351,20 @@ class RtcChannelManager( } override fun removePublishStreamUrl(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.removePublishStreamUrl(params["url"] as String)) + callback.code(RtcChannelManager[params["channelId"] as String]?.removePublishStreamUrl(params["url"] as String)) } override fun startRtmpStreamWithoutTranscoding(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.startRtmpStreamWithoutTranscoding(params["url"] as String)) + callback.code( + RtcChannelManager[params["channelId"] as String]?.startRtmpStreamWithoutTranscoding( + params["url"] as String + ) + ) } override fun startRtmpStreamWithTranscoding(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.startRtmpStreamWithTranscoding( + RtcChannelManager[params["channelId"] as String]?.startRtmpStreamWithTranscoding( params["url"] as String, mapToLiveTranscoding(params["transcoding"] as Map<*, *>) ) @@ -359,7 +373,7 @@ class RtcChannelManager( override fun updateRtmpTranscoding(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.updateRtmpTranscoding( + RtcChannelManager[params["channelId"] as String]?.updateRtmpTranscoding( mapToLiveTranscoding( params["transcoding"] as Map<*, *> ) @@ -368,12 +382,12 @@ class RtcChannelManager( } override fun stopRtmpStream(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.stopRtmpStream(params["url"] as String)) + callback.code(RtcChannelManager[params["channelId"] as String]?.stopRtmpStream(params["url"] as String)) } override fun startChannelMediaRelay(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.startChannelMediaRelay( + RtcChannelManager[params["channelId"] as String]?.startChannelMediaRelay( mapToChannelMediaRelayConfiguration(params["channelMediaRelayConfiguration"] as Map<*, *>) ) ) @@ -381,27 +395,27 @@ class RtcChannelManager( override fun updateChannelMediaRelay(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.updateChannelMediaRelay( + RtcChannelManager[params["channelId"] as String]?.updateChannelMediaRelay( mapToChannelMediaRelayConfiguration(params["channelMediaRelayConfiguration"] as Map<*, *>) ) ) } override fun stopChannelMediaRelay(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.stopChannelMediaRelay()) + callback.code(RtcChannelManager[params["channelId"] as String]?.stopChannelMediaRelay()) } override fun pauseAllChannelMediaRelay(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.pauseAllChannelMediaRelay()) + callback.code(RtcChannelManager[params["channelId"] as String]?.pauseAllChannelMediaRelay()) } override fun resumeAllChannelMediaRelay(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.resumeAllChannelMediaRelay()) + callback.code(RtcChannelManager[params["channelId"] as String]?.resumeAllChannelMediaRelay()) } override fun setRemoteVideoStreamType(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.setRemoteVideoStreamType( + RtcChannelManager[params["channelId"] as String]?.setRemoteVideoStreamType( (params["uid"] as Number).toNativeUInt(), (params["streamType"] as Number).toInt() ) @@ -409,12 +423,16 @@ class RtcChannelManager( } override fun setRemoteDefaultVideoStreamType(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.setRemoteDefaultVideoStreamType((params["streamType"] as Number).toInt())) + callback.code( + RtcChannelManager[params["channelId"] as String]?.setRemoteDefaultVideoStreamType( + (params["streamType"] as Number).toInt() + ) + ) } override fun setRemoteUserPriority(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.setRemoteUserPriority( + RtcChannelManager[params["channelId"] as String]?.setRemoteUserPriority( (params["uid"] as Number).toNativeUInt(), (params["userPriority"] as Number).toInt() ) @@ -429,7 +447,7 @@ class RtcChannelManager( data?.toMutableMap()?.apply { put("channelId", channelId) }) } callback.code( - this[channelId]?.registerMediaMetadataObserver( + RtcChannelManager[channelId]?.registerMediaMetadataObserver( mediaObserver, IMetadataObserver.VIDEO_METADATA ) @@ -442,7 +460,7 @@ class RtcChannelManager( override fun unregisterMediaMetadataObserver(params: Map, callback: Callback) { val channelId = params["channelId"] as String callback.code( - this[channelId]?.registerMediaMetadataObserver( + RtcChannelManager[channelId]?.registerMediaMetadataObserver( null, IMetadataObserver.VIDEO_METADATA ) @@ -467,12 +485,12 @@ class RtcChannelManager( } override fun setEncryptionSecret(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.setEncryptionSecret(params["secret"] as String)) + callback.code(RtcChannelManager[params["channelId"] as String]?.setEncryptionSecret(params["secret"] as String)) } override fun setEncryptionMode(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.setEncryptionMode( + RtcChannelManager[params["channelId"] as String]?.setEncryptionMode( when ((params["encryptionMode"] as Number).toInt()) { EncryptionConfig.EncryptionMode.AES_128_XTS.value -> "aes-128-xts" EncryptionConfig.EncryptionMode.AES_128_ECB.value -> "aes-128-ecb" @@ -485,7 +503,7 @@ class RtcChannelManager( override fun enableEncryption(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.enableEncryption( + RtcChannelManager[params["channelId"] as String]?.enableEncryption( params["enabled"] as Boolean, mapToEncryptionConfig(params["config"] as Map<*, *>) ) @@ -494,7 +512,7 @@ class RtcChannelManager( override fun addInjectStreamUrl(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.addInjectStreamUrl( + RtcChannelManager[params["channelId"] as String]?.addInjectStreamUrl( params["url"] as String, mapToLiveInjectStreamConfig(params["config"] as Map<*, *>) ) @@ -502,11 +520,11 @@ class RtcChannelManager( } override fun removeInjectStreamUrl(params: Map, callback: Callback) { - callback.code(this[params["channelId"] as String]?.removeInjectStreamUrl(params["url"] as String)) + callback.code(RtcChannelManager[params["channelId"] as String]?.removeInjectStreamUrl(params["url"] as String)) } override fun createDataStream(params: Map, callback: Callback) { - val channel = this[params["channelId"] as String] + val channel = RtcChannelManager[params["channelId"] as String] (params["config"] as? Map<*, *>)?.let { config -> callback.code(channel?.createDataStream(mapToDataStreamConfig(config))) { it } return@createDataStream @@ -521,7 +539,7 @@ class RtcChannelManager( override fun sendStreamMessage(params: Map, callback: Callback) { callback.code( - this[params["channelId"] as String]?.sendStreamMessage( + RtcChannelManager[params["channelId"] as String]?.sendStreamMessage( (params["streamId"] as Number).toInt(), (params["message"] as String).toByteArray() ) diff --git a/android/src/main/java/io/agora/rtc/base/RtcChannelEvent.kt b/android/src/main/java/io/agora/rtc/base/RtcChannelEvent.kt index 45cafae95..058745d4b 100644 --- a/android/src/main/java/io/agora/rtc/base/RtcChannelEvent.kt +++ b/android/src/main/java/io/agora/rtc/base/RtcChannelEvent.kt @@ -45,6 +45,7 @@ class RtcChannelEvents { const val UserSuperResolutionEnabled = "UserSuperResolutionEnabled" const val ProxyConnected = "ProxyConnected" const val ClientRoleChangeFailed = "ClientRoleChangeFailed" + const val FirstRemoteVideoFrame = "FirstRemoteVideoFrame" fun toMap(): Map { return hashMapOf( @@ -85,7 +86,8 @@ class RtcChannelEvents { "RtmpStreamingEvent" to RtmpStreamingEvent, "UserSuperResolutionEnabled" to UserSuperResolutionEnabled, "ProxyConnected" to ProxyConnected, - "ClientRoleChangeFailed" to ClientRoleChangeFailed + "ClientRoleChangeFailed" to ClientRoleChangeFailed, + "FirstRemoteVideoFrame" to FirstRemoteVideoFrame ) } } @@ -466,4 +468,21 @@ class RtcChannelEventHandler( override fun onClientRoleChangeFailed(rtcChannel: RtcChannel?, reason: Int, currentRole: Int) { callback(RtcChannelEvents.ClientRoleChangeFailed, rtcChannel, reason, currentRole) } + + override fun onFirstRemoteVideoFrame( + rtcChannel: RtcChannel?, + uid: Int, + width: Int, + height: Int, + elapsed: Int + ) { + callback( + RtcChannelEvents.FirstRemoteVideoFrame, + rtcChannel, + uid.toUInt().toLong(), + width, + height, + elapsed + ) + } } diff --git a/android/src/main/java/io/agora/rtc/base/RtcEngine.kt b/android/src/main/java/io/agora/rtc/base/RtcEngine.kt index 77c6f1ec2..936ba444d 100644 --- a/android/src/main/java/io/agora/rtc/base/RtcEngine.kt +++ b/android/src/main/java/io/agora/rtc/base/RtcEngine.kt @@ -3,6 +3,7 @@ package io.agora.rtc.base import android.content.Context import io.agora.rtc.* import io.agora.rtc.internal.EncryptionConfig +import io.agora.rtc.mediaio.AgoraDefaultSource import io.agora.rtc.models.UserInfo class IRtcEngine { @@ -155,6 +156,12 @@ class IRtcEngine { fun setLowLightEnhanceOptions(params: Map, callback: Callback) fun setColorEnhanceOptions(params: Map, callback: Callback) + + fun startScreenCapture(params: Map, callback: Callback) + + fun stopScreenCapture(callback: Callback) + + fun updateScreenCaptureParameters(params: Map, callback: Callback) } interface RtcAudioMixingInterface { @@ -251,12 +258,18 @@ class IRtcEngine { fun setAudioEffectParameters(params: Map, callback: Callback) fun setVoiceBeautifierParameters(params: Map, callback: Callback) + + fun enableLocalVoicePitchCallback(params: Map, callback: Callback) } interface RtcVoicePositionInterface { fun enableSoundPositionIndication(params: Map, callback: Callback) fun setRemoteVoicePosition(params: Map, callback: Callback) + + fun enableSpatialAudio(params: Map, callback: Callback) + + fun setRemoteUserSpatialAudioParams(params: Map, callback: Callback) } interface RtcPublishStreamInterface { @@ -430,8 +443,11 @@ open class RtcEngineManager( private val emit: (methodName: String, data: Map?) -> Unit, private val rtcEngineFactory: RtcEngineFactory = RtcEngineFactory() ) : IRtcEngine.RtcEngineInterface { - var engine: RtcEngine? = null - private set + companion object { + var engine: RtcEngine? = null + private set + } + private var eventHandler: RtcEngineEventHandler = RtcEngineEventHandler { methodName, data -> emit(methodName, data) } @@ -645,6 +661,27 @@ open class RtcEngineManager( ) } + override fun startScreenCapture(params: Map, callback: Callback) { + callback.code(engine?.startScreenCapture(mapToScreenCaptureParameters(params["parameters"] as Map<*, *>))) + } + + override fun stopScreenCapture(callback: Callback) { + engine?.setVideoSource(AgoraDefaultSource()) + callback.code(engine?.stopScreenCapture()) + } + + override fun updateScreenCaptureParameters(params: Map, callback: Callback) { + val screenCaptureParameters = + mapToScreenCaptureParameters(params["parameters"] as Map<*, *>) + callback.code( + engine?.updateScreenCaptureParameters( + screenCaptureParameters.captureVideo, + screenCaptureParameters.captureAudio, + screenCaptureParameters.videoCaptureParameters + ) + ) + } + override fun registerLocalUserAccount(params: Map, callback: Callback) { callback.code( engine?.registerLocalUserAccount( @@ -1062,6 +1099,19 @@ open class RtcEngineManager( callback.code(-Constants.ERR_NOT_SUPPORTED) } + override fun enableSpatialAudio(params: Map, callback: Callback) { + callback.code(engine?.enableSpatialAudio(params["enabled"] as Boolean)) + } + + override fun setRemoteUserSpatialAudioParams(params: Map, callback: Callback) { + callback.code( + engine?.setRemoteUserSpatialAudioParams( + (params["uid"] as Number).toNativeUInt(), + mapToSpatialAudioParams(params["params"] as Map<*, *>) + ) + ) + } + override fun setLocalVoiceChanger(params: Map, callback: Callback) { callback.code(engine?.setLocalVoiceChanger((params["voiceChanger"] as Number).toInt())) } @@ -1124,6 +1174,10 @@ open class RtcEngineManager( ) } + override fun enableLocalVoicePitchCallback(params: Map, callback: Callback) { + callback.code(engine?.enableLocalVoicePitchCallback((params["interval"] as Number).toInt())) + } + override fun enableSoundPositionIndication(params: Map, callback: Callback) { callback.code(engine?.enableSoundPositionIndication(params["enabled"] as Boolean)) } diff --git a/android/src/main/java/io/agora/rtc/base/RtcEngineEvent.kt b/android/src/main/java/io/agora/rtc/base/RtcEngineEvent.kt index 01097a270..15bffff55 100644 --- a/android/src/main/java/io/agora/rtc/base/RtcEngineEvent.kt +++ b/android/src/main/java/io/agora/rtc/base/RtcEngineEvent.kt @@ -3,7 +3,9 @@ package io.agora.rtc.base import android.graphics.Rect import androidx.annotation.IntRange import io.agora.rtc.AgoraMediaRecorder +import io.agora.rtc.Constants import io.agora.rtc.IRtcEngineEventHandler +import io.agora.rtc.mediaio.AgoraDefaultSource import io.agora.rtc.models.UserInfo class RtcEngineEvents { @@ -97,6 +99,7 @@ class RtcEngineEvents { const val WlAccMessage = "WlAccMessage" const val WlAccStats = "WlAccStats" const val ClientRoleChangeFailed = "ClientRoleChangeFailed" + const val LocalVoicePitchInHz = "LocalVoicePitchInHz" fun toMap(): Map { return hashMapOf( @@ -325,6 +328,9 @@ class RtcEngineEventHandler( @Annotations.AgoraLocalVideoStreamState localVideoState: Int, @Annotations.AgoraLocalVideoStreamError error: Int ) { + if (error == Constants.ERR_SCREEN_CAPTURE_PERMISSION_DENIED) { + RtcEngineManager.engine?.setVideoSource(AgoraDefaultSource()) + } callback(RtcEngineEvents.LocalVideoStateChanged, localVideoState, error) } @@ -771,4 +777,8 @@ class RtcEngineEventHandler( override fun onClientRoleChangeFailed(reason: Int, currentRole: Int) { callback(RtcEngineEvents.ClientRoleChangeFailed, reason, currentRole) } + + override fun onLocalVoicePitchInHz(pitchInHz: Int) { + callback(RtcEngineEvents.LocalVoicePitchInHz, pitchInHz) + } } diff --git a/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcChannelModule.kt b/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcChannelModule.kt index bdd2fe4a8..226eed64e 100644 --- a/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcChannelModule.kt +++ b/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcChannelModule.kt @@ -3,10 +3,9 @@ package io.agora.rtc.react import com.facebook.react.bridge.* import com.facebook.react.module.annotations.ReactModule import com.facebook.react.modules.core.DeviceEventManagerModule -import io.agora.rtc.RtcChannel -import io.agora.rtc.RtcEngine import io.agora.rtc.base.RtcChannelEventHandler import io.agora.rtc.base.RtcChannelManager +import io.agora.rtc.base.RtcEngineManager import io.agora.rtc.react.RCTAgoraRtcChannelModule.Companion.REACT_CLASS @ReactModule(name = REACT_CLASS) @@ -39,14 +38,6 @@ class RCTAgoraRtcChannelModule( .emit("${RtcChannelEventHandler.PREFIX}$methodName", Arguments.makeNativeMap(data)) } - private fun engine(): RtcEngine? { - return reactApplicationContext.getNativeModule(RCTAgoraRtcEngineModule::class.java)?.engine() - } - - fun channel(channelId: String): RtcChannel? { - return manager[channelId] - } - @ReactMethod fun addListener(eventName: String) { // Keep: Required for RN built in Event Emitter Calls. @@ -65,7 +56,7 @@ class RCTAgoraRtcChannelModule( val parameters = mutableListOf() params?.toHashMap()?.toMutableMap()?.let { if (methodName == "create") { - it["engine"] = engine() + it["engine"] = RtcEngineManager.engine } parameters.add(it) } diff --git a/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcEngineModule.kt b/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcEngineModule.kt index c9ce6a409..b7e789e11 100644 --- a/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcEngineModule.kt +++ b/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcEngineModule.kt @@ -3,7 +3,6 @@ package io.agora.rtc.react import com.facebook.react.bridge.* import com.facebook.react.module.annotations.ReactModule import com.facebook.react.modules.core.DeviceEventManagerModule -import io.agora.rtc.RtcEngine import io.agora.rtc.base.RtcEngineEventHandler import io.agora.rtc.base.RtcEngineManager import io.agora.rtc.react.RCTAgoraRtcEngineModule.Companion.REACT_CLASS @@ -38,10 +37,6 @@ class RCTAgoraRtcEngineModule( .emit("${RtcEngineEventHandler.PREFIX}$methodName", Arguments.makeNativeMap(data)) } - fun engine(): RtcEngine? { - return manager.engine - } - @ReactMethod fun addListener(eventName: String) { // Keep: Required for RN built in Event Emitter Calls. diff --git a/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcSurfaceViewManager.kt b/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcSurfaceViewManager.kt index 99110a920..80b89f16b 100644 --- a/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcSurfaceViewManager.kt +++ b/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcSurfaceViewManager.kt @@ -4,8 +4,8 @@ import com.facebook.react.bridge.ReadableMap import com.facebook.react.uimanager.SimpleViewManager import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp -import io.agora.rtc.RtcChannel -import io.agora.rtc.RtcEngine +import io.agora.rtc.base.RtcChannelManager +import io.agora.rtc.base.RtcEngineManager import io.agora.rtc.base.RtcSurfaceView class RCTAgoraRtcSurfaceViewManager : SimpleViewManager() { @@ -42,26 +42,18 @@ class RCTAgoraRtcSurfaceViewManager : SimpleViewManager() { @ReactProp(name = "data") fun setData(view: RtcSurfaceView, data: ReadableMap) { data.toHashMap().let { map -> - val channel = (map["channelId"] as? String)?.let { getChannel(it) } - getEngine()?.let { view.setData(it, channel, map["uid"] as Number) } + val channel = (map["channelId"] as? String)?.let { RtcChannelManager[it] } + RtcEngineManager.engine?.let { view.setData(it, channel, map["uid"] as Number) } } } @ReactProp(name = "renderMode") fun setRenderMode(view: RtcSurfaceView, renderMode: Int) { - getEngine()?.let { view.setRenderMode(it, renderMode) } + RtcEngineManager.engine?.let { view.setRenderMode(it, renderMode) } } @ReactProp(name = "mirrorMode") fun setMirrorMode(view: RtcSurfaceView, mirrorMode: Int) { - getEngine()?.let { view.setMirrorMode(it, mirrorMode) } - } - - private fun getEngine(): RtcEngine? { - return reactContext?.getNativeModule(RCTAgoraRtcEngineModule::class.java)?.engine() - } - - private fun getChannel(channelId: String): RtcChannel? { - return reactContext?.getNativeModule(RCTAgoraRtcChannelModule::class.java)?.channel(channelId) + RtcEngineManager.engine?.let { view.setMirrorMode(it, mirrorMode) } } } diff --git a/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcTextureViewManager.kt b/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcTextureViewManager.kt index c1b841ded..532de7ea2 100644 --- a/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcTextureViewManager.kt +++ b/android/src/main/java/io/agora/rtc/react/RCTAgoraRtcTextureViewManager.kt @@ -4,8 +4,8 @@ import com.facebook.react.bridge.ReadableMap import com.facebook.react.uimanager.SimpleViewManager import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp -import io.agora.rtc.RtcChannel -import io.agora.rtc.RtcEngine +import io.agora.rtc.base.RtcChannelManager +import io.agora.rtc.base.RtcEngineManager import io.agora.rtc.base.RtcTextureView class RCTAgoraRtcTextureViewManager : SimpleViewManager() { @@ -32,26 +32,18 @@ class RCTAgoraRtcTextureViewManager : SimpleViewManager() { @ReactProp(name = "data") fun setData(view: RtcTextureView, data: ReadableMap) { data.toHashMap().let { map -> - val channel = (map["channelId"] as? String)?.let { getChannel(it) } - getEngine()?.let { view.setData(it, channel, map["uid"] as Number) } + val channel = (map["channelId"] as? String)?.let { RtcChannelManager[it] } + RtcEngineManager.engine?.let { view.setData(it, channel, map["uid"] as Number) } } } @ReactProp(name = "renderMode") fun setRenderMode(view: RtcTextureView, renderMode: Int) { - getEngine()?.let { view.setRenderMode(it, renderMode) } + RtcEngineManager.engine?.let { view.setRenderMode(it, renderMode) } } @ReactProp(name = "mirrorMode") fun setMirrorMode(view: RtcTextureView, mirrorMode: Int) { - getEngine()?.let { view.setMirrorMode(it, mirrorMode) } - } - - private fun getEngine(): RtcEngine? { - return reactContext?.getNativeModule(RCTAgoraRtcEngineModule::class.java)?.engine() - } - - private fun getChannel(channelId: String): RtcChannel? { - return reactContext?.getNativeModule(RCTAgoraRtcChannelModule::class.java)?.channel(channelId) + RtcEngineManager.engine?.let { view.setMirrorMode(it, mirrorMode) } } } diff --git a/example/ios/AgoraExample.xcodeproj/project.pbxproj b/example/ios/AgoraExample.xcodeproj/project.pbxproj index 7304831e8..bfe10cf03 100644 --- a/example/ios/AgoraExample.xcodeproj/project.pbxproj +++ b/example/ios/AgoraExample.xcodeproj/project.pbxproj @@ -7,17 +7,46 @@ objects = { /* Begin PBXBuildFile section */ + 0B0236800265C4775C5371CD /* libPods-ScreenSharing.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CA3185BA40542F3555405314 /* libPods-ScreenSharing.a */; }; 0D1336C0461A88D01186E375 /* libPods-AgoraExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BCEA90A70F4BEAD7E9FA28B2 /* libPods-AgoraExample.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 20F357B024636CDF00C146DC /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20F357AF24636CDF00C146DC /* File.swift */; }; F550788525DF9BB20027CF64 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F550788425DF9BB20027CF64 /* LaunchScreen.storyboard */; }; + F55AC4EA2816A4AA00FAF1D9 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F55AC4E92816A4AA00FAF1D9 /* ReplayKit.framework */; }; + F55AC4EE2816A4AA00FAF1D9 /* SampleHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = F55AC4ED2816A4AA00FAF1D9 /* SampleHandler.m */; }; + F55AC4F32816A4AA00FAF1D9 /* ScreenSharing.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F55AC4E82816A4AA00FAF1D9 /* ScreenSharing.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; F5A97F0625D3B42C00263532 /* Sound_Horizon.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = F5A97ED425D3ADDC00263532 /* Sound_Horizon.mp3 */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + F55AC4F02816A4AA00FAF1D9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F55AC4E72816A4AA00FAF1D9; + remoteInfo = ScreenSharing; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + F55AC4F22816A4AA00FAF1D9 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + F55AC4F32816A4AA00FAF1D9 /* ScreenSharing.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + 073FF7CA576A950D20C4BF77 /* Pods-ScreenSharing.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ScreenSharing.release.xcconfig"; path = "Target Support Files/Pods-ScreenSharing/Pods-ScreenSharing.release.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* AgoraExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AgoraExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = AgoraExample/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = AgoraExample/AppDelegate.m; sourceTree = ""; }; @@ -29,8 +58,15 @@ 4D7192F03A36A017E887435B /* Pods-AgoraExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AgoraExample.release.xcconfig"; path = "Target Support Files/Pods-AgoraExample/Pods-AgoraExample.release.xcconfig"; sourceTree = ""; }; 871719007ECC5EAD276C345C /* Pods-AgoraExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AgoraExample.debug.xcconfig"; path = "Target Support Files/Pods-AgoraExample/Pods-AgoraExample.debug.xcconfig"; sourceTree = ""; }; BCEA90A70F4BEAD7E9FA28B2 /* libPods-AgoraExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AgoraExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + CA3185BA40542F3555405314 /* libPods-ScreenSharing.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ScreenSharing.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + DDCC2311CA77BA51D24DD629 /* Pods-ScreenSharing.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ScreenSharing.debug.xcconfig"; path = "Target Support Files/Pods-ScreenSharing/Pods-ScreenSharing.debug.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; F550788425DF9BB20027CF64 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = AgoraExample/LaunchScreen.storyboard; sourceTree = ""; }; + F55AC4E82816A4AA00FAF1D9 /* ScreenSharing.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ScreenSharing.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + F55AC4E92816A4AA00FAF1D9 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; }; + F55AC4EC2816A4AA00FAF1D9 /* SampleHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleHandler.h; sourceTree = ""; }; + F55AC4ED2816A4AA00FAF1D9 /* SampleHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleHandler.m; sourceTree = ""; }; + F55AC4EF2816A4AA00FAF1D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F5A97ED425D3ADDC00263532 /* Sound_Horizon.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Sound_Horizon.mp3; sourceTree = ""; }; /* End PBXFileReference section */ @@ -43,6 +79,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F55AC4E52816A4AA00FAF1D9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F55AC4EA2816A4AA00FAF1D9 /* ReplayKit.framework in Frameworks */, + 0B0236800265C4775C5371CD /* libPods-ScreenSharing.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -65,6 +110,8 @@ children = ( 871719007ECC5EAD276C345C /* Pods-AgoraExample.debug.xcconfig */, 4D7192F03A36A017E887435B /* Pods-AgoraExample.release.xcconfig */, + DDCC2311CA77BA51D24DD629 /* Pods-ScreenSharing.debug.xcconfig */, + 073FF7CA576A950D20C4BF77 /* Pods-ScreenSharing.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -74,6 +121,8 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, BCEA90A70F4BEAD7E9FA28B2 /* libPods-AgoraExample.a */, + F55AC4E92816A4AA00FAF1D9 /* ReplayKit.framework */, + CA3185BA40542F3555405314 /* libPods-ScreenSharing.a */, ); name = Frameworks; sourceTree = ""; @@ -92,6 +141,7 @@ 20F357AE24636CDF00C146DC /* AgoraExample-Bridging-Header.h */, 13B07FAE1A68108700A75B9A /* AgoraExample */, 832341AE1AAA6A7D00B99B32 /* Libraries */, + F55AC4EB2816A4AA00FAF1D9 /* ScreenSharing */, 83CBBA001A601CBA00E9B192 /* Products */, 2D16E6871FA4F8E400B85C8A /* Frameworks */, F5A97ED525D3ADEF00263532 /* Resources */, @@ -106,10 +156,21 @@ isa = PBXGroup; children = ( 13B07F961A680F5B00A75B9A /* AgoraExample.app */, + F55AC4E82816A4AA00FAF1D9 /* ScreenSharing.appex */, ); name = Products; sourceTree = ""; }; + F55AC4EB2816A4AA00FAF1D9 /* ScreenSharing */ = { + isa = PBXGroup; + children = ( + F55AC4EC2816A4AA00FAF1D9 /* SampleHandler.h */, + F55AC4ED2816A4AA00FAF1D9 /* SampleHandler.m */, + F55AC4EF2816A4AA00FAF1D9 /* Info.plist */, + ); + path = ScreenSharing; + sourceTree = ""; + }; F5A97ED525D3ADEF00263532 /* Resources */ = { isa = PBXGroup; children = ( @@ -133,16 +194,36 @@ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 784B7493D27387E7554188B3 /* [CP] Embed Pods Frameworks */, 6D3DD787D9DAA9B0C5163B24 /* [CP] Copy Pods Resources */, + F55AC4F22816A4AA00FAF1D9 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + F55AC4F12816A4AA00FAF1D9 /* PBXTargetDependency */, ); name = AgoraExample; productName = AgoraExample; productReference = 13B07F961A680F5B00A75B9A /* AgoraExample.app */; productType = "com.apple.product-type.application"; }; + F55AC4E72816A4AA00FAF1D9 /* ScreenSharing */ = { + isa = PBXNativeTarget; + buildConfigurationList = F55AC4F62816A4AA00FAF1D9 /* Build configuration list for PBXNativeTarget "ScreenSharing" */; + buildPhases = ( + 498DB0053E2BE90DCE13E975 /* [CP] Check Pods Manifest.lock */, + F55AC4E42816A4AA00FAF1D9 /* Sources */, + F55AC4E52816A4AA00FAF1D9 /* Frameworks */, + F55AC4E62816A4AA00FAF1D9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ScreenSharing; + productName = ScreenSharing; + productReference = F55AC4E82816A4AA00FAF1D9 /* ScreenSharing.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -157,6 +238,11 @@ LastSwiftMigration = 1110; ProvisioningStyle = Manual; }; + F55AC4E72816A4AA00FAF1D9 = { + CreatedOnToolsVersion = 13.3.1; + DevelopmentTeam = PV44H27855; + ProvisioningStyle = Manual; + }; }; }; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AgoraExample" */; @@ -174,6 +260,7 @@ projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* AgoraExample */, + F55AC4E72816A4AA00FAF1D9 /* ScreenSharing */, ); }; /* End PBXProject section */ @@ -189,6 +276,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F55AC4E62816A4AA00FAF1D9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -206,6 +300,28 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; + 498DB0053E2BE90DCE13E975 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ScreenSharing-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 6D3DD787D9DAA9B0C5163B24 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -231,21 +347,21 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-AgoraExample/Pods-AgoraExample-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/AINS/AgoraAIDenoiseExtension.framework/AgoraAIDenoiseExtension", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/AV1Dec/AgoraDav1dExtension.framework/AgoraDav1dExtension", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/ContentInspect/AgoraCIExtension.framework/AgoraCIExtension", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/FullAudioFormat/AgoraFullAudioFormatExtension.framework/AgoraFullAudioFormatExtension", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/JND/AgoraJNDExtension.framework/AgoraJNDExtension", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/ROIEnc/AgoraFDExtension.framework/AgoraFDExtension", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/ReplayKit/AgoraReplayKitExtension.framework/AgoraReplayKitExtension", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/RtcBasic/AgoraCore.framework/AgoraCore", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/RtcBasic/AgoraRtcKit.framework/AgoraRtcKit", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/RtcBasic/AgoraSoundTouch.framework/AgoraSoundTouch", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/RtcBasic/Agorafdkaac.framework/Agorafdkaac", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/RtcBasic/Agoraffmpeg.framework/Agoraffmpeg", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/SuperResolution/AgoraSuperResolutionExtension.framework/AgoraSuperResolutionExtension", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/VideoPreprocess/AgoraVideoProcessExtension.framework/AgoraVideoProcessExtension", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_Special_iOS/VirtualBackground/AgoraVideoSegmentationExtension.framework/AgoraVideoSegmentationExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/AINS/AgoraAIDenoiseExtension.framework/AgoraAIDenoiseExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/AV1Dec/AgoraDav1dExtension.framework/AgoraDav1dExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/ContentInspect/AgoraCIExtension.framework/AgoraCIExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/FullAudioFormat/AgoraFullAudioFormatExtension.framework/AgoraFullAudioFormatExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/ROIEnc/AgoraFDExtension.framework/AgoraFDExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/ReplayKit/AgoraReplayKitExtension.framework/AgoraReplayKitExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/RtcBasic/AgoraCore.framework/AgoraCore", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/RtcBasic/AgoraRtcKit.framework/AgoraRtcKit", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/RtcBasic/AgoraSoundTouch.framework/AgoraSoundTouch", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/RtcBasic/Agorafdkaac.framework/Agorafdkaac", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/RtcBasic/Agoraffmpeg.framework/Agoraffmpeg", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/SpatialAudio/AgoraSpatialAudioExtension.framework/AgoraSpatialAudioExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/SuperResolution/AgoraSuperResolutionExtension.framework/AgoraSuperResolutionExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/VideoPreprocess/AgoraVideoProcessExtension.framework/AgoraVideoProcessExtension", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/AgoraRtcEngine_iOS/VirtualBackground/AgoraVideoSegmentationExtension.framework/AgoraVideoSegmentationExtension", "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", ); name = "[CP] Embed Pods Frameworks"; @@ -254,7 +370,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraDav1dExtension.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraCIExtension.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraFullAudioFormatExtension.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraJNDExtension.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraFDExtension.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraReplayKitExtension.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraCore.framework", @@ -262,6 +377,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraSoundTouch.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Agorafdkaac.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Agoraffmpeg.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraSpatialAudioExtension.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraSuperResolutionExtension.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraVideoProcessExtension.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AgoraVideoSegmentationExtension.framework", @@ -326,8 +442,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F55AC4E42816A4AA00FAF1D9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F55AC4EE2816A4AA00FAF1D9 /* SampleHandler.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + F55AC4F12816A4AA00FAF1D9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F55AC4E72816A4AA00FAF1D9 /* ScreenSharing */; + targetProxy = F55AC4F02816A4AA00FAF1D9 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; @@ -357,6 +489,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "AgoraExample-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -387,6 +520,7 @@ PROVISIONING_PROFILE_SPECIFIER = AgoraQA2021; SWIFT_OBJC_BRIDGING_HEADER = "AgoraExample-Bridging-Header.h"; SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; @@ -492,6 +626,76 @@ }; name = Release; }; + F55AC4F42816A4AA00FAF1D9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DDCC2311CA77BA51D24DD629 /* Pods-ScreenSharing.debug.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = PV44H27855; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ScreenSharing/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = ScreenSharing; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Facebook. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = io.agora.react.AgoraExample.ScreenSharing; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = AgoraQA2021; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F55AC4F52816A4AA00FAF1D9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 073FF7CA576A950D20C4BF77 /* Pods-ScreenSharing.release.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = PV44H27855; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ScreenSharing/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = ScreenSharing; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Facebook. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = io.agora.react.AgoraExample.ScreenSharing; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = AgoraQA2021; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -513,6 +717,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F55AC4F62816A4AA00FAF1D9 /* Build configuration list for PBXNativeTarget "ScreenSharing" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F55AC4F42816A4AA00FAF1D9 /* Debug */, + F55AC4F52816A4AA00FAF1D9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; diff --git a/example/ios/AgoraExample/Info.plist b/example/ios/AgoraExample/Info.plist index 7361dfb89..268ed2575 100644 --- a/example/ios/AgoraExample/Info.plist +++ b/example/ios/AgoraExample/Info.plist @@ -43,6 +43,11 @@ NSMicrophoneUsageDescription Microphone + UIBackgroundModes + + audio + voip + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/example/ios/Podfile b/example/ios/Podfile index e7ec5d0c0..6c3bb608c 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -24,3 +24,7 @@ target 'AgoraExample' do react_native_post_install(installer) end end + +target 'ScreenSharing' do + pod 'AgoraRtcEngine_iOS', '3.7.0' +end diff --git a/example/ios/ScreenSharing/Info.plist b/example/ios/ScreenSharing/Info.plist new file mode 100644 index 000000000..729de36e4 --- /dev/null +++ b/example/ios/ScreenSharing/Info.plist @@ -0,0 +1,15 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.broadcast-services-upload + NSExtensionPrincipalClass + AgoraReplayKitHandler + RPBroadcastProcessMode + RPBroadcastProcessModeSampleBuffer + + + diff --git a/example/ios/ScreenSharing/SampleHandler.h b/example/ios/ScreenSharing/SampleHandler.h new file mode 100644 index 000000000..1501503c2 --- /dev/null +++ b/example/ios/ScreenSharing/SampleHandler.h @@ -0,0 +1,14 @@ +// +// SampleHandler.h +// ScreenSharing +// +// Created by LXH on 2022/4/25. +// Copyright © 2022 Facebook. All rights reserved. +// + +#import +#import + +@interface SampleHandler : RPBroadcastSampleHandler + +@end diff --git a/example/ios/ScreenSharing/SampleHandler.m b/example/ios/ScreenSharing/SampleHandler.m new file mode 100644 index 000000000..cb02b89c6 --- /dev/null +++ b/example/ios/ScreenSharing/SampleHandler.m @@ -0,0 +1,75 @@ +// +// SampleHandler.m +// ScreenSharing +// +// Created by LXH on 2022/4/25. +// Copyright © 2022 Facebook. All rights reserved. +// + + +#import "SampleHandler.h" + +@implementation SampleHandler + +- (void)broadcastStartedWithSetupInfo:(NSDictionary *)setupInfo { + // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional. + [[AgoraReplayKitExt shareInstance] start:self]; +} + +- (void)broadcastPaused { + // User has requested to pause the broadcast. Samples will stop being delivered. + NSLog(@"broadcastPaused"); + [[AgoraReplayKitExt shareInstance] pause]; +} + +- (void)broadcastResumed { + // User has requested to resume the broadcast. Samples delivery will resume. + NSLog(@"broadcastResumed"); + [[AgoraReplayKitExt shareInstance] resume]; +} + +- (void)broadcastFinished { + // User has requested to finish the broadcast. + NSLog(@"broadcastFinished"); + [[AgoraReplayKitExt shareInstance] stop]; +} + +- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType { + [[AgoraReplayKitExt shareInstance] pushSampleBuffer:sampleBuffer withType:sampleBufferType]; +} + +#pragma mark - AgoraReplayKitExtDelegate + +- (void)broadcastFinished:(AgoraReplayKitExt *_Nonnull)broadcast reason:(AgoraReplayKitExtReason)reason { + switch (reason) { + case AgoraReplayKitExtReasonInitiativeStop: { + // NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"Host app stop srceen capture"}; + // NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:userInfo]; + // [self finishBroadcastWithError:error]; + NSLog(@"AgoraReplayKitExtReasonInitiativeStop"); + break; + } + case AgoraReplayKitExtReasonConnectFail: + { + // NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"Connect host app fail need startScreenCapture in host app"}; + // NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:userInfo]; + // [self finishBroadcastWithError:error]; + NSLog(@"AgoraReplayKitExReasonConnectFail"); + } + break; + + case AgoraReplayKitExtReasonDisconnect: + { + // NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"disconnect with host app"}; + // NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:userInfo]; + // [self finishBroadcastWithError:error]; + NSLog(@"AgoraReplayKitExReasonDisconnect"); + } + break; + default: + break; + } + } + +@end + diff --git a/example/src/examples/basic/JoinChannelAudio/Item.tsx b/example/src/components/Item.tsx similarity index 97% rename from example/src/examples/basic/JoinChannelAudio/Item.tsx rename to example/src/components/Item.tsx index c2c038d83..498f1ff89 100644 --- a/example/src/examples/basic/JoinChannelAudio/Item.tsx +++ b/example/src/components/Item.tsx @@ -18,11 +18,11 @@ const styles = StyleSheet.create({ flexDirection: 'column', width: '100%', justifyContent: 'center', - alignItems: 'flex-start', + alignItems: 'flex-end', marginVertical: 10, }, slider: { - width: '35%', + width: '100%', height: 40, }, top: { diff --git a/example/src/examples/advanced/ChannelMediaRelay/ChannelMediaRelay.tsx b/example/src/examples/advanced/ChannelMediaRelay/ChannelMediaRelay.tsx index fa4225d75..7d46b97f9 100644 --- a/example/src/examples/advanced/ChannelMediaRelay/ChannelMediaRelay.tsx +++ b/example/src/examples/advanced/ChannelMediaRelay/ChannelMediaRelay.tsx @@ -108,7 +108,12 @@ export default class ChannelMediaRelay extends Component<{}, State, any> { this._engine?.addListener('LeaveChannel', (stats) => { console.info('LeaveChannel', stats); // RtcLocalView.SurfaceView must render after engine init and channel join - this.setState({ isJoined: false, remoteUid: [], isRelaying: false }); + this.setState({ + isJoined: false, + remoteUid: [], + anotherChannelName: undefined, + isRelaying: false, + }); }); this._engine?.addListener('UserJoined', (uid, elapsed) => { console.info('UserJoined', uid, elapsed); @@ -252,6 +257,7 @@ const styles = StyleSheet.create({ input: { borderColor: 'gray', borderWidth: 1, + color: 'black', }, videoContainer: { width: '100%', diff --git a/example/src/examples/advanced/ScreenSharing/ScreenSharing.tsx b/example/src/examples/advanced/ScreenSharing/ScreenSharing.tsx new file mode 100644 index 000000000..807117ba5 --- /dev/null +++ b/example/src/examples/advanced/ScreenSharing/ScreenSharing.tsx @@ -0,0 +1,223 @@ +import React, { Component } from 'react'; +import { + Button, + PermissionsAndroid, + Platform, + ScrollView, + StyleSheet, + TextInput, + View, +} from 'react-native'; + +import RtcEngine, { + ChannelProfile, + ClientRole, + LocalVideoStreamError, + RtcEngineContext, + RtcLocalView, + RtcRemoteView, + VideoRenderMode, +} from 'react-native-agora'; + +const config = require('../../../config/agora.config.json'); + +interface State { + channelId: string; + isJoined: boolean; + remoteUid: number[]; + isScreenSharing: boolean; +} + +export default class JoinChannelVideo extends Component<{}, State, any> { + _engine: RtcEngine | undefined; + + constructor(props: {}) { + super(props); + this.state = { + channelId: config.channelId, + isJoined: false, + remoteUid: [], + isScreenSharing: false, + }; + } + + UNSAFE_componentWillMount() { + this._initEngine(); + } + + componentWillUnmount() { + this._engine?.destroy(); + } + + _initEngine = async () => { + this._engine = await RtcEngine.createWithContext( + new RtcEngineContext(config.appId) + ); + this._addListeners(); + + await this._engine.enableVideo(); + await this._engine.startPreview(); + await this._engine.setChannelProfile(ChannelProfile.LiveBroadcasting); + await this._engine.setClientRole(ClientRole.Broadcaster); + }; + + _addListeners = () => { + this._engine?.addListener('Warning', (warningCode) => { + console.info('Warning', warningCode); + }); + this._engine?.addListener('Error', (errorCode) => { + console.info('Error', errorCode); + }); + this._engine?.addListener('JoinChannelSuccess', (channel, uid, elapsed) => { + console.info('JoinChannelSuccess', channel, uid, elapsed); + this.setState({ isJoined: true }); + }); + this._engine?.addListener('LeaveChannel', (stats) => { + console.info('LeaveChannel', stats); + this.setState({ isJoined: false, remoteUid: [] }); + }); + this._engine?.addListener('UserJoined', (uid, elapsed) => { + console.info('UserJoined', uid, elapsed); + this.setState({ remoteUid: [...this.state.remoteUid, uid] }); + }); + this._engine?.addListener('UserOffline', (uid, reason) => { + console.info('UserOffline', uid, reason); + this.setState({ + remoteUid: this.state.remoteUid.filter((value) => value !== uid), + }); + }); + this._engine?.addListener( + 'LocalVideoStateChanged', + (localVideoState, error) => { + console.info('LocalVideoStateChanged', localVideoState, error); + switch (error) { + case LocalVideoStreamError.ExtensionCaptureStarted: + this.setState({ isScreenSharing: true }); + break; + case LocalVideoStreamError.ExtensionCaptureStoped: + case LocalVideoStreamError.ExtensionCaptureDisconnected: + case LocalVideoStreamError.ScreenCapturePermissionDenied: + this.setState({ isScreenSharing: false }); + break; + default: + break; + } + } + ); + }; + + _joinChannel = async () => { + if (Platform.OS === 'android') { + await PermissionsAndroid.requestMultiple([ + PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, + PermissionsAndroid.PERMISSIONS.CAMERA, + ]); + } + await this._engine?.joinChannel( + config.token, + this.state.channelId, + null, + config.uid + ); + }; + + _leaveChannel = async () => { + await this._engine?.leaveChannel(); + }; + + _startScreenShare = async () => { + const { isScreenSharing } = this.state; + if (isScreenSharing) { + await this._engine?.stopScreenCapture(); + } else { + await this._engine?.startScreenCapture({ + captureAudio: true, + captureVideo: true, + }); + } + if (Platform.OS === 'android') { + this.setState({ isScreenSharing: !isScreenSharing }); + } + }; + + render() { + const { channelId, isJoined, isScreenSharing } = this.state; + return ( + + + this.setState({ channelId: text })} + placeholder={'Channel ID'} + value={channelId} + /> +