Compare commits

...

5 Commits

Author SHA1 Message Date
Vitaliy Zarubin 5e1013b156 [camera] Remove debug and commits 1 year ago
Vitaliy Zarubin f5c0dcfe3f [camera] Add pixel buffer 1 year ago
Vitaliy Zarubin ff482f15f3 [camera] Add pixel buffer 1 year ago
Vitaliy Zarubin 264a219740 [camera] Update dependency 1 year ago
Vitaliy Zarubin f41d9d9204 [camera] Init plugin camera_aurora 1 year ago
  1. 2
      example/aurora/desktop/ru.auroraos.flutter_example_packages.desktop
  2. 2
      example/aurora/rpm/ru.auroraos.flutter_example_packages.spec
  3. 64
      example/lib/packages/camera/extension/camera_controller.dart
  4. 35
      example/lib/packages/camera/extension/camera_description.dart
  5. 7
      example/lib/packages/camera/extension/export.dart
  6. 21
      example/lib/packages/camera/extension/uint8_list.dart
  7. 11
      example/lib/packages/camera/model.dart
  8. 26
      example/lib/packages/camera/package.dart
  9. 162
      example/lib/packages/camera/page.dart
  10. 109
      example/lib/packages/camera/widgets/camera_body.dart
  11. 130
      example/lib/packages/camera/widgets/camera_control_panel.dart
  12. 96
      example/lib/packages/camera/widgets/cameras_loading.dart
  13. 83
      example/lib/packages/camera/widgets/cameras_select.dart
  14. 2
      example/lib/packages/packages.dart
  15. 12
      example/pubspec.yaml
  16. 30
      packages/camera/camera_aurora/.gitignore
  17. 25
      packages/camera/camera_aurora/README.md
  18. 4
      packages/camera/camera_aurora/analysis_options.yaml
  19. 40
      packages/camera/camera_aurora/aurora/CMakeLists.txt
  20. 217
      packages/camera/camera_aurora/aurora/camera_aurora_plugin.cpp
  21. 47
      packages/camera/camera_aurora/aurora/include/camera_aurora/camera_aurora_plugin.h
  22. 14
      packages/camera/camera_aurora/aurora/include/camera_aurora/globals.h
  23. 57
      packages/camera/camera_aurora/aurora/include/camera_aurora/texture_camera.h
  24. 21
      packages/camera/camera_aurora/aurora/include/camera_aurora/texture_camera_egl_helper.h
  25. 33
      packages/camera/camera_aurora/aurora/include/camera_aurora/texture_camera_pixels_helper.h
  26. 218
      packages/camera/camera_aurora/aurora/texture_camera.cpp
  27. 42
      packages/camera/camera_aurora/aurora/texture_camera_egl_helper.cpp
  28. 77
      packages/camera/camera_aurora/aurora/texture_camera_pixels_helper.cpp
  29. 96
      packages/camera/camera_aurora/lib/camera_aurora.dart
  30. 159
      packages/camera/camera_aurora/lib/camera_aurora_method_channel.dart
  31. 77
      packages/camera/camera_aurora/lib/camera_aurora_platform_interface.dart
  32. 38
      packages/camera/camera_aurora/lib/camera_data.dart
  33. 99
      packages/camera/camera_aurora/lib/camera_viewfinder.dart
  34. 30
      packages/camera/camera_aurora/pubspec.yaml
  35. 14
      packages/xdga_directories/lib/xdga_directories.dart

2
example/aurora/desktop/ru.auroraos.flutter_example_packages.desktop

@ -7,6 +7,6 @@ Exec=/usr/bin/ru.auroraos.flutter_example_packages
X-Nemo-Application-Type=silica-qt5
[X-Application]
Permissions=DeviceInfo;UserDirs;Sensors
Permissions=DeviceInfo;UserDirs;Sensors;Camera
OrganizationName=ru.auroraos
ApplicationName=flutter_example_packages

2
example/aurora/rpm/ru.auroraos.flutter_example_packages.spec

@ -12,6 +12,8 @@ BuildRequires: cmake
BuildRequires: pkgconfig(sqlite3)
BuildRequires: pkgconfig(flutter-embedder)
BuildRequires: pkgconfig(sensord-qt5)
BuildRequires: pkgconfig(glesv2)
BuildRequires: pkgconfig(streamcamera)
%description
%{summary}.

64
example/lib/packages/camera/extension/camera_controller.dart

@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart' as provider;
import 'package:path_provider/path_provider.dart';
import 'uint8_list.dart';
extension ExtCameraController on CameraController {
/// Get photo
Future<File?> takeImageFile() async {
if (!value.isInitialized) {
return null;
}
if (value.isTakingPicture) {
return null;
}
try {
// Get image
final picture = await takePicture();
// Get bytes
final bytes = await picture.readAsBytes();
// Get path
final directory = await provider.getExternalStorageDirectories(
type: StorageDirectory.pictures);
// Save to file
final file = await bytes.writeToFile(directory![0], picture);
// Return saved file
return file;
} on CameraException catch (e) {
debugPrint(e.description);
return null;
}
}
/// Get video
Future<File?> takeVideoFileAndStopRecording() async {
if (!value.isInitialized) {
return null;
}
if (!value.isRecordingVideo) {
return null;
}
try {
// Stop recording
final video = await stopVideoRecording();
// Get bytes
final bytes = await video.readAsBytes();
// Get path
final directory = await provider.getApplicationDocumentsDirectory();
// Save to file
final file = await bytes.writeToFile(directory, video);
// Return saved file
return file;
} on CameraException catch (e) {
debugPrint(e.description);
return null;
}
}
}

35
example/lib/packages/camera/extension/camera_description.dart

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
extension ExtCameraDescription on CameraDescription {
/// Get [CameraController]
Future<CameraController?> getCameraController() async {
final cameraController = CameraController(
this,
ResolutionPreset.medium,
enableAudio: true,
imageFormatGroup: ImageFormatGroup.jpeg,
);
try {
await cameraController.initialize();
return cameraController;
} on CameraException catch (e) {
debugPrint(e.description);
return null;
}
}
/// Get Icon by direction
IconData getIcon() {
switch (lensDirection) {
case CameraLensDirection.back:
return Icons.camera_rear;
case CameraLensDirection.front:
return Icons.camera_front;
case CameraLensDirection.external:
return Icons.camera;
}
}
}

7
example/lib/packages/camera/extension/export.dart

@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
library camera;
export './camera_controller.dart';
export './camera_description.dart';
export './uint8_list.dart';

21
example/lib/packages/camera/extension/uint8_list.dart

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'package:path/path.dart' as p;
extension ExtUint8List on Uint8List {
Future<File> writeToFile(Directory directory, XFile file) {
if (file.name.isEmpty) {
/// @todo XFile.fromData not work name file
return File(p.join(directory.path,
'${DateTime.now().millisecondsSinceEpoch}.${file.mimeType == 'video/mp4' ? 'mp4' : 'jpg'}'))
.writeAsBytes(this);
} else {
return File(p.join(directory.path, file.name)).writeAsBytes(this);
}
}
}

11
example/lib/packages/camera/model.dart

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/widgets.dart';
import 'package:scoped_model/scoped_model.dart';
/// Model for [CameraPage]
class CameraModel extends Model {
/// Get [ScopedModel]
static CameraModel of(BuildContext context) =>
ScopedModel.of<CameraModel>(context);
}

26
example/lib/packages/camera/package.dart

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter_example_packages/base/package/package_page.dart';
import 'package:get_it/get_it.dart';
import 'model.dart';
import 'page.dart';
/// Package values
final packageCamera = PackagePage(
key: 'camera',
descEN: '''
A Flutter plugin for Aurora, iOS, Android and Web allowing access
to the device cameras.
''',
descRU: '''
Плагин Flutter для Aurora, iOS, Android и Интернета, обеспечивающий доступ
к камерам устройства.
''',
version: '0.10.5+2',
isPlatformDependent: true,
page: () => CameraPage(),
init: () {
GetIt.instance.registerFactory<CameraModel>(() => CameraModel());
},
);

162
example/lib/packages/camera/page.dart

@ -0,0 +1,162 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/base/di/app_di.dart';
import 'package:flutter_example_packages/base/package/package.dart';
import 'package:flutter_example_packages/packages/camera/extension/export.dart';
import 'package:flutter_example_packages/packages/camera/widgets/camera_body.dart';
import 'package:flutter_example_packages/packages/camera/widgets/camera_control_panel.dart';
import 'package:flutter_example_packages/packages/camera/widgets/cameras_loading.dart';
import 'package:flutter_example_packages/packages/camera/widgets/cameras_select.dart';
import 'package:flutter_example_packages/theme/colors.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_example_packages/widgets/layouts/block_layout.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'model.dart';
import 'package.dart';
class CameraPage extends AppStatefulWidget {
CameraPage({
super.key,
});
final Package package = packageCamera;
@override
State<CameraPage> createState() => _CameraPageState();
}
class _CameraPageState extends AppState<CameraPage> {
CameraController? _cameraController;
File? _photo;
File? _video;
bool _loading = false;
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
return BlockLayout<CameraModel>(
model: getIt<CameraModel>(),
title: widget.package.key,
builder: (context, child, model) {
return Padding(
padding: const EdgeInsets.all(20),
child: CamerasLoading(
package: widget.package,
builder: (context, cameras) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
flex: 1,
child: Column(
children: [
Flexible(
flex: 0,
child: CamerasSelect(
disable: _loading,
cameras: cameras,
onChange: (controller) => setState(() {
_photo = null;
_cameraController = controller;
}),
),
),
const SizedBox(height: 5),
Flexible(
flex: 1,
child: CameraBody(
loading: _loading,
controller: _cameraController,
photo: _photo,
),
),
CameraControlPanel(
disable: _loading,
controller: _cameraController,
photo: _photo,
// Start record video
onRecordingStart: () => _cameraController
?.startVideoRecording()
.then((picture) {
if (mounted) {
setState(() {});
}
}),
// Pause record video if record already start
onRecordingPause: () => _cameraController
?.pauseVideoRecording()
.then((value) {
if (mounted) {
setState(() {});
}
}),
// Resume record video if record already start and will pause
onRecordingResume: () => _cameraController
?.resumeVideoRecording()
.then((value) {
if (mounted) {
setState(() {});
}
}),
// Clear photo
onClearPhoto: () {
if (mounted) {
setState(() {
_photo = null;
});
}
},
// Stop record video and save to file (custom extension)
onRecordingStop: () => _cameraController
?.takeVideoFileAndStopRecording()
.then((video) {
if (mounted) {
showMessage(video);
setState(() {
_video = video;
});
}
}),
// Take photo and save to file (custom extension)
onTakePhoto: () => setState(() {
_loading = true;
_cameraController?.takeImageFile().then((photo) {
if (mounted) {
showMessage(photo);
setState(() {
_loading = false;
_photo = photo;
});
}
});
}),
),
],
),
),
],
);
},
),
);
},
);
}
void showMessage(File? file) {
if (file != null) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("File save to: ${file.path}"),
backgroundColor: AppColors.secondary,
));
}
}
}

109
example/lib/packages/camera/widgets/camera_body.dart

@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/theme/radius.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_example_packages/widgets/texts/export.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CameraBody extends AppStatefulWidget {
const CameraBody({
super.key,
required this.loading,
required this.controller,
required this.photo,
});
final bool loading;
final CameraController? controller;
final File? photo;
@override
State<CameraBody> createState() => _CameraBodyState();
}
class _CameraBodyState extends AppState<CameraBody> {
@override
void dispose() {
widget.controller?.dispose();
super.dispose();
}
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
return ClipRRect(
borderRadius: AppRadius.small,
child: Container(
color: Colors.black87,
child: Stack(
children: [
if (widget.loading)
const Center(
child: TextTitleLarge(
'Loading...',
color: Colors.white,
),
),
// Show info if not select camera
if (!widget.loading &&
widget.controller == null &&
widget.photo == null)
const Center(
child: TextTitleLarge(
'Select camera',
color: Colors.white,
),
),
// Show camera preview
if (!widget.loading &&
widget.controller != null &&
widget.controller!.value.isInitialized &&
widget.photo == null)
Container(
width: double.infinity,
height: double.infinity,
alignment: Alignment.center,
child: widget.controller!.buildPreview(),
),
// Show dot when recording is active
if (!widget.loading &&
(widget.controller?.value.isRecordingVideo ?? false) &&
!(widget.controller?.value.isRecordingPaused ?? false))
Padding(
padding: const EdgeInsets.all(16),
child: ClipOval(
child: Container(
color: Colors.red,
width: 16,
height: 16,
),
),
),
// Show take phone
if (!widget.loading && widget.photo != null)
Container(
width: double.infinity,
height: double.infinity,
alignment: Alignment.center,
child: Image.file(
widget.photo!,
fit: BoxFit.fill,
filterQuality: FilterQuality.high,
),
),
],
),
),
);
}
}

130
example/lib/packages/camera/widgets/camera_control_panel.dart

@ -0,0 +1,130 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/theme/colors.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CameraControlPanel extends AppStatefulWidget {
const CameraControlPanel({
super.key,
required this.disable,
required this.controller,
required this.photo,
required this.onRecordingStart,
required this.onRecordingStop,
required this.onRecordingPause,
required this.onRecordingResume,
required this.onTakePhoto,
required this.onClearPhoto,
});
final bool disable;
final CameraController? controller;
final File? photo;
final void Function() onRecordingStart;
final void Function() onRecordingStop;
final void Function() onRecordingPause;
final void Function() onRecordingResume;
final void Function() onTakePhoto;
final void Function() onClearPhoto;
@override
State<CameraControlPanel> createState() => _CameraControlPanelState();
}
class _CameraControlPanelState extends AppState<CameraControlPanel> {
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
final isPhoto = widget.photo != null;
final isRecordingVideo = widget.controller?.value.isRecordingVideo ?? false;
final isRecordingPaused =
widget.controller?.value.isRecordingPaused ?? false;
return Visibility(
visible: widget.controller != null,
child: Column(
children: [
const SizedBox(height: 10),
Row(
children: [
ClipOval(
child: Material(
child: IconButton(
icon: Icon(
isRecordingVideo ? Icons.stop_circle : Icons.videocam,
color: AppColors.primary.withOpacity(isPhoto ||
widget.disable ||
true /* @todo disable video record */
? 0.5
: 1),
),
onPressed: isPhoto ||
widget.disable ||
true // @todo disable video record
? null
: () {
if (isRecordingVideo) {
widget.onRecordingStop.call();
} else {
widget.onRecordingStart.call();
}
},
),
),
),
if (isRecordingVideo)
ClipOval(
child: Material(
child: IconButton(
icon: Icon(
isRecordingPaused
? Icons.play_circle
: Icons.pause_circle,
color: AppColors.primary,
),
onPressed: () {
if (isRecordingPaused) {
widget.onRecordingResume.call();
} else {
widget.onRecordingPause.call();
}
},
),
),
),
const Spacer(),
ClipOval(
child: Material(
child: IconButton(
icon: Icon(
isPhoto ? Icons.image_not_supported : Icons.photo_camera,
color: AppColors.primary.withOpacity(
isRecordingVideo || widget.disable ? 0.5 : 1),
),
onPressed: isRecordingVideo || widget.disable
? null
: () {
if (isPhoto) {
widget.onClearPhoto.call();
} else {
widget.onTakePhoto.call();
}
},
),
),
),
],
),
],
),
);
}
}

96
example/lib/packages/camera/widgets/cameras_loading.dart

@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/base/package/package.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_example_packages/widgets/blocks/block_alert.dart';
import 'package:flutter_example_packages/widgets/blocks/block_info_package.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CamerasLoading extends AppStatefulWidget {
const CamerasLoading({
super.key,
required this.package,
required this.builder,
});
final Package package;
final Widget Function(BuildContext context, List<CameraDescription> cameras)
builder;
@override
State<CamerasLoading> createState() => _CamerasLoadingState();
}
class _CamerasLoadingState extends AppState<CamerasLoading> {
List<CameraDescription>? _cameras;
String? _error;
@override
void initState() {
super.initState();
// Get list cameras
try {
availableCameras().then((cameras) {
if (mounted) {
setState(() {
_cameras = cameras;
});
}
}).catchError((e) {
setState(() {
_error = e.toString();
});
});
} catch (e) {
setState(() {
_error = e.toString();
});
}
}
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
flex: 0,
child: Column(
children: [
BlockInfoPackage(
widget.package,
),
],
),
),
Visibility(
visible: _cameras == null && _error == null,
child: const Flexible(
flex: 1,
child: Center(
child: CircularProgressIndicator(),
),
),
),
Visibility(
visible: _cameras?.isEmpty ?? false || _error != null,
child: Flexible(
flex: 0,
child: BlockAlert(_error ?? 'No camera found.'),
),
),
if (_cameras?.isNotEmpty ?? false)
Flexible(
flex: 1,
child: widget.builder.call(context, _cameras!),
),
],
);
}
}

83
example/lib/packages/camera/widgets/cameras_select.dart

@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example_packages/packages/camera/extension/export.dart';
import 'package:flutter_example_packages/theme/colors.dart';
import 'package:flutter_example_packages/widgets/base/export.dart';
import 'package:flutter_example_packages/widgets/texts/export.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class CamerasSelect extends AppStatefulWidget {
const CamerasSelect({
super.key,
required this.disable,
required this.cameras,
required this.onChange,
});
final bool disable;
final List<CameraDescription> cameras;
final void Function(CameraController controller) onChange;
@override
State<CamerasSelect> createState() => _CamerasSelectState();
}
class _CamerasSelectState extends AppState<CamerasSelect> {
CameraController? _cameraController;
Future<void> initCamera(CameraDescription? camera) async {
if (camera != null) {
if (_cameraController != null) {
// Check and stop if need image stream
if (_cameraController!.value.isStreamingImages) {
await _cameraController!.stopImageStream();
}
// Check and stop if need video recording
if (_cameraController!.value.isRecordingVideo) {
await _cameraController!.stopVideoRecording();
}
// Change camera
await _cameraController!.setDescription(camera);
} else {
_cameraController = await camera.getCameraController();
}
// Send signal about change camera
if (mounted) {
widget.onChange(_cameraController!);
}
}
}
@override
Widget buildWide(
BuildContext context,
MediaQueryData media,
AppLocalizations l10n,
) {
return Row(
children: [
const TextTitleLarge('Cameras:'),
const SizedBox(width: 8),
for (final CameraDescription camera in widget.cameras)
ClipOval(
child: Material(
child: IconButton(
icon: Icon(
camera.getIcon(),
color: _cameraController?.description == camera
? AppColors.secondary
: AppColors.primary,
),
onPressed:
_cameraController?.description == camera || widget.disable
? null
: () => initCamera(camera),
),
),
),
],
);
}
}

2
example/lib/packages/packages.dart

@ -4,6 +4,7 @@ import 'package:flutter_example_packages/base/package/package.dart';
import 'package:flutter_example_packages/packages/battery_plus/package.dart';
import 'package:flutter_example_packages/packages/build_runner/package.dart';
import 'package:flutter_example_packages/packages/cached_network_image/package.dart';
import 'package:flutter_example_packages/packages/camera/package.dart';
import 'package:flutter_example_packages/packages/crypto/package.dart';
import 'package:flutter_example_packages/packages/cupertino_icons/package.dart';
import 'package:flutter_example_packages/packages/dartz/package.dart';
@ -41,6 +42,7 @@ final packages = <Package>[
packageBatteryPlus,
packageBuildRunner,
packageCachedNetworkImage,
packageCamera,
packageCrypto,
packageCupertinoIcons,
packageDartz,

12
example/pubspec.yaml

@ -173,6 +173,18 @@ dependencies:
# ref: master
# path: packages/sensors_plus/sensors_plus_aurora
## https://pub.dev/packages/camera
camera: ^0.10.5+5
## https://pub.dev/packages/camera_android
camera_android: ^0.10.8+13
## https://os-git.omprussia.ru/non-oss/flutter/flutter-plugins/-/tree/master/packages/camera/camera_aurora
camera_aurora:
path: ../packages/camera/camera_aurora
# git:
# url: https://gitlab.com/omprussia/flutter/flutter-plugins.git
# ref: master
# path: packages/camera/camera_aurora
dev_dependencies:
flutter_test:
sdk:

30
packages/camera/camera_aurora/.gitignore vendored

@ -0,0 +1,30 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.packages
build/

25
packages/camera/camera_aurora/README.md

@ -0,0 +1,25 @@
# camera_aurora
The Aurora implementation of [camera](https://pub.dev/packages/camera).
## Usage
This package is not an _endorsed_ implementation of `camera`.
Therefore, you have to include `camera_aurora` alongside `camera` as dependencies in your `pubspec.yaml` file.
***.desktop**
```desktop
Permissions=Camera
```
**pubspec.yaml**
```yaml
dependencies:
camera: ^0.10.5+5
camera_aurora:
git:
url: https://gitlab.com/omprussia/flutter/flutter-plugins.git
ref: master
path: packages/device_info_plus/device_info_plus_aurora
```

4
packages/camera/camera_aurora/analysis_options.yaml

@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
# SPDX-License-Identifier: BSD-3-Clause
include: package:flutter_lints/flutter.yaml

40
packages/camera/camera_aurora/aurora/CMakeLists.txt

@ -0,0 +1,40 @@
# SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.10)
set(PROJECT_NAME camera_aurora)
set(PLUGIN_NAME camera_aurora_platform_plugin)
project(${PROJECT_NAME} LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-psabi")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
find_package(PkgConfig REQUIRED)
find_package(Qt5 COMPONENTS Core Multimedia REQUIRED)
pkg_check_modules(FlutterEmbedder REQUIRED IMPORTED_TARGET flutter-embedder)
pkg_check_modules(GLES REQUIRED IMPORTED_TARGET glesv2)
pkg_check_modules(SC REQUIRED IMPORTED_TARGET streamcamera)
add_library(${PLUGIN_NAME} SHARED
texture_camera_pixels_helper.cpp
texture_camera_egl_helper.cpp
texture_camera.cpp
camera_aurora_plugin.cpp
)
set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden AUTOMOC ON)
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::FlutterEmbedder PkgConfig::GLES PkgConfig::SC)
target_link_libraries(${PLUGIN_NAME} PUBLIC Qt5::Core Qt5::Multimedia)
target_include_directories(${PLUGIN_NAME} PRIVATE ${FLUTTER_DIR})
target_include_directories(${PLUGIN_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(${PLUGIN_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME})
target_compile_definitions(${PLUGIN_NAME} PRIVATE PLUGIN_IMPL)

217
packages/camera/camera_aurora/aurora/camera_aurora_plugin.cpp

@ -0,0 +1,217 @@
/*
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <camera_aurora/camera_aurora_plugin.h>
#include <flutter/method-channel.h>
#include <flutter/platform-data.h>
#include <flutter/platform-events.h>
#include <flutter/platform-methods.h>
#include <QtCore>
#include <QBuffer>
#include <QCamera>
#include <QCameraInfo>
#include <QMediaRecorder>
#include <QCameraImageCapture>
#include <unistd.h>
namespace CameraAuroraMethods
{
constexpr auto PluginKey = "camera_aurora";
constexpr auto AvailableCameras = "availableCameras";
constexpr auto CreateCamera = "createCamera";
constexpr auto Dispose = "dispose";
constexpr auto StartCapture = "startCapture";
constexpr auto StopCapture = "stopCapture";
constexpr auto TakePicture = "takePicture";
constexpr auto StartVideoRecording = "startVideoRecording";
constexpr auto StopVideoRecording = "stopVideoRecording";
constexpr auto PauseVideoRecording = "pauseVideoRecording";
constexpr auto ResumeVideoRecording = "resumeVideoRecording";
}
namespace CameraAuroraEvents
{
constexpr auto StateChanged = "cameraAuroraStateChanged";
}
CameraAuroraPlugin::CameraAuroraPlugin()
{
PlatformEvents::SubscribeOrientationChanged([this]([[maybe_unused]] DisplayRotation orientation) {
if (this->m_isEnableStateChanged) {
auto state = this->m_textureCamera->GetState();
EventChannel(CameraAuroraEvents::StateChanged, MethodCodecType::Standard).SendEvent(state);
}
});
}
void CameraAuroraPlugin::RegisterWithRegistrar(PluginRegistrar &registrar)
{
m_textureCamera = new TextureCamera(registrar.GetRegisterTexture(), [this]() {
auto state = this->m_textureCamera->GetState();
EventChannel(CameraAuroraEvents::StateChanged, MethodCodecType::Standard).SendEvent(state);
});
RegisterMethods(registrar);
RegisterEvents(registrar);
}
void CameraAuroraPlugin::RegisterMethods(PluginRegistrar &registrar)
{
auto methods = [this](const MethodCall &call)
{
const auto &method = call.GetMethod();
if (method == CameraAuroraMethods::AvailableCameras)
{
onAvailableCameras(call);
return;
}
if (method == CameraAuroraMethods::CreateCamera)
{
onCreateCamera(call);
return;
}
if (method == CameraAuroraMethods::Dispose)
{
onDispose(call);
return;
}
if (method == CameraAuroraMethods::StartCapture)
{
onStartCapture(call);
return;
}
if (method == CameraAuroraMethods::StopCapture)
{
onStopCapture(call);
return;
}
if (method == CameraAuroraMethods::TakePicture)
{
onTakePicture(call);
return;
}
if (method == CameraAuroraMethods::StartVideoRecording)
{
onStartVideoRecording(call);
return;
}
if (method == CameraAuroraMethods::StopVideoRecording)
{
onStopVideoRecording(call);
return;
}
if (method == CameraAuroraMethods::PauseVideoRecording)
{
onPauseVideoRecording(call);
return;
}
if (method == CameraAuroraMethods::ResumeVideoRecording)
{
onResumeVideoRecording(call);
return;
}
unimplemented(call);
};
registrar.RegisterMethodChannel(
CameraAuroraMethods::PluginKey,
MethodCodecType::Standard,
methods);
}
void CameraAuroraPlugin::RegisterEvents(PluginRegistrar &registrar)
{
registrar.RegisterEventChannel(
CameraAuroraEvents::StateChanged, MethodCodecType::Standard,
[this](const Encodable &)
{ this->m_isEnableStateChanged = true; return EventResponse(); },
[this](const Encodable &)
{ this->m_isEnableStateChanged = true; return EventResponse(); });
}
/**
* Camera
*/
void CameraAuroraPlugin::onAvailableCameras(const MethodCall &call)
{
call.SendSuccessResponse(m_textureCamera->GetAvailableCameras());
}
void CameraAuroraPlugin::onCreateCamera(const MethodCall &call)
{
auto cameraName = call.GetArgument<Encodable::String>("cameraName");
auto state = m_textureCamera->Register(cameraName);
EventChannel(CameraAuroraEvents::StateChanged, MethodCodecType::Standard).SendEvent(state);
call.SendSuccessResponse(state);
}
void CameraAuroraPlugin::onDispose(const MethodCall &call)
{
auto state = m_textureCamera->Unregister();
EventChannel(CameraAuroraEvents::StateChanged, MethodCodecType::Standard).SendEvent(state);
unimplemented(call);
}
void CameraAuroraPlugin::onStartCapture(const MethodCall &call)
{
auto width = call.GetArgument<Encodable::Int>("width");
auto height = call.GetArgument<Encodable::Int>("height");
auto state = m_textureCamera->StartCapture(width, height);
EventChannel(CameraAuroraEvents::StateChanged, MethodCodecType::Standard).SendEvent(state);
unimplemented(call);
}
void CameraAuroraPlugin::onStopCapture(const MethodCall &call)
{
m_textureCamera->StopCapture();
unimplemented(call);
}
void CameraAuroraPlugin::onTakePicture(const MethodCall &call)
{
call.SendSuccessResponse(m_textureCamera->GetImageBase64());
}
void CameraAuroraPlugin::onStartVideoRecording(const MethodCall &call)
{
unimplemented(call);
}
void CameraAuroraPlugin::onStopVideoRecording(const MethodCall &call)
{
unimplemented(call);
}
void CameraAuroraPlugin::onPauseVideoRecording(const MethodCall &call)
{
unimplemented(call);
}
void CameraAuroraPlugin::onResumeVideoRecording(const MethodCall &call)
{
unimplemented(call);
}
void CameraAuroraPlugin::unimplemented(const MethodCall &call)
{
call.SendSuccessResponse(nullptr);
}
#include "moc_camera_aurora_plugin.cpp"

47
packages/camera/camera_aurora/aurora/include/camera_aurora/camera_aurora_plugin.h

@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_H
#define FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_H
#include <flutter/plugin-interface.h>
#include <camera_aurora/globals.h>
#include <camera_aurora/texture_camera.h>
class PLUGIN_EXPORT CameraAuroraPlugin final
: public QObject,
public PluginInterface
{
Q_OBJECT
public:
CameraAuroraPlugin();
void RegisterWithRegistrar(PluginRegistrar &registrar) override;
private:
void RegisterMethods(PluginRegistrar &registrar);
void RegisterEvents(PluginRegistrar &registrar);
void onAvailableCameras(const MethodCall &call);
void onCreateCamera(const MethodCall &call);
void onDispose(const MethodCall &call);
void onStartCapture(const MethodCall &call);
void onStopCapture(const MethodCall &call);
void onTakePicture(const MethodCall &call);
void onStartVideoRecording(const MethodCall &call);
void onStopVideoRecording(const MethodCall &call);
void onPauseVideoRecording(const MethodCall &call);
void onResumeVideoRecording(const MethodCall &call);
void unimplemented(const MethodCall &call);
private:
TextureCamera *m_textureCamera;
bool m_isEnableStateChanged = false;
};
#endif /* FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_H */

14
packages/camera/camera_aurora/aurora/include/camera_aurora/globals.h

@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_GLOBALS_H
#define FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_GLOBALS_H
#ifdef PLUGIN_IMPL
#define PLUGIN_EXPORT __attribute__((visibility("default")))
#else
#define PLUGIN_EXPORT
#endif
#endif /* FLUTTER_PLUGIN_CAMERA_AURORA_PLUGIN_GLOBALS_H */

57
packages/camera/camera_aurora/aurora/include/camera_aurora/texture_camera.h

@ -0,0 +1,57 @@
/**
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef TEXTURE_CAMERA_BUFFER_H
#define TEXTURE_CAMERA_BUFFER_H
#include <flutter/plugin-interface.h>
#include <streamcamera/streamcamera.h>
#include <QImage>
#include <QtCore>
typedef std::function<void()> CameraErrorHandler;
class TextureCamera : public Aurora::StreamCamera::CameraListener
{
public:
TextureCamera(TextureRegistrar *plugin, const CameraErrorHandler &onError);
void onCameraError(const std::string &errorDescription) override;
void onCameraFrame(std::shared_ptr<Aurora::StreamCamera::GraphicBuffer> buffer) override;
void onCameraParameterChanged(Aurora::StreamCamera::CameraParameter,
const std::string &value) override;
std::vector<Encodable> GetAvailableCameras();
std::map<Encodable, Encodable> Register(std::string cameraName);
std::map<Encodable, Encodable> Unregister();
std::map<Encodable, Encodable> StartCapture(size_t width, size_t height);
void StopCapture();
std::map<Encodable, Encodable> GetState();
std::string GetImageBase64();
private:
bool CreateCamera(std::string cameraName);
void SendError(std::string error);
private:
TextureRegistrar *m_plugin;
CameraErrorHandler m_onError;
std::string m_error;
Aurora::StreamCamera::CameraManager *m_manager;
std::shared_ptr<Aurora::StreamCamera::Camera> m_camera;
int64_t m_textureId = 0;
size_t m_captureWidth = 0;
size_t m_captureHeight = 0;
size_t m_viewWidth = 0;
size_t m_viewHeight = 0;
TextureVariant *m_variant;
QImage *m_image;
};
#endif /* TEXTURE_CAMERA_BUFFER_H */

21
packages/camera/camera_aurora/aurora/include/camera_aurora/texture_camera_egl_helper.h

@ -0,0 +1,21 @@
/**
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef TEXTURE_CAMERA_EGL_HELPER_H
#define TEXTURE_CAMERA_EGL_HELPER_H
#include <streamcamera/streamcamera.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
class TextureCameraEGLHelper
{
public:
static void EGLInit();
static EGLImageKHR EGLCreateImage(std::shared_ptr<Aurora::StreamCamera::GraphicBuffer> buffer);
static void EGLDestroyImage(EGLImageKHR image);
};
#endif /* TEXTURE_CAMERA_EGL_HELPER_H */

33
packages/camera/camera_aurora/aurora/include/camera_aurora/texture_camera_pixels_helper.h

@ -0,0 +1,33 @@
/**
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef TEXTURE_CAMERA_PIXELS_HELPER_H
#define TEXTURE_CAMERA_PIXELS_HELPER_H
#include <flutter/plugin-interface.h>
#include <streamcamera/streamcamera.h>
#include <QImage>
#include <QtCore>
class TextureCameraPixelsHelper
{
public:
static QImage *YUVtoARGB(std::shared_ptr<const Aurora::StreamCamera::YCbCrFrame> frame);
private:
static quint32 yuvToArgb(qint32 y, qint32 rv, qint32 guv, qint32 bu, qint32 a);
static void planarYuv420ToArgb(const uchar *y,
const uchar *u,
const uchar *v,
qint32 yStride,
qint32 uStride,
qint32 vStride,
qint32 uvPixelStride,
quint32 *rgb,
qint32 width,
qint32 height);
};
#endif /* TEXTURE_CAMERA_PIXELS_HELPER_H */

218
packages/camera/camera_aurora/aurora/texture_camera.cpp

@ -0,0 +1,218 @@
/**
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <camera_aurora/texture_camera.h>
#include <camera_aurora/texture_camera_egl_helper.h>
#include <camera_aurora/texture_camera_pixels_helper.h>
#include <flutter/platform-data.h>
#include <flutter/platform-methods.h>
#include <QtCore>
#include <QBuffer>
TextureCamera::TextureCamera(TextureRegistrar *plugin, const CameraErrorHandler &onError)
: m_plugin(plugin)
, m_onError(onError)
, m_variant(nullptr)
, m_image(nullptr)
, m_manager(StreamCameraManager())
{
TextureCameraEGLHelper::EGLInit();
}
std::vector<Encodable> TextureCamera::GetAvailableCameras()
{
std::vector<Encodable> cameras;
auto count = m_manager->getNumberOfCameras();
for (int index = 0; index < count; index++) {
Aurora::StreamCamera::CameraInfo info;
if (m_manager->getCameraInfo(index, info)) {
cameras.push_back(std::map<Encodable, Encodable>{
{"id", info.id},
{"name", info.name},
{"provider", info.provider},
{"mountAngle", info.mountAngle},
});
}
}
return cameras;
}
std::string TextureCamera::GetImageBase64()
{
if (m_image && m_camera) {
Aurora::StreamCamera::CameraInfo info;
if (m_camera->getInfo(info)) {
QBuffer qbuffer;
qbuffer.open(QIODevice::WriteOnly);
QImage rotatedImg = m_image->transformed(QMatrix().rotate(info.mountAngle));
rotatedImg.save(&qbuffer, "JPEG");
return qbuffer.data().toBase64().toStdString();
}
}
return "";
}
std::map<Encodable, Encodable> TextureCamera::GetState()
{
Aurora::StreamCamera::CameraInfo info;
if (m_camera && m_camera->getInfo(info)) {
auto orientation = static_cast<int>(PlatformMethods::GetOrientation());
return std::map<Encodable, Encodable>{
{"id", info.id},
{"textureId", m_textureId},
{"width", m_captureWidth},
{"height", m_captureHeight},
{"rotationCamera", info.mountAngle},
{"rotationDisplay", orientation},
{"error", m_error},
};
}
return std::map<Encodable, Encodable>{
{"error", m_error}
};
}
bool TextureCamera::CreateCamera(std::string cameraName)
{
if (auto count = m_manager->getNumberOfCameras()) {
for (int index = 0; index < count; index++) {
Aurora::StreamCamera::CameraInfo info;
if (m_manager->getCameraInfo(index, info)) {
if (info.id == cameraName) {
m_camera = m_manager->openCamera(info.id);
if (m_camera) {
m_camera->setListener(this);
return true;
} else {
Unregister();
SendError("Stream camera error open camera");
return false;
}
}
}
}
}
return false;
}
void TextureCamera::SendError(std::string error)
{
m_error = error;
m_onError();
}
std::map<Encodable, Encodable> TextureCamera::StartCapture(size_t width, size_t height)
{
m_viewWidth = width;
m_viewHeight = height;
if (m_camera) {
if (m_camera->captureStarted()) {
m_camera->stopCapture();
}
Aurora::StreamCamera::CameraInfo info;
if (m_camera->getInfo(info)) {
std::vector<Aurora::StreamCamera::CameraCapability> caps;
if (m_manager->queryCapabilities(info.id, caps)) {
for(unsigned int i = 0; i< caps.size(); i++) {
if (width + height <= caps[i].width + caps[i].height || caps.size() == i - 1) {
m_captureWidth = caps[i].width;
m_captureHeight = caps[i].height;
if (!m_camera->startCapture(caps[i])) {
Unregister();
SendError("Stream camera error start capture");
}
break;
}
}
}
}
}
return GetState();
}
void TextureCamera::StopCapture()
{
if (m_camera && m_camera->captureStarted()) {
m_camera->stopCapture();
}
}
std::map<Encodable, Encodable> TextureCamera::Register(std::string cameraName)
{
m_textureId = m_plugin->RegisterTexture(
[this]([[maybe_unused]] size_t width, [[maybe_unused]] size_t height) {
return m_variant;
});
if (CreateCamera(cameraName) && m_viewWidth != 0 && m_viewHeight != 0) {
StartCapture(m_viewWidth, m_viewHeight);
}
return GetState();
}
std::map<Encodable, Encodable> TextureCamera::Unregister()
{
StopCapture();
m_error = "";
m_textureId = 0;
m_captureWidth = 0;
m_captureHeight = 0;
m_variant = nullptr;
m_camera = nullptr;
m_plugin->UnregisterTexture(m_textureId);
return GetState();
}
void TextureCamera::onCameraFrame(std::shared_ptr<Aurora::StreamCamera::GraphicBuffer> buffer)
{
if (buffer->handleType == Aurora::StreamCamera::HandleType::EGL && false) {
// @todo not tested
auto eglImage = TextureCameraEGLHelper::EGLCreateImage(buffer);
m_variant = new TextureVariant(FlutterEGLImage{
eglImage,
buffer->width,
buffer->height,
});
} else {
m_image = TextureCameraPixelsHelper::YUVtoARGB(buffer->mapYCbCr());
auto pixels = static_cast<uint8_t *>(m_image->bits());
m_variant = new TextureVariant(FlutterPixelBuffer{
pixels,
buffer->width,
buffer->height,
});
}
m_plugin->MarkTextureAvailable(m_textureId);
}
void TextureCamera::onCameraError(const std::string &errorDescription)
{
Unregister();
SendError(errorDescription);
}
void TextureCamera::onCameraParameterChanged([[maybe_unused]] Aurora::StreamCamera::CameraParameter parameter,
const std::string &value)
{
std::cout << "onCameraParameterChanged: " << value << std::endl;
}

42
packages/camera/camera_aurora/aurora/texture_camera_egl_helper.cpp

@ -0,0 +1,42 @@
/**
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <camera_aurora/texture_camera_egl_helper.h>
#include <flutter/platform-methods.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
void TextureCameraEGLHelper::EGLInit()
{
eglCreateImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(
eglGetProcAddress("eglCreateImageKHR"));
eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(
eglGetProcAddress("eglDestroyImageKHR"));
}
EGLImageKHR TextureCameraEGLHelper::EGLCreateImage(
std::shared_ptr<Aurora::StreamCamera::GraphicBuffer> buffer)
{
auto display = PlatformMethods::GetEGLDisplay();
auto context = PlatformMethods::GetEGLContext();
const void *handle = buffer->handle;
GLint eglImgAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE};
return eglCreateImageKHR(display,
context,
EGL_NATIVE_BUFFER_ANDROID,
(EGLClientBuffer) handle,
eglImgAttrs);
}
void TextureCameraEGLHelper::EGLDestroyImage(EGLImageKHR image)
{
auto display = PlatformMethods::GetEGLDisplay();
eglDestroyImageKHR(display, image);
}

77
packages/camera/camera_aurora/aurora/texture_camera_pixels_helper.cpp

@ -0,0 +1,77 @@
/**
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <camera_aurora/texture_camera_pixels_helper.h>
QImage *TextureCameraPixelsHelper::YUVtoARGB(std::shared_ptr<const Aurora::StreamCamera::YCbCrFrame> frame)
{
QSize size(frame->width, frame->height);
QImage *image = new QImage(size, QImage::Format_RGBA8888);
planarYuv420ToArgb(frame->y,
frame->cr,
frame->cb,
frame->yStride,
frame->cStride,
frame->cStride,
frame->chromaStep,
reinterpret_cast<quint32 *>(image->bits()),
frame->width,
frame->height);
return image;
}
quint32 TextureCameraPixelsHelper::yuvToArgb(qint32 y, qint32 rv, qint32 guv, qint32 bu, qint32 a = 255)
{
qint32 yy = (y - 16) * 298;
return (a << 24)
| qBound(0, (yy + rv) >> 8, 255) << 16
| qBound(0, (yy - guv) >> 8, 255) << 8
| qBound(0, (yy + bu) >> 8, 255);
}
void TextureCameraPixelsHelper::planarYuv420ToArgb(const uchar *y,
const uchar *u,
const uchar *v,
qint32 yStride,
qint32 uStride,
qint32 vStride,
qint32 uvPixelStride,
quint32 *rgb,
qint32 width,
qint32 height)
{
quint32 *rgb0 = rgb;
quint32 *rgb1 = rgb + width;
for (qint32 j = 0; j < height; j += 2) {
const uchar *lineY0 = y;
const uchar *lineY1 = y + yStride;
const uchar *lineU = u;
const uchar *lineV = v;
for (qint32 i = 0; i < width; i += 2) {
const qint32 uu = *lineU - 128;
const qint32 vv = *lineV - 128;
const qint32 rv = 409 * vv + 128;
const qint32 guv = 100 * uu + 208 * vv + 128;
const qint32 bu = 516 * uu + 128;
lineU += uvPixelStride;
lineV += uvPixelStride;
*rgb0++ = yuvToArgb(*lineY0++, rv, guv, bu);
*rgb0++ = yuvToArgb(*lineY0++, rv, guv, bu);
*rgb1++ = yuvToArgb(*lineY1++, rv, guv, bu);
*rgb1++ = yuvToArgb(*lineY1++, rv, guv, bu);
}
y += yStride << 1;
u += uStride;
v += vStride;
rgb0 += width;
rgb1 += width;
}
}

96
packages/camera/camera_aurora/lib/camera_aurora.dart

@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:async';
import 'package:camera_aurora/camera_viewfinder.dart';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'camera_aurora_platform_interface.dart';
class CameraAurora extends CameraPlatform {
/// Registers this class as the default instance of [CameraPlatform].
static void registerWith() {
CameraPlatform.instance = CameraAurora();
}
@override
Future<List<CameraDescription>> availableCameras() =>
CameraAuroraPlatform.instance.availableCameras();
@override
Future<void> initializeCamera(
int cameraId, {
ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown,
}) async {}
@override
Future<int> createCamera(
CameraDescription cameraDescription,
ResolutionPreset? resolutionPreset, {
bool enableAudio = false,
}) async {
return (await CameraAuroraPlatform.instance
.createCamera(cameraDescription.name))
.textureId;
}
@override
Future<void> dispose(int cameraId) => CameraAuroraPlatform.instance.dispose();
@override
Future<XFile> takePicture(int cameraId) =>
CameraAuroraPlatform.instance.takePicture(cameraId);
@override
Future<void> startVideoRecording(int cameraId,
{Duration? maxVideoDuration}) =>
CameraAuroraPlatform.instance.startVideoRecording(cameraId);
@override
Future<XFile> stopVideoRecording(int cameraId) =>
CameraAuroraPlatform.instance.stopVideoRecording(cameraId);
@override
Future<void> pauseVideoRecording(int cameraId) =>
CameraAuroraPlatform.instance.pauseVideoRecording(cameraId);
@override
Future<void> resumeVideoRecording(int cameraId) =>
CameraAuroraPlatform.instance.resumeVideoRecording(cameraId);
@override
Stream<CameraInitializedEvent> onCameraInitialized(int cameraId) async* {
yield CameraInitializedEvent(
cameraId,
0,
0,
ExposureMode.auto,
true,
FocusMode.auto,
true,
);
}
@override
Stream<DeviceOrientationChangedEvent> onDeviceOrientationChanged() async* {
yield const DeviceOrientationChangedEvent(DeviceOrientation.landscapeLeft);
}
@override
Widget buildPreview(int cameraId) {
if (cameraId != 0) {
return LayoutBuilder(builder: (
BuildContext context,
BoxConstraints constraints,
) {
return CameraViewfinder(
width: constraints.maxWidth,
height: constraints.maxHeight,
);
});
}
return const SizedBox.shrink();
}
}

159
packages/camera/camera_aurora/lib/camera_aurora_method_channel.dart

@ -0,0 +1,159 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'dart:convert';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'camera_aurora_platform_interface.dart';
import 'camera_data.dart';
enum CameraAuroraMethods {
availableCameras,
createCamera,
dispose,
startCapture,
stopCapture,
takePicture,
startVideoRecording,
stopVideoRecording,
pauseVideoRecording,
resumeVideoRecording,
}
enum CameraAuroraEvents {
cameraAuroraStateChanged,
}
/// An implementation of [CameraAuroraPlatform] that uses method channels.
class MethodChannelCameraAurora extends CameraAuroraPlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodsChannel = const MethodChannel('camera_aurora');
@override
Stream<CameraState> onChangeState() async* {
await for (final data
in EventChannel(CameraAuroraEvents.cameraAuroraStateChanged.name)
.receiveBroadcastStream()) {
yield CameraState.fromJson(data);
}
}
@override
Future<List<CameraDescription>> availableCameras() async {
final List<CameraDescription> result = [];
final cameras = await methodsChannel.invokeMethod<List<dynamic>?>(
CameraAuroraMethods.availableCameras.name) ??
[];
for (final camera in cameras) {
final data = camera['id'].split(':');
var direction = CameraLensDirection.external;
if (data[1].toString().contains('rear')) {
direction = CameraLensDirection.back;
} else if (data[1].toString().contains('front')) {
direction = CameraLensDirection.front;
}
result.add(CameraDescription(
name: camera['id'],
lensDirection: direction,
sensorOrientation: camera['mountAngle'],
));
}
return result;
}
@override
Future<CameraState> createCamera(String cameraName) async {
final data = await methodsChannel
.invokeMethod<Map<dynamic, dynamic>?>('createCamera', {
'cameraName': cameraName,
});
return CameraState.fromJson(data ?? {});
}
@override
Future<void> startCapture(double width, double height) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.startCapture.name, {
'width': width.round(),
'height': height.round(),
});
}
@override
Future<void> stopCapture() async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.stopCapture.name, {});
}
@override
Future<void> dispose() async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.dispose.name);
}
@override
Future<XFile> takePicture(int cameraId) async {
final image = await methodsChannel.invokeMethod<String?>(
CameraAuroraMethods.takePicture.name,
{'cameraId': cameraId},
);
final bytes = base64Decode(image!);
return XFile.fromData(
bytes,
name: 'temp.jpg',
mimeType: 'image/jpeg',
length: bytes.length,
);
}
@override
Future<void> startVideoRecording(int cameraId,
{Duration? maxVideoDuration}) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.startVideoRecording.name, {
'cameraId': cameraId,
});
}
@override
Future<XFile> stopVideoRecording(int cameraId) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.stopVideoRecording.name, {
'cameraId': cameraId,
});
// @todo Save empty data
return XFile.fromData(
Uint8List(0),
name: 'file_name.mp4',
/// @todo not set
mimeType: 'video/mp4',
length: 0,
);
}
@override
Future<void> pauseVideoRecording(int cameraId) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.pauseVideoRecording.name, {
'cameraId': cameraId,
});
}
@override
Future<void> resumeVideoRecording(int cameraId) async {
await methodsChannel
.invokeMethod<Object?>(CameraAuroraMethods.resumeVideoRecording.name, {
'cameraId': cameraId,
});
}
}

77
packages/camera/camera_aurora/lib/camera_aurora_platform_interface.dart

@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'camera_aurora_method_channel.dart';
import 'camera_data.dart';
abstract class CameraAuroraPlatform extends PlatformInterface {
/// Constructs a CameraAuroraPlatform.
CameraAuroraPlatform() : super(token: _token);
static final Object _token = Object();
static CameraAuroraPlatform _instance = MethodChannelCameraAurora();
/// The default instance of [CameraAuroraPlatform] to use.
///
/// Defaults to [MethodChannelCameraAurora].
static CameraAuroraPlatform get instance => _instance;
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [CameraAuroraPlatform] when
/// they register themselves.
static set instance(CameraAuroraPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Stream<CameraState> onChangeState() {
throw UnimplementedError('onChangeState() has not been implemented.');
}
/// Camera
Future<List<CameraDescription>> availableCameras() {
throw UnimplementedError('availableCameras() has not been implemented.');
}
Future<void> startCapture(double width, double height) {
throw UnimplementedError('startCapture() has not been implemented.');
}
Future<void> stopCapture() {
throw UnimplementedError('startCapture() has not been implemented.');
}
Future<CameraState> createCamera(String cameraName) {
throw UnimplementedError('createCamera() has not been implemented.');
}
Future<void> dispose() {
throw UnimplementedError('dispose() has not been implemented.');
}
// make photo
Future<XFile> takePicture(int cameraId) {
throw UnimplementedError('takePicture() has not been implemented.');
}
// record video
Future<void> startVideoRecording(int cameraId) {
throw UnimplementedError('startVideoRecording() has not been implemented.');
}
Future<XFile> stopVideoRecording(int cameraId) {
throw UnimplementedError('stopVideoRecording() has not been implemented.');
}
Future<void> pauseVideoRecording(int cameraId) {
throw UnimplementedError('pauseVideoRecording() has not been implemented.');
}
Future<void> resumeVideoRecording(int cameraId) {
throw UnimplementedError(
'resumeVideoRecording() has not been implemented.');
}
}

38
packages/camera/camera_aurora/lib/camera_data.dart

@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
enum OrientationEvent {
undefined,
portrait,
landscape,
portraitFlipped,
landscapeFlipped,
}
class CameraState {
CameraState.fromJson(Map<dynamic, dynamic> json)
: id = json['id'] ?? "",
textureId = json['textureId'] ?? -1,
width = (json['width'] ?? 0).toDouble(),
height = (json['height'] ?? 0).toDouble(),
rotationCamera = json['rotationCamera'] ?? 0,
rotationDisplay = json['rotationDisplay'] ?? 0,
error = json['error'] ?? '';
final String id;
final int textureId;
final double width;
final double height;
final int rotationCamera;
final int rotationDisplay;
final String error;
bool isNotEmpty() => textureId != -1;
bool hasError() => error.isNotEmpty;
@override
String toString() {
return '{id: $id, textureId: $textureId, width: $width, height: $height, rotationCamera: $rotationCamera, rotationDisplay: $rotationDisplay, error: $error}';
}
}

99
packages/camera/camera_aurora/lib/camera_viewfinder.dart

@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import 'package:camera_aurora/camera_aurora_platform_interface.dart';
import 'package:flutter/material.dart';
import 'camera_data.dart';
class CameraViewfinder extends StatefulWidget {
const CameraViewfinder({
super.key,
required this.width,
required this.height,
});
final double width;
final double height;
@override
State<CameraViewfinder> createState() => _CameraViewfinderState();
}
class _CameraViewfinderState extends State<CameraViewfinder> {
CameraState _cameraState = CameraState.fromJson({});
@override
initState() {
super.initState();
CameraAuroraPlatform.instance.startCapture(widget.width, widget.height);
CameraAuroraPlatform.instance.onChangeState().listen((event) {
if (mounted) {
setState(() {
_cameraState = event;
});
}
});
}
@override
void dispose() {
super.dispose();
CameraAuroraPlatform.instance.stopCapture();
}
@override
Widget build(BuildContext context) {
if (_cameraState.hasError()) {
return Center(
child: Text(
'Error: ${_cameraState.error}',
style:
const TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
);
} else if (_cameraState.isNotEmpty()) {
int turn = 0;
switch (_cameraState.rotationDisplay) {
case 0:
turn = _cameraState.id.contains('front') ? -1 : 1;
break;
case 90:
turn = 0;
break;
case 180:
turn = _cameraState.id.contains('front') ? 1 : -1;
break;
default: // 270
turn = 2;
}
double height = 10;
double width = 10;
if (_cameraState.height != 0 && _cameraState.width != 0) {
if (_cameraState.rotationDisplay == 90 ||
_cameraState.rotationDisplay == 270) {
width = widget.width * _cameraState.height / _cameraState.width;
height = widget.height * _cameraState.width / _cameraState.height;
} else {
width = _cameraState.height * widget.height / _cameraState.width;
height = _cameraState.width * widget.width / _cameraState.height;
}
}
return RotatedBox(
quarterTurns: turn,
child: SizedBox(
width: height, // height
height: width, // widht
child: Opacity(
opacity: _cameraState.height == 0 ? 0 : 1,
child: Texture(textureId: _cameraState.textureId),
),
),
);
}
return const SizedBox.shrink();
}
}

30
packages/camera/camera_aurora/pubspec.yaml

@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
# SPDX-License-Identifier: BSD-3-Clause
name: camera_aurora
description: Aurora implementation of the camera plugin.
version: 0.0.1
environment:
sdk: '>=2.18.6 <4.0.0'
flutter: ">=3.0.0"
dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
camera_platform_interface: ^2.6.0
image: ^4.1.3
async: ^2.9.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
plugin:
platforms:
aurora:
pluginClass: CameraAuroraPlugin
dartPluginClass: CameraAurora

14
packages/xdga_directories/lib/xdga_directories.dart

@ -13,12 +13,18 @@ final DynamicLibrary _dylib = () {
final XdgaDirectoriesBindings _bindings = XdgaDirectoriesBindings(_dylib);
/// QStandardPaths::CacheLocation
String getCacheLocation() =>
_bindings.getCacheLocation().cast<Utf8>().toDartString();
String getCacheLocation() => _bindings
.getCacheLocation()
.cast<Utf8>()
.toDartString()
.replaceAll("/qsource", "");
/// QStandardPaths::AppDataLocation
String getAppDataLocation() =>
_bindings.getAppDataLocation().cast<Utf8>().toDartString();
String getAppDataLocation() => _bindings
.getAppDataLocation()
.cast<Utf8>()
.toDartString()
.replaceAll("/qsource", "");
/// QStandardPaths::DocumentsLocation
String getDocumentsLocation() =>

Loading…
Cancel
Save