feature: switch between front and back cameras
This commit is contained in:
@@ -5,6 +5,8 @@ import 'options.dart';
|
||||
import 'video_track.dart';
|
||||
|
||||
class LocalVideoTrack extends VideoTrack {
|
||||
RTCRtpSender? get sender => transceiver?.sender;
|
||||
|
||||
LocalVideoTrack(String name, MediaStreamTrack mediaTrack, MediaStream stream)
|
||||
: super(name, mediaTrack, stream);
|
||||
|
||||
@@ -14,6 +16,41 @@ class LocalVideoTrack extends VideoTrack {
|
||||
options = LocalVideoTrackOptions(params: VideoPresets.qhd);
|
||||
}
|
||||
|
||||
try {
|
||||
var stream = await _createCameraStream(options);
|
||||
return LocalVideoTrack("camera", stream.getVideoTracks().first, stream);
|
||||
} catch (e) {
|
||||
return Future.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> restartTrack([LocalVideoTrackOptions? options]) async {
|
||||
if (sender == null) {
|
||||
return Future.error(TrackCreateError('could not restart track'));
|
||||
}
|
||||
|
||||
if (options == null) {
|
||||
options = LocalVideoTrackOptions(params: VideoPresets.qhd);
|
||||
}
|
||||
|
||||
try {
|
||||
var stream = await _createCameraStream(options);
|
||||
var track = stream.getVideoTracks().first;
|
||||
mediaStream = stream;
|
||||
await mediaTrack.stop();
|
||||
mediaTrack = track;
|
||||
await sender?.replaceTrack(track);
|
||||
} catch (e) {
|
||||
return Future.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<MediaStream> _createCameraStream(
|
||||
LocalVideoTrackOptions? options) async {
|
||||
if (options == null) {
|
||||
options = LocalVideoTrackOptions(params: VideoPresets.qhd);
|
||||
}
|
||||
|
||||
try {
|
||||
var stream = await navigator.mediaDevices.getUserMedia({
|
||||
"audio": false,
|
||||
@@ -24,7 +61,7 @@ class LocalVideoTrack extends VideoTrack {
|
||||
return Future.error(TrackCreateError());
|
||||
}
|
||||
|
||||
return LocalVideoTrack("camera", stream.getVideoTracks().first, stream);
|
||||
return stream;
|
||||
} catch (e) {
|
||||
return Future.error(e);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@ class LocalVideoTrackOptions {
|
||||
VideoParameter params;
|
||||
|
||||
LocalVideoTrackOptions({
|
||||
required VideoParameter params,
|
||||
VideoParameter? params,
|
||||
CameraPosition? position,
|
||||
}) : this.params = params {
|
||||
}) : params = VideoPresets.qhd {
|
||||
if (params != null) {
|
||||
this.params = params;
|
||||
}
|
||||
if (position != null) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ class RemoteTrackPublication extends TrackPublication {
|
||||
_participant.delegate?.onTrackUnmuted(_participant, this);
|
||||
_participant.roomDelegate?.onTrackUnmuted(_participant, this);
|
||||
}
|
||||
if (subscribed) {
|
||||
track?.mediaTrack.enabled = !val;
|
||||
}
|
||||
_participant.muteChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ class Track {
|
||||
|
||||
Track(this.kind, this.name, this.mediaTrack);
|
||||
|
||||
bool get muted => mediaTrack.muted == null ? false : mediaTrack.muted!;
|
||||
|
||||
RTCRtpMediaType get mediaType {
|
||||
switch (kind) {
|
||||
case TrackType.AUDIO:
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||
|
||||
import '../proto/livekit_models.pb.dart';
|
||||
import 'track.dart';
|
||||
|
||||
class VideoTrack extends Track {
|
||||
MediaStream? mediaStream;
|
||||
/// A video track will notify when its mediaTrack has changed.
|
||||
class VideoTrack extends Track with ChangeNotifier {
|
||||
MediaStream? _mediaStream;
|
||||
|
||||
VideoTrack(String name, MediaStreamTrack mediaTrack, this.mediaStream)
|
||||
VideoTrack(String name, MediaStreamTrack mediaTrack, this._mediaStream)
|
||||
: super(TrackType.VIDEO, name, mediaTrack);
|
||||
|
||||
MediaStream? get mediaStream => _mediaStream;
|
||||
|
||||
set mediaStream(MediaStream? stream) {
|
||||
_mediaStream = stream;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
stop() {
|
||||
super.stop();
|
||||
mediaStream?.dispose();
|
||||
mediaStream = null;
|
||||
_mediaStream?.dispose();
|
||||
_mediaStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,22 +23,35 @@ class _VideoTrackRendererState extends State<VideoTrackRenderer> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget.track.addListener(_trackChanged);
|
||||
_initRenderer();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.track.removeListener(_trackChanged);
|
||||
_renderer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_initRenderer() async {
|
||||
await _renderer.initialize();
|
||||
@override
|
||||
void didUpdateWidget(covariant VideoTrackRenderer oldWidget) {
|
||||
oldWidget.track.removeListener(_trackChanged);
|
||||
widget.track.addListener(_trackChanged);
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
_trackChanged() {
|
||||
setState(() {
|
||||
_renderer.srcObject = widget.track.mediaStream;
|
||||
});
|
||||
}
|
||||
|
||||
_initRenderer() async {
|
||||
await _renderer.initialize();
|
||||
_trackChanged();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var isLocal = widget.track is LocalVideoTrack;
|
||||
|
||||
Reference in New Issue
Block a user