AVClub 0.1.0

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.
@@ -0,0 +1,155 @@
1
+ /*
2
+ File: AVCamRecorder.m
3
+ Abstract: An interface to manage the use of AVCaptureMovieFileOutput for recording videos. Its responsibilities include
4
+ configuring the AVCaptureMovieFileOutput, adding it to the desired capture session, and starting and stopping video recordings.
5
+ Version: 1.2
6
+
7
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
8
+ Inc. ("Apple") in consideration of your agreement to the following
9
+ terms, and your use, installation, modification or redistribution of
10
+ this Apple software constitutes acceptance of these terms. If you do
11
+ not agree with these terms, please do not use, install, modify or
12
+ redistribute this Apple software.
13
+
14
+ In consideration of your agreement to abide by the following terms, and
15
+ subject to these terms, Apple grants you a personal, non-exclusive
16
+ license, under Apple's copyrights in this original Apple software (the
17
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
18
+ Software, with or without modifications, in source and/or binary forms;
19
+ provided that if you redistribute the Apple Software in its entirety and
20
+ without modifications, you must retain this notice and the following
21
+ text and disclaimers in all such redistributions of the Apple Software.
22
+ Neither the name, trademarks, service marks or logos of Apple Inc. may
23
+ be used to endorse or promote products derived from the Apple Software
24
+ without specific prior written permission from Apple. Except as
25
+ expressly stated in this notice, no other rights or licenses, express or
26
+ implied, are granted by Apple herein, including but not limited to any
27
+ patent rights that may be infringed by your derivative works or by other
28
+ works in which the Apple Software may be incorporated.
29
+
30
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
31
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
32
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
33
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
34
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
35
+
36
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
37
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
40
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
41
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
42
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
43
+ POSSIBILITY OF SUCH DAMAGE.
44
+
45
+ Copyright (C) 2011 Apple Inc. All Rights Reserved.
46
+
47
+ */
48
+
49
+ #import "AVCamRecorder.h"
50
+ #import "AVCamUtilities.h"
51
+
52
+ @interface AVCamRecorder (FileOutputDelegate) <AVCaptureFileOutputRecordingDelegate>
53
+ @end
54
+
55
+ @implementation AVCamRecorder
56
+
57
+ @synthesize session;
58
+ @synthesize movieFileOutput;
59
+ @synthesize outputFileURL;
60
+ @synthesize delegate;
61
+
62
+ - (id) initWithSession:(AVCaptureSession *)aSession outputFileURL:(NSURL *)anOutputFileURL
63
+ {
64
+ self = [super init];
65
+ if (self != nil) {
66
+ AVCaptureMovieFileOutput *aMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
67
+ if ([aSession canAddOutput:aMovieFileOutput])
68
+ [aSession addOutput:aMovieFileOutput];
69
+ [self setMovieFileOutput:aMovieFileOutput];
70
+
71
+ [self setSession:aSession];
72
+ [self setOutputFileURL:anOutputFileURL];
73
+ }
74
+
75
+ return self;
76
+ }
77
+
78
+ - (void) dealloc
79
+ {
80
+ [[self session] removeOutput:[self movieFileOutput]];
81
+ }
82
+
83
+ -(BOOL)isOrientationSupported
84
+ {
85
+ AVCaptureConnection *videoConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self movieFileOutput] connections]];
86
+ return [videoConnection isVideoOrientationSupported];
87
+ }
88
+
89
+ -(void)setOrientation:(AVCaptureVideoOrientation)orientation
90
+ {
91
+ AVCaptureConnection *videoConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self movieFileOutput] connections]];
92
+ return [videoConnection setVideoOrientation:orientation];
93
+ }
94
+
95
+ -(BOOL)isMirrored
96
+ {
97
+ AVCaptureConnection *videoConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self movieFileOutput] connections]];
98
+ return [videoConnection isVideoMirrored];
99
+ }
100
+
101
+ -(BOOL)recordsVideo
102
+ {
103
+ AVCaptureConnection *videoConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self movieFileOutput] connections]];
104
+ return [videoConnection isActive];
105
+ }
106
+
107
+ -(BOOL)recordsAudio
108
+ {
109
+ AVCaptureConnection *audioConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeAudio fromConnections:[[self movieFileOutput] connections]];
110
+ return [audioConnection isActive];
111
+ }
112
+
113
+ -(BOOL)isRecording
114
+ {
115
+ return [[self movieFileOutput] isRecording];
116
+ }
117
+
118
+ -(void)startRecordingWithOrientation:(AVCaptureVideoOrientation)videoOrientation;
119
+ {
120
+ AVCaptureConnection *videoConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self movieFileOutput] connections]];
121
+ if ([videoConnection isVideoOrientationSupported])
122
+ [videoConnection setVideoOrientation:videoOrientation];
123
+
124
+ [[self movieFileOutput] startRecordingToOutputFileURL:[self outputFileURL] recordingDelegate:self];
125
+ }
126
+
127
+ -(void)stopRecording
128
+ {
129
+ [[self movieFileOutput] stopRecording];
130
+ }
131
+
132
+ @end
133
+
134
+ @implementation AVCamRecorder (FileOutputDelegate)
135
+
136
+ - (void) captureOutput:(AVCaptureFileOutput *)captureOutput
137
+ didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
138
+ fromConnections:(NSArray *)connections
139
+ {
140
+ if ([[self delegate] respondsToSelector:@selector(recorderRecordingDidBegin:)]) {
141
+ [[self delegate] recorderRecordingDidBegin:self];
142
+ }
143
+ }
144
+
145
+ - (void) captureOutput:(AVCaptureFileOutput *)captureOutput
146
+ didFinishRecordingToOutputFileAtURL:(NSURL *)anOutputFileURL
147
+ fromConnections:(NSArray *)connections
148
+ error:(NSError *)error
149
+ {
150
+ if ([[self delegate] respondsToSelector:@selector(recorder:recordingDidFinishToOutputFileURL:error:)]) {
151
+ [[self delegate] recorder:self recordingDidFinishToOutputFileURL:anOutputFileURL error:error];
152
+ }
153
+ }
154
+
155
+ @end
@@ -0,0 +1,61 @@
1
+ /*
2
+ File: AVCamUtilities.h
3
+ Abstract: A utility class containing a method to find an AVCaptureConnection of a particular media type from an array of AVCaptureConnections.
4
+ Version: 1.2
5
+
6
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
7
+ Inc. ("Apple") in consideration of your agreement to the following
8
+ terms, and your use, installation, modification or redistribution of
9
+ this Apple software constitutes acceptance of these terms. If you do
10
+ not agree with these terms, please do not use, install, modify or
11
+ redistribute this Apple software.
12
+
13
+ In consideration of your agreement to abide by the following terms, and
14
+ subject to these terms, Apple grants you a personal, non-exclusive
15
+ license, under Apple's copyrights in this original Apple software (the
16
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
17
+ Software, with or without modifications, in source and/or binary forms;
18
+ provided that if you redistribute the Apple Software in its entirety and
19
+ without modifications, you must retain this notice and the following
20
+ text and disclaimers in all such redistributions of the Apple Software.
21
+ Neither the name, trademarks, service marks or logos of Apple Inc. may
22
+ be used to endorse or promote products derived from the Apple Software
23
+ without specific prior written permission from Apple. Except as
24
+ expressly stated in this notice, no other rights or licenses, express or
25
+ implied, are granted by Apple herein, including but not limited to any
26
+ patent rights that may be infringed by your derivative works or by other
27
+ works in which the Apple Software may be incorporated.
28
+
29
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
30
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34
+
35
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42
+ POSSIBILITY OF SUCH DAMAGE.
43
+
44
+ Copyright (C) 2011 Apple Inc. All Rights Reserved.
45
+
46
+ */
47
+
48
+ #import <Foundation/Foundation.h>
49
+ #import <UIKit/UIKit.h>
50
+ #import <AVFoundation/AVFoundation.h>
51
+
52
+
53
+ @class AVCaptureConnection;
54
+
55
+ @interface AVCamUtilities : NSObject {
56
+
57
+ }
58
+
59
+ + (AVCaptureConnection *)connectionWithMediaType:(NSString *)mediaType fromConnections:(NSArray *)connections;
60
+
61
+ @end
@@ -0,0 +1,65 @@
1
+ /*
2
+ File: AVCamUtilities.m
3
+ Abstract: A utility class containing a method to find an AVCaptureConnection of a particular media type from an array of AVCaptureConnections.
4
+ Version: 1.2
5
+
6
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
7
+ Inc. ("Apple") in consideration of your agreement to the following
8
+ terms, and your use, installation, modification or redistribution of
9
+ this Apple software constitutes acceptance of these terms. If you do
10
+ not agree with these terms, please do not use, install, modify or
11
+ redistribute this Apple software.
12
+
13
+ In consideration of your agreement to abide by the following terms, and
14
+ subject to these terms, Apple grants you a personal, non-exclusive
15
+ license, under Apple's copyrights in this original Apple software (the
16
+ "Apple Software"), to use, reproduce, modify and redistribute the Apple
17
+ Software, with or without modifications, in source and/or binary forms;
18
+ provided that if you redistribute the Apple Software in its entirety and
19
+ without modifications, you must retain this notice and the following
20
+ text and disclaimers in all such redistributions of the Apple Software.
21
+ Neither the name, trademarks, service marks or logos of Apple Inc. may
22
+ be used to endorse or promote products derived from the Apple Software
23
+ without specific prior written permission from Apple. Except as
24
+ expressly stated in this notice, no other rights or licenses, express or
25
+ implied, are granted by Apple herein, including but not limited to any
26
+ patent rights that may be infringed by your derivative works or by other
27
+ works in which the Apple Software may be incorporated.
28
+
29
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE
30
+ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31
+ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32
+ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33
+ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34
+
35
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38
+ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39
+ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40
+ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41
+ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42
+ POSSIBILITY OF SUCH DAMAGE.
43
+
44
+ Copyright (C) 2011 Apple Inc. All Rights Reserved.
45
+
46
+ */
47
+
48
+ #import "AVCamUtilities.h"
49
+
50
+
51
+ @implementation AVCamUtilities
52
+
53
+ + (AVCaptureConnection *)connectionWithMediaType:(NSString *)mediaType fromConnections:(NSArray *)connections
54
+ {
55
+ for ( AVCaptureConnection *connection in connections ) {
56
+ for ( AVCaptureInputPort *port in [connection inputPorts] ) {
57
+ if ( [[port mediaType] isEqual:mediaType] ) {
58
+ return connection;
59
+ }
60
+ }
61
+ }
62
+ return nil;
63
+ }
64
+
65
+ @end
@@ -0,0 +1,9 @@
1
+ //
2
+ // Prefix header for all source files of the 'AVClub' target in the 'AVClub' project
3
+ //
4
+
5
+ #ifdef __OBJC__
6
+ #import <Foundation/Foundation.h>
7
+ #import <UIKit/UIKit.h>
8
+ #import <AVFoundation/AVFoundation.h>
9
+ #endif
@@ -0,0 +1,72 @@
1
+ //
2
+ // AVClub.h
3
+ // AVClub
4
+ //
5
+ // Created by Colin Thomas-Arnold on 11/16/12.
6
+ // Copyright (c) 2012 colinta. All rights reserved.
7
+ //
8
+
9
+ #import <Foundation/Foundation.h>
10
+ #import <UIKit/UIKit.h>
11
+ #import <AVFoundation/AVFoundation.h>
12
+
13
+
14
+ @class AVCamRecorder, AVCaptureVideoPreviewLayer;
15
+ @protocol AVClubDelegate;
16
+
17
+
18
+ @interface AVClub : NSObject
19
+
20
+
21
+ @property (nonatomic,retain) AVCaptureSession *session;
22
+ @property (nonatomic,assign) AVCaptureVideoOrientation orientation;
23
+ @property (nonatomic,retain) AVCaptureDeviceInput *videoInput;
24
+ @property (nonatomic,retain) AVCaptureDeviceInput *audioInput;
25
+ @property (nonatomic,retain) AVCaptureStillImageOutput *stillImageOutput;
26
+ @property (nonatomic,retain) AVCamRecorder *recorder;
27
+ @property (nonatomic,assign) id deviceConnectedObserver;
28
+ @property (nonatomic,assign) id deviceDisconnectedObserver;
29
+ @property (nonatomic,assign) UIBackgroundTaskIdentifier backgroundRecordingID;
30
+ @property (nonatomic,assign) id <AVClubDelegate> delegate;
31
+ @property (nonatomic,assign) UIView *viewFinderView;
32
+ @property (nonatomic,assign) BOOL isRunning;
33
+ @property (nonatomic,retain) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;
34
+
35
+
36
+ - (void) startInView:(UIView*)videoView;
37
+ - (void) startSession;
38
+ - (void) stopSession;
39
+
40
+ - (CGPoint)convertToPointOfInterestFromViewCoordinates:(CGPoint)viewCoordinates;
41
+ - (void) startRecording;
42
+ - (void) stopRecording;
43
+ - (void) saveImageToLibrary:(UIImage*)image;
44
+ - (void) captureStillImage;
45
+ - (void) captureStillImageAnimated:(BOOL)animated;
46
+ - (BOOL) toggleCamera;
47
+ - (BOOL) toggleCameraAnimated:(BOOL)animated;
48
+ - (void) autoFocusAtPoint:(CGPoint)point;
49
+ - (void) continuousFocusAtPoint:(CGPoint)point;
50
+
51
+
52
+ - (NSUInteger) cameraCount;
53
+ - (NSUInteger) micCount;
54
+ - (BOOL) hasCamera;
55
+ - (BOOL) hasMultipleCameras;
56
+ - (BOOL) hasAudio;
57
+ - (BOOL) hasVideo;
58
+
59
+
60
+ @end
61
+
62
+
63
+ // These delegate methods are always called on the main CFRunLoop
64
+ @protocol AVClubDelegate <NSObject>
65
+ @optional
66
+ - (void) club:(AVClub *)club didFailWithError:(NSError *)error;
67
+ - (void) clubRecordingBegan:(AVClub *)club;
68
+ - (void) clubRecordingFinished:(AVClub *)club;
69
+ - (void) club:(AVClub*)club stillImageCaptured:(UIImage*)image error:(NSError*)error;
70
+ - (void) club:(AVClub*)club assetSavedToURL:(NSURL*)assetURL error:(NSError*)error;
71
+ - (void) clubDeviceConfigurationChanged:(AVClub *)club;
72
+ @end
@@ -0,0 +1,673 @@
1
+ //
2
+ // AVClub.m
3
+ // AVClub
4
+ //
5
+ // Created by Colin Thomas-Arnold on 11/16/12.
6
+ // Copyright (c) 2012 colinta. All rights reserved.
7
+ //
8
+
9
+ #import "AVClub.h"
10
+ #import "AVCamRecorder.h"
11
+ #import "AVCamUtilities.h"
12
+ #import <MobileCoreServices/UTCoreTypes.h>
13
+ #import <AssetsLibrary/AssetsLibrary.h>
14
+ #import <ImageIO/CGImageProperties.h>
15
+
16
+
17
+ @interface AVClub (RecorderDelegate) <AVCamRecorderDelegate>
18
+ @end
19
+
20
+
21
+ #pragma mark -
22
+ @interface AVClub (InternalUtilityMethods)
23
+ - (AVCaptureDevice *) cameraWithPosition:(AVCaptureDevicePosition)position;
24
+ - (AVCaptureDevice *) frontFacingCamera;
25
+ - (AVCaptureDevice *) backFacingCamera;
26
+ - (AVCaptureDevice *) audioDevice;
27
+ - (NSURL *) tempFileURL;
28
+ - (void) removeFile:(NSURL *)outputFileURL;
29
+ - (void) copyFileToDocuments:(NSURL *)fileURL;
30
+ @end
31
+
32
+
33
+ #pragma mark -
34
+ @implementation AVClub
35
+
36
+
37
+ @synthesize session = session;
38
+ @synthesize delegate;
39
+
40
+
41
+ - (id) init
42
+ {
43
+ self = [super init];
44
+ if (self != nil) {
45
+ self.isRunning = NO;
46
+
47
+ __block id weakSelf = self;
48
+ void (^deviceConnectedBlock)(NSNotification *) = ^(NSNotification *notification) {
49
+ AVCaptureDevice *device = [notification object];
50
+
51
+ BOOL sessionHasDeviceWithMatchingMediaType = NO;
52
+ NSString *deviceMediaType = nil;
53
+ if ( [device hasMediaType:AVMediaTypeAudio] )
54
+ deviceMediaType = AVMediaTypeAudio;
55
+ else if ( [device hasMediaType:AVMediaTypeVideo] )
56
+ deviceMediaType = AVMediaTypeVideo;
57
+
58
+ if ( deviceMediaType )
59
+ {
60
+ for (AVCaptureDeviceInput *input in [self.session inputs])
61
+ {
62
+ if ( [[input device] hasMediaType:deviceMediaType] )
63
+ {
64
+ sessionHasDeviceWithMatchingMediaType = YES;
65
+ break;
66
+ }
67
+ }
68
+
69
+ if ( ! sessionHasDeviceWithMatchingMediaType )
70
+ {
71
+ NSError *error;
72
+ AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
73
+ if ( [session canAddInput:input] )
74
+ [session addInput:input];
75
+ }
76
+ }
77
+
78
+ if ( [delegate respondsToSelector:@selector(clubDeviceConfigurationChanged:)] )
79
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate clubDeviceConfigurationChanged:self]; });
80
+ };
81
+ void (^deviceDisconnectedBlock)(NSNotification *) = ^(NSNotification *notification) {
82
+ AVCaptureDevice *device = [notification object];
83
+
84
+ if ( [device hasMediaType:AVMediaTypeAudio] ) {
85
+ [session removeInput:[weakSelf audioInput]];
86
+ [weakSelf setAudioInput:nil];
87
+ }
88
+ else if ( [device hasMediaType:AVMediaTypeVideo] ) {
89
+ [session removeInput:[weakSelf videoInput]];
90
+ [weakSelf setVideoInput:nil];
91
+ }
92
+
93
+ if ( [delegate respondsToSelector:@selector(clubDeviceConfigurationChanged:)] )
94
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate clubDeviceConfigurationChanged:self]; });
95
+ };
96
+
97
+ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
98
+ [self setDeviceConnectedObserver:[notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification object:nil queue:nil usingBlock:deviceConnectedBlock]];
99
+ [self setDeviceDisconnectedObserver:[notificationCenter addObserverForName:AVCaptureDeviceWasDisconnectedNotification object:nil queue:nil usingBlock:deviceDisconnectedBlock]];
100
+ [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
101
+ [notificationCenter addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil];
102
+ self.orientation = AVCaptureVideoOrientationPortrait;
103
+ }
104
+
105
+ return self;
106
+ }
107
+
108
+ - (void) dealloc
109
+ {
110
+ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
111
+ [notificationCenter removeObserver:[self deviceConnectedObserver]];
112
+ [notificationCenter removeObserver:[self deviceDisconnectedObserver]];
113
+ [notificationCenter removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
114
+ [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
115
+
116
+ [self stopSession];
117
+ }
118
+
119
+ - (void) startInView:(UIView*)videoView
120
+ {
121
+ // Set torch and flash mode to auto
122
+ if ( [[self backFacingCamera] hasFlash] )
123
+ {
124
+ if ( [[self backFacingCamera] lockForConfiguration:nil] )
125
+ {
126
+ if ( [[self backFacingCamera] isFlashModeSupported:AVCaptureFlashModeAuto] )
127
+ [[self backFacingCamera] setFlashMode:AVCaptureFlashModeAuto];
128
+
129
+ [[self backFacingCamera] unlockForConfiguration];
130
+ }
131
+ }
132
+ if ( [[self backFacingCamera] hasTorch] )
133
+ {
134
+ if ( [[self backFacingCamera] lockForConfiguration:nil] )
135
+ {
136
+ if ( [[self backFacingCamera] isTorchModeSupported:AVCaptureTorchModeAuto] )
137
+ [[self backFacingCamera] setTorchMode:AVCaptureTorchModeAuto];
138
+
139
+ [[self backFacingCamera] unlockForConfiguration];
140
+ }
141
+ }
142
+
143
+ // Init the device inputs
144
+ AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backFacingCamera] error:nil];
145
+ AVCaptureDeviceInput *newAudioInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self audioDevice] error:nil];
146
+
147
+
148
+ // Setup the still image file output
149
+ AVCaptureStillImageOutput *newStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
150
+ NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
151
+ AVVideoCodecJPEG, AVVideoCodecKey,
152
+ nil];
153
+ [newStillImageOutput setOutputSettings:outputSettings];
154
+
155
+
156
+ // Create session (use default AVCaptureSessionPresetHigh)
157
+ AVCaptureSession *newCaptureSession = [[AVCaptureSession alloc] init];
158
+
159
+
160
+ // Add inputs and output to the capture session
161
+ if ( [newCaptureSession canAddInput:newVideoInput] )
162
+ [newCaptureSession addInput:newVideoInput];
163
+
164
+ if ( [newCaptureSession canAddInput:newAudioInput] )
165
+ [newCaptureSession addInput:newAudioInput];
166
+
167
+ if ( [newCaptureSession canAddOutput:newStillImageOutput] )
168
+ [newCaptureSession addOutput:newStillImageOutput];
169
+
170
+ [self setStillImageOutput:newStillImageOutput];
171
+ [self setVideoInput:newVideoInput];
172
+ [self setAudioInput:newAudioInput];
173
+ [self setSession:newCaptureSession];
174
+
175
+ // Set up the movie file output
176
+ NSURL *outputFileURL = [self tempFileURL];
177
+ AVCamRecorder *newRecorder = [[AVCamRecorder alloc] initWithSession:[self session] outputFileURL:outputFileURL];
178
+ [newRecorder setDelegate:self];
179
+
180
+ // Send an error to the delegate if video recording is unavailable
181
+ if ( ! [newRecorder recordsVideo] && [newRecorder recordsAudio] )
182
+ {
183
+ NSString *localizedDescription = NSLocalizedString(@"Video recording unavailable", @"Video recording unavailable description");
184
+ NSString *localizedFailureReason = NSLocalizedString(@"Movies recorded on this device will only contain audio. They will be accessible through iTunes file sharing.", @"Video recording unavailable failure reason");
185
+ NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
186
+ localizedDescription, NSLocalizedDescriptionKey,
187
+ localizedFailureReason, NSLocalizedFailureReasonErrorKey,
188
+ nil];
189
+ NSError *noVideoError = [NSError errorWithDomain:@"AVCam" code:0 userInfo:errorDict];
190
+ if ( [delegate respondsToSelector:@selector(club:didFailWithError:)] )
191
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self didFailWithError:noVideoError]; });
192
+ }
193
+
194
+ [self setRecorder:newRecorder];
195
+
196
+ // Create video preview layer and add it to the UI
197
+ if ( videoView )
198
+ {
199
+ AVCaptureVideoPreviewLayer *newCaptureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:[self session]];
200
+ CALayer *viewLayer = [videoView layer];
201
+ [viewLayer setMasksToBounds:YES];
202
+
203
+ CGRect bounds = [videoView bounds];
204
+ [newCaptureVideoPreviewLayer setFrame:bounds];
205
+
206
+ if ( [[self recorder] isOrientationSupported] )
207
+ [[self recorder] setOrientation:AVCaptureVideoOrientationPortrait];
208
+
209
+ [newCaptureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
210
+
211
+ [viewLayer insertSublayer:newCaptureVideoPreviewLayer below:[[viewLayer sublayers] objectAtIndex:0]];
212
+
213
+ self.captureVideoPreviewLayer = newCaptureVideoPreviewLayer;
214
+
215
+ // Start the session. This is done asychronously since -startRunning doesn't return until the session is running.
216
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
217
+ [self startSession];
218
+ });
219
+
220
+ [self setVideoPreviewView:videoView];
221
+ }
222
+ }
223
+
224
+ - (void) startSession
225
+ {
226
+ if ( ! self.isRunning )
227
+ {
228
+ [[self session] startRunning];
229
+ self.isRunning = YES;
230
+ }
231
+ }
232
+
233
+ - (void) stopSession
234
+ {
235
+ if ( self.isRunning )
236
+ {
237
+ [[self session] stopRunning];
238
+ self.isRunning = NO;
239
+ }
240
+ }
241
+
242
+ - (void) startRecording
243
+ {
244
+ if ( [[UIDevice currentDevice] isMultitaskingSupported] )
245
+ {
246
+ // Setup background task. This is needed because the captureOutput:didFinishRecordingToOutputFileAtURL: callback is not received until AVCam returns
247
+ // to the foreground unless you request background execution time. This also ensures that there will be time to write the file to the assets library
248
+ // when AVCam is backgrounded. To conclude this background execution, -endBackgroundTask is called in -recorder:recordingDidFinishToOutputFileURL:error:
249
+ // after the recorded file has been saved.
250
+ [self setBackgroundRecordingID:[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{}]];
251
+ }
252
+
253
+ [self removeFile:[[self recorder] outputFileURL]];
254
+ [[self recorder] startRecordingWithOrientation:self.orientation];
255
+ }
256
+
257
+ - (void) stopRecording
258
+ {
259
+ [[self recorder] stopRecording];
260
+ }
261
+
262
+ - (void) saveImageToLibrary:(UIImage*)image
263
+ {
264
+ ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
265
+
266
+ ALAssetsLibraryWriteImageCompletionBlock completionBlock = ^(NSURL *assetURL, NSError *error) {
267
+ if ( [delegate respondsToSelector:@selector(club:assetSavedToURL:error:)] )
268
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self assetSavedToURL:assetURL error:error]; });
269
+ };
270
+
271
+ [library writeImageToSavedPhotosAlbum:[image CGImage]
272
+ orientation:(ALAssetOrientation)[image imageOrientation]
273
+ completionBlock:completionBlock];
274
+ }
275
+
276
+ - (void) captureStillImage
277
+ {
278
+ return [self captureStillImageAnimated:NO];
279
+ }
280
+ - (void) captureStillImageAnimated:(BOOL)animated
281
+ {
282
+ AVCaptureConnection *stillImageConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self stillImageOutput] connections]];
283
+ if ( [stillImageConnection isVideoOrientationSupported] )
284
+ [stillImageConnection setVideoOrientation:self.orientation];
285
+
286
+ if ( animated )
287
+ {
288
+ // Flash the screen white and fade it out to give UI feedback that a still image was taken
289
+ UIView *flashView = [[UIView alloc] initWithFrame:[[[self viewFinderView] window] bounds]];
290
+ [flashView setBackgroundColor:[UIColor whiteColor]];
291
+ [[[self viewFinderView] window] addSubview:flashView];
292
+
293
+ [UIView animateWithDuration:.4f
294
+ animations:^{
295
+ [flashView setAlpha:0.f];
296
+ }
297
+ completion:^(BOOL finished){
298
+ [flashView removeFromSuperview];
299
+ }
300
+ ];
301
+ }
302
+
303
+ [[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:stillImageConnection
304
+ completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
305
+
306
+ void (^completionBlock)(UIImage*,NSError*) = ^(UIImage *image, NSError *error) {
307
+ if ( error )
308
+ {
309
+ if ( [delegate respondsToSelector:@selector(club:stillImageCaptured:error:)] )
310
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self stillImageCaptured:nil error:error]; });
311
+ }
312
+ else if ( [delegate respondsToSelector:@selector(club:stillImageCaptured:error:)] )
313
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self stillImageCaptured:image error:nil]; });
314
+ };
315
+
316
+ if ( imageDataSampleBuffer )
317
+ {
318
+ NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
319
+
320
+ UIImage *image = [[UIImage alloc] initWithData:imageData];
321
+ completionBlock(image, nil);
322
+ }
323
+ else
324
+ completionBlock(nil, error);
325
+ }];
326
+ }
327
+
328
+ // Toggle between the front and back camera, if both are present.
329
+ - (BOOL) toggleCamera
330
+ {
331
+ return [self toggleCameraAnimated:NO];
332
+ }
333
+ - (BOOL) toggleCameraAnimated:(BOOL)animated;
334
+ {
335
+ BOOL success = NO;
336
+
337
+ if ( [self cameraCount] > 1 )
338
+ {
339
+ NSError *error;
340
+ AVCaptureDeviceInput *newVideoInput;
341
+ AVCaptureDevicePosition position = [[self.videoInput device] position];
342
+
343
+ if ( position == AVCaptureDevicePositionBack )
344
+ newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self frontFacingCamera] error:&error];
345
+ else if ( position == AVCaptureDevicePositionFront )
346
+ newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backFacingCamera] error:&error];
347
+ else
348
+ goto bail;
349
+
350
+ if ( newVideoInput != nil )
351
+ {
352
+ [[self session] beginConfiguration];
353
+ [[self session] removeInput:[self videoInput]];
354
+ if ( [[self session] canAddInput:newVideoInput] )
355
+ {
356
+ [[self session] addInput:newVideoInput];
357
+ [self setVideoInput:newVideoInput];
358
+ }
359
+ else
360
+ [[self session] addInput:[self videoInput]];
361
+ [[self session] commitConfiguration];
362
+ success = YES;
363
+ }
364
+ else if ( error )
365
+ {
366
+ if ( [delegate respondsToSelector:@selector(club:didFailWithError:)] )
367
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self didFailWithError:error]; });
368
+ }
369
+ }
370
+
371
+ bail:
372
+ return success;
373
+ }
374
+
375
+
376
+ #pragma mark Device Counts
377
+ - (NSUInteger) cameraCount
378
+ {
379
+ return [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
380
+ }
381
+
382
+ - (NSUInteger) micCount
383
+ {
384
+ return [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] count];
385
+ }
386
+
387
+ - (BOOL) hasCamera
388
+ {
389
+ return [self cameraCount] > 0;
390
+ }
391
+ - (BOOL) hasMultipleCameras
392
+ {
393
+ return [self cameraCount] > 1;
394
+ }
395
+ - (BOOL) hasAudio
396
+ {
397
+ return [self micCount] > 0;
398
+ }
399
+ - (BOOL) hasVideo
400
+ {
401
+ return [self hasCamera] && [self hasAudio];
402
+ }
403
+
404
+
405
+ #pragma mark Camera Properties
406
+ // Perform an auto focus at the specified point. The focus mode will automatically change to locked once the auto focus is complete.
407
+ - (void) autoFocusAtPoint:(CGPoint)point
408
+ {
409
+ AVCaptureDevice *device = [[self videoInput] device];
410
+ if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
411
+ NSError *error;
412
+ if ( [device lockForConfiguration:&error] )
413
+ {
414
+ [device setFocusPointOfInterest:point];
415
+ [device setFocusMode:AVCaptureFocusModeAutoFocus];
416
+ [device unlockForConfiguration];
417
+ }
418
+ else
419
+ {
420
+ if ([delegate respondsToSelector:@selector(club:didFailWithError:)])
421
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self didFailWithError:error]; });
422
+ }
423
+ }
424
+ }
425
+
426
+ // Switch to continuous auto focus mode at the specified point
427
+ - (void) continuousFocusAtPoint:(CGPoint)point
428
+ {
429
+ AVCaptureDevice *device = [[self videoInput] device];
430
+
431
+ if ( [device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus] )
432
+ {
433
+ NSError *error;
434
+ if ( [device lockForConfiguration:&error] )
435
+ {
436
+ [device setFocusPointOfInterest:point];
437
+ [device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
438
+ [device unlockForConfiguration];
439
+ }
440
+ else
441
+ {
442
+ if ( [delegate respondsToSelector:@selector(club:didFailWithError:)] )
443
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self didFailWithError:error]; });
444
+ }
445
+ }
446
+ }
447
+
448
+ // Convert from view coordinates to camera coordinates, where {0,0} represents the top left of the picture area, and {1,1} represents
449
+ // the bottom right in landscape mode with the home button on the right.
450
+ - (CGPoint)convertToPointOfInterestFromViewCoordinates:(CGPoint)viewCoordinates
451
+ {
452
+ CGPoint pointOfInterest = CGPointMake(.5f, .5f);
453
+ CGSize frameSize = [[self viewFinderView] frame].size;
454
+
455
+ if ( [[self recorder] isMirrored] )
456
+ viewCoordinates.x = frameSize.width - viewCoordinates.x;
457
+
458
+ if ( [[self.captureVideoPreviewLayer videoGravity] isEqualToString:AVLayerVideoGravityResize] )
459
+ {
460
+ // Scale, switch x and y, and reverse x
461
+ pointOfInterest = CGPointMake(viewCoordinates.y / frameSize.height, 1.f - (viewCoordinates.x / frameSize.width));
462
+ }
463
+ else
464
+ {
465
+ CGRect cleanAperture;
466
+ for (AVCaptureInputPort *port in [[self videoInput] ports])
467
+ {
468
+ if ( [port mediaType] == AVMediaTypeVideo )
469
+ {
470
+ cleanAperture = CMVideoFormatDescriptionGetCleanAperture([port formatDescription], YES);
471
+ CGSize apertureSize = cleanAperture.size;
472
+ CGPoint point = viewCoordinates;
473
+
474
+ CGFloat apertureRatio = apertureSize.height / apertureSize.width;
475
+ CGFloat viewRatio = frameSize.width / frameSize.height;
476
+ CGFloat xc = .5f;
477
+ CGFloat yc = .5f;
478
+
479
+ if ( [[self.captureVideoPreviewLayer videoGravity] isEqualToString:AVLayerVideoGravityResizeAspect] )
480
+ {
481
+ if (viewRatio > apertureRatio)
482
+ {
483
+ CGFloat y2 = frameSize.height;
484
+ CGFloat x2 = frameSize.height * apertureRatio;
485
+ CGFloat x1 = frameSize.width;
486
+ CGFloat blackBar = (x1 - x2) / 2;
487
+ // If point is inside letterboxed area, do coordinate conversion; otherwise, don't change the default value returned (.5,.5)
488
+ if (point.x >= blackBar && point.x <= blackBar + x2) {
489
+ // Scale (accounting for the letterboxing on the left and right of the video preview), switch x and y, and reverse x
490
+ xc = point.y / y2;
491
+ yc = 1.f - ((point.x - blackBar) / x2);
492
+ }
493
+ }
494
+ else
495
+ {
496
+ CGFloat y2 = frameSize.width / apertureRatio;
497
+ CGFloat y1 = frameSize.height;
498
+ CGFloat x2 = frameSize.width;
499
+ CGFloat blackBar = (y1 - y2) / 2;
500
+ // If point is inside letterboxed area, do coordinate conversion. Otherwise, don't change the default value returned (.5,.5)
501
+ if (point.y >= blackBar && point.y <= blackBar + y2) {
502
+ // Scale (accounting for the letterboxing on the top and bottom of the video preview), switch x and y, and reverse x
503
+ xc = ((point.y - blackBar) / y2);
504
+ yc = 1.f - (point.x / x2);
505
+ }
506
+ }
507
+ }
508
+ else if ( [[self.captureVideoPreviewLayer videoGravity] isEqualToString:AVLayerVideoGravityResizeAspectFill] )
509
+ {
510
+ // Scale, switch x and y, and reverse x
511
+ if ( viewRatio > apertureRatio )
512
+ {
513
+ CGFloat y2 = apertureSize.width * (frameSize.width / apertureSize.height);
514
+ xc = (point.y + ((y2 - frameSize.height) / 2.f)) / y2; // Account for cropped height
515
+ yc = (frameSize.width - point.x) / frameSize.width;
516
+ }
517
+ else
518
+ {
519
+ CGFloat x2 = apertureSize.height * (frameSize.height / apertureSize.width);
520
+ yc = 1.f - ((point.x + ((x2 - frameSize.width) / 2)) / x2); // Account for cropped width
521
+ xc = point.y / frameSize.height;
522
+ }
523
+ }
524
+
525
+ pointOfInterest = CGPointMake(xc, yc);
526
+ break;
527
+ }
528
+ }
529
+ }
530
+
531
+ return pointOfInterest;
532
+ }
533
+
534
+ @end
535
+
536
+
537
+ #pragma mark -
538
+ @implementation AVClub (InternalUtilityMethods)
539
+
540
+ // Keep track of current device orientation so it can be applied to movie recordings and still image captures
541
+ - (void)deviceOrientationDidChange
542
+ {
543
+ UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
544
+
545
+ if ( deviceOrientation == UIDeviceOrientationPortrait )
546
+ self.orientation = AVCaptureVideoOrientationPortrait;
547
+ else if ( deviceOrientation == UIDeviceOrientationPortraitUpsideDown )
548
+ self.orientation = AVCaptureVideoOrientationPortraitUpsideDown;
549
+
550
+ // AVCapture and UIDevice have opposite meanings for landscape left and right (AVCapture orientation is the same as UIInterfaceOrientation)
551
+ else if ( deviceOrientation == UIDeviceOrientationLandscapeLeft )
552
+ self.orientation = AVCaptureVideoOrientationLandscapeRight;
553
+ else if ( deviceOrientation == UIDeviceOrientationLandscapeRight )
554
+ self.orientation = AVCaptureVideoOrientationLandscapeLeft;
555
+
556
+ // Ignore device orientations for which there is no corresponding still image orientation (e.g. UIDeviceOrientationFaceUp)
557
+ }
558
+
559
+ // Find a camera with the specificed AVCaptureDevicePosition, returning nil if one is not found
560
+ - (AVCaptureDevice *) cameraWithPosition:(AVCaptureDevicePosition) position
561
+ {
562
+ NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
563
+ for (AVCaptureDevice *device in devices)
564
+ {
565
+ if ( [device position] == position )
566
+ return device;
567
+ }
568
+ return nil;
569
+ }
570
+
571
+ // Find a front facing camera, returning nil if one is not found
572
+ - (AVCaptureDevice *) frontFacingCamera
573
+ {
574
+ return [self cameraWithPosition:AVCaptureDevicePositionFront];
575
+ }
576
+
577
+ // Find a back facing camera, returning nil if one is not found
578
+ - (AVCaptureDevice *) backFacingCamera
579
+ {
580
+ return [self cameraWithPosition:AVCaptureDevicePositionBack];
581
+ }
582
+
583
+ // Find and return an audio device, returning nil if one is not found
584
+ - (AVCaptureDevice *) audioDevice
585
+ {
586
+ NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
587
+ if ( [devices count] > 0 )
588
+ return [devices objectAtIndex:0];
589
+ return nil;
590
+ }
591
+
592
+ - (NSURL *) tempFileURL
593
+ {
594
+ return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]];
595
+ }
596
+
597
+ - (void) removeFile:(NSURL *)fileURL
598
+ {
599
+ NSString *filePath = [fileURL path];
600
+ NSFileManager *fileManager = [NSFileManager defaultManager];
601
+ if ( [fileManager fileExistsAtPath:filePath] )
602
+ {
603
+ NSError *error;
604
+ if ( [fileManager removeItemAtPath:filePath error:&error] == NO )
605
+ {
606
+ if ( [delegate respondsToSelector:@selector(club:didFailWithError:)] )
607
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self didFailWithError:error]; });
608
+ }
609
+ }
610
+ }
611
+
612
+ - (void) copyFileToDocuments:(NSURL *)fileURL
613
+ {
614
+ NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
615
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
616
+ [dateFormatter setDateFormat:@"yyyy-MM-dd_HH-mm-ss"];
617
+ NSString *destinationPath = [documentsDirectory stringByAppendingFormat:@"/output_%@.mov", [dateFormatter stringFromDate:[NSDate date]]];
618
+ NSError *error;
619
+ if ( ! [[NSFileManager defaultManager] copyItemAtURL:fileURL toURL:[NSURL fileURLWithPath:destinationPath] error:&error] )
620
+ {
621
+ if ( [delegate respondsToSelector:@selector(club:didFailWithError:)] )
622
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self didFailWithError:error]; });
623
+ }
624
+ }
625
+
626
+ @end
627
+
628
+
629
+ #pragma mark -
630
+ @implementation AVClub (RecorderDelegate)
631
+
632
+ -(void)recorderRecordingDidBegin:(AVCamRecorder *)recorder
633
+ {
634
+ if ( [delegate respondsToSelector:@selector(clubRecordingBegan:)] )
635
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate clubRecordingBegan:self]; });
636
+ }
637
+
638
+ -(void)recorder:(AVCamRecorder *)recorder recordingDidFinishToOutputFileURL:(NSURL *)outputFileURL error:(NSError *)error
639
+ {
640
+ if ( [[self recorder] recordsAudio] && ![[self recorder] recordsVideo] )
641
+ {
642
+ // If the file was created on a device that doesn't support video recording, it can't be saved to the assets
643
+ // library. Instead, save it in the app's Documents directory, whence it can be copied from the device via
644
+ // iTunes file sharing.
645
+ [self copyFileToDocuments:outputFileURL];
646
+
647
+ if ( [[UIDevice currentDevice] isMultitaskingSupported] )
648
+ [[UIApplication sharedApplication] endBackgroundTask:[self backgroundRecordingID]];
649
+
650
+ if ( [delegate respondsToSelector:@selector(clubRecordingFinished:)] )
651
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate clubRecordingFinished:self]; });
652
+ }
653
+ else
654
+ {
655
+ ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
656
+ [library writeVideoAtPathToSavedPhotosAlbum:outputFileURL
657
+ completionBlock:^(NSURL *assetURL, NSError *error) {
658
+ if ( error )
659
+ {
660
+ if ([delegate respondsToSelector:@selector(club:didFailWithError:)])
661
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate club:self didFailWithError:error]; });
662
+ }
663
+
664
+ if ( [[UIDevice currentDevice] isMultitaskingSupported] )
665
+ [[UIApplication sharedApplication] endBackgroundTask:[self backgroundRecordingID]];
666
+
667
+ if ( [delegate respondsToSelector:@selector(clubRecordingFinished:)] )
668
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{ [delegate clubRecordingFinished:self]; });
669
+ }];
670
+ }
671
+ }
672
+
673
+ @end