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
目录
配置
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 文件中添加 NSCameraUsageDescription
和 NSMicrophoneUsageDescription
:
<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
更多关于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");
}
}
}