diff --git a/README.md b/README.md index ced8307ae..963321efc 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Download the `iris_web`(see the link below) artifact and include it as a ` ``` -Download: https://download.agora.io/staging/iris-web-rtc_n430_w4182_0.5.0.js +Download: https://download.agora.io/sdk/release/iris-web-rtc_n430_w4200_0.6.0.js **For Testing Purposes** @@ -101,7 +101,7 @@ You can directly depend on the Agora CDN for testing purposes: ... ... - + ``` diff --git a/example/lib/examples/advanced/index.dart b/example/lib/examples/advanced/index.dart index cf4739fd6..ffcaa80a4 100644 --- a/example/lib/examples/advanced/index.dart +++ b/example/lib/examples/advanced/index.dart @@ -51,7 +51,7 @@ final advanced = [ 'name': 'SetVideoEncoderConfiguration', 'widget': const SetVideoEncoderConfiguration() }, - if (!kIsWeb) {'name': 'StreamMessage', 'widget': const StreamMessage()}, + {'name': 'StreamMessage', 'widget': const StreamMessage()}, if (!kIsWeb) {'name': 'VoiceChanger', 'widget': const VoiceChanger()}, if (!kIsWeb) { diff --git a/example/lib/examples/basic/index.dart b/example/lib/examples/basic/index.dart index ff1b5bd37..9f058bd65 100644 --- a/example/lib/examples/basic/index.dart +++ b/example/lib/examples/basic/index.dart @@ -9,5 +9,5 @@ final basic = [ {'name': 'Basic'}, {'name': 'JoinChannelAudio', 'widget': const JoinChannelAudio()}, {'name': 'JoinChannelVideo', 'widget': const JoinChannelVideo()}, - if (!kIsWeb) {'name': 'StringUid', 'widget': const StringUid()} + {'name': 'StringUid', 'widget': const StringUid()} ]; diff --git a/example/lib/examples/basic/join_channel_audio/join_channel_audio.dart b/example/lib/examples/basic/join_channel_audio/join_channel_audio.dart index 3bc3a3ed5..a6cb44a18 100644 --- a/example/lib/examples/basic/join_channel_audio/join_channel_audio.dart +++ b/example/lib/examples/basic/join_channel_audio/join_channel_audio.dart @@ -18,8 +18,11 @@ class JoinChannelAudio extends StatefulWidget { class _State extends State { late final RtcEngine _engine; String channelId = config.channelId; + final String _selectedUid = ''; bool isJoined = false, openMicrophone = true, + muteMicrophone = false, + muteAllRemoteAudio = false, enableSpeakerphone = true, playEffect = false; bool _enableInEarMonitoring = false; @@ -27,6 +30,7 @@ class _State extends State { _playbackVolume = 100, _inEarMonitoringVolume = 100; late TextEditingController _controller; + late final TextEditingController _selectedUidController; ChannelProfileType _channelProfileType = ChannelProfileType.channelProfileLiveBroadcasting; late final RtcEngineEventHandler _rtcEngineEventHandler; @@ -35,6 +39,7 @@ class _State extends State { void initState() { super.initState(); _controller = TextEditingController(text: channelId); + _selectedUidController = TextEditingController(text: _selectedUid); _initEngine(); } @@ -67,6 +72,11 @@ class _State extends State { isJoined = true; }); }, + onRemoteAudioStateChanged: (RtcConnection connection, int remoteUid, + RemoteAudioState state, RemoteAudioStateReason reason, int elapsed) { + logSink.log( + '[onRemoteAudioStateChanged] connection: ${connection.toJson()} remoteUid: $remoteUid state: $state reason: $reason elapsed: $elapsed'); + }, onLeaveChannel: (RtcConnection connection, RtcStats stats) { logSink.log( '[onLeaveChannel] connection: ${connection.toJson()} stats: ${stats.toJson()}'); @@ -106,6 +116,8 @@ class _State extends State { setState(() { isJoined = false; openMicrophone = true; + muteMicrophone = false; + muteAllRemoteAudio = false; enableSpeakerphone = true; playEffect = false; _enableInEarMonitoring = false; @@ -123,6 +135,20 @@ class _State extends State { }); } + _muteLocalAudioStream() async { + await _engine.muteLocalAudioStream(!muteMicrophone); + setState(() { + muteMicrophone = !muteMicrophone; + }); + } + + _muteAllRemoteAudioStreams() async { + await _engine.muteAllRemoteAudioStreams(!muteAllRemoteAudio); + setState(() { + muteAllRemoteAudio = !muteAllRemoteAudio; + }); + } + _switchSpeakerphone() async { await _engine.setEnableSpeakerphone(!enableSpeakerphone); setState(() { @@ -219,6 +245,29 @@ class _State extends State { ) ], ), + if (kIsWeb) ...[ + TextField( + controller: _selectedUidController, + decoration: const InputDecoration( + hintText: 'input userID you want to mute/unmute'), + ), + ElevatedButton( + onPressed: () async { + await _engine.muteRemoteAudioStream( + uid: int.tryParse(_selectedUidController.text) ?? -1, + mute: true); + }, + child: Text('mute Remote Audio'), + ), + ElevatedButton( + onPressed: () async { + await _engine.muteRemoteAudioStream( + uid: int.tryParse(_selectedUidController.text) ?? -1, + mute: false); + }, + child: Text('unmute Remote Audio'), + ), + ], ], ), Align( @@ -228,6 +277,18 @@ class _State extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.end, children: [ + if (kIsWeb) ...[ + ElevatedButton( + onPressed: _muteLocalAudioStream, + child: Text( + 'Microphone ${muteMicrophone ? 'muted' : 'unmute'}'), + ), + ElevatedButton( + onPressed: _muteAllRemoteAudioStreams, + child: Text( + 'All Remote Microphone ${muteAllRemoteAudio ? 'muted' : 'unmute'}'), + ), + ], ElevatedButton( onPressed: _switchMicrophone, child: Text('Microphone ${openMicrophone ? 'on' : 'off'}'), diff --git a/example/lib/examples/basic/join_channel_video/join_channel_video.dart b/example/lib/examples/basic/join_channel_video/join_channel_video.dart index c36b9b325..c4ca1fc18 100644 --- a/example/lib/examples/basic/join_channel_video/join_channel_video.dart +++ b/example/lib/examples/basic/join_channel_video/join_channel_video.dart @@ -18,7 +18,12 @@ class JoinChannelVideo extends StatefulWidget { class _State extends State { late final RtcEngine _engine; - bool isJoined = false, switchCamera = true, switchRender = true; + bool isJoined = false, + switchCamera = true, + switchRender = true, + openCamera = true, + muteCamera = false, + muteAllRemoteVideo = false; Set remoteUid = {}; late TextEditingController _controller; bool _isUseFlutterTexture = false; @@ -86,6 +91,15 @@ class _State extends State { remoteUid.clear(); }); }, + onRemoteVideoStateChanged: ( + RtcConnection connection, + int remoteUid, + RemoteVideoState state, + RemoteVideoStateReason reason, + int elapsed) { + logSink.log( + '[onRemoteVideoStateChanged] connection: ${connection.toJson()} remoteUid: $remoteUid state: $state reason: $reason elapsed: $elapsed'); + }, ); _engine.registerEventHandler(_rtcEngineEventHandler); @@ -108,6 +122,11 @@ class _State extends State { Future _leaveChannel() async { await _engine.leaveChannel(); + setState(() { + openCamera = true; + muteCamera = false; + muteAllRemoteVideo = false; + }); } Future _switchCamera() async { @@ -117,6 +136,27 @@ class _State extends State { }); } + _openCamera() async { + await _engine.enableLocalVideo(!openCamera); + setState(() { + openCamera = !openCamera; + }); + } + + _muteLocalVideoStream() async { + await _engine.muteLocalVideoStream(!muteCamera); + setState(() { + muteCamera = !muteCamera; + }); + } + + _muteAllRemoteVideoStreams() async { + await _engine.muteAllRemoteVideoStreams(!muteAllRemoteVideo); + setState(() { + muteAllRemoteVideo = !muteAllRemoteVideo; + }); + } + @override Widget build(BuildContext context) { return ExampleActionsWidget( @@ -268,6 +308,24 @@ class _State extends State { child: Text('Camera ${switchCamera ? 'front' : 'rear'}'), ), ], + if (kIsWeb) ...[ + const SizedBox( + height: 20, + ), + ElevatedButton( + onPressed: _muteLocalVideoStream, + child: Text('Camera ${muteCamera ? 'muted' : 'unmute'}'), + ), + ElevatedButton( + onPressed: _muteAllRemoteVideoStreams, + child: Text( + 'All Remote Camera ${muteAllRemoteVideo ? 'muted' : 'unmute'}'), + ), + ElevatedButton( + onPressed: _openCamera, + child: Text('Camera ${openCamera ? 'on' : 'off'}'), + ), + ], ], ); }, diff --git a/example/lib/examples/basic/string_uid/string_uid.dart b/example/lib/examples/basic/string_uid/string_uid.dart index 78a511f40..cacdbfda3 100644 --- a/example/lib/examples/basic/string_uid/string_uid.dart +++ b/example/lib/examples/basic/string_uid/string_uid.dart @@ -20,6 +20,7 @@ class _State extends State { late final RtcEngine _engine; bool isJoined = false; late TextEditingController _controller0, _controller1; + late final RtcEngineEventHandler _rtcEngineEventHandler; @override void initState() { @@ -36,6 +37,7 @@ class _State extends State { } Future _dispose() async { + _engine.unregisterEventHandler(_rtcEngineEventHandler); await _engine.leaveChannel(); await _engine.release(); } @@ -47,7 +49,7 @@ class _State extends State { channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, )); - _engine.registerEventHandler(RtcEngineEventHandler( + _rtcEngineEventHandler = RtcEngineEventHandler( onError: (ErrorCodeType err, String msg) { logSink.log('[onError] err: $err, msg: $msg'); }, @@ -58,6 +60,10 @@ class _State extends State { isJoined = true; }); }, + onUserInfoUpdated: (int uid, UserInfo info) { + logSink + .log('[onUserInfoUpdated] uid: ${uid} UserInfo: ${info.toJson()}'); + }, onLeaveChannel: (RtcConnection connection, RtcStats stats) { logSink.log( '[onLeaveChannel] connection: ${connection.toJson()} stats: ${stats.toJson()}'); @@ -65,7 +71,8 @@ class _State extends State { isJoined = false; }); }, - )); + ); + _engine.registerEventHandler(_rtcEngineEventHandler); await _engine.enableAudio(); await _engine.setClientRole(role: ClientRoleType.clientRoleBroadcaster); diff --git a/example/web/index.html b/example/web/index.html index 6840f97ae..e6dca9210 100644 --- a/example/web/index.html +++ b/example/web/index.html @@ -100,6 +100,6 @@ loadMainDartJs(); } - + diff --git a/pubspec.yaml b/pubspec.yaml index 526592516..accabd6bd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: ffi: '>=1.1.2' async: ^2.8.2 meta: ^1.7.0 - iris_method_channel: 2.0.1 + iris_method_channel: 2.1.0 js: '>=0.6.3' dev_dependencies: flutter_test: diff --git a/scripts/iris_web_version.js b/scripts/iris_web_version.js index a329e566d..415bbf98d 100644 --- a/scripts/iris_web_version.js +++ b/scripts/iris_web_version.js @@ -1,8 +1,8 @@ // Share the iris web url to all the tests // This url should be same as the url inside the `example/web/index.html` -const irisWebUrl = 'https://download.agora.io/staging/iris-web-rtc_n430_w4182_0.5.0.js'; -const irisWebFakeUrl = 'https://download.agora.io/staging/iris-web-rtc-fake_n430_w4182_0.5.0.js'; +const irisWebUrl = 'https://download.agora.io/sdk/release/iris-web-rtc_n430_w4200_0.6.0.js'; +const irisWebFakeUrl = 'https://download.agora.io/sdk/release/iris-web-rtc-fake_n430_w4200_0.6.0.js'; (function() { var scriptLoaded = false; diff --git a/test_shard/iris_tester/lib/src/platform/web/iris_tester_bindings_web.dart b/test_shard/iris_tester/lib/src/platform/web/iris_tester_bindings_web.dart index fe6cd19ca..f6b39fbed 100644 --- a/test_shard/iris_tester/lib/src/platform/web/iris_tester_bindings_web.dart +++ b/test_shard/iris_tester/lib/src/platform/web/iris_tester_bindings_web.dart @@ -30,6 +30,9 @@ class EventParam { @JS('createIrisRtcEngineFake') external Object createIrisRtcEngineFake(Object irisApiEngine); +@JS('irisMock') +external void irisMock(); + @JS('triggerEventWithFakeApiEngine') external int triggerEventWithFakeApiEngine( // ignore: non_constant_identifier_names diff --git a/test_shard/iris_tester/lib/src/platform/web/iris_tester_web.dart b/test_shard/iris_tester/lib/src/platform/web/iris_tester_web.dart index 0ef2b0573..736444e32 100644 --- a/test_shard/iris_tester/lib/src/platform/web/iris_tester_web.dart +++ b/test_shard/iris_tester/lib/src/platform/web/iris_tester_web.dart @@ -21,6 +21,7 @@ class IrisTesterWeb implements IrisTester { isCreatedIrisRtcEngineFake = true; _irisRtcEngineFake = bindings.createIrisRtcEngineFake(irisApiEngine); + bindings.irisMock(); return _irisRtcEngineFake; }