diff --git a/example/lib/widgets/controls.dart b/example/lib/widgets/controls.dart index f0af946..68aa62a 100644 --- a/example/lib/widgets/controls.dart +++ b/example/lib/widgets/controls.dart @@ -104,10 +104,9 @@ class _ControlsWidgetState extends State { await FlutterBackground.initialize(androidConfig: androidConfig); await FlutterBackground.enableBackgroundExecution(); - final screenTrack = - await LocalVideoTrack.createScreenTrack(); // Defaults to camera + final screenShareTrack = await LocalVideoTrack.createScreenShareTrack(); await widget.room.localParticipant.publishVideoTrack( - screenTrack, + screenShareTrack, ); } catch (e) { print('could not publish video: $e'); diff --git a/lib/livekit_client.dart b/lib/livekit_client.dart index 995ef18..caf4684 100644 --- a/lib/livekit_client.dart +++ b/lib/livekit_client.dart @@ -16,10 +16,10 @@ export 'src/room.dart'; export 'src/track/local_audio_track.dart'; export 'src/track/local_track_publication.dart'; export 'src/track/local_video_track.dart'; +export 'src/track/local_track.dart'; export 'src/track/options.dart'; export 'src/track/remote_track_publication.dart'; export 'src/track/track.dart'; export 'src/track/track_publication.dart'; -export 'src/track/video_track.dart'; export 'src/types.dart'; export 'src/widget/video_track_renderer.dart'; diff --git a/lib/src/participant/local_participant.dart b/lib/src/participant/local_participant.dart index ea1d0f2..f1caca4 100644 --- a/lib/src/participant/local_participant.dart +++ b/lib/src/participant/local_participant.dart @@ -279,7 +279,7 @@ extension LocalParticipantTrackSourceExt on LocalParticipant { final track = await LocalAudioTrack.create(); await publishAudioTrack(track); } else if (source == TrackSource.screenShareVideo) { - final track = await LocalVideoTrack.createScreenTrack(); + final track = await LocalVideoTrack.createScreenShareTrack(); await publishVideoTrack(track); } } diff --git a/lib/src/participant/remote_participant.dart b/lib/src/participant/remote_participant.dart index fb7ae85..6e2bac4 100644 --- a/lib/src/participant/remote_participant.dart +++ b/lib/src/participant/remote_participant.dart @@ -104,10 +104,10 @@ class RemoteParticipant extends Participant { final Track track; if (pub.kind == lk_models.TrackType.AUDIO) { // audio track - track = RemoteAudioTrack(pub.source, pub.name, mediaTrack, stream); + track = RemoteAudioTrack(pub.name, pub.source, stream, mediaTrack); } else { // video track - track = RemoteVideoTrack(pub.source, pub.name, mediaTrack, stream); + track = RemoteVideoTrack(pub.name, pub.source, stream, mediaTrack); } await track.start(); diff --git a/lib/src/track/audio_track.dart b/lib/src/track/audio_management.dart similarity index 83% rename from lib/src/track/audio_track.dart rename to lib/src/track/audio_management.dart index 31a09bc..4ca5bcc 100644 --- a/lib/src/track/audio_track.dart +++ b/lib/src/track/audio_management.dart @@ -1,16 +1,14 @@ // import 'package:audio_session/audio_session.dart' as _as; + import 'dart:io'; import 'package:flutter/foundation.dart'; -import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; import 'package:synchronized/synchronized.dart' as sync; import '../logger.dart'; -import '../proto/livekit_models.pb.dart' as lk_models; import '../support/native_audio.dart'; -import '../types.dart'; import 'local_audio_track.dart'; -import 'track.dart'; +import 'local_track.dart'; enum AudioTrackState { none, @@ -22,7 +20,7 @@ enum AudioTrackState { typedef ConfigureNativeAudioFunc = Future Function( AudioTrackState state); -abstract class AudioTrack extends Track { +mixin AudioManagementMixin on AudioTrack { // it's possible to set custom function here to customize audio session configuration static ConfigureNativeAudioFunc nativeAudioConfigurationForAudioTrackState = defaultNativeAudioConfigurationFunc; @@ -32,20 +30,6 @@ abstract class AudioTrack extends Track { static int _localTrackCount = 0; static int _remoteTrackCount = 0; - rtc.MediaStream? mediaStream; - - AudioTrack( - TrackSource source, - String name, - rtc.MediaStreamTrack track, - this.mediaStream, - ) : super( - lk_models.TrackType.AUDIO, - source, - name, - track, - ); - /// Start playing audio track. On web platform, create an audio element and /// start playback @override @@ -149,17 +133,6 @@ Future defaultNativeAudioConfigurationFunc( ); } - // TODO: .record category causes exception in WebRTC lib for unknown reason - // if (this == AudioTrackState.localOnly) { - // return NativeAudioConfiguration( - // iosCategory: IosAudioCategory.record, - // iosCategoryOptions: { - // // IosAudioCategoryOption.allowBluetooth, - // }, - // iosMode: IosAudioMode.spokenAudio, - // ); - // } - return NativeAudioConfiguration( appleAudioCategory: AppleAudioCategory.soloAmbient, appleAudioCategoryOptions: {}, diff --git a/lib/src/track/local_audio_track.dart b/lib/src/track/local_audio_track.dart index 547bfbd..196b3f8 100644 --- a/lib/src/track/local_audio_track.dart +++ b/lib/src/track/local_audio_track.dart @@ -2,50 +2,45 @@ import 'dart:async'; import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; -import '../exceptions.dart'; -import '../logger.dart'; +import '../proto/livekit_models.pb.dart' as lk_models; import '../types.dart'; -import 'audio_track.dart'; +import 'audio_management.dart'; import 'local_track.dart'; import 'options.dart'; -class LocalAudioTrack extends AudioTrack with LocalTrack { +class LocalAudioTrack extends LocalTrack with AudioTrack, AudioManagementMixin { + // Options used for this track + @override + covariant LocalAudioTrackOptions currentOptions; + // private constructor LocalAudioTrack._( - TrackSource source, String name, - rtc.MediaStreamTrack track, + TrackSource source, rtc.MediaStream stream, - ) : super(source, name, track, stream); + rtc.MediaStreamTrack track, + this.currentOptions, + ) : super( + name, + lk_models.TrackType.AUDIO, + source, + stream, + track, + ); /// Creates a new audio track from the default audio input device. - static Future create( - [LocalAudioTrackOptions? options]) async { - final audioConstraints = options?.toMediaConstraintsMap() ?? - const LocalAudioTrackOptions().toMediaConstraintsMap(); - final stream = - await rtc.navigator.mediaDevices.getUserMedia({ - 'audio': audioConstraints, - 'video': false, - }); - - if (stream.getAudioTracks().isEmpty) throw TrackCreateException(); + static Future create([ + LocalAudioTrackOptions? options, + ]) async { + options ??= const LocalAudioTrackOptions(); + final stream = await LocalTrack.createStream(options); return LocalAudioTrack._( - TrackSource.microphone, '', - stream.getAudioTracks().first, + TrackSource.microphone, stream, + stream.getAudioTracks().first, + options, ); } - - @override - Future stop() async { - final didStop = await super.stop(); - if (didStop) { - logger.fine('Stopping mediaStreamTrack...'); - await mediaStreamTrack.stop(); - } - return didStop; - } } diff --git a/lib/src/track/local_track.dart b/lib/src/track/local_track.dart index 48a71a9..f7c84e1 100644 --- a/lib/src/track/local_track.dart +++ b/lib/src/track/local_track.dart @@ -1,13 +1,41 @@ +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 LocalTrack on Track { +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 Future mute() async { logger.fine('LocalTrack.mute() muted: $muted'); if (muted) return; await disable(); + await stop(); updateMuted(true); events.emit(TrackMuteUpdatedEvent(track: this, muted: muted)); } @@ -15,8 +43,82 @@ mixin LocalTrack on Track { Future unmute() async { logger.fine('LocalTrack.unmute() muted: $muted'); if (!muted) return; + await restartTrack(); await enable(); updateMuted(false); events.emit(TrackMuteUpdatedEvent(track: this, muted: muted)); } + + @override + Future 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 createStream( + LocalTrackOptions options, + ) async { + final constraints = { + '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 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(); + } } diff --git a/lib/src/track/local_track_publication.dart b/lib/src/track/local_track_publication.dart index bdc0b0a..60d9c5b 100644 --- a/lib/src/track/local_track_publication.dart +++ b/lib/src/track/local_track_publication.dart @@ -10,6 +10,9 @@ import 'track_publication.dart'; class LocalTrackPublication extends TrackPublication { final LocalParticipant _participant; + @override + covariant LocalTrack? track; + LocalTrackPublication( lk_models.TrackInfo info, Track track, diff --git a/lib/src/track/local_video_track.dart b/lib/src/track/local_video_track.dart index 89f5ef8..3e2ba76 100644 --- a/lib/src/track/local_video_track.dart +++ b/lib/src/track/local_video_track.dart @@ -1,73 +1,45 @@ import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; -import '../exceptions.dart'; import '../logger.dart'; +import '../proto/livekit_models.pb.dart' as lk_models; import '../types.dart'; import 'local_track.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 with LocalTrack { - // +class LocalVideoTrack extends LocalTrack with VideoTrack { // Options used for this track - // - LocalVideoTrackOptions currentOptions; + @override + covariant LocalVideoTrackOptions currentOptions; - // // Private constructor - // LocalVideoTrack._( - TrackSource source, String name, - rtc.MediaStreamTrack mediaTrack, + TrackSource source, rtc.MediaStream stream, + rtc.MediaStreamTrack track, this.currentOptions, - ) : super(source, name, mediaTrack, stream); - - rtc.RTCRtpSender? get sender => transceiver?.sender; - - /// Restarts the track with new options. This is useful when switching between - /// front and back cameras. - Future restartTrack([ - LocalVideoTrackOptions? 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 the current track & stream - await mediaStreamTrack.stop(); - await mediaStream.dispose(); - - // create new track with options - final newStream = await _createStream(currentOptions); - final newTrack = newStream.getVideoTracks().first; - - // replace track on sender - await sender?.replaceTrack(newTrack); - - // set new stream & track to this object - setMediaStream(newStream); - mediaStreamTrack = newTrack; - } + ) : super( + name, + lk_models.TrackType.VIDEO, + source, + stream, + track, + ); /// Creates a LocalVideoTrack from camera input. static Future createCameraTrack([ CameraTrackOptions? options, ]) async { options ??= const CameraTrackOptions(); - final stream = await _createStream(options); + final stream = await LocalTrack.createStream(options); return LocalVideoTrack._( - TrackSource.camera, Track.cameraName, - stream.getVideoTracks().first, + TrackSource.camera, stream, + stream.getVideoTracks().first, options, ); } @@ -76,49 +48,19 @@ class LocalVideoTrack extends VideoTrack with LocalTrack { /// /// Note: Android requires a foreground service to be started prior to /// creating a screen track. Refer to the example app for an implementation. - static Future createScreenTrack([ - ScreenTrackOptions? options, + static Future createScreenShareTrack([ + ScreenShareTrackOptions? options, ]) async { - options ??= const ScreenTrackOptions(); - final stream = await _createStream(options); + options ??= const ScreenShareTrackOptions(); + final stream = await LocalTrack.createStream(options); return LocalVideoTrack._( - TrackSource.screenShareVideo, Track.screenShareName, - stream.getVideoTracks().first, + TrackSource.screenShareVideo, stream, + stream.getVideoTracks().first, options, ); } - - static Future _createStream( - LocalVideoTrackOptions options, - ) async { - final constraints = { - 'audio': false, - 'video': options.toMediaConstraintsMap(), - }; - - final rtc.MediaStream stream; - if (options is ScreenTrackOptions) { - stream = await rtc.navigator.mediaDevices.getDisplayMedia(constraints); - } else { - // options is CameraVideoTrackOptions - stream = await rtc.navigator.mediaDevices.getUserMedia(constraints); - } - - if (stream.getVideoTracks().isEmpty) throw TrackCreateException(); - return stream; - } - - @override - Future stop() async { - final didStop = await super.stop(); - if (didStop) { - logger.fine('Stopping mediaStreamTrack...'); - await mediaStreamTrack.stop(); - } - return didStop; - } } // diff --git a/lib/src/track/options.dart b/lib/src/track/options.dart index f8535bd..f9e8402 100644 --- a/lib/src/track/options.dart +++ b/lib/src/track/options.dart @@ -43,12 +43,19 @@ class CameraTrackOptions extends LocalVideoTrackOptions { ); } -class ScreenTrackOptions extends LocalVideoTrackOptions { - const ScreenTrackOptions(); +class ScreenShareTrackOptions extends LocalVideoTrackOptions { + const ScreenShareTrackOptions(); +} + +/// Base class for track options. +abstract class LocalTrackOptions { + const LocalTrackOptions(); + // All subclasses must be able to report constraints + Map toMediaConstraintsMap(); } /// Options when creating a LocalVideoTrack. -abstract class LocalVideoTrackOptions { +abstract class LocalVideoTrackOptions extends LocalTrackOptions { // final LocalVideoTrackType type; final VideoParameters params; @@ -56,6 +63,7 @@ abstract class LocalVideoTrackOptions { this.params = VideoParameters.presetQHD169, }); + @override Map toMediaConstraintsMap() => { 'mandatory': params.toMediaConstraintsMap(), }; @@ -235,7 +243,7 @@ class VideoParameters { } /// Options when creating an LocalAudioTrack. Placeholder for now. -class LocalAudioTrackOptions { +class LocalAudioTrackOptions extends LocalTrackOptions { final bool noiseSuppression; final bool echoCancellation; final bool autoGainControl; @@ -250,6 +258,7 @@ class LocalAudioTrackOptions { this.typingNoiseDetection = true, }); + @override Map toMediaConstraintsMap() => { 'optional': >[ {'echoCancellation': echoCancellation}, diff --git a/lib/src/track/remote_audio_track.dart b/lib/src/track/remote_audio_track.dart index 03c0714..e58a5df 100644 --- a/lib/src/track/remote_audio_track.dart +++ b/lib/src/track/remote_audio_track.dart @@ -1,16 +1,27 @@ import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; + +import '../proto/livekit_models.pb.dart' as lk_models; import '../types.dart'; import '_audio_api.dart' if (dart.library.html) '_audio_html.dart' as audio; -import 'audio_track.dart'; +import 'audio_management.dart'; +import 'local_track.dart'; +import 'remote_track.dart'; -class RemoteAudioTrack extends AudioTrack { +class RemoteAudioTrack extends RemoteTrack + with AudioTrack, AudioManagementMixin { // RemoteAudioTrack( - TrackSource source, String name, - rtc.MediaStreamTrack track, + TrackSource source, rtc.MediaStream stream, - ) : super(source, name, track, stream); + rtc.MediaStreamTrack track, + ) : super( + name, + lk_models.TrackType.AUDIO, + source, + stream, + track, + ); @override Future start() async { @@ -18,7 +29,6 @@ class RemoteAudioTrack extends AudioTrack { if (didStart) { // web support audio.startAudio(getCid(), mediaStreamTrack); - await enable(); } return didStart; } @@ -29,7 +39,6 @@ class RemoteAudioTrack extends AudioTrack { if (didStop) { // web support audio.stopAudio(getCid()); - await disable(); } return didStop; } diff --git a/lib/src/track/remote_track.dart b/lib/src/track/remote_track.dart new file mode 100644 index 0000000..7deef67 --- /dev/null +++ b/lib/src/track/remote_track.dart @@ -0,0 +1,39 @@ +import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; + +import '../proto/livekit_models.pb.dart' as lk_models; +import '../types.dart'; +import 'track.dart'; + +abstract class RemoteTrack extends Track { + RemoteTrack( + String name, + lk_models.TrackType kind, + TrackSource source, + rtc.MediaStream stream, + rtc.MediaStreamTrack track, + ) : super( + name, + kind, + source, + stream, + track, + ); + + @override + Future start() async { + final didStart = await super.start(); + if (didStart) { + await enable(); + } + return didStart; + } + + @override + Future stop() async { + final didStop = await super.stop(); + if (didStop) { + await disable(); + } + return didStop; + } +} diff --git a/lib/src/track/remote_track_publication.dart b/lib/src/track/remote_track_publication.dart index fb0aee1..e37a946 100644 --- a/lib/src/track/remote_track_publication.dart +++ b/lib/src/track/remote_track_publication.dart @@ -12,6 +12,7 @@ import '../participant/remote_participant.dart'; import '../proto/livekit_models.pb.dart' as lk_models; import '../proto/livekit_rtc.pb.dart' as lk_rtc; import '../utils.dart'; +import 'remote_track.dart'; import 'track.dart'; import 'track_publication.dart'; @@ -31,6 +32,9 @@ class RendererVisibility { /// Represents a track publication from a RemoteParticipant. Provides methods to /// control if we should subscribe to the track, and its quality (for video). class RemoteTrackPublication extends TrackPublication { + @override + covariant RemoteTrack? track; + final RemoteParticipant _participant; bool _enabled = true; lk_rtc.VideoQuality _videoQuality = lk_rtc.VideoQuality.HIGH; diff --git a/lib/src/track/remote_video_track.dart b/lib/src/track/remote_video_track.dart index 0e11c83..7cc318c 100644 --- a/lib/src/track/remote_video_track.dart +++ b/lib/src/track/remote_video_track.dart @@ -1,32 +1,22 @@ import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; +import 'package:livekit_client/src/track/local_track.dart'; -import '../track/video_track.dart'; +import '../proto/livekit_models.pb.dart' as lk_models; import '../types.dart'; +import 'remote_track.dart'; -class RemoteVideoTrack extends VideoTrack { +class RemoteVideoTrack extends RemoteTrack with VideoTrack { // RemoteVideoTrack( - TrackSource source, String name, - rtc.MediaStreamTrack mediaTrack, + TrackSource source, rtc.MediaStream stream, - ) : super(source, name, mediaTrack, stream); - - @override - Future start() async { - final didStart = await super.start(); - if (didStart) { - await enable(); - } - return didStart; - } - - @override - Future stop() async { - final didStop = await super.stop(); - if (didStop) { - await disable(); - } - return didStop; - } + rtc.MediaStreamTrack track, + ) : super( + name, + lk_models.TrackType.VIDEO, + source, + stream, + track, + ); } diff --git a/lib/src/track/track.dart b/lib/src/track/track.dart index 2475d7f..fc62758 100644 --- a/lib/src/track/track.dart +++ b/lib/src/track/track.dart @@ -5,6 +5,7 @@ import 'package:uuid/uuid.dart'; import '../events.dart'; import '../extensions.dart'; +import '../internal/events.dart'; import '../logger.dart'; import '../managers/event.dart'; import '../proto/livekit_models.pb.dart' as lk_models; @@ -23,7 +24,14 @@ abstract class Track extends DisposableChangeNotifier final String name; final lk_models.TrackType kind; final TrackSource source; - rtc.MediaStreamTrack mediaStreamTrack; + + // read only + rtc.MediaStream get mediaStream => _mediaStream; + rtc.MediaStream _mediaStream; + + // read only + rtc.MediaStreamTrack get mediaStreamTrack => _mediaStreamTrack; + rtc.MediaStreamTrack _mediaStreamTrack; String? sid; rtc.RTCRtpTransceiver? transceiver; @@ -36,11 +44,14 @@ abstract class Track extends DisposableChangeNotifier bool _muted = false; bool get muted => _muted; + rtc.RTCRtpSender? get sender => transceiver?.sender; + Track( + this.name, this.kind, this.source, - this.name, - this.mediaStreamTrack, + this._mediaStream, + this._mediaStreamTrack, ) { // Any event emitted will trigger ChangeNotifier events.listen((event) { @@ -128,4 +139,15 @@ abstract class Track extends DisposableChangeNotifier @internal void updateMuted(bool muted) => _muted = muted; + + @internal + void updateMediaStreamAndTrack( + rtc.MediaStream stream, rtc.MediaStreamTrack track) { + _mediaStream = stream; + _mediaStreamTrack = track; + events.emit(TrackStreamUpdatedEvent( + track: this, + stream: stream, + )); + } } diff --git a/lib/src/track/track_publication.dart b/lib/src/track/track_publication.dart index a5945ee..fa5a418 100644 --- a/lib/src/track/track_publication.dart +++ b/lib/src/track/track_publication.dart @@ -18,8 +18,8 @@ abstract class TrackPublication extends Disposable { final lk_models.TrackType kind; final TrackSource source; - Track? _track; - Track? get track => _track; + abstract Track? track; + // Track? get track => _track; // metadata-muted bool _muted = false; @@ -28,7 +28,7 @@ abstract class TrackPublication extends Disposable { bool simulcasted = false; TrackDimension? dimension; - bool get subscribed => _track != null; + bool get subscribed => track != null; TrackPublication.fromInfo(lk_models.TrackInfo info) : sid = info.sid, @@ -66,10 +66,10 @@ abstract class TrackPublication extends Disposable { // Intended for internal use only. @internal Future updateTrack(Track? newValue) async { - if (_track == newValue) return false; + if (track == newValue) return false; // dispose previous track (if exists) - await _track?.dispose(); - _track = newValue; + await track?.dispose(); + track = newValue; return true; } } diff --git a/lib/src/track/video_track.dart b/lib/src/track/video_track.dart deleted file mode 100644 index 8a927f5..0000000 --- a/lib/src/track/video_track.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; -import 'package:meta/meta.dart'; - -import '../internal/events.dart'; -import '../proto/livekit_models.pb.dart' as lk_models; -import '../types.dart'; -import 'track.dart'; - -/// A video track will notify when its mediaTrack has changed. -abstract class VideoTrack extends Track { - rtc.MediaStream _mediaStream; - - VideoTrack( - TrackSource source, - String name, - rtc.MediaStreamTrack mediaTrack, - this._mediaStream, - ) : super( - lk_models.TrackType.VIDEO, - source, - name, - mediaTrack, - ); - - rtc.MediaStream get mediaStream => _mediaStream; - - /// internal use - /// {@nodoc} - @internal - void setMediaStream(rtc.MediaStream stream) { - _mediaStream = stream; - events.emit(TrackStreamUpdatedEvent( - track: this, - stream: stream, - )); - } - - @override - Future stop() async { - final didStop = await super.stop(); - if (didStop) { - await _mediaStream.dispose(); - } - // _mediaStream = null; - return didStop; - } -} diff --git a/lib/src/widget/video_track_renderer.dart b/lib/src/widget/video_track_renderer.dart index e824c4c..4f967c3 100644 --- a/lib/src/widget/video_track_renderer.dart +++ b/lib/src/widget/video_track_renderer.dart @@ -1,6 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; +import 'package:livekit_client/src/track/local_track.dart'; import 'package:visibility_detector/visibility_detector.dart'; import '../events.dart'; @@ -9,7 +10,6 @@ import '../internal/events.dart'; import '../logger.dart'; import '../managers/event.dart'; import '../track/local_video_track.dart'; -import '../track/video_track.dart'; /// Widget that renders a [VideoTrack]. class VideoTrackRenderer extends StatefulWidget {