Flutter视频会议插件custom_new_jitsi_meet的使用

Flutter视频会议插件custom_new_jitsi_meet的使用

Jitsi Meet 插件用于 Flutter,支持 Android、iOS 和 Web 平台。

Jitsi Meet 是一个开源(Apache 许可证)的 WebRTC JavaScript 应用程序,它使用 Jitsi Videobridge 提供高质量、安全且可扩展的视频会议。

更多关于 Jitsi Meet 的信息可以在这里找到:https://github.com/jitsi/jitsi-meet


目录

  1. 配置
  2. 加入会议
  3. 监听会议事件
  4. 程序化关闭会议
  5. 贡献

配置

iOS

Podfile

确保在您的 Podfile 中添加以下条目,声明平台为 12.0 或更高版本,并禁用 BITCODE:

platform :ios, '12.0'

...

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['ENABLE_BITCODE'] = 'NO'
    end
  end
end

Info.plist

在 Info.plist 文件中添加 NSCameraUsageDescriptionNSMicrophoneUsageDescription

<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) MyApp 需要访问您的相机进行会议。</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) MyApp 需要访问您的麦克风进行会议。</string>

Android

Gradle

将构建工具 Gradle 的依赖设置为至少 7.3.1:

dependencies {
    classpath 'com.android.tools.build:gradle:7.3.1' <!-- 升级此版本 -->
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}

设置 Gradle Wrapper 的最低版本为 7.4:

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip <!-- 升级此版本 -->

AndroidManifest.xml

Jitsi Meet 的 AndroidManifest.xml 会与您的项目冲突,尤其是 application:label 字段。解决方法是在 android/app/src/main/AndroidManifest.xml 中添加 tools 库并使用 tools:replace="android:label" 替换应用标签:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="yourpackage.com"
    xmlns:tools="http://schemas.android.com/tools"> <!-- 添加此行 -->
    <application tools:replace="android:label" android:name="your.application.name" android:label="My Application" android:icon="@mipmap/ic_launcher">
        ...
    </application>
    ...
</manifest>

最低 SDK 版本 24

android/app/build.gradle 中将最小 SDK 版本更新为 24:

defaultConfig {
    applicationId "com.thorito.jitsi_meet_example"
    minSdkVersion 24 // 必须设置为 24
    targetSdkVersion 33
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}

Proguard

Jitsi 的 SDK 启用了 Proguard,但如果没有 proguard-rules.pro 文件,发布 APK 构建可能会缺少 Flutter Wrapper 和 React Native 代码。在 android/app/build.gradle 中启用 Proguard:

buildTypes {
    release {
        signingConfig signingConfigs.debug

        // 添加以下两行以启用 Proguard
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

然后在同一目录下创建 proguard-rules.pro 文件,内容参考官方示例:https://github.com/rise-ng/custom_new_jitsi_meet/blob/master/omni_jitsi_meet/example/android/app/proguard-rules.pro

注意: 如果不创建 proguard-rules.pro 文件,尝试加入会议或打开会议界面时,应用可能会崩溃。日志中可能会看到以下错误:

## 应用崩溃 ##
java.lang.RuntimeException: Parcel android.os.Parcel@8530c57: Unmarshalling unknown type code 7536745 at offset 104
    at android.os.Parcel.readValue(Parcel.java:2747)
    at android.os.Parcel.readSparseArrayInternal(Parcel.java:3118)
    at android.os.Parcel.readSparseArray(Parcel.java:2351)
    .....

## 会议无法打开,返回上一页 ##
W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.BV.LinearGradient.LinearGradientManager
W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.uimanager.g
W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.ARTGroupViewManager
W/unknown:ViewManagerPropertyUpdater: Could not find generated setter for class com.facebook.react.views.art.a
.....

Web

在 Web 端实现时,需要在 index.html 中包含 Jitsi 的 JS 库:

<script src="https://meet.jit.si/external_api.js" type="application/javascript"></script>

示例代码:

<body>
  <!-- 此脚本安装 service_worker.js 以提供 PWA 功能 -->
  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', function () {
        navigator.serviceWorker.register('/flutter_service_worker.js');
      });
    }
  </script>
  <script src="https://meet.jit.si/external_api.js" type="application/javascript"></script>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

加入会议

示例代码如下:

_joinMeeting() async {
  try {
    FeatureFlag featureFlag = FeatureFlag();
    featureFlag.resolution = FeatureFlagVideoResolution.MD_RESOLUTION; // 限制视频分辨率为 360p

    final options = JitsiMeetingOptions(
        room: 'myroom',
        // 必填,空格会被去掉
        serverURL: 'https://someHost.com',
        // 示例: https://meet.jit.si
        subject: 'Meeting with thorito',
        userDisplayName: 'My Name',
        userEmail: 'myemail@email.com',
        userAvatarURL: 'https://someimageurl.com/image.jpg',
        // 或 .png
        audioOnly: false,
        audioMuted: false,
        videoMuted: false,
        featureFlags: featureFlag,
        webOptions: {
          'roomName': 'myroom',
          'width': '100%',
          'height': '100%',
          'enableWelcomePage': false,
          'enableNoAudioDetection': true,
          'enableLobbyChat': false,
          'enableNoisyMicDetection': true,
          'enableClosePage': false,
          'disableRemoveRaisedHandOnFocus': false,
          'disableReactions': true,
          'prejoinPageEnabled': false,
          'hideDisplayName': true,
          'hideConferenceSubject': true,
          'hideConferenceTimer': true,
          'hideParticipantsStats': true,
          'hideLobbyButton': true,
          'disableInviteFunctions': true,
          'chromeExtensionBanner': null,
          'readOnlyName': true,
          'participantsPane': {
            'hideModeratorSettingsTab': true,
            'hideMoreActionsButton': true,
            'hideMuteAllButton': true,
          },
          'hideAddRoomButton': true,
          'breakoutRooms': {
            'hideAddRoomButton': true,
            'hideAutoAssignButton': true,
            'hideJoinRoomButton': true,
            'hideModeratorSettingsTab': true,
            'hideMoreActionsButton': true,
            'hideMuteAllButton': true,
          },
          'lang': 'en',
          'configOverwrite': {
            'doNotFlipLocalVideo': true,
            'prejoinPageEnabled': false,
            'disableDeepLinking': true,
            'enableLobbyChat': false,
            'enableClosePage': false,
            'hideDisplayName': true,
            'hideConferenceTimer': true,
            'hideParticipantsStats': true,
            'hideLobbyButton': true,
            'readOnlyName': true,
            'giphy': {
              'enable': false,
            },
            'participantsPane': {
              'hideModeratorSettingsTab': true,
              'hideMoreActionsButton': true,
              'hideMuteAllButton': true,
            },
            'hideAddRoomButton': true,
            'breakoutRooms': {
              'hideAddRoomButton': true,
              'hideAutoAssignButton': true,
              'hideJoinRoomButton': true,
              'hideModeratorSettingsTab': true,
              'hideMoreActionsButton': true,
              'hideMuteAllButton': true,
            },
            // https://github.com/jitsi/jitsi-meet/blob/master/config.js
            'toolbarButtons': [
              'camera',
              'desktop',
              'download',
              'filmstrip',
              'hangup',
              'highlight',
              'microphone',
              'noisesuppression',
              'select-background',
              'tileview',
              'toggle-camera',
              'whiteboard'
            ]
          },
          'userInfo': {
            'displayName': 'My Name',
            'email': 'myemail@email.com',
          }
        });

  } catch (error) {
    debugPrint("error: $error");
  }
}

JitsiMeetingOptions 字段说明

字段 是否必填 默认值 描述
room N/A 唯一房间名称,附加到 serverURL。有效字符:字母数字、破折号和下划线。
subject $room 会议名称,显示在会议顶部。如果为空,默认为房间名称,破折号和下划线替换为空格,首字母大写。
userDisplayName Fellow Jitster 用户显示名称。
userEmail none 用户电子邮件地址。
audioOnly false 开启音频会议,无视频。
audioMuted false 开启会议时静音。
videoMuted false 开启会议时关闭摄像头。
serverURL meet.jitsi.si 指定自定义服务器。必须为格式正确的绝对 URL,例如 https://someHost.com。默认为 Jitsi Meet 的服务器。
userAvatarURL N/A none 用户头像 URL。
token N/A none JWT 令牌用于身份验证。
featureFlag 见下方说明 FeatureFlag 类对象,用于启用/禁用功能和设置视频分辨率。
configOverrides 见下方说明 ConfigOverrides 类对象,用于启用/禁用功能和设置视频分辨率。
webOptions 见下方说明 Web 设置

FeatureFlag 字段说明

FeatureFlag 允许您限制视频分辨率并启用/禁用 Jitsi Meet SDK 的某些功能。如果未提供任何标志,则使用默认值。

我们使用了官方标志列表:https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js

标志 默认值(Android) 默认值(iOS) 描述
addPeopleEnabled true true 启用蓝色按钮“添加人员”,当您单独通话时显示。需要 inviteEnabled 标志工作。
audioFocusDisabled false false 标志表示 SDK 是否不应要求音频焦点。
audioMuteButtonEnabled true true 标志表示是否应显示音频静音按钮。
audioOnlyButtonEnabled true true 标志表示溢出菜单中的音频按钮是否启用。
calendarEnabled true auto 启用日历集成。
callIntegrationEnabled true true 启用呼叫集成(iOS 上的 CallKit,Android 上的 ConnectionService)。
carModeEnabled true auto 标志表示是否应启用日历集成。
closeCaptionsEnabled true true 启用菜单中的字幕选项。
conferenceTimerEnabled true true 启用会议计时器。
chatEnabled true true 启用聊天(按钮和功能)。
filmstripEnabled true true 标志表示是否应启用胶片条。
fullscreenEnabled true true 标志表示是否应启用全屏(沉浸式)模式。
helpButtonEnabled true true 标志表示是否应启用帮助按钮。
inviteEnabled true true 启用菜单中的邀请选项。
iOSRecordingEnabled N/A false 在 iOS 上启用录制。
iOSScreenSharingEnabled N/A false 标志表示是否应在 iOS 上启用屏幕共享。
androidScreenSharingEnabled true N/A 标志表示是否应在 Android 上启用屏幕共享。
speakerStatsEnabled true true 标志表示是否应启用发言人统计。
kickOutEnabled true true 启用视频缩略图中的踢出选项。
liveStreamingEnabled auto auto 启用菜单中的直播选项。
lobbyModeEnabled true true 标志表示是否应启用候机室按钮。
meetingNameEnabled true true 显示会议名称。
meetingPasswordEnabled true true 启用菜单中的会议密码选项(如果设置了密码,对话框仍然会出现)。
notificationsEnabled true true 标志表示是否应启用通知。
overflowMenuEnabled auto auto 标志表示是否应显示音频溢出菜单按钮。
pipEnabled auto auto 启用画中画模式。
pipWhileScreenSharingEnabled false false 标志表示是否应在屏幕共享时显示画中画按钮。
prejoinPageEnabled true true 标志表示是否应启用预加入页面。
prejoinPageHideDisplayName false false 标志表示是否应在预加入页面上显示参与者名称编辑字段。
raiseHandEnabled true true 启用菜单中的举手选项。
recordingEnabled auto N/A 启用菜单中的录制选项。
replaceParticipant false false 标志表示用户是否应通过 replaceParticipant 功能加入会议。
resoulution N/A N/A 设置本地和最大远程视频分辨率。覆盖服务器配置。接受值:LD_RESOLUTION(180p)、MD_RESOLUTION(360p)、SD_RESOLUTION(480p/SD)、HD_RESOLUTION(720p/HD)。
securityOptionsEnabled true true 标志表示是否应启用安全选项按钮。
serverURLChangeEnabled true true 启用服务器 URL 更改。
settingsEnabled true true 标志表示是否应启用设置。
tileViewEnabled true true 启用菜单中的平铺视图选项。
toolboxAlwaysVisible false false 工具栏(按钮和菜单)始终可见(否则单击后显示)。
toolboxEnabled true true 标志表示是否应启用工具栏。
videoMuteButtonEnabled true true 标志表示是否应显示视频静音按钮。
videoShareButtonEnabled true true 启用视频共享按钮。
welcomePageEnabled false false 启用欢迎页面。“欢迎页面列出最近的会议和日历约会,旨在用于独立应用程序。”

关于呼叫集成的备注: Android 上的呼叫集成(称为 ConnectionService)已在官方 Jitsi Meet 应用中禁用,因为它会造成很多问题。您也应禁用它以避免这些问题。


JitsiMeetingResponse 字段说明

字段 类型 描述
isSuccess bool 成功指示符。
message String 成功消息或错误作为字符串。
error dynamic 可选,仅在 isSuccess 为 false 时存在。错误对象。

监听会议事件

支持的事件

名称 描述 参数
onOpened 打开事件 none
onClosed 关闭事件 none
onConferenceWillJoin 会议正在加载 url
onConferenceJoined 用户已加入会议 url
onConferenceTerminated 用户退出会议 url, error
onPictureInPictureWillEnter 用户进入画中画模式 message
onPictureInPictureTerminated 用户退出画中画模式 message
onError 监听会议事件时发生错误 error
onAudioMutedChanged 音频静音状态改变 isMuted
onVideoMutedChanged 视频静音状态改变 isMuted
onScreenShareToggled 屏幕共享切换 participantId, isSharing
onParticipantJoined 参与者加入 email, name, role, participantId
onParticipantLeft 参与者离开 participantId
onParticipantsInfoRetrieved 获取参与者信息 participantsInfo, requestId
onChatMessageReceived 收到聊天消息 senderOd, message, isPrivate
onChatToggled 聊天窗口切换 isOpen

每场会议的事件

要监听每场会议的事件,在调用 joinMeeting 时传递 JitsiMeetingListener。当触发 onConferenceTerminated 事件时,监听器会自动移除。

final JitsiMeetingResponse response = await JitsiMeet.joinMeeting(
  options,
  listener: JitsiMeetingListener(
      onOpened: () {
        debugPrint("JitsiMeetingListener - onOpened");
      },
      onClosed: () {
        debugPrint("JitsiMeetingListener - onClosed");
      },
      onError: (error) {
        debugPrint("JitsiMeetingListener - onError: error: $error");
      },
      onConferenceWillJoin: (url) {
        debugPrint(
            "JitsiMeetingListener - onConferenceWillJoin: url: $url");
      },
      onConferenceJoined: (url) {
        debugPrint("JitsiMeetingListener - onConferenceJoined: url:$url");
      },
      onConferenceTerminated: (url, error) {
        debugPrint(
            "JitsiMeetingListener - onConferenceTerminated: url: $url, error: $error");
      },
      onParticipantLeft: (participantId) {
        debugPrint(
            "JitsiMeetingListener - onParticipantLeft: $participantId");
      },
      onParticipantJoined: (email, name, role, participantId) {
        debugPrint("JitsiMeetingListener - onParticipantJoined: "
            "email: $email, name: $name, role: $role, "
            "participantId: $participantId");
      },
      onAudioMutedChanged: (muted) {
        debugPrint(
            "JitsiMeetingListener - onAudioMutedChanged: muted: $muted");
      },
      onVideoMutedChanged: (muted) {
        debugPrint(
            "JitsiMeetingListener - onVideoMutedChanged: muted: $muted");
      },
      onScreenShareToggled: (participantId, isSharing) {
        debugPrint("JitsiMeetingListener - onScreenShareToggled: "
            "participantId: $participantId, isSharing: $isSharing");
      },
      genericListeners: [
        JitsiGenericListener(
            eventName: 'readyToClose',
            callback: (dynamic message) {
              debugPrint("JitsiMeetingListener - readyToClose callback");
            }),
      ]),
);

程序化关闭会议

JitsiMeet.hangUp();

更多关于Flutter视频会议插件custom_new_jitsi_meet的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter视频会议插件custom_new_jitsi_meet的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


custom_new_jitsi_meet 是一个用于在 Flutter 应用中集成 Jitsi Meet 视频会议功能的插件。它基于 Jitsi Meet SDK,允许你在应用中轻松地创建和加入视频会议。以下是如何使用 custom_new_jitsi_meet 插件的详细步骤:

1. 添加依赖

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

dependencies:
  flutter:
    sdk: flutter
  custom_new_jitsi_meet: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来获取依赖。

2. 配置 Android 和 iOS 项目

Android

android/app/src/main/AndroidManifest.xml 文件中添加以下权限:

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

iOS

ios/Runner/Info.plist 文件中添加以下权限:

<key>NSCameraUsageDescription</key>
<string>We need access to your camera for video calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>We need access to your microphone for audio calls.</string>

3. 使用插件

初始化插件

在你的 Dart 文件中导入插件并初始化:

import 'package:custom_new_jitsi_meet/custom_new_jitsi_meet.dart';

创建和加入会议

你可以使用 CustomNewJitsiMeet 类来创建和加入会议。以下是一个简单的示例:

void joinMeeting() async {
  try {
    var options = CustomNewJitsiMeetOptions(
      roomName: "your_room_name", // 会议房间名称
      serverUrl: "https://meet.jit.si", // Jitsi Meet 服务器 URL
      subject: "Meeting Subject", // 会议主题
      userDisplayName: "Your Name", // 用户显示名称
      userEmail: "your_email@example.com", // 用户邮箱
      audioMuted: false, // 是否静音
      videoMuted: false, // 是否关闭视频
      audioOnly: false, // 是否仅音频
    );

    await CustomNewJitsiMeet.joinMeeting(options);
  } catch (error) {
    print("Error: $error");
  }
}

处理会议事件

你可以监听会议事件,例如会议结束、用户加入或离开等:

CustomNewJitsiMeet.addListener(
  JitsiMeetingListener(
    onConferenceWillJoin: (url) {
      print("Conference will join with url: $url");
    },
    onConferenceJoined: (url) {
      print("Conference joined with url: $url");
    },
    onConferenceTerminated: (url) {
      print("Conference terminated with url: $url");
    },
    onError: (error) {
      print("Error: $error");
    },
  ),
);

离开会议

你可以通过调用 CustomNewJitsiMeet.closeMeeting() 来离开会议:

void leaveMeeting() async {
  await CustomNewJitsiMeet.closeMeeting();
}

4. 运行应用

确保你已经配置好了 Android 和 iOS 项目,然后运行你的 Flutter 应用。你应该能够通过调用 joinMeeting() 方法来创建和加入 Jitsi Meet 视频会议。

5. 其他注意事项

  • 服务器 URL: 如果你使用的是自定义的 Jitsi Meet 服务器,请确保 serverUrl 指向正确的地址。
  • 权限: 确保在 Android 和 iOS 上正确配置了所需的权限。
  • 错误处理: 在实际应用中,建议添加适当的错误处理逻辑,以处理可能出现的异常情况。

6. 示例代码

以下是一个完整的示例代码:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Jitsi Meet Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: joinMeeting,
            child: Text('Join Meeting'),
          ),
        ),
      ),
    );
  }

  void joinMeeting() async {
    try {
      var options = CustomNewJitsiMeetOptions(
        roomName: "your_room_name",
        serverUrl: "https://meet.jit.si",
        subject: "Meeting Subject",
        userDisplayName: "Your Name",
        userEmail: "your_email@example.com",
        audioMuted: false,
        videoMuted: false,
        audioOnly: false,
      );

      await CustomNewJitsiMeet.joinMeeting(options);
    } catch (error) {
      print("Error: $error");
    }
  }
}
回到顶部