[camera] update for camera ^0.10.0+1 / flutter 3.3.0 release (#57)

* Update for camera ^0.10.0+1 / flutter 3.3.0 release

Signed-off-by: Hidenori Matsubayashi <hidenori.matsubayashi@gmail.com>
This commit is contained in:
Hidenori Matsubayashi
2022-09-19 14:47:24 +09:00
committed by GitHub
parent 02260a1eab
commit 1dd2b8bbfa
9 changed files with 389 additions and 197 deletions

View File

@ -1,3 +1,6 @@
## 0.2.1
* Update for camera v0.10.0+1 / flutter 3.3.0 release
## 0.2.0
* Fix wrong orientation issue.
* Change the camera type from back to external.

View File

@ -19,7 +19,7 @@ $ sudo apt install libgstreamer-plugins-base1.0-dev \
### pubspec.yaml
```yaml
dependencies:
camera: ^0.8.1+7
camera: ^0.10.0+1
camera_elinux:
git:
url: https://github.com/sony/flutter-elinux-plugins.git

View File

@ -1,4 +1,6 @@
cmake_minimum_required(VERSION 3.15)
# stop cmake from taking make from CMAKE_SYSROOT
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
project(runner LANGUAGES CXX)
set(BINARY_NAME "camera_example")
@ -7,21 +9,12 @@ cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# Basically we use this include when we got the following error:
# fatal error: 'bits/c++config.h' file not found
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
include_directories(SYSTEM ${FLUTTER_SYSTEM_INCLUDE_DIRECTORIES})
endif()
endif()
# Basically we use this include when we got the following error:
# fatal error: 'bits/c++config.h' file not found
include_directories(SYSTEM ${FLUTTER_SYSTEM_INCLUDE_DIRECTORIES})
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# Configure build options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)

View File

@ -1,4 +1,4 @@
// Copyright 2021 Sony Corporation. All rights reserved.
// Copyright 2022 Sony Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -13,8 +13,6 @@
#include <unordered_map>
#include <vector>
// todo: Supports other types besides int, string.
namespace commandline {
namespace {
@ -39,30 +37,49 @@ class CommandOptions {
CommandOptions() = default;
~CommandOptions() = default;
void AddWithoutValue(const std::string& name, const std::string& short_name,
const std::string& description, bool required) {
void AddWithoutValue(const std::string& name,
const std::string& short_name,
const std::string& description,
bool required) {
Add<std::string, ReaderString>(name, short_name, description, "",
ReaderString(), required, false);
}
void AddInt(const std::string& name, const std::string& short_name,
const std::string& description, const int& default_value,
void AddInt(const std::string& name,
const std::string& short_name,
const std::string& description,
const int& default_value,
bool required) {
Add<int, ReaderInt>(name, short_name, description, default_value,
ReaderInt(), required, true);
}
void AddString(const std::string& name, const std::string& short_name,
void AddDouble(const std::string& name,
const std::string& short_name,
const std::string& description,
const double& default_value,
bool required) {
Add<double, ReaderDouble>(name, short_name, description, default_value,
ReaderDouble(), required, true);
}
void AddString(const std::string& name,
const std::string& short_name,
const std::string& description,
const std::string& default_value, bool required) {
const std::string& default_value,
bool required) {
Add<std::string, ReaderString>(name, short_name, description, default_value,
ReaderString(), required, true);
}
template <typename T, typename F>
void Add(const std::string& name, const std::string& short_name,
const std::string& description, const T default_value,
F reader = F(), bool required = true, bool required_value = true) {
void Add(const std::string& name,
const std::string& short_name,
const std::string& description,
const T default_value,
F reader = F(),
bool required = true,
bool required_value = true) {
if (options_.find(name) != options_.end()) {
std::cerr << "Already registered option: " << name << std::endl;
return;
@ -213,7 +230,7 @@ class CommandOptions {
}
size_t index_adjust = 0;
constexpr int kSpacerNum = 5;
constexpr int kSpacerNum = 10;
auto need_value = registration_order_options_[i]->IsRequiredValue();
ostream << kOptionStyleNormal
<< registration_order_options_[i]->GetName();
@ -240,10 +257,17 @@ class CommandOptions {
std::string operator()(const std::string& value) { return value; }
};
struct ReaderDouble {
double operator()(const std::string& value) { return std::stod(value); }
};
class Option {
public:
Option(const std::string& name, const std::string& short_name,
const std::string& description, bool required, bool required_value)
Option(const std::string& name,
const std::string& short_name,
const std::string& description,
bool required,
bool required_value)
: name_(name),
short_name_(short_name),
description_(description),
@ -288,9 +312,12 @@ class CommandOptions {
template <typename T>
class OptionValue : public Option {
public:
OptionValue(const std::string& name, const std::string& short_name,
const std::string& description, const T& default_value,
bool required, bool required_value)
OptionValue(const std::string& name,
const std::string& short_name,
const std::string& description,
const T& default_value,
bool required,
bool required_value)
: Option(name, short_name, description, required, required_value),
default_value_(default_value),
value_(default_value){};
@ -316,10 +343,18 @@ class CommandOptions {
template <typename T, typename F>
class OptionValueReader : public OptionValue<T> {
public:
OptionValueReader(const std::string& name, const std::string& short_name,
const std::string& description, const T default_value,
F reader, bool required, bool required_value)
: OptionValue<T>(name, short_name, description, default_value, required,
OptionValueReader(const std::string& name,
const std::string& short_name,
const std::string& description,
const T default_value,
F reader,
bool required,
bool required_value)
: OptionValue<T>(name,
short_name,
description,
default_value,
required,
required_value),
reader_(reader) {}
~OptionValueReader() = default;

View File

@ -1,4 +1,4 @@
// Copyright 2021 Sony Corporation. All rights reserved.
// Copyright 2022 Sony Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -14,18 +14,24 @@
class FlutterEmbedderOptions {
public:
FlutterEmbedderOptions() {
options_.AddString("bundle", "b", "Path to Flutter app bundle", "./",
false);
options_.AddString("bundle", "b", "Path to Flutter project bundle",
"./bundle", true);
options_.AddWithoutValue("no-cursor", "n", "No mouse cursor/pointer",
false);
options_.AddInt("rotation", "r",
"Window rotation(degree) [0(default)|90|180|270]", 0,
false);
options_.AddDouble("force-scale-factor", "s",
"Force a scale factor instead using default value", 1.0,
false);
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
// no more options.
#elif defined(FLUTTER_TARGET_BACKEND_X11)
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
false);
options_.AddInt("width", "w", "Flutter app window width", 1280, false);
options_.AddInt("height", "h", "Flutter app window height", 720, false);
options_.AddInt("width", "w", "Window width", 1280, false);
options_.AddInt("height", "h", "Window height", 720, false);
#else // FLUTTER_TARGET_BACKEND_WAYLAND
options_.AddWithoutValue("onscreen-keyboard", "k",
"Enable on-screen keyboard", false);
@ -33,8 +39,8 @@ class FlutterEmbedderOptions {
"Enable window decorations", false);
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
false);
options_.AddInt("width", "w", "Flutter app window width", 1280, false);
options_.AddInt("height", "h", "Flutter app window height", 720, false);
options_.AddInt("width", "w", "Window width", 1280, false);
options_.AddInt("height", "h", "Window height", 720, false);
#endif
}
~FlutterEmbedderOptions() = default;
@ -48,6 +54,34 @@ class FlutterEmbedderOptions {
bundle_path_ = options_.GetValue<std::string>("bundle");
use_mouse_cursor_ = !options_.Exist("no-cursor");
if (options_.Exist("rotation")) {
switch (options_.GetValue<int>("rotation")) {
case 90:
window_view_rotation_ =
flutter::FlutterViewController::ViewRotation::kRotation_90;
break;
case 180:
window_view_rotation_ =
flutter::FlutterViewController::ViewRotation::kRotation_180;
break;
case 270:
window_view_rotation_ =
flutter::FlutterViewController::ViewRotation::kRotation_270;
break;
default:
window_view_rotation_ =
flutter::FlutterViewController::ViewRotation::kRotation_0;
break;
}
}
if (options_.Exist("force-scale-factor")) {
is_force_scale_factor_ = true;
scale_factor_ = options_.GetValue<double>("force-scale-factor");
} else {
is_force_scale_factor_ = false;
scale_factor_ = 1.0;
}
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
@ -86,6 +120,11 @@ class FlutterEmbedderOptions {
}
int WindowWidth() const { return window_width_; }
int WindowHeight() const { return window_height_; }
flutter::FlutterViewController::ViewRotation WindowRotation() const {
return window_view_rotation_;
}
bool IsForceScaleFactor() const { return is_force_scale_factor_; }
double ScaleFactor() const { return scale_factor_; }
private:
commandline::CommandOptions options_;
@ -98,6 +137,10 @@ class FlutterEmbedderOptions {
flutter::FlutterViewController::ViewMode::kNormal;
int window_width_ = 1280;
int window_height_ = 720;
flutter::FlutterViewController::ViewRotation window_view_rotation_ =
flutter::FlutterViewController::ViewRotation::kRotation_0;
bool is_force_scale_factor_;
double scale_factor_;
};
#endif // FLUTTER_EMBEDDER_OPTIONS_

View File

@ -1,4 +1,4 @@
// Copyright 2021 Sony Corporation. All rights reserved.
// Copyright 2022 Sony Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@ -29,9 +29,12 @@ int main(int argc, char** argv) {
view_properties.width = options.WindowWidth();
view_properties.height = options.WindowHeight();
view_properties.view_mode = options.WindowViewMode();
view_properties.view_rotation = options.WindowRotation();
view_properties.use_mouse_cursor = options.IsUseMouseCursor();
view_properties.use_onscreen_keyboard = options.IsUseOnscreenKeyboard();
view_properties.use_window_decoration = options.IsUseWindowDecoraation();
view_properties.force_scale_factor = options.IsForceScaleFactor();
view_properties.scale_factor = options.ScaleFactor();
// The Flutter instance hosted by this window.
FlutterWindow window(view_properties, project);

View File

@ -2,19 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:video_player/video_player.dart';
/// Camera example home widget.
class CameraExampleHome extends StatefulWidget {
/// Default Constructor
const CameraExampleHome({Key? key}) : super(key: key);
@override
_CameraExampleHomeState createState() {
State<CameraExampleHome> createState() {
return _CameraExampleHomeState();
}
}
@ -33,7 +36,7 @@ IconData getCameraLensIcon(CameraLensDirection direction) {
}
}
void logError(String code, String? message) {
void _logError(String code, String? message) {
if (message != null) {
print('Error: $code\nError Message: $message');
} else {
@ -105,6 +108,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
super.dispose();
}
// #docregion AppLifecycle
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController? cameraController = controller;
@ -120,13 +124,11 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
onNewCameraSelected(cameraController.description);
}
}
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
// #enddocregion AppLifecycle
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text('Camera example'),
),
@ -134,12 +136,6 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
children: <Widget>[
Expanded(
child: Container(
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Center(
child: _cameraPreviewWidget(),
),
),
decoration: BoxDecoration(
color: Colors.black,
border: Border.all(
@ -150,6 +146,12 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
width: 3.0,
),
),
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Center(
child: _cameraPreviewWidget(),
),
),
),
),
_captureControlRowWidget(),
@ -157,7 +159,6 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
Padding(
padding: const EdgeInsets.all(5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
_cameraTogglesRowWidget(),
_thumbnailWidget(),
@ -194,7 +195,8 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
behavior: HitTestBehavior.opaque,
onScaleStart: _handleScaleStart,
onScaleUpdate: _handleScaleUpdate,
onTapDown: (details) => onViewFinderTap(details, constraints),
onTapDown: (TapDownDetails details) =>
onViewFinderTap(details, constraints),
);
}),
),
@ -228,27 +230,34 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
localVideoController == null && imageFile == null
? Container()
: SizedBox(
child: (localVideoController == null)
? Image.file(File(imageFile!.path))
: Container(
child: Center(
child: AspectRatio(
aspectRatio:
localVideoController.value.size != null
? localVideoController
.value.aspectRatio
: 1.0,
child: VideoPlayer(localVideoController)),
),
decoration: BoxDecoration(
border: Border.all(color: Colors.pink)),
),
width: 64.0,
height: 64.0,
),
if (localVideoController == null && imageFile == null)
Container()
else
SizedBox(
width: 64.0,
height: 64.0,
child: (localVideoController == null)
? (
// The captured image on the web contains a network-accessible URL
// pointing to a location within the browser. It may be displayed
// either with Image.network or Image.memory after loading the image
// bytes to memory.
kIsWeb
? Image.network(imageFile!.path)
: Image.file(File(imageFile!.path)))
: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.pink)),
child: Center(
child: AspectRatio(
aspectRatio:
localVideoController.value.size != null
? localVideoController.value.aspectRatio
: 1.0,
child: VideoPlayer(localVideoController)),
),
),
),
],
),
),
@ -258,27 +267,33 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
/// Display a bar with buttons to change the flash and exposure modes
Widget _modeControlRowWidget() {
return Column(
children: [
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: Icon(Icons.flash_on),
icon: const Icon(Icons.flash_on),
color: Colors.blue,
onPressed: controller != null ? onFlashModeButtonPressed : null,
),
IconButton(
icon: Icon(Icons.exposure),
color: Colors.blue,
onPressed:
controller != null ? onExposureModeButtonPressed : null,
),
IconButton(
icon: Icon(Icons.filter_center_focus),
color: Colors.blue,
onPressed: controller != null ? onFocusModeButtonPressed : null,
),
// The exposure and focus mode are currently not supported on the web.
...!kIsWeb
? <Widget>[
IconButton(
icon: const Icon(Icons.exposure),
color: Colors.blue,
onPressed: controller != null
? onExposureModeButtonPressed
: null,
),
IconButton(
icon: const Icon(Icons.filter_center_focus),
color: Colors.blue,
onPressed:
controller != null ? onFocusModeButtonPressed : null,
)
]
: <Widget>[],
IconButton(
icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute),
color: Colors.blue,
@ -308,10 +323,9 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
child: ClipRect(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
children: <Widget>[
IconButton(
icon: Icon(Icons.flash_off),
icon: const Icon(Icons.flash_off),
color: controller?.value.flashMode == FlashMode.off
? Colors.orange
: Colors.blue,
@ -320,7 +334,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
: null,
),
IconButton(
icon: Icon(Icons.flash_auto),
icon: const Icon(Icons.flash_auto),
color: controller?.value.flashMode == FlashMode.auto
? Colors.orange
: Colors.blue,
@ -329,7 +343,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
: null,
),
IconButton(
icon: Icon(Icons.flash_on),
icon: const Icon(Icons.flash_on),
color: controller?.value.flashMode == FlashMode.always
? Colors.orange
: Colors.blue,
@ -338,7 +352,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
: null,
),
IconButton(
icon: Icon(Icons.highlight),
icon: const Icon(Icons.highlight),
color: controller?.value.flashMode == FlashMode.torch
? Colors.orange
: Colors.blue,
@ -354,11 +368,15 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
Widget _exposureModeControlRowWidget() {
final ButtonStyle styleAuto = TextButton.styleFrom(
// TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724
// ignore: deprecated_member_use
primary: controller?.value.exposureMode == ExposureMode.auto
? Colors.orange
: Colors.blue,
);
final ButtonStyle styleLocked = TextButton.styleFrom(
// TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724
// ignore: deprecated_member_use
primary: controller?.value.exposureMode == ExposureMode.locked
? Colors.orange
: Colors.blue,
@ -370,16 +388,14 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
child: Container(
color: Colors.grey.shade50,
child: Column(
children: [
Center(
child: Text("Exposure Mode"),
children: <Widget>[
const Center(
child: Text('Exposure Mode'),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
children: <Widget>[
TextButton(
child: Text('AUTO'),
style: styleAuto,
onPressed: controller != null
? () =>
@ -391,24 +407,31 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
showInSnackBar('Resetting exposure point');
}
},
child: const Text('AUTO'),
),
TextButton(
child: Text('LOCKED'),
style: styleLocked,
onPressed: controller != null
? () =>
onSetExposureModeButtonPressed(ExposureMode.locked)
: null,
child: const Text('LOCKED'),
),
TextButton(
style: styleLocked,
onPressed: controller != null
? () => controller!.setExposureOffset(0.0)
: null,
child: const Text('RESET OFFSET'),
),
],
),
Center(
child: Text("Exposure Offset"),
const Center(
child: Text('Exposure Offset'),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
children: <Widget>[
Text(_minAvailableExposureOffset.toString()),
Slider(
value: _currentExposureOffset,
@ -432,11 +455,15 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
Widget _focusModeControlRowWidget() {
final ButtonStyle styleAuto = TextButton.styleFrom(
// TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724
// ignore: deprecated_member_use
primary: controller?.value.focusMode == FocusMode.auto
? Colors.orange
: Colors.blue,
);
final ButtonStyle styleLocked = TextButton.styleFrom(
// TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724
// ignore: deprecated_member_use
primary: controller?.value.focusMode == FocusMode.locked
? Colors.orange
: Colors.blue,
@ -448,31 +475,32 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
child: Container(
color: Colors.grey.shade50,
child: Column(
children: [
Center(
child: Text("Focus Mode"),
children: <Widget>[
const Center(
child: Text('Focus Mode'),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: [
children: <Widget>[
TextButton(
child: Text('AUTO'),
style: styleAuto,
onPressed: controller != null
? () => onSetFocusModeButtonPressed(FocusMode.auto)
: null,
onLongPress: () {
if (controller != null) controller!.setFocusPoint(null);
if (controller != null) {
controller!.setFocusPoint(null);
}
showInSnackBar('Resetting focus point');
},
child: const Text('AUTO'),
),
TextButton(
child: Text('LOCKED'),
style: styleLocked,
onPressed: controller != null
? () => onSetFocusModeButtonPressed(FocusMode.locked)
: null,
child: const Text('LOCKED'),
),
],
),
@ -489,7 +517,6 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: const Icon(Icons.camera_alt),
@ -512,8 +539,8 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
IconButton(
icon: cameraController != null &&
cameraController.value.isRecordingPaused
? Icon(Icons.play_arrow)
: Icon(Icons.pause),
? const Icon(Icons.play_arrow)
: const Icon(Icons.pause),
color: Colors.blue,
onPressed: cameraController != null &&
cameraController.value.isInitialized &&
@ -531,7 +558,16 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
cameraController.value.isRecordingVideo
? onStopButtonPressed
: null,
)
),
IconButton(
icon: const Icon(Icons.pause_presentation),
color:
cameraController != null && cameraController.value.isPreviewPaused
? Colors.red
: Colors.blue,
onPressed:
cameraController == null ? null : onPausePreviewButtonPressed,
),
],
);
}
@ -540,18 +576,21 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
Widget _cameraTogglesRowWidget() {
final List<Widget> toggles = <Widget>[];
final onChanged = (CameraDescription? description) {
void onChanged(CameraDescription? description) {
if (description == null) {
return;
}
onNewCameraSelected(description);
};
}
if (cameras.isEmpty) {
return const Text('No camera found');
if (_cameras.isEmpty) {
_ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async {
showInSnackBar('No camera found.');
});
return const Text('None');
} else {
for (CameraDescription cameraDescription in cameras) {
for (final CameraDescription cameraDescription in _cameras) {
toggles.add(
SizedBox(
width: 90.0,
@ -575,8 +614,8 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
void showInSnackBar(String message) {
// ignore: deprecated_member_use
_scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text(message)));
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message)));
}
void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) {
@ -586,7 +625,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
final CameraController cameraController = controller!;
final offset = Offset(
final Offset offset = Offset(
details.localPosition.dx / constraints.maxWidth,
details.localPosition.dy / constraints.maxHeight,
);
@ -594,21 +633,32 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
cameraController.setFocusPoint(offset);
}
void onNewCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) {
await controller!.dispose();
Future<void> onNewCameraSelected(CameraDescription cameraDescription) async {
final CameraController? oldController = controller;
if (oldController != null) {
// `controller` needs to be set to null before getting disposed,
// to avoid a race condition when we use the controller that is being
// disposed. This happens when camera permission dialog shows up,
// which triggers `didChangeAppLifecycleState`, which disposes and
// re-creates the controller.
controller = null;
await oldController.dispose();
}
final CameraController cameraController = CameraController(
cameraDescription,
ResolutionPreset.medium,
kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium,
enableAudio: enableAudio,
imageFormatGroup: ImageFormatGroup.jpeg,
);
controller = cameraController;
// If the controller is updated then update the UI.
cameraController.addListener(() {
if (mounted) setState(() {});
if (mounted) {
setState(() {});
}
if (cameraController.value.hasError) {
showInSnackBar(
'Camera error ${cameraController.value.errorDescription}');
@ -617,22 +667,52 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
try {
await cameraController.initialize();
await Future.wait([
cameraController
.getMinExposureOffset()
.then((value) => _minAvailableExposureOffset = value),
cameraController
.getMaxExposureOffset()
.then((value) => _maxAvailableExposureOffset = value),
await Future.wait(<Future<Object?>>[
// The exposure mode is currently not supported on the web.
...!kIsWeb
? <Future<Object?>>[
cameraController.getMinExposureOffset().then(
(double value) => _minAvailableExposureOffset = value),
cameraController
.getMaxExposureOffset()
.then((double value) => _maxAvailableExposureOffset = value)
]
: <Future<Object?>>[],
cameraController
.getMaxZoomLevel()
.then((value) => _maxAvailableZoom = value),
.then((double value) => _maxAvailableZoom = value),
cameraController
.getMinZoomLevel()
.then((value) => _minAvailableZoom = value),
.then((double value) => _minAvailableZoom = value),
]);
} on CameraException catch (e) {
_showCameraException(e);
switch (e.code) {
case 'CameraAccessDenied':
showInSnackBar('You have denied camera access.');
break;
case 'CameraAccessDeniedWithoutPrompt':
// iOS only
showInSnackBar('Please go to Settings app to enable camera access.');
break;
case 'CameraAccessRestricted':
// iOS only
showInSnackBar('Camera access is restricted.');
break;
case 'AudioAccessDenied':
showInSnackBar('You have denied audio access.');
break;
case 'AudioAccessDeniedWithoutPrompt':
// iOS only
showInSnackBar('Please go to Settings app to enable audio access.');
break;
case 'AudioAccessRestricted':
// iOS only
showInSnackBar('Audio access is restricted.');
break;
default:
_showCameraException(e);
break;
}
}
if (mounted) {
@ -648,7 +728,9 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
videoController?.dispose();
videoController = null;
});
if (file != null) showInSnackBar('Picture saved to ${file.path}');
if (file != null) {
showInSnackBar('Picture saved to ${file.path}');
}
}
});
}
@ -690,50 +772,64 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
}
}
void onCaptureOrientationLockButtonPressed() async {
if (controller != null) {
final CameraController cameraController = controller!;
if (cameraController.value.isCaptureOrientationLocked) {
await cameraController.unlockCaptureOrientation();
showInSnackBar('Capture orientation unlocked');
} else {
await cameraController.lockCaptureOrientation();
showInSnackBar(
'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
Future<void> onCaptureOrientationLockButtonPressed() async {
try {
if (controller != null) {
final CameraController cameraController = controller!;
if (cameraController.value.isCaptureOrientationLocked) {
await cameraController.unlockCaptureOrientation();
showInSnackBar('Capture orientation unlocked');
} else {
await cameraController.lockCaptureOrientation();
showInSnackBar(
'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
}
}
} on CameraException catch (e) {
_showCameraException(e);
}
}
void onSetFlashModeButtonPressed(FlashMode mode) {
setFlashMode(mode).then((_) {
if (mounted) setState(() {});
if (mounted) {
setState(() {});
}
showInSnackBar('Flash mode set to ${mode.toString().split('.').last}');
});
}
void onSetExposureModeButtonPressed(ExposureMode mode) {
setExposureMode(mode).then((_) {
if (mounted) setState(() {});
if (mounted) {
setState(() {});
}
showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}');
});
}
void onSetFocusModeButtonPressed(FocusMode mode) {
setFocusMode(mode).then((_) {
if (mounted) setState(() {});
if (mounted) {
setState(() {});
}
showInSnackBar('Focus mode set to ${mode.toString().split('.').last}');
});
}
void onVideoRecordButtonPressed() {
startVideoRecording().then((_) {
if (mounted) setState(() {});
if (mounted) {
setState(() {});
}
});
}
void onStopButtonPressed() {
stopVideoRecording().then((file) {
if (mounted) setState(() {});
stopVideoRecording().then((XFile? file) {
if (mounted) {
setState(() {});
}
if (file != null) {
showInSnackBar('Video recorded to ${file.path}');
videoFile = file;
@ -742,16 +838,39 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
});
}
Future<void> onPausePreviewButtonPressed() async {
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isInitialized) {
showInSnackBar('Error: select a camera first.');
return;
}
if (cameraController.value.isPreviewPaused) {
await cameraController.resumePreview();
} else {
await cameraController.pausePreview();
}
if (mounted) {
setState(() {});
}
}
void onPauseButtonPressed() {
pauseVideoRecording().then((_) {
if (mounted) setState(() {});
if (mounted) {
setState(() {});
}
showInSnackBar('Video recording paused');
});
}
void onResumeButtonPressed() {
resumeVideoRecording().then((_) {
if (mounted) setState(() {});
if (mounted) {
setState(() {});
}
showInSnackBar('Video recording resumed');
});
}
@ -796,7 +915,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isRecordingVideo) {
return null;
return;
}
try {
@ -811,7 +930,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
final CameraController? cameraController = controller;
if (cameraController == null || !cameraController.value.isRecordingVideo) {
return null;
return;
}
try {
@ -882,12 +1001,16 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
return;
}
final VideoPlayerController vController =
VideoPlayerController.file(File(videoFile!.path));
final VideoPlayerController vController = kIsWeb
? VideoPlayerController.network(videoFile!.path)
: VideoPlayerController.file(File(videoFile!.path));
videoPlayerListener = () {
if (videoController != null && videoController!.value.size != null) {
// Refreshing the state to update video player with the correct ratio.
if (mounted) setState(() {});
if (mounted) {
setState(() {});
}
videoController!.removeListener(videoPlayerListener!);
}
};
@ -917,7 +1040,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
}
try {
XFile file = await cameraController.takePicture();
final XFile file = await cameraController.takePicture();
return file;
} on CameraException catch (e) {
_showCameraException(e);
@ -926,41 +1049,35 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
}
void _showCameraException(CameraException e) {
logError(e.code, e.description);
_logError(e.code, e.description);
showInSnackBar('Error: ${e.code}\n${e.description}');
}
}
/// CameraApp is the Main Application.
class CameraApp extends StatelessWidget {
/// Default Constructor
const CameraApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
scrollBehavior: MyCustomScrollBehavior(),
return const MaterialApp(
home: CameraExampleHome(),
);
}
}
class MyCustomScrollBehavior extends MaterialScrollBehavior {
// Override behavior methods and getters like dragDevices
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
List<CameraDescription> cameras = [];
List<CameraDescription> _cameras = <CameraDescription>[];
Future<void> main() async {
// Fetch the available cameras before initializing the app.
try {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
_cameras = await availableCameras();
} on CameraException catch (e) {
logError(e.code, e.description);
_logError(e.code, e.description);
}
runApp(CameraApp());
runApp(const CameraApp());
}
/// This allows a value of type T or T? to be treated as a value of type T?.

View File

@ -4,18 +4,17 @@ publish_to: none
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=1.22.0"
flutter: ">=2.10.0"
dependencies:
camera: ^0.8.1+7
camera: ^0.10.0+1
camera_elinux:
path: ../
flutter:
sdk: flutter
path_provider: ^2.0.0
path_provider_elinux:
path: ../../path_provider
video_player: ^2.1.4
video_player: ^2.4.7
video_player_elinux:
path: ../../video_player
@ -26,7 +25,6 @@ dev_dependencies:
sdk: flutter
integration_test:
sdk: flutter
pedantic: ^1.10.0
flutter:
uses-material-design: true

View File

@ -2,13 +2,13 @@ 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.2.0
version: 0.2.1
homepage: https://github.com/sony/flutter-elinux-plugins
repository: https://github.com/sony/flutter-elinux-plugins/tree/main/packages/camera
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=1.20.0"
flutter: ">=2.10.0"
dependencies:
flutter: