AVClub 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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