Update param computation (#87)

* import sorter config

* organize video types

* ignore import sort for proto files

* equality for video types

* impl

* adjust defaults

* max
This commit is contained in:
Hiroshi Horie
2022-03-01 10:45:24 +09:00
committed by GitHub
parent 7843e87254
commit 7c16d2f3ec
5 changed files with 112 additions and 27 deletions
+7
View File
@@ -6,6 +6,7 @@ import 'track/options.dart';
import 'track/track.dart';
import 'types/other.dart';
import 'types/video_encoding.dart';
import 'types/video_parameters.dart';
/// Options used when connecting to the server.
class ConnectOptions {
@@ -88,9 +89,15 @@ class VideoPublishOptions {
/// Defaults to true.
final bool simulcast;
final List<VideoParameters> videoSimulcastLayers;
final List<VideoParameters> screenShareSimulcastLayers;
const VideoPublishOptions({
this.videoEncoding,
this.simulcast = true,
this.videoSimulcastLayers = const [],
this.screenShareSimulcastLayers = const [],
});
@override
+3 -3
View File
@@ -23,7 +23,7 @@ class CameraCaptureOptions extends VideoCaptureOptions {
const CameraCaptureOptions({
this.cameraPosition = CameraPosition.front,
VideoParameters params = VideoParametersPresets.h720_169,
VideoParameters params = VideoParametersPresets.h540_169,
}) : super(params: params);
CameraCaptureOptions.from({required VideoCaptureOptions captureOptions})
@@ -51,7 +51,7 @@ class CameraCaptureOptions extends VideoCaptureOptions {
/// Options used when creating a [LocalVideoTrack] that captures the screen.
class ScreenShareCaptureOptions extends VideoCaptureOptions {
const ScreenShareCaptureOptions({
VideoParameters params = VideoParametersPresets.h720_169,
VideoParameters params = VideoParametersPresets.screenShareH720FPS15,
}) : super(params: params);
ScreenShareCaptureOptions.from({required VideoCaptureOptions captureOptions})
@@ -71,7 +71,7 @@ abstract class VideoCaptureOptions extends LocalTrackOptions {
final VideoParameters params;
const VideoCaptureOptions({
this.params = VideoParametersPresets.h720_169,
this.params = VideoParametersPresets.h540_169,
});
@override
+6
View File
@@ -40,6 +40,12 @@ class VideoDimensions {
}
extension VideoDimensionsHelpers on VideoDimensions {
// aspect ratios
static const aspect169 = 16.0 / 9.0;
static const aspect43 = 4.0 / 3.0;
double aspect() => width > height ? width / height : height / width;
/// Returns the larger value
int max() => math.max(width, height);
+12
View File
@@ -56,6 +56,18 @@ class VideoParameters implements Comparable<VideoParameters> {
}
extension VideoParametersPresets on VideoParameters {
// 16:9 default
static final List<VideoParameters> defaultSimulcast169 = [
h180_169,
h360_169,
];
// 4:3 default
static final List<VideoParameters> defaultSimulcast43 = [
h180_43,
h360_43,
];
// all 16:9 presets
static final List<VideoParameters> all169 = [
h90_169,
+84 -24
View File
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
@@ -34,6 +35,9 @@ typedef RetryCondition = bool Function(
// Collection of state-less static methods
class Utils {
// order of rids
static final videoRids = ['q', 'h', 'f'];
/// Returns a [Future] that will retry [future] while it throws
/// for a maximum of [tries] times with [delay] in between.
/// If all the attempts throws, the future will throw a [List] of the
@@ -191,17 +195,62 @@ class Utils {
required bool isScreenShare,
required VideoDimensions dimensions,
}) {
if (isScreenShare) return VideoParametersPresets.allScreenShare;
if (isScreenShare) {
return VideoParametersPresets.allScreenShare;
}
final double aspect = dimensions.width > dimensions.height
? dimensions.width / dimensions.height
: dimensions.height / dimensions.width;
if ((aspect - 16.0 / 9.0).abs() < (aspect - 4.0 / 3.0).abs()) {
final a = dimensions.aspect();
if ((a - VideoDimensionsHelpers.aspect169).abs() <
(a - VideoDimensionsHelpers.aspect43).abs()) {
return VideoParametersPresets.all169;
}
return VideoParametersPresets.all43;
}
static List<VideoParameters> _computeDefaultScreenShareSimulcastParams({
required VideoParameters original,
}) {
final layers = [
rtc.RTCRtpEncoding(scaleResolutionDownBy: 2, maxFramerate: 3)
];
return layers.map((e) {
final scale = e.scaleResolutionDownBy ?? 1;
final fps = e.maxFramerate ?? 3;
return VideoParameters(
dimensions: VideoDimensions((original.dimensions.width / scale).floor(),
(original.dimensions.height / scale).floor()),
encoding: VideoEncoding(
maxBitrate: math.max(
150 * 1000,
(original.encoding.maxBitrate /
(math.pow(scale, 2) *
(original.encoding.maxFramerate / fps)))
.floor(),
),
maxFramerate: fps,
),
);
}).toList();
}
static List<VideoParameters> _computeDefaultSimulcastParams({
required bool isScreenShare,
required VideoParameters original,
}) {
if (isScreenShare) {
return _computeDefaultScreenShareSimulcastParams(original: original);
}
final a = original.dimensions.aspect();
if ((a - VideoDimensionsHelpers.aspect169).abs() <
(a - VideoDimensionsHelpers.aspect43).abs()) {
return VideoParametersPresets.defaultSimulcast169;
}
return VideoParametersPresets.defaultSimulcast43;
}
static VideoEncoding _findAppropriateEncoding({
required bool isScreenShare,
required VideoDimensions dimensions,
@@ -221,8 +270,6 @@ class Utils {
return result;
}
static final videoRids = ['q', 'h', 'f'];
@internal
static List<rtc.RTCRtpEncoding> encodingsFromPresets(
VideoDimensions dimensions, {
@@ -233,11 +280,12 @@ class Utils {
if (i >= videoRids.length) {
return;
}
final size = dimensions.min();
final rid = videoRids[i];
result.add(e.encoding.toRTCRtpEncoding(
rid: rid,
scaleResolutionDownBy: findEvenScaleDownBy(dimensions, e.dimensions),
scaleResolutionDownBy: math.max(1, size / e.dimensions.min()),
));
});
return result;
@@ -279,12 +327,10 @@ class Utils {
VideoEncoding? videoEncoding = options.videoEncoding;
final useSimulcast = !isScreenShare && options.simulcast;
if ((videoEncoding == null && !useSimulcast) || dimensions == null) {
if ((videoEncoding == null && !options.simulcast) || dimensions == null) {
// don't set encoding when we are not simulcasting and user isn't restricting
// encoding parameters
return null;
return [rtc.RTCRtpEncoding()];
}
final presets = _presetsForDimensions(
@@ -299,34 +345,48 @@ class Utils {
dimensions: dimensions,
presets: presets,
);
logger.fine('using video encoding', videoEncoding);
}
// Not simulcast
if (!useSimulcast) return [videoEncoding.toRTCRtpEncoding()];
final VideoParameters lowPreset = presets.first;
VideoParameters? midPreset;
if (presets.length > 1) {
midPreset = presets[1];
if (!options.simulcast) {
// not using simulcast
return [videoEncoding.toRTCRtpEncoding()];
}
final original = VideoParameters(
dimensions: dimensions,
encoding: videoEncoding,
);
final userParams = isScreenShare
? options.screenShareSimulcastLayers
: options.videoSimulcastLayers;
final params = (userParams.isNotEmpty
? userParams
: _computeDefaultSimulcastParams(
isScreenShare: isScreenShare, original: original))
.sorted();
final VideoParameters lowPreset = params.first;
VideoParameters? midPreset;
if (params.length > 1) {
midPreset = params[1];
}
final size = dimensions.max();
List<VideoParameters> computedPresets = [original];
List<VideoParameters> computedParams = [original];
if (size >= 960 && midPreset != null) {
computedPresets = [lowPreset, midPreset, original];
} else if (size >= 500) {
computedPresets = [lowPreset, original];
computedParams = [lowPreset, midPreset, original];
} else if (size >= 480) {
computedParams = [lowPreset, original];
}
return encodingsFromPresets(
dimensions,
presets: computedPresets,
presets: computedParams,
);
}