Switch to new event system (#9)

* `SignalEvents` and `synchronized` mode for `EventsListenable`

* cascade syntax

* clean up

* `RoomEvents`

* `ParticipantEvents`

* all events implemented

* make it compile with M1 macs

* dispose logic

* cleaner connect logic

* ask to publish

* fix initial EngineTrackAddedEvent glitch

* fix unpublishTrack bug

* clean up

* better events docs

* organize event types

* clean up

* cleaner wait logic

* wait for event instead of using Completer

* notifyListeners
This commit is contained in:
Hiroshi Horie
2021-09-24 23:37:26 +09:00
committed by GitHub
parent f4bd82a919
commit 6445b8d2a8
36 changed files with 1270 additions and 948 deletions
+1 -1
View File
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
<string>9.0</string>
</dict>
</plist>
+4
View File
@@ -44,6 +44,10 @@ post_install do |installer|
#
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.1'
#
# Make it compile with M1 macs
#
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = 'arm64'
end
end
+10 -10
View File
@@ -2,14 +2,14 @@ PODS:
- Flutter (1.0.0)
- flutter_webrtc (0.2.2):
- Flutter
- GoogleWebRTC (= 1.1.31999)
- Libyuv (= 1703)
- GoogleWebRTC (1.1.31999)
- WebRTC-SDK (= 92.4515.05)
- Libyuv (1703)
- path_provider (0.0.1):
- Flutter
- shared_preferences (0.0.1):
- Flutter
- WebRTC-SDK (92.4515.05)
DEPENDENCIES:
- Flutter (from `Flutter`)
@@ -19,8 +19,8 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- GoogleWebRTC
- Libyuv
- WebRTC-SDK
EXTERNAL SOURCES:
Flutter:
@@ -33,13 +33,13 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences/ios"
SPEC CHECKSUMS:
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
flutter_webrtc: 39898454258b54ba51996850d5da8d5d53bf1524
GoogleWebRTC: b39a78c4f5cc6b0323415b9233db03a2faa7b0f0
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_webrtc: c0cb88c7cbd057e6e667ab1560e10c74e2ceb65b
Libyuv: 5f79ced0ee66e60a612ca97de1e6ccacd187a437
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
path_provider: d1e9807085df1f9cc9318206cd649dc0b76be3de
shared_preferences: 5033afbb22d372e15aff8ff766df9021b845f273
WebRTC-SDK: 7c76a541dbbffb0fc212aeb9902ec45a43c23996
PODFILE CHECKSUM: 6055d9653e1011c0b3b671abb92cdea979357e63
PODFILE CHECKSUM: 82aed1035f46bfa5b522f0d0dbf4730f17ec65ff
COCOAPODS: 1.10.1
COCOAPODS: 1.11.2
+3 -2
View File
@@ -331,6 +331,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
EXCLUDED_ARCHS = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -342,7 +343,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@@ -406,6 +406,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
EXCLUDED_ARCHS = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -461,6 +462,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
EXCLUDED_ARCHS = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -472,7 +474,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
+19 -1
View File
@@ -2,6 +2,24 @@ import 'package:flutter/material.dart';
extension LKExampleExt on BuildContext {
//
Future<bool?> showPublishDialog() => showDialog<bool>(
context: this,
builder: (ctx) => AlertDialog(
title: const Text('Publish'),
content: const Text('Would you like to publish your Camera & Mic ?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: const Text('NO'),
),
TextButton(
onPressed: () => Navigator.pop(ctx, true),
child: const Text('YES'),
),
],
),
);
Future<void> showErrorDialog(dynamic exception) => showDialog<void>(
context: this,
builder: (ctx) => AlertDialog(
@@ -74,7 +92,7 @@ extension LKExampleExt on BuildContext {
context: this,
builder: (ctx) => AlertDialog(
title: const Text('Received data'),
content: Text('"${data}"'),
content: Text(data),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, true),
+2
View File
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:livekit_example/theme.dart';
import 'package:logging/logging.dart';
import 'pages/connect.dart';
void main() {
print('This is a test for ${SignalTrickleEvent} test.');
// configure logs for debugging
Logger.root.level = Level.FINE;
Logger.root.onRecord.listen((record) {
+7 -3
View File
@@ -62,6 +62,9 @@ class _ConnectPageState extends State<ConnectPage> {
_busy = true;
});
// Save for next time
await _writePrefs();
print('Connecting with url: ${_uriCtrl.text}, token: ${_tokenCtrl.text}...');
final room = await LiveKitClient.connect(
@@ -74,9 +77,6 @@ class _ConnectPageState extends State<ConnectPage> {
),
);
// Save for next time
await _writePrefs();
await Navigator.push<void>(
ctx,
MaterialPageRoute(builder: (_) => RoomPage(room)),
@@ -122,10 +122,14 @@ class _ConnectPageState extends State<ConnectPage> {
mainAxisSize: MainAxisSize.min,
children: [
TextField(
enableSuggestions: false,
autocorrect: false,
controller: _uriCtrl,
decoration: const InputDecoration(labelText: 'URL'),
),
TextField(
enableSuggestions: false,
autocorrect: false,
controller: _tokenCtrl,
decoration: const InputDecoration(labelText: 'Token'),
),
+64 -57
View File
@@ -2,12 +2,12 @@ import 'dart:convert';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:provider/provider.dart';
import '../exts.dart';
import '../widgets/controls.dart';
import '../widgets/participant.dart';
import '../exts.dart';
class RoomPage extends StatefulWidget {
//
@@ -22,29 +22,51 @@ class RoomPage extends StatefulWidget {
State<StatefulWidget> createState() => _RoomPageState();
}
class _RoomPageState extends State<RoomPage> with RoomDelegate {
class _RoomPageState extends State<RoomPage> {
//
List<Participant> participants = [];
late final _listener = EventsListener<LiveKitEvent>(widget.room.events);
@override
void initState() {
super.initState();
widget.room.delegate = this;
widget.room.addListener(_onChange);
_onConnected();
widget.room.addListener(_onRoomDidUpdate);
_setUpListeners();
_sortParticipants();
WidgetsBinding.instance?.addPostFrameCallback((_) => _askPublish());
}
@override
void dispose() {
widget.room.delegate = null;
widget.room.removeListener(_onChange);
// always dispose listener
(() async {
widget.room.removeListener(_onRoomDidUpdate);
await _listener.dispose();
await widget.room.dispose();
})();
super.dispose();
}
void _onConnected() async {
void _setUpListeners() => _listener
..on<RoomDisconnectedEvent>((_) => Navigator.pop(context))
..on<DataReceivedEvent>((event) {
String decoded = 'Failed to decode';
try {
decoded = utf8.decode(event.data);
} catch (_) {
print('Failed to decode: $_');
}
context.showDataReceivedDialog(decoded);
});
void _askPublish() async {
final result = await context.showPublishDialog();
if (result != true) return;
// video will fail when running in ios simulator
try {
final localVideo = await LocalVideoTrack.createCameraTrack(); // Defaults to camera
// Create video track
final localVideo = await LocalVideoTrack.createCameraTrack();
// Try to publish the video
await widget.room.localParticipant.publishVideoTrack(
localVideo,
// options: TrackPublishOptions(
@@ -52,20 +74,22 @@ class _RoomPageState extends State<RoomPage> with RoomDelegate {
// videoEncoding: VideoParameters.presetQVGA169.encoding,
// ),
);
} catch (e) {
print('could not publish video: $e');
// Create mic track
final localAudio = await LocalAudioTrack.create();
// // Try to publish audio
await widget.room.localParticipant.publishAudioTrack(localAudio);
} catch (error) {
print('could not publish video: $error');
await context.showErrorDialog(error);
}
final localAudio = await LocalAudioTrack.create();
await widget.room.localParticipant.publishAudioTrack(localAudio);
sortParticipants();
}
void _onChange() {
sortParticipants();
void _onRoomDidUpdate() {
_sortParticipants();
}
void sortParticipants() {
void _sortParticipants() {
List<Participant> participants = [];
participants.addAll(widget.room.participants.values);
// sort speakers for the grid
@@ -106,48 +130,31 @@ class _RoomPageState extends State<RoomPage> with RoomDelegate {
});
}
@override
void onDataReceived(RemoteParticipant participant, List<int> data) async {
await context.showDataReceivedDialog(utf8.decode(data));
}
@override
void onDisconnected() {
print('disconnected: $context');
Navigator.pop(context);
}
@override
Widget build(BuildContext context) => Scaffold(
// with a provider, any child/descendent widget can be updated if they
// are a Consumer of Room.
body: ChangeNotifierProvider.value(
value: widget.room,
child: Column(
children: [
Expanded(
child: participants.isNotEmpty
? ParticipantWidget(participants.first)
: Container()),
SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: math.max(0, participants.length - 1),
itemBuilder: (BuildContext context, int index) => Container(
width: 100,
height: 100,
padding: const EdgeInsets.all(2),
child: ParticipantWidget(participants[index + 1], quality: VideoQuality.LOW),
),
body: Column(
children: [
Expanded(
child:
participants.isNotEmpty ? ParticipantWidget(participants.first) : Container()),
SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: math.max(0, participants.length - 1),
itemBuilder: (BuildContext context, int index) => Container(
width: 100,
height: 100,
padding: const EdgeInsets.all(2),
child: ParticipantWidget(participants[index + 1], quality: VideoQuality.LOW),
),
),
SafeArea(
top: false,
child: ControlsWidget(widget.room),
),
],
),
),
SafeArea(
top: false,
child: ControlsWidget(widget.room),
),
],
),
);
}
+2 -2
View File
@@ -102,8 +102,8 @@ class _ControlsWidgetState extends State<ControlsWidget> {
//
final lp = widget.room.localParticipant;
for (final tracks in lp.videoTracks) {
await lp.unpublishTrack(tracks.track!);
for (final track in lp.videoTracks) {
await lp.unpublishTrack(track.sid);
}
try {
+1 -1
View File
@@ -21,7 +21,7 @@ class ParticipantWidget extends StatefulWidget {
State<StatefulWidget> createState() => _ParticipantWidgetState();
}
class _ParticipantWidgetState extends State<ParticipantWidget> with ParticipantDelegate {
class _ParticipantWidgetState extends State<ParticipantWidget> {
//
TrackPublication? videoPub;
TrackPublication? audioPub;
+12 -10
View File
@@ -56,7 +56,7 @@ packages:
name: eva_icons_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.0.2"
fake_async:
dependency: transitive
description:
@@ -110,9 +110,11 @@ packages:
flutter_webrtc:
dependency: transitive
description:
name: flutter_webrtc
url: "https://pub.dartlang.org"
source: hosted
path: "."
ref: use-custom-webrtc-build
resolved-ref: "4942e7faec2e5775d35c42e22c2929ca6ca53769"
url: "https://github.com/livekit/flutter-webrtc"
source: git
version: "0.6.7"
google_fonts:
dependency: "direct main"
@@ -197,14 +199,14 @@ packages:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.5"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
version: "2.1.0"
path_provider_macos:
dependency: transitive
description:
@@ -274,7 +276,7 @@ packages:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
version: "2.0.8"
shared_preferences_linux:
dependency: transitive
description:
@@ -391,7 +393,7 @@ packages:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.8"
version: "2.2.9"
xdg_directories:
dependency: transitive
description:
@@ -400,5 +402,5 @@ packages:
source: hosted
version: "0.2.0"
sdks:
dart: ">=2.13.0 <3.0.0"
flutter: ">=2.0.0"
dart: ">=2.14.0 <3.0.0"
flutter: ">=2.5.0"