Agora Flutter video call stops after some time with PlatformException(-7, , null, null) - flutter

I am working on a Flutter app that uses a video call feature. I have set up engine initialization and everything correctly and am able to make the video call on both ends successfully. Following is my current version of Agora RTC Engine:
agora_rtc_engine: ^5.3.1
Following is the code I used for engine initialization and initiating video calls.
RtcEngine engine;
var _localUid = 0;
await [Permission.camera, Permission.microphone].request();
engine = await RtcEngine.create(Constants.AGORA_APP_ID);
engine.setEventHandler(
RtcEngineEventHandler(
joinChannelSuccess: (channelId, uid, elapsed) { _localUid = uid; /* my_own_logic */ },
leaveChannel: (RtcStats stats) { /* my_own_logic */ },
userJoined: (int remoteUid, int reason) { /* my_own_logic */ },
userOffline: (remoteUid, reason) { /* my_own_logic */ },
));
await engine.enableVideo();
await engine.enableAudio();
await engine.setVideoEncoderConfiguration(VideoEncoderConfiguration(
dimensions: const VideoDimensions(width: 360, height: 640),
frameRate: VideoFrameRate.Fps60,
bitrate: 0,
));
await engine.setDefaultAudioRouteToSpeakerphone(true);
agoraToken = await fetchAgoraToken(uid: _localUid, channelId: "generatedChannelId");
await engine.setChannelProfile(ChannelProfile.LiveBroadcasting);
await engine.setClientRole(ClientRole.Broadcaster);
await engine.startPreview();
await engine.joinChannel(agoraToken, "generatedChannelId", '', _localUid,);
I'm generating call joining tokens with channel ID, Agora APP ID and Certificate from Firebase Functions and the channel name is always unique between the same two callers.
The video call is working well and I can see/hear other people. But after a few seconds, not more than a minute, the video call stops suddenly. Following is the exact error message I receive on my console
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(-7, , null, null)
E/flutter (25342): #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:653:7)
E/flutter (25342): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:296:18)
I'm stuck here and unable to work further. Could anybody suggest me how should I fix this?

It turned out that I had earlier placed a timer to schedule cancellation of call if there's no response from the callee. The calling screen was closing after the specified time without leaving channel and destroying the engine, and that was causing the exception.
Timer? callEndTimer;
void scheduleCallEnd() {
callEndTimer = Timer(const Duration(seconds: Constants.INCOMMING_CALL_TIME), () {
if (mounted) {
setState(() {
_onCallEnd(context);
});
}
});
}
Then I cancelled the timer if the call was responded by callee.
void cancelScheduledCallEnd() {
if (callEndTimer != null) callEndTimer!.cancel();
}

Related

Flutter - an async function returns before really finishing?

I have a function scanAndConnect() that should scan for BLE devices and connect to the device with the specified service ID. This function should be async and should return Future.
The problem is that scanAndConnect() prints 99999 and returns without waiting for flutterReactiveBle.statusStream.listen() to finish although I use await before it.
Future scanAndConnect(Uuid serviceId, Uuid charctId) async {
StreamSubscription<BleStatus>? bleStatusStreamSubscription;
StreamSubscription<DiscoveredDevice>? deviceStreamSubscription;
Stream<DiscoveredDevice> stream;
bleStatusStreamSubscription =
await flutterReactiveBle.statusStream.listen((bleStatus) async {
print("new listen ${bleStatus.toString()}");
if (bleStatus == BleStatus.ready) {
await bleStatusStreamSubscription!.cancel();
connectionStatus = BLEConnectionStatus.Connecting;
stream = await flutterReactiveBle.scanForDevices(
withServices: [serviceId],
scanMode: ScanMode.lowLatency,
);
}
});
print("9999999");
}
....
Future connectToDevice() async {
await ble.scanAndConnect(BLE_SERVICE_UUID, BLE_CHAR_UUID)
print("Statement after await in main");
setState(() {
loading = false;
print("Changing state to ${loading.toString()}");
});
}
This is the output I get in Xcode:
flutter: 9999999
flutter: Statement after await in main
flutter: Changing state to false
flutter: new listen BleStatus.unknown
flutter: new listen BleStatus.ready
How can I make scanAndConnect doesn't return before really finishing?
According to the documentation, FlutterReactiveBle.scanForDevices() returns a Stream, not a Future, so await will not work here. You can use
await for
listen()
await stream.first()
to wait for data from a Stream.

got an AgoraRtcException(20, Make sure you call RtcEngine.initialize first) even after initializing the engine

trying to make a 1:1 video meeting with agora with flutter and after following the docs i got
AgoraRtcException(20, Make sure you call RtcEngine.initialize first) exception although I am sure I am initializing it first however this the initialize code
void initState() {
super.initState();
setupVideoSDKEngine();
join();
the setupVideoSDKEngine() method code is
Future<void> setupVideoSDKEngine() async {
// retrieve or request camera and microphone permissions
await [Permission.microphone, Permission.camera].request();
//create an instance of the Agora engine
agoraEngine = createAgoraRtcEngine();
await agoraEngine
.initialize(RtcEngineContext(appId: Environment.agoraAppId));
await agoraEngine.enableVideo();
// Register the event handler
agoraEngine.registerEventHandler(
RtcEngineEventHandler(
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
showMessage(
"Local user uid:${connection.localUid} joined the channel");
setState(() {
_isJoined = true;
});
},
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
showMessage("Remote user uid:$remoteUid joined the channel");
setState(() {
_remoteUid = uid;
player.stop();
customTimer!.resetAndStart();
});
},
onUserOffline: (RtcConnection connection, int remoteUid,
UserOfflineReasonType reason) {
showMessage("Remote user uid:$remoteUid left the channel");
callEnded = true;
setState(() {
_remoteUid = null;
});
print('stats ${reason.name}');
if (!userOffline) {
Future.delayed(Duration(seconds: 1), () => Navigator.pop(context));
}
userOffline = true;
},
),
);
}
I am expecting to join the channel but nothing happens and it throws this error
I tried to delete the app and reinstall it but nothing happens
and got this exception too AgoraRtcException(-17, null)
you can to call this before using any other agora function i.e. You are trying to use agora sdk function without initializing it so here it is
This will be written globally
late final RtcEngineEx _engine;
this will be written in initState
_engine = createAgoraRtcEngineEx();
await _engine.initialize(const RtcEngineContext(
appId: APP_ID,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
This piece of advice I'm giving is for the correct usage of async and await keywords.
In your initState() function, you are calling two functions one after another; out of which the first function setupVideoSDKEngine() is an async function, and another function join() chould also be an async because the Agora join channel code returns a Future.
await engine.joinChannel(agoraToken, channelId, '', 0,);
Your code right now does not wait for the engine initialization and starts joining channel. Thus, the error. So you got to await your initialization and then write your join channel code.
For eg.
/* permission stuffs */
await agoraEngine.initialize(RtcEngineContext(appId: Environment.agoraAppId));
/* do event handling tasks */
// Now join the channel.
await engine.joinChannel(agoraToken, channelId, '', 0,);
It is recommended that you put the whole code into one async function.

Switching stream with Flutter WebRTC seems to fail

I'm trying to switch from the front to back camera using Flutter WebRTC but cannot get it working.
I have the following
// Stop the current stream and remove the tracks
await Future.forEach(videoStream!.getVideoTracks(), (MediaStreamTrack? track) async {
if (track != null) {
try {
await track.stop();
await videoStream!.removeTrack(track);
} catch (e) {
if (kDebugMode) {
print(e);
}
}
}
});
videoStream!.getVideoTracks().forEach((track) {
track.stop();
videoStream!.removeTrack(track, removeFromNative: true);
});
final mediaConstraints = {
'audio': false, // NO need to capture audio again
'video': {
'deviceId': videoInputDeviceId,
}
};
MediaStream newStream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
final newTrack = newStream.getVideoTracks()[0];
await videoStream!.addTrack(newTrack, addToNative: true);
Getting the following errors if I place try catch around them
flutter: PlatformException(mediaStreamRemoveTrack: Track is nil, null, null, null)
flutter: !--- Event: Failed to enable webcam
flutter: Concurrent modification during iteration: Instance(length:0) of '_GrowableList'.

Flutter Agora PlatformException(7, not initialized, null, null)

I am developing a voice call application using Flutter Agora SDK. So far, I have been able to comfortably perform voice calls without any errors. But today, while testing it on my real device, it gives an error even though I haven't made any changes to the initializing process. Even strangely, it works without any error on the emulator, but only on the real device gives error.
Future<void> initAgora() async {
await [Permission.microphone].request();
try {
engine = await RtcEngine.createWithContext(RtcEngineContext(APP_ID));
await engine.enableAudio();
await engine.setChannelProfile(ChannelProfile.Communication);
engine.setEventHandler(
RtcEngineEventHandler(
activeSpeaker: (i) {
log("Active Speaker: $i");
},
microphoneEnabled: (enable) {
log("Microphone: " + enable.toString());
callingController.microphoneState.value = enable;
},
warning: (warningCode) {
print(warningCode.toString());
},
rtcStats: (stats) {
log("User Count: ${stats.userCount}");
},
connectionStateChanged: (state, reason) {
log("Connection Changed : ${state.toString()}, ${reason.toString()}");
},
joinChannelSuccess: (String channel, int uid, int elapsed) {
log('joinChannelSuccess $channel $uid');
},
userJoined: (int uid, int elapsed) {
log('userJoined $uid');
},
userOffline: (int uid, UserOfflineReason reason) {
log('userOffline $uid');
callingController.finishCall(uid, "user_left");
},
error: (error) {
log("ERROR: $error", name: "AGORA");
},
),
);
if (callingController.isCallerMe) {
await joinChannel();
}
} catch (e) {
print(e);
failureSnackbar(e.toString());
}
}
The error that you shared occurs when you try calling a method before the SDK has been initialised properly. Can you please check your RtcEngine config. Also, if you have enabled tokens in your project please make sure that you pass it to the config.

Flutter Isolate Issue with Platform Channel code

I tried to clear my app notification count using the platform code. And I try to implement isolate for this function in dart side.
I got the above error message to both compute and isolate ways. Both are failed.
Error
Exception: Null check operator used on a null value
MethodChannel.binaryMessenger (package:flutter/src/services/platform_channel.dart:142:86)
MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:148:36)
MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:331:12)
NotificationCountService.computeFunction (package:yellow/services/notificationCountService.dart:32:16)
_IsolateConfiguration.apply (package:flutter/src/foundation/_isolates_io.dart:81:34)
_spawn.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:88:65)
_spawn.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:87:5)
Timeline.timeSync (dart:developer/timeline.dart:163:22)
_spawn (package:flutter/src/foundation/_isolates_io.dart:85:35)
_delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:286:17)
_RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
Full Code
class NotificationCountService {
static const platform = MethodChannel('path/notificationCount');
static Future<void> setCountCompute(int count) async {
//await compute(computeFunction, count);
spawnNewIsolate(count);
}
static computeFunction(int count) async {
try {
count = (count ?? 0) <= 0 ? 0 : count;
compute(await platform.invokeMethod('setCount', {'count': count}), );
print('result is $result');
} on PlatformException catch (e) {
print("Failed to get battery level: '${e.message}'.");
return null;
}
}
static void spawnNewIsolate(int count) async {
ReceivePort receivePort = ReceivePort();
try {
await Isolate.spawn(createFunction, receivePort.sendPort);
SendPort childSendPort = await receivePort.first;
ReceivePort responsePort = ReceivePort();
childSendPort.send([count, receivePort.sendPort]);
var response = await responsePort.first;
print('response $response');
} catch (e) {
print("Error: $e");
}
}
static void createFunction(SendPort mainPort) async {
ReceivePort childPort = ReceivePort();
mainPort.send(childPort.sendPort);
await for (var message in childPort) {
int count = message[0];
SendPort replyPort = message[1];
var result = await platform.invokeMethod('setCount', {'count': count});
replyPort.send(result);
}
}
}
The engine code dereferences null and crashes when attempting to send a platform message from the secondary isolate. I haven't pinpointed exactly where yet; I get a tombstone, but need to learn how to interpret such a thing.
As a (clumsy) workaround, the secondary isolate could ask the primary one to send the messa
I got the same issue. To track this issue, please subscribe https://github.com/flutter/flutter/issues/13937