Files
client-sdk-flutter/lib/src/track/local.dart
T
2021-12-11 18:26:58 +07:00

135 lines
3.8 KiB
Dart

import 'dart:io' show Platform;
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
import 'package:meta/meta.dart';
import '../exceptions.dart';
import '../internal/events.dart';
import '../logger.dart';
import '../proto/livekit_models.pb.dart' as lk_models;
import '../types.dart';
import 'options.dart';
import 'track.dart';
mixin VideoTrack on Track {}
mixin AudioTrack on Track {}
abstract class LocalTrack extends Track {
// Options used for this track
abstract LocalTrackOptions currentOptions;
LocalTrack(
String name,
lk_models.TrackType kind,
TrackSource source,
rtc.MediaStream mediaStream,
rtc.MediaStreamTrack mediaStreamTrack,
) : super(
name,
kind,
source,
mediaStream,
mediaStreamTrack,
);
// Only local tracks can set muted.
// Returns true if muted, false if unchanged.
Future<bool> mute() async {
logger.fine('LocalTrack.mute() muted: $muted');
if (muted) return false; // already muted
await disable();
if (!Platform.isWindows) {
await stop();
}
updateMuted(true);
events.emit(TrackMuteUpdatedEvent(track: this, muted: muted));
return true;
}
// Returns true if unmuted, false if unchanged.
Future<bool> unmute() async {
logger.fine('LocalTrack.unmute() muted: $muted');
if (!muted) return false; // already un-muted
if (!Platform.isWindows) {
await restartTrack();
}
await enable();
updateMuted(false);
events.emit(TrackMuteUpdatedEvent(track: this, muted: muted));
return true;
}
@override
Future<bool> stop() async {
final didStop = await super.stop();
if (didStop) {
logger.fine('Stopping mediaStreamTrack...');
await mediaStreamTrack.stop();
await mediaStream.dispose();
}
return didStop;
}
/// Creates a [rtc.MediaStream] from LocalTrackOptions.
@internal
static Future<rtc.MediaStream> createStream(
LocalTrackOptions options,
) async {
final constraints = <String, dynamic>{
'audio': options is LocalAudioTrackOptions
? options.toMediaConstraintsMap()
: false,
'video': options is LocalVideoTrackOptions
? options.toMediaConstraintsMap()
: false,
};
final rtc.MediaStream stream;
if (options is ScreenShareTrackOptions) {
stream = await rtc.navigator.mediaDevices.getDisplayMedia(constraints);
} else {
// options is CameraVideoTrackOptions
stream = await rtc.navigator.mediaDevices.getUserMedia(constraints);
}
// Check if the stream looks good
if ((options is LocalVideoTrackOptions &&
stream.getVideoTracks().isEmpty) ||
(options is LocalAudioTrackOptions &&
stream.getAudioTracks().isEmpty)) {
throw TrackCreateException(
'Failed to create stream, at least 1 video or audio track should exist');
}
return stream;
}
/// Restarts the track with new options. This is useful when switching between
/// front and back cameras.
Future<void> restartTrack([
LocalTrackOptions? options,
]) async {
if (sender == null) throw TrackCreateException('could not restart track');
if (options != null && currentOptions.runtimeType != options.runtimeType) {
throw Exception('options must be a ${currentOptions.runtimeType}');
}
currentOptions = options ?? currentOptions;
// stop if not already stopped...
await stop();
// create new track with options
final newStream = await LocalTrack.createStream(currentOptions);
final newTrack = newStream.getTracks().first;
// replace track on sender
await sender?.replaceTrack(newTrack);
// set new stream & track to this object
updateMediaStreamAndTrack(newStream, newTrack);
// mark as started
await start();
}
}