Documentation
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import 'room.dart';
|
||||
import 'signal_client.dart';
|
||||
import 'options.dart';
|
||||
|
||||
/// Main entry point to connect to a room.
|
||||
/// {@category Room}
|
||||
class LiveKitClient {
|
||||
/// Connects to a LiveKit room
|
||||
static Future<Room> connect(String url, String token,
|
||||
[JoinOptions? options]) {
|
||||
var room = Room();
|
||||
|
||||
@@ -11,6 +11,7 @@ import '../track/track.dart';
|
||||
import '../track/track_publication.dart';
|
||||
import 'participant.dart';
|
||||
|
||||
/// Represents the current participant in the room.
|
||||
class LocalParticipant extends Participant {
|
||||
RTCEngine _engine;
|
||||
|
||||
@@ -22,6 +23,8 @@ class LocalParticipant extends Participant {
|
||||
updateFromInfo(info);
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
RTCEngine get engine => _engine;
|
||||
|
||||
/// publish an audio track to the room
|
||||
@@ -53,7 +56,7 @@ class LocalParticipant extends Participant {
|
||||
}
|
||||
}
|
||||
|
||||
/// publish a video track to the room
|
||||
/// Publish a video track to the room
|
||||
Future<TrackPublication> publishVideoTrack(LocalVideoTrack track) async {
|
||||
if (videoTracks.values.any(
|
||||
(element) => element.track?.mediaTrack.id == track.mediaTrack.id)) {
|
||||
@@ -83,6 +86,7 @@ class LocalParticipant extends Participant {
|
||||
}
|
||||
}
|
||||
|
||||
/// Unpublish a track that's already published
|
||||
unpublishTrack(Track track) {
|
||||
var existing = tracks.values.where((element) => element.track == track);
|
||||
if (existing.isEmpty) {
|
||||
@@ -107,6 +111,8 @@ class LocalParticipant extends Participant {
|
||||
}
|
||||
}
|
||||
|
||||
/// Publish a new data payload to the room.
|
||||
/// @param destinationSids When empty, data will be forwarded to each participant in the room.
|
||||
publishData(List<int> data, DataPacket_Kind reliability,
|
||||
{List<String>? destinationSids}) {
|
||||
RTCDataChannel? channel;
|
||||
@@ -135,6 +141,9 @@ class LocalParticipant extends Participant {
|
||||
channel.send(RTCDataChannelMessage.fromBinary(buffer));
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
@override
|
||||
updateFromInfo(ParticipantInfo info) {
|
||||
super.updateFromInfo(info);
|
||||
}
|
||||
|
||||
@@ -6,20 +6,42 @@ import '../track/remote_track_publication.dart';
|
||||
import '../track/track.dart';
|
||||
import '../track/track_publication.dart';
|
||||
|
||||
/// Callbacks for participant changes
|
||||
mixin ParticipantDelegate {
|
||||
/// The participant's metadata has changed
|
||||
void onMetadataChanged(Participant participant) {}
|
||||
|
||||
/// The participant's isSpeaking property has changed
|
||||
void onSpeakingChanged(Participant participant, bool speaking) {}
|
||||
|
||||
/// This participant has muted one of their tracks
|
||||
void onTrackMuted(Participant participant, TrackPublication publication) {}
|
||||
|
||||
/// This participant has unmuted one of their tracks
|
||||
void onTrackUnmuted(Participant participant, TrackPublication publication) {}
|
||||
|
||||
/// This participant has published a new [Track] to the [Room].
|
||||
void onTrackPublished(
|
||||
RemoteParticipant participant, RemoteTrackPublication publication) {}
|
||||
|
||||
/// This participant has unpublished one of their [Track].
|
||||
void onTrackUnpublished(
|
||||
RemoteParticipant participant, RemoteTrackPublication publication) {}
|
||||
|
||||
/// The [LocalParticipant] has subscribed to a new track published by this
|
||||
/// [RemoteParticipant]
|
||||
void onTrackSubscribed(RemoteParticipant participant, Track track,
|
||||
RemoteTrackPublication publication) {}
|
||||
|
||||
/// The [LocalParticipant] has unsubscribed from a track published by this
|
||||
/// [RemoteParticipant]. This event is fired when the track was unpublished
|
||||
void onTrackUnsubscribed(RemoteParticipant participant, Track track,
|
||||
RemoteTrackPublication publication) {}
|
||||
|
||||
/// Data received from this [RemoteParticipant].
|
||||
void onDataReceived(RemoteParticipant participant, List<int> data) {}
|
||||
|
||||
/// An error has occured during track subscription.
|
||||
void onTrackSubscriptionFailed(
|
||||
RemoteParticipant participant, String sid, String? message) {}
|
||||
}
|
||||
@@ -97,11 +119,14 @@ class Participant extends ChangeNotifier {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// internal use
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
bool get hasInfo => _participantInfo != null;
|
||||
|
||||
Participant(this.sid, this.identity);
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
set isSpeaking(bool speaking) {
|
||||
if (_isSpeaking == speaking) {
|
||||
return;
|
||||
@@ -125,6 +150,8 @@ class Participant extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
updateFromInfo(ParticipantInfo info) {
|
||||
this.identity = info.identity;
|
||||
this.sid = info.sid;
|
||||
@@ -134,10 +161,14 @@ class Participant extends ChangeNotifier {
|
||||
this._participantInfo = info;
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
muteChanged() {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
addTrackPublication(TrackPublication pub) {
|
||||
pub.track?.sid = pub.sid;
|
||||
tracks[pub.sid] = pub;
|
||||
|
||||
@@ -7,6 +7,7 @@ import '../track/track.dart';
|
||||
import '../track/video_track.dart';
|
||||
import 'participant.dart';
|
||||
|
||||
/// Represents other participant in the [Room].
|
||||
class RemoteParticipant extends Participant {
|
||||
SignalClient _client;
|
||||
|
||||
@@ -27,6 +28,8 @@ class RemoteParticipant extends Participant {
|
||||
}
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
addSubscribedMediaTrack(
|
||||
MediaStreamTrack mediaTrack, MediaStream stream, String? sid) async {
|
||||
if (sid == null) {
|
||||
@@ -70,6 +73,8 @@ class RemoteParticipant extends Participant {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
@override
|
||||
void updateFromInfo(ParticipantInfo info) {
|
||||
var hadInfo = hasInfo;
|
||||
|
||||
+53
-5
@@ -8,6 +8,7 @@ import 'package:tuple/tuple.dart';
|
||||
import 'errors.dart';
|
||||
import 'extensions.dart';
|
||||
import 'logger.dart';
|
||||
import 'options.dart';
|
||||
import 'participant/local_participant.dart';
|
||||
import 'participant/participant.dart';
|
||||
import 'participant/remote_participant.dart';
|
||||
@@ -25,39 +26,83 @@ enum RoomState {
|
||||
Reconnecting,
|
||||
}
|
||||
|
||||
/// Delegate for [Room] callbacks
|
||||
mixin RoomDelegate {
|
||||
// room level callbacks
|
||||
/// When the connection to the server has been interrupted and it's attempting
|
||||
/// to reconnect.
|
||||
void onReconnecting() {}
|
||||
|
||||
/// Connection to room is re-established. All existing state is preserved.
|
||||
void onReconnected() {}
|
||||
|
||||
/// Disconnected from the room
|
||||
void onDisconnected() {}
|
||||
|
||||
/// When a new [RemoteParticipant] joins *after* the current participant has connected
|
||||
/// It will not fire for participants that are already in the room
|
||||
void onParticipantConnected(Participant participant) {}
|
||||
|
||||
/// When a [RemoteParticipant] leaves the room
|
||||
void onParticipantDisconnected(Participant participant) {}
|
||||
|
||||
/// Active speakers changed. List of speakers are ordered by their audio level.
|
||||
/// loudest speakers first. This will include the [LocalParticipant] too.
|
||||
void onActiveSpeakersChanged(List<Participant> participants) {}
|
||||
|
||||
// callbacks about participant events
|
||||
|
||||
/// Participant metadata is a simple way for app-specific state to be pushed to
|
||||
/// all users.
|
||||
/// When RoomService.UpdateParticipantMetadata is called to change a
|
||||
/// participant's state, *all* participants in the room will fire this event.
|
||||
void onMetadataChanged(Participant participant) {}
|
||||
|
||||
/// A track that was muted, fires on both [RemoteParticipant]s and
|
||||
/// [LocalParticipant]
|
||||
void onTrackMuted(Participant participant, TrackPublication publication) {}
|
||||
|
||||
/// A track that was unmuted, fires on both [RemoteParticipant]s and
|
||||
/// [LocalParticipant]
|
||||
void onTrackUnmuted(Participant participant, TrackPublication publication) {}
|
||||
|
||||
/// When a new track is published to room *after* the current participant has
|
||||
/// joined. It will not fire for tracks that are already published
|
||||
void onTrackPublished(
|
||||
RemoteParticipant participant, RemoteTrackPublication publication) {}
|
||||
|
||||
/// A [RemoteParticipant] has unpublished a track
|
||||
void onTrackUnpublished(
|
||||
RemoteParticipant participant, RemoteTrackPublication publication) {}
|
||||
|
||||
/// The [LocalParticipant] has subscribed to a new track. This event will **always**
|
||||
/// fire as long as new tracks are ready for use.
|
||||
void onTrackSubscribed(RemoteParticipant participant, Track track,
|
||||
RemoteTrackPublication publication) {}
|
||||
|
||||
/// A subscribed track is no longer available.
|
||||
void onTrackUnsubscribed(RemoteParticipant participant, Track track,
|
||||
RemoteTrackPublication publication) {}
|
||||
|
||||
/// Data received from another [RemoteParticipant].
|
||||
/// Data packets provides the ability to use LiveKit to send/receive arbitrary
|
||||
/// payloads.
|
||||
void onDataReceived(RemoteParticipant participant, List<int> data) {}
|
||||
|
||||
/// Encountered failure attempting to subscribe to track.
|
||||
void onTrackSubscriptionFailed(
|
||||
RemoteParticipant participant, String sid, String? message) {}
|
||||
}
|
||||
|
||||
/// Room is the main entrypoint to working with LiveKit. It provides
|
||||
/// updates to its state via two ways, by assigning a delegate, or using
|
||||
/// Room is the primary construct for LiveKit conferences. It contains a
|
||||
/// group of [Participant]s, each publishing and subscribing to [Track]s.
|
||||
/// Notifies changes to its state via two ways, by assigning a delegate, or using
|
||||
/// it as a provider.
|
||||
/// Room will trigger a change update when
|
||||
/// Room will trigger a change notification update when
|
||||
/// * state changes
|
||||
/// * participant membership changes
|
||||
/// * active speakers are different
|
||||
/// {@category Room}
|
||||
class Room extends ChangeNotifier with ParticipantDelegate {
|
||||
RoomState _state = RoomState.Disconnected;
|
||||
|
||||
@@ -92,6 +137,8 @@ class Room extends ChangeNotifier with ParticipantDelegate {
|
||||
|
||||
Completer<Room>? _connectCompleter;
|
||||
|
||||
/// internal use
|
||||
/// {@nodoc}
|
||||
Room([RTCConfiguration? rtcConfig])
|
||||
: _engine = new RTCEngine(SignalClient(), rtcConfig) {
|
||||
_engine.onTrack = _onTrackAdded;
|
||||
@@ -148,6 +195,7 @@ class Room extends ChangeNotifier with ParticipantDelegate {
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
/// Disconnects from the room, notifying server of disconnection.
|
||||
disconnect() {
|
||||
_engine.client.sendLeave();
|
||||
_handleDisconnect();
|
||||
@@ -288,7 +336,7 @@ class Room extends ChangeNotifier with ParticipantDelegate {
|
||||
return;
|
||||
}
|
||||
|
||||
var parsed = unpackStreamId(stream.id);
|
||||
var parsed = _unpackStreamId(stream.id);
|
||||
var trackSid = parsed.item2;
|
||||
if (trackSid == null) {
|
||||
trackSid = track.id;
|
||||
@@ -354,7 +402,7 @@ class Room extends ChangeNotifier with ParticipantDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
Tuple2<String, String?> unpackStreamId(String streamId) {
|
||||
Tuple2<String, String?> _unpackStreamId(String streamId) {
|
||||
var parts = streamId.split('|');
|
||||
if (parts.length != 2) {
|
||||
return Tuple2(parts[0], null);
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||
import 'errors.dart';
|
||||
import 'extensions.dart';
|
||||
import 'logger.dart';
|
||||
import 'options.dart';
|
||||
import 'proto/livekit_rtc.pb.dart';
|
||||
import 'proto/livekit_models.pb.dart';
|
||||
import 'signal_client.dart';
|
||||
|
||||
@@ -3,11 +3,12 @@ import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'errors.dart';
|
||||
import 'logger.dart';
|
||||
import 'options.dart';
|
||||
import 'track/track.dart';
|
||||
import 'version.dart';
|
||||
import 'proto/livekit_models.pb.dart';
|
||||
@@ -16,12 +17,6 @@ import '_websocket_api.dart'
|
||||
if (dart.library.io) '_websocket_io.dart'
|
||||
if (dart.library.html) '_websocket_html.dart' as platform;
|
||||
|
||||
class JoinOptions {
|
||||
final bool? autoSubscribe;
|
||||
|
||||
const JoinOptions({this.autoSubscribe});
|
||||
}
|
||||
|
||||
mixin SignalClientDelegate {
|
||||
// initial connection established
|
||||
void onConnected(JoinResponse response);
|
||||
|
||||
@@ -11,6 +11,8 @@ class AudioTrack extends Track {
|
||||
AudioTrack(String name, MediaStreamTrack track, this.mediaStream)
|
||||
: super(TrackType.AUDIO, name, track);
|
||||
|
||||
/// Start playing audio track. On web platform, create an audio element and
|
||||
/// start playback
|
||||
start() {
|
||||
if (!(this is LocalAudioTrack)) {
|
||||
audio.startAudio(getCid(), mediaTrack);
|
||||
|
||||
@@ -8,6 +8,7 @@ class LocalAudioTrack extends AudioTrack {
|
||||
LocalAudioTrack(String name, MediaStreamTrack track, MediaStream stream)
|
||||
: super(name, track, stream);
|
||||
|
||||
/// Creates a new audio track from the default audio input device.
|
||||
static Future<LocalAudioTrack> createTrack(
|
||||
[LocalAudioTrackOptions? options]) async {
|
||||
try {
|
||||
|
||||
@@ -11,6 +11,7 @@ class LocalTrackPublication extends TrackPublication {
|
||||
this.track = track;
|
||||
}
|
||||
|
||||
/// Mute or unmute the current track. When muted, track will stop sending data
|
||||
set muted(bool val) {
|
||||
if (val == muted) {
|
||||
return;
|
||||
|
||||
@@ -4,12 +4,15 @@ import '../errors.dart';
|
||||
import 'options.dart';
|
||||
import 'video_track.dart';
|
||||
|
||||
/// A video track from the local device. Use static methods in this class to create
|
||||
/// video tracks.
|
||||
class LocalVideoTrack extends VideoTrack {
|
||||
RTCRtpSender? get sender => transceiver?.sender;
|
||||
|
||||
LocalVideoTrack(String name, MediaStreamTrack mediaTrack, MediaStream stream)
|
||||
: super(name, mediaTrack, stream);
|
||||
|
||||
/// Creates a LocalVideoTrack from camera input.
|
||||
static Future<LocalVideoTrack> createCameraTrack(
|
||||
[LocalVideoTrackOptions? options]) async {
|
||||
if (options == null) {
|
||||
@@ -24,6 +27,8 @@ class LocalVideoTrack extends VideoTrack {
|
||||
}
|
||||
}
|
||||
|
||||
/// Restarts the track with new options. This is useful when switching between
|
||||
/// front and back cameras.
|
||||
Future<void> restartTrack([LocalVideoTrackOptions? options]) async {
|
||||
if (sender == null) {
|
||||
return Future.error(TrackCreateError('could not restart track'));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/// Options when creating a LocalVideoTrack.
|
||||
class LocalVideoTrackOptions {
|
||||
CameraPosition position = CameraPosition.FRONT;
|
||||
VideoParameter params;
|
||||
@@ -65,4 +66,5 @@ class VideoPresets {
|
||||
];
|
||||
}
|
||||
|
||||
/// Options when creating an LocalAudioTrack. Placeholder for now.
|
||||
class LocalAudioTrackOptions {}
|
||||
|
||||
@@ -4,6 +4,8 @@ import '../participant/remote_participant.dart';
|
||||
import 'track.dart';
|
||||
import 'track_publication.dart';
|
||||
|
||||
/// Represents a track publication from a RemoteParticipant. Provides methods to
|
||||
/// control if we should subscribe to the track, and its quality (for video).
|
||||
class RemoteTrackPublication extends TrackPublication {
|
||||
RemoteParticipant _participant;
|
||||
bool _unsubscribed = false;
|
||||
@@ -37,6 +39,8 @@ class RemoteTrackPublication extends TrackPublication {
|
||||
_sendUpdateTrackSettings();
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
set muted(bool val) {
|
||||
if (val == muted) {
|
||||
return;
|
||||
|
||||
@@ -10,6 +10,7 @@ class TrackDimension {
|
||||
TrackDimension(this.width, this.height);
|
||||
}
|
||||
|
||||
/// Wrapper around a MediaStreamTrack with additional metadata.
|
||||
class Track {
|
||||
static const ScreenShareName = "screen";
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import '../proto/livekit_models.pb.dart';
|
||||
import 'track.dart';
|
||||
|
||||
/// Represents a track that's published to the server. This class contains
|
||||
/// metadata associated with tracks.
|
||||
class TrackPublication {
|
||||
Track? track;
|
||||
String name;
|
||||
@@ -19,6 +21,7 @@ class TrackPublication {
|
||||
updateFromInfo(info);
|
||||
}
|
||||
|
||||
/// True when the track is published with name [Track.ScreenShareName].
|
||||
bool get isScreenShare =>
|
||||
kind == TrackType.VIDEO && name == Track.ScreenShareName;
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ class VideoTrack extends Track with ChangeNotifier {
|
||||
|
||||
MediaStream? get mediaStream => _mediaStream;
|
||||
|
||||
/// internal use
|
||||
/// {@nodoc}
|
||||
set mediaStream(MediaStream? stream) {
|
||||
_mediaStream = stream;
|
||||
notifyListeners();
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||
import '../track/video_track.dart';
|
||||
import '../track/local_video_track.dart';
|
||||
|
||||
/// Widget that renders a [VideoTrack].
|
||||
class VideoTrackRenderer extends StatefulWidget {
|
||||
final VideoTrack track;
|
||||
final RTCVideoRenderer renderer;
|
||||
|
||||
Reference in New Issue
Block a user