Skip to content

Commit 1ca4eca

Browse files
committed
Merge main into adaptive stream pixel density
2 parents 6c55e59 + ead71ca commit 1ca4eca

10 files changed

Lines changed: 685 additions & 48 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patch type="fixed" "Align screen share simulcast default low layer"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patch type="fixed" "Clamp simulcast lower layers to the top layer"

.github/workflows/build.yaml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ on:
2424

2525
jobs:
2626
dart-format-check:
27-
name: Dart Format Check
27+
name: Dart Format
2828
runs-on: ubuntu-latest
2929

3030
steps:
@@ -35,7 +35,7 @@ jobs:
3535
run: dart format . --set-exit-if-changed
3636

3737
import-sorter-check:
38-
name: Import Sorter Check
38+
name: Import Sorter
3939
runs-on: ubuntu-latest
4040

4141
steps:
@@ -46,7 +46,7 @@ jobs:
4646
run: dart run import_sorter:main --no-comments --exit-if-changed
4747

4848
version-consistency-check:
49-
name: Version Consistency Check
49+
name: Version Consistency
5050
runs-on: ubuntu-latest
5151

5252
steps:
@@ -57,7 +57,7 @@ jobs:
5757
run: dart run scripts/check_version.dart
5858

5959
dart-analyze-check:
60-
name: Dart Analyze Check
60+
name: Dart Analyze
6161
runs-on: ubuntu-latest
6262

6363
steps:
@@ -68,7 +68,7 @@ jobs:
6868
run: flutter analyze
6969

7070
dart-test-check:
71-
name: Dart Test Check
71+
name: Dart Test
7272
runs-on: ubuntu-latest
7373

7474
steps:
@@ -79,7 +79,7 @@ jobs:
7979
run: flutter test
8080

8181
build-for-android:
82-
name: Build for Flutter Android
82+
name: Android
8383
runs-on: ubuntu-latest
8484

8585
steps:
@@ -93,7 +93,7 @@ jobs:
9393
run: flutter build apk
9494

9595
build-for-ios:
96-
name: Build for Flutter iOS
96+
name: iOS
9797
runs-on: macos-latest
9898

9999
steps:
@@ -112,7 +112,7 @@ jobs:
112112
run: flutter build ios --release --no-codesign
113113

114114
build-for-windows:
115-
name: Build for Flutter Windows
115+
name: Windows
116116
runs-on: windows-latest
117117

118118
steps:
@@ -124,7 +124,7 @@ jobs:
124124
run: flutter build windows --release
125125

126126
build-for-macos:
127-
name: Build for Flutter macOS
127+
name: macOS
128128
runs-on: macos-latest
129129

130130
steps:
@@ -136,7 +136,7 @@ jobs:
136136
run: flutter build macos --release
137137

138138
build-for-linux:
139-
name: Build for Flutter Linux
139+
name: Linux
140140
runs-on: ubuntu-latest
141141

142142
steps:
@@ -154,7 +154,7 @@ jobs:
154154
run: flutter build linux
155155

156156
build-for-web:
157-
name: Build for Flutter Web
157+
name: Web
158158
runs-on: ubuntu-latest
159159

160160
steps:
@@ -168,7 +168,7 @@ jobs:
168168
run: flutter build web
169169

170170
build-for-web-wasm:
171-
name: Build for Flutter Web WASM
171+
name: Web WASM
172172
runs-on: ubuntu-latest
173173

174174
steps:

example/windows/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ add_subdirectory("runner")
5050
# them to the application.
5151
include(flutter/generated_plugins.cmake)
5252

53+
# VS 2026 treats C++/WinRT's experimental coroutine include as a hard error.
54+
if(TARGET permission_handler_windows_plugin)
55+
target_compile_definitions(permission_handler_windows_plugin PRIVATE
56+
_SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS)
57+
endif()
5358

5459
# === Installation ===
5560
# Support files are copied into place next to the executable, so that it can

lib/src/publication/remote.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ class RemoteTrackPublication<T extends RemoteTrack> extends TrackPublication<T>
4242
@override
4343
final RemoteParticipant participant;
4444

45-
bool get enabled => _enabledPreference != TrackEnabledPreference.disabled;
45+
bool get enabled => !resolveDisabled(
46+
enabledPreference: _enabledPreference,
47+
adaptiveStreamActive: _adaptiveStreamActive,
48+
adaptiveStreamVisible: _adaptiveStreamVisible,
49+
);
4650

4751
/// The user's explicit enable/disable request via [enable] / [disable].
4852
/// [TrackEnabledPreference.unset] means no explicit request, in which case
@@ -228,6 +232,14 @@ class RemoteTrackPublication<T extends RemoteTrack> extends TrackPublication<T>
228232
_cancelPendingTrackSettingsUpdateRequest?.call();
229233
_visibilityTimer?.cancel();
230234

235+
// The track changed, so any adaptive-stream visibility computed for the
236+
// previous track is stale. Reset to the construction defaults so it can't
237+
// leak into a later _buildTrackSettings (e.g. via enable() / disable(),
238+
// which emit regardless of visibility). Repopulated by the visibility
239+
// observer below while adaptive stream is active.
240+
_adaptiveStreamDimensions = null;
241+
_adaptiveStreamVisible = true;
242+
231243
final roomOptions = participant.room.roomOptions;
232244
if (roomOptions.adaptiveStream && newValue is RemoteVideoTrack) {
233245
_adaptiveStreamActive = true;
@@ -245,6 +257,8 @@ class RemoteTrackPublication<T extends RemoteTrack> extends TrackPublication<T>
245257
_computeVideoViewVisibility(quick: true);
246258
}
247259
};
260+
261+
_computeVideoViewVisibility(quick: true);
248262
} else {
249263
_adaptiveStreamActive = false;
250264
}

lib/src/publication/track_settings.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ VideoSettings resolveVideoSettings({
5555
minDimensions = adaptiveStreamDimensions;
5656
}
5757
} else if (userPreference?.quality != null) {
58-
// Compare adaptive dimensions with the max quality layer dimensions
59-
final maxQualityLayer = layerDimensionsForQuality?.call(userPreference!.quality!);
60-
if (maxQualityLayer != null && adaptiveStreamDimensions.area() < maxQualityLayer.area()) {
58+
// Compare adaptive dimensions with the dimensions implied by the requested quality.
59+
final requestedQualityLayerDimensions = layerDimensionsForQuality?.call(userPreference!.quality!);
60+
if (requestedQualityLayerDimensions != null &&
61+
adaptiveStreamDimensions.area() < requestedQualityLayerDimensions.area()) {
6162
minDimensions = adaptiveStreamDimensions;
6263
}
6364
} else {

lib/src/types/video_parameters.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ extension VideoParametersPresets on VideoParameters {
274274
static const screenShareH720FPS5 = VideoParameters(
275275
dimensions: VideoDimensionsPresets.h720_169,
276276
encoding: VideoEncoding(
277-
maxBitrate: 400 * 1000,
277+
maxBitrate: 800 * 1000,
278278
maxFramerate: 5,
279279
),
280280
);
@@ -298,7 +298,7 @@ extension VideoParametersPresets on VideoParameters {
298298
static const screenShareH1080FPS30 = VideoParameters(
299299
dimensions: VideoDimensionsPresets.h1080_169,
300300
encoding: VideoEncoding(
301-
maxBitrate: 4000 * 1000,
301+
maxBitrate: 5000 * 1000,
302302
maxFramerate: 30,
303303
),
304304
);

lib/src/utils.dart

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -229,23 +229,24 @@ class Utils {
229229
static List<VideoParameters> _computeDefaultScreenShareSimulcastParams({
230230
required VideoParameters original,
231231
}) {
232-
final layers = [rtc.RTCRtpEncoding(scaleResolutionDownBy: 2, maxFramerate: 3)];
233-
return layers.map((e) {
234-
final scale = e.scaleResolutionDownBy ?? 1;
235-
final fps = e.maxFramerate ?? 3;
232+
final originalEncoding = original.encoding!;
233+
const scale = 2.0;
236234

237-
return VideoParameters(
235+
return [
236+
VideoParameters(
238237
dimensions:
239238
VideoDimensions((original.dimensions.width / scale).floor(), (original.dimensions.height / scale).floor()),
240239
encoding: VideoEncoding(
241240
maxBitrate: math.max(
242241
150 * 1000,
243-
(original.encoding!.maxBitrate / (math.pow(scale, 2) * (original.encoding!.maxFramerate / fps))).floor(),
242+
(originalEncoding.maxBitrate / math.pow(scale, 2)).floor(),
244243
),
245-
maxFramerate: fps,
244+
maxFramerate: originalEncoding.maxFramerate,
245+
bitratePriority: originalEncoding.bitratePriority,
246+
networkPriority: originalEncoding.networkPriority,
246247
),
247-
);
248-
}).toList();
248+
),
249+
];
249250
}
250251

251252
static List<VideoParameters> _computeDefaultSimulcastParams({
@@ -311,18 +312,83 @@ class Utils {
311312
if (i >= videoRids.length) {
312313
return;
313314
}
314-
final size = dimensions.min();
315+
final size = dimensions.max();
315316
final rid = videoRids[i];
316317
if (e.encoding != null) {
317318
result.add(e.encoding!.toRTCRtpEncoding(
318319
rid: rid,
319-
scaleResolutionDownBy: math.max(1, size / e.dimensions.min()),
320+
scaleResolutionDownBy: math.max(1, size / e.dimensions.max()),
320321
));
321322
}
322323
});
323324
return result;
324325
}
325326

327+
@internal
328+
static List<VideoParameters> computeSimulcastPresets({
329+
required VideoDimensions dimensions,
330+
required VideoParameters original,
331+
required List<VideoParameters> requestedPresets,
332+
required bool isScreenShare,
333+
}) {
334+
final params = (requestedPresets.isNotEmpty
335+
? requestedPresets
336+
: _computeDefaultSimulcastParams(isScreenShare: isScreenShare, original: original))
337+
.sorted();
338+
339+
if (params.isEmpty) {
340+
return [original];
341+
}
342+
final lowPreset = params.first;
343+
final midPreset = params.length > 1 ? params[1] : null;
344+
345+
final size = dimensions.max();
346+
if (size >= 960 && midPreset != null) {
347+
return [
348+
_clampSimulcastPreset(lowPreset, to: original, inDimensions: dimensions),
349+
_clampSimulcastPreset(midPreset, to: original, inDimensions: dimensions),
350+
original,
351+
];
352+
}
353+
if (size >= 480) {
354+
return [
355+
_clampSimulcastPreset(lowPreset, to: original, inDimensions: dimensions),
356+
original,
357+
];
358+
}
359+
return [original];
360+
}
361+
362+
static VideoParameters _clampSimulcastPreset(
363+
VideoParameters preset, {
364+
required VideoParameters to,
365+
required VideoDimensions inDimensions,
366+
}) {
367+
final presetEncoding = preset.encoding;
368+
final topEncoding = to.encoding;
369+
if (presetEncoding == null || topEncoding == null) {
370+
return preset;
371+
}
372+
373+
final rawScaleDownBy = inDimensions.max() / preset.dimensions.max();
374+
final clampedFramerate = math.min(presetEncoding.maxFramerate, topEncoding.maxFramerate);
375+
final clampedBitrate =
376+
rawScaleDownBy <= 1.0 ? math.min(presetEncoding.maxBitrate, topEncoding.maxBitrate) : presetEncoding.maxBitrate;
377+
378+
if (clampedFramerate == presetEncoding.maxFramerate && clampedBitrate == presetEncoding.maxBitrate) {
379+
return preset;
380+
}
381+
382+
return VideoParameters(
383+
description: preset.description,
384+
dimensions: preset.dimensions,
385+
encoding: presetEncoding.copyWith(
386+
maxFramerate: clampedFramerate,
387+
maxBitrate: clampedBitrate,
388+
),
389+
);
390+
}
391+
326392
@internal
327393
static FutureOr<String> getNetworkType() async {
328394
if (!kIsWeb && lkPlatformIsTest()) {
@@ -450,25 +516,12 @@ class Utils {
450516
// compute simulcast encodings
451517
final userParams = isScreenShare ? options.screenShareSimulcastLayers : options.videoSimulcastLayers;
452518

453-
final params = (userParams.isNotEmpty
454-
? userParams
455-
: _computeDefaultSimulcastParams(isScreenShare: isScreenShare, original: original))
456-
.sorted();
457-
458-
final VideoParameters lowPreset = params.first;
459-
VideoParameters? midPreset;
460-
if (params.length > 1) {
461-
midPreset = params[1];
462-
}
463-
464-
final size = dimensions.max();
465-
List<VideoParameters> computedParams = [original];
466-
467-
if (size >= 960 && midPreset != null) {
468-
computedParams = [lowPreset, midPreset, original];
469-
} else if (size >= 480) {
470-
computedParams = [lowPreset, original];
471-
}
519+
final computedParams = computeSimulcastPresets(
520+
dimensions: dimensions,
521+
original: original,
522+
requestedPresets: userParams,
523+
isScreenShare: isScreenShare,
524+
);
472525

473526
return encodingsFromPresets(
474527
dimensions,

0 commit comments

Comments
 (0)