more docs
This commit is contained in:
+24
-1
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import 'participant/local_participant.dart';
|
import 'participant/local_participant.dart';
|
||||||
import 'participant/participant.dart';
|
import 'participant/participant.dart';
|
||||||
@@ -10,17 +11,26 @@ import 'publication/remote_track_publication.dart';
|
|||||||
import 'publication/track_publication.dart';
|
import 'publication/track_publication.dart';
|
||||||
import 'track/track.dart';
|
import 'track/track.dart';
|
||||||
import 'types.dart';
|
import 'types.dart';
|
||||||
|
import 'room.dart';
|
||||||
|
import 'rtc_engine.dart';
|
||||||
|
import 'signal_client.dart';
|
||||||
|
|
||||||
|
/// Base type for all LiveKit events.
|
||||||
abstract class LiveKitEvent {}
|
abstract class LiveKitEvent {}
|
||||||
|
|
||||||
|
/// Base type for all [Room] events.
|
||||||
abstract class RoomEvent implements LiveKitEvent {}
|
abstract class RoomEvent implements LiveKitEvent {}
|
||||||
|
|
||||||
|
/// Base type for all [Participant] events.
|
||||||
abstract class ParticipantEvent implements LiveKitEvent {}
|
abstract class ParticipantEvent implements LiveKitEvent {}
|
||||||
|
|
||||||
|
/// Base type for all [Track] events.
|
||||||
abstract class TrackEvent implements LiveKitEvent {}
|
abstract class TrackEvent implements LiveKitEvent {}
|
||||||
|
|
||||||
|
/// Base type for all [RTCEngine] events.
|
||||||
abstract class EngineEvent implements LiveKitEvent {}
|
abstract class EngineEvent implements LiveKitEvent {}
|
||||||
|
|
||||||
|
/// Base type for all [SignalClient] events.
|
||||||
abstract class SignalEvent implements LiveKitEvent {}
|
abstract class SignalEvent implements LiveKitEvent {}
|
||||||
|
|
||||||
/// When the connection to the server has been interrupted and it's attempting
|
/// When the connection to the server has been interrupted and it's attempting
|
||||||
@@ -277,6 +287,8 @@ class EngineRemoteMuteChangedEvent with EngineEvent {
|
|||||||
//
|
//
|
||||||
// Signal events
|
// Signal events
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@internal
|
||||||
class SignalConnectedEvent with SignalEvent {
|
class SignalConnectedEvent with SignalEvent {
|
||||||
final lk_rtc.JoinResponse response;
|
final lk_rtc.JoinResponse response;
|
||||||
const SignalConnectedEvent({
|
const SignalConnectedEvent({
|
||||||
@@ -284,6 +296,7 @@ class SignalConnectedEvent with SignalEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
class SignalCloseEvent with SignalEvent {
|
class SignalCloseEvent with SignalEvent {
|
||||||
final CloseReason? reason;
|
final CloseReason? reason;
|
||||||
const SignalCloseEvent({
|
const SignalCloseEvent({
|
||||||
@@ -291,6 +304,7 @@ class SignalCloseEvent with SignalEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
class SignalOfferEvent with SignalEvent {
|
class SignalOfferEvent with SignalEvent {
|
||||||
final rtc.RTCSessionDescription sd;
|
final rtc.RTCSessionDescription sd;
|
||||||
const SignalOfferEvent({
|
const SignalOfferEvent({
|
||||||
@@ -298,6 +312,7 @@ class SignalOfferEvent with SignalEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
class SignalAnswerEvent with SignalEvent {
|
class SignalAnswerEvent with SignalEvent {
|
||||||
final rtc.RTCSessionDescription sd;
|
final rtc.RTCSessionDescription sd;
|
||||||
const SignalAnswerEvent({
|
const SignalAnswerEvent({
|
||||||
@@ -305,6 +320,7 @@ class SignalAnswerEvent with SignalEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
class SignalTrickleEvent with SignalEvent {
|
class SignalTrickleEvent with SignalEvent {
|
||||||
final rtc.RTCIceCandidate candidate;
|
final rtc.RTCIceCandidate candidate;
|
||||||
final lk_rtc.SignalTarget target;
|
final lk_rtc.SignalTarget target;
|
||||||
@@ -314,6 +330,7 @@ class SignalTrickleEvent with SignalEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
// relayed by Engine
|
// relayed by Engine
|
||||||
class SignalParticipantUpdateEvent with SignalEvent, EngineEvent {
|
class SignalParticipantUpdateEvent with SignalEvent, EngineEvent {
|
||||||
final List<lk_models.ParticipantInfo> participants;
|
final List<lk_models.ParticipantInfo> participants;
|
||||||
@@ -322,6 +339,7 @@ class SignalParticipantUpdateEvent with SignalEvent, EngineEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
class SignalConnectionQualityUpdateEvent with SignalEvent, EngineEvent {
|
class SignalConnectionQualityUpdateEvent with SignalEvent, EngineEvent {
|
||||||
final List<lk_rtc.ConnectionQualityInfo> updates;
|
final List<lk_rtc.ConnectionQualityInfo> updates;
|
||||||
const SignalConnectionQualityUpdateEvent({
|
const SignalConnectionQualityUpdateEvent({
|
||||||
@@ -329,6 +347,7 @@ class SignalConnectionQualityUpdateEvent with SignalEvent, EngineEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
class SignalLocalTrackPublishedEvent with SignalEvent {
|
class SignalLocalTrackPublishedEvent with SignalEvent {
|
||||||
final String cid;
|
final String cid;
|
||||||
final lk_models.TrackInfo track;
|
final lk_models.TrackInfo track;
|
||||||
@@ -338,7 +357,8 @@ class SignalLocalTrackPublishedEvent with SignalEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// speaker update received through websocket
|
@internal
|
||||||
|
// Speaker update received through websocket
|
||||||
// relayed by Engine
|
// relayed by Engine
|
||||||
class SignalSpeakersChangedEvent with SignalEvent, EngineEvent {
|
class SignalSpeakersChangedEvent with SignalEvent, EngineEvent {
|
||||||
final List<lk_models.SpeakerInfo> speakers;
|
final List<lk_models.SpeakerInfo> speakers;
|
||||||
@@ -347,6 +367,7 @@ class SignalSpeakersChangedEvent with SignalEvent, EngineEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
// Event received through data channel
|
// Event received through data channel
|
||||||
class EngineActiveSpeakersUpdateEvent with EngineEvent {
|
class EngineActiveSpeakersUpdateEvent with EngineEvent {
|
||||||
final List<lk_models.SpeakerInfo> speakers;
|
final List<lk_models.SpeakerInfo> speakers;
|
||||||
@@ -355,6 +376,7 @@ class EngineActiveSpeakersUpdateEvent with EngineEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
class SignalLeaveEvent with SignalEvent {
|
class SignalLeaveEvent with SignalEvent {
|
||||||
final bool canReconnect;
|
final bool canReconnect;
|
||||||
const SignalLeaveEvent({
|
const SignalLeaveEvent({
|
||||||
@@ -362,6 +384,7 @@ class SignalLeaveEvent with SignalEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
class SignalMuteTrackEvent with SignalEvent {
|
class SignalMuteTrackEvent with SignalEvent {
|
||||||
final String sid;
|
final String sid;
|
||||||
final bool muted;
|
final bool muted;
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import 'room.dart';
|
|||||||
class LiveKitClient {
|
class LiveKitClient {
|
||||||
static const version = '0.5.4';
|
static const version = '0.5.4';
|
||||||
|
|
||||||
/// Connects to a LiveKit room
|
/// Convenience method for connecting to a LiveKit server.
|
||||||
|
/// Returns a [Room] upon a successful connect or throws when it fails.
|
||||||
|
/// Alternatively, it is possible to instantiate [Room] and call [Room.connect] directly.
|
||||||
static Future<Room> connect(
|
static Future<Room> connect(
|
||||||
String url,
|
String url,
|
||||||
String token, {
|
String token, {
|
||||||
|
|||||||
@@ -12,13 +12,16 @@ import '../options.dart';
|
|||||||
import '../proto/livekit_models.pb.dart' as lk_models;
|
import '../proto/livekit_models.pb.dart' as lk_models;
|
||||||
import '../publication/local_track_publication.dart';
|
import '../publication/local_track_publication.dart';
|
||||||
import '../rtc_engine.dart';
|
import '../rtc_engine.dart';
|
||||||
|
import '../track/local.dart';
|
||||||
import '../track/local/audio.dart';
|
import '../track/local/audio.dart';
|
||||||
import '../track/local/video.dart';
|
import '../track/local/video.dart';
|
||||||
import '../types.dart';
|
import '../types.dart';
|
||||||
import '../utils.dart';
|
import '../utils.dart';
|
||||||
|
import '../room.dart';
|
||||||
import 'participant.dart';
|
import 'participant.dart';
|
||||||
|
|
||||||
/// Represents the current participant in the room.
|
/// Represents the current participant in the room. Instance of [LocalParticipant] is automatically
|
||||||
|
/// created after successfully connecting to a [Room] and will be accessible from [Room.localParticipant].
|
||||||
class LocalParticipant extends Participant<LocalTrackPublication> {
|
class LocalParticipant extends Participant<LocalTrackPublication> {
|
||||||
@internal
|
@internal
|
||||||
final VideoPublishOptions? defaultVideoPublishOptions;
|
final VideoPublishOptions? defaultVideoPublishOptions;
|
||||||
@@ -40,7 +43,8 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
|
|||||||
updateFromInfo(info);
|
updateFromInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// publish an audio track to the room
|
/// Publish an [AudioTrack] to the [Room].
|
||||||
|
/// For most cases, using [setMicrophoneEnabled] would be simpler and recommended.
|
||||||
Future<LocalTrackPublication<LocalAudioTrack>> publishAudioTrack(
|
Future<LocalTrackPublication<LocalAudioTrack>> publishAudioTrack(
|
||||||
LocalAudioTrack track, {
|
LocalAudioTrack track, {
|
||||||
AudioPublishOptions? options,
|
AudioPublishOptions? options,
|
||||||
@@ -250,12 +254,14 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
|
|||||||
List<LocalTrackPublication> get subscribedTracks =>
|
List<LocalTrackPublication> get subscribedTracks =>
|
||||||
super.subscribedTracks.cast<LocalTrackPublication>().toList();
|
super.subscribedTracks.cast<LocalTrackPublication>().toList();
|
||||||
|
|
||||||
|
/// A convenience property to get all video tracks.
|
||||||
@override
|
@override
|
||||||
List<LocalTrackPublication<LocalVideoTrack>> get videoTracks =>
|
List<LocalTrackPublication<LocalVideoTrack>> get videoTracks =>
|
||||||
trackPublications.values
|
trackPublications.values
|
||||||
.whereType<LocalTrackPublication<LocalVideoTrack>>()
|
.whereType<LocalTrackPublication<LocalVideoTrack>>()
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
/// A convenience property to get all audio tracks.
|
||||||
@override
|
@override
|
||||||
List<LocalTrackPublication<LocalAudioTrack>> get audioTracks =>
|
List<LocalTrackPublication<LocalAudioTrack>> get audioTracks =>
|
||||||
trackPublications.values
|
trackPublications.values
|
||||||
|
|||||||
@@ -29,30 +29,31 @@ abstract class Participant<T extends TrackPublication>
|
|||||||
@internal
|
@internal
|
||||||
final RTCEngine engine;
|
final RTCEngine engine;
|
||||||
|
|
||||||
/// map of track sid => published track
|
/// Map of track sid => published track
|
||||||
final Map<String, T> trackPublications = {};
|
final Map<String, T> trackPublications = {};
|
||||||
|
|
||||||
/// audio level between 0-1, 1 being the loudest
|
/// Audio level between 0-1, 1 being the loudest.
|
||||||
double audioLevel = 0;
|
double audioLevel = 0;
|
||||||
|
|
||||||
/// server assigned unique id
|
/// Server assigned unique id.
|
||||||
final String sid;
|
final String sid;
|
||||||
|
|
||||||
/// user-assigned identity
|
/// User-assigned identity.
|
||||||
String identity;
|
String identity;
|
||||||
|
|
||||||
/// client-assigned metadata, opaque to livekit
|
/// Client-assigned metadata, opaque to livekit.
|
||||||
String? metadata;
|
String? metadata;
|
||||||
|
|
||||||
/// when the participant had last spoken
|
/// When the participant had last spoken.
|
||||||
DateTime? lastSpokeAt;
|
DateTime? lastSpokeAt;
|
||||||
|
|
||||||
lk_models.ParticipantInfo? _participantInfo;
|
lk_models.ParticipantInfo? _participantInfo;
|
||||||
bool _isSpeaking = false;
|
bool _isSpeaking = false;
|
||||||
|
|
||||||
|
/// Connection quality between the [Participant] and the server.
|
||||||
ConnectionQuality _connectionQuality = ConnectionQuality.unknown;
|
ConnectionQuality _connectionQuality = ConnectionQuality.unknown;
|
||||||
|
|
||||||
// suppport for multiple event listeners
|
// Suppport for multiple event listeners.
|
||||||
final EventsEmitter<RoomEvent> roomEvents;
|
final EventsEmitter<RoomEvent> roomEvents;
|
||||||
|
|
||||||
/// when the participant joined the room
|
/// when the participant joined the room
|
||||||
@@ -169,9 +170,10 @@ abstract class Participant<T extends TrackPublication>
|
|||||||
trackPublications[pub.sid] = pub;
|
trackPublications[pub.sid] = pub;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must implement
|
// Must be implemented by subclasses.
|
||||||
Future<void> unpublishTrack(String trackSid, {bool notify = true});
|
Future<void> unpublishTrack(String trackSid, {bool notify = true});
|
||||||
|
|
||||||
|
/// Convenience method to unpublish all tracks.
|
||||||
Future<void> unpublishAllTracks({bool notify = true}) async {
|
Future<void> unpublishAllTracks({bool notify = true}) async {
|
||||||
final trackSids = trackPublications.keys.toSet();
|
final trackSids = trackPublications.keys.toSet();
|
||||||
for (final trackid in trackSids) {
|
for (final trackid in trackSids) {
|
||||||
@@ -179,31 +181,26 @@ abstract class Participant<T extends TrackPublication>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/// Convenience property to check whether [TrackSource.camera] is published or not.
|
||||||
// Equality operators
|
|
||||||
// Object is considered equal when sid is equal
|
|
||||||
//
|
|
||||||
@override
|
|
||||||
int get hashCode => sid.hashCode;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => other is Participant && sid == other.sid;
|
|
||||||
|
|
||||||
bool isCameraEnabled() {
|
bool isCameraEnabled() {
|
||||||
return !(getTrackPublicationBySource(TrackSource.camera)?.muted ?? true);
|
return !(getTrackPublicationBySource(TrackSource.camera)?.muted ?? true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience property to check whether [TrackSource.microphone] is published or not.
|
||||||
bool isMicrophoneEnabled() {
|
bool isMicrophoneEnabled() {
|
||||||
return !(getTrackPublicationBySource(TrackSource.microphone)?.muted ??
|
return !(getTrackPublicationBySource(TrackSource.microphone)?.muted ??
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience property to check whether [TrackSource.screenShareVideo] is published or not.
|
||||||
bool isScreenShareEnabled() {
|
bool isScreenShareEnabled() {
|
||||||
return !(getTrackPublicationBySource(TrackSource.screenShareVideo)?.muted ??
|
return !(getTrackPublicationBySource(TrackSource.screenShareVideo)?.muted ??
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find a track publication by its [TrackSource]
|
/// Tries to find a [TrackPublication] by its [TrackSource]. Otherwise, will
|
||||||
|
/// return a compatible type of [TrackPublication] for the [TrackSource] specified.
|
||||||
|
/// returns null when not found.
|
||||||
T? getTrackPublicationBySource(TrackSource source) {
|
T? getTrackPublicationBySource(TrackSource source) {
|
||||||
if (source == TrackSource.unknown) return null;
|
if (source == TrackSource.unknown) return null;
|
||||||
// try to find by source
|
// try to find by source
|
||||||
@@ -226,4 +223,12 @@ abstract class Participant<T extends TrackPublication>
|
|||||||
e.kind == lk_models.TrackType.AUDIO &&
|
e.kind == lk_models.TrackType.AUDIO &&
|
||||||
e.name == Track.screenShareName));
|
e.name == Track.screenShareName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equality operators
|
||||||
|
// Object is considered equal when sid is equal
|
||||||
|
@override
|
||||||
|
int get hashCode => sid.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => other is Participant && sid == other.sid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import 'signal_client.dart';
|
|||||||
import 'support/disposable.dart';
|
import 'support/disposable.dart';
|
||||||
import 'transport.dart';
|
import 'transport.dart';
|
||||||
import 'types.dart';
|
import 'types.dart';
|
||||||
|
import 'room.dart';
|
||||||
|
|
||||||
class RTCEngine extends Disposable with EventsEmittable<EngineEvent> {
|
class RTCEngine extends Disposable with EventsEmittable<EngineEvent> {
|
||||||
static const _lossyDCLabel = '_lossy';
|
static const _lossyDCLabel = '_lossy';
|
||||||
@@ -49,7 +50,7 @@ class RTCEngine extends Disposable with EventsEmittable<EngineEvent> {
|
|||||||
|
|
||||||
ConnectionState _connectionState = ConnectionState.disconnected;
|
ConnectionState _connectionState = ConnectionState.disconnected;
|
||||||
|
|
||||||
/// connection state of the room
|
/// Connection state of the [Room].
|
||||||
ConnectionState get connectionState => _connectionState;
|
ConnectionState get connectionState => _connectionState;
|
||||||
|
|
||||||
// true if publisher connection has already been established.
|
// true if publisher connection has already been established.
|
||||||
@@ -120,7 +121,7 @@ class RTCEngine extends Disposable with EventsEmittable<EngineEvent> {
|
|||||||
return event.response;
|
return event.response;
|
||||||
}
|
}
|
||||||
|
|
||||||
// there is no side-effect calling this method multiple times
|
/// Close connection between the server.
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
logger.fine('[$objectId] close()');
|
logger.fine('[$objectId] close()');
|
||||||
if (_connectionState == ConnectionState.disconnected) {
|
if (_connectionState == ConnectionState.disconnected) {
|
||||||
@@ -143,6 +144,7 @@ class RTCEngine extends Disposable with EventsEmittable<EngineEvent> {
|
|||||||
// notifyListeners();
|
// notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
Future<lk_models.TrackInfo> addTrack({
|
Future<lk_models.TrackInfo> addTrack({
|
||||||
required String cid,
|
required String cid,
|
||||||
required String name,
|
required String name,
|
||||||
@@ -173,6 +175,7 @@ class RTCEngine extends Disposable with EventsEmittable<EngineEvent> {
|
|||||||
return event.track;
|
return event.track;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
Future<void> negotiate({bool? iceRestart}) async {
|
Future<void> negotiate({bool? iceRestart}) async {
|
||||||
if (publisher == null) {
|
if (publisher == null) {
|
||||||
return;
|
return;
|
||||||
@@ -182,7 +185,7 @@ class RTCEngine extends Disposable with EventsEmittable<EngineEvent> {
|
|||||||
publisher!.negotiate(null);
|
publisher!.negotiate(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @internal */
|
@internal
|
||||||
Future<void> sendDataPacket(
|
Future<void> sendDataPacket(
|
||||||
lk_models.DataPacket packet,
|
lk_models.DataPacket packet,
|
||||||
) async {
|
) async {
|
||||||
@@ -227,6 +230,7 @@ class RTCEngine extends Disposable with EventsEmittable<EngineEvent> {
|
|||||||
logger.fine('[PUBLISHER] connected');
|
logger.fine('[PUBLISHER] connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
Future<void> reconnect() async {
|
Future<void> reconnect() async {
|
||||||
if (_connectionState == ConnectionState.disconnected) {
|
if (_connectionState == ConnectionState.disconnected) {
|
||||||
logger.fine('$objectId reconnect() already closed');
|
logger.fine('$objectId reconnect() already closed');
|
||||||
|
|||||||
Reference in New Issue
Block a user