scoplan.camera 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +18 -0
- package/plugin.xml +119 -0
- package/src/android/AutoFitSurfaceView.java +66 -0
- package/src/android/CCropActivity.java +17 -0
- package/src/android/CDrawActivity.java +16 -0
- package/src/android/CTextActivity.java +17 -0
- package/src/android/CameraEventListener.java +8 -0
- package/src/android/CameraFragment.java +635 -0
- package/src/android/CameraSeekBarListener.java +105 -0
- package/src/android/CameraUtils.java +94 -0
- package/src/android/CustomI.java +37 -0
- package/src/android/FakeR.java +31 -0
- package/src/android/ImageCameraAvailableListener.java +65 -0
- package/src/android/OnImageCaptureListener.java +11 -0
- package/src/android/PhotoEditorActivity.java +128 -0
- package/src/android/PhotoEditorMenu.java +111 -0
- package/src/android/PhotoEditorMesureCustomActivity.java +218 -0
- package/src/android/PhotoEditorStore.java +50 -0
- package/src/android/ScoplanCamera.java +98 -0
- package/src/android/gradle/scoplanCamera.gradle +9 -0
- package/src/android/libs/ds-photo-editor-sdk-v9.aar +0 -0
- package/src/android/res/drawable/back_arrow.xml +5 -0
- package/src/android/res/drawable/camsicon.png +0 -0
- package/src/android/res/drawable/custom_spinner.xml +14 -0
- package/src/android/res/drawable/delete_img.png +0 -0
- package/src/android/res/drawable/draw_img.png +0 -0
- package/src/android/res/drawable/flash.png +0 -0
- package/src/android/res/drawable/flash_on.png +0 -0
- package/src/android/res/drawable/mesure.png +0 -0
- package/src/android/res/drawable/mesure_bar.xml +19 -0
- package/src/android/res/drawable/shape_back.xml +12 -0
- package/src/android/res/drawable/shape_cancel.xml +12 -0
- package/src/android/res/drawable/shape_rectangle.xml +4 -0
- package/src/android/res/layout/fragment_camera.xml +157 -0
- package/src/android/res/layout/fragment_photo_editor_menu.xml +122 -0
- package/src/ios/AVCamPhotoCaptureDelegate.h +20 -0
- package/src/ios/AVCamPhotoCaptureDelegate.m +244 -0
- package/src/ios/AVCamPreviewView.h +18 -0
- package/src/ios/AVCamPreviewView.m +33 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDK +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Assets.car +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/0dZ-3o-Xdu-view-Uan-aZ-nTR.nib/objects-11.0+.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/0dZ-3o-Xdu-view-Uan-aZ-nTR.nib/objects-13.0+.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/0dZ-3o-Xdu-view-Uan-aZ-nTR.nib/runtime.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/DSDrawViewController.nib/objects-11.0+.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/DSDrawViewController.nib/objects-13.0+.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/DSDrawViewController.nib/runtime.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/DSPhotoEditor.nib/objects-11.0+.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/DSPhotoEditor.nib/objects-13.0+.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/DSPhotoEditor.nib/runtime.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/Info.plist +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/Okc-Jl-tk5-view-siV-MN-Zgc.nib/objects-11.0+.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/Okc-Jl-tk5-view-siV-MN-Zgc.nib/objects-13.0+.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDK.storyboardc/Okc-Jl-tk5-view-siV-MN-Zgc.nib/runtime.nib +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Base.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/Info.plist +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ar.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ar.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/de.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/de.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/es.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/es.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/fr.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/fr.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/id.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/id.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/it.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/it.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ja.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ja.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ko.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ko.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ms.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ms.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/pt-PT.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/pt-PT.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ru.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/ru.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/th.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/th.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/tr.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/tr.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/vi.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/vi.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/zh-Hans.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/zh-Hans.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/zh-Hant.lproj/DSPhotoEditorSDK.strings +57 -0
- package/src/ios/DSPhotoEditorSDK.framework/DSPhotoEditorSDKResourceBundle.bundle/zh-Hant.lproj/DSPhotoEditorSDKLocalizable.strings +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/Headers/DSPhotoEditorSDK.h +18 -0
- package/src/ios/DSPhotoEditorSDK.framework/Headers/DSPhotoEditorViewController.h +112 -0
- package/src/ios/DSPhotoEditorSDK.framework/Info.plist +0 -0
- package/src/ios/DSPhotoEditorSDK.framework/License.md +70 -0
- package/src/ios/DSPhotoEditorSDK.framework/Modules/module.modulemap +6 -0
- package/src/ios/DSPhotoEditorSDK.framework/framework_version.txt +5 -0
- package/src/ios/DSPhotoEditorSDK.framework/strip-frameworks.sh +80 -0
- package/src/ios/Resources/Overlay.png +0 -0
- package/src/ios/Resources/Overlay@2x.png +0 -0
- package/src/ios/Resources/back_arrows.png +0 -0
- package/src/ios/Resources/ds_btn_calc.png +0 -0
- package/src/ios/Resources/ds_btn_crop.png +0 -0
- package/src/ios/Resources/ds_btn_del.png +0 -0
- package/src/ios/Resources/ds_btn_draw.png +0 -0
- package/src/ios/Resources/ds_btn_text.png +0 -0
- package/src/ios/Resources/measure_0.png +0 -0
- package/src/ios/Resources/measure_1.png +0 -0
- package/src/ios/Resources/measure_10.png +0 -0
- package/src/ios/Resources/measure_11.png +0 -0
- package/src/ios/Resources/measure_2.png +0 -0
- package/src/ios/Resources/measure_3.png +0 -0
- package/src/ios/Resources/measure_4.png +0 -0
- package/src/ios/Resources/measure_5.png +0 -0
- package/src/ios/Resources/measure_6.png +0 -0
- package/src/ios/Resources/measure_7.png +0 -0
- package/src/ios/Resources/measure_8.png +0 -0
- package/src/ios/Resources/measure_9.png +0 -0
- package/src/ios/Resources/no.png +0 -0
- package/src/ios/Resources/ok.png +0 -0
- package/src/ios/Resources/toward_arrows.png +0 -0
- package/src/ios/ScoplanCamera.h +18 -0
- package/src/ios/ScoplanCamera.m +179 -0
- package/src/ios/ScoplanCamera.storyboard +115 -0
- package/src/ios/UICustomPickerController.h +11 -0
- package/src/ios/UICustomPickerController.m +119 -0
- package/src/ios/UIImagePickerDelegate.h +11 -0
- package/src/ios/UIImagePickerDelegate.m +161 -0
- package/src/ios/UIScoplanCamera.h +20 -0
- package/src/ios/UIScoplanCamera.m +1098 -0
- package/src/ios/bottomPhotoEditor.xib +222 -0
- package/src/ios/camsicons.xcassets/Contents.json +6 -0
- package/src/ios/camsicons.xcassets/cameraicon.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/cameraicon.imageset/camsicon.png +0 -0
- package/src/ios/camsicons.xcassets/image000001.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image000001.imageset/image0000002.png +0 -0
- package/src/ios/camsicons.xcassets/image0000010.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000010.imageset/image0000010.png +0 -0
- package/src/ios/camsicons.xcassets/image0000011.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000011.imageset/image0000011.png +0 -0
- package/src/ios/camsicons.xcassets/image0000012.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000012.imageset/image0000012.png +0 -0
- package/src/ios/camsicons.xcassets/image0000013.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000013.imageset/image0000013.png +0 -0
- package/src/ios/camsicons.xcassets/image0000014.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000014.imageset/image0000014.png +0 -0
- package/src/ios/camsicons.xcassets/image0000015.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000015.imageset/image0000015.png +0 -0
- package/src/ios/camsicons.xcassets/image0000016.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000016.imageset/image0000016.png +0 -0
- package/src/ios/camsicons.xcassets/image0000017.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000017.imageset/image0000017.png +0 -0
- package/src/ios/camsicons.xcassets/image0000018.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000018.imageset/image0000018.png +0 -0
- package/src/ios/camsicons.xcassets/image0000019.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image0000019.imageset/image0000019.png +0 -0
- package/src/ios/camsicons.xcassets/image000002.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image000002.imageset/image0000001.png +0 -0
- package/src/ios/camsicons.xcassets/image000003.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image000003.imageset/image0000003.png +0 -0
- package/src/ios/camsicons.xcassets/image000004.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image000004.imageset/image0000004.png +0 -0
- package/src/ios/camsicons.xcassets/image000005.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image000005.imageset/image0000005.png +0 -0
- package/src/ios/camsicons.xcassets/image000006.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image000006.imageset/image0000006.png +0 -0
- package/src/ios/camsicons.xcassets/image000007.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image000007.imageset/image0000007.png +0 -0
- package/src/ios/camsicons.xcassets/image000008.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image000008.imageset/image0000008.png +0 -0
- package/src/ios/camsicons.xcassets/image000009.imageset/Contents.json +21 -0
- package/src/ios/camsicons.xcassets/image000009.imageset/image0000009.png +0 -0
- package/src/ios/multicam.xib +113 -0
- package/www/ScoplanCamera.js +5 -0
|
@@ -0,0 +1,1098 @@
|
|
|
1
|
+
//
|
|
2
|
+
// UIScoplanCamera.m
|
|
3
|
+
// My happy client
|
|
4
|
+
//
|
|
5
|
+
// Created by Adriela on 30/04/2018.
|
|
6
|
+
//
|
|
7
|
+
@import Photos;
|
|
8
|
+
|
|
9
|
+
#import <Foundation/Foundation.h>
|
|
10
|
+
#import "UIScoplanCamera.h"
|
|
11
|
+
#import "ScoplanCamera.h"
|
|
12
|
+
#import "AVCamPhotoCaptureDelegate.h"
|
|
13
|
+
#import "AVCamPreviewView.h"
|
|
14
|
+
#import <CoreMotion/CoreMotion.h>
|
|
15
|
+
|
|
16
|
+
static void * SessionRunningContext = &SessionRunningContext;
|
|
17
|
+
static AVCaptureVideoOrientation curOr;
|
|
18
|
+
|
|
19
|
+
typedef NS_ENUM( NSInteger, AVCamSetupResult ) {
|
|
20
|
+
AVCamSetupResultSuccess,
|
|
21
|
+
AVCamSetupResultCameraNotAuthorized,
|
|
22
|
+
AVCamSetupResultSessionConfigurationFailed
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
typedef NS_ENUM( NSInteger, AVCamCaptureMode ) {
|
|
26
|
+
AVCamCaptureModePhoto = 0,
|
|
27
|
+
AVCamCaptureModeMovie = 1
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
|
|
31
|
+
AVCamLivePhotoModeOn,
|
|
32
|
+
AVCamLivePhotoModeOff
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
typedef NS_ENUM( NSInteger, AVCamDepthDataDeliveryMode ) {
|
|
36
|
+
AVCamDepthDataDeliveryModeOn,
|
|
37
|
+
AVCamDepthDataDeliveryModeOff
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
@interface AVCaptureDeviceDiscoverySession (Utilities)
|
|
41
|
+
|
|
42
|
+
- (NSInteger)uniqueDevicePositionsCount;
|
|
43
|
+
|
|
44
|
+
@end
|
|
45
|
+
|
|
46
|
+
@implementation AVCaptureDeviceDiscoverySession (Utilities)
|
|
47
|
+
|
|
48
|
+
- (NSInteger)uniqueDevicePositionsCount
|
|
49
|
+
{
|
|
50
|
+
NSMutableArray<NSNumber *> *uniqueDevicePositions = [NSMutableArray array];
|
|
51
|
+
|
|
52
|
+
for ( AVCaptureDevice *device in self.devices ) {
|
|
53
|
+
if ( ! [uniqueDevicePositions containsObject:@(device.position)] ) {
|
|
54
|
+
[uniqueDevicePositions addObject:@(device.position)];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return uniqueDevicePositions.count;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@end
|
|
62
|
+
|
|
63
|
+
@interface UIScoplanCamera () <AVCaptureFileOutputRecordingDelegate>
|
|
64
|
+
@property (strong, nonatomic) IBOutlet UIView *rootView;
|
|
65
|
+
@property (strong, nonatomic) IBOutlet UIImageView *imageview;
|
|
66
|
+
@property (strong, nonatomic) IBOutlet UIButton *okbutton;
|
|
67
|
+
@property (strong, nonatomic) IBOutlet UIView *blackbottom;
|
|
68
|
+
@property (strong, nonatomic) IBOutlet UIImageView *progress;
|
|
69
|
+
|
|
70
|
+
// Session management.
|
|
71
|
+
@property (nonatomic, weak) IBOutlet AVCamPreviewView *previewView;
|
|
72
|
+
@property (nonatomic, weak) IBOutlet UISegmentedControl *captureModeControl;
|
|
73
|
+
|
|
74
|
+
@property (nonatomic) AVCamSetupResult setupResult;
|
|
75
|
+
@property (nonatomic) dispatch_queue_t sessionQueue;
|
|
76
|
+
@property (nonatomic) AVCaptureSession *session;
|
|
77
|
+
@property (nonatomic, getter=isSessionRunning) BOOL sessionRunning;
|
|
78
|
+
@property (nonatomic) AVCaptureDeviceInput *videoDeviceInput;
|
|
79
|
+
|
|
80
|
+
// Device configuration.
|
|
81
|
+
@property (nonatomic, weak) IBOutlet UIButton *cameraButton;
|
|
82
|
+
//@property (nonatomic, weak) IBOutlet UILabel *cameraUnavailableLabel;
|
|
83
|
+
@property (nonatomic) AVCaptureDeviceDiscoverySession *videoDeviceDiscoverySession;
|
|
84
|
+
|
|
85
|
+
// Capturing photos.
|
|
86
|
+
@property (nonatomic, weak) IBOutlet UIButton *photoButton;
|
|
87
|
+
//@property (nonatomic, weak) IBOutlet UIButton *livePhotoModeButton;
|
|
88
|
+
@property (nonatomic) AVCamLivePhotoMode livePhotoMode;
|
|
89
|
+
//@property (nonatomic, weak) IBOutlet UILabel *capturingLivePhotoLabel;
|
|
90
|
+
//@property (nonatomic, weak) IBOutlet UIButton *depthDataDeliveryButton;
|
|
91
|
+
@property (nonatomic) AVCamDepthDataDeliveryMode depthDataDeliveryMode;
|
|
92
|
+
|
|
93
|
+
@property (nonatomic) AVCapturePhotoOutput *photoOutput;
|
|
94
|
+
@property (nonatomic) NSMutableDictionary<NSNumber *, AVCamPhotoCaptureDelegate *> *inProgressPhotoCaptureDelegates;
|
|
95
|
+
@property (nonatomic) NSInteger inProgressLivePhotoCapturesCount;
|
|
96
|
+
|
|
97
|
+
// Recording movies.
|
|
98
|
+
//@property (nonatomic, weak) IBOutlet UIButton *recordButton;
|
|
99
|
+
//@property (nonatomic, weak) IBOutlet UIButton *resumeButton;
|
|
100
|
+
|
|
101
|
+
@property (nonatomic, strong) AVCaptureMovieFileOutput *movieFileOutput;
|
|
102
|
+
@property (nonatomic) UIBackgroundTaskIdentifier backgroundRecordingID;
|
|
103
|
+
|
|
104
|
+
@end
|
|
105
|
+
|
|
106
|
+
@implementation UIScoplanCamera
|
|
107
|
+
|
|
108
|
+
#pragma mark View Controller Life Cycle
|
|
109
|
+
|
|
110
|
+
- (void) setMCam:(ScoplanCamera *)mCam{
|
|
111
|
+
mCamera = mCam;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
- (void)insertImgUrl:(NSString*)url{
|
|
115
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
116
|
+
[_okbutton setTitle: @"OK" forState: UIControlStateNormal];
|
|
117
|
+
[_photoButton setHidden:FALSE];
|
|
118
|
+
[_progress stopAnimating];
|
|
119
|
+
[_progress setHidden:TRUE];
|
|
120
|
+
});
|
|
121
|
+
[mCamera insertPicture:url];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
- (NSInteger)getOrientation{
|
|
125
|
+
return curOr;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
- (void)viewDidLoad
|
|
129
|
+
{
|
|
130
|
+
[super viewDidLoad];
|
|
131
|
+
|
|
132
|
+
// Disable UI. The UI is enabled if and only if the session starts running.
|
|
133
|
+
// self.cameraButton.enabled = NO;
|
|
134
|
+
// self.recordButton.enabled = NO;
|
|
135
|
+
self.photoButton.enabled = NO;
|
|
136
|
+
NSMutableArray * framesLoader = [[NSMutableArray alloc] initWithCapacity:18];
|
|
137
|
+
for (int i = 1; i < 20; i++) {
|
|
138
|
+
NSString * imageName = [NSString stringWithFormat:@"image00000%d.png",i];
|
|
139
|
+
UIImage * img = [UIImage imageNamed:imageName];
|
|
140
|
+
[framesLoader addObject:img];
|
|
141
|
+
}
|
|
142
|
+
self.progress.animationImages = framesLoader;
|
|
143
|
+
self.progress.animationDuration = 1.0f;
|
|
144
|
+
self.progress.animationRepeatCount = 0;
|
|
145
|
+
//self.livePhotoModeButton.enabled = NO;
|
|
146
|
+
self.captureModeControl.enabled = YES;
|
|
147
|
+
//self.depthDataDeliveryButton.enabled = NO;
|
|
148
|
+
|
|
149
|
+
// Create the AVCaptureSession.
|
|
150
|
+
self.session = [[AVCaptureSession alloc] init];
|
|
151
|
+
|
|
152
|
+
// Create a device discovery session.
|
|
153
|
+
NSArray<AVCaptureDeviceType> *deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInDualCamera];
|
|
154
|
+
self.videoDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
|
|
155
|
+
/*
|
|
156
|
+
Remove the AVCaptureMovieFileOutput from the session because movie recording is
|
|
157
|
+
not supported with AVCaptureSessionPresetPhoto. Additionally, Live Photo
|
|
158
|
+
capture is not supported when an AVCaptureMovieFileOutput is connected to the session.
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
// Set up the preview view.
|
|
162
|
+
self.previewView.session = self.session;
|
|
163
|
+
|
|
164
|
+
// Communicate with the session and other session objects on this queue.
|
|
165
|
+
self.sessionQueue = dispatch_queue_create( "session queue", DISPATCH_QUEUE_SERIAL );
|
|
166
|
+
|
|
167
|
+
self.setupResult = AVCamSetupResultSuccess;
|
|
168
|
+
|
|
169
|
+
[self.session beginConfiguration];
|
|
170
|
+
[self.session removeOutput:self.movieFileOutput];
|
|
171
|
+
if([self.session canSetSessionPreset:AVCaptureSessionPresetPhoto]){
|
|
172
|
+
self.session.sessionPreset = AVCaptureSessionPresetPhoto;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
self.movieFileOutput = nil;
|
|
176
|
+
|
|
177
|
+
if ( self.photoOutput.livePhotoCaptureSupported ) {
|
|
178
|
+
self.photoOutput.livePhotoCaptureEnabled = YES;
|
|
179
|
+
|
|
180
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
181
|
+
//self.livePhotoModeButton.enabled = YES;
|
|
182
|
+
//self.livePhotoModeButton.hidden = NO;
|
|
183
|
+
} );
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if ( self.photoOutput.depthDataDeliverySupported ) {
|
|
187
|
+
self.photoOutput.depthDataDeliveryEnabled = YES;
|
|
188
|
+
|
|
189
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
190
|
+
//self.depthDataDeliveryButton.hidden = NO;
|
|
191
|
+
//self.depthDataDeliveryButton.enabled = YES;
|
|
192
|
+
} );
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
//[testButn addTarget:self action:@selector(staypressed:) forControlEvents:UIControlEventTouchUpInside];
|
|
196
|
+
UIPinchGestureRecognizer* pinchGst = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchForZoom:)];
|
|
197
|
+
[self.previewView addGestureRecognizer:pinchGst];
|
|
198
|
+
[self.session commitConfiguration];
|
|
199
|
+
|
|
200
|
+
/*
|
|
201
|
+
Setup the capture session.
|
|
202
|
+
In general it is not safe to mutate an AVCaptureSession or any of its
|
|
203
|
+
inputs, outputs, or connections from multiple threads at the same time.
|
|
204
|
+
|
|
205
|
+
Why not do all of this on the main queue?
|
|
206
|
+
Because -[AVCaptureSession startRunning] is a blocking call which can
|
|
207
|
+
take a long time. We dispatch session setup to the sessionQueue so
|
|
208
|
+
that the main queue isn't blocked, which keeps the UI responsive.
|
|
209
|
+
*/
|
|
210
|
+
dispatch_async( self.sessionQueue, ^{
|
|
211
|
+
[self configureSession];
|
|
212
|
+
} );
|
|
213
|
+
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
- (void)pinchForZoom:(UIPinchGestureRecognizer *)pinchRecognizer{
|
|
217
|
+
static CGFloat zoomFactorBegin = .0;
|
|
218
|
+
if ( UIGestureRecognizerStateBegan == pinchRecognizer.state ) {
|
|
219
|
+
zoomFactorBegin = self.videoDeviceInput.device.videoZoomFactor;
|
|
220
|
+
|
|
221
|
+
} else if (UIGestureRecognizerStateChanged == pinchRecognizer.state) {
|
|
222
|
+
NSError *error = nil;
|
|
223
|
+
if ([self.videoDeviceInput.device lockForConfiguration:&error]) {
|
|
224
|
+
|
|
225
|
+
CGFloat desiredZoomFactor = zoomFactorBegin * pinchRecognizer.scale;
|
|
226
|
+
CGFloat zoomFactor = MAX(1.0, MIN(desiredZoomFactor, self.videoDeviceInput.device.activeFormat.videoMaxZoomFactor));
|
|
227
|
+
[self.videoDeviceInput.device rampToVideoZoomFactor:zoomFactor withRate:3.0];
|
|
228
|
+
|
|
229
|
+
[self.videoDeviceInput.device unlockForConfiguration];
|
|
230
|
+
} else {
|
|
231
|
+
NSLog(@"error: %@", error);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
- (void)viewWillAppear:(BOOL)animated
|
|
238
|
+
{
|
|
239
|
+
[super viewWillAppear:animated];
|
|
240
|
+
cm = [[CMMotionManager alloc] init];
|
|
241
|
+
cm.deviceMotionUpdateInterval=0.5f;
|
|
242
|
+
if([cm isDeviceMotionAvailable])
|
|
243
|
+
[cm startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
|
|
244
|
+
withHandler:^(CMDeviceMotion *data, NSError *error) {
|
|
245
|
+
if(fabs(data.gravity.x)>fabs(data.gravity.y)){
|
|
246
|
+
//NSLog(@"LANSCAPE");
|
|
247
|
+
if(data.gravity.x>=0){
|
|
248
|
+
//NSLog(@"LEFT");
|
|
249
|
+
curOr = AVCaptureVideoOrientationLandscapeLeft;
|
|
250
|
+
}
|
|
251
|
+
else{
|
|
252
|
+
//NSLog(@"RIGHT");
|
|
253
|
+
curOr = AVCaptureVideoOrientationLandscapeRight;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
}
|
|
257
|
+
else{
|
|
258
|
+
//NSLog(@"PORTRAIT");
|
|
259
|
+
if(data.gravity.y>=0){
|
|
260
|
+
//NSLog(@"DOWN");
|
|
261
|
+
curOr = AVCaptureVideoOrientationPortraitUpsideDown;
|
|
262
|
+
}
|
|
263
|
+
else{
|
|
264
|
+
//NSLog(@"UP");
|
|
265
|
+
curOr = AVCaptureVideoOrientationPortrait;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
}];
|
|
271
|
+
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
|
|
272
|
+
previewLayer.frame = _rootView.bounds;
|
|
273
|
+
previewLayer.videoGravity = AVLayerVideoGravityResizeAspect;
|
|
274
|
+
[_rootView.layer addSublayer:previewLayer];
|
|
275
|
+
_blackbottom.layer.zPosition = 1;
|
|
276
|
+
_photoButton.imageView.layer.zPosition = 1;
|
|
277
|
+
_photoButton.layer.zPosition = 1;
|
|
278
|
+
_imageview.layer.zPosition = 1;
|
|
279
|
+
_okbutton.layer.zPosition = 1;
|
|
280
|
+
_progress.layer.zPosition = 1;
|
|
281
|
+
[_progress setHidden:TRUE];
|
|
282
|
+
dispatch_async( self.sessionQueue, ^{
|
|
283
|
+
switch ( self.setupResult )
|
|
284
|
+
{
|
|
285
|
+
case AVCamSetupResultSuccess:
|
|
286
|
+
{
|
|
287
|
+
// Only setup observers and start the session running if setup succeeded.
|
|
288
|
+
[self addObservers];
|
|
289
|
+
[self.session startRunning];
|
|
290
|
+
self.sessionRunning = self.session.isRunning;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
case AVCamSetupResultCameraNotAuthorized:
|
|
294
|
+
{
|
|
295
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
296
|
+
NSString *message = NSLocalizedString( @"AVCam doesn't have permission to use the camera, please change privacy settings", @"Alert message when the user has denied access to the camera" );
|
|
297
|
+
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"AVCam" message:message preferredStyle:UIAlertControllerStyleAlert];
|
|
298
|
+
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString( @"OK", @"Alert OK button" ) style:UIAlertActionStyleCancel handler:nil];
|
|
299
|
+
[alertController addAction:cancelAction];
|
|
300
|
+
// Provide quick access to Settings.
|
|
301
|
+
UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:NSLocalizedString( @"Settings", @"Alert button to open Settings" ) style:UIAlertActionStyleDefault handler:^( UIAlertAction *action ) {
|
|
302
|
+
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
|
|
303
|
+
}];
|
|
304
|
+
[alertController addAction:settingsAction];
|
|
305
|
+
[self presentViewController:alertController animated:YES completion:nil];
|
|
306
|
+
} );
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
case AVCamSetupResultSessionConfigurationFailed:
|
|
310
|
+
{
|
|
311
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
312
|
+
NSString *message = NSLocalizedString( @"Unable to capture media", @"Alert message when something goes wrong during capture session configuration" );
|
|
313
|
+
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"AVCam" message:message preferredStyle:UIAlertControllerStyleAlert];
|
|
314
|
+
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString( @"OK", @"Alert OK button" ) style:UIAlertActionStyleCancel handler:nil];
|
|
315
|
+
[alertController addAction:cancelAction];
|
|
316
|
+
[self presentViewController:alertController animated:YES completion:nil];
|
|
317
|
+
} );
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} );
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
- (void) setPhotodata:(AVCamPhotoCaptureDelegate*)photoCaptureDelegate{
|
|
325
|
+
UIImage *imgdata = [[UIImage alloc]initWithData:[photoCaptureDelegate getPhotodata]];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
- (void)viewDidDisappear:(BOOL)animated
|
|
329
|
+
{
|
|
330
|
+
dispatch_async( self.sessionQueue, ^{
|
|
331
|
+
if ( self.setupResult == AVCamSetupResultSuccess ) {
|
|
332
|
+
[self.session stopRunning];
|
|
333
|
+
[self removeObservers];
|
|
334
|
+
}
|
|
335
|
+
} );
|
|
336
|
+
|
|
337
|
+
[super viewDidDisappear:animated];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
- (BOOL)shouldAutorotate
|
|
341
|
+
{
|
|
342
|
+
// Disable autorotation of the interface when recording is in progress.
|
|
343
|
+
return ! self.movieFileOutput.isRecording;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
|
347
|
+
{
|
|
348
|
+
return UIInterfaceOrientationMaskPortrait;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
|
|
352
|
+
{
|
|
353
|
+
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
|
354
|
+
|
|
355
|
+
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
|
|
356
|
+
|
|
357
|
+
if ( UIDeviceOrientationIsPortrait( deviceOrientation ) || UIDeviceOrientationIsLandscape( deviceOrientation ) ) {
|
|
358
|
+
self.previewView.videoPreviewLayer.connection.videoOrientation = (AVCaptureVideoOrientation)deviceOrientation;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
#pragma mark Session Management
|
|
363
|
+
|
|
364
|
+
// Call this on the session queue.
|
|
365
|
+
- (void)configureSession
|
|
366
|
+
{
|
|
367
|
+
if ( self.setupResult != AVCamSetupResultSuccess ) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
NSError *error = nil;
|
|
372
|
+
|
|
373
|
+
[self.session beginConfiguration];
|
|
374
|
+
|
|
375
|
+
/*
|
|
376
|
+
We do not create an AVCaptureMovieFileOutput when setting up the session because the
|
|
377
|
+
AVCaptureMovieFileOutput does not support movie recording with AVCaptureSessionPresetPhoto.
|
|
378
|
+
*/
|
|
379
|
+
self.session.sessionPreset = AVCaptureSessionPresetPhoto;
|
|
380
|
+
|
|
381
|
+
// Add video input.
|
|
382
|
+
|
|
383
|
+
// Choose the back dual camera if available, otherwise default to a wide angle camera.
|
|
384
|
+
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
|
|
385
|
+
if ( ! videoDevice ) {
|
|
386
|
+
// If the back dual camera is not available, default to the back wide angle camera.
|
|
387
|
+
videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
|
|
388
|
+
|
|
389
|
+
// In some cases where users break their phones, the back wide angle camera is not available. In this case, we should default to the front wide angle camera.
|
|
390
|
+
if ( ! videoDevice ) {
|
|
391
|
+
videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionFront];
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
|
|
395
|
+
if ( ! videoDeviceInput ) {
|
|
396
|
+
NSLog( @"Could not create video device input: %@", error );
|
|
397
|
+
self.setupResult = AVCamSetupResultSessionConfigurationFailed;
|
|
398
|
+
[self.session commitConfiguration];
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if ( [self.session canAddInput:videoDeviceInput] ) {
|
|
402
|
+
[self.session addInput:videoDeviceInput];
|
|
403
|
+
self.videoDeviceInput = videoDeviceInput;
|
|
404
|
+
|
|
405
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
406
|
+
/*
|
|
407
|
+
Why are we dispatching this to the main queue?
|
|
408
|
+
Because AVCaptureVideoPreviewLayer is the backing layer for AVCamPreviewView and UIView
|
|
409
|
+
can only be manipulated on the main thread.
|
|
410
|
+
Note: As an exception to the above rule, it is not necessary to serialize video orientation changes
|
|
411
|
+
on the AVCaptureVideoPreviewLayer’s connection with other session manipulation.
|
|
412
|
+
|
|
413
|
+
Use the status bar orientation as the initial video orientation. Subsequent orientation changes are
|
|
414
|
+
handled by -[AVCamCameraViewController viewWillTransitionToSize:withTransitionCoordinator:].
|
|
415
|
+
*/
|
|
416
|
+
UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
|
|
417
|
+
AVCaptureVideoOrientation initialVideoOrientation = AVCaptureVideoOrientationPortrait;
|
|
418
|
+
if ( statusBarOrientation != UIInterfaceOrientationUnknown ) {
|
|
419
|
+
initialVideoOrientation = (AVCaptureVideoOrientation)statusBarOrientation;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
self.previewView.videoPreviewLayer.connection.videoOrientation = initialVideoOrientation;
|
|
423
|
+
} );
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
NSLog( @"Could not add video device input to the session" );
|
|
427
|
+
self.setupResult = AVCamSetupResultSessionConfigurationFailed;
|
|
428
|
+
[self.session commitConfiguration];
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Add photo output.
|
|
433
|
+
AVCapturePhotoOutput *photoOutput = [[AVCapturePhotoOutput alloc] init];
|
|
434
|
+
if ( [self.session canAddOutput:photoOutput] ) {
|
|
435
|
+
[self.session addOutput:photoOutput];
|
|
436
|
+
self.photoOutput = photoOutput;
|
|
437
|
+
|
|
438
|
+
self.photoOutput.highResolutionCaptureEnabled = YES;
|
|
439
|
+
self.photoOutput.livePhotoCaptureEnabled = self.photoOutput.livePhotoCaptureSupported;
|
|
440
|
+
self.photoOutput.depthDataDeliveryEnabled = self.photoOutput.depthDataDeliverySupported;
|
|
441
|
+
|
|
442
|
+
self.livePhotoMode = self.photoOutput.livePhotoCaptureSupported ? AVCamLivePhotoModeOn : AVCamLivePhotoModeOff;
|
|
443
|
+
self.depthDataDeliveryMode = self.photoOutput.depthDataDeliverySupported ? AVCamDepthDataDeliveryModeOn : AVCamDepthDataDeliveryModeOff;
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
self.inProgressPhotoCaptureDelegates = [NSMutableDictionary dictionary];
|
|
447
|
+
self.inProgressLivePhotoCapturesCount = 0;
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
NSLog( @"Could not add photo output to the session" );
|
|
451
|
+
self.setupResult = AVCamSetupResultSessionConfigurationFailed;
|
|
452
|
+
[self.session commitConfiguration];
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
self.backgroundRecordingID = UIBackgroundTaskInvalid;
|
|
457
|
+
|
|
458
|
+
[self.session commitConfiguration];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
- (IBAction)resumeInterruptedSession:(id)sender
|
|
462
|
+
{
|
|
463
|
+
dispatch_async( self.sessionQueue, ^{
|
|
464
|
+
/*
|
|
465
|
+
The session might fail to start running, e.g., if a phone or FaceTime call is still
|
|
466
|
+
using audio or video. A failure to start the session running will be communicated via
|
|
467
|
+
a session runtime error notification. To avoid repeatedly failing to start the session
|
|
468
|
+
running, we only try to restart the session running in the session runtime error handler
|
|
469
|
+
if we aren't trying to resume the session running.
|
|
470
|
+
*/
|
|
471
|
+
[self.session startRunning];
|
|
472
|
+
self.sessionRunning = self.session.isRunning;
|
|
473
|
+
if ( ! self.session.isRunning ) {
|
|
474
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
475
|
+
NSString *message = NSLocalizedString( @"Unable to resume", @"Alert message when unable to resume the session running" );
|
|
476
|
+
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"AVCam" message:message preferredStyle:UIAlertControllerStyleAlert];
|
|
477
|
+
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString( @"OK", @"Alert OK button" ) style:UIAlertActionStyleCancel handler:nil];
|
|
478
|
+
[alertController addAction:cancelAction];
|
|
479
|
+
[self presentViewController:alertController animated:YES completion:nil];
|
|
480
|
+
} );
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
484
|
+
//self.resumeButton.hidden = YES;
|
|
485
|
+
} );
|
|
486
|
+
}
|
|
487
|
+
} );
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
- (IBAction)toggleCaptureMode:(UISegmentedControl *)captureModeControl
|
|
491
|
+
{
|
|
492
|
+
if ( captureModeControl.selectedSegmentIndex == AVCamCaptureModePhoto ) {
|
|
493
|
+
//self.recordButton.enabled = NO;
|
|
494
|
+
|
|
495
|
+
dispatch_async( self.sessionQueue, ^{
|
|
496
|
+
/*
|
|
497
|
+
Remove the AVCaptureMovieFileOutput from the session because movie recording is
|
|
498
|
+
not supported with AVCaptureSessionPresetPhoto. Additionally, Live Photo
|
|
499
|
+
capture is not supported when an AVCaptureMovieFileOutput is connected to the session.
|
|
500
|
+
*/
|
|
501
|
+
[self.session beginConfiguration];
|
|
502
|
+
[self.session removeOutput:self.movieFileOutput];
|
|
503
|
+
self.session.sessionPreset = AVCaptureSessionPresetPhoto;
|
|
504
|
+
|
|
505
|
+
self.movieFileOutput = nil;
|
|
506
|
+
|
|
507
|
+
if ( self.photoOutput.livePhotoCaptureSupported ) {
|
|
508
|
+
self.photoOutput.livePhotoCaptureEnabled = YES;
|
|
509
|
+
|
|
510
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
511
|
+
//self.livePhotoModeButton.enabled = YES;
|
|
512
|
+
//self.livePhotoModeButton.hidden = NO;
|
|
513
|
+
} );
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if ( self.photoOutput.depthDataDeliverySupported ) {
|
|
517
|
+
self.photoOutput.depthDataDeliveryEnabled = YES;
|
|
518
|
+
|
|
519
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
520
|
+
//self.depthDataDeliveryButton.hidden = NO;
|
|
521
|
+
//self.depthDataDeliveryButton.enabled = YES;
|
|
522
|
+
} );
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
[self.session commitConfiguration];
|
|
526
|
+
} );
|
|
527
|
+
}
|
|
528
|
+
else if ( captureModeControl.selectedSegmentIndex == AVCamCaptureModeMovie ) {
|
|
529
|
+
//self.livePhotoModeButton.hidden = YES;
|
|
530
|
+
//self.depthDataDeliveryButton.hidden = YES;
|
|
531
|
+
|
|
532
|
+
dispatch_async( self.sessionQueue, ^{
|
|
533
|
+
AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
|
|
534
|
+
|
|
535
|
+
if ( [self.session canAddOutput:movieFileOutput] )
|
|
536
|
+
{
|
|
537
|
+
[self.session beginConfiguration];
|
|
538
|
+
[self.session addOutput:movieFileOutput];
|
|
539
|
+
self.session.sessionPreset = AVCaptureSessionPresetHigh;
|
|
540
|
+
AVCaptureConnection *connection = [movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
|
|
541
|
+
if ( connection.isVideoStabilizationSupported ) {
|
|
542
|
+
connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
|
|
543
|
+
}
|
|
544
|
+
[self.session commitConfiguration];
|
|
545
|
+
|
|
546
|
+
self.movieFileOutput = movieFileOutput;
|
|
547
|
+
|
|
548
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
549
|
+
//self.recordButton.enabled = YES;
|
|
550
|
+
} );
|
|
551
|
+
}
|
|
552
|
+
} );
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
#pragma mark Device Configuration
|
|
557
|
+
|
|
558
|
+
- (IBAction)changeCamera:(id)sender
|
|
559
|
+
{
|
|
560
|
+
self.cameraButton.enabled = NO;
|
|
561
|
+
//self.recordButton.enabled = NO;
|
|
562
|
+
self.photoButton.enabled = NO;
|
|
563
|
+
//self.livePhotoModeButton.enabled = NO;
|
|
564
|
+
self.captureModeControl.enabled = NO;
|
|
565
|
+
|
|
566
|
+
dispatch_async( self.sessionQueue, ^{
|
|
567
|
+
AVCaptureDevice *currentVideoDevice = self.videoDeviceInput.device;
|
|
568
|
+
AVCaptureDevicePosition currentPosition = currentVideoDevice.position;
|
|
569
|
+
|
|
570
|
+
AVCaptureDevicePosition preferredPosition;
|
|
571
|
+
AVCaptureDeviceType preferredDeviceType;
|
|
572
|
+
|
|
573
|
+
switch ( currentPosition )
|
|
574
|
+
{
|
|
575
|
+
case AVCaptureDevicePositionUnspecified:
|
|
576
|
+
case AVCaptureDevicePositionFront:
|
|
577
|
+
preferredPosition = AVCaptureDevicePositionBack;
|
|
578
|
+
preferredDeviceType = AVCaptureDeviceTypeBuiltInDualCamera;
|
|
579
|
+
break;
|
|
580
|
+
case AVCaptureDevicePositionBack:
|
|
581
|
+
preferredPosition = AVCaptureDevicePositionFront;
|
|
582
|
+
preferredDeviceType = AVCaptureDeviceTypeBuiltInWideAngleCamera;
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
NSArray<AVCaptureDevice *> *devices = self.videoDeviceDiscoverySession.devices;
|
|
587
|
+
AVCaptureDevice *newVideoDevice = nil;
|
|
588
|
+
|
|
589
|
+
// First, look for a device with both the preferred position and device type.
|
|
590
|
+
for ( AVCaptureDevice *device in devices ) {
|
|
591
|
+
if ( device.position == preferredPosition && [device.deviceType isEqualToString:preferredDeviceType] ) {
|
|
592
|
+
newVideoDevice = device;
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Otherwise, look for a device with only the preferred position.
|
|
598
|
+
if ( ! newVideoDevice ) {
|
|
599
|
+
for ( AVCaptureDevice *device in devices ) {
|
|
600
|
+
if ( device.position == preferredPosition ) {
|
|
601
|
+
newVideoDevice = device;
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if ( newVideoDevice ) {
|
|
608
|
+
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:newVideoDevice error:NULL];
|
|
609
|
+
|
|
610
|
+
[self.session beginConfiguration];
|
|
611
|
+
|
|
612
|
+
// Remove the existing device input first, since using the front and back camera simultaneously is not supported.
|
|
613
|
+
[self.session removeInput:self.videoDeviceInput];
|
|
614
|
+
|
|
615
|
+
if ( [self.session canAddInput:videoDeviceInput] ) {
|
|
616
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:currentVideoDevice];
|
|
617
|
+
|
|
618
|
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:newVideoDevice];
|
|
619
|
+
|
|
620
|
+
[self.session addInput:videoDeviceInput];
|
|
621
|
+
self.videoDeviceInput = videoDeviceInput;
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
[self.session addInput:self.videoDeviceInput];
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
AVCaptureConnection *movieFileOutputConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
|
|
628
|
+
if ( movieFileOutputConnection.isVideoStabilizationSupported ) {
|
|
629
|
+
movieFileOutputConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/*
|
|
633
|
+
Set Live Photo capture and depth data delivery if it is supported. When changing cameras, the
|
|
634
|
+
`livePhotoCaptureEnabled` and `depthDataDeliveryEnabled` properties of the AVCapturePhotoOutput gets set to NO when
|
|
635
|
+
a video device is disconnected from the session. After the new video device is
|
|
636
|
+
added to the session, re-enable Live Photo capture and depth data delivery if they are supported.
|
|
637
|
+
*/
|
|
638
|
+
self.photoOutput.livePhotoCaptureEnabled = self.photoOutput.livePhotoCaptureSupported;
|
|
639
|
+
self.photoOutput.depthDataDeliveryEnabled = self.photoOutput.depthDataDeliverySupported;
|
|
640
|
+
|
|
641
|
+
[self.session commitConfiguration];
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
645
|
+
self.cameraButton.enabled = YES;
|
|
646
|
+
//self.recordButton.enabled = self.captureModeControl.selectedSegmentIndex == AVCamCaptureModeMovie;
|
|
647
|
+
self.photoButton.enabled = YES;
|
|
648
|
+
// self.livePhotoModeButton.enabled = YES;
|
|
649
|
+
// self.captureModeControl.enabled = YES;
|
|
650
|
+
// self.depthDataDeliveryButton.enabled = self.photoOutput.isDepthDataDeliveryEnabled;
|
|
651
|
+
// self.depthDataDeliveryButton.hidden = !self.photoOutput.depthDataDeliverySupported;
|
|
652
|
+
} );
|
|
653
|
+
} );
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
- (IBAction)focusAndExposeTap:(UIGestureRecognizer *)gestureRecognizer
|
|
657
|
+
{
|
|
658
|
+
CGPoint devicePoint = [self.previewView.videoPreviewLayer captureDevicePointOfInterestForPoint:[gestureRecognizer locationInView:gestureRecognizer.view]];
|
|
659
|
+
[self focusWithMode:AVCaptureFocusModeAutoFocus exposeWithMode:AVCaptureExposureModeAutoExpose atDevicePoint:devicePoint monitorSubjectAreaChange:YES];
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
- (void)focusWithMode:(AVCaptureFocusMode)focusMode exposeWithMode:(AVCaptureExposureMode)exposureMode atDevicePoint:(CGPoint)point monitorSubjectAreaChange:(BOOL)monitorSubjectAreaChange
|
|
663
|
+
{
|
|
664
|
+
dispatch_async( self.sessionQueue, ^{
|
|
665
|
+
AVCaptureDevice *device = self.videoDeviceInput.device;
|
|
666
|
+
NSError *error = nil;
|
|
667
|
+
if ( [device lockForConfiguration:&error] ) {
|
|
668
|
+
/*
|
|
669
|
+
Setting (focus/exposure)PointOfInterest alone does not initiate a (focus/exposure) operation.
|
|
670
|
+
Call set(Focus/Exposure)Mode() to apply the new point of interest.
|
|
671
|
+
*/
|
|
672
|
+
if ( device.isFocusPointOfInterestSupported && [device isFocusModeSupported:focusMode] ) {
|
|
673
|
+
device.focusPointOfInterest = point;
|
|
674
|
+
device.focusMode = focusMode;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if ( device.isExposurePointOfInterestSupported && [device isExposureModeSupported:exposureMode] ) {
|
|
678
|
+
device.exposurePointOfInterest = point;
|
|
679
|
+
device.exposureMode = exposureMode;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
device.subjectAreaChangeMonitoringEnabled = monitorSubjectAreaChange;
|
|
683
|
+
[device unlockForConfiguration];
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
NSLog( @"Could not lock device for configuration: %@", error );
|
|
687
|
+
}
|
|
688
|
+
} );
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
#pragma mark Capturing Photos
|
|
692
|
+
|
|
693
|
+
- (IBAction)backtowebview:(id)sender {
|
|
694
|
+
[mCamera dismissCam];
|
|
695
|
+
}
|
|
696
|
+
- (IBAction)capturePhoto:(id)sender
|
|
697
|
+
{
|
|
698
|
+
/*
|
|
699
|
+
Retrieve the video preview layer's video orientation on the main queue before
|
|
700
|
+
entering the session queue. We do this to ensure UI elements are accessed on
|
|
701
|
+
the main thread and session configuration is done on the session queue.
|
|
702
|
+
*/
|
|
703
|
+
[_photoButton setHidden:TRUE];
|
|
704
|
+
[_progress setHidden:FALSE];
|
|
705
|
+
[_progress startAnimating];
|
|
706
|
+
dispatch_async( self.sessionQueue, ^{
|
|
707
|
+
// Update the photo output's connection to match the video orientation of the video preview layer.
|
|
708
|
+
AVCaptureConnection *photoOutputConnection = [self.photoOutput connectionWithMediaType:AVMediaTypeVideo];
|
|
709
|
+
photoOutputConnection.videoOrientation = curOr;
|
|
710
|
+
|
|
711
|
+
AVCapturePhotoSettings *photoSettings;
|
|
712
|
+
// Capture HEIF photo when supported, with flash set to auto and high resolution photo enabled.
|
|
713
|
+
if ( [self.photoOutput.availablePhotoCodecTypes containsObject:AVVideoCodecTypeHEVC] ) {
|
|
714
|
+
photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:@{ AVVideoCodecKey : AVVideoCodecTypeHEVC }];
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
photoSettings = [AVCapturePhotoSettings photoSettings];
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if ( self.videoDeviceInput.device.isFlashAvailable ) {
|
|
721
|
+
photoSettings.flashMode = AVCaptureFlashModeAuto;
|
|
722
|
+
}
|
|
723
|
+
photoSettings.highResolutionPhotoEnabled = YES;
|
|
724
|
+
if ( photoSettings.availablePreviewPhotoPixelFormatTypes.count > 0 ) {
|
|
725
|
+
photoSettings.previewPhotoFormat = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : photoSettings.availablePreviewPhotoPixelFormatTypes.firstObject };
|
|
726
|
+
}
|
|
727
|
+
if ( self.livePhotoMode == AVCamLivePhotoModeOn && self.photoOutput.livePhotoCaptureSupported ) { // Live Photo capture is not supported in movie mode.
|
|
728
|
+
NSString *livePhotoMovieFileName = [NSUUID UUID].UUIDString;
|
|
729
|
+
NSString *livePhotoMovieFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[livePhotoMovieFileName stringByAppendingPathExtension:@"mov"]];
|
|
730
|
+
photoSettings.livePhotoMovieFileURL = [NSURL fileURLWithPath:livePhotoMovieFilePath];
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if ( self.depthDataDeliveryMode == AVCamDepthDataDeliveryModeOn && self.photoOutput.isDepthDataDeliverySupported ) {
|
|
734
|
+
photoSettings.depthDataDeliveryEnabled = YES;
|
|
735
|
+
} else {
|
|
736
|
+
photoSettings.depthDataDeliveryEnabled = NO;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// Use a separate object for the photo capture delegate to isolate each capture life cycle.
|
|
740
|
+
AVCamPhotoCaptureDelegate *photoCaptureDelegate = [[AVCamPhotoCaptureDelegate alloc] initWithRequestedPhotoSettings:photoSettings willCapturePhotoAnimation:^{
|
|
741
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
742
|
+
self.previewView.videoPreviewLayer.opacity = 0.0;
|
|
743
|
+
[UIView animateWithDuration:0.25 animations:^{
|
|
744
|
+
self.previewView.videoPreviewLayer.opacity = 1.0;
|
|
745
|
+
}];
|
|
746
|
+
} );
|
|
747
|
+
} livePhotoCaptureHandler:^( BOOL capturing ) {
|
|
748
|
+
/*
|
|
749
|
+
Because Live Photo captures can overlap, we need to keep track of the
|
|
750
|
+
number of in progress Live Photo captures to ensure that the
|
|
751
|
+
Live Photo label stays visible during these captures.
|
|
752
|
+
*/
|
|
753
|
+
dispatch_async( self.sessionQueue, ^{
|
|
754
|
+
if ( capturing ) {
|
|
755
|
+
self.inProgressLivePhotoCapturesCount++;
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
self.inProgressLivePhotoCapturesCount--;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
NSInteger inProgressLivePhotoCapturesCount = self.inProgressLivePhotoCapturesCount;
|
|
762
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
763
|
+
if ( inProgressLivePhotoCapturesCount > 0 ) {
|
|
764
|
+
//self.capturingLivePhotoLabel.hidden = NO;
|
|
765
|
+
}
|
|
766
|
+
else if ( inProgressLivePhotoCapturesCount == 0 ) {
|
|
767
|
+
//self.capturingLivePhotoLabel.hidden = YES;
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
NSLog( @"Error: In progress live photo capture count is less than 0" );
|
|
771
|
+
}
|
|
772
|
+
} );
|
|
773
|
+
} );
|
|
774
|
+
} completionHandler:^( AVCamPhotoCaptureDelegate *photoCaptureDelegate ) {
|
|
775
|
+
// When the capture is complete, remove a reference to the photo capture delegate so it can be deallocated.
|
|
776
|
+
dispatch_async( self.sessionQueue, ^{
|
|
777
|
+
self.inProgressPhotoCaptureDelegates[@(photoCaptureDelegate.requestedPhotoSettings.uniqueID)] = nil;
|
|
778
|
+
} );
|
|
779
|
+
}];
|
|
780
|
+
|
|
781
|
+
/*
|
|
782
|
+
The Photo Output keeps a weak reference to the photo capture delegate so
|
|
783
|
+
we store it in an array to maintain a strong reference to this object
|
|
784
|
+
until the capture is completed.
|
|
785
|
+
*/
|
|
786
|
+
self.inProgressPhotoCaptureDelegates[@(photoCaptureDelegate.requestedPhotoSettings.uniqueID)] = photoCaptureDelegate;
|
|
787
|
+
[photoCaptureDelegate setUICam:self];
|
|
788
|
+
[self.photoOutput capturePhotoWithSettings:photoSettings delegate:photoCaptureDelegate];
|
|
789
|
+
} );
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
- (IBAction)toggleLivePhotoMode:(UIButton *)livePhotoModeButton
|
|
793
|
+
{
|
|
794
|
+
dispatch_async( self.sessionQueue, ^{
|
|
795
|
+
self.livePhotoMode = ( self.livePhotoMode == AVCamLivePhotoModeOn ) ? AVCamLivePhotoModeOff : AVCamLivePhotoModeOn;
|
|
796
|
+
AVCamLivePhotoMode livePhotoMode = self.livePhotoMode;
|
|
797
|
+
|
|
798
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
799
|
+
if ( livePhotoMode == AVCamLivePhotoModeOn ) {
|
|
800
|
+
//[self.livePhotoModeButton setTitle:NSLocalizedString( @"Live Photo Mode: On", @"Live photo mode button on title" ) forState:UIControlStateNormal];
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
//[self.livePhotoModeButton setTitle:NSLocalizedString( @"Live Photo Mode: Off", @"Live photo mode button off title" ) forState:UIControlStateNormal];
|
|
804
|
+
}
|
|
805
|
+
} );
|
|
806
|
+
} );
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
- (IBAction)toggleDepthDataDeliveryMode:(UIButton *)depthDataDeliveryButton
|
|
810
|
+
{
|
|
811
|
+
dispatch_async( self.sessionQueue, ^{
|
|
812
|
+
self.depthDataDeliveryMode = ( self.depthDataDeliveryMode == AVCamDepthDataDeliveryModeOn ) ? AVCamDepthDataDeliveryModeOff : AVCamDepthDataDeliveryModeOn;
|
|
813
|
+
AVCamDepthDataDeliveryMode depthDataDeliveryMode = self.depthDataDeliveryMode;
|
|
814
|
+
|
|
815
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
816
|
+
if ( depthDataDeliveryMode == AVCamDepthDataDeliveryModeOn ) {
|
|
817
|
+
//[self.depthDataDeliveryButton setTitle:NSLocalizedString( @"Depth Data Delivery: On", @"Depth Data mode button on title" ) forState:UIControlStateNormal];
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
//[self.depthDataDeliveryButton setTitle:NSLocalizedString( @"Depth Data Delivery: Off", @"Depth Data mode button off title" ) forState:UIControlStateNormal];
|
|
821
|
+
}
|
|
822
|
+
} );
|
|
823
|
+
} );
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
#pragma mark Recording Movies
|
|
827
|
+
|
|
828
|
+
- (IBAction)toggleMovieRecording:(id)sender
|
|
829
|
+
{
|
|
830
|
+
/*
|
|
831
|
+
Disable the Camera button until recording finishes, and disable
|
|
832
|
+
the Record button until recording starts or finishes.
|
|
833
|
+
|
|
834
|
+
See the AVCaptureFileOutputRecordingDelegate methods.
|
|
835
|
+
*/
|
|
836
|
+
self.cameraButton.enabled = NO;
|
|
837
|
+
//self.recordButton.enabled = NO;
|
|
838
|
+
self.captureModeControl.enabled = NO;
|
|
839
|
+
|
|
840
|
+
/*
|
|
841
|
+
Retrieve the video preview layer's video orientation on the main queue
|
|
842
|
+
before entering the session queue. We do this to ensure UI elements are
|
|
843
|
+
accessed on the main thread and session configuration is done on the session queue.
|
|
844
|
+
*/
|
|
845
|
+
AVCaptureVideoOrientation videoPreviewLayerVideoOrientation = self.previewView.videoPreviewLayer.connection.videoOrientation;
|
|
846
|
+
|
|
847
|
+
dispatch_async( self.sessionQueue, ^{
|
|
848
|
+
if ( ! self.movieFileOutput.isRecording ) {
|
|
849
|
+
if ( [UIDevice currentDevice].isMultitaskingSupported ) {
|
|
850
|
+
/*
|
|
851
|
+
Setup background task.
|
|
852
|
+
This is needed because the -[captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:]
|
|
853
|
+
callback is not received until AVCam returns to the foreground unless you request background execution time.
|
|
854
|
+
This also ensures that there will be time to write the file to the photo library when AVCam is backgrounded.
|
|
855
|
+
To conclude this background execution, -[endBackgroundTask:] is called in
|
|
856
|
+
-[captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:] after the recorded file has been saved.
|
|
857
|
+
*/
|
|
858
|
+
self.backgroundRecordingID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Update the orientation on the movie file output video connection before starting recording.
|
|
862
|
+
AVCaptureConnection *movieFileOutputConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
|
|
863
|
+
movieFileOutputConnection.videoOrientation = videoPreviewLayerVideoOrientation;
|
|
864
|
+
|
|
865
|
+
// Use HEVC codec if supported
|
|
866
|
+
if ( [self.movieFileOutput.availableVideoCodecTypes containsObject:AVVideoCodecTypeHEVC] ) {
|
|
867
|
+
[self.movieFileOutput setOutputSettings:@{ AVVideoCodecKey : AVVideoCodecTypeHEVC } forConnection:movieFileOutputConnection];
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// Start recording to a temporary file.
|
|
871
|
+
NSString *outputFileName = [NSUUID UUID].UUIDString;
|
|
872
|
+
NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[outputFileName stringByAppendingPathExtension:@"mov"]];
|
|
873
|
+
[self.movieFileOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputFilePath] recordingDelegate:self];
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
[self.movieFileOutput stopRecording];
|
|
877
|
+
}
|
|
878
|
+
} );
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections
|
|
882
|
+
{
|
|
883
|
+
// Enable the Record button to let the user stop the recording.
|
|
884
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
885
|
+
//self.recordButton.enabled = YES;
|
|
886
|
+
//[self.recordButton setTitle:NSLocalizedString( @"Stop", @"Recording button stop title" ) forState:UIControlStateNormal];
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
|
|
891
|
+
{
|
|
892
|
+
/*
|
|
893
|
+
Note that currentBackgroundRecordingID is used to end the background task
|
|
894
|
+
associated with this recording. This allows a new recording to be started,
|
|
895
|
+
associated with a new UIBackgroundTaskIdentifier, once the movie file output's
|
|
896
|
+
`recording` property is back to NO — which happens sometime after this method
|
|
897
|
+
returns.
|
|
898
|
+
|
|
899
|
+
Note: Since we use a unique file path for each recording, a new recording will
|
|
900
|
+
not overwrite a recording currently being saved.
|
|
901
|
+
*/
|
|
902
|
+
UIBackgroundTaskIdentifier currentBackgroundRecordingID = self.backgroundRecordingID;
|
|
903
|
+
self.backgroundRecordingID = UIBackgroundTaskInvalid;
|
|
904
|
+
|
|
905
|
+
dispatch_block_t cleanUp = ^{
|
|
906
|
+
if ( [[NSFileManager defaultManager] fileExistsAtPath:outputFileURL.path] ) {
|
|
907
|
+
[[NSFileManager defaultManager] removeItemAtPath:outputFileURL.path error:NULL];
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if ( currentBackgroundRecordingID != UIBackgroundTaskInvalid ) {
|
|
911
|
+
[[UIApplication sharedApplication] endBackgroundTask:currentBackgroundRecordingID];
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
BOOL success = YES;
|
|
916
|
+
|
|
917
|
+
if ( error ) {
|
|
918
|
+
NSLog( @"Movie file finishing error: %@", error );
|
|
919
|
+
success = [error.userInfo[AVErrorRecordingSuccessfullyFinishedKey] boolValue];
|
|
920
|
+
}
|
|
921
|
+
if ( success ) {
|
|
922
|
+
// Check authorization status.
|
|
923
|
+
[PHPhotoLibrary requestAuthorization:^( PHAuthorizationStatus status ) {
|
|
924
|
+
if ( status == PHAuthorizationStatusAuthorized ) {
|
|
925
|
+
// Save the movie file to the photo library and cleanup.
|
|
926
|
+
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
|
927
|
+
PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
|
|
928
|
+
options.shouldMoveFile = YES;
|
|
929
|
+
PHAssetCreationRequest *creationRequest = [PHAssetCreationRequest creationRequestForAsset];
|
|
930
|
+
[creationRequest addResourceWithType:PHAssetResourceTypeVideo fileURL:outputFileURL options:options];
|
|
931
|
+
} completionHandler:^( BOOL success, NSError *error ) {
|
|
932
|
+
if ( ! success ) {
|
|
933
|
+
NSLog( @"Could not save movie to photo library: %@", error );
|
|
934
|
+
}
|
|
935
|
+
cleanUp();
|
|
936
|
+
}];
|
|
937
|
+
}
|
|
938
|
+
else {
|
|
939
|
+
cleanUp();
|
|
940
|
+
}
|
|
941
|
+
}];
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
cleanUp();
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Enable the Camera and Record buttons to let the user switch camera and start another recording.
|
|
948
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
949
|
+
// Only enable the ability to change camera if the device has more than one camera.
|
|
950
|
+
self.cameraButton.enabled = ( self.videoDeviceDiscoverySession.uniqueDevicePositionsCount > 1 );
|
|
951
|
+
//self.recordButton.enabled = YES;
|
|
952
|
+
self.captureModeControl.enabled = YES;
|
|
953
|
+
//[self.recordButton setTitle:NSLocalizedString( @"Record", @"Recording button record title" ) forState:UIControlStateNormal];
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
#pragma mark KVO and Notifications
|
|
958
|
+
|
|
959
|
+
- (void)addObservers
|
|
960
|
+
{
|
|
961
|
+
[self.session addObserver:self forKeyPath:@"running" options:NSKeyValueObservingOptionNew context:SessionRunningContext];
|
|
962
|
+
|
|
963
|
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:self.videoDeviceInput.device];
|
|
964
|
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:self.session];
|
|
965
|
+
|
|
966
|
+
/*
|
|
967
|
+
A session can only run when the app is full screen. It will be interrupted
|
|
968
|
+
in a multi-app layout, introduced in iOS 9, see also the documentation of
|
|
969
|
+
AVCaptureSessionInterruptionReason. Add observers to handle these session
|
|
970
|
+
interruptions and show a preview is paused message. See the documentation
|
|
971
|
+
of AVCaptureSessionWasInterruptedNotification for other interruption reasons.
|
|
972
|
+
*/
|
|
973
|
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:self.session];
|
|
974
|
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionInterruptionEnded:) name:AVCaptureSessionInterruptionEndedNotification object:self.session];
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
- (void)removeObservers
|
|
978
|
+
{
|
|
979
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
980
|
+
|
|
981
|
+
[self.session removeObserver:self forKeyPath:@"running" context:SessionRunningContext];
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
|
985
|
+
{
|
|
986
|
+
if ( context == SessionRunningContext ) {
|
|
987
|
+
BOOL isSessionRunning = [change[NSKeyValueChangeNewKey] boolValue];
|
|
988
|
+
|
|
989
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
990
|
+
// Only enable the ability to change camera if the device has more than one camera.
|
|
991
|
+
self.cameraButton.enabled = isSessionRunning && ( self.videoDeviceDiscoverySession.uniqueDevicePositionsCount > 1 );
|
|
992
|
+
//self.recordButton.enabled = isSessionRunning && ( self.captureModeControl.selectedSegmentIndex == AVCamCaptureModeMovie );
|
|
993
|
+
self.photoButton.enabled = isSessionRunning;
|
|
994
|
+
self.captureModeControl.enabled = isSessionRunning;
|
|
995
|
+
//self.livePhotoModeButton.enabled = isSessionRunning && livePhotoCaptureEnabled;
|
|
996
|
+
//self.livePhotoModeButton.hidden = ! ( isSessionRunning && livePhotoCaptureSupported );
|
|
997
|
+
//self.depthDataDeliveryButton.enabled = isSessionRunning && depthDataDeliveryEnabled ;
|
|
998
|
+
//self.depthDataDeliveryButton.hidden = ! ( isSessionRunning && depthDataDeliverySupported );
|
|
999
|
+
} );
|
|
1000
|
+
}
|
|
1001
|
+
else {
|
|
1002
|
+
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
- (void)subjectAreaDidChange:(NSNotification *)notification
|
|
1007
|
+
{
|
|
1008
|
+
CGPoint devicePoint = CGPointMake( 0.5, 0.5 );
|
|
1009
|
+
[self focusWithMode:AVCaptureFocusModeContinuousAutoFocus exposeWithMode:AVCaptureExposureModeContinuousAutoExposure atDevicePoint:devicePoint monitorSubjectAreaChange:NO];
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
- (void)sessionRuntimeError:(NSNotification *)notification
|
|
1013
|
+
{
|
|
1014
|
+
NSError *error = notification.userInfo[AVCaptureSessionErrorKey];
|
|
1015
|
+
NSLog( @"Capture session runtime error: %@", error );
|
|
1016
|
+
|
|
1017
|
+
/*
|
|
1018
|
+
Automatically try to restart the session running if media services were
|
|
1019
|
+
reset and the last start running succeeded. Otherwise, enable the user
|
|
1020
|
+
to try to resume the session running.
|
|
1021
|
+
*/
|
|
1022
|
+
if ( error.code == AVErrorMediaServicesWereReset ) {
|
|
1023
|
+
dispatch_async( self.sessionQueue, ^{
|
|
1024
|
+
if ( self.isSessionRunning ) {
|
|
1025
|
+
[self.session startRunning];
|
|
1026
|
+
self.sessionRunning = self.session.isRunning;
|
|
1027
|
+
}
|
|
1028
|
+
else {
|
|
1029
|
+
dispatch_async( dispatch_get_main_queue(), ^{
|
|
1030
|
+
//self.resumeButton.hidden = NO;
|
|
1031
|
+
} );
|
|
1032
|
+
}
|
|
1033
|
+
} );
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
//self.resumeButton.hidden = NO;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
- (void)sessionWasInterrupted:(NSNotification *)notification
|
|
1041
|
+
{
|
|
1042
|
+
/*
|
|
1043
|
+
In some scenarios we want to enable the user to resume the session running.
|
|
1044
|
+
For example, if music playback is initiated via control center while
|
|
1045
|
+
using AVCam, then the user can let AVCam resume
|
|
1046
|
+
the session running, which will stop music playback. Note that stopping
|
|
1047
|
+
music playback in control center will not automatically resume the session
|
|
1048
|
+
running. Also note that it is not always possible to resume, see -[resumeInterruptedSession:].
|
|
1049
|
+
*/
|
|
1050
|
+
BOOL showResumeButton = NO;
|
|
1051
|
+
|
|
1052
|
+
AVCaptureSessionInterruptionReason reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue];
|
|
1053
|
+
NSLog( @"Capture session was interrupted with reason %ld", (long)reason );
|
|
1054
|
+
|
|
1055
|
+
if ( reason == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient ||
|
|
1056
|
+
reason == AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient ) {
|
|
1057
|
+
showResumeButton = YES;
|
|
1058
|
+
}
|
|
1059
|
+
else if ( reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps ) {
|
|
1060
|
+
// Simply fade-in a label to inform the user that the camera is unavailable.
|
|
1061
|
+
//self.cameraUnavailableLabel.alpha = 0.0;
|
|
1062
|
+
//self.cameraUnavailableLabel.hidden = NO;
|
|
1063
|
+
[UIView animateWithDuration:0.25 animations:^{
|
|
1064
|
+
//self.cameraUnavailableLabel.alpha = 1.0;
|
|
1065
|
+
}];
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
if ( showResumeButton ) {
|
|
1069
|
+
// Simply fade-in a button to enable the user to try to resume the session running.
|
|
1070
|
+
//self.resumeButton.alpha = 0.0;
|
|
1071
|
+
//self.resumeButton.hidden = NO;
|
|
1072
|
+
[UIView animateWithDuration:0.25 animations:^{
|
|
1073
|
+
//self.resumeButton.alpha = 1.0;
|
|
1074
|
+
}];
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
- (void)sessionInterruptionEnded:(NSNotification *)notification
|
|
1079
|
+
{
|
|
1080
|
+
NSLog( @"Capture session interruption ended" );
|
|
1081
|
+
|
|
1082
|
+
// if ( ! self.resumeButton.hidden ) {
|
|
1083
|
+
// [UIView animateWithDuration:0.25 animations:^{
|
|
1084
|
+
// self.resumeButton.alpha = 0.0;
|
|
1085
|
+
// } completion:^( BOOL finished ) {
|
|
1086
|
+
// self.resumeButton.hidden = YES;
|
|
1087
|
+
// }];
|
|
1088
|
+
// }
|
|
1089
|
+
// if ( ! self.cameraUnavailableLabel.hidden ) {
|
|
1090
|
+
// [UIView animateWithDuration:0.25 animations:^{
|
|
1091
|
+
// self.cameraUnavailableLabel.alpha = 0.0;
|
|
1092
|
+
// } completion:^( BOOL finished ) {
|
|
1093
|
+
// self.cameraUnavailableLabel.hidden = YES;
|
|
1094
|
+
// }];
|
|
1095
|
+
// }
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
@end
|