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:
@@ -21,6 +21,6 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
<string>9.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user