diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 9efbd0fd10f6..6073e0c3ad8a 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.2.3 +* Converts native platform calls to Pigeon. * Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. ## 0.2.2 diff --git a/packages/camera/camera_windows/lib/camera_windows.dart b/packages/camera/camera_windows/lib/camera_windows.dart index fc4ce3b09c81..b54bc68a19fb 100644 --- a/packages/camera/camera_windows/lib/camera_windows.dart +++ b/packages/camera/camera_windows/lib/camera_windows.dart @@ -10,17 +10,21 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_transform/stream_transform.dart'; +import 'src/messages.g.dart'; + /// An implementation of [CameraPlatform] for Windows. class CameraWindows extends CameraPlatform { + /// Creates a new Windows [CameraPlatform] implementation instance. + CameraWindows({@visibleForTesting CameraApi? api}) + : _hostApi = api ?? CameraApi(); + /// Registers the Windows implementation of CameraPlatform. static void registerWith() { CameraPlatform.instance = CameraWindows(); } - /// The method channel used to interact with the native platform. - @visibleForTesting - final MethodChannel pluginChannel = - const MethodChannel('plugins.flutter.io/camera_windows'); + /// Interface for calling host-side code. + final CameraApi _hostApi; /// Camera specific method channels to allow communicating with specific cameras. final Map _cameraChannels = {}; @@ -43,19 +47,18 @@ class CameraWindows extends CameraPlatform { @override Future> availableCameras() async { try { - final List>? cameras = await pluginChannel - .invokeListMethod>('availableCameras'); + final List cameras = await _hostApi.getAvailableCameras(); - if (cameras == null) { - return []; - } - - return cameras.map((Map camera) { + return cameras.map((String? cameraName) { return CameraDescription( - name: camera['name'] as String, - lensDirection: - parseCameraLensDirection(camera['lensFacing'] as String), - sensorOrientation: camera['sensorOrientation'] as int, + // This type is only nullable due to Pigeon limitations, see + // https://github.com/flutter/flutter/issues/97848. The native code + // will never return null. + name: cameraName!, + // TODO(stuartmorgan): Implement these; see + // https://github.com/flutter/flutter/issues/97540. + lensDirection: CameraLensDirection.front, + sensorOrientation: 0, ); }).toList(); } on PlatformException catch (e) { @@ -83,23 +86,8 @@ class CameraWindows extends CameraPlatform { ) async { try { // If resolutionPreset is not specified, plugin selects the highest resolution possible. - final Map? reply = await pluginChannel - .invokeMapMethod('create', { - 'cameraName': cameraDescription.name, - 'resolutionPreset': null != mediaSettings?.resolutionPreset - ? _serializeResolutionPreset(mediaSettings!.resolutionPreset) - : null, - 'fps': mediaSettings?.fps, - 'videoBitrate': mediaSettings?.videoBitrate, - 'audioBitrate': mediaSettings?.audioBitrate, - 'enableAudio': mediaSettings?.enableAudio ?? true, - }); - - if (reply == null) { - throw CameraException('System', 'Cannot create camera'); - } - - return reply['cameraId']! as int; + return await _hostApi.create( + cameraDescription.name, _pigeonMediaSettings(mediaSettings)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); } @@ -110,35 +98,28 @@ class CameraWindows extends CameraPlatform { int cameraId, { ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown, }) async { - final int requestedCameraId = cameraId; - /// Creates channel for camera events. - _cameraChannels.putIfAbsent(requestedCameraId, () { - final MethodChannel channel = MethodChannel( - 'plugins.flutter.io/camera_windows/camera$requestedCameraId'); + _cameraChannels.putIfAbsent(cameraId, () { + final MethodChannel channel = + MethodChannel('plugins.flutter.io/camera_windows/camera$cameraId'); channel.setMethodCallHandler( - (MethodCall call) => handleCameraMethodCall(call, requestedCameraId), + (MethodCall call) => handleCameraMethodCall(call, cameraId), ); return channel; }); - final Map? reply; + final PlatformSize reply; try { - reply = await pluginChannel.invokeMapMethod( - 'initialize', - { - 'cameraId': requestedCameraId, - }, - ); + reply = await _hostApi.initialize(cameraId); } on PlatformException catch (e) { throw CameraException(e.code, e.message); } cameraEventStreamController.add( CameraInitializedEvent( - requestedCameraId, - reply!['previewWidth']!, - reply['previewHeight']!, + cameraId, + reply.width, + reply.height, ExposureMode.auto, false, FocusMode.auto, @@ -149,10 +130,7 @@ class CameraWindows extends CameraPlatform { @override Future dispose(int cameraId) async { - await pluginChannel.invokeMethod( - 'dispose', - {'cameraId': cameraId}, - ); + await _hostApi.dispose(cameraId); // Destroy method channel after camera is disposed to be able to handle last messages. if (_cameraChannels.containsKey(cameraId)) { @@ -217,18 +195,15 @@ class CameraWindows extends CameraPlatform { @override Future takePicture(int cameraId) async { - final String? path; - path = await pluginChannel.invokeMethod( - 'takePicture', - {'cameraId': cameraId}, - ); + final String path = await _hostApi.takePicture(cameraId); - return XFile(path!); + return XFile(path); } @override - Future prepareForVideoRecording() => - pluginChannel.invokeMethod('prepareForVideoRecording'); + Future prepareForVideoRecording() async { + // No-op. + } @override Future startVideoRecording(int cameraId, @@ -244,25 +219,15 @@ class CameraWindows extends CameraPlatform { 'Streaming is not currently supported on Windows'); } - await pluginChannel.invokeMethod( - 'startVideoRecording', - { - 'cameraId': options.cameraId, - 'maxVideoDuration': options.maxDuration?.inMilliseconds, - }, - ); + await _hostApi.startVideoRecording( + options.cameraId, _pigeonVideoCaptureOptions(options)); } @override Future stopVideoRecording(int cameraId) async { - final String? path; - - path = await pluginChannel.invokeMethod( - 'stopVideoRecording', - {'cameraId': cameraId}, - ); + final String path = await _hostApi.stopVideoRecording(cameraId); - return XFile(path!); + return XFile(path); } @override @@ -362,18 +327,12 @@ class CameraWindows extends CameraPlatform { @override Future pausePreview(int cameraId) async { - await pluginChannel.invokeMethod( - 'pausePreview', - {'cameraId': cameraId}, - ); + await _hostApi.pausePreview(cameraId); } @override Future resumePreview(int cameraId) async { - await pluginChannel.invokeMethod( - 'resumePreview', - {'cameraId': cameraId}, - ); + await _hostApi.resumePreview(cameraId); } @override @@ -381,26 +340,6 @@ class CameraWindows extends CameraPlatform { return Texture(textureId: cameraId); } - /// Returns the resolution preset as a nullable String. - String? _serializeResolutionPreset(ResolutionPreset? resolutionPreset) { - switch (resolutionPreset) { - case null: - return null; - case ResolutionPreset.max: - return 'max'; - case ResolutionPreset.ultraHigh: - return 'ultraHigh'; - case ResolutionPreset.veryHigh: - return 'veryHigh'; - case ResolutionPreset.high: - return 'high'; - case ResolutionPreset.medium: - return 'medium'; - case ResolutionPreset.low: - return 'low'; - } - } - /// Converts messages received from the native platform into camera events. /// /// This is only exposed for test purposes. It shouldn't be used by clients @@ -440,17 +379,52 @@ class CameraWindows extends CameraPlatform { } } - /// Parses string presentation of the camera lens direction and returns enum value. - @visibleForTesting - CameraLensDirection parseCameraLensDirection(String string) { - switch (string) { - case 'front': - return CameraLensDirection.front; - case 'back': - return CameraLensDirection.back; - case 'external': - return CameraLensDirection.external; + /// Returns a [MediaSettings]'s Pigeon representation. + PlatformMediaSettings _pigeonMediaSettings(MediaSettings? settings) { + return PlatformMediaSettings( + resolutionPreset: _pigeonResolutionPreset(settings?.resolutionPreset), + enableAudio: settings?.enableAudio ?? true, + framesPerSecond: settings?.fps, + videoBitrate: settings?.videoBitrate, + audioBitrate: settings?.audioBitrate, + ); + } + + /// Returns a [ResolutionPreset]'s Pigeon representation. + PlatformResolutionPreset _pigeonResolutionPreset( + ResolutionPreset? resolutionPreset) { + if (resolutionPreset == null) { + // Provide a default if one isn't provided, since the native side needs + // to set something. + return PlatformResolutionPreset.max; + } + switch (resolutionPreset) { + case ResolutionPreset.max: + return PlatformResolutionPreset.max; + case ResolutionPreset.ultraHigh: + return PlatformResolutionPreset.ultraHigh; + case ResolutionPreset.veryHigh: + return PlatformResolutionPreset.veryHigh; + case ResolutionPreset.high: + return PlatformResolutionPreset.high; + case ResolutionPreset.medium: + return PlatformResolutionPreset.medium; + case ResolutionPreset.low: + return PlatformResolutionPreset.low; } - throw ArgumentError('Unknown CameraLensDirection value'); + // The enum comes from a different package, which could get a new value at + // any time, so provide a fallback that ensures this won't break when used + // with a version that contains new values. This is deliberately outside + // the switch rather than a `default` so that the linter will flag the + // switch as needing an update. + // ignore: dead_code + return PlatformResolutionPreset.max; + } + + /// Returns a [VideoCamptureOptions]'s Pigeon representation. + PlatformVideoCaptureOptions _pigeonVideoCaptureOptions( + VideoCaptureOptions options) { + return PlatformVideoCaptureOptions( + maxDurationMilliseconds: options.maxDuration?.inMilliseconds); } } diff --git a/packages/camera/camera_windows/lib/src/messages.g.dart b/packages/camera/camera_windows/lib/src/messages.g.dart new file mode 100644 index 000000000000..2640b62037f8 --- /dev/null +++ b/packages/camera/camera_windows/lib/src/messages.g.dart @@ -0,0 +1,428 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v20.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +/// Pigeon version of platform interface's ResolutionPreset. +enum PlatformResolutionPreset { + low, + medium, + high, + veryHigh, + ultraHigh, + max, +} + +/// Pigeon version of MediaSettings. +class PlatformMediaSettings { + PlatformMediaSettings({ + required this.resolutionPreset, + this.framesPerSecond, + this.videoBitrate, + this.audioBitrate, + required this.enableAudio, + }); + + PlatformResolutionPreset resolutionPreset; + + int? framesPerSecond; + + int? videoBitrate; + + int? audioBitrate; + + bool enableAudio; + + Object encode() { + return [ + resolutionPreset, + framesPerSecond, + videoBitrate, + audioBitrate, + enableAudio, + ]; + } + + static PlatformMediaSettings decode(Object result) { + result as List; + return PlatformMediaSettings( + resolutionPreset: result[0]! as PlatformResolutionPreset, + framesPerSecond: result[1] as int?, + videoBitrate: result[2] as int?, + audioBitrate: result[3] as int?, + enableAudio: result[4]! as bool, + ); + } +} + +/// A representation of a size from the native camera APIs. +class PlatformSize { + PlatformSize({ + required this.width, + required this.height, + }); + + double width; + + double height; + + Object encode() { + return [ + width, + height, + ]; + } + + static PlatformSize decode(Object result) { + result as List; + return PlatformSize( + width: result[0]! as double, + height: result[1]! as double, + ); + } +} + +/// Pigeon version of the relevant subset of VideoCaptureOptions. +class PlatformVideoCaptureOptions { + PlatformVideoCaptureOptions({ + this.maxDurationMilliseconds, + }); + + int? maxDurationMilliseconds; + + Object encode() { + return [ + maxDurationMilliseconds, + ]; + } + + static PlatformVideoCaptureOptions decode(Object result) { + result as List; + return PlatformVideoCaptureOptions( + maxDurationMilliseconds: result[0] as int?, + ); + } +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is PlatformMediaSettings) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is PlatformSize) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is PlatformVideoCaptureOptions) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is PlatformResolutionPreset) { + buffer.putUint8(132); + writeValue(buffer, value.index); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return PlatformMediaSettings.decode(readValue(buffer)!); + case 130: + return PlatformSize.decode(readValue(buffer)!); + case 131: + return PlatformVideoCaptureOptions.decode(readValue(buffer)!); + case 132: + final int? value = readValue(buffer) as int?; + return value == null ? null : PlatformResolutionPreset.values[value]; + default: + return super.readValueOfType(type, buffer); + } + } +} + +class CameraApi { + /// Constructor for [CameraApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + CameraApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? __pigeon_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String __pigeon_messageChannelSuffix; + + /// Returns the names of all of the available capture devices. + Future> getAvailableCameras() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.camera_windows.CameraApi.getAvailableCameras$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)!.cast(); + } + } + + /// Creates a camera instance for the given device name and settings. + Future create(String cameraName, PlatformMediaSettings settings) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.camera_windows.CameraApi.create$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([cameraName, settings]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as int?)!; + } + } + + /// Initializes a camera, and returns the size of its preview. + Future initialize(int cameraId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.camera_windows.CameraApi.initialize$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as PlatformSize?)!; + } + } + + /// Disposes a camera that is no longer in use. + Future dispose(int cameraId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.camera_windows.CameraApi.dispose$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Takes a picture with the given camera, and returns the path to the + /// resulting file. + Future takePicture(int cameraId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.camera_windows.CameraApi.takePicture$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as String?)!; + } + } + + /// Starts recording video with the given camera. + Future startVideoRecording( + int cameraId, PlatformVideoCaptureOptions options) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.camera_windows.CameraApi.startVideoRecording$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([cameraId, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Finishes recording video with the given camera, and returns the path to + /// the resulting file. + Future stopVideoRecording(int cameraId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.camera_windows.CameraApi.stopVideoRecording$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as String?)!; + } + } + + /// Starts the preview stream for the given camera. + Future pausePreview(int cameraId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.camera_windows.CameraApi.pausePreview$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Resumes the preview stream for the given camera. + Future resumePreview(int cameraId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.camera_windows.CameraApi.resumePreview$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } +} diff --git a/packages/camera/camera_windows/pigeons/copyright.txt b/packages/camera/camera_windows/pigeons/copyright.txt new file mode 100644 index 000000000000..1236b63caf3a --- /dev/null +++ b/packages/camera/camera_windows/pigeons/copyright.txt @@ -0,0 +1,3 @@ +Copyright 2013 The Flutter Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. diff --git a/packages/camera/camera_windows/pigeons/messages.dart b/packages/camera/camera_windows/pigeons/messages.dart new file mode 100644 index 000000000000..fe5a86af0582 --- /dev/null +++ b/packages/camera/camera_windows/pigeons/messages.dart @@ -0,0 +1,87 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + cppOptions: CppOptions(namespace: 'camera_windows'), + cppHeaderOut: 'windows/messages.g.h', + cppSourceOut: 'windows/messages.g.cpp', + copyrightHeader: 'pigeons/copyright.txt', +)) + +/// Pigeon version of platform interface's ResolutionPreset. +enum PlatformResolutionPreset { low, medium, high, veryHigh, ultraHigh, max } + +/// Pigeon version of MediaSettings. +class PlatformMediaSettings { + PlatformMediaSettings({ + required this.resolutionPreset, + required this.framesPerSecond, + required this.videoBitrate, + required this.audioBitrate, + required this.enableAudio, + }); + + final PlatformResolutionPreset resolutionPreset; + final int? framesPerSecond; + final int? videoBitrate; + final int? audioBitrate; + final bool enableAudio; +} + +/// A representation of a size from the native camera APIs. +class PlatformSize { + PlatformSize({required this.width, required this.height}); + + final double width; + final double height; +} + +/// Pigeon version of the relevant subset of VideoCaptureOptions. +class PlatformVideoCaptureOptions { + PlatformVideoCaptureOptions({required this.maxDurationMilliseconds}); + + final int? maxDurationMilliseconds; +} + +@HostApi() +abstract class CameraApi { + /// Returns the names of all of the available capture devices. + List getAvailableCameras(); + + /// Creates a camera instance for the given device name and settings. + @async + int create(String cameraName, PlatformMediaSettings settings); + + /// Initializes a camera, and returns the size of its preview. + @async + PlatformSize initialize(int cameraId); + + /// Disposes a camera that is no longer in use. + void dispose(int cameraId); + + /// Takes a picture with the given camera, and returns the path to the + /// resulting file. + @async + String takePicture(int cameraId); + + /// Starts recording video with the given camera. + @async + void startVideoRecording(int cameraId, PlatformVideoCaptureOptions options); + + /// Finishes recording video with the given camera, and returns the path to + /// the resulting file. + @async + String stopVideoRecording(int cameraId); + + /// Starts the preview stream for the given camera. + @async + void pausePreview(int cameraId); + + /// Resumes the preview stream for the given camera. + @async + void resumePreview(int cameraId); +} diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 6fc2e8a9c7da..492adffbb12d 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.2 +version: 0.2.3 environment: sdk: ^3.2.0 @@ -25,8 +25,11 @@ dependencies: dev_dependencies: async: ^2.5.0 + build_runner: ^2.4.9 flutter_test: sdk: flutter + mockito: 5.4.4 + pigeon: ^20.0.0 topics: - camera diff --git a/packages/camera/camera_windows/test/camera_windows_test.dart b/packages/camera/camera_windows/test/camera_windows_test.dart index 228cae2c952b..887f2eb67689 100644 --- a/packages/camera/camera_windows/test/camera_windows_test.dart +++ b/packages/camera/camera_windows/test/camera_windows_test.dart @@ -5,14 +5,17 @@ import 'package:async/async.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:camera_windows/camera_windows.dart'; +import 'package:camera_windows/src/messages.g.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; -import './utils/method_channel_mock.dart'; +import 'camera_windows_test.mocks.dart'; +@GenerateNiceMocks(>[MockSpec()]) void main() { - const String pluginChannelName = 'plugins.flutter.io/camera_windows'; TestWidgetsFlutterBinding.ensureInitialized(); group('$CameraWindows()', () { @@ -24,20 +27,15 @@ void main() { group('Creation, Initialization & Disposal Tests', () { test('Should send creation data and receive back a camera id', () async { // Arrange - final MethodChannelMock cameraMockChannel = MethodChannelMock( - channelName: pluginChannelName, - methods: { - 'create': { - 'cameraId': 1, - 'imageFormatGroup': 'unknown', - } - }); - final CameraWindows plugin = CameraWindows(); + final MockCameraApi mockApi = MockCameraApi(); + when(mockApi.create(any, any)).thenAnswer((_) async => 1); + final CameraWindows plugin = CameraWindows(api: mockApi); + const String cameraName = 'Test'; // Act final int cameraId = await plugin.createCameraWithSettings( const CameraDescription( - name: 'Test', + name: cameraName, lensDirection: CameraLensDirection.front, sensorOrientation: 0), const MediaSettings( @@ -49,19 +47,13 @@ void main() { ); // Assert - expect(cameraMockChannel.log, [ - isMethodCall( - 'create', - arguments: { - 'cameraName': 'Test', - 'resolutionPreset': 'low', - 'fps': 15, - 'videoBitrate': 200000, - 'audioBitrate': 32000, - 'enableAudio': false - }, - ), - ]); + final VerificationResult verification = + verify(mockApi.create(captureAny, captureAny)); + expect(verification.captured[0], cameraName); + final PlatformMediaSettings? settings = + verification.captured[1] as PlatformMediaSettings?; + expect(settings, isNotNull); + expect(settings?.resolutionPreset, PlatformResolutionPreset.low); expect(cameraId, 1); }); @@ -69,38 +61,31 @@ void main() { 'Should throw CameraException when create throws a PlatformException', () { // Arrange - MethodChannelMock( - channelName: pluginChannelName, - methods: { - 'create': PlatformException( - code: 'TESTING_ERROR_CODE', - message: 'Mock error message used during testing.', - ) - }); - final CameraWindows plugin = CameraWindows(); + const String exceptionCode = 'TESTING_ERROR_CODE'; + const String exceptionMessage = + 'Mock error message used during testing.'; + final MockCameraApi mockApi = MockCameraApi(); + when(mockApi.create(any, any)).thenAnswer((_) async { + throw PlatformException( + code: exceptionCode, message: exceptionMessage); + }); + final CameraWindows camera = CameraWindows(api: mockApi); // Act expect( - () => plugin.createCameraWithSettings( + () => camera.createCamera( const CameraDescription( name: 'Test', lensDirection: CameraLensDirection.back, sensorOrientation: 0, ), - const MediaSettings( - resolutionPreset: ResolutionPreset.low, - fps: 15, - videoBitrate: 200000, - audioBitrate: 32000, - enableAudio: true, - ), + ResolutionPreset.high, ), throwsA( isA() - .having( - (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.code, 'code', exceptionCode) .having((CameraException e) => e.description, 'description', - 'Mock error message used during testing.'), + exceptionMessage), ), ); }); @@ -109,16 +94,15 @@ void main() { 'Should throw CameraException when initialize throws a PlatformException', () { // Arrange - MethodChannelMock( - channelName: pluginChannelName, - methods: { - 'initialize': PlatformException( - code: 'TESTING_ERROR_CODE', - message: 'Mock error message used during testing.', - ) - }, - ); - final CameraWindows plugin = CameraWindows(); + const String exceptionCode = 'TESTING_ERROR_CODE'; + const String exceptionMessage = + 'Mock error message used during testing.'; + final MockCameraApi mockApi = MockCameraApi(); + when(mockApi.initialize(any)).thenAnswer((_) async { + throw PlatformException( + code: exceptionCode, message: exceptionMessage); + }); + final CameraWindows plugin = CameraWindows(api: mockApi); // Act expect( @@ -139,19 +123,10 @@ void main() { test('Should send initialization data', () async { // Arrange - final MethodChannelMock cameraMockChannel = MethodChannelMock( - channelName: pluginChannelName, - methods: { - 'create': { - 'cameraId': 1, - 'imageFormatGroup': 'unknown', - }, - 'initialize': { - 'previewWidth': 1920.toDouble(), - 'previewHeight': 1080.toDouble() - }, - }); - final CameraWindows plugin = CameraWindows(); + final MockCameraApi mockApi = MockCameraApi(); + when(mockApi.initialize(any)) + .thenAnswer((_) async => PlatformSize(width: 1920, height: 1080)); + final CameraWindows plugin = CameraWindows(api: mockApi); final int cameraId = await plugin.createCameraWithSettings( const CameraDescription( name: 'Test', @@ -171,30 +146,17 @@ void main() { await plugin.initializeCamera(cameraId); // Assert - expect(cameraId, 1); - expect(cameraMockChannel.log, [ - anything, - isMethodCall( - 'initialize', - arguments: {'cameraId': 1}, - ), - ]); + final VerificationResult verification = + verify(mockApi.initialize(captureAny)); + expect(verification.captured[0], cameraId); }); test('Should send a disposal call on dispose', () async { // Arrange - final MethodChannelMock cameraMockChannel = MethodChannelMock( - channelName: pluginChannelName, - methods: { - 'create': {'cameraId': 1}, - 'initialize': { - 'previewWidth': 1920.toDouble(), - 'previewHeight': 1080.toDouble() - }, - 'dispose': {'cameraId': 1} - }); - - final CameraWindows plugin = CameraWindows(); + final MockCameraApi mockApi = MockCameraApi(); + when(mockApi.initialize(any)) + .thenAnswer((_) async => PlatformSize(width: 1920, height: 1080)); + final CameraWindows plugin = CameraWindows(api: mockApi); final int cameraId = await plugin.createCameraWithSettings( const CameraDescription( name: 'Test', @@ -215,15 +177,9 @@ void main() { await plugin.dispose(cameraId); // Assert - expect(cameraId, 1); - expect(cameraMockChannel.log, [ - anything, - anything, - isMethodCall( - 'dispose', - arguments: {'cameraId': 1}, - ), - ]); + final VerificationResult verification = + verify(mockApi.dispose(captureAny)); + expect(verification.captured[0], cameraId); }); }); @@ -231,18 +187,11 @@ void main() { late CameraWindows plugin; late int cameraId; setUp(() async { - MethodChannelMock( - channelName: pluginChannelName, - methods: { - 'create': {'cameraId': 1}, - 'initialize': { - 'previewWidth': 1920.toDouble(), - 'previewHeight': 1080.toDouble() - }, - }, - ); - - plugin = CameraWindows(); + final MockCameraApi mockApi = MockCameraApi(); + when(mockApi.create(any, any)).thenAnswer((_) async => 1); + when(mockApi.initialize(any)) + .thenAnswer((_) async => PlatformSize(width: 1920, height: 1080)); + plugin = CameraWindows(api: mockApi); cameraId = await plugin.createCameraWithSettings( const CameraDescription( name: 'Test', @@ -313,21 +262,16 @@ void main() { }); group('Function Tests', () { + late MockCameraApi mockApi; late CameraWindows plugin; late int cameraId; setUp(() async { - MethodChannelMock( - channelName: pluginChannelName, - methods: { - 'create': {'cameraId': 1}, - 'initialize': { - 'previewWidth': 1920.toDouble(), - 'previewHeight': 1080.toDouble() - }, - }, - ); - plugin = CameraWindows(); + mockApi = MockCameraApi(); + when(mockApi.create(any, any)).thenAnswer((_) async => 1); + when(mockApi.initialize(any)) + .thenAnswer((_) async => PlatformSize(width: 1920, height: 1080)); + plugin = CameraWindows(api: mockApi); cameraId = await plugin.createCameraWithSettings( const CameraDescription( name: 'Test', @@ -343,46 +287,29 @@ void main() { ), ); await plugin.initializeCamera(cameraId); + clearInteractions(mockApi); }); test('Should fetch CameraDescription instances for available cameras', () async { // Arrange - final List returnData = [ - { - 'name': 'Test 1', - 'lensFacing': 'front', - 'sensorOrientation': 1 - }, - { - 'name': 'Test 2', - 'lensFacing': 'back', - 'sensorOrientation': 2 - } + final List returnData = [ + 'Test 1', + 'Test 2', ]; - final MethodChannelMock channel = MethodChannelMock( - channelName: pluginChannelName, - methods: {'availableCameras': returnData}, - ); + when(mockApi.getAvailableCameras()).thenAnswer((_) async => returnData); // Act final List cameras = await plugin.availableCameras(); // Assert - expect(channel.log, [ - isMethodCall('availableCameras', arguments: null), - ]); expect(cameras.length, returnData.length); for (int i = 0; i < returnData.length; i++) { - final Map typedData = - (returnData[i] as Map).cast(); - final CameraDescription cameraDescription = CameraDescription( - name: typedData['name']! as String, - lensDirection: plugin - .parseCameraLensDirection(typedData['lensFacing']! as String), - sensorOrientation: typedData['sensorOrientation']! as int, - ); - expect(cameras[i], cameraDescription); + expect(cameras[i].name, returnData[i]); + // This value isn't provided by the platform, so is hard-coded to front. + expect(cameras[i].lensDirection, CameraLensDirection.front); + // This value isn't provided by the platform, so is hard-coded to 0. + expect(cameras[i].sensorOrientation, 0); } }); @@ -390,14 +317,10 @@ void main() { 'Should throw CameraException when availableCameras throws a PlatformException', () { // Arrange - MethodChannelMock( - channelName: pluginChannelName, - methods: { - 'availableCameras': PlatformException( - code: 'TESTING_ERROR_CODE', - message: 'Mock error message used during testing.', - ) - }); + const String code = 'TESTING_ERROR_CODE'; + const String message = 'Mock error message used during testing.'; + when(mockApi.getAvailableCameras()).thenAnswer( + (_) async => throw PlatformException(code: code, message: message)); // Act expect( @@ -414,65 +337,34 @@ void main() { test('Should take a picture and return an XFile instance', () async { // Arrange - final MethodChannelMock channel = MethodChannelMock( - channelName: pluginChannelName, - methods: {'takePicture': '/test/path.jpg'}); + const String stubPath = '/test/path.jpg'; + when(mockApi.takePicture(any)).thenAnswer((_) async => stubPath); // Act final XFile file = await plugin.takePicture(cameraId); // Assert - expect(channel.log, [ - isMethodCall('takePicture', arguments: { - 'cameraId': cameraId, - }), - ]); expect(file.path, '/test/path.jpg'); }); - test('Should prepare for video recording', () async { - // Arrange - final MethodChannelMock channel = MethodChannelMock( - channelName: pluginChannelName, - methods: {'prepareForVideoRecording': null}, - ); - + test('prepare for video recording should no-op', () async { // Act await plugin.prepareForVideoRecording(); // Assert - expect(channel.log, [ - isMethodCall('prepareForVideoRecording', arguments: null), - ]); + verifyNoMoreInteractions(mockApi); }); test('Should start recording a video', () async { - // Arrange - final MethodChannelMock channel = MethodChannelMock( - channelName: pluginChannelName, - methods: {'startVideoRecording': null}, - ); - // Act await plugin.startVideoRecording(cameraId); // Assert - expect(channel.log, [ - isMethodCall('startVideoRecording', arguments: { - 'cameraId': cameraId, - 'maxVideoDuration': null, - }), - ]); + verify(mockApi.startVideoRecording(any, any)); }); test('Should pass maxVideoDuration when starting recording a video', () async { - // Arrange - final MethodChannelMock channel = MethodChannelMock( - channelName: pluginChannelName, - methods: {'startVideoRecording': null}, - ); - // Act await plugin.startVideoRecording( cameraId, @@ -480,12 +372,11 @@ void main() { ); // Assert - expect(channel.log, [ - isMethodCall('startVideoRecording', arguments: { - 'cameraId': cameraId, - 'maxVideoDuration': 10000 - }), - ]); + final VerificationResult verification = + verify(mockApi.startVideoRecording(any, captureAny)); + final PlatformVideoCaptureOptions? options = + verification.captured[0] as PlatformVideoCaptureOptions?; + expect(options?.maxDurationMilliseconds, 10000); }); test('capturing fails if trying to stream', () async { @@ -499,20 +390,13 @@ void main() { test('Should stop a video recording and return the file', () async { // Arrange - final MethodChannelMock channel = MethodChannelMock( - channelName: pluginChannelName, - methods: {'stopVideoRecording': '/test/path.mp4'}, - ); + const String stubPath = '/test/path.mp4'; + when(mockApi.stopVideoRecording(any)).thenAnswer((_) async => stubPath); // Act final XFile file = await plugin.stopVideoRecording(cameraId); // Assert - expect(channel.log, [ - isMethodCall('stopVideoRecording', arguments: { - 'cameraId': cameraId, - }), - ]); expect(file.path, '/test/path.mp4'); }); @@ -624,7 +508,7 @@ void main() { }); test('Should throw UnimplementedError when handling unknown method', () { - final CameraWindows plugin = CameraWindows(); + final CameraWindows plugin = CameraWindows(api: mockApi); expect( () => plugin.handleCameraMethodCall( @@ -677,37 +561,23 @@ void main() { }); test('Should pause the camera preview', () async { - // Arrange - final MethodChannelMock channel = MethodChannelMock( - channelName: pluginChannelName, - methods: {'pausePreview': null}, - ); - // Act await plugin.pausePreview(cameraId); // Assert - expect(channel.log, [ - isMethodCall('pausePreview', - arguments: {'cameraId': cameraId}), - ]); + final VerificationResult verification = + verify(mockApi.pausePreview(captureAny)); + expect(verification.captured[0], cameraId); }); test('Should resume the camera preview', () async { - // Arrange - final MethodChannelMock channel = MethodChannelMock( - channelName: pluginChannelName, - methods: {'resumePreview': null}, - ); - // Act await plugin.resumePreview(cameraId); // Assert - expect(channel.log, [ - isMethodCall('resumePreview', - arguments: {'cameraId': cameraId}), - ]); + final VerificationResult verification = + verify(mockApi.resumePreview(captureAny)); + expect(verification.captured[0], cameraId); }); }); }); diff --git a/packages/camera/camera_windows/test/camera_windows_test.mocks.dart b/packages/camera/camera_windows/test/camera_windows_test.mocks.dart new file mode 100644 index 000000000000..fe72c8165aa1 --- /dev/null +++ b/packages/camera/camera_windows/test/camera_windows_test.mocks.dart @@ -0,0 +1,181 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in camera_windows/test/camera_windows_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:camera_windows/src/messages.g.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i4; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakePlatformSize_0 extends _i1.SmartFake implements _i2.PlatformSize { + _FakePlatformSize_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [CameraApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCameraApi extends _i1.Mock implements _i2.CameraApi { + @override + _i3.Future> getAvailableCameras() => (super.noSuchMethod( + Invocation.method( + #getAvailableCameras, + [], + ), + returnValue: _i3.Future>.value([]), + returnValueForMissingStub: _i3.Future>.value([]), + ) as _i3.Future>); + + @override + _i3.Future create( + String? cameraName, + _i2.PlatformMediaSettings? settings, + ) => + (super.noSuchMethod( + Invocation.method( + #create, + [ + cameraName, + settings, + ], + ), + returnValue: _i3.Future.value(0), + returnValueForMissingStub: _i3.Future.value(0), + ) as _i3.Future); + + @override + _i3.Future<_i2.PlatformSize> initialize(int? cameraId) => (super.noSuchMethod( + Invocation.method( + #initialize, + [cameraId], + ), + returnValue: _i3.Future<_i2.PlatformSize>.value(_FakePlatformSize_0( + this, + Invocation.method( + #initialize, + [cameraId], + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.PlatformSize>.value(_FakePlatformSize_0( + this, + Invocation.method( + #initialize, + [cameraId], + ), + )), + ) as _i3.Future<_i2.PlatformSize>); + + @override + _i3.Future dispose(int? cameraId) => (super.noSuchMethod( + Invocation.method( + #dispose, + [cameraId], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future takePicture(int? cameraId) => (super.noSuchMethod( + Invocation.method( + #takePicture, + [cameraId], + ), + returnValue: _i3.Future.value(_i4.dummyValue( + this, + Invocation.method( + #takePicture, + [cameraId], + ), + )), + returnValueForMissingStub: + _i3.Future.value(_i4.dummyValue( + this, + Invocation.method( + #takePicture, + [cameraId], + ), + )), + ) as _i3.Future); + + @override + _i3.Future startVideoRecording( + int? cameraId, + _i2.PlatformVideoCaptureOptions? options, + ) => + (super.noSuchMethod( + Invocation.method( + #startVideoRecording, + [ + cameraId, + options, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future stopVideoRecording(int? cameraId) => (super.noSuchMethod( + Invocation.method( + #stopVideoRecording, + [cameraId], + ), + returnValue: _i3.Future.value(_i4.dummyValue( + this, + Invocation.method( + #stopVideoRecording, + [cameraId], + ), + )), + returnValueForMissingStub: + _i3.Future.value(_i4.dummyValue( + this, + Invocation.method( + #stopVideoRecording, + [cameraId], + ), + )), + ) as _i3.Future); + + @override + _i3.Future pausePreview(int? cameraId) => (super.noSuchMethod( + Invocation.method( + #pausePreview, + [cameraId], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future resumePreview(int? cameraId) => (super.noSuchMethod( + Invocation.method( + #resumePreview, + [cameraId], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); +} diff --git a/packages/camera/camera_windows/test/utils/method_channel_mock.dart b/packages/camera/camera_windows/test/utils/method_channel_mock.dart deleted file mode 100644 index 357eec370bc1..000000000000 --- a/packages/camera/camera_windows/test/utils/method_channel_mock.dart +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -/// A mock [MethodChannel] implementation for use in tests. -class MethodChannelMock { - /// Creates a new instance with the specified channel name. - /// - /// This method channel will handle all method invocations specified by - /// returning the value mapped to the method name key. If a delay is - /// specified, results are returned after the delay has elapsed. - MethodChannelMock({ - required String channelName, - this.delay, - required this.methods, - }) : methodChannel = MethodChannel(channelName) { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, _handler); - } - - final Duration? delay; - final MethodChannel methodChannel; - final Map methods; - final List log = []; - - Future _handler(MethodCall methodCall) async { - log.add(methodCall); - - if (!methods.containsKey(methodCall.method)) { - throw MissingPluginException('No TEST implementation found for method ' - '${methodCall.method} on channel ${methodChannel.name}'); - } - - return Future.delayed(delay ?? Duration.zero, () { - final dynamic result = methods[methodCall.method]; - if (result is Exception) { - throw result; - } - - return Future.value(result); - }); - } -} diff --git a/packages/camera/camera_windows/windows/CMakeLists.txt b/packages/camera/camera_windows/windows/CMakeLists.txt index 534372fd31b0..e209a22ec0fe 100644 --- a/packages/camera/camera_windows/windows/CMakeLists.txt +++ b/packages/camera/camera_windows/windows/CMakeLists.txt @@ -22,6 +22,8 @@ list(APPEND PLUGIN_SOURCES "string_utils.cpp" "capture_device_info.h" "capture_device_info.cpp" + "messages.g.h" + "messages.g.cpp" "preview_handler.h" "preview_handler.cpp" "record_handler.h" diff --git a/packages/camera/camera_windows/windows/camera.cpp b/packages/camera/camera_windows/windows/camera.cpp index 94a0ed89ac46..023d1377b569 100644 --- a/packages/camera/camera_windows/windows/camera.cpp +++ b/packages/camera/camera_windows/windows/camera.cpp @@ -49,34 +49,63 @@ CameraImpl::~CameraImpl() { bool CameraImpl::InitCamera(flutter::TextureRegistrar* texture_registrar, flutter::BinaryMessenger* messenger, - ResolutionPreset resolution_preset, - const RecordSettings& record_settings) { + const PlatformMediaSettings& media_settings) { auto capture_controller_factory = std::make_unique(); return InitCamera(std::move(capture_controller_factory), texture_registrar, - messenger, resolution_preset, record_settings); + messenger, media_settings); } bool CameraImpl::InitCamera( std::unique_ptr capture_controller_factory, flutter::TextureRegistrar* texture_registrar, - flutter::BinaryMessenger* messenger, ResolutionPreset resolution_preset, - const RecordSettings& record_settings) { + flutter::BinaryMessenger* messenger, + const PlatformMediaSettings& media_settings) { assert(!device_id_.empty()); messenger_ = messenger; capture_controller_ = capture_controller_factory->CreateCaptureController(this); - return capture_controller_->InitCaptureDevice( - texture_registrar, device_id_, resolution_preset, record_settings); + return capture_controller_->InitCaptureDevice(texture_registrar, device_id_, + media_settings); } -bool CameraImpl::AddPendingResult( - PendingResultType type, std::unique_ptr> result) { +bool CameraImpl::AddPendingVoidResult( + PendingResultType type, + std::function reply)> result) { assert(result); + return AddPendingResult(type, result); +} + +bool CameraImpl::AddPendingIntResult( + PendingResultType type, + std::function reply)> result) { + assert(result); + return AddPendingResult(type, result); +} + +bool CameraImpl::AddPendingStringResult( + PendingResultType type, + std::function reply)> result) { + assert(result); + return AddPendingResult(type, result); +} + +bool CameraImpl::AddPendingSizeResult( + PendingResultType type, + std::function reply)> result) { + assert(result); + return AddPendingResult(type, result); +} +bool CameraImpl::AddPendingResult(PendingResultType type, + CameraImpl::AsyncResult result) { auto it = pending_results_.find(type); if (it != pending_results_.end()) { - result->Error("Duplicate request", "Method handler already called"); + std::visit( + [](auto&& r) { + r(FlutterError("Duplicate request", "Method handler already called")); + }, + result); return false; } @@ -84,29 +113,67 @@ bool CameraImpl::AddPendingResult( return true; } -std::unique_ptr> CameraImpl::GetPendingResultByType( +std::function reply)> +CameraImpl::GetPendingVoidResultByType(PendingResultType type) { + std::optional result = GetPendingResultByType(type); + if (!result) { + return nullptr; + } + return std::get)>>( + result.value()); +} + +std::function reply)> +CameraImpl::GetPendingIntResultByType(PendingResultType type) { + std::optional result = GetPendingResultByType(type); + if (!result) { + return nullptr; + } + return std::get)>>(result.value()); +} + +std::function reply)> +CameraImpl::GetPendingStringResultByType(PendingResultType type) { + std::optional result = GetPendingResultByType(type); + if (!result) { + return nullptr; + } + return std::get)>>(result.value()); +} + +std::function reply)> +CameraImpl::GetPendingSizeResultByType(PendingResultType type) { + std::optional result = GetPendingResultByType(type); + if (!result) { + return nullptr; + } + return std::get)>>(result.value()); +} + +std::optional CameraImpl::GetPendingResultByType( PendingResultType type) { auto it = pending_results_.find(type); if (it == pending_results_.end()) { - return nullptr; + return std::nullopt; } - auto result = std::move(it->second); + CameraImpl::AsyncResult result = std::move(it->second); pending_results_.erase(it); return result; } bool CameraImpl::HasPendingResultByType(PendingResultType type) const { auto it = pending_results_.find(type); - if (it == pending_results_.end()) { - return false; - } - return it->second != nullptr; + return it != pending_results_.end(); } void CameraImpl::SendErrorForPendingResults(const std::string& error_code, const std::string& description) { for (const auto& pending_result : pending_results_) { - pending_result.second->Error(error_code, description); + std::visit( + [&error_code, &description](auto&& result) { + result(FlutterError(error_code, description)); + }, + pending_result.second); } pending_results_.clear(); } @@ -133,126 +200,128 @@ void CameraImpl::OnCreateCaptureEngineSucceeded(int64_t texture_id) { // Use texture id as camera id camera_id_ = texture_id; auto pending_result = - GetPendingResultByType(PendingResultType::kCreateCamera); + GetPendingIntResultByType(PendingResultType::kCreateCamera); if (pending_result) { - pending_result->Success(EncodableMap( - {{EncodableValue("cameraId"), EncodableValue(texture_id)}})); + pending_result(texture_id); } } void CameraImpl::OnCreateCaptureEngineFailed(CameraResult result, const std::string& error) { auto pending_result = - GetPendingResultByType(PendingResultType::kCreateCamera); + GetPendingIntResultByType(PendingResultType::kCreateCamera); if (pending_result) { std::string error_code = GetErrorCode(result); - pending_result->Error(error_code, error); + pending_result(FlutterError(error_code, error)); } } void CameraImpl::OnStartPreviewSucceeded(int32_t width, int32_t height) { - auto pending_result = GetPendingResultByType(PendingResultType::kInitialize); + auto pending_result = + GetPendingSizeResultByType(PendingResultType::kInitialize); if (pending_result) { - pending_result->Success(EncodableValue(EncodableMap({ - {EncodableValue("previewWidth"), - EncodableValue(static_cast(width))}, - {EncodableValue("previewHeight"), - EncodableValue(static_cast(height))}, - }))); + pending_result( + PlatformSize(static_cast(width), static_cast(height))); } }; void CameraImpl::OnStartPreviewFailed(CameraResult result, const std::string& error) { - auto pending_result = GetPendingResultByType(PendingResultType::kInitialize); + auto pending_result = + GetPendingSizeResultByType(PendingResultType::kInitialize); if (pending_result) { std::string error_code = GetErrorCode(result); - pending_result->Error(error_code, error); + pending_result(FlutterError(error_code, error)); } }; void CameraImpl::OnResumePreviewSucceeded() { auto pending_result = - GetPendingResultByType(PendingResultType::kResumePreview); + GetPendingVoidResultByType(PendingResultType::kResumePreview); if (pending_result) { - pending_result->Success(); + pending_result(std::nullopt); } } void CameraImpl::OnResumePreviewFailed(CameraResult result, const std::string& error) { auto pending_result = - GetPendingResultByType(PendingResultType::kResumePreview); + GetPendingVoidResultByType(PendingResultType::kResumePreview); if (pending_result) { std::string error_code = GetErrorCode(result); - pending_result->Error(error_code, error); + pending_result(FlutterError(error_code, error)); } } void CameraImpl::OnPausePreviewSucceeded() { auto pending_result = - GetPendingResultByType(PendingResultType::kPausePreview); + GetPendingVoidResultByType(PendingResultType::kPausePreview); if (pending_result) { - pending_result->Success(); + pending_result(std::nullopt); } } void CameraImpl::OnPausePreviewFailed(CameraResult result, const std::string& error) { auto pending_result = - GetPendingResultByType(PendingResultType::kPausePreview); + GetPendingVoidResultByType(PendingResultType::kPausePreview); if (pending_result) { std::string error_code = GetErrorCode(result); - pending_result->Error(error_code, error); + pending_result(FlutterError(error_code, error)); } } void CameraImpl::OnStartRecordSucceeded() { - auto pending_result = GetPendingResultByType(PendingResultType::kStartRecord); + auto pending_result = + GetPendingVoidResultByType(PendingResultType::kStartRecord); if (pending_result) { - pending_result->Success(); + pending_result(std::nullopt); } }; void CameraImpl::OnStartRecordFailed(CameraResult result, const std::string& error) { - auto pending_result = GetPendingResultByType(PendingResultType::kStartRecord); + auto pending_result = + GetPendingVoidResultByType(PendingResultType::kStartRecord); if (pending_result) { std::string error_code = GetErrorCode(result); - pending_result->Error(error_code, error); + pending_result(FlutterError(error_code, error)); } }; void CameraImpl::OnStopRecordSucceeded(const std::string& file_path) { - auto pending_result = GetPendingResultByType(PendingResultType::kStopRecord); + auto pending_result = + GetPendingStringResultByType(PendingResultType::kStopRecord); if (pending_result) { - pending_result->Success(EncodableValue(file_path)); + pending_result(file_path); } }; void CameraImpl::OnStopRecordFailed(CameraResult result, const std::string& error) { - auto pending_result = GetPendingResultByType(PendingResultType::kStopRecord); + auto pending_result = + GetPendingStringResultByType(PendingResultType::kStopRecord); if (pending_result) { std::string error_code = GetErrorCode(result); - pending_result->Error(error_code, error); + pending_result(FlutterError(error_code, error)); } }; void CameraImpl::OnTakePictureSucceeded(const std::string& file_path) { - auto pending_result = GetPendingResultByType(PendingResultType::kTakePicture); + auto pending_result = + GetPendingStringResultByType(PendingResultType::kTakePicture); if (pending_result) { - pending_result->Success(EncodableValue(file_path)); + pending_result(file_path); } }; void CameraImpl::OnTakePictureFailed(CameraResult result, const std::string& error) { auto pending_take_picture_result = - GetPendingResultByType(PendingResultType::kTakePicture); + GetPendingStringResultByType(PendingResultType::kTakePicture); if (pending_take_picture_result) { std::string error_code = GetErrorCode(result); - pending_take_picture_result->Error(error_code, error); + pending_take_picture_result(FlutterError(error_code, error)); } }; diff --git a/packages/camera/camera_windows/windows/camera.h b/packages/camera/camera_windows/windows/camera.h index 50ec6a75e74b..14084f5e4847 100644 --- a/packages/camera/camera_windows/windows/camera.h +++ b/packages/camera/camera_windows/windows/camera.h @@ -10,6 +10,7 @@ #include #include +#include #include "capture_controller.h" @@ -50,11 +51,33 @@ class Camera : public CaptureControllerListener { // Tests if this camera has the specified camera ID. virtual bool HasCameraId(int64_t camera_id) const = 0; - // Adds a pending result. + // Adds a pending result for a void return. // // Returns an error result if the result has already been added. - virtual bool AddPendingResult(PendingResultType type, - std::unique_ptr> result) = 0; + virtual bool AddPendingVoidResult( + PendingResultType type, + std::function reply)> result) = 0; + + // Adds a pending result for a string return. + // + // Returns an error result if the result has already been added. + virtual bool AddPendingIntResult( + PendingResultType type, + std::function reply)> result) = 0; + + // Adds a pending result for a string return. + // + // Returns an error result if the result has already been added. + virtual bool AddPendingStringResult( + PendingResultType type, + std::function reply)> result) = 0; + + // Adds a pending result for a size return. + // + // Returns an error result if the result has already been added. + virtual bool AddPendingSizeResult( + PendingResultType type, + std::function reply)> result) = 0; // Checks if a pending result of the specified type already exists. virtual bool HasPendingResultByType(PendingResultType type) const = 0; @@ -68,8 +91,7 @@ class Camera : public CaptureControllerListener { // Returns false if initialization fails. virtual bool InitCamera(flutter::TextureRegistrar* texture_registrar, flutter::BinaryMessenger* messenger, - ResolutionPreset resolution_preset, - const RecordSettings& record_settings) = 0; + const PlatformMediaSettings& media_settings) = 0; }; // Concrete implementation of the |Camera| interface. @@ -121,16 +143,25 @@ class CameraImpl : public Camera { bool HasCameraId(int64_t camera_id) const override { return camera_id_ == camera_id; } - bool AddPendingResult(PendingResultType type, - std::unique_ptr> result) override; + bool AddPendingVoidResult( + PendingResultType type, + std::function reply)> result) override; + bool AddPendingIntResult( + PendingResultType type, + std::function reply)> result) override; + bool AddPendingStringResult( + PendingResultType type, + std::function reply)> result) override; + bool AddPendingSizeResult( + PendingResultType type, + std::function reply)> result) override; bool HasPendingResultByType(PendingResultType type) const override; camera_windows::CaptureController* GetCaptureController() override { return capture_controller_.get(); } bool InitCamera(flutter::TextureRegistrar* texture_registrar, flutter::BinaryMessenger* messenger, - ResolutionPreset resolution_preset, - const RecordSettings& record_settings) override; + const PlatformMediaSettings& media_settings) override; // Initializes the camera and its associated capture controller. // @@ -141,10 +172,17 @@ class CameraImpl : public Camera { bool InitCamera( std::unique_ptr capture_controller_factory, flutter::TextureRegistrar* texture_registrar, - flutter::BinaryMessenger* messenger, ResolutionPreset resolution_preset, - const RecordSettings& record_settings); + flutter::BinaryMessenger* messenger, + const PlatformMediaSettings& media_settings); private: + // A generic type for any pending asyncronous result. + using AsyncResult = + std::variant reply)>, + std::function reply)>, + std::function reply)>, + std::function reply)>>; + // Loops through all pending results and calls their error handler with given // error ID and description. Pending results are cleared in the process. // @@ -160,12 +198,45 @@ class CameraImpl : public Camera { // Initializes method channel instance and returns pointer it. MethodChannel<>* GetMethodChannel(); - // Finds pending result by type. - // Returns nullptr if type is not present. - std::unique_ptr> GetPendingResultByType( + // Finds pending void result by type. + // + // Returns an empty function if type is not present. + std::function reply)> + GetPendingVoidResultByType(PendingResultType type); + + // Finds pending int result by type. + // + // Returns an empty function if type is not present. + std::function reply)> GetPendingIntResultByType( + PendingResultType type); + + // Finds pending string result by type. + // + // Returns an empty function if type is not present. + std::function reply)> GetPendingStringResultByType( + PendingResultType type); + + // Finds pending size result by type. + // + // Returns an empty function if type is not present. + std::function reply)> GetPendingSizeResultByType( PendingResultType type); - std::map>> pending_results_; + // Finds pending result by type. + // + // Returns a nullopt if type is not present. + // + // This should not be used directly in most code, it's just a helper for the + // typed versions above. + std::optional GetPendingResultByType(PendingResultType type); + + // Adds pending result by type. + // + // This should not be used directly in most code, it's just a helper for the + // typed versions in the public interface. + bool AddPendingResult(PendingResultType type, AsyncResult result); + + std::map pending_results_; std::unique_ptr capture_controller_; std::unique_ptr> camera_channel_; flutter::BinaryMessenger* messenger_ = nullptr; diff --git a/packages/camera/camera_windows/windows/camera_plugin.cpp b/packages/camera/camera_windows/windows/camera_plugin.cpp index c99565dd49c8..5bbc951956a6 100644 --- a/packages/camera/camera_windows/windows/camera_plugin.cpp +++ b/packages/camera/camera_windows/windows/camera_plugin.cpp @@ -20,6 +20,7 @@ #include "capture_device_info.h" #include "com_heap_ptr.h" +#include "messages.g.h" #include "string_utils.h" namespace camera_windows { @@ -29,86 +30,9 @@ using flutter::EncodableValue; namespace { -// Channel events -constexpr char kChannelName[] = "plugins.flutter.io/camera_windows"; - -constexpr char kAvailableCamerasMethod[] = "availableCameras"; -constexpr char kCreateMethod[] = "create"; -constexpr char kInitializeMethod[] = "initialize"; -constexpr char kTakePictureMethod[] = "takePicture"; -constexpr char kStartVideoRecordingMethod[] = "startVideoRecording"; -constexpr char kStopVideoRecordingMethod[] = "stopVideoRecording"; -constexpr char kPausePreview[] = "pausePreview"; -constexpr char kResumePreview[] = "resumePreview"; -constexpr char kDisposeMethod[] = "dispose"; - -constexpr char kCameraNameKey[] = "cameraName"; -constexpr char kResolutionPresetKey[] = "resolutionPreset"; -constexpr char kFpsKey[] = "fps"; -constexpr char kVideoBitrateKey[] = "videoBitrate"; -constexpr char kAudioBitrateKey[] = "audioBitrate"; -constexpr char kEnableAudioKey[] = "enableAudio"; - -constexpr char kCameraIdKey[] = "cameraId"; -constexpr char kMaxVideoDurationKey[] = "maxVideoDuration"; - -constexpr char kResolutionPresetValueLow[] = "low"; -constexpr char kResolutionPresetValueMedium[] = "medium"; -constexpr char kResolutionPresetValueHigh[] = "high"; -constexpr char kResolutionPresetValueVeryHigh[] = "veryHigh"; -constexpr char kResolutionPresetValueUltraHigh[] = "ultraHigh"; -constexpr char kResolutionPresetValueMax[] = "max"; - const std::string kPictureCaptureExtension = "jpeg"; const std::string kVideoCaptureExtension = "mp4"; -// Looks for |key| in |map|, returning the associated value if it is present, or -// a nullptr if not. -const EncodableValue* ValueOrNull(const EncodableMap& map, const char* key) { - auto it = map.find(EncodableValue(key)); - if (it == map.end()) { - return nullptr; - } - return &(it->second); -} - -// Looks for |key| in |map|, returning the associated int64 value if it is -// present, or std::nullopt if not. -std::optional GetInt64ValueOrNull(const EncodableMap& map, - const char* key) { - auto value = ValueOrNull(map, key); - if (!value) { - return std::nullopt; - } - - if (std::holds_alternative(*value)) { - return static_cast(std::get(*value)); - } - auto val64 = std::get_if(value); - if (!val64) { - return std::nullopt; - } - return *val64; -} - -// Parses resolution preset argument to enum value. -ResolutionPreset ParseResolutionPreset(const std::string& resolution_preset) { - if (resolution_preset.compare(kResolutionPresetValueLow) == 0) { - return ResolutionPreset::kLow; - } else if (resolution_preset.compare(kResolutionPresetValueMedium) == 0) { - return ResolutionPreset::kMedium; - } else if (resolution_preset.compare(kResolutionPresetValueHigh) == 0) { - return ResolutionPreset::kHigh; - } else if (resolution_preset.compare(kResolutionPresetValueVeryHigh) == 0) { - return ResolutionPreset::kVeryHigh; - } else if (resolution_preset.compare(kResolutionPresetValueUltraHigh) == 0) { - return ResolutionPreset::kUltraHigh; - } else if (resolution_preset.compare(kResolutionPresetValueMax) == 0) { - return ResolutionPreset::kMax; - } - return ResolutionPreset::kAuto; -} - // Builds CaptureDeviceInfo object from given device holding device name and id. std::unique_ptr GetDeviceInfo(IMFActivate* device) { assert(device); @@ -195,17 +119,10 @@ std::optional GetFilePathForVideo() { // static void CameraPlugin::RegisterWithRegistrar( flutter::PluginRegistrarWindows* registrar) { - auto channel = std::make_unique>( - registrar->messenger(), kChannelName, - &flutter::StandardMethodCodec::GetInstance()); - std::unique_ptr plugin = std::make_unique( registrar->texture_registrar(), registrar->messenger()); - channel->SetMethodCallHandler( - [plugin_pointer = plugin.get()](const auto& call, auto result) { - plugin_pointer->HandleMethodCall(call, std::move(result)); - }); + CameraApi::SetUp(registrar->messenger(), plugin.get()); registrar->AddPlugin(std::move(plugin)); } @@ -225,66 +142,6 @@ CameraPlugin::CameraPlugin(flutter::TextureRegistrar* texture_registrar, CameraPlugin::~CameraPlugin() {} -void CameraPlugin::HandleMethodCall( - const flutter::MethodCall<>& method_call, - std::unique_ptr> result) { - const std::string& method_name = method_call.method_name(); - - if (method_name.compare(kAvailableCamerasMethod) == 0) { - return AvailableCamerasMethodHandler(std::move(result)); - } else if (method_name.compare(kCreateMethod) == 0) { - const auto* arguments = - std::get_if(method_call.arguments()); - assert(arguments); - - return CreateMethodHandler(*arguments, std::move(result)); - } else if (method_name.compare(kInitializeMethod) == 0) { - const auto* arguments = - std::get_if(method_call.arguments()); - assert(arguments); - - return this->InitializeMethodHandler(*arguments, std::move(result)); - } else if (method_name.compare(kTakePictureMethod) == 0) { - const auto* arguments = - std::get_if(method_call.arguments()); - assert(arguments); - - return TakePictureMethodHandler(*arguments, std::move(result)); - } else if (method_name.compare(kStartVideoRecordingMethod) == 0) { - const auto* arguments = - std::get_if(method_call.arguments()); - assert(arguments); - - return StartVideoRecordingMethodHandler(*arguments, std::move(result)); - } else if (method_name.compare(kStopVideoRecordingMethod) == 0) { - const auto* arguments = - std::get_if(method_call.arguments()); - assert(arguments); - - return StopVideoRecordingMethodHandler(*arguments, std::move(result)); - } else if (method_name.compare(kPausePreview) == 0) { - const auto* arguments = - std::get_if(method_call.arguments()); - assert(arguments); - - return PausePreviewMethodHandler(*arguments, std::move(result)); - } else if (method_name.compare(kResumePreview) == 0) { - const auto* arguments = - std::get_if(method_call.arguments()); - assert(arguments); - - return ResumePreviewMethodHandler(*arguments, std::move(result)); - } else if (method_name.compare(kDisposeMethod) == 0) { - const auto* arguments = - std::get_if(method_call.arguments()); - assert(arguments); - - return DisposeMethodHandler(*arguments, std::move(result)); - } else { - result->NotImplemented(); - } -} - Camera* CameraPlugin::GetCameraByDeviceId(std::string& device_id) { for (auto it = begin(cameras_); it != end(cameras_); ++it) { if ((*it)->HasDeviceId(device_id)) { @@ -312,20 +169,13 @@ void CameraPlugin::DisposeCameraByCameraId(int64_t camera_id) { } } -void CameraPlugin::AvailableCamerasMethodHandler( - std::unique_ptr> result) { +ErrorOr CameraPlugin::GetAvailableCameras() { // Enumerate devices. ComHeapPtr devices; UINT32 count = 0; if (!this->EnumerateVideoCaptureDeviceSources(&devices, &count)) { - result->Error("System error", "Failed to get available cameras"); - // No need to free devices here, cos allocation failed. - return; - } - - if (count == 0) { - result->Success(EncodableValue(EncodableList())); - return; + // No need to free devices here, since allocation failed. + return FlutterError("System error", "Failed to get available cameras"); } // Format found devices to the response. @@ -334,14 +184,10 @@ void CameraPlugin::AvailableCamerasMethodHandler( auto device_info = GetDeviceInfo(devices[i]); auto deviceName = device_info->GetUniqueDeviceName(); - devices_list.push_back(EncodableMap({ - {EncodableValue("name"), EncodableValue(deviceName)}, - {EncodableValue("lensFacing"), EncodableValue("front")}, - {EncodableValue("sensorOrientation"), EncodableValue(0)}, - })); + devices_list.push_back(EncodableValue(deviceName)); } - result->Success(std::move(EncodableValue(devices_list))); + return devices_list; } bool CameraPlugin::EnumerateVideoCaptureDeviceSources(IMFActivate*** devices, @@ -350,181 +196,120 @@ bool CameraPlugin::EnumerateVideoCaptureDeviceSources(IMFActivate*** devices, count); } -void CameraPlugin::CreateMethodHandler( - const EncodableMap& args, std::unique_ptr> result) { - // Parse enableAudio argument. - const auto* record_audio = - std::get_if(ValueOrNull(args, kEnableAudioKey)); - if (!record_audio) { - return result->Error("argument_error", - std::string(kEnableAudioKey) + " argument missing"); - } - - // Parse cameraName argument. - const auto* camera_name = - std::get_if(ValueOrNull(args, kCameraNameKey)); - if (!camera_name) { - return result->Error("argument_error", - std::string(kCameraNameKey) + " argument missing"); - } - +void CameraPlugin::Create(const std::string& camera_name, + const PlatformMediaSettings& settings, + std::function reply)> result) { auto device_info = std::make_unique(); - if (!device_info->ParseDeviceInfoFromCameraName(*camera_name)) { - return result->Error( - "camera_error", "Cannot parse argument " + std::string(kCameraNameKey)); + if (!device_info->ParseDeviceInfoFromCameraName(camera_name)) { + return result(FlutterError("camera_error", + "Cannot parse device info from " + camera_name)); } auto device_id = device_info->GetDeviceId(); if (GetCameraByDeviceId(device_id)) { - return result->Error("camera_error", - "Camera with given device id already exists. Existing " - "camera must be disposed before creating it again."); + return result( + FlutterError("camera_error", + "Camera with given device id already exists. Existing " + "camera must be disposed before creating it again.")); } std::unique_ptr camera = camera_factory_->CreateCamera(device_id); if (camera->HasPendingResultByType(PendingResultType::kCreateCamera)) { - return result->Error("camera_error", - "Pending camera creation request exists"); - } - - if (camera->AddPendingResult(PendingResultType::kCreateCamera, - std::move(result))) { - // Parse resolution preset argument. - const auto* resolution_preset_argument = - std::get_if(ValueOrNull(args, kResolutionPresetKey)); - ResolutionPreset resolution_preset; - if (resolution_preset_argument) { - resolution_preset = ParseResolutionPreset(*resolution_preset_argument); - } else { - resolution_preset = ResolutionPreset::kAuto; - } + return result( + FlutterError("camera_error", "Pending camera creation request exists")); + } - const auto* fps_argument = std::get_if(ValueOrNull(args, kFpsKey)); - const auto* video_bitrate_argument = - std::get_if(ValueOrNull(args, kVideoBitrateKey)); - const auto* audio_bitrate_argument = - std::get_if(ValueOrNull(args, kAudioBitrateKey)); - - RecordSettings record_settings; - record_settings.record_audio = *record_audio; - record_settings.fps = - fps_argument ? std::make_optional(*fps_argument) : std::nullopt; - record_settings.video_bitrate = - video_bitrate_argument ? std::make_optional(*video_bitrate_argument) - : std::nullopt; - record_settings.audio_bitrate = - audio_bitrate_argument ? std::make_optional(*audio_bitrate_argument) - : std::nullopt; - - bool initialized = camera->InitCamera(texture_registrar_, messenger_, - resolution_preset, record_settings); + if (camera->AddPendingIntResult(PendingResultType::kCreateCamera, + std::move(result))) { + bool initialized = + camera->InitCamera(texture_registrar_, messenger_, settings); if (initialized) { cameras_.push_back(std::move(camera)); } } } -void CameraPlugin::InitializeMethodHandler( - const EncodableMap& args, std::unique_ptr> result) { - auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); - if (!camera_id) { - return result->Error("argument_error", - std::string(kCameraIdKey) + " missing"); - } - - auto camera = GetCameraByCameraId(*camera_id); +void CameraPlugin::Initialize( + int64_t camera_id, + std::function reply)> result) { + auto camera = GetCameraByCameraId(camera_id); if (!camera) { - return result->Error("camera_error", "Camera not created"); + return result(FlutterError("camera_error", "Camera not created")); } if (camera->HasPendingResultByType(PendingResultType::kInitialize)) { - return result->Error("camera_error", - "Pending initialization request exists"); + return result( + FlutterError("camera_error", "Pending initialization request exists")); } - if (camera->AddPendingResult(PendingResultType::kInitialize, - std::move(result))) { + if (camera->AddPendingSizeResult(PendingResultType::kInitialize, + std::move(result))) { auto cc = camera->GetCaptureController(); assert(cc); cc->StartPreview(); } } -void CameraPlugin::PausePreviewMethodHandler( - const EncodableMap& args, std::unique_ptr> result) { - auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); - if (!camera_id) { - return result->Error("argument_error", - std::string(kCameraIdKey) + " missing"); - } - - auto camera = GetCameraByCameraId(*camera_id); +void CameraPlugin::PausePreview( + int64_t camera_id, + std::function reply)> result) { + auto camera = GetCameraByCameraId(camera_id); if (!camera) { - return result->Error("camera_error", "Camera not created"); + return result(FlutterError("camera_error", "Camera not created")); } if (camera->HasPendingResultByType(PendingResultType::kPausePreview)) { - return result->Error("camera_error", - "Pending pause preview request exists"); + return result( + FlutterError("camera_error", "Pending pause preview request exists")); } - if (camera->AddPendingResult(PendingResultType::kPausePreview, - std::move(result))) { + if (camera->AddPendingVoidResult(PendingResultType::kPausePreview, + std::move(result))) { auto cc = camera->GetCaptureController(); assert(cc); cc->PausePreview(); } } -void CameraPlugin::ResumePreviewMethodHandler( - const EncodableMap& args, std::unique_ptr> result) { - auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); - if (!camera_id) { - return result->Error("argument_error", - std::string(kCameraIdKey) + " missing"); - } - - auto camera = GetCameraByCameraId(*camera_id); +void CameraPlugin::ResumePreview( + int64_t camera_id, + std::function reply)> result) { + auto camera = GetCameraByCameraId(camera_id); if (!camera) { - return result->Error("camera_error", "Camera not created"); + return result(FlutterError("camera_error", "Camera not created")); } if (camera->HasPendingResultByType(PendingResultType::kResumePreview)) { - return result->Error("camera_error", - "Pending resume preview request exists"); + return result( + FlutterError("camera_error", "Pending resume preview request exists")); } - if (camera->AddPendingResult(PendingResultType::kResumePreview, - std::move(result))) { + if (camera->AddPendingVoidResult(PendingResultType::kResumePreview, + std::move(result))) { auto cc = camera->GetCaptureController(); assert(cc); cc->ResumePreview(); } } -void CameraPlugin::StartVideoRecordingMethodHandler( - const EncodableMap& args, std::unique_ptr> result) { - auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); - if (!camera_id) { - return result->Error("argument_error", - std::string(kCameraIdKey) + " missing"); - } - - auto camera = GetCameraByCameraId(*camera_id); +void CameraPlugin::StartVideoRecording( + int64_t camera_id, const PlatformVideoCaptureOptions& options, + std::function reply)> result) { + auto camera = GetCameraByCameraId(camera_id); if (!camera) { - return result->Error("camera_error", "Camera not created"); + return result(FlutterError("camera_error", "Camera not created")); } if (camera->HasPendingResultByType(PendingResultType::kStartRecord)) { - return result->Error("camera_error", - "Pending start recording request exists"); + return result( + FlutterError("camera_error", "Pending start recording request exists")); } int64_t max_video_duration_ms = -1; - auto requested_max_video_duration_ms = - std::get_if(ValueOrNull(args, kMaxVideoDurationKey)); + const int64_t* requested_max_video_duration_ms = + options.max_duration_milliseconds(); if (requested_max_video_duration_ms != nullptr) { max_video_duration_ms = *requested_max_video_duration_ms; @@ -532,85 +317,67 @@ void CameraPlugin::StartVideoRecordingMethodHandler( std::optional path = GetFilePathForVideo(); if (path) { - if (camera->AddPendingResult(PendingResultType::kStartRecord, - std::move(result))) { + if (camera->AddPendingVoidResult(PendingResultType::kStartRecord, + std::move(result))) { auto cc = camera->GetCaptureController(); assert(cc); cc->StartRecord(*path, max_video_duration_ms); } } else { - return result->Error("system_error", - "Failed to get path for video capture"); + return result( + FlutterError("system_error", "Failed to get path for video capture")); } } -void CameraPlugin::StopVideoRecordingMethodHandler( - const EncodableMap& args, std::unique_ptr> result) { - auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); - if (!camera_id) { - return result->Error("argument_error", - std::string(kCameraIdKey) + " missing"); - } - - auto camera = GetCameraByCameraId(*camera_id); +void CameraPlugin::StopVideoRecording( + int64_t camera_id, std::function reply)> result) { + auto camera = GetCameraByCameraId(camera_id); if (!camera) { - return result->Error("camera_error", "Camera not created"); + return result(FlutterError("camera_error", "Camera not created")); } if (camera->HasPendingResultByType(PendingResultType::kStopRecord)) { - return result->Error("camera_error", - "Pending stop recording request exists"); + return result( + FlutterError("camera_error", "Pending stop recording request exists")); } - if (camera->AddPendingResult(PendingResultType::kStopRecord, - std::move(result))) { + if (camera->AddPendingStringResult(PendingResultType::kStopRecord, + std::move(result))) { auto cc = camera->GetCaptureController(); assert(cc); cc->StopRecord(); } } -void CameraPlugin::TakePictureMethodHandler( - const EncodableMap& args, std::unique_ptr> result) { - auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); - if (!camera_id) { - return result->Error("argument_error", - std::string(kCameraIdKey) + " missing"); - } - - auto camera = GetCameraByCameraId(*camera_id); +void CameraPlugin::TakePicture( + int64_t camera_id, std::function reply)> result) { + auto camera = GetCameraByCameraId(camera_id); if (!camera) { - return result->Error("camera_error", "Camera not created"); + return result(FlutterError("camera_error", "Camera not created")); } if (camera->HasPendingResultByType(PendingResultType::kTakePicture)) { - return result->Error("camera_error", "Pending take picture request exists"); + return result( + FlutterError("camera_error", "Pending take picture request exists")); } std::optional path = GetFilePathForPicture(); if (path) { - if (camera->AddPendingResult(PendingResultType::kTakePicture, - std::move(result))) { + if (camera->AddPendingStringResult(PendingResultType::kTakePicture, + std::move(result))) { auto cc = camera->GetCaptureController(); assert(cc); cc->TakePicture(*path); } } else { - return result->Error("system_error", - "Failed to get capture path for picture"); + return result( + FlutterError("system_error", "Failed to get capture path for picture")); } } -void CameraPlugin::DisposeMethodHandler( - const EncodableMap& args, std::unique_ptr> result) { - auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey); - if (!camera_id) { - return result->Error("argument_error", - std::string(kCameraIdKey) + " missing"); - } - - DisposeCameraByCameraId(*camera_id); - result->Success(); +std::optional CameraPlugin::Dispose(int64_t camera_id) { + DisposeCameraByCameraId(camera_id); + return std::nullopt; } } // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/camera_plugin.h b/packages/camera/camera_windows/windows/camera_plugin.h index 1baa2477beb5..43f0e3a4619d 100644 --- a/packages/camera/camera_windows/windows/camera_plugin.h +++ b/packages/camera/camera_windows/windows/camera_plugin.h @@ -15,6 +15,7 @@ #include "camera.h" #include "capture_controller.h" #include "capture_controller_listener.h" +#include "messages.g.h" namespace camera_windows { using flutter::MethodResult; @@ -27,6 +28,7 @@ class MockCameraPlugin; } // namespace test class CameraPlugin : public flutter::Plugin, + public CameraApi, public VideoCaptureDeviceEnumerator { public: static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); @@ -46,9 +48,30 @@ class CameraPlugin : public flutter::Plugin, CameraPlugin(const CameraPlugin&) = delete; CameraPlugin& operator=(const CameraPlugin&) = delete; - // Called when a method is called on plugin channel. - void HandleMethodCall(const flutter::MethodCall<>& method_call, - std::unique_ptr> result); + // CameraApi: + ErrorOr GetAvailableCameras() override; + void Create(const std::string& camera_name, + const PlatformMediaSettings& settings, + std::function reply)> result) override; + void Initialize( + int64_t camera_id, + std::function reply)> result) override; + void PausePreview( + int64_t camera_id, + std::function reply)> result) override; + void ResumePreview( + int64_t camera_id, + std::function reply)> result) override; + void StartVideoRecording( + int64_t camera_id, const PlatformVideoCaptureOptions& options, + std::function reply)> result) override; + void StopVideoRecording( + int64_t camera_id, + std::function reply)> result) override; + void TakePicture( + int64_t camera_id, + std::function reply)> result) override; + std::optional Dispose(int64_t camera_id) override; private: // Loops through cameras and returns camera @@ -66,59 +89,6 @@ class CameraPlugin : public flutter::Plugin, bool EnumerateVideoCaptureDeviceSources(IMFActivate*** devices, UINT32* count) override; - // Handles availableCameras method calls. - // Enumerates video capture devices and - // returns list of available camera devices. - void AvailableCamerasMethodHandler( - std::unique_ptr> result); - - // Handles create method calls. - // Creates camera and initializes capture controller for requested device. - // Stores result object to be handled after request is processed. - void CreateMethodHandler(const EncodableMap& args, - std::unique_ptr> result); - - // Handles initialize method calls. - // Requests existing camera controller to start preview. - // Stores result object to be handled after request is processed. - void InitializeMethodHandler(const EncodableMap& args, - std::unique_ptr> result); - - // Handles takePicture method calls. - // Requests existing camera controller to take photo. - // Stores result object to be handled after request is processed. - void TakePictureMethodHandler(const EncodableMap& args, - std::unique_ptr> result); - - // Handles startVideoRecording method calls. - // Requests existing camera controller to start recording. - // Stores result object to be handled after request is processed. - void StartVideoRecordingMethodHandler(const EncodableMap& args, - std::unique_ptr> result); - - // Handles stopVideoRecording method calls. - // Requests existing camera controller to stop recording. - // Stores result object to be handled after request is processed. - void StopVideoRecordingMethodHandler(const EncodableMap& args, - std::unique_ptr> result); - - // Handles pausePreview method calls. - // Requests existing camera controller to pause recording. - // Stores result object to be handled after request is processed. - void PausePreviewMethodHandler(const EncodableMap& args, - std::unique_ptr> result); - - // Handles resumePreview method calls. - // Requests existing camera controller to resume preview. - // Stores result object to be handled after request is processed. - void ResumePreviewMethodHandler(const EncodableMap& args, - std::unique_ptr> result); - - // Handles dsipose method calls. - // Disposes camera if exists. - void DisposeMethodHandler(const EncodableMap& args, - std::unique_ptr> result); - std::unique_ptr camera_factory_; flutter::TextureRegistrar* texture_registrar_; flutter::BinaryMessenger* messenger_; diff --git a/packages/camera/camera_windows/windows/capture_controller.cpp b/packages/camera/camera_windows/windows/capture_controller.cpp index 0c06f69a4a4a..9c61d81ab0ff 100644 --- a/packages/camera/camera_windows/windows/capture_controller.cpp +++ b/packages/camera/camera_windows/windows/capture_controller.cpp @@ -33,7 +33,10 @@ CameraResult GetCameraResult(HRESULT hr) { CaptureControllerImpl::CaptureControllerImpl( CaptureControllerListener* listener) - : capture_controller_listener_(listener), CaptureController(){}; + : capture_controller_listener_(listener), + media_settings_( + PlatformMediaSettings(PlatformResolutionPreset::max, true)), + CaptureController(){}; CaptureControllerImpl::~CaptureControllerImpl() { ResetCaptureController(); @@ -217,7 +220,7 @@ HRESULT CaptureControllerImpl::CreateCaptureEngine() { } // Creates audio source only if not already initialized by test framework - if (media_settings_.record_audio && !audio_source_) { + if (media_settings_.enable_audio() && !audio_source_) { hr = CreateDefaultAudioCaptureSource(); if (FAILED(hr)) { return hr; @@ -241,7 +244,7 @@ HRESULT CaptureControllerImpl::CreateCaptureEngine() { } hr = attributes->SetUINT32(MF_CAPTURE_ENGINE_USE_VIDEO_DEVICE_ONLY, - !media_settings_.record_audio); + !media_settings_.enable_audio()); if (FAILED(hr)) { return hr; } @@ -301,7 +304,7 @@ void CaptureControllerImpl::ResetCaptureController() { bool CaptureControllerImpl::InitCaptureDevice( flutter::TextureRegistrar* texture_registrar, const std::string& device_id, - ResolutionPreset resolution_preset, const RecordSettings& record_settings) { + const PlatformMediaSettings& media_settings) { assert(capture_controller_listener_); if (IsInitialized()) { @@ -315,8 +318,7 @@ bool CaptureControllerImpl::InitCaptureDevice( } capture_engine_state_ = CaptureEngineState::kInitializing; - resolution_preset_ = resolution_preset; - media_settings_ = record_settings; + media_settings_ = media_settings; texture_registrar_ = texture_registrar; video_device_id_ = device_id; @@ -382,28 +384,21 @@ void CaptureControllerImpl::TakePicture(const std::string& file_path) { } uint32_t CaptureControllerImpl::GetMaxPreviewHeight() const { - switch (resolution_preset_) { - case ResolutionPreset::kLow: + switch (media_settings_.resolution_preset()) { + case PlatformResolutionPreset::low: return 240; - break; - case ResolutionPreset::kMedium: + case PlatformResolutionPreset::medium: return 480; - break; - case ResolutionPreset::kHigh: + case PlatformResolutionPreset::high: return 720; - break; - case ResolutionPreset::kVeryHigh: + case PlatformResolutionPreset::veryHigh: return 1080; - break; - case ResolutionPreset::kUltraHigh: + case PlatformResolutionPreset::ultraHigh: return 2160; - break; - case ResolutionPreset::kMax: - case ResolutionPreset::kAuto: + case PlatformResolutionPreset::max: default: // no limit. return 0xffffffff; - break; } } diff --git a/packages/camera/camera_windows/windows/capture_controller.h b/packages/camera/camera_windows/windows/capture_controller.h index c6807002e589..f2288b5e913f 100644 --- a/packages/camera/camera_windows/windows/capture_controller.h +++ b/packages/camera/camera_windows/windows/capture_controller.h @@ -20,6 +20,7 @@ #include "capture_controller_listener.h" #include "capture_engine_listener.h" +#include "messages.g.h" #include "photo_handler.h" #include "preview_handler.h" #include "record_handler.h" @@ -29,24 +30,6 @@ namespace camera_windows { using flutter::TextureRegistrar; using Microsoft::WRL::ComPtr; -// Camera resolution presets. Used to request a capture resolution. -enum class ResolutionPreset { - // Automatic resolution, uses the highest resolution available. - kAuto, - // 240p (320x240) - kLow, - // 480p (720x480) - kMedium, - // 720p (1280x720) - kHigh, - // 1080p (1920x1080) - kVeryHigh, - // 2160p (4096x2160) - kUltraHigh, - // The highest resolution available. - kMax, -}; - // Camera capture engine state. // // On creation, |CaptureControllers| start in state |kNotInitialized|. @@ -83,13 +66,10 @@ class CaptureController { // register texture for capture preview. // device_id: A string that holds information of camera device id to // be captured. - // record_audio: A boolean value telling if audio should be captured on - // video recording. - // resolution_preset: Maximum capture resolution height. - virtual bool InitCaptureDevice(TextureRegistrar* texture_registrar, - const std::string& device_id, - ResolutionPreset resolution_preset, - const RecordSettings& record_settings) = 0; + // media_settings: Settings controlling capture behavior. + virtual bool InitCaptureDevice( + TextureRegistrar* texture_registrar, const std::string& device_id, + const PlatformMediaSettings& media_settings) = 0; // Returns preview frame width virtual uint32_t GetPreviewWidth() const = 0; @@ -138,8 +118,7 @@ class CaptureControllerImpl : public CaptureController, // CaptureController bool InitCaptureDevice(TextureRegistrar* texture_registrar, const std::string& device_id, - ResolutionPreset resolution_preset, - const RecordSettings& record_settings) override; + const PlatformMediaSettings& media_settings) override; uint32_t GetPreviewWidth() const override { return preview_frame_width_; } uint32_t GetPreviewHeight() const override { return preview_frame_height_; } void StartPreview() override; @@ -247,8 +226,7 @@ class CaptureControllerImpl : public CaptureController, std::string video_device_id_; CaptureEngineState capture_engine_state_ = CaptureEngineState::kNotInitialized; - ResolutionPreset resolution_preset_ = ResolutionPreset::kMedium; - RecordSettings media_settings_; + PlatformMediaSettings media_settings_; ComPtr capture_engine_; ComPtr capture_engine_callback_handler_; ComPtr dxgi_device_manager_; diff --git a/packages/camera/camera_windows/windows/messages.g.cpp b/packages/camera/camera_windows/windows/messages.g.cpp new file mode 100644 index 000000000000..426dc22c0103 --- /dev/null +++ b/packages/camera/camera_windows/windows/messages.g.cpp @@ -0,0 +1,651 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v20.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#undef _HAS_EXCEPTIONS + +#include "messages.g.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace camera_windows { +using flutter::BasicMessageChannel; +using flutter::CustomEncodableValue; +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; + +FlutterError CreateConnectionError(const std::string channel_name) { + return FlutterError( + "channel-error", + "Unable to establish connection on channel: '" + channel_name + "'.", + EncodableValue("")); +} + +// PlatformMediaSettings + +PlatformMediaSettings::PlatformMediaSettings( + const PlatformResolutionPreset& resolution_preset, bool enable_audio) + : resolution_preset_(resolution_preset), enable_audio_(enable_audio) {} + +PlatformMediaSettings::PlatformMediaSettings( + const PlatformResolutionPreset& resolution_preset, + const int64_t* frames_per_second, const int64_t* video_bitrate, + const int64_t* audio_bitrate, bool enable_audio) + : resolution_preset_(resolution_preset), + frames_per_second_(frames_per_second + ? std::optional(*frames_per_second) + : std::nullopt), + video_bitrate_(video_bitrate ? std::optional(*video_bitrate) + : std::nullopt), + audio_bitrate_(audio_bitrate ? std::optional(*audio_bitrate) + : std::nullopt), + enable_audio_(enable_audio) {} + +const PlatformResolutionPreset& PlatformMediaSettings::resolution_preset() + const { + return resolution_preset_; +} + +void PlatformMediaSettings::set_resolution_preset( + const PlatformResolutionPreset& value_arg) { + resolution_preset_ = value_arg; +} + +const int64_t* PlatformMediaSettings::frames_per_second() const { + return frames_per_second_ ? &(*frames_per_second_) : nullptr; +} + +void PlatformMediaSettings::set_frames_per_second(const int64_t* value_arg) { + frames_per_second_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void PlatformMediaSettings::set_frames_per_second(int64_t value_arg) { + frames_per_second_ = value_arg; +} + +const int64_t* PlatformMediaSettings::video_bitrate() const { + return video_bitrate_ ? &(*video_bitrate_) : nullptr; +} + +void PlatformMediaSettings::set_video_bitrate(const int64_t* value_arg) { + video_bitrate_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void PlatformMediaSettings::set_video_bitrate(int64_t value_arg) { + video_bitrate_ = value_arg; +} + +const int64_t* PlatformMediaSettings::audio_bitrate() const { + return audio_bitrate_ ? &(*audio_bitrate_) : nullptr; +} + +void PlatformMediaSettings::set_audio_bitrate(const int64_t* value_arg) { + audio_bitrate_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void PlatformMediaSettings::set_audio_bitrate(int64_t value_arg) { + audio_bitrate_ = value_arg; +} + +bool PlatformMediaSettings::enable_audio() const { return enable_audio_; } + +void PlatformMediaSettings::set_enable_audio(bool value_arg) { + enable_audio_ = value_arg; +} + +EncodableList PlatformMediaSettings::ToEncodableList() const { + EncodableList list; + list.reserve(5); + list.push_back(CustomEncodableValue(resolution_preset_)); + list.push_back(frames_per_second_ ? EncodableValue(*frames_per_second_) + : EncodableValue()); + list.push_back(video_bitrate_ ? EncodableValue(*video_bitrate_) + : EncodableValue()); + list.push_back(audio_bitrate_ ? EncodableValue(*audio_bitrate_) + : EncodableValue()); + list.push_back(EncodableValue(enable_audio_)); + return list; +} + +PlatformMediaSettings PlatformMediaSettings::FromEncodableList( + const EncodableList& list) { + PlatformMediaSettings decoded(std::any_cast( + std::get(list[0])), + std::get(list[4])); + auto& encodable_frames_per_second = list[1]; + if (!encodable_frames_per_second.IsNull()) { + decoded.set_frames_per_second(encodable_frames_per_second.LongValue()); + } + auto& encodable_video_bitrate = list[2]; + if (!encodable_video_bitrate.IsNull()) { + decoded.set_video_bitrate(encodable_video_bitrate.LongValue()); + } + auto& encodable_audio_bitrate = list[3]; + if (!encodable_audio_bitrate.IsNull()) { + decoded.set_audio_bitrate(encodable_audio_bitrate.LongValue()); + } + return decoded; +} + +// PlatformSize + +PlatformSize::PlatformSize(double width, double height) + : width_(width), height_(height) {} + +double PlatformSize::width() const { return width_; } + +void PlatformSize::set_width(double value_arg) { width_ = value_arg; } + +double PlatformSize::height() const { return height_; } + +void PlatformSize::set_height(double value_arg) { height_ = value_arg; } + +EncodableList PlatformSize::ToEncodableList() const { + EncodableList list; + list.reserve(2); + list.push_back(EncodableValue(width_)); + list.push_back(EncodableValue(height_)); + return list; +} + +PlatformSize PlatformSize::FromEncodableList(const EncodableList& list) { + PlatformSize decoded(std::get(list[0]), std::get(list[1])); + return decoded; +} + +// PlatformVideoCaptureOptions + +PlatformVideoCaptureOptions::PlatformVideoCaptureOptions() {} + +PlatformVideoCaptureOptions::PlatformVideoCaptureOptions( + const int64_t* max_duration_milliseconds) + : max_duration_milliseconds_( + max_duration_milliseconds + ? std::optional(*max_duration_milliseconds) + : std::nullopt) {} + +const int64_t* PlatformVideoCaptureOptions::max_duration_milliseconds() const { + return max_duration_milliseconds_ ? &(*max_duration_milliseconds_) : nullptr; +} + +void PlatformVideoCaptureOptions::set_max_duration_milliseconds( + const int64_t* value_arg) { + max_duration_milliseconds_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void PlatformVideoCaptureOptions::set_max_duration_milliseconds( + int64_t value_arg) { + max_duration_milliseconds_ = value_arg; +} + +EncodableList PlatformVideoCaptureOptions::ToEncodableList() const { + EncodableList list; + list.reserve(1); + list.push_back(max_duration_milliseconds_ + ? EncodableValue(*max_duration_milliseconds_) + : EncodableValue()); + return list; +} + +PlatformVideoCaptureOptions PlatformVideoCaptureOptions::FromEncodableList( + const EncodableList& list) { + PlatformVideoCaptureOptions decoded; + auto& encodable_max_duration_milliseconds = list[0]; + if (!encodable_max_duration_milliseconds.IsNull()) { + decoded.set_max_duration_milliseconds( + encodable_max_duration_milliseconds.LongValue()); + } + return decoded; +} + +PigeonCodecSerializer::PigeonCodecSerializer() {} + +EncodableValue PigeonCodecSerializer::ReadValueOfType( + uint8_t type, flutter::ByteStreamReader* stream) const { + switch (type) { + case 129: + return CustomEncodableValue(PlatformMediaSettings::FromEncodableList( + std::get(ReadValue(stream)))); + case 130: + return CustomEncodableValue(PlatformSize::FromEncodableList( + std::get(ReadValue(stream)))); + case 131: + return CustomEncodableValue( + PlatformVideoCaptureOptions::FromEncodableList( + std::get(ReadValue(stream)))); + case 132: { + const auto& encodable_enum_arg = ReadValue(stream); + const int64_t enum_arg_value = + encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue(); + return encodable_enum_arg.IsNull() + ? EncodableValue() + : CustomEncodableValue( + static_cast(enum_arg_value)); + } + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } +} + +void PigeonCodecSerializer::WriteValue( + const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = + std::get_if(&value)) { + if (custom_value->type() == typeid(PlatformMediaSettings)) { + stream->WriteByte(129); + WriteValue( + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(PlatformSize)) { + stream->WriteByte(130); + WriteValue( + EncodableValue( + std::any_cast(*custom_value).ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(PlatformVideoCaptureOptions)) { + stream->WriteByte(131); + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(PlatformResolutionPreset)) { + stream->WriteByte(132); + WriteValue(EncodableValue(static_cast( + std::any_cast(*custom_value))), + stream); + return; + } + } + flutter::StandardCodecSerializer::WriteValue(value, stream); +} + +/// The codec used by CameraApi. +const flutter::StandardMessageCodec& CameraApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance( + &PigeonCodecSerializer::GetInstance()); +} + +// Sets up an instance of `CameraApi` to handle messages through the +// `binary_messenger`. +void CameraApi::SetUp(flutter::BinaryMessenger* binary_messenger, + CameraApi* api) { + CameraApi::SetUp(binary_messenger, api, ""); +} + +void CameraApi::SetUp(flutter::BinaryMessenger* binary_messenger, + CameraApi* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = + message_channel_suffix.length() > 0 + ? std::string(".") + message_channel_suffix + : ""; + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.camera_windows.CameraApi.getAvailableCameras" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + ErrorOr output = api->GetAvailableCameras(); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.camera_windows.CameraApi.create" + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_camera_name_arg = args.at(0); + if (encodable_camera_name_arg.IsNull()) { + reply(WrapError("camera_name_arg unexpectedly null.")); + return; + } + const auto& camera_name_arg = + std::get(encodable_camera_name_arg); + const auto& encodable_settings_arg = args.at(1); + if (encodable_settings_arg.IsNull()) { + reply(WrapError("settings_arg unexpectedly null.")); + return; + } + const auto& settings_arg = + std::any_cast( + std::get(encodable_settings_arg)); + api->Create(camera_name_arg, settings_arg, + [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.camera_windows.CameraApi.initialize" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_camera_id_arg = args.at(0); + if (encodable_camera_id_arg.IsNull()) { + reply(WrapError("camera_id_arg unexpectedly null.")); + return; + } + const int64_t camera_id_arg = encodable_camera_id_arg.LongValue(); + api->Initialize( + camera_id_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.camera_windows.CameraApi.dispose" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_camera_id_arg = args.at(0); + if (encodable_camera_id_arg.IsNull()) { + reply(WrapError("camera_id_arg unexpectedly null.")); + return; + } + const int64_t camera_id_arg = encodable_camera_id_arg.LongValue(); + std::optional output = api->Dispose(camera_id_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.camera_windows.CameraApi.takePicture" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_camera_id_arg = args.at(0); + if (encodable_camera_id_arg.IsNull()) { + reply(WrapError("camera_id_arg unexpectedly null.")); + return; + } + const int64_t camera_id_arg = encodable_camera_id_arg.LongValue(); + api->TakePicture( + camera_id_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.camera_windows.CameraApi.startVideoRecording" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_camera_id_arg = args.at(0); + if (encodable_camera_id_arg.IsNull()) { + reply(WrapError("camera_id_arg unexpectedly null.")); + return; + } + const int64_t camera_id_arg = encodable_camera_id_arg.LongValue(); + const auto& encodable_options_arg = args.at(1); + if (encodable_options_arg.IsNull()) { + reply(WrapError("options_arg unexpectedly null.")); + return; + } + const auto& options_arg = + std::any_cast( + std::get(encodable_options_arg)); + api->StartVideoRecording( + camera_id_arg, options_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.camera_windows.CameraApi.stopVideoRecording" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_camera_id_arg = args.at(0); + if (encodable_camera_id_arg.IsNull()) { + reply(WrapError("camera_id_arg unexpectedly null.")); + return; + } + const int64_t camera_id_arg = encodable_camera_id_arg.LongValue(); + api->StopVideoRecording( + camera_id_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.camera_windows.CameraApi.pausePreview" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_camera_id_arg = args.at(0); + if (encodable_camera_id_arg.IsNull()) { + reply(WrapError("camera_id_arg unexpectedly null.")); + return; + } + const int64_t camera_id_arg = encodable_camera_id_arg.LongValue(); + api->PausePreview(camera_id_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.camera_windows.CameraApi.resumePreview" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_camera_id_arg = args.at(0); + if (encodable_camera_id_arg.IsNull()) { + reply(WrapError("camera_id_arg unexpectedly null.")); + return; + } + const int64_t camera_id_arg = encodable_camera_id_arg.LongValue(); + api->ResumePreview(camera_id_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } +} + +EncodableValue CameraApi::WrapError(std::string_view error_message) { + return EncodableValue( + EncodableList{EncodableValue(std::string(error_message)), + EncodableValue("Error"), EncodableValue()}); +} + +EncodableValue CameraApi::WrapError(const FlutterError& error) { + return EncodableValue(EncodableList{EncodableValue(error.code()), + EncodableValue(error.message()), + error.details()}); +} + +} // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/messages.g.h b/packages/camera/camera_windows/windows/messages.g.h new file mode 100644 index 000000000000..e2ee53253fff --- /dev/null +++ b/packages/camera/camera_windows/windows/messages.g.h @@ -0,0 +1,237 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v20.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_MESSAGES_G_H_ +#define PIGEON_MESSAGES_G_H_ +#include +#include +#include +#include + +#include +#include +#include + +namespace camera_windows { + +// Generated class from Pigeon. + +class FlutterError { + public: + explicit FlutterError(const std::string& code) : code_(code) {} + explicit FlutterError(const std::string& code, const std::string& message) + : code_(code), message_(message) {} + explicit FlutterError(const std::string& code, const std::string& message, + const flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} + + const std::string& code() const { return code_; } + const std::string& message() const { return message_; } + const flutter::EncodableValue& details() const { return details_; } + + private: + std::string code_; + std::string message_; + flutter::EncodableValue details_; +}; + +template +class ErrorOr { + public: + ErrorOr(const T& rhs) : v_(rhs) {} + ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} + ErrorOr(const FlutterError& rhs) : v_(rhs) {} + ErrorOr(const FlutterError&& rhs) : v_(std::move(rhs)) {} + + bool has_error() const { return std::holds_alternative(v_); } + const T& value() const { return std::get(v_); }; + const FlutterError& error() const { return std::get(v_); }; + + private: + friend class CameraApi; + ErrorOr() = default; + T TakeValue() && { return std::get(std::move(v_)); } + + std::variant v_; +}; + +// Pigeon version of platform interface's ResolutionPreset. +enum class PlatformResolutionPreset { + low = 0, + medium = 1, + high = 2, + veryHigh = 3, + ultraHigh = 4, + max = 5 +}; + +// Pigeon version of MediaSettings. +// +// Generated class from Pigeon that represents data sent in messages. +class PlatformMediaSettings { + public: + // Constructs an object setting all non-nullable fields. + explicit PlatformMediaSettings( + const PlatformResolutionPreset& resolution_preset, bool enable_audio); + + // Constructs an object setting all fields. + explicit PlatformMediaSettings( + const PlatformResolutionPreset& resolution_preset, + const int64_t* frames_per_second, const int64_t* video_bitrate, + const int64_t* audio_bitrate, bool enable_audio); + + const PlatformResolutionPreset& resolution_preset() const; + void set_resolution_preset(const PlatformResolutionPreset& value_arg); + + const int64_t* frames_per_second() const; + void set_frames_per_second(const int64_t* value_arg); + void set_frames_per_second(int64_t value_arg); + + const int64_t* video_bitrate() const; + void set_video_bitrate(const int64_t* value_arg); + void set_video_bitrate(int64_t value_arg); + + const int64_t* audio_bitrate() const; + void set_audio_bitrate(const int64_t* value_arg); + void set_audio_bitrate(int64_t value_arg); + + bool enable_audio() const; + void set_enable_audio(bool value_arg); + + private: + static PlatformMediaSettings FromEncodableList( + const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class CameraApi; + friend class PigeonCodecSerializer; + PlatformResolutionPreset resolution_preset_; + std::optional frames_per_second_; + std::optional video_bitrate_; + std::optional audio_bitrate_; + bool enable_audio_; +}; + +// A representation of a size from the native camera APIs. +// +// Generated class from Pigeon that represents data sent in messages. +class PlatformSize { + public: + // Constructs an object setting all fields. + explicit PlatformSize(double width, double height); + + double width() const; + void set_width(double value_arg); + + double height() const; + void set_height(double value_arg); + + private: + static PlatformSize FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class CameraApi; + friend class PigeonCodecSerializer; + double width_; + double height_; +}; + +// Pigeon version of the relevant subset of VideoCaptureOptions. +// +// Generated class from Pigeon that represents data sent in messages. +class PlatformVideoCaptureOptions { + public: + // Constructs an object setting all non-nullable fields. + PlatformVideoCaptureOptions(); + + // Constructs an object setting all fields. + explicit PlatformVideoCaptureOptions( + const int64_t* max_duration_milliseconds); + + const int64_t* max_duration_milliseconds() const; + void set_max_duration_milliseconds(const int64_t* value_arg); + void set_max_duration_milliseconds(int64_t value_arg); + + private: + static PlatformVideoCaptureOptions FromEncodableList( + const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class CameraApi; + friend class PigeonCodecSerializer; + std::optional max_duration_milliseconds_; +}; + +class PigeonCodecSerializer : public flutter::StandardCodecSerializer { + public: + PigeonCodecSerializer(); + inline static PigeonCodecSerializer& GetInstance() { + static PigeonCodecSerializer sInstance; + return sInstance; + } + + void WriteValue(const flutter::EncodableValue& value, + flutter::ByteStreamWriter* stream) const override; + + protected: + flutter::EncodableValue ReadValueOfType( + uint8_t type, flutter::ByteStreamReader* stream) const override; +}; + +// Generated interface from Pigeon that represents a handler of messages from +// Flutter. +class CameraApi { + public: + CameraApi(const CameraApi&) = delete; + CameraApi& operator=(const CameraApi&) = delete; + virtual ~CameraApi() {} + // Returns the names of all of the available capture devices. + virtual ErrorOr GetAvailableCameras() = 0; + // Creates a camera instance for the given device name and settings. + virtual void Create(const std::string& camera_name, + const PlatformMediaSettings& settings, + std::function reply)> result) = 0; + // Initializes a camera, and returns the size of its preview. + virtual void Initialize( + int64_t camera_id, + std::function reply)> result) = 0; + // Disposes a camera that is no longer in use. + virtual std::optional Dispose(int64_t camera_id) = 0; + // Takes a picture with the given camera, and returns the path to the + // resulting file. + virtual void TakePicture( + int64_t camera_id, + std::function reply)> result) = 0; + // Starts recording video with the given camera. + virtual void StartVideoRecording( + int64_t camera_id, const PlatformVideoCaptureOptions& options, + std::function reply)> result) = 0; + // Finishes recording video with the given camera, and returns the path to + // the resulting file. + virtual void StopVideoRecording( + int64_t camera_id, + std::function reply)> result) = 0; + // Starts the preview stream for the given camera. + virtual void PausePreview( + int64_t camera_id, + std::function reply)> result) = 0; + // Resumes the preview stream for the given camera. + virtual void ResumePreview( + int64_t camera_id, + std::function reply)> result) = 0; + + // The codec used by CameraApi. + static const flutter::StandardMessageCodec& GetCodec(); + // Sets up an instance of `CameraApi` to handle messages through the + // `binary_messenger`. + static void SetUp(flutter::BinaryMessenger* binary_messenger, CameraApi* api); + static void SetUp(flutter::BinaryMessenger* binary_messenger, CameraApi* api, + const std::string& message_channel_suffix); + static flutter::EncodableValue WrapError(std::string_view error_message); + static flutter::EncodableValue WrapError(const FlutterError& error); + + protected: + CameraApi() = default; +}; +} // namespace camera_windows +#endif // PIGEON_MESSAGES_G_H_ diff --git a/packages/camera/camera_windows/windows/record_handler.cpp b/packages/camera/camera_windows/windows/record_handler.cpp index 6e1cb78d94f1..4a4ae0c7d332 100644 --- a/packages/camera/camera_windows/windows/record_handler.cpp +++ b/packages/camera/camera_windows/windows/record_handler.cpp @@ -176,15 +176,16 @@ HRESULT RecordHandler::InitRecordSink(IMFCaptureEngine* capture_engine, return hr; } - if (media_settings_.fps.has_value()) { - assert(media_settings_.fps.value() > 0); - SetFrameRate(video_record_media_type.Get(), media_settings_.fps.value(), 1); + if (media_settings_.frames_per_second()) { + assert(*media_settings_.frames_per_second() > 0); + SetFrameRate(video_record_media_type.Get(), + static_cast(*media_settings_.frames_per_second()), 1); } - if (media_settings_.video_bitrate.has_value()) { - assert(media_settings_.video_bitrate.value() > 0); + if (media_settings_.video_bitrate()) { + assert(*media_settings_.video_bitrate() > 0); SetVideoBitrate(video_record_media_type.Get(), - media_settings_.video_bitrate.value()); + static_cast(*media_settings_.video_bitrate())); } DWORD video_record_sink_stream_index; @@ -195,17 +196,17 @@ HRESULT RecordHandler::InitRecordSink(IMFCaptureEngine* capture_engine, return hr; } - if (media_settings_.record_audio) { + if (media_settings_.enable_audio()) { ComPtr audio_record_media_type; HRESULT audio_capture_hr = S_OK; audio_capture_hr = BuildMediaTypeForAudioCapture(audio_record_media_type.GetAddressOf()); if (SUCCEEDED(audio_capture_hr)) { - if (media_settings_.audio_bitrate.has_value()) { - assert(media_settings_.audio_bitrate.value() > 0); + if (media_settings_.audio_bitrate()) { + assert(*media_settings_.audio_bitrate() > 0); SetAudioBitrate(audio_record_media_type.Get(), - media_settings_.audio_bitrate.value()); + static_cast(*media_settings_.audio_bitrate())); } DWORD audio_record_sink_stream_index; diff --git a/packages/camera/camera_windows/windows/record_handler.h b/packages/camera/camera_windows/windows/record_handler.h index 612e14a4eee9..a8866e75b71c 100644 --- a/packages/camera/camera_windows/windows/record_handler.h +++ b/packages/camera/camera_windows/windows/record_handler.h @@ -14,6 +14,8 @@ #include #include +#include "messages.g.h" + namespace camera_windows { using Microsoft::WRL::ComPtr; @@ -32,47 +34,13 @@ enum class RecordingType { // sequential order through the states. enum class RecordState { kNotStarted, kStarting, kRunning, kStopping }; -// Recording media settings. -// -// Used in [Camera::InitCamera]. -// Allows to tune recorded video parameters, such as resolution, frame rate, -// bitrate. If [fps], [video_bitrate] or [audio_bitrate] are passed, they must -// be greater than zero. -struct RecordSettings { - explicit RecordSettings( - const bool record_audio = false, - const std::optional& fps = std::nullopt, - const std::optional& video_bitrate = std::nullopt, - const std::optional& audio_bitrate = std::nullopt) - : record_audio(record_audio), - fps(fps), - video_bitrate(video_bitrate), - audio_bitrate(audio_bitrate) { - assert(!fps.has_value() || fps.value() > 0); - assert(!video_bitrate.has_value() || video_bitrate.value() > 0); - assert(!audio_bitrate.has_value() || audio_bitrate.value() > 0); - } - - // Controls audio presence in recorded video. - bool record_audio{false}; - - // Rate at which frames should be captured by the camera in frames per second. - std::optional fps{std::nullopt}; - - // The video encoding bit rate for recording. - std::optional video_bitrate{std::nullopt}; - - // The audio encoding bit rate for recording. - std::optional audio_bitrate{std::nullopt}; -}; - // Handler for video recording via the camera. // // Handles record sink initialization and manages the state of video recording. class RecordHandler { public: - explicit RecordHandler(const RecordSettings& record_settings) - : media_settings_(record_settings) {} + explicit RecordHandler(const PlatformMediaSettings& media_settings) + : media_settings_(media_settings) {} virtual ~RecordHandler() = default; @@ -141,7 +109,7 @@ class RecordHandler { HRESULT InitRecordSink(IMFCaptureEngine* capture_engine, IMFMediaType* base_media_type); - const RecordSettings media_settings_; + const PlatformMediaSettings media_settings_; int64_t max_video_duration_ms_ = -1; int64_t recording_start_timestamp_us_ = -1; uint64_t recording_duration_us_ = 0; diff --git a/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp b/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp index 7a9a3cdbde7b..5329a03e09e8 100644 --- a/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp +++ b/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp @@ -37,11 +37,11 @@ void MockInitCamera(MockCamera* camera, bool success) { .WillOnce(Return(false)); EXPECT_CALL(*camera, - AddPendingResult(Eq(PendingResultType::kCreateCamera), _)) + AddPendingIntResult(Eq(PendingResultType::kCreateCamera), _)) .Times(1) .WillOnce([camera](PendingResultType type, - std::unique_ptr> result) { - camera->pending_result_ = std::move(result); + std::function reply)> result) { + camera->pending_int_result_ = result; return true; }); @@ -52,14 +52,14 @@ void MockInitCamera(MockCamera* camera, bool success) { .Times(1) .WillOnce([camera, success](flutter::TextureRegistrar* texture_registrar, flutter::BinaryMessenger* messenger, - ResolutionPreset resolution_preset, - const RecordSettings& record_settings) { - assert(camera->pending_result_); + const PlatformMediaSettings& media_settings) { + assert(camera->pending_int_result_); if (success) { - camera->pending_result_->Success(EncodableValue(1)); + camera->pending_int_result_(1); return true; } else { - camera->pending_result_->Error("camera_error", "InitCamera failed."); + camera->pending_int_result_( + FlutterError("camera_error", "InitCamera failed.")); return false; } }); @@ -72,8 +72,6 @@ TEST(CameraPlugin, AvailableCamerasHandlerSuccessIfNoCameras) { std::make_unique(); std::unique_ptr camera_factory_ = std::make_unique(); - std::unique_ptr result = - std::make_unique(); MockCameraPlugin plugin(texture_registrar_.get(), messenger_.get(), std::move(camera_factory_)); @@ -87,13 +85,10 @@ TEST(CameraPlugin, AvailableCamerasHandlerSuccessIfNoCameras) { return true; }); - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal).Times(1); + ErrorOr result = plugin.GetAvailableCameras(); - plugin.HandleMethodCall( - flutter::MethodCall("availableCameras", - std::make_unique()), - std::move(result)); + EXPECT_FALSE(result.has_error()); + EXPECT_EQ(result.value().size(), 0); } TEST(CameraPlugin, AvailableCamerasHandlerErrorIfFailsToEnumerateDevices) { @@ -103,8 +98,6 @@ TEST(CameraPlugin, AvailableCamerasHandlerErrorIfFailsToEnumerateDevices) { std::make_unique(); std::unique_ptr camera_factory_ = std::make_unique(); - std::unique_ptr result = - std::make_unique(); MockCameraPlugin plugin(texture_registrar_.get(), messenger_.get(), std::move(camera_factory_)); @@ -113,18 +106,12 @@ TEST(CameraPlugin, AvailableCamerasHandlerErrorIfFailsToEnumerateDevices) { .Times(1) .WillOnce([](IMFActivate*** devices, UINT32* count) { return false; }); - EXPECT_CALL(*result, ErrorInternal).Times(1); - EXPECT_CALL(*result, SuccessInternal).Times(0); + ErrorOr result = plugin.GetAvailableCameras(); - plugin.HandleMethodCall( - flutter::MethodCall("availableCameras", - std::make_unique()), - std::move(result)); + EXPECT_TRUE(result.has_error()); } TEST(CameraPlugin, CreateHandlerCallsInitCamera) { - std::unique_ptr result = - std::make_unique(); std::unique_ptr texture_registrar_ = std::make_unique(); std::unique_ptr messenger_ = @@ -142,26 +129,26 @@ TEST(CameraPlugin, CreateHandlerCallsInitCamera) { EXPECT_CALL(*camera_factory_, CreateCamera(MOCK_DEVICE_ID)); - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(1)))); - CameraPlugin plugin(texture_registrar_.get(), messenger_.get(), std::move(camera_factory_)); - EncodableMap args = { - {EncodableValue("cameraName"), EncodableValue(MOCK_CAMERA_NAME)}, - {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, - {EncodableValue("enableAudio"), EncodableValue(true)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(result)); + + bool result_called = false; + std::function)> create_result = + [&result_called](ErrorOr reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_FALSE(reply.has_error()); + EXPECT_EQ(reply.value(), 1); + }; + + plugin.Create(MOCK_CAMERA_NAME, + PlatformMediaSettings(PlatformResolutionPreset::max, true), + std::move(create_result)); + + EXPECT_TRUE(result_called); } TEST(CameraPlugin, CreateHandlerErrorOnInvalidDeviceId) { - std::unique_ptr result = - std::make_unique(); std::unique_ptr texture_registrar_ = std::make_unique(); std::unique_ptr messenger_ = @@ -171,25 +158,23 @@ TEST(CameraPlugin, CreateHandlerErrorOnInvalidDeviceId) { CameraPlugin plugin(texture_registrar_.get(), messenger_.get(), std::move(camera_factory_)); - EncodableMap args = { - {EncodableValue("cameraName"), EncodableValue(MOCK_INVALID_CAMERA_NAME)}, - {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, - {EncodableValue("enableAudio"), EncodableValue(true)}, - }; - - EXPECT_CALL(*result, ErrorInternal).Times(1); - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(result)); + + bool result_called = false; + std::function)> create_result = + [&result_called](ErrorOr reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_TRUE(reply.has_error()); + }; + + plugin.Create(MOCK_INVALID_CAMERA_NAME, + PlatformMediaSettings(PlatformResolutionPreset::max, true), + std::move(create_result)); + + EXPECT_TRUE(result_called); } TEST(CameraPlugin, CreateHandlerErrorOnExistingDeviceId) { - std::unique_ptr first_create_result = - std::make_unique(); - std::unique_ptr second_create_result = - std::make_unique(); std::unique_ptr texture_registrar_ = std::make_unique(); std::unique_ptr messenger_ = @@ -207,37 +192,39 @@ TEST(CameraPlugin, CreateHandlerErrorOnExistingDeviceId) { EXPECT_CALL(*camera_factory_, CreateCamera(MOCK_DEVICE_ID)); - EXPECT_CALL(*first_create_result, ErrorInternal).Times(0); - EXPECT_CALL(*first_create_result, - SuccessInternal(Pointee(EncodableValue(1)))); - CameraPlugin plugin(texture_registrar_.get(), messenger_.get(), std::move(camera_factory_)); - EncodableMap args = { - {EncodableValue("cameraName"), EncodableValue(MOCK_CAMERA_NAME)}, - {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, - {EncodableValue("enableAudio"), EncodableValue(true)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(first_create_result)); - - EXPECT_CALL(*second_create_result, ErrorInternal).Times(1); - EXPECT_CALL(*second_create_result, SuccessInternal).Times(0); - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(second_create_result)); + + bool first_result_called = false; + std::function)> first_create_result = + [&first_result_called](ErrorOr reply) { + EXPECT_FALSE(first_result_called); // Ensure only one reply call. + first_result_called = true; + EXPECT_FALSE(reply.has_error()); + EXPECT_EQ(reply.value(), 1); + }; + + PlatformMediaSettings media_settings(PlatformResolutionPreset::max, true); + plugin.Create(MOCK_CAMERA_NAME, media_settings, + std::move(first_create_result)); + + EXPECT_TRUE(first_result_called); + + bool second_result_called = false; + std::function)> second_create_result = + [&second_result_called](ErrorOr reply) { + EXPECT_FALSE(second_result_called); // Ensure only one reply call. + second_result_called = true; + EXPECT_TRUE(reply.has_error()); + }; + + plugin.Create(MOCK_CAMERA_NAME, media_settings, + std::move(second_create_result)); + + EXPECT_TRUE(second_result_called); } TEST(CameraPlugin, CreateHandlerAllowsRetry) { - std::unique_ptr first_create_result = - std::make_unique(); - std::unique_ptr second_create_result = - std::make_unique(); std::unique_ptr texture_registrar_ = std::make_unique(); std::unique_ptr messenger_ = @@ -265,38 +252,41 @@ TEST(CameraPlugin, CreateHandlerAllowsRetry) { return second_camera; }); - EXPECT_CALL(*first_create_result, ErrorInternal).Times(1); - EXPECT_CALL(*first_create_result, SuccessInternal).Times(0); - CameraPlugin plugin(texture_registrar_.get(), messenger_.get(), std::move(camera_factory_)); - EncodableMap args = { - {EncodableValue("cameraName"), EncodableValue(MOCK_CAMERA_NAME)}, - {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, - {EncodableValue("enableAudio"), EncodableValue(true)}, - }; - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(first_create_result)); - - EXPECT_CALL(*second_create_result, ErrorInternal).Times(0); - EXPECT_CALL(*second_create_result, - SuccessInternal(Pointee(EncodableValue(1)))); - - plugin.HandleMethodCall( - flutter::MethodCall("create", - std::make_unique(EncodableMap(args))), - std::move(second_create_result)); + + bool first_result_called = false; + std::function)> first_create_result = + [&first_result_called](ErrorOr reply) { + EXPECT_FALSE(first_result_called); // Ensure only one reply call. + first_result_called = true; + EXPECT_TRUE(reply.has_error()); + }; + + PlatformMediaSettings media_settings(PlatformResolutionPreset::max, true); + plugin.Create(MOCK_CAMERA_NAME, media_settings, + std::move(first_create_result)); + + EXPECT_TRUE(first_result_called); + + bool second_result_called = false; + std::function)> second_create_result = + [&second_result_called](ErrorOr reply) { + EXPECT_FALSE(second_result_called); // Ensure only one reply call. + second_result_called = true; + EXPECT_FALSE(reply.has_error()); + EXPECT_EQ(reply.value(), 1); + }; + + plugin.Create(MOCK_CAMERA_NAME, media_settings, + std::move(second_create_result)); + + EXPECT_TRUE(second_result_called); } TEST(CameraPlugin, InitializeHandlerCallStartPreview) { int64_t mock_camera_id = 1234; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -314,26 +304,28 @@ TEST(CameraPlugin, InitializeHandlerCallStartPreview) { .Times(1) .WillOnce(Return(false)); - EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kInitialize), _)) + EXPECT_CALL(*camera, + AddPendingSizeResult(Eq(PendingResultType::kInitialize), _)) .Times(1) - .WillOnce([cam = camera.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); + .WillOnce([cam = camera.get()]( + PendingResultType type, + std::function)> result) { + cam->pending_size_result_ = std::move(result); return true; }); EXPECT_CALL(*camera, GetCaptureController) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); + assert(cam->pending_size_result_); return cam->capture_controller_.get(); }); EXPECT_CALL(*capture_controller, StartPreview()) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); - return cam->pending_result_->Success(); + assert(cam->pending_size_result_); + return cam->pending_size_result_(PlatformSize(800, 600)); }); camera->camera_id_ = mock_camera_id; @@ -346,26 +338,23 @@ TEST(CameraPlugin, InitializeHandlerCallStartPreview) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + bool result_called = false; + std::function)> initialize_result = + [&result_called](ErrorOr reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_FALSE(reply.has_error()); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, - }; + plugin.Initialize(mock_camera_id, std::move(initialize_result)); - plugin.HandleMethodCall( - flutter::MethodCall("initialize", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, InitializeHandlerErrorOnInvalidCameraId) { int64_t mock_camera_id = 1234; int64_t missing_camera_id = 5678; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -379,7 +368,7 @@ TEST(CameraPlugin, InitializeHandlerErrorOnInvalidCameraId) { }); EXPECT_CALL(*camera, HasPendingResultByType).Times(0); - EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, AddPendingSizeResult).Times(0); EXPECT_CALL(*camera, GetCaptureController).Times(0); EXPECT_CALL(*capture_controller, StartPreview).Times(0); @@ -392,25 +381,22 @@ TEST(CameraPlugin, InitializeHandlerErrorOnInvalidCameraId) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + bool result_called = false; + std::function)> initialize_result = + [&result_called](ErrorOr reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_TRUE(reply.has_error()); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, - }; + plugin.Initialize(missing_camera_id, std::move(initialize_result)); - plugin.HandleMethodCall( - flutter::MethodCall("initialize", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, TakePictureHandlerCallsTakePictureWithPath) { int64_t mock_camera_id = 1234; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -428,26 +414,28 @@ TEST(CameraPlugin, TakePictureHandlerCallsTakePictureWithPath) { .Times(1) .WillOnce(Return(false)); - EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kTakePicture), _)) + EXPECT_CALL(*camera, + AddPendingStringResult(Eq(PendingResultType::kTakePicture), _)) .Times(1) - .WillOnce([cam = camera.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); + .WillOnce([cam = camera.get()]( + PendingResultType type, + std::function)> result) { + cam->pending_string_result_ = std::move(result); return true; }); EXPECT_CALL(*camera, GetCaptureController) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); + assert(cam->pending_string_result_); return cam->capture_controller_.get(); }); EXPECT_CALL(*capture_controller, TakePicture(EndsWith(".jpeg"))) .Times(1) .WillOnce([cam = camera.get()](const std::string& file_path) { - assert(cam->pending_result_); - return cam->pending_result_->Success(); + assert(cam->pending_string_result_); + return cam->pending_string_result_(file_path); }); camera->camera_id_ = mock_camera_id; @@ -460,26 +448,23 @@ TEST(CameraPlugin, TakePictureHandlerCallsTakePictureWithPath) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + bool result_called = false; + std::function)> take_picture_result = + [&result_called](ErrorOr reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_FALSE(reply.has_error()); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, - }; + plugin.TakePicture(mock_camera_id, std::move(take_picture_result)); - plugin.HandleMethodCall( - flutter::MethodCall("takePicture", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, TakePictureHandlerErrorOnInvalidCameraId) { int64_t mock_camera_id = 1234; int64_t missing_camera_id = 5678; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -493,7 +478,7 @@ TEST(CameraPlugin, TakePictureHandlerErrorOnInvalidCameraId) { }); EXPECT_CALL(*camera, HasPendingResultByType).Times(0); - EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, AddPendingStringResult).Times(0); EXPECT_CALL(*camera, GetCaptureController).Times(0); EXPECT_CALL(*capture_controller, TakePicture).Times(0); @@ -506,25 +491,22 @@ TEST(CameraPlugin, TakePictureHandlerErrorOnInvalidCameraId) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + bool result_called = false; + std::function)> take_picture_result = + [&result_called](ErrorOr reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_TRUE(reply.has_error()); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, - }; + plugin.TakePicture(missing_camera_id, std::move(take_picture_result)); - plugin.HandleMethodCall( - flutter::MethodCall("takePicture", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, StartVideoRecordingHandlerCallsStartRecordWithPath) { int64_t mock_camera_id = 1234; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -542,18 +524,20 @@ TEST(CameraPlugin, StartVideoRecordingHandlerCallsStartRecordWithPath) { .Times(1) .WillOnce(Return(false)); - EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kStartRecord), _)) + EXPECT_CALL(*camera, + AddPendingVoidResult(Eq(PendingResultType::kStartRecord), _)) .Times(1) - .WillOnce([cam = camera.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); + .WillOnce([cam = camera.get()]( + PendingResultType type, + std::function)> result) { + cam->pending_void_result_ = std::move(result); return true; }); EXPECT_CALL(*camera, GetCaptureController) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); + assert(cam->pending_void_result_); return cam->capture_controller_.get(); }); @@ -561,8 +545,8 @@ TEST(CameraPlugin, StartVideoRecordingHandlerCallsStartRecordWithPath) { .Times(1) .WillOnce([cam = camera.get()](const std::string& file_path, int64_t max_video_duration_ms) { - assert(cam->pending_result_); - return cam->pending_result_->Success(); + assert(cam->pending_void_result_); + return cam->pending_void_result_(std::nullopt); }); camera->camera_id_ = mock_camera_id; @@ -575,26 +559,24 @@ TEST(CameraPlugin, StartVideoRecordingHandlerCallsStartRecordWithPath) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + bool result_called = false; + std::function)> start_video_result = + [&result_called](std::optional reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_FALSE(reply); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, - }; + plugin.StartVideoRecording(mock_camera_id, PlatformVideoCaptureOptions(), + std::move(start_video_result)); - plugin.HandleMethodCall( - flutter::MethodCall("startVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, StartVideoRecordingHandlerCallsStartRecordWithPathAndCaptureDuration) { int64_t mock_camera_id = 1234; - int32_t mock_video_duration = 100000; - - std::unique_ptr initialize_result = - std::make_unique(); + int64_t mock_video_duration = 100000; std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -613,18 +595,20 @@ TEST(CameraPlugin, .Times(1) .WillOnce(Return(false)); - EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kStartRecord), _)) + EXPECT_CALL(*camera, + AddPendingVoidResult(Eq(PendingResultType::kStartRecord), _)) .Times(1) - .WillOnce([cam = camera.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); + .WillOnce([cam = camera.get()]( + PendingResultType type, + std::function)> result) { + cam->pending_void_result_ = std::move(result); return true; }); EXPECT_CALL(*camera, GetCaptureController) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); + assert(cam->pending_void_result_); return cam->capture_controller_.get(); }); @@ -633,8 +617,8 @@ TEST(CameraPlugin, .Times(1) .WillOnce([cam = camera.get()](const std::string& file_path, int64_t max_video_duration_ms) { - assert(cam->pending_result_); - return cam->pending_result_->Success(); + assert(cam->pending_void_result_); + return cam->pending_void_result_(std::nullopt); }); camera->camera_id_ = mock_camera_id; @@ -647,27 +631,25 @@ TEST(CameraPlugin, // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + bool result_called = false; + std::function)> start_video_result = + [&result_called](std::optional reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_FALSE(reply); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, - {EncodableValue("maxVideoDuration"), EncodableValue(mock_video_duration)}, - }; + plugin.StartVideoRecording(mock_camera_id, + PlatformVideoCaptureOptions(&mock_video_duration), + std::move(start_video_result)); - plugin.HandleMethodCall( - flutter::MethodCall("startVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, StartVideoRecordingHandlerErrorOnInvalidCameraId) { int64_t mock_camera_id = 1234; int64_t missing_camera_id = 5678; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -681,7 +663,7 @@ TEST(CameraPlugin, StartVideoRecordingHandlerErrorOnInvalidCameraId) { }); EXPECT_CALL(*camera, HasPendingResultByType).Times(0); - EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, AddPendingVoidResult).Times(0); EXPECT_CALL(*camera, GetCaptureController).Times(0); EXPECT_CALL(*capture_controller, StartRecord(_, -1)).Times(0); @@ -694,24 +676,23 @@ TEST(CameraPlugin, StartVideoRecordingHandlerErrorOnInvalidCameraId) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + bool result_called = false; + std::function)> start_video_result = + [&result_called](std::optional reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_TRUE(reply); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, - }; + plugin.StartVideoRecording(missing_camera_id, PlatformVideoCaptureOptions(), + std::move(start_video_result)); - plugin.HandleMethodCall( - flutter::MethodCall("startVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, StopVideoRecordingHandlerCallsStopRecord) { int64_t mock_camera_id = 1234; - - std::unique_ptr initialize_result = - std::make_unique(); + std::string mock_video_path = "path/to/video.mpeg"; std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -730,26 +711,28 @@ TEST(CameraPlugin, StopVideoRecordingHandlerCallsStopRecord) { .Times(1) .WillOnce(Return(false)); - EXPECT_CALL(*camera, AddPendingResult(Eq(PendingResultType::kStopRecord), _)) + EXPECT_CALL(*camera, + AddPendingStringResult(Eq(PendingResultType::kStopRecord), _)) .Times(1) - .WillOnce([cam = camera.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); + .WillOnce([cam = camera.get()]( + PendingResultType type, + std::function)> result) { + cam->pending_string_result_ = std::move(result); return true; }); EXPECT_CALL(*camera, GetCaptureController) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); + assert(cam->pending_string_result_); return cam->capture_controller_.get(); }); EXPECT_CALL(*capture_controller, StopRecord) .Times(1) - .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); - return cam->pending_result_->Success(); + .WillOnce([cam = camera.get(), mock_video_path]() { + assert(cam->pending_string_result_); + return cam->pending_string_result_(mock_video_path); }); camera->camera_id_ = mock_camera_id; @@ -762,26 +745,24 @@ TEST(CameraPlugin, StopVideoRecordingHandlerCallsStopRecord) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + bool result_called = false; + std::function)> stop_recording_result = + [&result_called, mock_video_path](ErrorOr reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_FALSE(reply.has_error()); + EXPECT_EQ(reply.value(), mock_video_path); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, - }; + plugin.StopVideoRecording(mock_camera_id, std::move(stop_recording_result)); - plugin.HandleMethodCall( - flutter::MethodCall("stopVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, StopVideoRecordingHandlerErrorOnInvalidCameraId) { int64_t mock_camera_id = 1234; int64_t missing_camera_id = 5678; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -795,7 +776,7 @@ TEST(CameraPlugin, StopVideoRecordingHandlerErrorOnInvalidCameraId) { }); EXPECT_CALL(*camera, HasPendingResultByType).Times(0); - EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, AddPendingStringResult).Times(0); EXPECT_CALL(*camera, GetCaptureController).Times(0); EXPECT_CALL(*capture_controller, StopRecord).Times(0); @@ -808,25 +789,23 @@ TEST(CameraPlugin, StopVideoRecordingHandlerErrorOnInvalidCameraId) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + bool result_called = false; + std::function)> stop_recording_result = + [&result_called](ErrorOr reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_TRUE(reply.has_error()); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, - }; + plugin.StopVideoRecording(missing_camera_id, + std::move(stop_recording_result)); - plugin.HandleMethodCall( - flutter::MethodCall("stopVideoRecording", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, ResumePreviewHandlerCallsResumePreview) { int64_t mock_camera_id = 1234; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -845,26 +824,27 @@ TEST(CameraPlugin, ResumePreviewHandlerCallsResumePreview) { .WillOnce(Return(false)); EXPECT_CALL(*camera, - AddPendingResult(Eq(PendingResultType::kResumePreview), _)) + AddPendingVoidResult(Eq(PendingResultType::kResumePreview), _)) .Times(1) - .WillOnce([cam = camera.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); + .WillOnce([cam = camera.get()]( + PendingResultType type, + std::function)> result) { + cam->pending_void_result_ = std::move(result); return true; }); EXPECT_CALL(*camera, GetCaptureController) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); + assert(cam->pending_void_result_); return cam->capture_controller_.get(); }); EXPECT_CALL(*capture_controller, ResumePreview) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); - return cam->pending_result_->Success(); + assert(cam->pending_void_result_); + return cam->pending_void_result_(std::nullopt); }); camera->camera_id_ = mock_camera_id; @@ -877,26 +857,23 @@ TEST(CameraPlugin, ResumePreviewHandlerCallsResumePreview) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + bool result_called = false; + std::function)> resume_preview_result = + [&result_called](std::optional reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_FALSE(reply); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, - }; + plugin.ResumePreview(mock_camera_id, std::move(resume_preview_result)); - plugin.HandleMethodCall( - flutter::MethodCall("resumePreview", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, ResumePreviewHandlerErrorOnInvalidCameraId) { int64_t mock_camera_id = 1234; int64_t missing_camera_id = 5678; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -910,7 +887,7 @@ TEST(CameraPlugin, ResumePreviewHandlerErrorOnInvalidCameraId) { }); EXPECT_CALL(*camera, HasPendingResultByType).Times(0); - EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, AddPendingVoidResult).Times(0); EXPECT_CALL(*camera, GetCaptureController).Times(0); EXPECT_CALL(*capture_controller, ResumePreview).Times(0); @@ -923,25 +900,22 @@ TEST(CameraPlugin, ResumePreviewHandlerErrorOnInvalidCameraId) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + bool result_called = false; + std::function)> resume_preview_result = + [&result_called](std::optional reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_TRUE(reply); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, - }; + plugin.ResumePreview(missing_camera_id, std::move(resume_preview_result)); - plugin.HandleMethodCall( - flutter::MethodCall("resumePreview", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, PausePreviewHandlerCallsPausePreview) { int64_t mock_camera_id = 1234; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -960,26 +934,27 @@ TEST(CameraPlugin, PausePreviewHandlerCallsPausePreview) { .WillOnce(Return(false)); EXPECT_CALL(*camera, - AddPendingResult(Eq(PendingResultType::kPausePreview), _)) + AddPendingVoidResult(Eq(PendingResultType::kPausePreview), _)) .Times(1) - .WillOnce([cam = camera.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); + .WillOnce([cam = camera.get()]( + PendingResultType type, + std::function)> result) { + cam->pending_void_result_ = std::move(result); return true; }); EXPECT_CALL(*camera, GetCaptureController) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); + assert(cam->pending_void_result_); return cam->capture_controller_.get(); }); EXPECT_CALL(*capture_controller, PausePreview) .Times(1) .WillOnce([cam = camera.get()]() { - assert(cam->pending_result_); - return cam->pending_result_->Success(); + assert(cam->pending_void_result_); + return cam->pending_void_result_(std::nullopt); }); camera->camera_id_ = mock_camera_id; @@ -992,26 +967,23 @@ TEST(CameraPlugin, PausePreviewHandlerCallsPausePreview) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(0); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(1); + bool result_called = false; + std::function)> pause_preview_result = + [&result_called](std::optional reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_FALSE(reply); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(mock_camera_id)}, - }; + plugin.PausePreview(mock_camera_id, std::move(pause_preview_result)); - plugin.HandleMethodCall( - flutter::MethodCall("pausePreview", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } TEST(CameraPlugin, PausePreviewHandlerErrorOnInvalidCameraId) { int64_t mock_camera_id = 1234; int64_t missing_camera_id = 5678; - std::unique_ptr initialize_result = - std::make_unique(); - std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -1025,7 +997,7 @@ TEST(CameraPlugin, PausePreviewHandlerErrorOnInvalidCameraId) { }); EXPECT_CALL(*camera, HasPendingResultByType).Times(0); - EXPECT_CALL(*camera, AddPendingResult).Times(0); + EXPECT_CALL(*camera, AddPendingVoidResult).Times(0); EXPECT_CALL(*camera, GetCaptureController).Times(0); EXPECT_CALL(*capture_controller, PausePreview).Times(0); @@ -1038,17 +1010,17 @@ TEST(CameraPlugin, PausePreviewHandlerErrorOnInvalidCameraId) { // Add mocked camera to plugins camera list. plugin.AddCamera(std::move(camera)); - EXPECT_CALL(*initialize_result, ErrorInternal).Times(1); - EXPECT_CALL(*initialize_result, SuccessInternal).Times(0); + bool result_called = false; + std::function)> pause_preview_result = + [&result_called](std::optional reply) { + EXPECT_FALSE(result_called); // Ensure only one reply call. + result_called = true; + EXPECT_TRUE(reply); + }; - EncodableMap args = { - {EncodableValue("cameraId"), EncodableValue(missing_camera_id)}, - }; + plugin.PausePreview(missing_camera_id, std::move(pause_preview_result)); - plugin.HandleMethodCall( - flutter::MethodCall("pausePreview", - std::make_unique(EncodableMap(args))), - std::move(initialize_result)); + EXPECT_TRUE(result_called); } } // namespace test diff --git a/packages/camera/camera_windows/windows/test/camera_test.cpp b/packages/camera/camera_windows/windows/test/camera_test.cpp index 9ab81897254d..986cb72b43a0 100644 --- a/packages/camera/camera_windows/windows/test/camera_test.cpp +++ b/packages/camera/camera_windows/windows/test/camera_test.cpp @@ -16,6 +16,7 @@ #include #include +#include "messages.g.h" #include "mocks.h" namespace camera_windows { @@ -48,17 +49,13 @@ TEST(Camera, InitCameraCreatesCaptureController) { EXPECT_TRUE(camera->GetCaptureController() == nullptr); - RecordSettings record_settings(false); - record_settings.fps = 5; - record_settings.video_bitrate = 200000; - record_settings.audio_bitrate = 32000; + PlatformMediaSettings media_settings(PlatformResolutionPreset::max, false); // Init camera with mock capture controller factory - bool result = - camera->InitCamera(std::move(capture_controller_factory), - std::make_unique().get(), - std::make_unique().get(), - ResolutionPreset::kAuto, record_settings); + bool result = camera->InitCamera( + std::move(capture_controller_factory), + std::make_unique().get(), + std::make_unique().get(), media_settings); EXPECT_TRUE(result); EXPECT_TRUE(camera->GetCaptureController() != nullptr); } @@ -84,391 +81,538 @@ TEST(Camera, InitCameraReportsFailure) { EXPECT_TRUE(camera->GetCaptureController() == nullptr); - RecordSettings record_settings(false); - record_settings.fps = 5; - record_settings.video_bitrate = 200000; - record_settings.audio_bitrate = 32000; + PlatformMediaSettings media_settings(PlatformResolutionPreset::max, false); // Init camera with mock capture controller factory - bool result = - camera->InitCamera(std::move(capture_controller_factory), - std::make_unique().get(), - std::make_unique().get(), - ResolutionPreset::kAuto, record_settings); + bool result = camera->InitCamera( + std::move(capture_controller_factory), + std::make_unique().get(), + std::make_unique().get(), media_settings); EXPECT_FALSE(result); EXPECT_TRUE(camera->GetCaptureController() != nullptr); } -TEST(Camera, AddPendingResultReturnsErrorForDuplicates) { +TEST(Camera, AddPendingVoidResultReturnsErrorForDuplicates) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr first_pending_result = - std::make_unique(); - std::unique_ptr second_pending_result = - std::make_unique(); - - EXPECT_CALL(*first_pending_result, ErrorInternal).Times(0); - EXPECT_CALL(*first_pending_result, SuccessInternal); - EXPECT_CALL(*second_pending_result, ErrorInternal).Times(1); + bool first_result_called = false; + std::function)> first_pending_result = + [&first_result_called](std::optional reply) { + first_result_called = true; + }; + bool second_result_called = false; + std::function)> second_pending_result = + [&second_result_called](std::optional reply) { + second_result_called = true; + EXPECT_TRUE(reply); + }; + + camera->AddPendingVoidResult(PendingResultType::kStartRecord, + std::move(first_pending_result)); + camera->AddPendingVoidResult(PendingResultType::kStartRecord, + std::move(second_pending_result)); + + EXPECT_FALSE(first_result_called); + EXPECT_TRUE(second_result_called); +} - camera->AddPendingResult(PendingResultType::kCreateCamera, - std::move(first_pending_result)); +TEST(Camera, AddPendingIntResultReturnsErrorForDuplicates) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + bool first_result_called = false; + std::function)> first_pending_result = + [&first_result_called](ErrorOr reply) { + first_result_called = true; + }; + bool second_result_called = false; + std::function)> second_pending_result = + [&second_result_called](ErrorOr reply) { + second_result_called = true; + EXPECT_TRUE(reply.has_error()); + }; + + camera->AddPendingIntResult(PendingResultType::kCreateCamera, + std::move(first_pending_result)); + camera->AddPendingIntResult(PendingResultType::kCreateCamera, + std::move(second_pending_result)); + + EXPECT_FALSE(first_result_called); + EXPECT_TRUE(second_result_called); +} - // This should fail - camera->AddPendingResult(PendingResultType::kCreateCamera, - std::move(second_pending_result)); +TEST(Camera, AddPendingStringResultReturnsErrorForDuplicates) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + bool first_result_called = false; + std::function)> first_pending_result = + [&first_result_called](ErrorOr reply) { + first_result_called = true; + }; + bool second_result_called = false; + std::function)> second_pending_result = + [&second_result_called](ErrorOr reply) { + second_result_called = true; + EXPECT_TRUE(reply.has_error()); + }; + + camera->AddPendingStringResult(PendingResultType::kStopRecord, + std::move(first_pending_result)); + camera->AddPendingStringResult(PendingResultType::kStopRecord, + std::move(second_pending_result)); + + EXPECT_FALSE(first_result_called); + EXPECT_TRUE(second_result_called); +} - // Mark pending result as succeeded - camera->OnCreateCaptureEngineSucceeded(0); +TEST(Camera, AddPendingSizeResultReturnsErrorForDuplicates) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + bool first_result_called = false; + std::function)> first_pending_result = + [&first_result_called](ErrorOr reply) { + first_result_called = true; + }; + bool second_result_called = false; + std::function)> second_pending_result = + [&second_result_called](ErrorOr reply) { + second_result_called = true; + EXPECT_TRUE(reply.has_error()); + }; + + camera->AddPendingSizeResult(PendingResultType::kInitialize, + std::move(first_pending_result)); + camera->AddPendingSizeResult(PendingResultType::kInitialize, + std::move(second_pending_result)); + + EXPECT_FALSE(first_result_called); + EXPECT_TRUE(second_result_called); } TEST(Camera, OnCreateCaptureEngineSucceededReturnsCameraId) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const int64_t texture_id = 12345; - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL( - *result, - SuccessInternal(Pointee(EncodableValue(EncodableMap( - {{EncodableValue("cameraId"), EncodableValue(texture_id)}}))))); - - camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); + bool result_called = false; + camera->AddPendingIntResult( + PendingResultType::kCreateCamera, + [&result_called, texture_id](ErrorOr reply) { + result_called = true; + EXPECT_FALSE(reply.has_error()); + EXPECT_EQ(reply.value(), texture_id); + }); camera->OnCreateCaptureEngineSucceeded(texture_id); + + EXPECT_TRUE(result_called); } TEST(Camera, CreateCaptureEngineReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); + bool result_called = false; + camera->AddPendingIntResult( + PendingResultType::kCreateCamera, + [&result_called, error_text](ErrorOr reply) { + result_called = true; + EXPECT_TRUE(reply.has_error()); + EXPECT_EQ(reply.error().code(), "camera_error"); + EXPECT_EQ(reply.error().message(), error_text); + }); camera->OnCreateCaptureEngineFailed(CameraResult::kError, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, CreateCaptureEngineReportsAccessDenied) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, - ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); + bool result_called = false; + camera->AddPendingIntResult( + PendingResultType::kCreateCamera, + [&result_called, error_text](ErrorOr reply) { + result_called = true; + EXPECT_TRUE(reply.has_error()); + EXPECT_EQ(reply.error().code(), "CameraAccessDenied"); + EXPECT_EQ(reply.error().message(), error_text); + }); camera->OnCreateCaptureEngineFailed(CameraResult::kAccessDenied, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, OnStartPreviewSucceededReturnsFrameSize) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const int32_t width = 123; const int32_t height = 456; - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL( - *result, - SuccessInternal(Pointee(EncodableValue(EncodableMap({ - {EncodableValue("previewWidth"), EncodableValue((float)width)}, - {EncodableValue("previewHeight"), EncodableValue((float)height)}, - }))))); - - camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); + bool result_called = false; + camera->AddPendingSizeResult( + PendingResultType::kInitialize, + [&result_called, width, height](ErrorOr reply) { + result_called = true; + EXPECT_FALSE(reply.has_error()); + EXPECT_EQ(reply.value().width(), width); + EXPECT_EQ(reply.value().height(), height); + }); camera->OnStartPreviewSucceeded(width, height); + + EXPECT_TRUE(result_called); } TEST(Camera, StartPreviewReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); + bool result_called = false; + camera->AddPendingSizeResult( + PendingResultType::kInitialize, + [&result_called, error_text](ErrorOr reply) { + result_called = true; + EXPECT_TRUE(reply.has_error()); + EXPECT_EQ(reply.error().code(), "camera_error"); + EXPECT_EQ(reply.error().message(), error_text); + }); camera->OnStartPreviewFailed(CameraResult::kError, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, StartPreviewReportsAccessDenied) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, - ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); + bool result_called = false; + camera->AddPendingSizeResult( + PendingResultType::kInitialize, + [&result_called, error_text](ErrorOr reply) { + result_called = true; + EXPECT_TRUE(reply.has_error()); + EXPECT_EQ(reply.error().code(), "CameraAccessDenied"); + EXPECT_EQ(reply.error().message(), error_text); + }); camera->OnStartPreviewFailed(CameraResult::kAccessDenied, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, OnPausePreviewSucceededReturnsSuccess) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(nullptr)); - - camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); + bool result_called = false; + camera->AddPendingVoidResult( + PendingResultType::kPausePreview, + [&result_called](std::optional reply) { + result_called = true; + EXPECT_FALSE(reply); + }); camera->OnPausePreviewSucceeded(); + + EXPECT_TRUE(result_called); } TEST(Camera, PausePreviewReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); + bool result_called = false; + camera->AddPendingVoidResult( + PendingResultType::kPausePreview, + [&result_called, error_text](std::optional reply) { + result_called = true; + EXPECT_TRUE(reply); + EXPECT_EQ(reply.value().code(), "camera_error"); + EXPECT_EQ(reply.value().message(), error_text); + }); camera->OnPausePreviewFailed(CameraResult::kError, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, PausePreviewReportsAccessDenied) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, - ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); + bool result_called = false; + camera->AddPendingVoidResult( + PendingResultType::kPausePreview, + [&result_called, error_text](std::optional reply) { + result_called = true; + EXPECT_TRUE(reply); + EXPECT_EQ(reply.value().code(), "CameraAccessDenied"); + EXPECT_EQ(reply.value().message(), error_text); + }); camera->OnPausePreviewFailed(CameraResult::kAccessDenied, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, OnResumePreviewSucceededReturnsSuccess) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(nullptr)); - - camera->AddPendingResult(PendingResultType::kResumePreview, - std::move(result)); + bool result_called = false; + camera->AddPendingVoidResult( + PendingResultType::kResumePreview, + [&result_called](std::optional reply) { + result_called = true; + EXPECT_FALSE(reply); + }); camera->OnResumePreviewSucceeded(); + + EXPECT_TRUE(result_called); } TEST(Camera, ResumePreviewReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kResumePreview, - std::move(result)); + bool result_called = false; + camera->AddPendingVoidResult( + PendingResultType::kResumePreview, + [&result_called, error_text](std::optional reply) { + result_called = true; + EXPECT_TRUE(reply); + EXPECT_EQ(reply.value().code(), "camera_error"); + EXPECT_EQ(reply.value().message(), error_text); + }); camera->OnResumePreviewFailed(CameraResult::kError, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, OnResumePreviewPermissionFailureReturnsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, - ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kResumePreview, - std::move(result)); + bool result_called = false; + camera->AddPendingVoidResult( + PendingResultType::kResumePreview, + [&result_called, error_text](std::optional reply) { + result_called = true; + EXPECT_TRUE(reply); + EXPECT_EQ(reply.value().code(), "CameraAccessDenied"); + EXPECT_EQ(reply.value().message(), error_text); + }); camera->OnResumePreviewFailed(CameraResult::kAccessDenied, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, OnStartRecordSucceededReturnsSuccess) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(nullptr)); - - camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); + bool result_called = false; + camera->AddPendingVoidResult( + PendingResultType::kStartRecord, + [&result_called](std::optional reply) { + result_called = true; + EXPECT_FALSE(reply); + }); camera->OnStartRecordSucceeded(); + + EXPECT_TRUE(result_called); } TEST(Camera, StartRecordReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); + bool result_called = false; + camera->AddPendingVoidResult( + PendingResultType::kStartRecord, + [&result_called, error_text](std::optional reply) { + result_called = true; + EXPECT_TRUE(reply); + EXPECT_EQ(reply.value().code(), "camera_error"); + EXPECT_EQ(reply.value().message(), error_text); + }); camera->OnStartRecordFailed(CameraResult::kError, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, StartRecordReportsAccessDenied) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, - ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); + bool result_called = false; + camera->AddPendingVoidResult( + PendingResultType::kStartRecord, + [&result_called, error_text](std::optional reply) { + result_called = true; + EXPECT_TRUE(reply); + EXPECT_EQ(reply.value().code(), "CameraAccessDenied"); + EXPECT_EQ(reply.value().message(), error_text); + }); camera->OnStartRecordFailed(CameraResult::kAccessDenied, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, OnStopRecordSucceededReturnsSuccess) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string file_path = "C:\temp\filename.mp4"; - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); - - camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); + bool result_called = false; + camera->AddPendingStringResult( + PendingResultType::kStopRecord, + [&result_called, file_path](ErrorOr reply) { + result_called = true; + EXPECT_FALSE(reply.has_error()); + EXPECT_EQ(reply.value(), file_path); + }); camera->OnStopRecordSucceeded(file_path); + + EXPECT_TRUE(result_called); } TEST(Camera, StopRecordReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); + bool result_called = false; + camera->AddPendingStringResult( + PendingResultType::kStopRecord, + [&result_called, error_text](ErrorOr reply) { + result_called = true; + EXPECT_TRUE(reply.has_error()); + EXPECT_EQ(reply.error().code(), "camera_error"); + EXPECT_EQ(reply.error().message(), error_text); + }); camera->OnStopRecordFailed(CameraResult::kError, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, StopRecordReportsAccessDenied) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, - ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); + bool result_called = false; + camera->AddPendingStringResult( + PendingResultType::kStopRecord, + [&result_called, error_text](ErrorOr reply) { + result_called = true; + EXPECT_TRUE(reply.has_error()); + EXPECT_EQ(reply.error().code(), "CameraAccessDenied"); + EXPECT_EQ(reply.error().message(), error_text); + }); camera->OnStopRecordFailed(CameraResult::kAccessDenied, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, OnTakePictureSucceededReturnsSuccess) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string file_path = "C:\\temp\\filename.jpeg"; - EXPECT_CALL(*result, ErrorInternal).Times(0); - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); - - camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); + bool result_called = false; + camera->AddPendingStringResult( + PendingResultType::kTakePicture, + [&result_called, file_path](ErrorOr reply) { + result_called = true; + EXPECT_FALSE(reply.has_error()); + EXPECT_EQ(reply.value(), file_path); + }); camera->OnTakePictureSucceeded(file_path); + + EXPECT_TRUE(result_called); } TEST(Camera, TakePictureReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); + bool result_called = false; + camera->AddPendingStringResult( + PendingResultType::kTakePicture, + [&result_called, error_text](ErrorOr reply) { + result_called = true; + EXPECT_TRUE(reply.has_error()); + EXPECT_EQ(reply.error().code(), "camera_error"); + EXPECT_EQ(reply.error().message(), error_text); + }); camera->OnTakePictureFailed(CameraResult::kError, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, TakePictureReportsAccessDenied) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - std::unique_ptr result = - std::make_unique(); const std::string error_text = "error_text"; - EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, - ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); - - camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); + bool result_called = false; + camera->AddPendingStringResult( + PendingResultType::kTakePicture, + [&result_called, error_text](ErrorOr reply) { + result_called = true; + EXPECT_TRUE(reply.has_error()); + EXPECT_EQ(reply.error().code(), "CameraAccessDenied"); + EXPECT_EQ(reply.error().message(), error_text); + }); camera->OnTakePictureFailed(CameraResult::kAccessDenied, error_text); + + EXPECT_TRUE(result_called); } TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { @@ -497,17 +641,12 @@ TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { // and second is camera closing message. EXPECT_CALL(*binary_messenger, Send(Eq(camera_channel), _, _, _)).Times(2); - RecordSettings record_settings; - record_settings.record_audio = false; - record_settings.fps = 5; - record_settings.video_bitrate = 200000; - record_settings.audio_bitrate = 32000; + PlatformMediaSettings media_settings(PlatformResolutionPreset::max, false); // Init camera with mock capture controller factory camera->InitCamera(std::move(capture_controller_factory), std::make_unique().get(), - binary_messenger.get(), ResolutionPreset::kAuto, - record_settings); + binary_messenger.get(), media_settings); // Pass camera id for camera camera->OnCreateCaptureEngineSucceeded(camera_id); diff --git a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp index 145879bae97b..adee8addd9c8 100644 --- a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp +++ b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp @@ -33,7 +33,8 @@ void MockInitCaptureController( CaptureControllerImpl* capture_controller, MockTextureRegistrar* texture_registrar, MockCaptureEngine* engine, MockCamera* camera, int64_t mock_texture_id, - const RecordSettings record_settings = RecordSettings(true)) { + const PlatformMediaSettings media_settings = + PlatformMediaSettings(PlatformResolutionPreset::max, true)) { ComPtr video_source = new MockMediaSource(); ComPtr audio_source = new MockMediaSource(); @@ -61,8 +62,7 @@ void MockInitCaptureController( EXPECT_CALL(*engine, Initialize).Times(1); bool result = capture_controller->InitCaptureDevice( - texture_registrar, MOCK_DEVICE_ID, ResolutionPreset::kAuto, - record_settings); + texture_registrar, MOCK_DEVICE_ID, media_settings); EXPECT_TRUE(result); @@ -260,8 +260,8 @@ TEST(CaptureController, InitCaptureEngineCanOnlyBeCalledOnce) { EXPECT_CALL(*camera, OnCreateCaptureEngineFailed).Times(1); bool result = capture_controller->InitCaptureDevice( - texture_registrar.get(), MOCK_DEVICE_ID, ResolutionPreset::kAuto, - RecordSettings(true)); + texture_registrar.get(), MOCK_DEVICE_ID, + PlatformMediaSettings(PlatformResolutionPreset::max, true)); EXPECT_FALSE(result); @@ -302,8 +302,8 @@ TEST(CaptureController, InitCaptureEngineReportsFailure) { .Times(1); bool result = capture_controller->InitCaptureDevice( - texture_registrar.get(), MOCK_DEVICE_ID, ResolutionPreset::kAuto, - RecordSettings(true)); + texture_registrar.get(), MOCK_DEVICE_ID, + PlatformMediaSettings(PlatformResolutionPreset::max, true)); EXPECT_FALSE(result); EXPECT_FALSE(engine->initialized_); @@ -347,8 +347,8 @@ TEST(CaptureController, InitCaptureEngineReportsAccessDenied) { .Times(1); bool result = capture_controller->InitCaptureDevice( - texture_registrar.get(), MOCK_DEVICE_ID, ResolutionPreset::kAuto, - RecordSettings(true)); + texture_registrar.get(), MOCK_DEVICE_ID, + PlatformMediaSettings(PlatformResolutionPreset::max, true)); EXPECT_FALSE(result); EXPECT_FALSE(engine->initialized_); @@ -734,16 +734,15 @@ TEST(CaptureController, StartRecordWithSettingsSuccess) { const auto kVideoBitrate = 200000; const auto kAudioBitrate = 32000; - RecordSettings record_settings; - record_settings.record_audio = true; - record_settings.fps = kFps; - record_settings.video_bitrate = kVideoBitrate; - record_settings.audio_bitrate = kAudioBitrate; + PlatformMediaSettings media_settings(PlatformResolutionPreset::max, true); + media_settings.set_frames_per_second(kFps); + media_settings.set_video_bitrate(kVideoBitrate); + media_settings.set_audio_bitrate(kAudioBitrate); // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id, - record_settings); + media_settings); ComPtr capture_source = new MockCaptureSource(); diff --git a/packages/camera/camera_windows/windows/test/mocks.h b/packages/camera/camera_windows/windows/test/mocks.h index 1f71a0e4d5dc..5d52797b0dde 100644 --- a/packages/camera/camera_windows/windows/test/mocks.h +++ b/packages/camera/camera_windows/windows/test/mocks.h @@ -13,6 +13,8 @@ #include #include +#include + #include "camera.h" #include "camera_plugin.h" #include "capture_controller.h" @@ -28,19 +30,6 @@ using flutter::EncodableMap; using flutter::EncodableValue; using ::testing::_; -class MockMethodResult : public flutter::MethodResult<> { - public: - ~MockMethodResult() = default; - - MOCK_METHOD(void, SuccessInternal, (const EncodableValue* result), - (override)); - MOCK_METHOD(void, ErrorInternal, - (const std::string& error_code, const std::string& error_message, - const EncodableValue* details), - (override)); - MOCK_METHOD(void, NotImplementedInternal, (), (override)); -}; - class MockBinaryMessenger : public flutter::BinaryMessenger { public: ~MockBinaryMessenger() = default; @@ -194,8 +183,21 @@ class MockCamera : public Camera { MOCK_METHOD(bool, HasDeviceId, (std::string & device_id), (const override)); MOCK_METHOD(bool, HasCameraId, (int64_t camera_id), (const override)); - MOCK_METHOD(bool, AddPendingResult, - (PendingResultType type, std::unique_ptr> result), + MOCK_METHOD(bool, AddPendingVoidResult, + (PendingResultType type, + std::function reply)> result), + (override)); + MOCK_METHOD(bool, AddPendingIntResult, + (PendingResultType type, + std::function reply)> result), + (override)); + MOCK_METHOD(bool, AddPendingStringResult, + (PendingResultType type, + std::function reply)> result), + (override)); + MOCK_METHOD(bool, AddPendingSizeResult, + (PendingResultType type, + std::function reply)> result), (override)); MOCK_METHOD(bool, HasPendingResultByType, (PendingResultType type), (const override)); @@ -206,12 +208,14 @@ class MockCamera : public Camera { MOCK_METHOD(bool, InitCamera, (flutter::TextureRegistrar * texture_registrar, flutter::BinaryMessenger* messenger, - ResolutionPreset resolution_preset, - const RecordSettings& record_settings), + const PlatformMediaSettings& media_settings), (override)); std::unique_ptr capture_controller_; - std::unique_ptr> pending_result_; + std::function reply)> pending_void_result_; + std::function reply)> pending_int_result_; + std::function reply)> pending_string_result_; + std::function reply)> pending_size_result_; std::string device_id_; int64_t camera_id_ = -1; }; @@ -236,8 +240,8 @@ class MockCaptureController : public CaptureController { MOCK_METHOD(bool, InitCaptureDevice, (flutter::TextureRegistrar * texture_registrar, - const std::string& device_id, ResolutionPreset resolution_preset, - const RecordSettings& record_settings), + const std::string& device_id, + const PlatformMediaSettings& media_settings), (override)); MOCK_METHOD(uint32_t, GetPreviewWidth, (), (const override));