Skip to content

Commit e0fe2d2

Browse files
committed
Mute feature focused on better user experience without bringing the track to off state.
1 parent 29b2e8d commit e0fe2d2

File tree

1 file changed

+59
-9
lines changed

1 file changed

+59
-9
lines changed

lib/src/track/local/local.dart

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ abstract class LocalTrack extends Track {
8686
bool _stopped = false;
8787

8888
TrackProcessor? _processor;
89+
90+
// Store original sender parameters to restore on unmute
91+
rtc.RTCRtpParameters? _originalSenderParameters;
8992

9093
TrackProcessor? get processor => _processor;
9194

@@ -103,30 +106,77 @@ abstract class LocalTrack extends Track {
103106
};
104107
}
105108

106-
/// Mutes this [LocalTrack]. This will stop the sending of track data
109+
/// Mutes this [LocalTrack]. This will zero the audio frames
107110
/// and notify the [RemoteParticipant] with [TrackMutedEvent].
108111
/// Returns true if muted, false if unchanged.
109112
Future<bool> mute({bool stopOnMute = true}) async {
110113
logger.fine('LocalTrack.mute() muted: $muted');
111114
if (muted) return false; // already muted
112-
await disable();
113-
if (!skipStopForTrackMute() && stopOnMute) {
114-
await stop();
115+
116+
// For audio tracks, zero the frames by manipulating sender parameters
117+
if (this is AudioTrack && sender != null) {
118+
try {
119+
final parameters = sender!.parameters;
120+
// Store original parameters if not already stored
121+
if (_originalSenderParameters == null) {
122+
_originalSenderParameters = parameters;
123+
}
124+
// Zero out the audio by setting maxBitrate to 0 for all encodings
125+
if (parameters.encodings != null && parameters.encodings!.isNotEmpty) {
126+
for (var encoding in parameters.encodings!) {
127+
encoding.maxBitrate = 0;
128+
}
129+
await sender!.setParameters(parameters);
130+
}
131+
} catch (error) {
132+
logger.warning('Failed to modify sender parameters on mute: $error');
133+
}
134+
} else {
135+
// For video tracks, use the original disable logic
136+
await disable();
137+
if (!skipStopForTrackMute() && stopOnMute) {
138+
await stop();
139+
}
115140
}
141+
116142
updateMuted(true, shouldSendSignal: true);
117143
return true;
118144
}
119145

120-
/// Un-mutes this [LocalTrack]. This will re-start the sending of track data
146+
/// Un-mutes this [LocalTrack]. This will restore the audio frames to normal
121147
/// and notify the [RemoteParticipant] with [TrackUnmutedEvent].
122148
/// Returns true if un-muted, false if unchanged.
123149
Future<bool> unmute({bool stopOnMute = true}) async {
124150
logger.fine('LocalTrack.unmute() muted: $muted');
125151
if (!muted) return false; // already un-muted
126-
if (!skipStopForTrackMute() && stopOnMute) {
127-
await restartTrack();
152+
153+
// For audio tracks, restore the frames by restoring sender parameters
154+
if (this is AudioTrack && sender != null) {
155+
try {
156+
if (_originalSenderParameters != null) {
157+
// Restore the original sender parameters
158+
await sender!.setParameters(_originalSenderParameters!);
159+
} else {
160+
// If no stored parameters, reset to allow full bitrate
161+
final parameters = sender!.parameters;
162+
if (parameters.encodings != null && parameters.encodings!.isNotEmpty) {
163+
for (var encoding in parameters.encodings!) {
164+
encoding.maxBitrate = null; // Remove bitrate limitation
165+
}
166+
await sender!.setParameters(parameters);
167+
}
168+
}
169+
} catch (error) {
170+
logger.warning('Failed to restore sender parameters on unmute: $error');
171+
}
172+
} else {
173+
// For video tracks, use the original enable logic
174+
if (!skipStopForTrackMute() && stopOnMute) {
175+
await restartTrack();
176+
}
177+
await enable();
128178
}
129-
await enable();
179+
130180
updateMuted(false, shouldSendSignal: true);
131181
return true;
132182
}
@@ -322,4 +372,4 @@ abstract class LocalTrack extends Track {
322372
_published = false;
323373
return true;
324374
}
325-
}
375+
}

0 commit comments

Comments
 (0)