Flutter视频通信插件livekit_client_custom的使用
Flutter视频通信插件livekit_client_custom的使用
LiveKit Flutter SDK
官方Flutter SDK用于LiveKit。可以轻松地为您的Flutter应用添加实时音视频功能。
文档
更多文档和指南可在 https://docs.livekit.io 查看。
当前支持的功能
功能 | 订阅/发布 | 多流传输 | 背景音频 | 屏幕共享 |
---|---|---|---|---|
Web | 🟢 | 🟢 | 🟢 | 🟢 |
iOS | 🟢 | 🟢 | 🟢 | 🟢 |
Android | 🟢 | 🟢 | 🟢 | 🟢 |
Mac | 🟢 | 🟢 | 🟢 | 🟢 |
Windows | 🟢 | 🟢 | 🟢 | 🟢 |
🟢 = 可用
🟡 = 即将推出(进行中)
🔴 = 当前不可用(未来可能)
示例应用
我们在example/文件夹中构建了一个多人会议应用。您可以从任何支持LiveKit客户端的应用程序加入同一个房间。
安装
在 pubspec.yaml
文件中包含此包:
dependencies:
livekit_client_custom: <version>
iOS
在您的 Info.plist
文件中声明相机和麦克风的使用需求。
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) 使用您的摄像头</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) 使用您的麦克风</string>
如果启用后台模式,您的应用程序可以在切换到后台时运行语音通话。在Xcode中选择应用程序目标,点击“能力”标签,启用后台模式,并选中“音频、AirPlay和画中画”。
您的 Info.plist
文件应包含以下条目:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
注意事项
由于xcode 14不再支持32位构建,我们的最新版本基于libwebrtc m104+,iOS框架不再支持32位构建。强烈建议升级到Flutter 3.3.0+。如果您使用的是Flutter 3.0.0或更低版本,则由于缺少i386和arm 32位框架,您的Flutter应用可能无法正确编译(参见issue 132 和 issue 172)。
您可以通过修改 {projects_dir}/ios/Podfile
来解决此问题:
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES' # <= 这一行
end
end
end
对于iOS,最低支持的部署目标为 12.1
。您需要在Podfile中添加以下内容:
platform :ios, '12.1'
更新部署目标后,可能需要删除 Podfile.lock
并重新运行 pod install
。
Android
我们需要声明一组权限,这些权限需要在您的 AppManifest.xml
中声明。这些权限是由我们依赖的Flutter WebRTC所需的。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.your.package">
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
...
</manifest>
桌面支持
为了启用Flutter桌面开发,请遵循 此处 的说明。
在M1 Mac上,您还需要安装x86_64版本的FFI:
sudo arch -x86_64 gem install ffi
在Windows上,需要安装 VS 2019(参见Flutter文档中的链接将下载VS 2022)。
使用
连接到房间,发布视频和音频
final roomOptions = RoomOptions(
adaptiveStream: true,
dynacast: true,
// ... 你的房间选项
);
final room = await LiveKitClient.connect(url, token, roomOptions: roomOptions);
try {
// 在iOS模拟器中运行时视频可能会失败
await room.localParticipant.setCameraEnabled(true);
} catch (error) {
print('无法发布视频,错误: $error');
}
await room.localParticipant.setMicrophoneEnabled(true);
屏幕共享
屏幕共享在所有平台上都受支持。您可以使用以下方法启用它:
room.localParticipant.setScreenShareEnabled(true);
Android
在Android上,您需要在 AndroidManifest.xml
中定义一个前台服务。
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
...
<service
android:name="de.julianassmann.flutter_background.IsolateHolderService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaProjection" />
</application>
</manifest>
iOS
在iOS上,您需要一个广播扩展来捕获其他应用程序的屏幕内容。请参阅 设置指南 获取详细说明。
桌面(Windows/MacOS)
在桌面上,您可以使用 ScreenSelectDialog
选择要共享的窗口或屏幕。
try {
final source = await showDialog<DesktopCapturerSource>(
context: context,
builder: (context) => ScreenSelectDialog(),
);
if (source == null) {
print('取消屏幕共享');
return;
}
print('DesktopCapturerSource: ${source.id}');
var track = await LocalVideoTrack.createScreenShareTrack(
ScreenShareCaptureOptions(
sourceId: source.id,
maxFrameRate: 15.0,
),
);
await room.localParticipant.publishVideoTrack(track);
} catch (e) {
print('无法发布屏幕共享: $e');
}
高级轨道操作
setCameraEnabled
和 setMicrophoneEnabled
辅助函数是轨道API的包装器。
您也可以手动创建和发布轨道:
var localVideo = await LocalVideoTrack.createCameraTrack();
await room.localParticipant.publishVideoTrack(localVideo);
渲染视频
每个轨道都可以通过提供的 VideoTrackRenderer
小部件单独渲染。
VideoTrack? track;
@override
Widget build(BuildContext context) {
if (track != null) {
return VideoTrackRenderer(track);
} else {
return Container(
color: Colors.grey,
);
}
}
音频处理
只要订阅了音频轨道,音频就会自动播放。
处理变化
LiveKit客户端使得构建响应状态变化的声明式UI变得简单。它以两种方式通知更改:
ChangeNotifier
- 通用更改通知。这在构建反应式UI且只关心可能影响渲染的更改时非常有用。EventsListener<Event>
- 监听特定事件的模式(请参阅 events.dart)。
以下示例展示了如何使用两者来响应房间事件。
class RoomWidget extends StatefulWidget {
final Room room;
RoomWidget(this.room);
@override
State<StatefulWidget> createState() {
return _RoomState();
}
}
class _RoomState extends State<RoomWidget> {
late final EventsListener<RoomEvent> _listener = widget.room.createListener();
@override
void initState() {
super.initState();
// 用于通用更改更新
widget.room.addListener(_onChange);
// 用于特定事件
_listener
..on<RoomDisconnectedEvent>((_) {
// 处理断开连接
})
..on<ParticipantConnectedEvent>((e) {
print("参与者已加入: ${e.participant.identity}");
});
}
@override
void dispose() {
// 确保释放监听器以停止接收进一步更新
_listener.dispose();
widget.room.removeListener(_onChange);
super.dispose();
}
void _onChange() {
// 执行计算并调用setState
// setState将触发构建
setState(() {
// 您的更新在这里
});
}
@override
Widget build(BuildContext context) {
// 您的构建函数
}
}
类似地,当渲染参与者时,您也可以这样做。响应变化使得处理发布的/未发布的轨道或在UI中重新排序参与者成为可能。
class VideoView extends StatefulWidget {
final Participant participant;
VideoView(this.participant);
@override
State<StatefulWidget> createState() {
return _VideoViewState();
}
}
class _VideoViewState extends State<VideoView> {
TrackPublication? videoPub;
@override
void initState() {
super.initState();
widget.participant.addListener(this._onParticipantChanged);
// 触发初始更改
_onParticipantChanged();
}
@override
void dispose() {
widget.participant.removeListener(this._onParticipantChanged);
super.dispose();
}
@override
void didUpdateWidget(covariant VideoView oldWidget) {
oldWidget.participant.removeListener(_onParticipantChanged);
widget.participant.addListener(_onParticipantChanged);
_onParticipantChanged();
super.didUpdateWidget(oldWidget);
}
void _onParticipantChanged() {
var subscribedVideos = widget.participant.videoTracks.values.where((pub) {
return pub.kind == TrackType.VIDEO && !pub.isScreenShare && pub.subscribed;
});
setState(() {
if (subscribedVideos.length > 0) {
var videoPub = subscribedVideos.first;
// 当静音时显示占位符
if (!videoPub.muted) {
this.videoPub = videoPub;
return;
}
}
this.videoPub = null;
});
}
@override
Widget build(BuildContext context) {
var videoPub = this.videoPub;
if (videoPub != null) {
return VideoTrackRenderer(videoPub.track as VideoTrack);
} else {
return Container(
color: Colors.grey,
);
}
}
}
静音/取消静音本地轨道
在 LocalTrackPublication
上,您可以控制轨道是否静音,通过设置其 muted
属性。更改静音状态将生成 onTrackMuted
或 onTrackUnmuted
委托调用给本地参与者。其他参与者也会收到状态更改的通知。
// 静音轨道
trackPub.muted = true;
// 取消静音轨道
trackPub.muted = false;
更多关于Flutter视频通信插件livekit_client_custom的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter视频通信插件livekit_client_custom的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
livekit_client_custom
是一个用于 Flutter 的插件,用于实现视频通信功能。它基于 LiveKit 的 SDK,允许开发者在 Flutter 应用中集成实时音视频通信功能。以下是使用 livekit_client_custom
插件的基本步骤:
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 livekit_client_custom
插件的依赖:
dependencies:
flutter:
sdk: flutter
livekit_client_custom: ^1.0.0 # 请根据最新版本号进行替换
然后运行 flutter pub get
来安装依赖。
2. 初始化 LiveKit 客户端
在你的 Flutter 应用中,首先需要初始化 LiveKit 客户端。通常,你会在应用的入口处进行初始化。
import 'package:livekit_client_custom/livekit_client_custom.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 初始化 LiveKit 客户端
await LiveKitClient.initialize();
runApp(MyApp());
}
3. 连接到房间
要加入一个房间并进行音视频通信,你需要连接到 LiveKit 服务器。通常,你需要提供一个 WebSocket URL 和一个访问令牌(Token)。
import 'package:livekit_client_custom/livekit_client_custom.dart';
class VideoCallScreen extends StatefulWidget {
@override
_VideoCallScreenState createState() => _VideoCallScreenState();
}
class _VideoCallScreenState extends State<VideoCallScreen> {
Room? _room;
@override
void initState() {
super.initState();
_connectToRoom();
}
Future<void> _connectToRoom() async {
try {
// 连接到房间
_room = await LiveKitClient.connect(
'wss://your-livekit-server-url',
'your-access-token',
);
// 监听房间事件
_room?.on(RoomEvent.trackSubscribed, _onTrackSubscribed);
_room?.on(RoomEvent.trackUnsubscribed, _onTrackUnsubscribed);
// 开始发布音视频
await _room?.localParticipant?.enableCameraAndMicrophone();
} catch (e) {
print('Failed to connect to room: $e');
}
}
void _onTrackSubscribed(Track track, RemoteParticipant participant) {
// 处理订阅的轨道
setState(() {});
}
void _onTrackUnsubscribed(Track track, RemoteParticipant participant) {
// 处理取消订阅的轨道
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Video Call'),
),
body: _buildVideoView(),
);
}
Widget _buildVideoView() {
if (_room == null) {
return Center(child: Text('Connecting...'));
}
// 显示本地和远程视频
return ListView(
children: [
if (_room?.localParticipant?.cameraTrack != null)
VideoTrackWidget(_room!.localParticipant!.cameraTrack!),
for (var participant in _room!.remoteParticipants.values)
if (participant.cameraTrack != null)
VideoTrackWidget(participant.cameraTrack!),
],
);
}
}
4. 显示视频轨道
在上面的代码中,VideoTrackWidget
是一个自定义的 Widget,用于显示视频轨道。你可以使用 VideoRenderer
来渲染视频。
import 'package:flutter/material.dart';
import 'package:livekit_client_custom/livekit_client_custom.dart';
class VideoTrackWidget extends StatelessWidget {
final VideoTrack track;
VideoTrackWidget(this.track);
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 16 / 9,
child: VideoRenderer(track),
);
}
}
5. 处理房间事件
你可以监听房间的各种事件,例如参与者加入、离开、轨道订阅等,以便在 UI 上做出相应的更新。
void _listenToRoomEvents() {
_room?.on(RoomEvent.participantConnected, (participant) {
// 处理参与者连接事件
setState(() {});
});
_room?.on(RoomEvent.participantDisconnected, (participant) {
// 处理参与者断开连接事件
setState(() {});
});
}
6. 断开连接
当用户离开房间时,记得断开连接并清理资源。
@override
void dispose() {
_room?.disconnect();
super.dispose();
}
7. 发布和订阅音视频
你可以通过 localParticipant
来发布音视频轨道,并通过 remoteParticipants
来订阅其他参与者的音视频轨道。
// 发布音视频
await _room?.localParticipant?.enableCameraAndMicrophone();
// 订阅远程音视频
for (var participant in _room!.remoteParticipants.values) {
participant.subscribeTracks();
}
8. 处理权限
确保在应用中请求摄像头和麦克风的权限。
import 'package:permission_handler/permission_handler.dart';
Future<void> _requestPermissions() async {
await [Permission.camera, Permission.microphone].request();
}
9. 错误处理
在处理音视频通信时,可能会遇到各种错误,例如网络问题、权限问题等。确保在代码中适当地处理这些错误。
try {
await _connectToRoom();
} catch (e) {
print('Failed to connect to room: $e');
}
10. 自定义 UI
你可以根据需要自定义 UI,例如添加按钮来控制摄像头、麦克风、切换摄像头等。
IconButton(
icon: Icon(_room?.localParticipant?.isCameraEnabled ?? false ? Icons.videocam : Icons.videocam_off),
onPressed: () {
_room?.localParticipant?.setCameraEnabled(!_room!.localParticipant!.isCameraEnabled);
setState(() {});
},
),