Match muted behavior with JS SDK (#32)
* attempt 1 * implement * clean up * clean up * cleaner code * mute was opposite * fix suggestion * fix local pub muted * update example * update pubs * format
This commit is contained in:
@@ -51,7 +51,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
if (result == true) await participant.unpublishAllTracks();
|
if (result == true) await participant.unpublishAllTracks();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _muteAudio() async {
|
void _disableAudio() async {
|
||||||
await participant.setMicrophoneEnabled(false);
|
await participant.setMicrophoneEnabled(false);
|
||||||
// The following code is an example how to mute a track
|
// The following code is an example how to mute a track
|
||||||
// if (participant.hasAudio) {
|
// if (participant.hasAudio) {
|
||||||
@@ -60,7 +60,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _unmuteAudio() async {
|
Future<void> _enableAudio() async {
|
||||||
await participant.setMicrophoneEnabled(true);
|
await participant.setMicrophoneEnabled(true);
|
||||||
// The following code is an example how to unmute / publish a audio track
|
// The following code is an example how to unmute / publish a audio track
|
||||||
// if (participant.hasAudio) {
|
// if (participant.hasAudio) {
|
||||||
@@ -73,7 +73,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void _muteVideo() async {
|
void _disableVideo() async {
|
||||||
await participant.setCameraEnabled(false);
|
await participant.setCameraEnabled(false);
|
||||||
// The following code is an example how to mute a video track
|
// The following code is an example how to mute a video track
|
||||||
// if (participant.hasVideo) {
|
// if (participant.hasVideo) {
|
||||||
@@ -82,7 +82,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void _unmuteVideo() async {
|
void _enableVideo() async {
|
||||||
await participant.setCameraEnabled(true);
|
await participant.setCameraEnabled(true);
|
||||||
// The following code is an example how to unmute / publish a video track
|
// The following code is an example how to unmute / publish a video track
|
||||||
// if (participant.hasVideo) {
|
// if (participant.hasVideo) {
|
||||||
@@ -114,7 +114,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _shareScreen() async {
|
void _enableScreenShare() async {
|
||||||
final lp = widget.room.localParticipant;
|
final lp = widget.room.localParticipant;
|
||||||
|
|
||||||
for (final track in lp.videoTracks) {
|
for (final track in lp.videoTracks) {
|
||||||
@@ -143,7 +143,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _unshareScreen() async {
|
void _disableScreenShare() async {
|
||||||
final lp = widget.room.localParticipant;
|
final lp = widget.room.localParticipant;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -175,16 +175,6 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// mute audio
|
|
||||||
final canMute = participant.hasAudio && !participant.isMuted;
|
|
||||||
|
|
||||||
final videoPub =
|
|
||||||
participant.getTrackPublicationBySource(TrackSource.camera);
|
|
||||||
final videoEnabled = videoPub != null && !videoPub.muted;
|
|
||||||
final screenSharePub =
|
|
||||||
participant.getTrackPublicationBySource(TrackSource.screenShareVideo);
|
|
||||||
final screenShareEnabled = screenSharePub != null && !screenSharePub.muted;
|
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 15,
|
vertical: 15,
|
||||||
@@ -200,27 +190,27 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
icon: const Icon(EvaIcons.closeCircleOutline),
|
icon: const Icon(EvaIcons.closeCircleOutline),
|
||||||
tooltip: 'Unpublish all',
|
tooltip: 'Unpublish all',
|
||||||
),
|
),
|
||||||
if (canMute)
|
if (participant.isMicrophoneEnabled())
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: _muteAudio,
|
onPressed: _disableAudio,
|
||||||
icon: const Icon(EvaIcons.mic),
|
icon: const Icon(EvaIcons.mic),
|
||||||
tooltip: 'mute audio',
|
tooltip: 'mute audio',
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: _unmuteAudio,
|
onPressed: _enableAudio,
|
||||||
icon: const Icon(EvaIcons.micOff),
|
icon: const Icon(EvaIcons.micOff),
|
||||||
tooltip: 'un-mute audio',
|
tooltip: 'un-mute audio',
|
||||||
),
|
),
|
||||||
if (videoEnabled)
|
if (participant.isCameraEnabled())
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: _muteVideo,
|
onPressed: _disableVideo,
|
||||||
icon: const Icon(EvaIcons.video),
|
icon: const Icon(EvaIcons.video),
|
||||||
tooltip: 'mute video',
|
tooltip: 'mute video',
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: _unmuteVideo,
|
onPressed: _enableVideo,
|
||||||
icon: const Icon(EvaIcons.videoOff),
|
icon: const Icon(EvaIcons.videoOff),
|
||||||
tooltip: 'un-mute video',
|
tooltip: 'un-mute video',
|
||||||
),
|
),
|
||||||
@@ -231,16 +221,16 @@ class _ControlsWidgetState extends State<ControlsWidget> {
|
|||||||
onPressed: () => _toggleCamera(),
|
onPressed: () => _toggleCamera(),
|
||||||
tooltip: 'toggle camera',
|
tooltip: 'toggle camera',
|
||||||
),
|
),
|
||||||
if (screenShareEnabled)
|
if (participant.isScreenShareEnabled())
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(EvaIcons.monitorOutline),
|
icon: const Icon(EvaIcons.monitorOutline),
|
||||||
onPressed: () => _unshareScreen(),
|
onPressed: () => _disableScreenShare(),
|
||||||
tooltip: 'unshare screen (experimental)',
|
tooltip: 'unshare screen (experimental)',
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(EvaIcons.monitor),
|
icon: const Icon(EvaIcons.monitor),
|
||||||
onPressed: () => _shareScreen(),
|
onPressed: () => _enableScreenShare(),
|
||||||
tooltip: 'share screen (experimental)',
|
tooltip: 'share screen (experimental)',
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|||||||
@@ -232,28 +232,28 @@ packages:
|
|||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6"
|
version: "2.0.7"
|
||||||
path_provider_ios:
|
path_provider_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_ios
|
name: path_provider_ios
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6"
|
version: "2.0.7"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_linux
|
name: path_provider_linux
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_macos:
|
path_provider_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_macos
|
name: path_provider_macos
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.3"
|
||||||
path_provider_platform_interface:
|
path_provider_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -61,3 +61,13 @@ class TrackVisibilityUpdatedEvent with TrackEvent, InternalEvent {
|
|||||||
required this.info,
|
required this.info,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
|
class TrackMuteUpdatedEvent with TrackEvent, InternalEvent {
|
||||||
|
final Track track;
|
||||||
|
final bool muted;
|
||||||
|
const TrackMuteUpdatedEvent({
|
||||||
|
required this.track,
|
||||||
|
required this.muted,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -247,15 +247,16 @@ extension LocalParticipantTrackSourceExt on LocalParticipant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setSourceEnabled(TrackSource source, bool enabled) async {
|
Future<void> setSourceEnabled(TrackSource source, bool enabled) async {
|
||||||
final pub = getTrackPublicationBySource(source);
|
logger.fine('setSourceEnabled(source: $source, enabled: $enabled)');
|
||||||
|
final pub = getTrackPublicationBySource(source) as LocalTrackPublication?;
|
||||||
if (pub != null) {
|
if (pub != null) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
pub.muted = false;
|
await pub.unmute();
|
||||||
} else {
|
} else {
|
||||||
if (source == TrackSource.screenShareVideo) {
|
if (source == TrackSource.screenShareVideo) {
|
||||||
await unpublishTrack(pub.sid);
|
await unpublishTrack(pub.sid);
|
||||||
} else {
|
} else {
|
||||||
pub.muted = true;
|
await pub.mute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (enabled) {
|
} else if (enabled) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:livekit_client/src/track/track.dart';
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import '../events.dart';
|
import '../events.dart';
|
||||||
@@ -8,6 +7,7 @@ import '../logger.dart';
|
|||||||
import '../managers/event.dart';
|
import '../managers/event.dart';
|
||||||
import '../proto/livekit_models.pb.dart' as lk_models;
|
import '../proto/livekit_models.pb.dart' as lk_models;
|
||||||
import '../support/disposable.dart';
|
import '../support/disposable.dart';
|
||||||
|
import '../track/track.dart';
|
||||||
import '../track/track_publication.dart';
|
import '../track/track_publication.dart';
|
||||||
import '../types.dart';
|
import '../types.dart';
|
||||||
import 'remote_participant.dart';
|
import 'remote_participant.dart';
|
||||||
@@ -199,6 +199,11 @@ extension ParticipantTrackSourceExt on Participant {
|
|||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isScreenShareEnabled() {
|
||||||
|
return !(getTrackPublicationBySource(TrackSource.screenShareVideo)?.muted ??
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
/// Find a track publication by its [TrackSource]
|
/// Find a track publication by its [TrackSource]
|
||||||
TrackPublication? getTrackPublicationBySource(TrackSource source) {
|
TrackPublication? getTrackPublicationBySource(TrackSource source) {
|
||||||
if (source == TrackSource.unknown) return null;
|
if (source == TrackSource.unknown) return null;
|
||||||
|
|||||||
@@ -98,8 +98,7 @@ class RemoteParticipant extends Participant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await track.start();
|
await track.start();
|
||||||
|
await pub.updateTrack(track);
|
||||||
pub.track = track;
|
|
||||||
addTrackPublication(pub);
|
addTrackPublication(pub);
|
||||||
|
|
||||||
[events, roomEvents].emit(TrackSubscribedEvent(
|
[events, roomEvents].emit(TrackSubscribedEvent(
|
||||||
|
|||||||
+8
-2
@@ -16,6 +16,7 @@ import 'proto/livekit_models.pb.dart' as lk_models;
|
|||||||
import 'proto/livekit_rtc.pb.dart' as lk_rtc;
|
import 'proto/livekit_rtc.pb.dart' as lk_rtc;
|
||||||
import 'rtc_engine.dart';
|
import 'rtc_engine.dart';
|
||||||
import 'support/disposable.dart';
|
import 'support/disposable.dart';
|
||||||
|
import 'track/local_track_publication.dart';
|
||||||
import 'track/track.dart';
|
import 'track/track.dart';
|
||||||
import 'types.dart';
|
import 'types.dart';
|
||||||
|
|
||||||
@@ -178,8 +179,13 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
|
|||||||
(event) => _onSignalConnectionQualityUpdateEvent(event.updates))
|
(event) => _onSignalConnectionQualityUpdateEvent(event.updates))
|
||||||
..on<EngineDataPacketReceivedEvent>(_onDataMessageEvent)
|
..on<EngineDataPacketReceivedEvent>(_onDataMessageEvent)
|
||||||
..on<EngineRemoteMuteChangedEvent>((event) async {
|
..on<EngineRemoteMuteChangedEvent>((event) async {
|
||||||
final track = localParticipant.trackPublications[event.sid];
|
final publication = localParticipant.trackPublications[event.sid]
|
||||||
track?.muted = event.muted;
|
as LocalTrackPublication?;
|
||||||
|
if (event.muted) {
|
||||||
|
await publication?.mute();
|
||||||
|
} else {
|
||||||
|
await publication?.unmute();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
..on<EngineTrackAddedEvent>((event) async {
|
..on<EngineTrackAddedEvent>((event) async {
|
||||||
final idParts = event.stream.id.split('|');
|
final idParts = event.stream.id.split('|');
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import '../exceptions.dart';
|
|||||||
import '../logger.dart';
|
import '../logger.dart';
|
||||||
import '../types.dart';
|
import '../types.dart';
|
||||||
import 'audio_track.dart';
|
import 'audio_track.dart';
|
||||||
|
import 'local_track.dart';
|
||||||
import 'options.dart';
|
import 'options.dart';
|
||||||
|
|
||||||
class LocalAudioTrack extends AudioTrack {
|
class LocalAudioTrack extends AudioTrack with LocalTrack {
|
||||||
// private constructor
|
// private constructor
|
||||||
LocalAudioTrack._(
|
LocalAudioTrack._(
|
||||||
TrackSource source,
|
TrackSource source,
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import '../internal/events.dart';
|
||||||
|
import '../logger.dart';
|
||||||
|
import 'track.dart';
|
||||||
|
|
||||||
|
mixin LocalTrack on Track {
|
||||||
|
// only local tracks can set muted
|
||||||
|
Future<void> mute() async {
|
||||||
|
logger.fine('LocalTrack.mute() muted: $muted');
|
||||||
|
if (muted) return;
|
||||||
|
await disable();
|
||||||
|
updateMuted(true);
|
||||||
|
events.emit(TrackMuteUpdatedEvent(track: this, muted: muted));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> unmute() async {
|
||||||
|
logger.fine('LocalTrack.unmute() muted: $muted');
|
||||||
|
if (!muted) return;
|
||||||
|
await enable();
|
||||||
|
updateMuted(false);
|
||||||
|
events.emit(TrackMuteUpdatedEvent(track: this, muted: muted));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import '../events.dart';
|
import '../events.dart';
|
||||||
import '../extensions.dart';
|
import '../extensions.dart';
|
||||||
import '../logger.dart';
|
import '../internal/events.dart';
|
||||||
import '../participant/local_participant.dart';
|
import '../participant/local_participant.dart';
|
||||||
import '../proto/livekit_models.pb.dart' as lk_models;
|
import '../proto/livekit_models.pb.dart' as lk_models;
|
||||||
|
import 'local_track.dart';
|
||||||
import 'track.dart';
|
import 'track.dart';
|
||||||
import 'track_publication.dart';
|
import 'track_publication.dart';
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ class LocalTrackPublication extends TrackPublication {
|
|||||||
Track track,
|
Track track,
|
||||||
this._participant,
|
this._participant,
|
||||||
) : super.fromInfo(info) {
|
) : super.fromInfo(info) {
|
||||||
this.track = track;
|
updateTrack(track);
|
||||||
// register dispose func
|
// register dispose func
|
||||||
onDispose(() async {
|
onDispose(() async {
|
||||||
// this object is responsible for disposing track
|
// this object is responsible for disposing track
|
||||||
@@ -22,28 +23,42 @@ class LocalTrackPublication extends TrackPublication {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mute or unmute the current track. When muted, track will stop sending data
|
|
||||||
@override
|
@override
|
||||||
set muted(bool val) {
|
Future<bool> updateTrack(Track? newValue) async {
|
||||||
if (val == muted) return;
|
final didUpdate = await super.updateTrack(newValue);
|
||||||
logger.finer('setMute: ${val}');
|
|
||||||
|
|
||||||
super.muted = val;
|
if (newValue != null) {
|
||||||
track?.mediaStreamTrack.enabled = !val;
|
// attach listener to track
|
||||||
_participant.engine.signalClient.sendMuteTrack(sid, val);
|
final listener = newValue.createListener()
|
||||||
|
// listen for track muted events
|
||||||
if (val) {
|
..on<TrackMuteUpdatedEvent>((event) {
|
||||||
// Track muted
|
// send signal to server
|
||||||
[_participant.events, _participant.roomEvents].emit(TrackMutedEvent(
|
_participant.engine.signalClient.sendMuteTrack(sid, event.muted);
|
||||||
participant: _participant,
|
// emit events
|
||||||
track: this,
|
final newEvent = event.muted
|
||||||
));
|
? TrackMutedEvent(participant: _participant, track: this)
|
||||||
} else {
|
: TrackUnmutedEvent(participant: _participant, track: this);
|
||||||
// Track un-muted
|
[_participant.events, _participant.roomEvents].emit(newEvent);
|
||||||
[_participant.events, _participant.roomEvents].emit(TrackUnmutedEvent(
|
});
|
||||||
participant: _participant,
|
// dispose listener when the track is disposed
|
||||||
track: this,
|
newValue.onDispose(() => listener.dispose());
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return didUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get muted => track?.muted ?? super.muted;
|
||||||
|
|
||||||
|
Future<void> mute() async {
|
||||||
|
if (track is! LocalTrack) return;
|
||||||
|
// Mute the track associated with this publication
|
||||||
|
return (track as LocalTrack).mute();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> unmute() async {
|
||||||
|
if (track is! LocalTrack) return;
|
||||||
|
// Unmute the track associated with this publication
|
||||||
|
return (track as LocalTrack).unmute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
|
|||||||
import '../exceptions.dart';
|
import '../exceptions.dart';
|
||||||
import '../logger.dart';
|
import '../logger.dart';
|
||||||
import '../types.dart';
|
import '../types.dart';
|
||||||
|
import 'local_track.dart';
|
||||||
import 'options.dart';
|
import 'options.dart';
|
||||||
import 'track.dart';
|
import 'track.dart';
|
||||||
import 'video_track.dart';
|
import 'video_track.dart';
|
||||||
|
|
||||||
/// A video track from the local device. Use static methods in this class to create
|
/// A video track from the local device. Use static methods in this class to create
|
||||||
/// video tracks.
|
/// video tracks.
|
||||||
class LocalVideoTrack extends VideoTrack {
|
class LocalVideoTrack extends VideoTrack with LocalTrack {
|
||||||
//
|
//
|
||||||
// Options used for this track
|
// Options used for this track
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -60,7 +60,15 @@ class RemoteTrackPublication extends TrackPublication {
|
|||||||
wait: const Duration(seconds: 2),
|
wait: const Duration(seconds: 2),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.track = track;
|
updateTrack(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
|
@override
|
||||||
|
void updateFromInfo(lk_models.TrackInfo info) {
|
||||||
|
super.updateFromInfo(info);
|
||||||
|
updateMuted(info.muted);
|
||||||
|
track?.updateMuted(info.muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// called any time visibility info updates
|
// called any time visibility info updates
|
||||||
@@ -133,33 +141,33 @@ class RemoteTrackPublication extends TrackPublication {
|
|||||||
_participant.engine.signalClient.sendUpdateTrackSettings(settings);
|
_participant.engine.signalClient.sendUpdateTrackSettings(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
@override
|
@override
|
||||||
set track(Track? newValue) {
|
Future<bool> updateTrack(Track? newValue) async {
|
||||||
if (super.track != newValue) {
|
final didUpdate = await super.updateTrack(track);
|
||||||
logger.fine('setTrack ${newValue} $sid ${objectId}');
|
|
||||||
// dispose previous track (if exists)
|
|
||||||
super.track?.dispose();
|
|
||||||
super.track = newValue;
|
|
||||||
|
|
||||||
// Only listen for visibility updates if video optimization is on
|
// Only listen for visibility updates if video optimization is on
|
||||||
// and the attached track is a video track
|
// and the attached track is a video track
|
||||||
if (_participant.engine.connectOptions.optimizeVideo &&
|
if (didUpdate &&
|
||||||
newValue != null &&
|
newValue != null &&
|
||||||
newValue.kind == lk_models.TrackType.VIDEO) {
|
_participant.engine.connectOptions.optimizeVideo &&
|
||||||
//
|
newValue.kind == lk_models.TrackType.VIDEO) {
|
||||||
// Attach visibility event listener (if video track)
|
//
|
||||||
//
|
// Attach visibility event listener (if video track)
|
||||||
final listener = newValue.createListener();
|
//
|
||||||
listener.on<TrackVisibilityUpdatedEvent>(
|
final listener = newValue.createListener();
|
||||||
_onVideoRendererVisibilityUpdateEvent);
|
listener.on<TrackVisibilityUpdatedEvent>(
|
||||||
newValue.onDispose(() async {
|
_onVideoRendererVisibilityUpdateEvent);
|
||||||
await listener.dispose();
|
//
|
||||||
// consider all views are disposed when track is null
|
newValue.onDispose(() async {
|
||||||
_visibilities.clear();
|
await listener.dispose();
|
||||||
if (!isDisposed) _visibilityDidUpdate?.call(null);
|
// consider all views are disposed when track is null
|
||||||
});
|
_visibilities.clear();
|
||||||
}
|
if (!isDisposed) _visibilityDidUpdate?.call(null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return didUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
set videoQuality(lk_rtc.VideoQuality val) {
|
set videoQuality(lk_rtc.VideoQuality val) {
|
||||||
@@ -190,34 +198,7 @@ class RemoteTrackPublication extends TrackPublication {
|
|||||||
publication: this,
|
publication: this,
|
||||||
));
|
));
|
||||||
// Simply set to null for now
|
// Simply set to null for now
|
||||||
track = null;
|
updateTrack(null);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// for internal use
|
|
||||||
/// {@nodoc}
|
|
||||||
@override
|
|
||||||
@internal
|
|
||||||
set muted(bool val) {
|
|
||||||
if (val == muted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.muted = val;
|
|
||||||
if (val) {
|
|
||||||
// Track muted
|
|
||||||
[_participant.events, _participant.roomEvents].emit(TrackMutedEvent(
|
|
||||||
participant: _participant,
|
|
||||||
track: this,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// Track un-muted
|
|
||||||
[_participant.events, _participant.roomEvents].emit(TrackUnmutedEvent(
|
|
||||||
participant: _participant,
|
|
||||||
track: this,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if (subscribed) {
|
|
||||||
track?.mediaStreamTrack.enabled = !val;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ abstract class Track extends DisposableChangeNotifier
|
|||||||
bool _active = false;
|
bool _active = false;
|
||||||
bool get isActive => _active;
|
bool get isActive => _active;
|
||||||
|
|
||||||
|
bool _muted = false;
|
||||||
|
bool get muted => _muted;
|
||||||
|
|
||||||
Track(
|
Track(
|
||||||
this.kind,
|
this.kind,
|
||||||
this.source,
|
this.source,
|
||||||
@@ -52,9 +55,6 @@ abstract class Track extends DisposableChangeNotifier
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get muted =>
|
|
||||||
mediaStreamTrack.muted == null ? false : mediaStreamTrack.muted!;
|
|
||||||
|
|
||||||
rtc.RTCRtpMediaType get mediaType {
|
rtc.RTCRtpMediaType get mediaType {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case lk_models.TrackType.AUDIO:
|
case lk_models.TrackType.AUDIO:
|
||||||
@@ -125,4 +125,7 @@ abstract class Track extends DisposableChangeNotifier
|
|||||||
'[$objectId] set rtc.mediaStreamTrack.enabled did throw ${_}');
|
'[$objectId] set rtc.mediaStreamTrack.enabled did throw ${_}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
|
void updateMuted(bool muted) => _muted = muted;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import '../support/disposable.dart';
|
import 'package:meta/meta.dart';
|
||||||
import '../proto/livekit_models.pb.dart' as lk_models;
|
|
||||||
import '../types.dart';
|
|
||||||
import '../extensions.dart';
|
import '../extensions.dart';
|
||||||
|
import '../proto/livekit_models.pb.dart' as lk_models;
|
||||||
|
import '../support/disposable.dart';
|
||||||
|
import '../types.dart';
|
||||||
import 'track.dart';
|
import 'track.dart';
|
||||||
|
|
||||||
/// Represents a track that's published to the server. This class contains
|
/// Represents a track that's published to the server. This class contains
|
||||||
@@ -16,12 +18,17 @@ abstract class TrackPublication extends Disposable {
|
|||||||
final lk_models.TrackType kind;
|
final lk_models.TrackType kind;
|
||||||
final TrackSource source;
|
final TrackSource source;
|
||||||
|
|
||||||
Track? track;
|
Track? _track;
|
||||||
bool muted = false;
|
Track? get track => _track;
|
||||||
|
|
||||||
|
// metadata-muted
|
||||||
|
bool _muted = false;
|
||||||
|
bool get muted => _muted;
|
||||||
|
|
||||||
bool simulcasted = false;
|
bool simulcasted = false;
|
||||||
TrackDimension? dimension;
|
TrackDimension? dimension;
|
||||||
|
|
||||||
bool get subscribed => track != null;
|
bool get subscribed => _track != null;
|
||||||
|
|
||||||
TrackPublication.fromInfo(lk_models.TrackInfo info)
|
TrackPublication.fromInfo(lk_models.TrackInfo info)
|
||||||
: sid = info.sid,
|
: sid = info.sid,
|
||||||
@@ -36,7 +43,6 @@ abstract class TrackPublication extends Disposable {
|
|||||||
kind == lk_models.TrackType.VIDEO && name == Track.screenShareName;
|
kind == lk_models.TrackType.VIDEO && name == Track.screenShareName;
|
||||||
|
|
||||||
void updateFromInfo(lk_models.TrackInfo info) {
|
void updateFromInfo(lk_models.TrackInfo info) {
|
||||||
muted = info.muted;
|
|
||||||
simulcasted = info.simulcast;
|
simulcasted = info.simulcast;
|
||||||
if (info.type == lk_models.TrackType.VIDEO) {
|
if (info.type == lk_models.TrackType.VIDEO) {
|
||||||
dimension = TrackDimension(info.width, info.height);
|
dimension = TrackDimension(info.width, info.height);
|
||||||
@@ -51,4 +57,19 @@ abstract class TrackPublication extends Disposable {
|
|||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
other is TrackPublication && sid == other.sid;
|
other is TrackPublication && sid == other.sid;
|
||||||
|
|
||||||
|
@internal
|
||||||
|
void updateMuted(bool muted) => _muted = muted;
|
||||||
|
|
||||||
|
// Update track to new value, dispose previous if exists.
|
||||||
|
// Returns true if value has changed.
|
||||||
|
// Intended for internal use only.
|
||||||
|
@internal
|
||||||
|
Future<bool> updateTrack(Track? newValue) async {
|
||||||
|
if (_track == newValue) return false;
|
||||||
|
// dispose previous track (if exists)
|
||||||
|
await _track?.dispose();
|
||||||
|
_track = newValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-6
@@ -169,21 +169,35 @@ packages:
|
|||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.0.7"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
|
path_provider_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_ios
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_linux
|
name: path_provider_linux
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.2"
|
||||||
path_provider_macos:
|
path_provider_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_macos
|
name: path_provider_macos
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.3"
|
||||||
path_provider_platform_interface:
|
path_provider_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -197,7 +211,7 @@ packages:
|
|||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.0.4"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -218,7 +232,7 @@ packages:
|
|||||||
name: process
|
name: process
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.3"
|
version: "4.2.4"
|
||||||
protobuf:
|
protobuf:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -314,7 +328,7 @@ packages:
|
|||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.9"
|
version: "2.3.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user