admin管理员组文章数量:1300200
When using CallKit in my flutter app audio(both mic and speaker) stop working. When not using call kit to answer calls the app work fine. I am using the flutter flutter_callkit_incoming to use callkit and flutter_webrtc for the telephony. Flutter_callkit_incoming has some boilerplate code code include sections to uncomment when using webrtc and I have seen multiple fixes to suggest to make sure the to configure sharedAudioSession before the callkit is sent. Neither of this approaches seemed to have worked.
Here are some snippets of codes from the AppDelegate side and the swift side. There is a lot more code, and it is very messy, but this is everything involving the issue at hand.
//AppDelagate.swift section that handle incoming voip push
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
NSLog("flutter ns didReceiveIncomingPushWith\n\(payload.dictionaryPayload as AnyObject)")
guard type == .voIP else { return }
let linkedid = payload.dictionaryPayload["linkedid"] as? String ?? ""
let from = payload.dictionaryPayload["from"] as? String ?? ""
let handle = payload.dictionaryPayload["callerIdNumber"] as? String ?? ""
let isVideo = payload.dictionaryPayload["isVideo"] as? Bool ?? false
let foundLessThan = from.firstIndex(of: "<") ?? from.startIndex
var nameCaller = String(from[..<foundLessThan])
if(nameCaller.count == 0){
nameCaller = handle
}
let data = flutter_callkit_incoming.Data(id: UUID().uuidString, nameCaller: nameCaller, handle: handle, type: isVideo ? 1 : 0)
//set more data
data.extra = ["from": from, "linkedid": linkedid, "platform": "ios"]
NSLog("flutter ns displaying callkit nameCaller: " + nameCaller + ", handle: " + handle)
// Display callkit
SwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(data, fromPushKit: true)
NSLog("flutter ns find controller")
let controller : FlutterViewController = self.window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(name: "voip_notification", binaryMessenger: controller.binaryMessenger)
let messageData = [String: String]()
NSLog("flutter ns Trying to get flutter to do things")
//Invoke channel doesn't do any logic but seems to ensure that the darr/flutter side of the app
//Is active, which is the side that handles connecting the call
channel.invokeMethod("voip_notification", arguments:messageData) { (result) in
if let resultString = result as? String {
NSLog("flutter ns flutter invoked: " + resultString)
}
}
//Make sure call completion()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
print("flutter ns Flutter exiting")
NSLog("flutter ns Flutter exiting")
completion()
}
}
This next section it the relevant dart/flutter portion of the code
import 'dart:async';
import 'package:flutter_callkit_incoming/entities/entities.dart';
import 'package:hcs_mobile/utilities/environment.dart' as env;
import 'package:hcs_mobile/utilities/user_agent.dart';
import 'package:hcs_mobile/utilities/call.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:sip_ua/sip_ua.dart' as SIP;
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
void initCallKitListener(){
FlutterCallkitIncoming.onEvent.listen((event) async {
env.logger.d('CallKit Event: $event');
switch (event!.event) {
case Event.actionCallIncoming:
// TODO: received an incoming call
break;
//...
case Event.actionCallAccept:
//env.callManager is where we hold pending and awnswered calls
final CallObj? call = findCall(env.callManager.pendingIncomingCalls, event);
print("INCOMEING CALLS ${env.callManager.pendingIncomingCalls}");
final String callId = event.body['id'] as String;
print("Found call $call");
if(call != null){
acceptIncomingCall(call.sipCall);
}
break;
case Event.actionCallDecline:
//...
});
}
// Finds pending call with matching(enough) from
Future<String?> findCall(final Map<String, CallObj> callMap, final dynamic event) async{
try{
final String eventFrom = event.body['extra']['from'] as String;
int startGrab = eventFrom.indexOf('>'), endGrab = eventFrom.indexOf(';');
if(endGrab == -1){
endGrab = eventFrom.length;
}
final String matchingPart = eventFrom.substring(startGrab +1, endGrab);
for(final CallObj checkCall in callMap.values){
print(checkCall.sipCall.session.remote_identity?.uri?.toString());
print(matchingPart);
if(checkCall.sipCall.session.remote_identity?.uri?.toString().contains(matchingPart)?? false){
return checkCall;
}
}
}catch(error){
env.logger.e('Encountered an error trying to match from to call: $error');
}
return null;
}
acceptIncomingCall(SIP.Call incomingCall) async {
try {
bool remoteHasVideo = incomingCall!.remote_has_video;
var mediaStream = await getUserMedia();
//Moves pending call to active list
await env.callManager.convertPendingToActive(incomingCall.id!);
// Puts all other active calls on hold
env.callManager.setActiveCall(incomingCall.id);
//env.ua.helper is our SIP.SIPUAHelper object that is connect to our phone system
incomingCall.answer(env.ua!.helper.buildCallOptions(!remoteHasVideo), mediaStream: mediaStream);
} catch (err) {
print('ERROR TRYING TO ACCEPT $err');
}
}
getUserMedia() async {
final mediaConstraints = <String, dynamic>{
'audio': true,
};
print('Requesting medai access...');
await requestMediaAccess();
print('Passed request media access...');
MediaStream? localStream;
List<MediaDeviceInfo>? mediaDevicesList;
final localRenderer = RTCVideoRenderer();
mediaDevicesList = await navigator.mediaDevices.enumerateDevices();
await localRenderer.initialize();
localStream = await Helper.openCamera({
'video': false,
'audio': true,
});
localRenderer.srcObject = localStream;
return localStream;
}
requestMediaAccess() async {
var audioStatus = await Permission.audio.status;
var microphoneStatus = await Permission.microphone.status;
//microphone and audio permissions is always denied when callkit is active
if (audioStatus.isDenied) {
await Permission.audio.request()
} else if (audioStatus.isPermanentlyDenied) {
openAppSettings();
}
if (microphoneStatus.isDenied) {
await Permission.microphone.request();
} else if (microphoneStatus.isPermanentlyDenied) {
openAppSettings();
}
}
When using CallKit in my flutter app audio(both mic and speaker) stop working. When not using call kit to answer calls the app work fine. I am using the flutter flutter_callkit_incoming to use callkit and flutter_webrtc for the telephony. Flutter_callkit_incoming has some boilerplate code code include sections to uncomment when using webrtc and I have seen multiple fixes to suggest to make sure the to configure sharedAudioSession before the callkit is sent. Neither of this approaches seemed to have worked.
Here are some snippets of codes from the AppDelegate side and the swift side. There is a lot more code, and it is very messy, but this is everything involving the issue at hand.
//AppDelagate.swift section that handle incoming voip push
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
NSLog("flutter ns didReceiveIncomingPushWith\n\(payload.dictionaryPayload as AnyObject)")
guard type == .voIP else { return }
let linkedid = payload.dictionaryPayload["linkedid"] as? String ?? ""
let from = payload.dictionaryPayload["from"] as? String ?? ""
let handle = payload.dictionaryPayload["callerIdNumber"] as? String ?? ""
let isVideo = payload.dictionaryPayload["isVideo"] as? Bool ?? false
let foundLessThan = from.firstIndex(of: "<") ?? from.startIndex
var nameCaller = String(from[..<foundLessThan])
if(nameCaller.count == 0){
nameCaller = handle
}
let data = flutter_callkit_incoming.Data(id: UUID().uuidString, nameCaller: nameCaller, handle: handle, type: isVideo ? 1 : 0)
//set more data
data.extra = ["from": from, "linkedid": linkedid, "platform": "ios"]
NSLog("flutter ns displaying callkit nameCaller: " + nameCaller + ", handle: " + handle)
// Display callkit
SwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(data, fromPushKit: true)
NSLog("flutter ns find controller")
let controller : FlutterViewController = self.window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(name: "voip_notification", binaryMessenger: controller.binaryMessenger)
let messageData = [String: String]()
NSLog("flutter ns Trying to get flutter to do things")
//Invoke channel doesn't do any logic but seems to ensure that the darr/flutter side of the app
//Is active, which is the side that handles connecting the call
channel.invokeMethod("voip_notification", arguments:messageData) { (result) in
if let resultString = result as? String {
NSLog("flutter ns flutter invoked: " + resultString)
}
}
//Make sure call completion()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
print("flutter ns Flutter exiting")
NSLog("flutter ns Flutter exiting")
completion()
}
}
This next section it the relevant dart/flutter portion of the code
import 'dart:async';
import 'package:flutter_callkit_incoming/entities/entities.dart';
import 'package:hcs_mobile/utilities/environment.dart' as env;
import 'package:hcs_mobile/utilities/user_agent.dart';
import 'package:hcs_mobile/utilities/call.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:sip_ua/sip_ua.dart' as SIP;
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
void initCallKitListener(){
FlutterCallkitIncoming.onEvent.listen((event) async {
env.logger.d('CallKit Event: $event');
switch (event!.event) {
case Event.actionCallIncoming:
// TODO: received an incoming call
break;
//...
case Event.actionCallAccept:
//env.callManager is where we hold pending and awnswered calls
final CallObj? call = findCall(env.callManager.pendingIncomingCalls, event);
print("INCOMEING CALLS ${env.callManager.pendingIncomingCalls}");
final String callId = event.body['id'] as String;
print("Found call $call");
if(call != null){
acceptIncomingCall(call.sipCall);
}
break;
case Event.actionCallDecline:
//...
});
}
// Finds pending call with matching(enough) from
Future<String?> findCall(final Map<String, CallObj> callMap, final dynamic event) async{
try{
final String eventFrom = event.body['extra']['from'] as String;
int startGrab = eventFrom.indexOf('>'), endGrab = eventFrom.indexOf(';');
if(endGrab == -1){
endGrab = eventFrom.length;
}
final String matchingPart = eventFrom.substring(startGrab +1, endGrab);
for(final CallObj checkCall in callMap.values){
print(checkCall.sipCall.session.remote_identity?.uri?.toString());
print(matchingPart);
if(checkCall.sipCall.session.remote_identity?.uri?.toString().contains(matchingPart)?? false){
return checkCall;
}
}
}catch(error){
env.logger.e('Encountered an error trying to match from to call: $error');
}
return null;
}
acceptIncomingCall(SIP.Call incomingCall) async {
try {
bool remoteHasVideo = incomingCall!.remote_has_video;
var mediaStream = await getUserMedia();
//Moves pending call to active list
await env.callManager.convertPendingToActive(incomingCall.id!);
// Puts all other active calls on hold
env.callManager.setActiveCall(incomingCall.id);
//env.ua.helper is our SIP.SIPUAHelper object that is connect to our phone system
incomingCall.answer(env.ua!.helper.buildCallOptions(!remoteHasVideo), mediaStream: mediaStream);
} catch (err) {
print('ERROR TRYING TO ACCEPT $err');
}
}
getUserMedia() async {
final mediaConstraints = <String, dynamic>{
'audio': true,
};
print('Requesting medai access...');
await requestMediaAccess();
print('Passed request media access...');
MediaStream? localStream;
List<MediaDeviceInfo>? mediaDevicesList;
final localRenderer = RTCVideoRenderer();
mediaDevicesList = await navigator.mediaDevices.enumerateDevices();
await localRenderer.initialize();
localStream = await Helper.openCamera({
'video': false,
'audio': true,
});
localRenderer.srcObject = localStream;
return localStream;
}
requestMediaAccess() async {
var audioStatus = await Permission.audio.status;
var microphoneStatus = await Permission.microphone.status;
//microphone and audio permissions is always denied when callkit is active
if (audioStatus.isDenied) {
await Permission.audio.request()
} else if (audioStatus.isPermanentlyDenied) {
openAppSettings();
}
if (microphoneStatus.isDenied) {
await Permission.microphone.request();
} else if (microphoneStatus.isPermanentlyDenied) {
openAppSettings();
}
}
Share
Improve this question
edited Feb 12 at 15:51
Mitchell
asked Feb 11 at 14:28
MitchellMitchell
112 bronze badges
1
- Please provide enough code so others can better understand or reproduce the problem. – Community Bot Commented Feb 12 at 6:24
1 Answer
Reset to default 0I resolved the issue, This didn't have anything to do with flutter. I was simply waiting too long to call the complete() function after receive the voip push info.
//Make sure call completion()
//DispatchQueue.main.asyncAfter(deadline: .now() + 3) { THis is the cause of my madness
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
print("flutter ns Flutter exiting")
NSLog("flutter ns Flutter exiting")
completion()
}
本文标签: Using callkit stops audio from working on flutter iOSStack Overflow
版权声明:本文标题:Using callkit stops audio from working on flutter iOS - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741657506a2390843.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论