Files
client-sdk-flutter/lib/src/widget/video_track_renderer.dart
T
Hiroshi HorieandGitHub ff65370cbc Automatic mirrorMode for VideoView (#119)
* update protos

* impl 1

* re-compute when options update

* minor fix
2022-05-17 10:51:38 +09:00

130 lines
3.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
import '../events.dart';
import '../extensions.dart';
import '../internal/events.dart';
import '../managers/event.dart';
import '../track/local/local.dart';
import '../track/local/video.dart';
import '../track/options.dart';
enum VideoViewMirrorMode {
auto,
off,
mirror,
}
/// Widget that renders a [VideoTrack].
class VideoTrackRenderer extends StatefulWidget {
final VideoTrack track;
final rtc.RTCVideoViewObjectFit fit;
final VideoViewMirrorMode mirrorMode;
const VideoTrackRenderer(
this.track, {
this.fit = rtc.RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
this.mirrorMode = VideoViewMirrorMode.auto,
Key? key,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _VideoTrackRendererState();
}
class _VideoTrackRendererState extends State<VideoTrackRenderer> {
final _renderer = rtc.RTCVideoRenderer();
bool _rendererReady = false;
EventsListener<TrackEvent>? _listener;
// Used to compute visibility information
late GlobalKey _internalKey;
@override
void initState() {
super.initState();
_internalKey = widget.track.addViewKey();
(() async {
await _renderer.initialize();
await _attach();
setState(() => _rendererReady = true);
})();
}
@override
void dispose() {
widget.track.removeViewKey(_internalKey);
_listener?.dispose();
_renderer.srcObject = null;
_renderer.dispose();
super.dispose();
}
Future<void> _attach() async {
_renderer.srcObject = widget.track.mediaStream;
await _listener?.dispose();
_listener = widget.track.createListener()
..on<TrackStreamUpdatedEvent>((event) {
if (!mounted) return;
_renderer.srcObject = event.stream;
})
..on<LocalTrackOptionsUpdatedEvent>((event) {
if (!mounted) return;
// force recompute of mirror mode
setState(() {});
});
}
@override
void didUpdateWidget(covariant VideoTrackRenderer oldWidget) {
super.didUpdateWidget(oldWidget);
//
if (widget.track != oldWidget.track) {
oldWidget.track.removeViewKey(_internalKey);
_internalKey = widget.track.addViewKey();
// TODO: re-attach only if needed
(() async {
await _attach();
})();
}
}
@override
Widget build(BuildContext context) => !_rendererReady
? Container()
: Builder(
key: _internalKey,
builder: (ctx) {
// let it render before notifying build
WidgetsBindingCompatible.instance
?.addPostFrameCallback((timeStamp) {
widget.track.onVideoViewBuild?.call(_internalKey);
});
return rtc.RTCVideoView(
_renderer,
mirror: _shouldMirror(),
filterQuality: FilterQuality.medium,
objectFit: widget.fit,
);
},
);
bool _shouldMirror() {
// on
if (widget.mirrorMode == VideoViewMirrorMode.mirror) return true;
// auto
if (widget.mirrorMode == VideoViewMirrorMode.auto) {
final track = widget.track;
if (track is LocalVideoTrack) {
final options = track.currentOptions;
if (options is CameraCaptureOptions) {
// mirror if front camera
return options.cameraPosition == CameraPosition.front;
}
}
}
// default to false
return false;
}
}