Skip to content

Commit

Permalink
feat: support FocusMode/ExposureMode for camera capture options. (#658)
Browse files Browse the repository at this point in the history
* feat: support FocusMode/ExposureMode for camera capture options.

* bump version for flutter-webrtc.

* fix analyze.
  • Loading branch information
cloudwebrtc authored Dec 21, 2024
1 parent ada82ff commit a0b88b5
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 51 deletions.
8 changes: 4 additions & 4 deletions example/lib/theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class LiveKitTheme {
// backgroundColor: WidgetStateProperty.all<Color>(accentColor),
backgroundColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.disabled)) {
return accentColor.withOpacity(0.5);
return accentColor.withValues(alpha: 0.5);
}
return accentColor;
}),
Expand All @@ -59,13 +59,13 @@ class LiveKitTheme {
if (states.contains(WidgetState.selected)) {
return accentColor;
}
return accentColor.withOpacity(0.3);
return accentColor.withValues(alpha: 0.3);
}),
thumbColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return Colors.white;
}
return Colors.white.withOpacity(0.3);
return Colors.white.withValues(alpha: 0.3);
}),
),
dialogTheme: DialogTheme(
Expand All @@ -87,7 +87,7 @@ class LiveKitTheme {
color: LKColors.lkBlue,
),
hintStyle: TextStyle(
color: LKColors.lkBlue.withOpacity(.5),
color: LKColors.lkBlue.withValues(alpha: 5),
),
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
Expand Down
33 changes: 16 additions & 17 deletions example/lib/widgets/participant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,19 +176,19 @@ abstract class _ParticipantWidgetState<T extends ParticipantWidget>
right: 30,
child: ParticipantStatsWidget(
participant: widget.participant,
)),
if(activeAudioTrack != null && !activeAudioTrack!.muted) Positioned(
top: 10,
right: 10,
left: 10,
bottom: 10,
child: SoundWaveformWidget(
key: ValueKey(activeAudioTrack!.hashCode),
audioTrack: activeAudioTrack!,
width: 8,
),
),

)),
if (activeAudioTrack != null && !activeAudioTrack!.muted)
Positioned(
top: 10,
right: 10,
left: 10,
bottom: 10,
child: SoundWaveformWidget(
key: ValueKey(activeAudioTrack!.hashCode),
audioTrack: activeAudioTrack!,
width: 8,
),
),
],
),
);
Expand Down Expand Up @@ -279,7 +279,7 @@ class RemoteTrackPublicationMenuWidget extends StatelessWidget {

@override
Widget build(BuildContext context) => Material(
color: Colors.black.withOpacity(0.3),
color: Colors.black.withValues(alpha: 0.3),
child: PopupMenuButton<Function>(
tooltip: 'Subscribe menu',
icon: Icon(icon,
Expand Down Expand Up @@ -317,7 +317,7 @@ class RemoteTrackFPSMenuWidget extends StatelessWidget {

@override
Widget build(BuildContext context) => Material(
color: Colors.black.withOpacity(0.3),
color: Colors.black.withValues(alpha: 0.3),
child: PopupMenuButton<Function>(
tooltip: 'Preferred FPS',
icon: Icon(icon, color: Colors.white),
Expand Down Expand Up @@ -351,7 +351,7 @@ class RemoteTrackQualityMenuWidget extends StatelessWidget {

@override
Widget build(BuildContext context) => Material(
color: Colors.black.withOpacity(0.3),
color: Colors.black.withValues(alpha: 0.3),
child: PopupMenuButton<Function>(
tooltip: 'Preferred Quality',
icon: Icon(icon, color: Colors.white),
Expand All @@ -373,4 +373,3 @@ class RemoteTrackQualityMenuWidget extends StatelessWidget {
),
);
}

2 changes: 1 addition & 1 deletion example/lib/widgets/participant_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class ParticipantInfoWidget extends StatelessWidget {

@override
Widget build(BuildContext context) => Container(
color: Colors.black.withOpacity(0.3),
color: Colors.black.withValues(alpha: 0.3),
padding: const EdgeInsets.symmetric(
vertical: 7,
horizontal: 10,
Expand Down
2 changes: 1 addition & 1 deletion example/lib/widgets/participant_stats.dart
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ class _ParticipantStatsWidgetState extends State<ParticipantStatsWidget> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black.withOpacity(0.3),
color: Colors.black.withValues(alpha: 0.3),
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 8,
Expand Down
2 changes: 1 addition & 1 deletion example/lib/widgets/text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class LKTextField extends StatelessWidget {
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: Colors.white.withOpacity(.3),
color: Colors.white.withValues(alpha: .3),
),
borderRadius: BorderRadius.circular(8),
),
Expand Down
10 changes: 4 additions & 6 deletions lib/src/core/engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1082,12 +1082,10 @@ extension EnginePrivateMethods on Engine {

extension EngineInternalMethods on Engine {
@internal
List<lk_rtc.DataChannelInfo> dataChannelInfo() =>
[_reliableDCPub, _lossyDCPub]
.whereNotNull()
.where((e) => e.id != -1)
.map((e) => e.toLKInfoType())
.toList();
List<lk_rtc.DataChannelInfo> dataChannelInfo() => [
_reliableDCPub,
_lossyDCPub
].nonNulls.where((e) => e.id != -1).map((e) => e.toLKInfoType()).toList();
@internal
Future<rtc.RTCRtpSender> createSimulcastTransceiverSender(
LocalVideoTrack track,
Expand Down
5 changes: 2 additions & 3 deletions lib/src/publication/remote.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import 'dart:math';

import 'package:flutter/widgets.dart';

import 'package:collection/collection.dart';
import 'package:meta/meta.dart';

import '../core/signal_client.dart';
Expand Down Expand Up @@ -155,9 +154,9 @@ class RemoteTrackPublication<T extends RemoteTrack>
// filter visible build contexts
final viewSizes = videoTrack.viewKeys
.map((e) => e.currentContext)
.whereNotNull()
.nonNulls
.map((e) => e.findRenderObject() as RenderBox?)
.whereNotNull()
.nonNulls
.where((e) => e.hasSize)
.map((e) => e.size);

Expand Down
14 changes: 14 additions & 0 deletions lib/src/track/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ enum CameraPosition {
back,
}

enum CameraFocusMode { auto, locked }

enum CameraExposureMode { auto, locked }

/// Convenience extension for [CameraPosition].
extension CameraPositionExt on CameraPosition {
/// Return a [CameraPosition] which front and back is switched.
Expand All @@ -41,8 +45,16 @@ class CameraCaptureOptions extends VideoCaptureOptions {
/// set to false to only toggle enabled instead of stop/replaceTrack for muting
final bool stopCameraCaptureOnMute;

/// The focus mode to use for the camera.
final CameraFocusMode focusMode;

/// The exposure mode to use for the camera.
final CameraExposureMode exposureMode;

const CameraCaptureOptions({
this.cameraPosition = CameraPosition.front,
this.focusMode = CameraFocusMode.auto,
this.exposureMode = CameraExposureMode.auto,
String? deviceId,
double? maxFrameRate,
VideoParameters params = VideoParametersPresets.h720_169,
Expand All @@ -55,6 +67,8 @@ class CameraCaptureOptions extends VideoCaptureOptions {

CameraCaptureOptions.from({required VideoCaptureOptions captureOptions})
: cameraPosition = CameraPosition.front,
focusMode = CameraFocusMode.auto,
exposureMode = CameraExposureMode.auto,
stopCameraCaptureOnMute = true,
super(
params: captureOptions.params,
Expand Down
77 changes: 60 additions & 17 deletions lib/src/widgets/video_track_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -77,6 +79,25 @@ class _VideoTrackRendererState extends State<VideoTrackRenderer> {
return _renderer!;
}

void setZoom(double zoomLevel) async {
final videoTrack = _renderer?.srcObject!.getVideoTracks().first;
if (videoTrack == null) return;
await rtc.Helper.setZoom(videoTrack, zoomLevel);
}

void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) {
final videoTrack = _renderer?.srcObject!.getVideoTracks().first;
if (videoTrack == null) return;

final point = Point<double>(
details.localPosition.dx / constraints.maxWidth,
details.localPosition.dy / constraints.maxHeight,
);

rtc.Helper.setFocusPoint(videoTrack, point);
rtc.Helper.setExposurePoint(videoTrack, point);
}

void disposeRenderer() {
try {
_renderer?.srcObject = null;
Expand Down Expand Up @@ -158,6 +179,28 @@ class _VideoTrackRendererState extends State<VideoTrackRenderer> {
},
);

Widget _videoRendererView() {
if (lkPlatformIs(PlatformType.iOS) &&
[VideoRenderMode.auto, VideoRenderMode.platformView]
.contains(widget.renderMode)) {
return rtc.RTCVideoPlatFormView(
mirror: _shouldMirror(),
objectFit: widget.fit,
onViewReady: (controller) {
_renderer = controller;
_renderer?.srcObject = widget.track.mediaStream;
_attach();
},
);
}
return rtc.RTCVideoView(
_renderer! as rtc.RTCVideoRenderer,
mirror: _shouldMirror(),
filterQuality: FilterQuality.medium,
objectFit: widget.fit,
);
}

Widget _videoViewForNative() => FutureBuilder(
future: _initializeRenderer(),
builder: (context, snapshot) {
Expand All @@ -172,24 +215,24 @@ class _VideoTrackRendererState extends State<VideoTrackRenderer> {
?.addPostFrameCallback((timeStamp) {
widget.track.onVideoViewBuild?.call(_internalKey);
});
if (lkPlatformIs(PlatformType.iOS) &&
[VideoRenderMode.auto, VideoRenderMode.platformView]
.contains(widget.renderMode)) {
return rtc.RTCVideoPlatFormView(
mirror: _shouldMirror(),
objectFit: widget.fit,
onViewReady: (controller) {
_renderer = controller;
_renderer?.srcObject = widget.track.mediaStream;
_attach();
},
);

if (!lkPlatformIsMobile() || widget.track is! LocalVideoTrack) {
return _videoRendererView();
}
return rtc.RTCVideoView(
_renderer! as rtc.RTCVideoRenderer,
mirror: _shouldMirror(),
filterQuality: FilterQuality.medium,
objectFit: widget.fit,
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return GestureDetector(
onScaleStart: (details) {},
onScaleUpdate: (details) {
if (details.scale != 1.0) {
setZoom(details.scale);
}
},
onTapDown: (TapDownDetails details) =>
onViewFinderTap(details, constraints),
child: _videoRendererView(),
);
},
);
},
);
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ dependencies:
uuid: '>=3.0.6'
synchronized: ^3.0.0+3
protobuf: ^3.0.0
flutter_webrtc: ^0.12.3
flutter_webrtc: ^0.12.4
device_info_plus: ^11.1.1
js: '>=0.6.4'
platform_detect: ^2.0.7
Expand Down

0 comments on commit a0b88b5

Please sign in to comment.