diff --git a/README.md b/README.md
index e43a9b3..0c9a2b7 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,22 @@ Region monitoring demo using iBeacon.
![](http://f.cl.ly/items/220D2F210D1x1D0L1Q20/beacon__.gif)
+Related Repository: [PulsingHalo](https://github.com/shu223/PulsingHalo)
+
+
+###120fps Video Recording
+
+SLO-MO video recorder using AVFoundation. It works with 120fps on iPhone5s.
+
+![](http://f.cl.ly/items/360a271y1G3Q2C2a3p2d/IMG_8907_r1_c1.jpg)
+
+Example:
+
+![](http://f.cl.ly/items/1b3Q0h0k3k2m261s3R3n/samplemovie__.gif)
+
+
See the 120fps Slo-Mo video in Vimeo 120fps.
+
+Related Repository: [SlowMotionVideoRecorder](https://github.com/shu223/SlowMotionVideoRecorder)
###Smile Detection
diff --git a/iOS7Sampler.xcodeproj/project.pbxproj b/iOS7Sampler.xcodeproj/project.pbxproj
index 803aa99..f7a0b8b 100644
--- a/iOS7Sampler.xcodeproj/project.pbxproj
+++ b/iOS7Sampler.xcodeproj/project.pbxproj
@@ -119,6 +119,9 @@
8AC907551850A66000A74F2E /* PulsingHaloLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AC907541850A66000A74F2E /* PulsingHaloLayer.m */; };
8AC907571850AB8000A74F2E /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8AC907561850AB8000A74F2E /* CoreBluetooth.framework */; };
8AC907591850B2F600A74F2E /* IPhone_5s@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8AC907581850B2F600A74F2E /* IPhone_5s@2x.png */; };
+ 8ACDEF3E1878F61D00BC94F6 /* AVCaptureManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8ACDEF3D1878F61D00BC94F6 /* AVCaptureManager.m */; };
+ 8ACDEF421878F68D00BC94F6 /* SloMoVideoRecordViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8ACDEF401878F68D00BC94F6 /* SloMoVideoRecordViewController.m */; };
+ 8ACDEF431878F68D00BC94F6 /* SloMoVideoRecordViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8ACDEF411878F68D00BC94F6 /* SloMoVideoRecordViewController.xib */; };
8AEF0F9417FBCCF600D2B9BE /* ReadingListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AEF0F9217FBCCF600D2B9BE /* ReadingListViewController.m */; };
8AEF0F9517FBCCF600D2B9BE /* ReadingListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8AEF0F9317FBCCF600D2B9BE /* ReadingListViewController.xib */; };
8AEF0F9717FBCDDB00D2B9BE /* SafariServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8AEF0F9617FBCDDB00D2B9BE /* SafariServices.framework */; };
@@ -282,6 +285,11 @@
8AC907541850A66000A74F2E /* PulsingHaloLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PulsingHaloLayer.m; sourceTree = ""; };
8AC907561850AB8000A74F2E /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; };
8AC907581850B2F600A74F2E /* IPhone_5s@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "IPhone_5s@2x.png"; sourceTree = ""; };
+ 8ACDEF3C1878F61D00BC94F6 /* AVCaptureManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVCaptureManager.h; sourceTree = ""; };
+ 8ACDEF3D1878F61D00BC94F6 /* AVCaptureManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVCaptureManager.m; sourceTree = ""; };
+ 8ACDEF3F1878F68D00BC94F6 /* SloMoVideoRecordViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SloMoVideoRecordViewController.h; sourceTree = ""; };
+ 8ACDEF401878F68D00BC94F6 /* SloMoVideoRecordViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SloMoVideoRecordViewController.m; sourceTree = ""; };
+ 8ACDEF411878F68D00BC94F6 /* SloMoVideoRecordViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SloMoVideoRecordViewController.xib; sourceTree = ""; };
8AEF0F9117FBCCF600D2B9BE /* ReadingListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReadingListViewController.h; sourceTree = ""; };
8AEF0F9217FBCCF600D2B9BE /* ReadingListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReadingListViewController.m; sourceTree = ""; };
8AEF0F9317FBCCF600D2B9BE /* ReadingListViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReadingListViewController.xib; sourceTree = ""; };
@@ -490,6 +498,9 @@
8AC9074D1850931C00A74F2E /* BeaconViewController.h */,
8AC9074E1850931C00A74F2E /* BeaconViewController.m */,
8AC9074F1850931C00A74F2E /* BeaconViewController.xib */,
+ 8ACDEF3F1878F68D00BC94F6 /* SloMoVideoRecordViewController.h */,
+ 8ACDEF401878F68D00BC94F6 /* SloMoVideoRecordViewController.m */,
+ 8ACDEF411878F68D00BC94F6 /* SloMoVideoRecordViewController.xib */,
);
path = SampleViewControllers;
sourceTree = "";
@@ -566,6 +577,7 @@
8A7BF3B117ED8B43004EF216 /* vendor */ = {
isa = PBXGroup;
children = (
+ 8ACDEF3B1878F61D00BC94F6 /* AVCaptureManager */,
8AC907521850A66000A74F2E /* PulsingHalo */,
8A43D5B917F12307005E8BE8 /* ZBCustomTransitions */,
8A43D5B317F11E72005E8BE8 /* SVProgressHUD */,
@@ -598,6 +610,15 @@
path = PulsingHalo;
sourceTree = "";
};
+ 8ACDEF3B1878F61D00BC94F6 /* AVCaptureManager */ = {
+ isa = PBXGroup;
+ children = (
+ 8ACDEF3C1878F61D00BC94F6 /* AVCaptureManager.h */,
+ 8ACDEF3D1878F61D00BC94F6 /* AVCaptureManager.m */,
+ );
+ path = AVCaptureManager;
+ sourceTree = "";
+ };
8AEFF739181007D5004182BD /* plists */ = {
isa = PBXGroup;
children = (
@@ -697,6 +718,7 @@
8A43D5B717F11E72005E8BE8 /* SVProgressHUD.bundle in Resources */,
8A7BF35E17ED5BB1004EF216 /* m1@2x.png in Resources */,
8A7BF35F17ED5BB1004EF216 /* m2@2x.png in Resources */,
+ 8ACDEF431878F68D00BC94F6 /* SloMoVideoRecordViewController.xib in Resources */,
8A7BF37517ED5BB1004EF216 /* m25@2x.png in Resources */,
8A7BF3A517ED7F5D004EF216 /* stage_4@2x.png in Resources */,
8A7BF39517ED7EF7004EF216 /* MotionEffectViewController.xib in Resources */,
@@ -786,6 +808,8 @@
8A7BF3BB17ED8B43004EF216 /* HUTransitionAnimator.m in Sources */,
8A6DBC7117F19718004311FE /* MapDirectionsViewController.m in Sources */,
8A6DBC8417F2CDC0004311FE /* ImageFiltersViewController.m in Sources */,
+ 8ACDEF3E1878F61D00BC94F6 /* AVCaptureManager.m in Sources */,
+ 8ACDEF421878F68D00BC94F6 /* SloMoVideoRecordViewController.m in Sources */,
8A7BF38817ED6882004EF216 /* ActivityTypesViewController.m in Sources */,
8A7BF3AF17ED8A9E004EF216 /* CustomTransitionViewController.m in Sources */,
8A7BF1D717ED384C004EF216 /* MasterViewController.m in Sources */,
diff --git a/iOS7Sampler/Images.xcassets/ShutterButtonStart.imageset/Contents.json b/iOS7Sampler/Images.xcassets/ShutterButtonStart.imageset/Contents.json
new file mode 100644
index 0000000..75676f7
--- /dev/null
+++ b/iOS7Sampler/Images.xcassets/ShutterButtonStart.imageset/Contents.json
@@ -0,0 +1,17 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "ShutterButton1@2x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/iOS7Sampler/Images.xcassets/ShutterButtonStart.imageset/ShutterButton1@2x.png b/iOS7Sampler/Images.xcassets/ShutterButtonStart.imageset/ShutterButton1@2x.png
new file mode 100644
index 0000000..bb223b0
Binary files /dev/null and b/iOS7Sampler/Images.xcassets/ShutterButtonStart.imageset/ShutterButton1@2x.png differ
diff --git a/iOS7Sampler/Images.xcassets/ShutterButtonStop.imageset/Contents.json b/iOS7Sampler/Images.xcassets/ShutterButtonStop.imageset/Contents.json
new file mode 100644
index 0000000..b66fb24
--- /dev/null
+++ b/iOS7Sampler/Images.xcassets/ShutterButtonStop.imageset/Contents.json
@@ -0,0 +1,17 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "ShutterButtonStop@2x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/iOS7Sampler/Images.xcassets/ShutterButtonStop.imageset/ShutterButtonStop@2x.png b/iOS7Sampler/Images.xcassets/ShutterButtonStop.imageset/ShutterButtonStop@2x.png
new file mode 100644
index 0000000..e9a190c
Binary files /dev/null and b/iOS7Sampler/Images.xcassets/ShutterButtonStop.imageset/ShutterButtonStop@2x.png differ
diff --git a/iOS7Sampler/Images.xcassets/outer1.imageset/Contents.json b/iOS7Sampler/Images.xcassets/outer1.imageset/Contents.json
new file mode 100644
index 0000000..8ad6212
--- /dev/null
+++ b/iOS7Sampler/Images.xcassets/outer1.imageset/Contents.json
@@ -0,0 +1,17 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "outer1@2x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/iOS7Sampler/Images.xcassets/outer1.imageset/outer1@2x.png b/iOS7Sampler/Images.xcassets/outer1.imageset/outer1@2x.png
new file mode 100644
index 0000000..aa62bcf
Binary files /dev/null and b/iOS7Sampler/Images.xcassets/outer1.imageset/outer1@2x.png differ
diff --git a/iOS7Sampler/Images.xcassets/outer2.imageset/Contents.json b/iOS7Sampler/Images.xcassets/outer2.imageset/Contents.json
new file mode 100644
index 0000000..94582e5
--- /dev/null
+++ b/iOS7Sampler/Images.xcassets/outer2.imageset/Contents.json
@@ -0,0 +1,17 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "outer2@2x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/iOS7Sampler/Images.xcassets/outer2.imageset/outer2@2x.png b/iOS7Sampler/Images.xcassets/outer2.imageset/outer2@2x.png
new file mode 100644
index 0000000..d8d7954
Binary files /dev/null and b/iOS7Sampler/Images.xcassets/outer2.imageset/outer2@2x.png differ
diff --git a/iOS7Sampler/MasterViewController.m b/iOS7Sampler/MasterViewController.m
index b24e8b0..7d00e0e 100644
--- a/iOS7Sampler/MasterViewController.m
+++ b/iOS7Sampler/MasterViewController.m
@@ -61,6 +61,12 @@ - (void)viewDidLoad
kItemKeyClassPrefix: @"Beacon",
},
+ // 120fps Video Recording
+ @{kItemKeyTitle: @"120 fps SLO-MO video recording",
+ kItemKeyDescription: @"SLO-MO video recorder using AVFoundation. It works with 120fps on iPhone5s.",
+ kItemKeyClassPrefix: @"SloMoVideoRecord",
+ },
+
// Smile Detection
@{kItemKeyTitle: @"Smile Detection",
kItemKeyDescription: @"Smile Detection using CIDetectorSmile and new properties of CIFeature such as \"bounds\".",
@@ -114,7 +120,7 @@ - (void)viewDidLoad
kItemKeyDescription: @"Counting steps and monitoring the activity using CMStepCounter and CMMotionActivityManager. It works only on iPhone5s (M7 chip).",
kItemKeyClassPrefix: @"ActivityTracking",
},
-
+
// Static Map Snapshots
@{kItemKeyTitle: @"Static Map Snapshots",
kItemKeyDescription: @"Creating a snapshot with MKMapSnapshotOptions, MKMapSnapshotter.",
diff --git a/iOS7Sampler/SampleViewControllers/SloMoVideoRecordViewController.h b/iOS7Sampler/SampleViewControllers/SloMoVideoRecordViewController.h
new file mode 100644
index 0000000..f273917
--- /dev/null
+++ b/iOS7Sampler/SampleViewControllers/SloMoVideoRecordViewController.h
@@ -0,0 +1,13 @@
+//
+// SloMoVideoRecordViewController.h
+// https://github.com/shu223/SlowMotionVideoRecorder
+//
+// Created by shuichi on 1/5/14.
+// Copyright (c) 2014 Shuichi Tsutsumi. All rights reserved.
+//
+
+#import
+
+@interface SloMoVideoRecordViewController : UIViewController
+
+@end
diff --git a/iOS7Sampler/SampleViewControllers/SloMoVideoRecordViewController.m b/iOS7Sampler/SampleViewControllers/SloMoVideoRecordViewController.m
new file mode 100644
index 0000000..483d730
--- /dev/null
+++ b/iOS7Sampler/SampleViewControllers/SloMoVideoRecordViewController.m
@@ -0,0 +1,248 @@
+//
+// SloMoVideoRecordViewController.m
+// https://github.com/shu223/SlowMotionVideoRecorder
+//
+// Created by shuichi on 1/5/14.
+// Copyright (c) 2014 Shuichi Tsutsumi. All rights reserved.
+//
+
+#import "SloMoVideoRecordViewController.h"
+#import "SVProgressHUD.h"
+#import "AVCaptureManager.h"
+#import
+
+
+@interface SloMoVideoRecordViewController ()
+
+{
+ NSTimeInterval startTime;
+ BOOL isNeededToSave;
+}
+@property (nonatomic, strong) AVCaptureManager *captureManager;
+@property (nonatomic, assign) NSTimer *timer;
+@property (nonatomic, strong) UIImage *recStartImage;
+@property (nonatomic, strong) UIImage *recStopImage;
+@property (nonatomic, strong) UIImage *outerImage1;
+@property (nonatomic, strong) UIImage *outerImage2;
+
+@property (nonatomic, weak) IBOutlet UILabel *statusLabel;
+@property (nonatomic, weak) IBOutlet UISegmentedControl *fpsControl;
+@property (nonatomic, weak) IBOutlet UIButton *recBtn;
+@property (nonatomic, weak) IBOutlet UIImageView *outerImageView;
+@end
+
+
+@implementation SloMoVideoRecordViewController
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ self.captureManager = [[AVCaptureManager alloc] initWithPreviewView:self.view];
+ self.captureManager.delegate = self;
+
+ UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self
+ action:@selector(handleDoubleTap:)];
+ tapGesture.numberOfTapsRequired = 2;
+ [self.view addGestureRecognizer:tapGesture];
+
+
+ // Setup images for the Shutter Button
+ UIImage *image;
+ image = [UIImage imageNamed:@"ShutterButtonStart"];
+ self.recStartImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ [self.recBtn setImage:self.recStartImage
+ forState:UIControlStateNormal];
+
+ image = [UIImage imageNamed:@"ShutterButtonStop"];
+ self.recStopImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+
+ [self.recBtn setTintColor:[UIColor colorWithRed:245./255.
+ green:51./255.
+ blue:51./255.
+ alpha:1.0]];
+ self.outerImage1 = [UIImage imageNamed:@"outer1"];
+ self.outerImage2 = [UIImage imageNamed:@"outer2"];
+ self.outerImageView.image = self.outerImage1;
+}
+
+- (void)didReceiveMemoryWarning
+{
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+
+// =============================================================================
+#pragma mark - Gesture Handler
+
+- (void)handleDoubleTap:(UITapGestureRecognizer *)sender {
+
+ [self.captureManager toggleContentsGravity];
+}
+
+
+// =============================================================================
+#pragma mark - Private
+
+
+- (void)saveRecordedFile:(NSURL *)recordedFile {
+
+ [SVProgressHUD showWithStatus:@"Saving..."
+ maskType:SVProgressHUDMaskTypeGradient];
+
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(queue, ^{
+
+ ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init];
+ [assetLibrary writeVideoAtPathToSavedPhotosAlbum:recordedFile
+ completionBlock:
+ ^(NSURL *assetURL, NSError *error) {
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+
+ [SVProgressHUD dismiss];
+
+ NSString *title;
+ NSString *message;
+
+ if (error != nil) {
+
+ title = @"Failed to save video";
+ message = [error localizedDescription];
+ }
+ else {
+ title = @"Saved!";
+ message = nil;
+ }
+
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
+ message:message
+ delegate:nil
+ cancelButtonTitle:@"OK"
+ otherButtonTitles:nil];
+ [alert show];
+ });
+ }];
+ });
+}
+
+
+
+// =============================================================================
+#pragma mark - Timer Handler
+
+- (void)timerHandler:(NSTimer *)timer {
+
+ NSTimeInterval current = [[NSDate date] timeIntervalSince1970];
+ NSTimeInterval recorded = current - startTime;
+
+ self.statusLabel.text = [NSString stringWithFormat:@"%.2f", recorded];
+}
+
+
+
+// =============================================================================
+#pragma mark - AVCaptureManagerDeleagte
+
+- (void)didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL error:(NSError *)error {
+
+ if (error) {
+ NSLog(@"error:%@", error);
+ return;
+ }
+
+ if (!isNeededToSave) {
+ return;
+ }
+
+ [self saveRecordedFile:outputFileURL];
+}
+
+
+// =============================================================================
+#pragma mark - IBAction
+
+- (IBAction)recButtonTapped:(id)sender {
+
+ // REC START
+ if (!self.captureManager.isRecording) {
+
+ // change UI
+ [self.recBtn setImage:self.recStopImage
+ forState:UIControlStateNormal];
+ self.fpsControl.enabled = NO;
+
+ // timer start
+ startTime = [[NSDate date] timeIntervalSince1970];
+ self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01
+ target:self
+ selector:@selector(timerHandler:)
+ userInfo:nil
+ repeats:YES];
+
+ [self.captureManager startRecording];
+ }
+ // REC STOP
+ else {
+
+ isNeededToSave = YES;
+ [self.captureManager stopRecording];
+
+ [self.timer invalidate];
+ self.timer = nil;
+
+ // change UI
+ [self.recBtn setImage:self.recStartImage
+ forState:UIControlStateNormal];
+ self.fpsControl.enabled = YES;
+ }
+}
+
+- (IBAction)fpsChanged:(UISegmentedControl *)sender {
+
+ // Switch FPS
+
+ CGFloat desiredFps = 0.0;;
+ switch (self.fpsControl.selectedSegmentIndex) {
+ case 0:
+ default:
+ {
+ break;
+ }
+ case 1:
+ desiredFps = 60.0;
+ break;
+ case 2:
+ desiredFps = 120.0;
+ break;
+ }
+
+
+ [SVProgressHUD showWithStatus:@"Switching..."
+ maskType:SVProgressHUDMaskTypeGradient];
+
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(queue, ^{
+
+ if (desiredFps > 0.0) {
+ [self.captureManager switchFormatWithDesiredFPS:desiredFps];
+ }
+ else {
+ [self.captureManager resetFormat];
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+
+ if (desiredFps > 30.0) {
+ self.outerImageView.image = self.outerImage2;
+ }
+ else {
+ self.outerImageView.image = self.outerImage1;
+ }
+ [SVProgressHUD dismiss];
+ });
+ });
+}
+
+@end
diff --git a/iOS7Sampler/SampleViewControllers/SloMoVideoRecordViewController.xib b/iOS7Sampler/SampleViewControllers/SloMoVideoRecordViewController.xib
new file mode 100644
index 0000000..2abca39
--- /dev/null
+++ b/iOS7Sampler/SampleViewControllers/SloMoVideoRecordViewController.xib
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vendor/AVCaptureManager/AVCaptureManager.h b/vendor/AVCaptureManager/AVCaptureManager.h
new file mode 100644
index 0000000..4cc59ea
--- /dev/null
+++ b/vendor/AVCaptureManager/AVCaptureManager.h
@@ -0,0 +1,31 @@
+//
+// AVCaptureManager.h
+// SlowMotionVideoRecorder
+// https://github.com/shu223/SlowMotionVideoRecorder
+//
+// Created by shuichi on 12/17/13.
+// Copyright (c) 2013 Shuichi Tsutsumi. All rights reserved.
+//
+
+#import
+
+
+@protocol AVCaptureManagerDelegate
+- (void)didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
+ error:(NSError *)error;
+@end
+
+
+@interface AVCaptureManager : NSObject
+
+@property (nonatomic, assign) id delegate;
+@property (nonatomic, readonly) BOOL isRecording;
+
+- (id)initWithPreviewView:(UIView *)previewView;
+- (void)toggleContentsGravity;
+- (void)resetFormat;
+- (void)switchFormatWithDesiredFPS:(CGFloat)desiredFPS;
+- (void)startRecording;
+- (void)stopRecording;
+
+@end
diff --git a/vendor/AVCaptureManager/AVCaptureManager.m b/vendor/AVCaptureManager/AVCaptureManager.m
new file mode 100644
index 0000000..48f484e
--- /dev/null
+++ b/vendor/AVCaptureManager/AVCaptureManager.m
@@ -0,0 +1,202 @@
+//
+// AVCaptureManager.m
+// SlowMotionVideoRecorder
+// https://github.com/shu223/SlowMotionVideoRecorder
+//
+// Created by shuichi on 12/17/13.
+// Copyright (c) 2013 Shuichi Tsutsumi. All rights reserved.
+//
+
+#import "AVCaptureManager.h"
+#import
+
+
+@interface AVCaptureManager ()
+
+{
+ CMTime defaultVideoMaxFrameDuration;
+}
+@property (nonatomic, strong) AVCaptureSession *captureSession;
+@property (nonatomic, strong) AVCaptureMovieFileOutput *fileOutput;
+@property (nonatomic, strong) AVCaptureDeviceFormat *defaultFormat;
+@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
+@end
+
+
+@implementation AVCaptureManager
+
+- (id)initWithPreviewView:(UIView *)previewView {
+
+ self = [super init];
+
+ if (self) {
+
+ NSError *error;
+
+ self.captureSession = [[AVCaptureSession alloc] init];
+ self.captureSession.sessionPreset = AVCaptureSessionPresetInputPriority;
+
+ AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+ AVCaptureDeviceInput *videoIn = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
+
+ if (error) {
+ NSLog(@"Video input creation failed");
+ return nil;
+ }
+
+ if (![self.captureSession canAddInput:videoIn]) {
+ NSLog(@"Video input add-to-session failed");
+ return nil;
+ }
+ [self.captureSession addInput:videoIn];
+
+
+ // save the default format
+ self.defaultFormat = videoDevice.activeFormat;
+ defaultVideoMaxFrameDuration = videoDevice.activeVideoMaxFrameDuration;
+
+
+ AVCaptureDevice *audioDevice= [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
+ AVCaptureDeviceInput *audioIn = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
+ [self.captureSession addInput:audioIn];
+
+ self.fileOutput = [[AVCaptureMovieFileOutput alloc] init];
+ [self.captureSession addOutput:self.fileOutput];
+
+
+ self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
+ self.previewLayer.frame = previewView.bounds;
+ self.previewLayer.contentsGravity = kCAGravityResizeAspectFill;
+ self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
+ [previewView.layer insertSublayer:self.previewLayer atIndex:0];
+
+ [self.captureSession startRunning];
+ }
+ return self;
+}
+
+
+
+// =============================================================================
+#pragma mark - Public
+
+- (void)toggleContentsGravity {
+
+ if ([self.previewLayer.videoGravity isEqualToString:AVLayerVideoGravityResizeAspectFill]) {
+
+ self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspect;
+ }
+ else {
+ self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
+ }
+}
+
+- (void)resetFormat {
+
+ BOOL isRunning = self.captureSession.isRunning;
+
+ if (isRunning) {
+ [self.captureSession stopRunning];
+ }
+
+ AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+ [videoDevice lockForConfiguration:nil];
+ videoDevice.activeFormat = self.defaultFormat;
+ videoDevice.activeVideoMaxFrameDuration = defaultVideoMaxFrameDuration;
+ [videoDevice unlockForConfiguration];
+
+ if (isRunning) {
+ [self.captureSession startRunning];
+ }
+}
+
+- (void)switchFormatWithDesiredFPS:(CGFloat)desiredFPS
+{
+ BOOL isRunning = self.captureSession.isRunning;
+
+ if (isRunning) [self.captureSession stopRunning];
+
+ AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
+ AVCaptureDeviceFormat *selectedFormat = nil;
+ int32_t maxWidth = 0;
+ AVFrameRateRange *frameRateRange = nil;
+
+ for (AVCaptureDeviceFormat *format in [videoDevice formats]) {
+
+ for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
+
+ CMFormatDescriptionRef desc = format.formatDescription;
+ CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(desc);
+ int32_t width = dimensions.width;
+
+ if (range.minFrameRate <= desiredFPS && desiredFPS <= range.maxFrameRate && width >= maxWidth) {
+
+ selectedFormat = format;
+ frameRateRange = range;
+ maxWidth = width;
+ }
+ }
+ }
+
+ if (selectedFormat) {
+
+ if ([videoDevice lockForConfiguration:nil]) {
+
+ NSLog(@"selected format:%@", selectedFormat);
+ videoDevice.activeFormat = selectedFormat;
+ videoDevice.activeVideoMinFrameDuration = CMTimeMake(1, (int32_t)desiredFPS);
+ videoDevice.activeVideoMaxFrameDuration = CMTimeMake(1, (int32_t)desiredFPS);
+ [videoDevice unlockForConfiguration];
+ }
+ }
+
+ if (isRunning) [self.captureSession startRunning];
+}
+
+- (void)startRecording {
+
+ NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
+ [formatter setDateFormat:@"yyyy-MM-dd-HH-mm-ss"];
+ NSString* dateTimePrefix = [formatter stringFromDate:[NSDate date]];
+
+ int fileNamePostfix = 0;
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *documentsDirectory = [paths objectAtIndex:0];
+ NSString *filePath = nil;
+ do
+ filePath =[NSString stringWithFormat:@"/%@/%@-%i.mp4", documentsDirectory, dateTimePrefix, fileNamePostfix++];
+ while ([[NSFileManager defaultManager] fileExistsAtPath:filePath]);
+
+ NSURL *fileURL = [NSURL URLWithString:[@"file://" stringByAppendingString:filePath]];
+ [self.fileOutput startRecordingToOutputFileURL:fileURL recordingDelegate:self];
+}
+
+- (void)stopRecording {
+
+ [self.fileOutput stopRecording];
+}
+
+
+// =============================================================================
+#pragma mark - AVCaptureFileOutputRecordingDelegate
+
+- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
+ didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
+ fromConnections:(NSArray *)connections
+{
+ _isRecording = YES;
+}
+
+- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
+ didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
+ fromConnections:(NSArray *)connections error:(NSError *)error
+{
+// [self saveRecordedFile:outputFileURL];
+ _isRecording = NO;
+
+ if ([self.delegate respondsToSelector:@selector(didFinishRecordingToOutputFileAtURL:error:)]) {
+ [self.delegate didFinishRecordingToOutputFileAtURL:outputFileURL error:error];
+ }
+}
+
+@end