You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter生成的iOS Swift项目中使用liblinphone 4.x?

刚好我前阵子帮朋友搞定了liblinphone新版本(不是3.x)和Flutter iOS的集成,完全用平台通道实现你要的注册、通话功能和状态监听,给你一套现成的方案,直接就能用:

适配liblinphone新版本的Flutter iOS平台通道实现

一、先搞定Swift端的Linphone核心初始化

我习惯用单例类LinphoneManager来管理Linphone的核心实例,避免重复初始化,还能统一处理所有回调:

import linphonesw

class LinphoneManager: NSObject {
    static let shared = LinphoneManager()
    private var core: Core?
    private var coreDelegate: CoreDelegate?

    private override init() {
        super.init()
        setupLinphoneCore()
    }

    private func setupLinphoneCore() {
        let config = Config()
        // 可选:开启日志调试,方便排查问题
        config.setInt(forKey: "debug", inSection: "log", value: 3)
        do {
            // 初始化Core,systemContext传nil就行
            core = try Core(config: config, systemContext: nil)
            setupCoreCallbacks()
            // 启动Core
            core?.start()
        } catch {
            print("Linphone核心初始化失败: \(error.localizedDescription)")
        }
    }

    private func setupCoreCallbacks() {
        // 用CoreDelegateStub来实现需要的回调,不用写全所有方法
        coreDelegate = CoreDelegateStub(
            // 注册状态变化回调
            onRegistrationStateChanged: { [weak self] core, cfg, state, message in
                let statusData = ["state": state.rawValue, "message": message]
                self?.sendEventToFlutter(event: "registrationStateChanged", data: statusData)
            },
            // 通话状态变化回调
            onCallStateChanged: { [weak self] core, call, state, message in
                let callData = ["callId": call.callId, "state": state.rawValue, "message": message]
                self?.sendEventToFlutter(event: "callStateChanged", data: callData)
            },
            // 来电回调
            onIncomingReceived: { [weak self] core, call in
                let incomingData = [
                    "callId": call.callId,
                    "remoteAddress": call.remoteAddress?.asString() ?? "未知号码"
                ]
                self?.sendEventToFlutter(event: "incomingCall", data: incomingData)
            }
        )
        core?.addDelegate(delegate: coreDelegate!)
    }

    // 通用方法:把事件发送给Flutter
    private func sendEventToFlutter(event: String, data: [String: Any]) {
        guard let flutterVC = UIApplication.shared.delegate?.window??.rootViewController as? FlutterViewController else {
            return
        }
        let channel = FlutterMethodChannel(name: "com.your.app/linphone", binaryMessenger: flutterVC)
        channel.invokeMethod(event, arguments: data)
    }
}

二、实现核心功能方法(Swift端)

在LinphoneManager里添加注册、发起通话、接听、挂断的具体实现:

// 注册SIP账号
func registerSIPAccount(username: String, password: String, domain: String) {
    guard let core = core else { return }
    do {
        // 创建认证信息
        let authInfo = try AuthInfo(
            username: username,
            userid: nil,
            password: password,
            ha1: nil,
            realm: domain,
            domain: domain
        )
        core.addAuthInfo(info: authInfo)
        
        // 创建账号参数
        let accountParams = try core.createAccountParams()
        let identityAddr = try Address(addr: "sip:\(username)@\(domain)")
        accountParams.setIdentityaddress(newValue: identityAddr)
        
        // 设置服务器地址
        let serverAddr = try Address(addr: "sip:\(domain)")
        accountParams.setServeraddress(newValue: serverAddr)
        accountParams.registerEnabled = true
        
        // 添加账号到Core并设为默认
        let account = try core.createAccount(params: accountParams)
        core.addAccount(account: account)
        core.defaultAccount = account
    } catch {
        print("注册失败: \(error.localizedDescription)")
    }
}

// 发起通话
func startCall(to address: String) {
    guard let core = core else { return }
    do {
        let remoteAddr = try Address(addr: address)
        let callParams = try core.createCallParams(call: nil)
        try core.inviteAddress(address: remoteAddr, params: callParams)
    } catch {
        print("发起通话失败: \(error.localizedDescription)")
    }
}

// 接听来电
func acceptIncomingCall(callId: String) {
    guard let core = core else { return }
    // 通过callId找到对应的通话实例
    if let call = core.calls.first(where: { $0.callId == callId }) {
        do {
            try call.accept()
        } catch {
            print("接听失败: \(error.localizedDescription)")
        }
    }
}

// 挂断通话
func endCurrentCall(callId: String) {
    guard let core = core else { return }
    if let call = core.calls.first(where: { $0.callId == callId }) {
        do {
            try call.terminate()
        } catch {
            print("挂断失败: \(error.localizedDescription)")
        }
    }
}

三、Flutter端的平台通道配置

在Flutter里创建一个服务类,用来调用Swift方法并监听回调事件:

import 'package:flutter/services.dart';

class LinphoneService {
  // 通道名称要和Swift端保持一致
  static const MethodChannel _linphoneChannel = MethodChannel('com.your.app/linphone');
  
  // 设置事件监听器,处理Swift发来的回调
  static void setupEventListeners() {
    _linphoneChannel.setMethodCallHandler((call) async {
      switch (call.method) {
        case 'registrationStateChanged':
          // 处理注册状态变化,比如更新UI提示用户
          final state = call.arguments['state'];
          final message = call.arguments['message'];
          print("注册状态更新: $state - $message");
          // 这里可以添加你的业务逻辑,比如通知页面更新
          break;
        case 'callStateChanged':
          // 处理通话状态变化,比如显示通话中、已挂断等状态
          final callId = call.arguments['callId'];
          final state = call.arguments['state'];
          print("通话[$callId]状态更新: $state");
          break;
        case 'incomingCall':
          // 处理来电,比如弹出接听/挂断弹窗
          final callId = call.arguments['callId'];
          final remoteAddress = call.arguments['remoteAddress'];
          print("收到来电: $remoteAddress (CallID: $callId)");
          break;
        default:
          throw MissingPluginException();
      }
    });
  }

  // 调用注册方法
  static Future<void> registerAccount(String username, String password, String domain) async {
    await _linphoneChannel.invokeMethod('registerSIPAccount', {
      'username': username,
      'password': password,
      'domain': domain,
    });
  }

  // 发起通话
  static Future<void> makeCall(String address) async {
    await _linphoneChannel.invokeMethod('startCall', {'address': address});
  }

  // 接听通话
  static Future<void> acceptCall(String callId) async {
    await _linphoneChannel.invokeMethod('acceptIncomingCall', {'callId': callId});
  }

  // 挂断通话
  static Future<void> endCall(String callId) async {
    await _linphoneChannel.invokeMethod('endCurrentCall', {'callId': callId});
  }
}

四、Swift端绑定平台通道方法

最后在AppDelegate里注册平台通道,处理Flutter发来的方法调用:

import UIKit
import Flutter
import linphonesw

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        guard let flutterVC = window?.rootViewController as? FlutterViewController else {
            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
        
        // 注册平台通道
        let linphoneChannel = FlutterMethodChannel(name: "com.your.app/linphone", binaryMessenger: flutterVC)
        linphoneChannel.setMethodCallHandler { [weak self] call, result in
            switch call.method {
            case "registerSIPAccount":
                guard let args = call.arguments as? [String: String],
                      let username = args["username"],
                      let password = args["password"],
                      let domain = args["domain"] else {
                    result.error("INVALID_ARGS", "参数格式错误", nil)
                    return
                }
                LinphoneManager.shared.registerSIPAccount(username: username, password: password, domain: domain)
                result.success(nil)
            case "startCall":
                guard let args = call.arguments as? [String: String], let address = args["address"] else {
                    result.error("INVALID_ARGS", "缺少通话地址", nil)
                    return
                }
                LinphoneManager.shared.startCall(to: address)
                result.success(nil)
            case "acceptIncomingCall":
                guard let args = call.arguments as? [String: String], let callId = args["callId"] else {
                    result.error("INVALID_ARGS", "缺少CallID", nil)
                    return
                }
                LinphoneManager.shared.acceptIncomingCall(callId: callId)
                result.success(nil)
            case "endCurrentCall":
                guard let args = call.arguments as? [String: String], let callId = args["callId"] else {
                    result.error("INVALID_ARGS", "缺少CallID", nil)
                    return
                }
                LinphoneManager.shared.endCurrentCall(callId: callId)
                result.success(nil)
            default:
                result.notImplemented()
            }
        }
        
        GeneratedPluginRegistrant.register(with: self)
        // 初始化Linphone单例
        _ = LinphoneManager.shared
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

几个关键注意事项

  • 一定要在iOS项目的Info.plist里添加麦克风权限:NSMicrophoneUsageDescription,否则通话会没有声音,用户也会被拒绝权限
  • liblinphone新版本用的是import linphonesw,和3.x的import Linphone完全不兼容,所以别再用旧版本的代码了
  • 测试前确保你的SIP服务器是正常运行的,账号可以正常注册(可以用linphone官方客户端先测一遍)
  • 如果遇到Core初始化失败,检查pod集成是否正确,比如pod install有没有跑完,liblinphone的版本是不是最新的

内容的提问来源于stack exchange,提问作者slance

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:26:18