camera_capturer_model.dart: Failed assertion: line 14 pos 14: 'source != null': is not true.
Current bug behaviour
I don't consider this to be a real bug, but rather some inconvenience.
In our app there's a way to answer the call through CallKit. Through method channel the information goes from our voip iOS plugin to Flutter, where room connection is happening in a way specified in docs (so it goes back to Twilio plugin).
When joining the call through CallKit on iOS on locked screen, the app either doesn't have permission to use camera or something on a system level blocks access to that. This causes the exception to be thrown from camera_capturer_model.dart
in debug mode. Of course in release it doesn't happen as assertions are removed.
flutter: [ NATIVE ] RemoteParticipantStreamHandler.onListen => RemoteParticipant eventChannel attached
flutter: [ NATIVE ] LocalParticipantStreamHandler.onListen => LocalParticipant eventChannel attached
flutter: [ NATIVE ] RemoteDataTrackStreamHandler.onListen => RemoteDataTrack eventChannel attached
RoomListener.roomDidConnect => room sid is 'RM7334321fa257fbe6d4cc9b9955ae6eab'
flutter: [ NATIVE ] RoomListener.roomDidConnect => room sid is 'RM7334321fa257fbe6d4cc9b9955ae6eab'
flutter: ----------------FIREBASE CRASHLYTICS----------------
flutter: 'package:twilio_programmable_video_platform_interface/src/models/capturers/camera_capturer_model.dart': Failed assertion: line 14 pos 14: 'source != null': is not true.
flutter:
[38;5;244m#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:46:39)[39;49m
[38;5;244m#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:36:5)[39;49m
[38;5;244m#2 new CameraCapturerModel[39;49m
[38;5;244m#3 new LocalVideoTrackModel.fromEventChannelMap[39;49m
[38;5;244m#4 new LocalVideoTrackPublicationModel.fromEventChannelMap[39;49m
[38;5;244m#5 new LocalParticipantModel.fromEventChannelMap[39;49m
[38;5;244m#6 MethodChannelProgrammableVideo._parseRoomEvent[39;49m
[38;5;244m#7 _MapStream._handleData (dart:async/stream_pipe.dart:213:31)[39;49m
[38;5;244m#8 _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)[39;49m
[38;5;244m#9 _rootRunUnary (dart:async/zone.dart:1370:13)[39;49m
[38;5;244m#10 _CustomZone.runUnary (dart:async/zone.dart:1265:19)[39;49m
[38;5;244m#11 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1170:7)[39;49m
[38;5;244m#12 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)[39;49m
[38;5;244m#13 _DelayedData.perform (dart:async/stream_impl.dart:591:14)[39;49m
[38;5;244m#14 _StreamImplEvents.handleNext (dart:async/stream_impl.dart:706:11)[39;49m
[38;5;244m#15 _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:663:7)[39;49m
[38;5;244m#16 _rootRun (dart:async/zone.dart:1346:47)[39;49m
[38;5;244m#17 _CustomZone.run (dart:async/zone.dart:1258:19)[39;49m
[38;5;244m#18 _CustomZone.runGuarded (dart:async/zone.dart:1162:7)[39;49m
[38;5;244m#19 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1202:23)[39;49m
[38;5;244m#20 _rootRun (dart:async/zone.dart:1354:13)[39;49m
[38;5;244m#21 _CustomZone.run (dart:async/zone.dart:1258:19)[39;49m
[38;5;244m#22 _CustomZone.runGuarded (dart:async/zone.dart:1162:7)[39;49m
[38;5;244m#23 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1202:23)[39;49m
[38;5;244m#24 _microtaskLoop (dart:async/schedule_microtask.dart:40:21)[39;49m
[38;5;244m#25 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)[39;49m
This doesn't happen when joining the call on unlocked screen with app in the background or when app is in the foreground.
This is how I join the call, but bear in mind that on iOS this code is run when the native CallKit screen is shown and screen is locked. The onConnected
, onFailed
and other are my app specific callbacks.
_cameraCapturer = CameraCapturer(CameraSource.FRONT_CAMERA);
final connectOptions = ConnectOptions(
token,
preferredAudioCodecs: [OpusCodec()],
audioTracks: [LocalAudioTrack(true)],
dataTracks: [LocalDataTrack()],
videoTracks: [LocalVideoTrack(videoEnabled, _cameraCapturer)],
enableNetworkQuality: true,
networkQualityConfiguration: NetworkQualityConfiguration(
remote: NetworkQualityVerbosity.NETWORK_QUALITY_VERBOSITY_MINIMAL,
),
enableDominantSpeaker: true,
);
_room = await TwilioProgrammableVideo.connect(connectOptions);
_onConnectedSub = _room.onConnected?.listen((_) {
onConnected?.call();
TwilioProgrammableVideo.setSpeakerphoneOn(true);
});
_onConnectFailSub = _room.onConnectFailure?.listen((failure) {
onFailed?.call(failure.exception);
});
_onParticipantConnectedSub = _room.onParticipantConnected?.listen((_) {
onAnswered?.call();
});
_onParticipantDisconnectedSub =
_room.onParticipantDisconnected?.listen((_) {
onEnded?.call();
});
_onDominantSpeakerChangeSub =
_room.onDominantSpeakerChange?.listen((event) {
onAnswered?.call();
});
_onReconnectingSub = _room.onReconnecting?.listen((_) {
onReconnecting?.call();
});
_onReconnectedSub = _room.onReconnected?.listen((_) {
onReconnected?.call();
});
Expected behaviour
I'd like this exception to be not thrown and wonder if I could just fork the plugin and remove these assertions without any negative effect. The call via CallKit should be accepted without the video either way.
Steps to reproduce
I wish I could provide some steps to reproduce it, but at the moment we're just before the release and it's hard to squeeze some time to prepare such. I know how useful it would be...
Flutter doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.0.5, on macOS 11.2.3 20D91 darwin-x64, locale pl-PL)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.1)
[✓] VS Code (version 1.55.2)
[✓] Connected device (4 available)
More environment information
- Plugin version: 0.7.1
- Platform affected: ios
- Platform version affected: ios 14