Files
client-sdk-flutter/lib/src/utils.dart
T
Hiroshi Horie f4bd82a919 Subscriber as primary (#8)
* Update protos

* Signal client

* Improve transport

* First implementation

* Fix publish

* Update `PCTransport` for debounce negotiation

* debounce func

* reconnect & events

* Update debounce func

* engine events

* make sure events don't emit after dispose

* prefix flutter_webrtc

* Cleaner `iceServers` update

* don't mutate user provided params

* fix tests

* event manager

* Fix: `Room.onDisconnected` gets fired multiple times

* data publish in example

* cleaner logic

* safer events

* un-prefix with LK

* organize imports

* Clean up

* remove `Tuple`

* `EventsEmitter` can now be directly listened to

`EventsEmitter` extends `EventsListenable`
2021-09-18 00:10:16 +09:00

152 lines
4.0 KiB
Dart

//
//
//
import 'dart:async';
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
import 'options.dart';
import 'track/options.dart';
enum ProtocolVersion {
protocol2,
protocol3,
}
extension ProtocolVersionExt on ProtocolVersion {
String toStringValue() => {
ProtocolVersion.protocol2: '2',
ProtocolVersion.protocol3: '3',
}[this]!;
}
extension UriExt on Uri {
bool get isSecureScheme => ['https', 'wss'].contains(scheme);
}
// Collection of state-less static methods
class Utils {
static Uri buildUri(
String uriString, {
required String token,
ConnectOptions? options,
bool reconnect = false,
bool validate = false,
bool forceSecure = false,
required ProtocolVersion protocol,
}) {
final Uri uri = Uri.parse(uriString);
final useSecure = uri.isSecureScheme || forceSecure;
final httpScheme = useSecure ? 'https' : 'http';
final wsScheme = useSecure ? 'wss' : 'ws';
return uri.replace(
scheme: validate ? httpScheme : wsScheme,
path: validate ? 'validate' : 'rtc',
queryParameters: <String, String>{
'access_token': token,
if (options != null) 'auto_subscribe': options.autoSubscribe ? '1' : '0',
if (reconnect) 'reconnect': '1',
'protocol': protocol.toStringValue(),
},
);
}
static List<VideoParameters> _presetsForResolution(
int width,
int height,
) {
final double aspect = width / height;
if ((aspect - 16.0 / 9.0).abs() < (aspect - 4.0 / 3.0).abs()) return VideoParameters.presets169;
return VideoParameters.presets43;
}
static VideoParameters _findPresetForResolution(
int width,
int height, {
required List<VideoParameters> presets,
}) {
assert(presets.isNotEmpty, 'presets should not be empty');
VideoParameters result = presets.first;
for (final preset in presets) {
if (width >= preset.width && height >= preset.height) result = preset;
}
return result;
}
static List<rtc.RTCRtpEncoding>? computeVideoEncodings({
int? width,
int? height,
TrackPublishOptions? options,
}) {
options ??= const TrackPublishOptions();
VideoEncoding? videoEncoding = options.videoEncoding;
if ((videoEncoding == null && !options.simulcast) || width == null || height == null) {
// don't set encoding when we are not simulcasting and user isn't restricting
// encoding parameters
return null;
}
final presets = _presetsForResolution(width, height);
if (videoEncoding == null) {
// find the right encoding based on width/height
final preset = _findPresetForResolution(width, height, presets: presets);
// print('Using preset: ${preset.id}');
videoEncoding = preset.encoding;
// log.debug('using video encoding', videoEncoding);
}
// Not simulcast
if (!options.simulcast) return [videoEncoding.toRTCRtpEncoding()];
// Compute for simulcast
final midPreset = presets[1];
final lowPreset = presets[0];
return [
videoEncoding.toRTCRtpEncoding(
rid: 'f',
),
// if resolution is high enough, we would send both h and q res..
// otherwise only send h
if (width >= 960) ...[
midPreset.encoding.toRTCRtpEncoding(
rid: 'h',
scaleResolutionDownBy: height / midPreset.height,
),
lowPreset.encoding.toRTCRtpEncoding(
rid: 'q',
scaleResolutionDownBy: height / lowPreset.height,
),
] else
lowPreset.encoding.toRTCRtpEncoding(
rid: 'h',
scaleResolutionDownBy: height / lowPreset.height,
),
];
}
// makes a debounce func
static Function createDebounceFunc(
Function f, {
Function(Function)? cancelFunc,
required Duration wait,
}) {
Timer? t;
return () {
t?.cancel();
t = Timer(wait, () {
t = null;
f();
});
// pass back the cancel method so we can cancel it when no longer needed
cancelFunc?.call(t!.cancel);
};
}
}