Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add native orientation support #8

Merged
6 commits merged into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<p align="center">
<a href="https://google.fr/">
<img src="https://via.placeholder.com/456x180" width="456" alt="camerawesome_logo">
<a href="https://apparence.io/">
<img src="logo/banner.png" width="456" alt="camerawesome_logo">
</a>
</p>
<br />

## 🚀&nbsp; Overview

Expand Down
18 changes: 9 additions & 9 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:io';
import 'dart:math';

import 'package:camerawesome/models/orientations.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:async';
Expand Down Expand Up @@ -154,13 +155,6 @@ class _MyAppState extends State<MyApp> {
child: Text("zoom x1", style: TextStyle(color: Colors.white)),
onPressed: () => zoomNotifier.value = 0
),
FlatButton(
color: Colors.blue[200],
child: Text("switch sensor", style: TextStyle(color: Colors.white)),
onPressed: () => sensor.value == Sensors.BACK
? sensor.value = Sensors.FRONT
: sensor.value = Sensors.BACK
),
],
),
)
Expand All @@ -185,6 +179,10 @@ class _MyAppState extends State<MyApp> {
sensor: sensor,
switchFlashMode: switchFlash,
zoom: zoomNotifier,
onOrientationChanged: (CameraOrientations orientation) {
// TODO: Orientation change here
print(orientation);
},
),
)
);
Expand Down Expand Up @@ -212,12 +210,14 @@ class _MyAppState extends State<MyApp> {
fitted: true,
switchFlashMode: switchFlash,
zoom: zoomNotifier,
onOrientationChanged: (CameraOrientations orientation) {
print('-- ORIENTATION CHANGED --');
print(orientation);
},
),
),
),
)
);
}


}
10 changes: 8 additions & 2 deletions ios/Classes/CameraView.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,17 @@ NS_ASSUME_NONNULL_BEGIN
@property(readonly, nonatomic) AVCaptureTorchMode torchMode;
@property(readonly, nonatomic) CameraSensor cameraSensor;
@property(readonly, nonatomic) FlutterResult result;
@property(readonly, nonatomic) NSString *currentPresset;
@property(readonly, nonatomic) NSObject<FlutterBinaryMessenger> *messenger;
@property(readonly) CVPixelBufferRef volatile latestPixelBuffer;
@property(readonly, nonatomic) CGSize previewSize;
@property(readonly, nonatomic) CGSize currentPreviewSize;
@property(nonatomic) FlutterEventSink eventSink;
@property(nonatomic, copy) void (^onFrameAvailable)(void);

- (instancetype)initWithCameraSensor:(CameraSensor)sensor andResult:(nonnull FlutterResult)result;
- (instancetype)initWithCameraSensor:(CameraSensor)sensor
result:(nonnull FlutterResult)result
messenger:(NSObject<FlutterBinaryMessenger> *)messenger
event:(FlutterEventSink)eventSink;
- (void)setPreviewSize:(CGSize)previewSize;
- (void)setFlashMode:(CameraFlashMode)flashMode;
- (void)start;
Expand All @@ -51,6 +56,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)flipCamera;
- (void)setZoom:(float)value;
- (CGFloat)getMaxZoom;
- (CGSize)getEffectivPreviewSize;

@end

Expand Down
88 changes: 66 additions & 22 deletions ios/Classes/CameraView.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,12 @@

@implementation CameraView

- (instancetype)initWithCameraSensor:(CameraSensor)sensor andResult:(nonnull FlutterResult)result {
- (instancetype)initWithCameraSensor:(CameraSensor)sensor result:(nonnull FlutterResult)result messenger:(NSObject<FlutterBinaryMessenger> *)messenger event:(FlutterEventSink)eventSink {
self = [super init];

_result = result;

// Creating motion detection
_motionManager = [[CMMotionManager alloc] init];
_motionManager.deviceMotionUpdateInterval = 0.2f;
[self startMyMotionDetect];
_messenger = messenger;
_eventSink = eventSink;

// Creating capture session
_captureSession = [[AVCaptureSession alloc] init];
Expand All @@ -42,23 +39,57 @@ - (instancetype)initWithCameraSensor:(CameraSensor)sensor andResult:(nonnull Flu

_cameraSensor = sensor;

// Creating motion detection
_motionManager = [[CMMotionManager alloc] init];
_motionManager.deviceMotionUpdateInterval = 0.2f;
[self startMyMotionDetect];

return self;
}

- (void)startMyMotionDetect {
// TODO: Add weakself
[_motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMDeviceMotion *data, NSError *error) {
UIDeviceOrientation newOrientation;
if(fabs(data.gravity.x) > fabs(data.gravity.y)) {
// Landscape
self->_deviceOrientation = (data.gravity.x >= 0) ? UIDeviceOrientationLandscapeLeft : UIDeviceOrientationLandscapeRight;
newOrientation = (data.gravity.x >= 0) ? UIDeviceOrientationLandscapeLeft : UIDeviceOrientationLandscapeRight;
} else {
// Portrait
self->_deviceOrientation = (data.gravity.y >= 0) ? UIDeviceOrientationPortraitUpsideDown : UIDeviceOrientationPortrait;
}}];
newOrientation = (data.gravity.y >= 0) ? UIDeviceOrientationPortraitUpsideDown : UIDeviceOrientationPortrait;
}
if (self->_deviceOrientation != newOrientation) {
self->_deviceOrientation = newOrientation;

NSString *orientationString;
switch (newOrientation) {
case UIDeviceOrientationLandscapeLeft:
orientationString = @"LANDSCAPE_LEFT";
break;
case UIDeviceOrientationLandscapeRight:
orientationString = @"LANDSCAPE_RIGHT";
break;
case UIDeviceOrientationPortrait:
orientationString = @"PORTRAIT_UP";
break;
case UIDeviceOrientationPortraitUpsideDown:
orientationString = @"PORTRAIT_DOWN";
break;
default:
break;
}
if (self->_eventSink != nil) {
self->_eventSink(orientationString);
}
}
}];
}

- (void)initCamera:(CameraSensor)sensor {
// Here we set a preset which wont crash the device before switching to front or back
[_captureSession setSessionPreset:AVCaptureSessionPresetPhoto];

NSError *error;
_captureDevice = [AVCaptureDevice deviceWithUniqueID:[self selectAvailableCamera:sensor]];
_captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice error:&error];
Expand All @@ -68,13 +99,6 @@ - (void)initCamera:(CameraSensor)sensor {
return;
}

// Set preset
if (sensor == Back) {
[self setPreviewSize:_previewSize];
} else {
[_captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
}

// Create connection
_captureConnection = [AVCaptureConnection connectionWithInputPorts:_captureVideoInput.ports
output:_captureVideoOutput];
Expand All @@ -92,6 +116,28 @@ - (void)initCamera:(CameraSensor)sensor {
[_captureConnection setAutomaticallyAdjustsVideoMirroring:NO];
[_captureConnection setVideoMirrored:(_cameraSensor == Back)];
[_captureConnection setVideoOrientation:AVCaptureVideoOrientationPortrait];

[self setCameraPresset:CGSizeMake(0, 0)];
}

- (void)setCameraPresset:(CGSize)currentPreviewSize {
NSString *presetSelected;
if (!CGSizeEqualToSize(CGSizeZero, currentPreviewSize)) {
// Try to get the quality requested
presetSelected = [CameraQualities selectVideoCapturePresset:currentPreviewSize session:_captureSession];
} else {
// Compute the best quality supported by the camera device
presetSelected = [CameraQualities selectVideoCapturePresset:_captureSession];
}
[_captureSession setSessionPreset:presetSelected];
_currentPresset = presetSelected;

// Get preview size according to presset selected
_currentPreviewSize = [CameraQualities getSizeForPresset:presetSelected];
}

- (CGSize)getEffectivPreviewSize {
return _currentPreviewSize;
}

- (void)setResult:(nonnull FlutterResult)result {
Expand All @@ -103,9 +149,7 @@ - (void)dispose {
}

- (void)setPreviewSize:(CGSize)previewSize {
NSString *selectedPresset = [CameraQualities selectVideoCapturePresset:previewSize session:_captureSession];
_previewSize = previewSize;
_captureSession.sessionPreset = selectedPresset;
[self setCameraPresset:previewSize];
}

- (void)start {
Expand Down Expand Up @@ -147,7 +191,7 @@ - (void)setZoom:(float)value {
_captureDevice.videoZoomFactor = scaledZoom;
[_captureDevice unlockForConfiguration];
} else {
NSLog(@"error: %@", error);
_result([FlutterError errorWithCode:@"ZOOM_NOT_SET" message:@"can't set the zoom value" details:[error localizedDescription]]);
}
}

Expand Down Expand Up @@ -198,8 +242,8 @@ - (void)instantFocus {
NSError *error;

// Get center point of the preview size
double focus_x = _previewSize.width / 2;
double focus_y = _previewSize.height / 2;
double focus_x = _currentPreviewSize.width / 2;
double focus_y = _currentPreviewSize.height / 2;

CGPoint thisFocusPoint = [_previewLayer captureDevicePointOfInterestForPoint:CGPointMake(focus_x, focus_y)];
if ([_captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus] && [_captureDevice isFocusPointOfInterestSupported]) {
Expand Down
2 changes: 1 addition & 1 deletion ios/Classes/CamerawesomePlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

NS_ASSUME_NONNULL_BEGIN

@interface CamerawesomePlugin : NSObject<FlutterPlugin>
@interface CamerawesomePlugin : NSObject<FlutterPlugin, FlutterStreamHandler>
@end

NS_ASSUME_NONNULL_END
50 changes: 40 additions & 10 deletions ios/Classes/CamerawesomePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ @interface CamerawesomePlugin ()

@property(readonly, nonatomic) NSObject<FlutterTextureRegistry> *registry;
@property(readonly, nonatomic) NSObject<FlutterBinaryMessenger> *messenger;
@property FlutterEventSink eventSink;
@property int64_t textureId;
@property CameraView *camera;

Expand All @@ -15,19 +16,35 @@ - (instancetype)initWithRegistry:(NSObject<FlutterTextureRegistry> *)registry me
@implementation CamerawesomePlugin
- (instancetype)initWithRegistry:(NSObject<FlutterTextureRegistry> *)registry
messenger:(NSObject<FlutterBinaryMessenger> *)messenger {
self = [super init];
NSAssert(self, @"super init cannot be nil");
_registry = registry;
_messenger = messenger;
self = [super init];
NSAssert(self, @"super init cannot be nil");
_registry = registry;
_messenger = messenger;

return self;
}

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"camerawesome"
binaryMessenger:[registrar messenger]];
CamerawesomePlugin* instance = [[CamerawesomePlugin alloc] initWithRegistry:[registrar textures] messenger:[registrar messenger]];
[registrar addMethodCallDelegate:instance channel:channel];
CamerawesomePlugin *instance = [[CamerawesomePlugin alloc] initWithRegistry:[registrar textures] messenger:[registrar messenger]];

FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:@"camerawesome/orientation"
binaryMessenger:[registrar messenger]];
[eventChannel setStreamHandler:instance];

// TODO: Change to "camerawesome/methods"
FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"camerawesome" binaryMessenger:[registrar messenger]];
[registrar addMethodCallDelegate:instance channel:methodChannel];
}

- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
eventSink:(FlutterEventSink)events {
_eventSink = events;
return nil;
}

- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments {
_eventSink = nil;
return nil;
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
Expand All @@ -49,6 +66,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
[self _handleGetTextures:call result:result];
} else if ([@"setPreviewSize" isEqualToString:call.method]) {
[self _handlePreviewSize:call result:result];
} else if ([@"getEffectivPreviewSize" isEqualToString:call.method]) {
[self _handleGetEffectivPreviewSize:call result:result];
} else if ([@"setPhotoSize" isEqualToString:call.method]) {
[self _handlePhotoSize:call result:result];
} else if ([@"takePhoto" isEqualToString:call.method]) {
Expand All @@ -71,6 +90,14 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
}
}

- (void)_handleGetEffectivPreviewSize:(FlutterMethodCall*)call result:(FlutterResult)result {
CGSize previewSize = [_camera getEffectivPreviewSize];
result(@{
@"width": [NSNumber numberWithInt:previewSize.width],
@"height": [NSNumber numberWithInt:previewSize.height],
});
}

- (void)_handleSetZoom:(FlutterMethodCall*)call result:(FlutterResult)result {
float value = [call.arguments[@"zoom"] floatValue];

Expand Down Expand Up @@ -188,7 +215,10 @@ - (void)_handleSetup:(FlutterMethodCall*)call result:(FlutterResult)result {
}

CameraSensor sensor = ([sensorName isEqualToString:@"FRONT"]) ? Front : Back;
self.camera = [[CameraView alloc] initWithCameraSensor:sensor andResult:result];
self.camera = [[CameraView alloc] initWithCameraSensor:sensor
result:result
messenger:_messenger
event:_eventSink];
[self->_registry textureFrameAvailable:_textureId];

__weak typeof(self) weakSelf = self;
Expand Down
2 changes: 2 additions & 0 deletions ios/Classes/Constants/CameraQualities.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface CameraQualities : NSObject

+ (NSString *)selectVideoCapturePresset:(CGSize)size session:(AVCaptureSession *)session;
+ (NSString *)selectVideoCapturePresset:(AVCaptureSession *)session;
+ (CGSize)getSizeForPresset:(NSString *)presset;

@end

Expand Down