Documentation
This commit is contained in:
@@ -1,3 +1,254 @@
|
||||
# LiveKit Flutter SDK
|
||||
|
||||
Docs coming soon. package available on pub.dev as [livekit_client](https://pub.dev/packages/livekit_client)
|
||||
Official Flutter SDK for [LiveKit](https://livekit.io). Easily add real-time video and audio to your Flutter apps.
|
||||
|
||||
This package is published to pub.dev as [livekit_client](https://pub.dev/packages/livekit_client).
|
||||
|
||||
## Docs
|
||||
|
||||
Docs and guides at [https://docs.livekit.io](https://docs.livekit.io)
|
||||
|
||||
## Installation
|
||||
|
||||
Include this package to your `pubspec.yaml`
|
||||
|
||||
```yaml
|
||||
...
|
||||
dependencies:
|
||||
livekit_client: <version>
|
||||
```
|
||||
|
||||
### iOS
|
||||
|
||||
Camera and microphone usage need to be declared in your `Info.plist` file.
|
||||
|
||||
```xml
|
||||
...
|
||||
<dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>$(PRODUCT_NAME) uses your camera</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>$(PRODUCT_NAME) uses your microphone</string>
|
||||
</dict>
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
We require a set of permissions that need to be declared in your `AppManifest.xml`. These are required by Flutter WebRTC, which we depend on.
|
||||
|
||||
```xml
|
||||
<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" />
|
||||
...
|
||||
</manifest>
|
||||
```
|
||||
|
||||
## Example app
|
||||
|
||||
We built a multi-user conferencing app as an example in the [example/](example/) folder. You can join the same room from any supported LiveKit clients.
|
||||
|
||||
## Usage
|
||||
|
||||
### Connecting to a room, publish video & audio
|
||||
|
||||
```dart
|
||||
var room = await LiveKitClient.connect(this.url, this.token);
|
||||
try {
|
||||
// video will fail when running in ios simulator
|
||||
var localVideo = await LocalVideoTrack.createCameraTrack();
|
||||
await room.localParticipant.publishVideoTrack(localVideo);
|
||||
} catch (e) {
|
||||
print('could not publish video: $e');
|
||||
}
|
||||
|
||||
var localAudio = await LocalAudioTrack.createTrack();
|
||||
await room.localParticipant.publishAudioTrack(localAudio);
|
||||
```
|
||||
|
||||
### Rendering video
|
||||
|
||||
Each track can be rendered separately with the provided `VideoTrackRenderer` widget.
|
||||
|
||||
```dart
|
||||
VideoTrack? track;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (track != null) {
|
||||
return VideoTrackRenderer(track);
|
||||
} else {
|
||||
return Container(
|
||||
color: Colors.grey,
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Audio handling
|
||||
|
||||
Audio tracks are rendered automatically as long as you are subscribed to them.
|
||||
|
||||
### Handling changes
|
||||
|
||||
LiveKit client makes it simple to build declarative UI that reacts to state changes. It notifies changes in two ways
|
||||
|
||||
* `ChangeNotifier` - generic notification of changes
|
||||
* `RoomDelegate` and `ParticipantDelegate` - notification of specific events.
|
||||
|
||||
This example will show you how to use both to react to room events.
|
||||
|
||||
```dart
|
||||
class RoomWidget extends StatefulWidget {
|
||||
final Room room;
|
||||
|
||||
RoomWidget(this.room);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _RoomState();
|
||||
}
|
||||
}
|
||||
|
||||
class _RoomState extends State<RoomWidget> with RoomDelegate {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget.room.delegate = this;
|
||||
widget.room.addListener(_onChange);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.room.delegate = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onChange() {
|
||||
// perform computations and then call setState
|
||||
// setState will trigger a build
|
||||
setState(() {
|
||||
// your updates here
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onDisconnected() {
|
||||
// onDisconnected is a RoomDelegate method, handle when disconnected from room
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// your build function
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, you could do the same when rendering participants. Reacting to changes makes it possible to handle tracks published/unpublished or re-ordering participants in your UI.
|
||||
|
||||
```dart
|
||||
class VideoView extends StatefulWidget {
|
||||
final Participant participant;
|
||||
|
||||
VideoView(this.participant);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _VideoViewState();
|
||||
}
|
||||
}
|
||||
|
||||
class _VideoViewState extends State<VideoView> with ParticipantDelegate {
|
||||
TrackPublication? videoPub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget.participant.addListener(this._onParticipantChanged);
|
||||
// trigger initial change
|
||||
_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 is RemoteTrackPublication) {
|
||||
videoPub.videoQuality = widget.quality;
|
||||
}
|
||||
// when muted, show placeholder
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Mute, unmute local tracks
|
||||
|
||||
On `LocalTrackPublication`s, you could control if the track is muted by setting its `muted` property. Changing the mute status will generate an `onTrackMuted` or `onTrack Unmuted` delegate call for the local participant. Other participant will receive the status change as well.
|
||||
|
||||
```dart
|
||||
// mute track
|
||||
trackPub.muted = true;
|
||||
|
||||
// unmute track
|
||||
trackPub.muted = false;
|
||||
```
|
||||
|
||||
### Subscriber controls
|
||||
|
||||
When subscribing to remote tracks, the client has precise control over status of its subscriptions. You could subscribe or unsubscribe to a track, change its quality, or disabling the track temporarily.
|
||||
|
||||
These controls are accessible on the `RemoteTrackPublication` object.
|
||||
|
||||
For more info, see [Subscriber controls](https://docs.livekit.io/guides/room/receive#subscriber-controls).
|
||||
|
||||
## License
|
||||
|
||||
Apache License 2.0
|
||||
|
||||
## Thanks
|
||||
|
||||
A huge thank you to [flutter-webrtc](https://github.com/flutter-webrtc/flutter-webrtc) for making it possible to use WebRTC in Flutter.
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/// Flutter Client SDK to LiveKit.
|
||||
library livekit_client;
|
||||
|
||||
export 'src/livekit.dart';
|
||||
export 'src/errors.dart';
|
||||
export 'src/room.dart';
|
||||
export 'src/options.dart';
|
||||
export 'src/participant/participant.dart';
|
||||
export 'src/participant/local_participant.dart';
|
||||
export 'src/participant/remote_participant.dart';
|
||||
|
||||
@@ -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