Simulcast, Screen sharing & Various improvements (#4)
* Respect `RTCIceTransportPolicy` enum and organize * Simplify syntax where possible etc. * Combine `VideoPreset` and `VideoPresets` * Default values for `ConnectOptions` * Build URI instead of String manipulation * Slight modifications to Exception * `LiveKitTheme` for example * `VideoEncoding` class * Organize imports * First simulcast implementation * Remove unnecessary try-catches * Update Android settings * Remember uri and token * example improvements * `fit` parameter for VideoTrackRenderer * Simulcast option for example * Pass `defaultPublishOptions` * Show only `VideoQuality` * Pass tests * Better buildUri logic * Named parameter to positional * Explicit imports * `VideoParameter` instead of `VideoPreset` * Use `mediaTrack.getSettings` when possible * Safer dispose logic * Safer `PCTransport` Update transport.dart * Synchronized events for `SignalClient` * Use logger instead of print * Make example compile for iOS * First screen share implementation * Make example work with screen share * Example improvement * Code optimization * Don't depend on web_socket_channel * Fix: Unpublish track bug * Show participant mute state & identity * Update protos * Remote mute/unmute * iOS Background mode * Separate `createCameraTrack` and `createScreenTrack` * Clean up * PB fix * format * Fix analyzer warning * Android clean up * Update README.md * Clean up
This commit is contained in:
@@ -1,66 +1,113 @@
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||
|
||||
import '../errors.dart';
|
||||
import '../logger.dart';
|
||||
import 'options.dart';
|
||||
import 'track.dart';
|
||||
import 'video_track.dart';
|
||||
|
||||
/// A video track from the local device. Use static methods in this class to create
|
||||
/// video tracks.
|
||||
class LocalVideoTrack extends VideoTrack {
|
||||
//
|
||||
// Options used for this track
|
||||
//
|
||||
LocalVideoTrackOptions currentOptions;
|
||||
|
||||
//
|
||||
// Private constructor
|
||||
//
|
||||
LocalVideoTrack._(
|
||||
String name,
|
||||
MediaStreamTrack mediaTrack,
|
||||
MediaStream stream,
|
||||
this.currentOptions,
|
||||
) : super(name, mediaTrack, stream);
|
||||
|
||||
RTCRtpSender? get sender => transceiver?.sender;
|
||||
|
||||
LocalVideoTrack(String name, MediaStreamTrack mediaTrack, MediaStream stream)
|
||||
: super(name, mediaTrack, stream);
|
||||
|
||||
/// Creates a LocalVideoTrack from camera input.
|
||||
static Future<LocalVideoTrack> createCameraTrack([LocalVideoTrackOptions? options]) async {
|
||||
options ??= LocalVideoTrackOptions(params: VideoPresets.qhd);
|
||||
|
||||
try {
|
||||
final stream = await _createCameraStream(options);
|
||||
return LocalVideoTrack('camera', stream.getVideoTracks().first, stream);
|
||||
} catch (e) {
|
||||
return Future.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// Restarts the track with new options. This is useful when switching between
|
||||
/// front and back cameras.
|
||||
Future<void> restartTrack([LocalVideoTrackOptions? options]) async {
|
||||
if (sender == null) {
|
||||
return Future.error(TrackCreateError('could not restart track'));
|
||||
Future<void> restartTrack([
|
||||
LocalVideoTrackOptions? options,
|
||||
]) async {
|
||||
if (sender == null) throw TrackCreateError('could not restart track');
|
||||
if (options != null && currentOptions.runtimeType != options.runtimeType) {
|
||||
throw Exception('options must be a ${currentOptions.runtimeType}');
|
||||
}
|
||||
|
||||
options ??= LocalVideoTrackOptions(params: VideoPresets.qhd);
|
||||
currentOptions = options ?? currentOptions;
|
||||
|
||||
try {
|
||||
final stream = await _createCameraStream(options);
|
||||
final track = stream.getVideoTracks().first;
|
||||
mediaStream = stream;
|
||||
await mediaTrack.stop();
|
||||
mediaTrack = track;
|
||||
await sender?.replaceTrack(track);
|
||||
} catch (e) {
|
||||
return Future.error(e);
|
||||
}
|
||||
final stream = await _createStream(currentOptions);
|
||||
final track = stream.getVideoTracks().first;
|
||||
setMediaStream(stream);
|
||||
await mediaStreamTrack.stop();
|
||||
mediaStreamTrack = track;
|
||||
await sender?.replaceTrack(track);
|
||||
}
|
||||
|
||||
static Future<MediaStream> _createCameraStream(LocalVideoTrackOptions? options) async {
|
||||
options ??= LocalVideoTrackOptions(params: VideoPresets.qhd);
|
||||
/// Creates a LocalVideoTrack from camera input.
|
||||
static Future<LocalVideoTrack> createCameraTrack([
|
||||
CameraTrackOptions? options,
|
||||
]) async {
|
||||
options ??= const CameraTrackOptions();
|
||||
final stream = await _createStream(options);
|
||||
return LocalVideoTrack._(
|
||||
Track.cameraName,
|
||||
stream.getVideoTracks().first,
|
||||
stream,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final stream = await navigator.mediaDevices.getUserMedia(<String, dynamic>{
|
||||
'audio': false,
|
||||
'video': options.mediaConstraints,
|
||||
});
|
||||
static Future<LocalVideoTrack> createScreenTrack([
|
||||
ScreenTrackOptions? options,
|
||||
]) async {
|
||||
options ??= const ScreenTrackOptions();
|
||||
final stream = await _createStream(options);
|
||||
return LocalVideoTrack._(
|
||||
Track.screenShareName,
|
||||
stream.getVideoTracks().first,
|
||||
stream,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
if (stream.getVideoTracks().isEmpty) {
|
||||
return Future.error(TrackCreateError());
|
||||
}
|
||||
static Future<MediaStream> _createStream(
|
||||
LocalVideoTrackOptions options,
|
||||
) async {
|
||||
final constraints = <String, dynamic>{
|
||||
'audio': false,
|
||||
'video': options.toMediaConstraintsMap(),
|
||||
};
|
||||
|
||||
return stream;
|
||||
} catch (e) {
|
||||
return Future.error(e);
|
||||
final MediaStream stream;
|
||||
if (options is ScreenTrackOptions) {
|
||||
stream = await navigator.mediaDevices.getDisplayMedia(constraints);
|
||||
} else {
|
||||
// options is CameraVideoTrackOptions
|
||||
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
}
|
||||
|
||||
if (stream.getVideoTracks().isEmpty) throw TrackCreateError();
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Convenience extensions
|
||||
//
|
||||
extension LKLocalVideoTrackExt on LocalVideoTrack {
|
||||
// Calls restartTrack under the hood
|
||||
Future<void> setCameraPosition(CameraPosition position) async {
|
||||
final options = currentOptions;
|
||||
if (options is! CameraTrackOptions) {
|
||||
logger.warning('Not a camera track');
|
||||
return;
|
||||
}
|
||||
|
||||
await restartTrack(
|
||||
options.copyWith(cameraPosition: position),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user