camera: add ELinuxCamera dartPluginClass (#81)
Fixed https://github.com/sony/flutter-elinux/issues/211 Signed-off-by: Hidenori Matsubayashi <Hidenori.Matsubayashi@gmail.com>
This commit is contained in:
committed by
GitHub
parent
96106f35e7
commit
57f3aa88bf
@@ -1,3 +1,6 @@
|
||||
## 0.3.2
|
||||
* Add ELinuxCamera (camera dartPluginClass for eLinux) for flutter 3.13.
|
||||
|
||||
## 0.3.1
|
||||
* Update for flutter official camera plugin v0.10.5+3
|
||||
|
||||
|
||||
@@ -86,6 +86,12 @@ class CameraPlugin : public flutter::Plugin {
|
||||
void HandleTakePictureCall(
|
||||
const flutter::EncodableValue* message,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
void HandleGetMinExposureOffsetCall(
|
||||
const flutter::EncodableValue* message,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
void HandleGetMaxExposureOffsetCall(
|
||||
const flutter::EncodableValue* message,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
void HandleStartImageStreamCall(
|
||||
const flutter::EncodableValue* message,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
@@ -169,9 +175,9 @@ void CameraPlugin::HandleMethodCall(
|
||||
} else if (!method_name.compare(kCameraChannelApiSetExposurePoint)) {
|
||||
result->NotImplemented();
|
||||
} else if (!method_name.compare(kCameraChannelApiGetMinExposureOffset)) {
|
||||
result->NotImplemented();
|
||||
HandleGetMinExposureOffsetCall(method_call.arguments(), std::move(result));
|
||||
} else if (!method_name.compare(kCameraChannelApiGetMaxExposureOffset)) {
|
||||
result->NotImplemented();
|
||||
HandleGetMaxExposureOffsetCall(method_call.arguments(), std::move(result));
|
||||
} else if (!method_name.compare(kCameraChannelApiGetExposureOffsetStepSize)) {
|
||||
result->NotImplemented();
|
||||
} else if (!method_name.compare(kCameraChannelApiSetExposureOffset)) {
|
||||
@@ -308,6 +314,34 @@ void CameraPlugin::HandleTakePictureCall(
|
||||
});
|
||||
}
|
||||
|
||||
void CameraPlugin::HandleGetMinExposureOffsetCall(
|
||||
const flutter::EncodableValue* message,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
if (!camera_) {
|
||||
result->Error("Not found an active camera",
|
||||
"Check for creating a camera device");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement exposure control support
|
||||
double value = 0.0;
|
||||
result->Success(flutter::EncodableValue(value));
|
||||
}
|
||||
|
||||
void CameraPlugin::HandleGetMaxExposureOffsetCall(
|
||||
const flutter::EncodableValue* message,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
if (!camera_) {
|
||||
result->Error("Not found an active camera",
|
||||
"Check for creating a camera device");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement exposure control support
|
||||
double value = 0.0;
|
||||
result->Success(flutter::EncodableValue(value));
|
||||
}
|
||||
|
||||
void CameraPlugin::HandleStartImageStreamCall(
|
||||
const flutter::EncodableValue* message,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <flutter/standard_method_codec.h>
|
||||
|
||||
namespace {
|
||||
constexpr char kChannelName[] = "flutter.io/cameraPlugin/camera";
|
||||
constexpr char kChannelName[] = "plugins.flutter.io/camera/camera";
|
||||
constexpr char kChannelMethodInitialized[] = "initialized";
|
||||
}; // namespace
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <flutter/standard_method_codec.h>
|
||||
|
||||
namespace {
|
||||
constexpr char kChannelName[] = "flutter.io/cameraPlugin/device";
|
||||
constexpr char kChannelName[] = "plugins.flutter.io/camera/fromPlatform";
|
||||
constexpr char kChannelMethodOrientationChanged[] = "orientation_changed";
|
||||
|
||||
constexpr char kOrientation[] = "orientation";
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2023 Sony Group Corporation. All rights reserved.
|
||||
// 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.
|
||||
|
||||
export 'src/elinux_camera.dart';
|
||||
@@ -0,0 +1,645 @@
|
||||
// Copyright 2023 Sony Group Corporation. All rights reserved.
|
||||
// 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 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:camera_platform_interface/camera_platform_interface.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:stream_transform/stream_transform.dart';
|
||||
|
||||
import 'type_conversion.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
const MethodChannel _channel =
|
||||
MethodChannel('plugins.flutter.io/camera');
|
||||
|
||||
/// The ELinux implementation of [CameraPlatform] that uses method channels.
|
||||
class ELinuxCamera extends CameraPlatform {
|
||||
/// Registers this class as the default instance of [CameraPlatform].
|
||||
static void registerWith() {
|
||||
CameraPlatform.instance = ELinuxCamera();
|
||||
}
|
||||
|
||||
final Map<int, MethodChannel> _channels = <int, MethodChannel>{};
|
||||
|
||||
/// The name of the channel that device events from the platform side are
|
||||
/// sent on.
|
||||
@visibleForTesting
|
||||
static const String deviceEventChannelName =
|
||||
'plugins.flutter.io/camera/fromPlatform';
|
||||
|
||||
/// The controller we need to broadcast the different events coming
|
||||
/// from handleMethodCall, specific to camera events.
|
||||
///
|
||||
/// It is a `broadcast` because multiple controllers will connect to
|
||||
/// different stream views of this Controller.
|
||||
/// This is only exposed for test purposes. It shouldn't be used by clients of
|
||||
/// the plugin as it may break or change at any time.
|
||||
@visibleForTesting
|
||||
final StreamController<CameraEvent> cameraEventStreamController =
|
||||
StreamController<CameraEvent>.broadcast();
|
||||
|
||||
/// The controller we need to broadcast the different events coming
|
||||
/// from handleMethodCall, specific to general device events.
|
||||
///
|
||||
/// It is a `broadcast` because multiple controllers will connect to
|
||||
/// different stream views of this Controller.
|
||||
late final StreamController<DeviceEvent> _deviceEventStreamController =
|
||||
_createDeviceEventStreamController();
|
||||
|
||||
StreamController<DeviceEvent> _createDeviceEventStreamController() {
|
||||
// Set up the method handler lazily.
|
||||
const MethodChannel channel = MethodChannel(deviceEventChannelName);
|
||||
channel.setMethodCallHandler(_handleDeviceMethodCall);
|
||||
return StreamController<DeviceEvent>.broadcast();
|
||||
}
|
||||
|
||||
// The stream to receive frames from the native code.
|
||||
StreamSubscription<dynamic>? _platformImageStreamSubscription;
|
||||
|
||||
// The stream for vending frames to platform interface clients.
|
||||
StreamController<CameraImageData>? _frameStreamController;
|
||||
|
||||
Stream<CameraEvent> _cameraEvents(int cameraId) =>
|
||||
cameraEventStreamController.stream
|
||||
.where((CameraEvent event) => event.cameraId == cameraId);
|
||||
|
||||
@override
|
||||
Future<List<CameraDescription>> availableCameras() async {
|
||||
try {
|
||||
final List<Map<dynamic, dynamic>>? cameras = await _channel
|
||||
.invokeListMethod<Map<dynamic, dynamic>>('availableCameras');
|
||||
|
||||
if (cameras == null) {
|
||||
return <CameraDescription>[];
|
||||
}
|
||||
|
||||
return cameras.map((Map<dynamic, dynamic> camera) {
|
||||
return CameraDescription(
|
||||
name: camera['name']! as String,
|
||||
lensDirection:
|
||||
parseCameraLensDirection(camera['lensFacing']! as String),
|
||||
sensorOrientation: camera['sensorOrientation']! as int,
|
||||
);
|
||||
}).toList();
|
||||
} on PlatformException catch (e) {
|
||||
throw CameraException(e.code, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> createCamera(
|
||||
CameraDescription cameraDescription,
|
||||
ResolutionPreset? resolutionPreset, {
|
||||
bool enableAudio = false,
|
||||
}) async {
|
||||
try {
|
||||
final Map<String, dynamic>? reply = await _channel
|
||||
.invokeMapMethod<String, dynamic>('create', <String, dynamic>{
|
||||
'cameraName': cameraDescription.name,
|
||||
'resolutionPreset': resolutionPreset != null
|
||||
? _serializeResolutionPreset(resolutionPreset)
|
||||
: null,
|
||||
'enableAudio': enableAudio,
|
||||
});
|
||||
|
||||
return reply!['cameraId']! as int;
|
||||
} on PlatformException catch (e) {
|
||||
throw CameraException(e.code, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> initializeCamera(
|
||||
int cameraId, {
|
||||
ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown,
|
||||
}) {
|
||||
_channels.putIfAbsent(cameraId, () {
|
||||
final MethodChannel channel =
|
||||
MethodChannel('plugins.flutter.io/camera/camera$cameraId');
|
||||
channel.setMethodCallHandler(
|
||||
(MethodCall call) => handleCameraMethodCall(call, cameraId));
|
||||
return channel;
|
||||
});
|
||||
|
||||
final Completer<void> completer = Completer<void>();
|
||||
|
||||
onCameraInitialized(cameraId).first.then((CameraInitializedEvent value) {
|
||||
completer.complete();
|
||||
});
|
||||
|
||||
_channel.invokeMapMethod<String, dynamic>(
|
||||
'initialize',
|
||||
<String, dynamic>{
|
||||
'cameraId': cameraId,
|
||||
'imageFormatGroup': imageFormatGroup.name(),
|
||||
},
|
||||
).catchError(
|
||||
// TODO(srawlins): This should return a value of the future's type. This
|
||||
// will fail upcoming analysis checks with
|
||||
// https://github.com/flutter/flutter/issues/105750.
|
||||
// ignore: body_might_complete_normally_catch_error
|
||||
(Object error, StackTrace stackTrace) {
|
||||
if (error is! PlatformException) {
|
||||
// ignore: only_throw_errors
|
||||
throw error;
|
||||
}
|
||||
completer.completeError(
|
||||
CameraException(error.code, error.message),
|
||||
stackTrace,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose(int cameraId) async {
|
||||
if (_channels.containsKey(cameraId)) {
|
||||
final MethodChannel? cameraChannel = _channels[cameraId];
|
||||
cameraChannel?.setMethodCallHandler(null);
|
||||
_channels.remove(cameraId);
|
||||
}
|
||||
|
||||
await _channel.invokeMethod<void>(
|
||||
'dispose',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<CameraInitializedEvent> onCameraInitialized(int cameraId) {
|
||||
return _cameraEvents(cameraId).whereType<CameraInitializedEvent>();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<CameraResolutionChangedEvent> onCameraResolutionChanged(int cameraId) {
|
||||
return _cameraEvents(cameraId).whereType<CameraResolutionChangedEvent>();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<CameraClosingEvent> onCameraClosing(int cameraId) {
|
||||
return _cameraEvents(cameraId).whereType<CameraClosingEvent>();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<CameraErrorEvent> onCameraError(int cameraId) {
|
||||
return _cameraEvents(cameraId).whereType<CameraErrorEvent>();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<VideoRecordedEvent> onVideoRecordedEvent(int cameraId) {
|
||||
return _cameraEvents(cameraId).whereType<VideoRecordedEvent>();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<DeviceOrientationChangedEvent> onDeviceOrientationChanged() {
|
||||
return _deviceEventStreamController.stream
|
||||
.whereType<DeviceOrientationChangedEvent>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> lockCaptureOrientation(
|
||||
int cameraId,
|
||||
DeviceOrientation orientation,
|
||||
) async {
|
||||
await _channel.invokeMethod<String>(
|
||||
'lockCaptureOrientation',
|
||||
<String, dynamic>{
|
||||
'cameraId': cameraId,
|
||||
'orientation': serializeDeviceOrientation(orientation)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> unlockCaptureOrientation(int cameraId) async {
|
||||
await _channel.invokeMethod<String>(
|
||||
'unlockCaptureOrientation',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<XFile> takePicture(int cameraId) async {
|
||||
final String? path = await _channel.invokeMethod<String>(
|
||||
'takePicture',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
|
||||
if (path == null) {
|
||||
throw CameraException(
|
||||
'INVALID_PATH',
|
||||
'The platform "$defaultTargetPlatform" did not return a path while reporting success. The platform should always return a valid path or report an error.',
|
||||
);
|
||||
}
|
||||
|
||||
return XFile(path);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> prepareForVideoRecording() =>
|
||||
_channel.invokeMethod<void>('prepareForVideoRecording');
|
||||
|
||||
@override
|
||||
Future<void> startVideoRecording(int cameraId,
|
||||
{Duration? maxVideoDuration}) async {
|
||||
return startVideoCapturing(
|
||||
VideoCaptureOptions(cameraId, maxDuration: maxVideoDuration));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startVideoCapturing(VideoCaptureOptions options) async {
|
||||
await _channel.invokeMethod<void>(
|
||||
'startVideoRecording',
|
||||
<String, dynamic>{
|
||||
'cameraId': options.cameraId,
|
||||
'maxVideoDuration': options.maxDuration?.inMilliseconds,
|
||||
'enableStream': options.streamCallback != null,
|
||||
},
|
||||
);
|
||||
|
||||
if (options.streamCallback != null) {
|
||||
_installStreamController().stream.listen(options.streamCallback);
|
||||
_startStreamListener();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<XFile> stopVideoRecording(int cameraId) async {
|
||||
final String? path = await _channel.invokeMethod<String>(
|
||||
'stopVideoRecording',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
|
||||
if (path == null) {
|
||||
throw CameraException(
|
||||
'INVALID_PATH',
|
||||
'The platform "$defaultTargetPlatform" did not return a path while reporting success. The platform should always return a valid path or report an error.',
|
||||
);
|
||||
}
|
||||
|
||||
return XFile(path);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> pauseVideoRecording(int cameraId) => _channel.invokeMethod<void>(
|
||||
'pauseVideoRecording',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> resumeVideoRecording(int cameraId) =>
|
||||
_channel.invokeMethod<void>(
|
||||
'resumeVideoRecording',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
|
||||
@override
|
||||
Stream<CameraImageData> onStreamedFrameAvailable(int cameraId,
|
||||
{CameraImageStreamOptions? options}) {
|
||||
_installStreamController(onListen: _onFrameStreamListen);
|
||||
return _frameStreamController!.stream;
|
||||
}
|
||||
|
||||
StreamController<CameraImageData> _installStreamController(
|
||||
{Function()? onListen}) {
|
||||
_frameStreamController = StreamController<CameraImageData>(
|
||||
onListen: onListen ?? () {},
|
||||
onPause: _onFrameStreamPauseResume,
|
||||
onResume: _onFrameStreamPauseResume,
|
||||
onCancel: _onFrameStreamCancel,
|
||||
);
|
||||
return _frameStreamController!;
|
||||
}
|
||||
|
||||
void _onFrameStreamListen() {
|
||||
_startPlatformStream();
|
||||
}
|
||||
|
||||
Future<void> _startPlatformStream() async {
|
||||
await _channel.invokeMethod<void>('startImageStream');
|
||||
_startStreamListener();
|
||||
}
|
||||
|
||||
void _startStreamListener() {
|
||||
const EventChannel cameraEventChannel =
|
||||
EventChannel('plugins.flutter.io/camera/imageStream');
|
||||
_platformImageStreamSubscription =
|
||||
cameraEventChannel.receiveBroadcastStream().listen((dynamic imageData) {
|
||||
_frameStreamController!
|
||||
.add(cameraImageFromPlatformData(imageData as Map<dynamic, dynamic>));
|
||||
});
|
||||
}
|
||||
|
||||
FutureOr<void> _onFrameStreamCancel() async {
|
||||
await _channel.invokeMethod<void>('stopImageStream');
|
||||
await _platformImageStreamSubscription?.cancel();
|
||||
_platformImageStreamSubscription = null;
|
||||
_frameStreamController = null;
|
||||
}
|
||||
|
||||
void _onFrameStreamPauseResume() {
|
||||
throw CameraException('InvalidCall',
|
||||
'Pause and resume are not supported for onStreamedFrameAvailable');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setFlashMode(int cameraId, FlashMode mode) =>
|
||||
_channel.invokeMethod<void>(
|
||||
'setFlashMode',
|
||||
<String, dynamic>{
|
||||
'cameraId': cameraId,
|
||||
'mode': _serializeFlashMode(mode),
|
||||
},
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> setExposureMode(int cameraId, ExposureMode mode) =>
|
||||
_channel.invokeMethod<void>(
|
||||
'setExposureMode',
|
||||
<String, dynamic>{
|
||||
'cameraId': cameraId,
|
||||
'mode': serializeExposureMode(mode),
|
||||
},
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> setExposurePoint(int cameraId, Point<double>? point) {
|
||||
assert(point == null || point.x >= 0 && point.x <= 1);
|
||||
assert(point == null || point.y >= 0 && point.y <= 1);
|
||||
|
||||
return _channel.invokeMethod<void>(
|
||||
'setExposurePoint',
|
||||
<String, dynamic>{
|
||||
'cameraId': cameraId,
|
||||
'reset': point == null,
|
||||
'x': point?.x,
|
||||
'y': point?.y,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> getMinExposureOffset(int cameraId) async {
|
||||
final double? minExposureOffset = await _channel.invokeMethod<double>(
|
||||
'getMinExposureOffset',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
|
||||
return minExposureOffset!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> getMaxExposureOffset(int cameraId) async {
|
||||
final double? maxExposureOffset = await _channel.invokeMethod<double>(
|
||||
'getMaxExposureOffset',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
|
||||
return maxExposureOffset!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> getExposureOffsetStepSize(int cameraId) async {
|
||||
final double? stepSize = await _channel.invokeMethod<double>(
|
||||
'getExposureOffsetStepSize',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
|
||||
return stepSize!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> setExposureOffset(int cameraId, double offset) async {
|
||||
final double? appliedOffset = await _channel.invokeMethod<double>(
|
||||
'setExposureOffset',
|
||||
<String, dynamic>{
|
||||
'cameraId': cameraId,
|
||||
'offset': offset,
|
||||
},
|
||||
);
|
||||
|
||||
return appliedOffset!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setFocusMode(int cameraId, FocusMode mode) =>
|
||||
_channel.invokeMethod<void>(
|
||||
'setFocusMode',
|
||||
<String, dynamic>{
|
||||
'cameraId': cameraId,
|
||||
'mode': serializeFocusMode(mode),
|
||||
},
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> setFocusPoint(int cameraId, Point<double>? point) {
|
||||
assert(point == null || point.x >= 0 && point.x <= 1);
|
||||
assert(point == null || point.y >= 0 && point.y <= 1);
|
||||
|
||||
return _channel.invokeMethod<void>(
|
||||
'setFocusPoint',
|
||||
<String, dynamic>{
|
||||
'cameraId': cameraId,
|
||||
'reset': point == null,
|
||||
'x': point?.x,
|
||||
'y': point?.y,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> getMaxZoomLevel(int cameraId) async {
|
||||
final double? maxZoomLevel = await _channel.invokeMethod<double>(
|
||||
'getMaxZoomLevel',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
|
||||
return maxZoomLevel!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<double> getMinZoomLevel(int cameraId) async {
|
||||
final double? minZoomLevel = await _channel.invokeMethod<double>(
|
||||
'getMinZoomLevel',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
|
||||
return minZoomLevel!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setZoomLevel(int cameraId, double zoom) async {
|
||||
try {
|
||||
await _channel.invokeMethod<double>(
|
||||
'setZoomLevel',
|
||||
<String, dynamic>{
|
||||
'cameraId': cameraId,
|
||||
'zoom': zoom,
|
||||
},
|
||||
);
|
||||
} on PlatformException catch (e) {
|
||||
throw CameraException(e.code, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> pausePreview(int cameraId) async {
|
||||
await _channel.invokeMethod<double>(
|
||||
'pausePreview',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> resumePreview(int cameraId) async {
|
||||
await _channel.invokeMethod<double>(
|
||||
'resumePreview',
|
||||
<String, dynamic>{'cameraId': cameraId},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setDescriptionWhileRecording(
|
||||
CameraDescription description) async {
|
||||
await _channel.invokeMethod<double>(
|
||||
'setDescriptionWhileRecording',
|
||||
<String, dynamic>{
|
||||
'cameraName': description.name,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildPreview(int cameraId) {
|
||||
return Texture(textureId: cameraId);
|
||||
}
|
||||
|
||||
/// Returns the flash mode as a String.
|
||||
String _serializeFlashMode(FlashMode flashMode) {
|
||||
switch (flashMode) {
|
||||
case FlashMode.off:
|
||||
return 'off';
|
||||
case FlashMode.auto:
|
||||
return 'auto';
|
||||
case FlashMode.always:
|
||||
return 'always';
|
||||
case FlashMode.torch:
|
||||
return 'torch';
|
||||
}
|
||||
// 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 'off';
|
||||
}
|
||||
|
||||
/// Returns the resolution preset as a String.
|
||||
String _serializeResolutionPreset(ResolutionPreset resolutionPreset) {
|
||||
switch (resolutionPreset) {
|
||||
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';
|
||||
}
|
||||
// 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 'max';
|
||||
}
|
||||
|
||||
/// Converts messages received from the native platform into device events.
|
||||
Future<dynamic> _handleDeviceMethodCall(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case 'orientation_changed':
|
||||
final Map<String, Object?> arguments = _getArgumentDictionary(call);
|
||||
_deviceEventStreamController.add(DeviceOrientationChangedEvent(
|
||||
deserializeDeviceOrientation(arguments['orientation']! as String)));
|
||||
break;
|
||||
default:
|
||||
throw MissingPluginException();
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts messages received from the native platform into camera events.
|
||||
///
|
||||
/// This is only exposed for test purposes. It shouldn't be used by clients of
|
||||
/// the plugin as it may break or change at any time.
|
||||
@visibleForTesting
|
||||
Future<dynamic> handleCameraMethodCall(MethodCall call, int cameraId) async {
|
||||
switch (call.method) {
|
||||
case 'initialized':
|
||||
final Map<String, Object?> arguments = _getArgumentDictionary(call);
|
||||
cameraEventStreamController.add(CameraInitializedEvent(
|
||||
cameraId,
|
||||
arguments['previewWidth']! as double,
|
||||
arguments['previewHeight']! as double,
|
||||
deserializeExposureMode(arguments['exposureMode']! as String),
|
||||
arguments['exposurePointSupported']! as bool,
|
||||
deserializeFocusMode(arguments['focusMode']! as String),
|
||||
arguments['focusPointSupported']! as bool,
|
||||
));
|
||||
break;
|
||||
case 'resolution_changed':
|
||||
final Map<String, Object?> arguments = _getArgumentDictionary(call);
|
||||
cameraEventStreamController.add(CameraResolutionChangedEvent(
|
||||
cameraId,
|
||||
arguments['captureWidth']! as double,
|
||||
arguments['captureHeight']! as double,
|
||||
));
|
||||
break;
|
||||
case 'camera_closing':
|
||||
cameraEventStreamController.add(CameraClosingEvent(
|
||||
cameraId,
|
||||
));
|
||||
break;
|
||||
case 'video_recorded':
|
||||
final Map<String, Object?> arguments = _getArgumentDictionary(call);
|
||||
cameraEventStreamController.add(VideoRecordedEvent(
|
||||
cameraId,
|
||||
XFile(arguments['path']! as String),
|
||||
arguments['maxVideoDuration'] != null
|
||||
? Duration(milliseconds: arguments['maxVideoDuration']! as int)
|
||||
: null,
|
||||
));
|
||||
break;
|
||||
case 'error':
|
||||
final Map<String, Object?> arguments = _getArgumentDictionary(call);
|
||||
cameraEventStreamController.add(CameraErrorEvent(
|
||||
cameraId,
|
||||
arguments['description']! as String,
|
||||
));
|
||||
break;
|
||||
default:
|
||||
throw MissingPluginException();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the arguments of [call] as typed string-keyed Map.
|
||||
///
|
||||
/// This does not do any type validation, so is only safe to call if the
|
||||
/// arguments are known to be a map.
|
||||
Map<String, Object?> _getArgumentDictionary(MethodCall call) {
|
||||
return (call.arguments as Map<Object?, Object?>).cast<String, Object?>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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 'dart:typed_data';
|
||||
|
||||
import 'package:camera_platform_interface/camera_platform_interface.dart';
|
||||
|
||||
/// Converts method channel call [data] for `receivedImageStreamData` to a
|
||||
/// [CameraImageData].
|
||||
CameraImageData cameraImageFromPlatformData(Map<dynamic, dynamic> data) {
|
||||
return CameraImageData(
|
||||
format: _cameraImageFormatFromPlatformData(data['format']),
|
||||
height: data['height'] as int,
|
||||
width: data['width'] as int,
|
||||
lensAperture: data['lensAperture'] as double?,
|
||||
sensorExposureTime: data['sensorExposureTime'] as int?,
|
||||
sensorSensitivity: data['sensorSensitivity'] as double?,
|
||||
planes: List<CameraImagePlane>.unmodifiable(
|
||||
(data['planes'] as List<dynamic>).map<CameraImagePlane>(
|
||||
(dynamic planeData) => _cameraImagePlaneFromPlatformData(
|
||||
planeData as Map<dynamic, dynamic>))));
|
||||
}
|
||||
|
||||
CameraImageFormat _cameraImageFormatFromPlatformData(dynamic data) {
|
||||
return CameraImageFormat(_imageFormatGroupFromPlatformData(data), raw: data);
|
||||
}
|
||||
|
||||
ImageFormatGroup _imageFormatGroupFromPlatformData(dynamic data) {
|
||||
switch (data) {
|
||||
case 35: // android.graphics.ImageFormat.YUV_420_888
|
||||
return ImageFormatGroup.yuv420;
|
||||
case 256: // android.graphics.ImageFormat.JPEG
|
||||
return ImageFormatGroup.jpeg;
|
||||
case 17: // android.graphics.ImageFormat.NV21
|
||||
return ImageFormatGroup.nv21;
|
||||
}
|
||||
|
||||
return ImageFormatGroup.unknown;
|
||||
}
|
||||
|
||||
CameraImagePlane _cameraImagePlaneFromPlatformData(Map<dynamic, dynamic> data) {
|
||||
return CameraImagePlane(
|
||||
bytes: data['bytes'] as Uint8List,
|
||||
bytesPerPixel: data['bytesPerPixel'] as int?,
|
||||
bytesPerRow: data['bytesPerRow'] as int,
|
||||
height: data['height'] as int?,
|
||||
width: data['width'] as int?);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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:camera_platform_interface/camera_platform_interface.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// Parses a string into a corresponding CameraLensDirection.
|
||||
CameraLensDirection parseCameraLensDirection(String string) {
|
||||
switch (string) {
|
||||
case 'front':
|
||||
return CameraLensDirection.front;
|
||||
case 'back':
|
||||
return CameraLensDirection.back;
|
||||
case 'external':
|
||||
return CameraLensDirection.external;
|
||||
}
|
||||
throw ArgumentError('Unknown CameraLensDirection value');
|
||||
}
|
||||
|
||||
/// Returns the device orientation as a String.
|
||||
String serializeDeviceOrientation(DeviceOrientation orientation) {
|
||||
switch (orientation) {
|
||||
case DeviceOrientation.portraitUp:
|
||||
return 'portraitUp';
|
||||
case DeviceOrientation.portraitDown:
|
||||
return 'portraitDown';
|
||||
case DeviceOrientation.landscapeRight:
|
||||
return 'landscapeRight';
|
||||
case DeviceOrientation.landscapeLeft:
|
||||
return 'landscapeLeft';
|
||||
}
|
||||
// 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 'portraitUp';
|
||||
}
|
||||
|
||||
/// Returns the device orientation for a given String.
|
||||
DeviceOrientation deserializeDeviceOrientation(String str) {
|
||||
switch (str) {
|
||||
case 'portraitUp':
|
||||
return DeviceOrientation.portraitUp;
|
||||
case 'portraitDown':
|
||||
return DeviceOrientation.portraitDown;
|
||||
case 'landscapeRight':
|
||||
return DeviceOrientation.landscapeRight;
|
||||
case 'landscapeLeft':
|
||||
return DeviceOrientation.landscapeLeft;
|
||||
default:
|
||||
throw ArgumentError('"$str" is not a valid DeviceOrientation value');
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ name: camera_elinux
|
||||
description: A Flutter plugin for getting information about and controlling the
|
||||
camera on eLinux. Supports previewing the camera feed, capturing images, capturing video,
|
||||
and streaming image buffers to dart.
|
||||
version: 0.3.1
|
||||
version: 0.3.2
|
||||
homepage: https://github.com/sony/flutter-elinux-plugins
|
||||
repository: https://github.com/sony/flutter-elinux-plugins/tree/main/packages/camera
|
||||
|
||||
@@ -11,6 +11,7 @@ environment:
|
||||
flutter: ">=3.3.0"
|
||||
|
||||
dependencies:
|
||||
camera_platform_interface: ^2.5.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
@@ -19,3 +20,4 @@ flutter:
|
||||
platforms:
|
||||
elinux:
|
||||
pluginClass: CameraElinuxPlugin
|
||||
dartPluginClass: ELinuxCamera
|
||||
|
||||
Reference in New Issue
Block a user