Skip to content

Commit c06a4c8

Browse files
authored
fix(web, firefox): backup old values before overwriting them (#819)
I'm not sure if the comment about disabling layers in Firefox is still accurate. In any case, this PR fixes an issue where quality would not get back up anymore once higher quality levels were disabled because the bitrate/framerate/scale values were overwritten in a shallow copy of the encoding This PR introduces a backup map to properly copy and restore the modified values
1 parent a871a81 commit c06a4c8

File tree

1 file changed

+83
-50
lines changed

1 file changed

+83
-50
lines changed

lib/src/track/local/video.dart

Lines changed: 83 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ class SimulcastTrackInfo {
4040

4141
List<rtc.RTCRtpEncoding>? encodings;
4242

43-
SimulcastTrackInfo(
44-
{required this.codec,
45-
this.encodings,
46-
required this.mediaStreamTrack,
47-
this.sender});
43+
SimulcastTrackInfo({
44+
required this.codec,
45+
this.encodings,
46+
required this.mediaStreamTrack,
47+
this.sender,
48+
});
4849
}
4950

5051
/// A video track from the local device. Use static methods in this class to create
@@ -62,6 +63,7 @@ class LocalVideoTrack extends LocalTrack with VideoTrack {
6263
final Map<String, num> _bitrateFoLayers = {};
6364

6465
Map<String, SimulcastTrackInfo> simulcastCodecs = {};
66+
Map<(String, int), rtc.RTCRtpEncoding> encodingBackups = {};
6567

6668
List<lk_rtc.SubscribedCodec> subscribedCodecs = [];
6769

@@ -100,11 +102,13 @@ class LocalVideoTrack extends LocalTrack with VideoTrack {
100102
}
101103
});
102104
_currentBitrate = totalBitrate;
103-
events.emit(VideoSenderStatsEvent(
104-
stats: statsMap,
105-
currentBitrate: currentBitrate,
106-
bitrateForLayers: _bitrateFoLayers,
107-
));
105+
events.emit(
106+
VideoSenderStatsEvent(
107+
stats: statsMap,
108+
currentBitrate: currentBitrate,
109+
bitrateForLayers: _bitrateFoLayers,
110+
),
111+
);
108112
}
109113

110114
prevStats = statsMap;
@@ -131,14 +135,22 @@ class LocalVideoTrack extends LocalTrack with VideoTrack {
131135
vs.bytesSent = getNumValFromReport(v.values, 'bytesSent');
132136
vs.framesSent = getNumValFromReport(v.values, 'framesSent');
133137
vs.rid = getStringValFromReport(v.values, 'rid');
134-
vs.encoderImplementation =
135-
getStringValFromReport(v.values, 'encoderImplementation');
136-
vs.retransmittedPacketsSent =
137-
getNumValFromReport(v.values, 'retransmittedPacketsSent');
138-
vs.qualityLimitationReason =
139-
getStringValFromReport(v.values, 'qualityLimitationReason');
140-
vs.qualityLimitationResolutionChanges =
141-
getNumValFromReport(v.values, 'qualityLimitationResolutionChanges');
138+
vs.encoderImplementation = getStringValFromReport(
139+
v.values,
140+
'encoderImplementation',
141+
);
142+
vs.retransmittedPacketsSent = getNumValFromReport(
143+
v.values,
144+
'retransmittedPacketsSent',
145+
);
146+
vs.qualityLimitationReason = getStringValFromReport(
147+
v.values,
148+
'qualityLimitationReason',
149+
);
150+
vs.qualityLimitationResolutionChanges = getNumValFromReport(
151+
v.values,
152+
'qualityLimitationResolutionChanges',
153+
);
142154

143155
// locate the appropriate remote-inbound-rtp item
144156
final remoteId = getStringValFromReport(v.values, 'remoteId');
@@ -162,14 +174,12 @@ class LocalVideoTrack extends LocalTrack with VideoTrack {
162174
}
163175

164176
// Private constructor
165-
LocalVideoTrack._(TrackSource source, rtc.MediaStream stream,
166-
rtc.MediaStreamTrack track, this.currentOptions)
167-
: super(
168-
TrackType.VIDEO,
169-
source,
170-
stream,
171-
track,
172-
);
177+
LocalVideoTrack._(
178+
TrackSource source,
179+
rtc.MediaStream stream,
180+
rtc.MediaStreamTrack track,
181+
this.currentOptions,
182+
) : super(TrackType.VIDEO, source, stream, track);
173183

174184
/// Creates a LocalVideoTrack from camera input.
175185
static Future<LocalVideoTrack> createCameraTrack([
@@ -201,7 +211,8 @@ class LocalVideoTrack extends LocalTrack with VideoTrack {
201211
]) async {
202212
if (lkPlatformIsWebMobile()) {
203213
throw TrackCreateException(
204-
'Screen sharing is not supported on mobile devices');
214+
'Screen sharing is not supported on mobile devices',
215+
);
205216
}
206217
options ??= const ScreenShareCaptureOptions();
207218

@@ -224,7 +235,8 @@ class LocalVideoTrack extends LocalTrack with VideoTrack {
224235
]) async {
225236
if (lkPlatformIsWebMobile()) {
226237
throw TrackCreateException(
227-
'Screen sharing is not supported on mobile devices');
238+
'Screen sharing is not supported on mobile devices',
239+
);
228240
}
229241
if (options == null) {
230242
options = const ScreenShareCaptureOptions(captureScreenAudio: true);
@@ -239,12 +251,18 @@ class LocalVideoTrack extends LocalTrack with VideoTrack {
239251
stream,
240252
stream.getVideoTracks().first,
241253
options,
242-
)
254+
),
243255
];
244256

245257
if (stream.getAudioTracks().isNotEmpty) {
246-
tracks.add(LocalAudioTrack(TrackSource.screenShareAudio, stream,
247-
stream.getAudioTracks().first, const AudioCaptureOptions()));
258+
tracks.add(
259+
LocalAudioTrack(
260+
TrackSource.screenShareAudio,
261+
stream,
262+
stream.getAudioTracks().first,
263+
const AudioCaptureOptions(),
264+
),
265+
);
248266
}
249267
return tracks;
250268
}
@@ -262,10 +280,11 @@ extension LocalVideoTrackExt on LocalVideoTrack {
262280
return;
263281
}
264282
final newOptions = CameraCaptureOptions(
265-
cameraPosition: position,
266-
deviceId: null,
267-
maxFrameRate: options.maxFrameRate,
268-
params: options.params);
283+
cameraPosition: position,
284+
deviceId: null,
285+
maxFrameRate: options.maxFrameRate,
286+
params: options.params,
287+
);
269288
await restartTrack(newOptions);
270289
await replaceTrackForMultiCodecSimulcast(mediaStreamTrack);
271290
currentOptions = newOptions;
@@ -284,23 +303,24 @@ extension LocalVideoTrackExt on LocalVideoTrack {
284303
return;
285304
}
286305

287-
await restartTrack(
288-
options.copyWith(deviceId: deviceId),
289-
);
306+
await restartTrack(options.copyWith(deviceId: deviceId));
290307

291308
await replaceTrackForMultiCodecSimulcast(mediaStreamTrack);
292309
}
293310

294311
Future<void> replaceTrackForMultiCodecSimulcast(
295-
rtc.MediaStreamTrack newTrack) async {
312+
rtc.MediaStreamTrack newTrack,
313+
) async {
296314
simulcastCodecs.forEach((key, simulcastTrack) async {
297315
await simulcastTrack.sender?.replaceTrack(newTrack);
298316
simulcastTrack.mediaStreamTrack = mediaStreamTrack;
299317
});
300318
}
301319

302320
Future<List<String>> setPublishingCodecs(
303-
List<lk_rtc.SubscribedCodec> codecs, LocalTrack track) async {
321+
List<lk_rtc.SubscribedCodec> codecs,
322+
LocalTrack track,
323+
) async {
304324
logger.fine('setPublishingCodecs $codecs');
305325

306326
// only enable simulcast codec for preference codec setted
@@ -341,7 +361,9 @@ extension LocalVideoTrackExt on LocalVideoTrack {
341361
}
342362

343363
Future<void> updatePublishingLayers(
344-
LocalTrack? track, List<lk_rtc.SubscribedQuality> layers) async {
364+
LocalTrack? track,
365+
List<lk_rtc.SubscribedQuality> layers,
366+
) async {
345367
logger.fine('Update publishing layers: $layers');
346368

347369
if (track?.sender == null) {
@@ -378,9 +400,10 @@ extension LocalVideoTrackExt on LocalVideoTrack {
378400
}
379401

380402
Future<void> setPublishingLayersForSender(
381-
rtc.RTCRtpSender sender,
382-
List<rtc.RTCRtpEncoding> encodings,
383-
List<lk_rtc.SubscribedQuality> layers) async {
403+
rtc.RTCRtpSender sender,
404+
List<rtc.RTCRtpEncoding> encodings,
405+
List<lk_rtc.SubscribedQuality> layers,
406+
) async {
384407
logger.fine('Update publishing layers: $layers');
385408

386409
final params = sender.parameters;
@@ -436,8 +459,9 @@ extension LocalVideoTrackExt on LocalVideoTrack {
436459
rid = 'q';
437460
}
438461
var quality = _videoQualityForRid(rid);
439-
var subscribedQuality =
440-
layers.firstWhereOrNull((q) => q.quality == quality);
462+
var subscribedQuality = layers.firstWhereOrNull(
463+
(q) => q.quality == quality,
464+
);
441465
if (subscribedQuality == null) {
442466
continue;
443467
}
@@ -452,11 +476,18 @@ extension LocalVideoTrackExt on LocalVideoTrack {
452476
// have a workaround of lowering its bitrate and resolution to the min.
453477
if (kIsWeb && lkBrowser() == BrowserType.firefox) {
454478
if (subscribedQuality.enabled) {
479+
final encodingBackup =
480+
encodingBackups[(sender.senderId, idx)] ?? encoding;
455481
encoding.scaleResolutionDownBy =
456-
encodings[idx].scaleResolutionDownBy;
457-
encoding.maxBitrate = encodings[idx].maxBitrate;
458-
encoding.maxFramerate = encodings[idx].maxBitrate;
482+
encodingBackup.scaleResolutionDownBy;
483+
encoding.maxBitrate = encodingBackup.maxBitrate;
484+
encoding.maxFramerate = encodingBackup.maxFramerate;
459485
} else {
486+
encodingBackups[(sender.senderId, idx)] = rtc.RTCRtpEncoding(
487+
scaleResolutionDownBy: encoding.scaleResolutionDownBy,
488+
maxBitrate: encoding.maxBitrate,
489+
maxFramerate: encoding.maxFramerate,
490+
);
460491
encoding.scaleResolutionDownBy = 4;
461492
encoding.maxBitrate = 10;
462493
encoding.maxFramerate = 2;
@@ -481,7 +512,9 @@ extension LocalVideoTrackExt on LocalVideoTrack {
481512
}
482513

483514
SimulcastTrackInfo addSimulcastTrack(
484-
String codec, List<rtc.RTCRtpEncoding> encodings) {
515+
String codec,
516+
List<rtc.RTCRtpEncoding> encodings,
517+
) {
485518
if (simulcastCodecs[codec] != null) {
486519
throw Exception('$codec already added');
487520
}

0 commit comments

Comments
 (0)