Subscriber as primary (#8)
* Update protos * Signal client * Improve transport * First implementation * Fix publish * Update `PCTransport` for debounce negotiation * debounce func * reconnect & events * Update debounce func * engine events * make sure events don't emit after dispose * prefix flutter_webrtc * Cleaner `iceServers` update * don't mutate user provided params * fix tests * event manager * Fix: `Room.onDisconnected` gets fired multiple times * data publish in example * cleaner logic * safer events * un-prefix with LK * organize imports * Clean up * remove `Tuple` * `EventsEmitter` can now be directly listened to `EventsEmitter` extends `EventsListenable`
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
||||
|
||||
import '../errors.dart';
|
||||
import '../extensions.dart';
|
||||
import '../logger.dart';
|
||||
import '../options.dart';
|
||||
import '../proto/livekit_models.pb.dart' as lk_models;
|
||||
@@ -11,6 +12,7 @@ import '../track/local_track_publication.dart';
|
||||
import '../track/local_video_track.dart';
|
||||
import '../track/track.dart';
|
||||
import '../track/track_publication.dart';
|
||||
import '../types.dart';
|
||||
import '../utils.dart';
|
||||
import 'participant.dart';
|
||||
|
||||
@@ -35,7 +37,7 @@ class LocalParticipant extends Participant {
|
||||
/// publish an audio track to the room
|
||||
Future<TrackPublication> publishAudioTrack(LocalAudioTrack track) async {
|
||||
if (audioTracks.any((e) => e.track?.mediaStreamTrack.id == track.mediaStreamTrack.id)) {
|
||||
throw TrackPublishError('track already exists');
|
||||
throw TrackPublishException('track already exists');
|
||||
}
|
||||
|
||||
// try {
|
||||
@@ -45,14 +47,16 @@ class LocalParticipant extends Participant {
|
||||
kind: track.kind,
|
||||
);
|
||||
|
||||
final transceiverInit = RTCRtpTransceiverInit(
|
||||
direction: TransceiverDirection.SendOnly,
|
||||
final transceiverInit = rtc.RTCRtpTransceiverInit(
|
||||
direction: rtc.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.mediaStreamTrack,
|
||||
kind: rtc.RTCRtpMediaType.RTCRtpMediaTypeAudio,
|
||||
init: transceiverInit,
|
||||
);
|
||||
await _engine.negotiate();
|
||||
|
||||
final pub = LocalTrackPublication(trackInfo, track, this);
|
||||
addTrackPublication(pub);
|
||||
@@ -67,7 +71,7 @@ class LocalParticipant extends Participant {
|
||||
TrackPublishOptions? options,
|
||||
}) async {
|
||||
if (videoTracks.any((e) => e.track?.mediaStreamTrack.id == track.mediaStreamTrack.id)) {
|
||||
throw TrackPublishError('track already exists');
|
||||
throw TrackPublishException('track already exists');
|
||||
}
|
||||
|
||||
// Use default options from `ConnectOptions` if options is null
|
||||
@@ -78,10 +82,9 @@ class LocalParticipant extends Participant {
|
||||
name: track.name,
|
||||
kind: track.kind,
|
||||
);
|
||||
logger.fine('publishVideoTrack addTrack response: ${trackInfo}');
|
||||
|
||||
//
|
||||
// Video encodings and simulcasts
|
||||
//
|
||||
|
||||
// use constraints passed to getUserMedia by default
|
||||
int? width = track.currentOptions.params.width;
|
||||
@@ -94,8 +97,6 @@ class LocalParticipant extends Participant {
|
||||
final settings = track.mediaStreamTrack.getSettings();
|
||||
width = settings['width'] as int?;
|
||||
height = settings['height'] as int?;
|
||||
// TODO: Get actual video dimensions to compute more accurately
|
||||
// mediaTrack.getConsstraints() is not implemented for mobile
|
||||
} catch (_) {
|
||||
logger.warning('Failed to call `mediaStreamTrack.getSettings()`');
|
||||
}
|
||||
@@ -111,19 +112,20 @@ class LocalParticipant extends Participant {
|
||||
|
||||
logger.fine('Using encodings: ${encodings?.map((e) => e.toMap())}');
|
||||
|
||||
final transceiverInit = RTCRtpTransceiverInit(
|
||||
direction: TransceiverDirection.SendOnly,
|
||||
final transceiverInit = rtc.RTCRtpTransceiverInit(
|
||||
direction: rtc.TransceiverDirection.SendOnly,
|
||||
sendEncodings: encodings,
|
||||
streams: [track.mediaStream],
|
||||
);
|
||||
|
||||
//
|
||||
// addTransceiver cannot pass in a kind parameter due to a bug in flutter-webrtc (web)
|
||||
//
|
||||
logger.fine('publishVideoTrack publisher: ${_engine.publisher}');
|
||||
|
||||
track.transceiver = await _engine.publisher?.pc.addTransceiver(
|
||||
track: track.mediaStreamTrack,
|
||||
kind: rtc.RTCRtpMediaType.RTCRtpMediaTypeVideo,
|
||||
init: transceiverInit,
|
||||
);
|
||||
await _engine.negotiate();
|
||||
|
||||
final pub = LocalTrackPublication(trackInfo, track, this);
|
||||
addTrackPublication(pub);
|
||||
@@ -144,6 +146,7 @@ class LocalParticipant extends Participant {
|
||||
final sender = track.transceiver?.sender;
|
||||
if (sender != null) {
|
||||
await engine.publisher?.pc.removeTrack(sender);
|
||||
await engine.negotiate();
|
||||
}
|
||||
|
||||
tracks.remove(pub.sid);
|
||||
@@ -151,26 +154,13 @@ 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.
|
||||
void publishData(
|
||||
List<int> data,
|
||||
lk_models.DataPacket_Kind reliability, {
|
||||
Future<void> publishData(
|
||||
List<int> data, {
|
||||
Reliability reliability = Reliability.reliable,
|
||||
List<String>? destinationSids,
|
||||
}) {
|
||||
RTCDataChannel? channel;
|
||||
switch (reliability) {
|
||||
case lk_models.DataPacket_Kind.RELIABLE:
|
||||
channel = engine.reliableDC;
|
||||
break;
|
||||
case lk_models.DataPacket_Kind.LOSSY:
|
||||
channel = engine.lossyDC;
|
||||
break;
|
||||
}
|
||||
if (channel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
}) async {
|
||||
final packet = lk_models.DataPacket(
|
||||
kind: reliability,
|
||||
kind: reliability.toPBType(),
|
||||
user: lk_models.UserPacket(
|
||||
payload: data,
|
||||
participantSid: sid,
|
||||
@@ -178,8 +168,7 @@ class LocalParticipant extends Participant {
|
||||
),
|
||||
);
|
||||
|
||||
final buffer = packet.writeToBuffer();
|
||||
channel.send(RTCDataChannelMessage.fromBinary(buffer));
|
||||
await engine.sendDataPacket(packet);
|
||||
}
|
||||
|
||||
/// for internal use
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../events.dart';
|
||||
import '../managers/event.dart';
|
||||
import '../proto/livekit_models.pb.dart' as lk_models;
|
||||
import '../track/remote_track_publication.dart';
|
||||
import '../track/track.dart';
|
||||
@@ -77,6 +79,9 @@ class Participant extends ChangeNotifier {
|
||||
lk_models.ParticipantInfo? _participantInfo;
|
||||
bool _isSpeaking = false;
|
||||
|
||||
// suppport for multiple event listeners
|
||||
final events = EventsEmitter<ParticipantEvent>();
|
||||
|
||||
/// when the participant joined the room
|
||||
DateTime get joinedAt {
|
||||
final pi = _participantInfo;
|
||||
@@ -159,7 +164,7 @@ class Participant extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// Convenience extension
|
||||
extension LKParticipantExt on Participant {
|
||||
extension ParticipantExt on Participant {
|
||||
List<TrackPublication> get videoTracks =>
|
||||
tracks.values.where((e) => e.kind == lk_models.TrackType.VIDEO).toList();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
||||
|
||||
import '../logger.dart';
|
||||
import '../proto/livekit_models.pb.dart' as lk_models;
|
||||
@@ -35,7 +35,11 @@ class RemoteParticipant extends Participant {
|
||||
|
||||
/// for internal use
|
||||
/// {@nodoc}
|
||||
void addSubscribedMediaTrack(MediaStreamTrack mediaTrack, MediaStream stream, String? sid) async {
|
||||
void addSubscribedMediaTrack(
|
||||
rtc.MediaStreamTrack mediaTrack,
|
||||
rtc.MediaStream stream,
|
||||
String? sid,
|
||||
) async {
|
||||
if (sid == null) {
|
||||
const msg = 'addSubscribedMediaTrack received null sid';
|
||||
delegate?.onTrackSubscriptionFailed(this, '', msg);
|
||||
|
||||
Reference in New Issue
Block a user