Flutter WebRTC通信插件telnyx_webrtc的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter WebRTC通信插件telnyx_webrtc的使用

Telnyx Flutter Voice SDK

启用Telnyx实时通信服务在Flutter应用程序(Android / iOS / Web)上 📞 🔥

特性

  • ✅ 创建/接收通话
  • ✅ 保持通话
  • ✅ 静音通话
  • ✅ 双音多频(DTMF)

使用

SIP凭据

要开始使用TelnyxRTC SDK进行通话,您需要获取SIP凭据:

Screenshot 2022-07-15 at 13 51 45
  1. 访问 https://portal.telnyx.com/
  2. 注册Telnyx账户。
  3. 创建凭证连接以配置您的通话方式。
  4. 创建出站语音配置文件以配置您的出站通话设置,并将其分配给您的凭证连接。

有关如何生成SIP凭据的更多信息,请参阅 Telnyx WebRTC快速入门指南

平台特定配置

Android

如果您正在将SDK实现到Android应用中,则需要记住添加以下权限到您的AndroidManifest.xml中,以便允许音频和互联网权限:

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

iOS

在iOS平台上,您需要在Info.plist文件中添加麦克风权限:

    <key>NSMicrophoneUsageDescription</key>
    <string>$(PRODUCT_NAME) Microphone Usage!</string>

基本使用

Telnyx客户端

TelnyxClient() 是SDK的核心类,可用于连接到我们的后端套接字连接,创建通话,检查状态和断开连接等。

一旦实例创建完成,您可以调用.connect()方法通过令牌或凭据连接到套接字(详见下文)。如果没有网络可用,将作为套接字响应出现错误:

TelnyxClient _telnyxClient = TelnyxClient();

登录Telnyx客户端

要登录Telnyx WebRTC客户端,您需要使用Telnyx SIP连接进行身份验证。请遵循我们的 快速入门指南 来创建JWT(JSON Web Tokens)进行身份验证。使用令牌登录时,我们使用connectWithToken()方法。也可以直接使用SIP连接的usernamepassword通过connectWithCredential()方法进行认证:

_telnyxClient.connectWithToken(tokenConfig)
                    //OR
_telnyxClient.connectWithCredential(credentialConfig)             

注意: tokenConfigcredentialConfig是简单的类,代表用于登录的客户端设置,它们扩展了一个具有共享属性的基础Config类。它们看起来像这样:

/// 创建一个可以用来登录的CredentialConfig实例
///
/// 使用[sipUser]和[sipPassword]字段登录
/// [sipCallerIDName]和[sipCallerIDNumber]将是名称和号码关联
/// [notificationToken]是用于注册设备的通知令牌(FCM或APNS)
/// [autoReconnect]标志决定是否在使用合法凭据登录失败的情况下尝试重新连接(最多3次尝试)
class CredentialConfig extends Config {
  CredentialConfig({
     required this.sipUser,
     required this.sipPassword,
     required super.sipCallerIDName,
     required super.sipCallerIDNumber,
     super.notificationToken,
     super.autoReconnect,
     required super.debug,
     super.ringTonePath,
     super.ringbackPath,
  });

  final String sipUser;
  final String sipPassword;
}

/// 创建一个可以用来登录的TokenConfig实例
///
/// 使用[sipToken]字段登录
/// [sipCallerIDName]和[sipCallerIDNumber]将是名称和号码关联
/// [notificationToken]是用于注册设备的通知令牌(FCM或APNS)
/// [autoReconnect]标志决定是否在使用合法令牌登录失败的情况下尝试重新连接(最多3次尝试)
class TokenConfig extends Config {
  TokenConfig({
     required this.sipToken,
     required super.sipCallerIDName,
     required super.sipCallerIDNumber,
     super.notificationToken,
     super.autoReconnect,
     required super.debug,
     super.ringTonePath,
     super.ringbackPath,
  });

  final String sipToken;
}

创建通话邀请

为了发起通话邀请,我们首先通过.call实例创建一个Call类实例。这会创建一个Call类,可以用于与通话交互(邀请、接受、拒绝等)。 然后,我们可以使用.newInvite()方法发送邀请,该方法需要您提供您的callerNamecallerNumber,目的地号码(或SIP凭据),以及您的clientState(任何字符串值)。

_telnyxClient
    .call
    .newInvite("callerName", "000000000", destination, "State");

接受通话

为了能够接受通话,我们首先需要监听邀请。我们通过获取Telnyx套接字响应回调来实现这一点:

// 观察收到的套接字消息
_telnyxClient.onSocketMessageReceived = (TelnyxMessage message) {
  switch (message.socketMethod) {
        case SocketMethod.CLIENT_READY:
        {
           // 当客户端正确设置并登录后触发,现在可以开始通话了。
           break;
        }
        case SocketMethod.LOGIN:
        {
            // 处理成功的登录 - 更新UI或导航到新屏幕等。
            break;
        }
        case SocketMethod.INVITE:
        {
            // 处理邀请 - 更新UI或导航到新屏幕等。
            // 然后,通过某种类型的接听按钮,我们可以接受通话:
            // 这将返回一个Call类的实例,可用于与通话交互或监控其状态。
            _incomingInvite = message.message.inviteParams;
            _call = _telnyxClient.acceptCall(
                _incomingInvite, "callerName", "000000000", "State");
            break;
        }
        case SocketMethod.ANSWER:
        {
           // 处理接收到的通话答案 - 更新UI或导航到新屏幕等。
          break;
        }
        case SocketMethod.BYE:
        {
           // 处理通话拒绝或结束 - 更新UI或导航到新屏幕等。
           break;
      }
    }
};

我们可以使用此方法创建一个监听器,该监听器监听邀请并在这种情况下立即接听。实际实现更适合显示一些UI并允许手动接受/拒绝操作。

拒绝/结束通话

为了结束通话,我们可以获取存储的Call实例并调用.endCall(callID)方法。要拒绝来电,我们首先使用.createCall()方法创建通话,然后调用.endCall(callID)方法:

if (_ongoingCall) {
  _telnyxClient.call.endCall(_telnyxClient.call.callId);
} else {
  _telnyxClient.createCall().endCall(_incomingInvite?.callID);
}

发送DTMF(双音多频)

要在通话时发送DTMF消息,可以调用.dtmf(callID, tone)方法,其中tone是一个表示您想要按下的字符的字符串值:

_telnyxClient.call.dtmf(_telnyxClient.call.callId, tone);

静音通话

要静音通话,只需调用.onMuteUnmutePressed()方法即可:

_telnyxClient.call.onMuteUnmutePressed();

切换扬声器

要切换扬声器,可以调用.enableSpeakerPhone(bool)方法:

_telnyxClient.call.enableSpeakerPhone(true);

保持通话

要保持通话,可以调用.onHoldUnholdPressed()方法:

_telnyxClient.call.onHoldUnholdPressed();

高级用法 - 推送通知

添加推送通知 - Android平台

Android平台使用Firebase Cloud Messaging来传递推送通知。为了在Android移动设备上接收来电时接收通知,您需要在应用程序中启用Firebase Cloud Messaging。 有关详细教程,请访问我们的官方 推送通知文档。 演示应用程序使用 flutter_callkit_incoming 插件来显示来电。要显示通知当接到来电时,您可以按照以下步骤操作:

  1. 监听后台推送通知,在您的main方法中实现FirebaseMessaging.onBackgroundMessage方法:
@pragma('vm:entry-point')
Future<void> main() async {
    WidgetsFlutterBinding.ensureInitialized();

    if (defaultTargetPlatform == TargetPlatform.android) {
      // Android Only - Push Notifications
        await Firebase.initializeApp();
        FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
      
        await FirebaseMessaging.instance
                .setForegroundNotificationPresentationOptions(
         alert: true,
         badge: true,
         sound: true,
      );
    }
      runApp(const MyApp());
}
  1. 选择性地将metadata添加到CallKitParams的extra字段中:
static Future showNotification(RemoteMessage message)  {
  CallKitParams callKitParams = CallKitParams(
    android:...,
      ios:...,
      extra: message.data,
  )
  await FlutterCallkitIncoming.showCallkitIncoming(callKitParams);
}
  1. _firebaseMessagingBackgroundHandler方法中处理推送通知:
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
      //显示通知
      showNotification(message);
      
      //监听来自FlutterCallkitIncoming的动作
      FlutterCallkitIncoming.onEvent.listen((CallEvent? event) async {
       switch (event!.event) {
        case Event.actionCallAccept:
         // 设置telnyx元数据以便在应用进入前台时访问
         TelnyxClient.setPushMetaData(
                 message.data, isAnswer: true, isDecline: false);
         break;
        case Event.actionCallDecline:
        /*
        * 当用户从推送通知中拒绝通话时,应用程序将不再可见,我们必须在这里处理endCall用户。
        * 登录到TelnyxClient并结束通话
        * */
          ...
       }});
}
  1. 当应用程序进入前台时,使用TelnyxClient.getPushMetaData()方法检索元数据。此数据仅在第一次访问时可用,并在之后变为null
Future<void> _handlePushNotification() async {
   final  data = await TelnyxClient.getPushMetaData();
   PushMetaData? pushMetaData = PushMetaData.fromJson(data);
  if (pushMetaData != null) {
    _telnyxClient.handlePushNotification(pushMetaData, credentialConfig, tokenConfig);
  }
}
  1. 为了在前台处理推送通话,监听通话事件并调用handlePushNotification方法:
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
   switch (event!.event) {
   case Event.actionCallIncoming:
   // 从extras中检索推送元数据
   final data = await TelnyxClient.getPushData();
   ...
  _telnyxClient.handlePushNotification(pushMetaData, credentialConfig, tokenConfig);
    break;
   case Event.actionCallStart:
    ....
   break;
   case Event.actionCallAccept:
     ...
   logger.i('Call Accepted Attach Call');
   break;
   });

最佳实践 - Android上的推送通知

  1. 请求Android 13+设备的通知权限以显示推送通知。更多信息可以在此处找到 这里
  2. 推送通知仅在运行在debug模式下的应用的前台有效(您将在终止应用时不会收到推送通知)。
  3. 在前台通话时,您可以使用FirebaseMessaging.onMessage.listen方法来监听来电并显示通知。
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
        TelnyxClient.setPushMetaData(message.data);
        NotificationService.showNotification(message);
        mainViewModel.callFromPush = true;
      });
  1. 为了在后台处理推送通知,使用FirebaseMessaging.onBackgroundMessage方法来监听来电并显示通知,并确保在用户接听电话时设置TelnyxClient.setPushMetaData
TelnyxClient.setPushMetaData(
                 message.data, isAnswer: true, isDecline: false);
  1. 当您调用telnyxClient.handlePushNotification时,它会连接到telnyxClient,确保在此之后不调用telnyxClient.connect()方法。例如,边缘情况可能是如果在Widget的init方法中调用telnyxClient.connect(),它将始终调用connect方法。
bool waitingForInvite = false;

void accept() {

if (_incomingInvite != null) {
  // 如果接收到的邀请及时到达,则接受通话
      _currentCall = _telnyxClient.acceptCall(
          _incomingInvite!, _localName, _localNumber, "State");
    } else {
      // 如果有早期接受,则将waitingForInvite设置为true
      waitingForInvite = true;
    }
}

_telnyxClient.onSocketMessageReceived = (TelnyxMessage message) {
      switch (message.socketMethod) {
        ...
        case SocketMethod.INVITE:
          {
            if (callFromPush) {
              // 对于早期接受通话
              if (waitingForInvite) {
                // 接受通话
                accept();
                waitingForInvite = false;
              }
              callFromPush = false;
            }

          }
        ...
      }
 }

添加推送通知 - iOS平台

iOS平台使用Apple Push Notification Service (APNS) 和 Pushkit来传递和接收推送通知。 有关详细教程,请访问我们的官方 推送通知文档

  1. 注册/失效iOS的推送设备令牌
func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
    print(credentials.token)
    let deviceToken = credentials.token.map { String(format: "%02x", $0) }.joined()
    // 将deviceToken保存到您的服务器
    SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(deviceToken)
}

func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
    SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP("")
}
  1. 为了让前台通话工作,您需要在restorationHandler代理函数中注册callkit。您也可以选择使用iOS官方文档注册callkit。
override func application(_ application: UIApplication,
                                continue userActivity: NSUserActivity,
                                restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
                                
            let nameCaller = handleObj.getDecryptHandle()["nameCaller"] as? String ?? ""
            let handle = handleObj.getDecryptHandle()["handle"] as? String ?? ""
            let data = flutter_callkit_incoming.Data(id: UUID().uuidString, nameCaller: nameCaller, handle: handle, type: isVideo ? 1 : 0)
            // 设置更多数据...
            data.nameCaller = "dummy"
            SwiftFlutterCallkitIncomingPlugin.sharedInstance?.startCall(data, fromPushKit: true)
         
         }                         
  1. 在AppDelegate.swift类中监听来电
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
            print("didReceiveIncomingPushWith")
            guard type == .voIP else { return }
            
            if let metadata = payload.dictionaryPayload["metadata"] as? [String: Any] {
                var callID = UUID.init().uuidString
                if let newCallId = (metadata["call_id"] as? String),
                   !newCallId.isEmpty {
                    callID = newCallId
                }
                let callerName = (metadata["caller_name"] as? String) ?? ""
                let callerNumber = (metadata["caller_number"] as? String) ?? ""
                
                let id = payload.dictionaryPayload["call_id"] as? String ??  UUID().uuidString
                
                let data = flutter_callkit_incoming.Data(id: id, nameCaller: callerName, handle: callerNumber, type: isVideo ? 1 : 0)
                data.extra = payload.dictionaryPayload as NSDictionary
                data.normalHandle = 1              
                
                let caller = callerName.isEmpty ? (callerNumber.isEmpty ? "Unknown" : callerNumber) : callerName
                let uuid = UUID(uuidString: callID)
                
                data.uuid = uuid!.uuidString
                data.nameCaller = caller
                
                SwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(data, fromPushKit: true)
            }
        }
  1. 监听通话事件并调用handlePushNotification方法
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
   switch (event!.event) {
   case Event.actionCallIncoming:
   // 从extras中检索推送元数据
    PushMetaData? pushMetaData = PushMetaData.fromJson(event.body['extra']['metadata']);
    _telnyxClient.handlePushNotification(pushMetaData, credentialConfig, tokenConfig);
    break;
   case Event.actionCallStart:
    ....
   break;
   case Event.actionCallAccept:
     ...
   logger.i('Call Accepted Attach Call');
   break;
   });

处理延迟通知

如果由于无网络连接而通知到达非常晚,最好将其标记为未接来电。您可以使用以下代码片段实现这一点:

const CALL_MISSED_TIMEOUT = 60;

DateTime nowTime = DateTime.now();
Duration? difference = nowTime?.difference(message.sentTime!);

if (difference.inSeconds > CALL_MISSED_TIMEOUT) {
    NotificationService.showMissedCallNotification(message);
    return;
}

更多关于Flutter WebRTC通信插件telnyx_webrtc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter WebRTC通信插件telnyx_webrtc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中使用telnyx_webrtc插件来实现WebRTC通信,你可以参考以下步骤和代码案例来设置基本的WebRTC功能。telnyx_webrtc插件允许你使用Telnyx的WebRTC服务进行音视频通信。以下是一个基本的实现示例:

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加telnyx_webrtc依赖:

dependencies:
  flutter:
    sdk: flutter
  telnyx_webrtc: ^最新版本号  # 替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

2. 配置权限

AndroidManifest.xml中,你可能需要添加一些必要的权限,比如摄像头和麦克风权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />

3. 初始化WebRTC

在你的Flutter应用中,你需要初始化WebRTC服务。这通常包括设置Telnyx的凭证和创建WebRTC连接。

import 'package:flutter/material.dart';
import 'package:telnyx_webrtc/telnyx_webrtc.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  TelnyxWebRTC? webRTC;

  @override
  void initState() {
    super.initState();
    initializeWebRTC();
  }

  void initializeWebRTC() async {
    // 替换为你的Telnyx凭证
    String apiKey = 'YOUR_TELNYX_API_KEY';
    String apiSecret = 'YOUR_TELNYX_API_SECRET';

    webRTC = TelnyxWebRTC(apiKey: apiKey, apiSecret: apiSecret);

    // 监听连接状态变化
    webRTC!.onConnectionChange = (state) {
      print('Connection state: $state');
    };

    // 监听消息
    webRTC!.onMessage = (message) {
      print('Received message: $message');
    };

    // 尝试建立连接
    await webRTC!.connect();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Telnyx WebRTC Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('WebRTC is initializing...'),
              ElevatedButton(
                onPressed: () {
                  if (webRTC != null && webRTC!.isConnected!) {
                    // 发送消息示例
                    webRTC!.sendMessage('Hello, WebRTC!');
                  }
                },
                child: Text('Send Message'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

4. 处理连接和消息

在上面的代码中,我们初始化了TelnyxWebRTC对象,并监听了连接状态变化和消息接收。在实际应用中,你可能需要处理更多的连接逻辑,比如处理远程流的接收和显示。

5. 释放资源

在适当的时候(比如应用关闭或用户注销时),你应该释放WebRTC资源:

@override
void dispose() {
  webRTC?.disconnect();
  webRTC = null;
  super.dispose();
}

注意

  • 确保你已经注册了Telnyx账号并获取了API凭证。
  • telnyx_webrtc插件的API可能会随着版本更新而变化,请参考最新的官方文档和示例代码。
  • 在实际应用中,处理错误和异常是很重要的,上面的示例代码为了简洁而省略了这些部分。

这个示例展示了如何使用telnyx_webrtc插件进行基本的WebRTC通信初始化。根据你的需求,你可能需要添加更多的功能和错误处理逻辑。

回到顶部