* Add flutter_lints package * Use key in widget constructors lint: use_key_in_widget_constructors * Unnecessary new keyword lint: unnecessary_new * Prefer const with constant constructors * Exclude protobuf files from analyzer * Private field could be final * Annotate overridden members * Use initializing formals when possible * Don't access members with `this` unless avoiding shadowing * Prefer is! operator * Use isEmpty instead of length * Use collection literals when possible * Revert comment changes * Cleaner null testing * Misc fixes * Avoid using `forEach` with a function literal * Prefer `final` over `var` where applicable * Enforce stricter type-checking * Ignore VS Code files * Flutter format * Prefer using lowerCamelCase for constant names * flutter format -l 100 * Disable `avoid_print` for examples * Slight improvements to example Should solve lint error: no_logic_in_create_state
150 lines
4.3 KiB
Dart
150 lines
4.3 KiB
Dart
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
|
|
|
import '../errors.dart';
|
|
import '../proto/livekit_models.pb.dart';
|
|
import '../proto/livekit_rtc.pbserver.dart';
|
|
import '../rtc_engine.dart';
|
|
import '../track/local_audio_track.dart';
|
|
import '../track/local_track_publication.dart';
|
|
import '../track/local_video_track.dart';
|
|
import '../track/track.dart';
|
|
import '../track/track_publication.dart';
|
|
import 'participant.dart';
|
|
|
|
/// Represents the current participant in the room.
|
|
class LocalParticipant extends Participant {
|
|
final RTCEngine _engine;
|
|
|
|
LocalParticipant({
|
|
required RTCEngine engine,
|
|
required ParticipantInfo info,
|
|
}) : _engine = engine,
|
|
super(info.sid, info.identity) {
|
|
updateFromInfo(info);
|
|
}
|
|
|
|
/// for internal use
|
|
/// {@nodoc}
|
|
RTCEngine get engine => _engine;
|
|
|
|
/// publish an audio track to the room
|
|
Future<TrackPublication> publishAudioTrack(LocalAudioTrack track) async {
|
|
if (audioTracks.values.any((element) => element.track?.mediaTrack.id == track.mediaTrack.id)) {
|
|
return Future.error(TrackPublishError('track already exists'));
|
|
}
|
|
|
|
try {
|
|
final trackInfo =
|
|
await _engine.addTrack(cid: track.getCid(), name: track.name, kind: track.kind);
|
|
final transceiverInit = RTCRtpTransceiverInit(
|
|
direction: TransceiverDirection.SendOnly,
|
|
);
|
|
// addTransceiver cannot pass in a kind parameter due to a bug in flutter-webrtc (web)
|
|
track.transceiver = await _engine.publisher?.pc.addTransceiver(
|
|
track: track.mediaTrack,
|
|
init: transceiverInit,
|
|
);
|
|
|
|
final pub = LocalTrackPublication(trackInfo, track, this);
|
|
addTrackPublication(pub);
|
|
notifyListeners();
|
|
|
|
return pub;
|
|
} catch (e) {
|
|
return Future.error(e);
|
|
}
|
|
}
|
|
|
|
/// 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)) {
|
|
return Future.error(TrackPublishError('track already exists'));
|
|
}
|
|
|
|
try {
|
|
final trackInfo =
|
|
await _engine.addTrack(cid: track.getCid(), name: track.name, kind: track.kind);
|
|
final transceiverInit = RTCRtpTransceiverInit(
|
|
direction: TransceiverDirection.SendOnly,
|
|
);
|
|
// TODO: video encodings and simulcasts
|
|
// addTransceiver cannot pass in a kind parameter due to a bug in flutter-webrtc (web)
|
|
track.transceiver = await _engine.publisher?.pc.addTransceiver(
|
|
track: track.mediaTrack,
|
|
init: transceiverInit,
|
|
);
|
|
|
|
final pub = LocalTrackPublication(trackInfo, track, this);
|
|
addTrackPublication(pub);
|
|
notifyListeners();
|
|
|
|
return pub;
|
|
} catch (e) {
|
|
return Future.error(e);
|
|
}
|
|
}
|
|
|
|
/// Unpublish a track that's already published
|
|
void unpublishTrack(Track track) {
|
|
final existing = tracks.values.where((element) => element.track == track);
|
|
if (existing.isEmpty) {
|
|
return;
|
|
}
|
|
final pub = existing.first;
|
|
|
|
track.stop();
|
|
final sender = track.transceiver?.sender;
|
|
if (sender != null) {
|
|
engine.publisher?.pc.removeTrack(sender);
|
|
}
|
|
|
|
tracks.remove(pub.sid);
|
|
switch (pub.kind) {
|
|
case TrackType.AUDIO:
|
|
audioTracks.remove(pub.sid);
|
|
break;
|
|
case TrackType.VIDEO:
|
|
videoTracks.remove(pub.sid);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// Publish a new data payload to the room.
|
|
/// @param destinationSids When empty, data will be forwarded to each participant in the room.
|
|
void publishData(List<int> data, DataPacket_Kind reliability, {List<String>? destinationSids}) {
|
|
RTCDataChannel? channel;
|
|
switch (reliability) {
|
|
case DataPacket_Kind.RELIABLE:
|
|
channel = engine.reliableDC;
|
|
break;
|
|
case DataPacket_Kind.LOSSY:
|
|
channel = engine.lossyDC;
|
|
break;
|
|
}
|
|
if (channel == null) {
|
|
return;
|
|
}
|
|
|
|
final packet = DataPacket(
|
|
kind: reliability,
|
|
user: UserPacket(
|
|
payload: data,
|
|
participantSid: sid,
|
|
destinationSids: destinationSids,
|
|
),
|
|
);
|
|
|
|
final buffer = packet.writeToBuffer();
|
|
channel.send(RTCDataChannelMessage.fromBinary(buffer));
|
|
}
|
|
|
|
/// for internal use
|
|
/// {@nodoc}
|
|
@override
|
|
void updateFromInfo(ParticipantInfo info) {
|
|
super.updateFromInfo(info);
|
|
}
|
|
}
|