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,14 @@
1
+ .repl_history
2
+ build
3
+ tags
4
+ resources/*.nib
5
+ resources/*.momd
6
+ resources/*.storyboardc
7
+ .DS_Store
8
+ nbproject
9
+ .redcar
10
+ #*#
11
+ *~
12
+ *.sw[po]
13
+ .eprj
14
+ /.dat*
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/AVClub/version.rb', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = 'AVClub'
6
+ gem.version = AVClub::VERSION
7
+
8
+ gem.authors = ['the rubymotion community']
9
+
10
+ gem.description = <<-DESC
11
+ Setup an AVCaptureSession with video, multiple cameras, and still image
12
+ capabilities. You can also easily have touch-to-focus and flash-on-picture
13
+ features.
14
+ DESC
15
+
16
+ gem.summary = 'A wrapper for AVFoundation to make it easy to implement a custom camera view.'
17
+ gem.homepage = 'https://github.com/rubymotion/AVClub'
18
+
19
+ gem.files = `git ls-files`.split($\)
20
+ gem.require_paths = ['lib']
21
+ gem.test_files = gem.files.grep(%r{^spec/})
22
+
23
+ gem.add_dependency 'rake'
24
+ gem.add_development_dependency 'rspec'
25
+
26
+ end
@@ -0,0 +1,56 @@
1
+ I've been pouring over the `AVCam` sample code from Apple, and I think I've
2
+ finally gotten my head around it enough to generalize it and, I hope, repackage
3
+ it for our benefit!
4
+
5
+ You can setup an AVCaptureSession with video, multiple cameras, and still image
6
+ capabilities. You can also easily have touch-to-focus and flash-on-picture
7
+ features. Just by implementing a few actions and delegate methods. I'm calling
8
+ it "AVClub".
9
+
10
+ Really, the code is largely unchanged from the sample code - if you've seen it,
11
+ I just moved more code into the Manager class, and renamed "AVCamManager" to
12
+ "AVClub", since that's the central "wrapper" class.
13
+
14
+ This tool - and AVFoundation in general - is much more low level than the
15
+ UIImagePickerController (see Camera Programming Topics for iOS). If you're
16
+ looking for an easy off-the-shelf solution, use BW::Camera or an instance of
17
+ UIImagePickerController.
18
+
19
+ Working with AVFoundation is like holding a dozen loose wires, plugging them all
20
+ into each other, and hoping that a photo or video comes out the end. If it goes
21
+ wrong, lemme know.
22
+
23
+
24
+ The basic process is this:
25
+
26
+ Create a view for where you want the camera to appear. Or don't, it's optional.
27
+ If you want to take a picture using the front camera with no preview, you can
28
+ do it! (creepy! :-P)
29
+
30
+ 1. Create a "club" - `AVClub.new`.
31
+ 2. Assign your controller as the delegate - `club.delegate = self`.
32
+ 3. and when you're ready - `startInView(viewfinder_view)`. You can start and
33
+ stop the session by calling `club.stopSession`
34
+
35
+ ```ruby
36
+ def viewDidLoad
37
+ @video_view = UIView.alloc.initWithFrame([[10, 10], [100, 100]]) # an AVCaptureVideoPreviewLayer will be added to this view
38
+ club = AVClub.new
39
+ club.delegate = self
40
+ club.startInView(@video_view)
41
+ end
42
+ ```
43
+
44
+
45
+ For convenience, there is an included `AVClubController` class that adds two
46
+ methods you can use or refer to:
47
+
48
+ ```ruby
49
+ # this method creates the club, assigns it to self.club, and assigns the
50
+ # viewFinderView that you pass to self.viewFinderView
51
+ def startInView(view)
52
+
53
+ # call this in willRotateToInterfaceOrientation(toInterfaceOrientation,
54
+ # duration:duration) and pass the new camera frame
55
+ def rotateCameraTo(new_frame, orientation:toInterfaceOrientation, duration:duration)
56
+ ```
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+ $:.unshift("/Library/RubyMotion/lib")
3
+ require 'motion/project'
4
+
5
+
6
+ Motion::Project::App.setup do |app|
7
+ # Use `rake config' to see complete project settings.
8
+ app.name = 'AVClub-Demo'
9
+ app.files.insert(0, 'lib/AVClub/AVClubController.rb')
10
+ # app.files_dependencies 'app/camera_controller.rb' => 'lib/AVClub/AVClubController.rb'
11
+
12
+ app.vendor_project('vendor/AVClub', :xcode)
13
+ # app.detect_dependencies = false
14
+ app.frameworks.concat [
15
+ 'MediaPlayer',
16
+ 'QuartzCore',
17
+ 'CoreVideo',
18
+ 'CoreMedia',
19
+ 'AssetsLibrary',
20
+ 'MobileCoreServices',
21
+ 'AVFoundation',
22
+ ]
23
+ end
@@ -0,0 +1,8 @@
1
+ class AppDelegate
2
+ def application(application, didFinishLaunchingWithOptions:launchOptions)
3
+ @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
4
+ @window.rootViewController = CameraController.new
5
+ @window.makeKeyAndVisible
6
+ true
7
+ end
8
+ end
@@ -0,0 +1,193 @@
1
+ class CameraController < AVClubController
2
+ attr_accessor :record_button, :still_button, :toggle_button;
3
+
4
+ def viewDidLoad
5
+ super
6
+ self.view.backgroundColor = UIColor.darkGrayColor
7
+
8
+ self.viewFinderView = UIView.alloc.initWithFrame(UIEdgeInsetsInsetRect(self.view.bounds, [50, 20, 20, 20]))
9
+ self.viewFinderView.autoresizingMask = UIViewAutoresizingFlexibleHeight |
10
+ UIViewAutoresizingFlexibleWidth
11
+ self.viewFinderView.backgroundColor = UIColor.lightGrayColor
12
+ self.view.addSubview(self.viewFinderView)
13
+
14
+ ### important ###
15
+ startInView(self.viewFinderView)
16
+
17
+ width = 0
18
+ height = 0
19
+ self.toggle_button = UIButton.buttonWithType(UIButtonTypeRoundedRect).tap do |button|
20
+ button.setTitle('Camera', forState:UIControlStateNormal)
21
+ button.sizeToFit
22
+ width += CGRectGetWidth(button.frame)
23
+ height = CGRectGetHeight(button.frame)
24
+
25
+ button.addTarget(self, action: 'toggleCamera:', forControlEvents:UIControlEventTouchUpInside)
26
+ end
27
+ width += 10
28
+
29
+ self.record_button = UIButton.buttonWithType(UIButtonTypeRoundedRect).tap do |button|
30
+ button.setTitle('Record', forState:UIControlStateNormal)
31
+ button.sizeToFit
32
+ width += CGRectGetWidth(button.frame)
33
+
34
+ button.addTarget(self, action: 'toggleRecording:', forControlEvents:UIControlEventTouchUpInside)
35
+ end
36
+ width += 10
37
+
38
+ self.still_button = UIButton.buttonWithType(UIButtonTypeRoundedRect).tap do |button|
39
+ button.setTitle('Photo', forState:UIControlStateNormal)
40
+ button.sizeToFit
41
+ width += CGRectGetWidth(button.frame)
42
+
43
+ button.addTarget(self, action: 'captureStillImage:', forControlEvents:UIControlEventTouchUpInside)
44
+ end
45
+
46
+ left = (CGRectGetWidth(self.view.frame) - width) / 2.0
47
+ top = 5
48
+ buttons_view = UIView.alloc.initWithFrame([[left, top], [width, height]])
49
+ buttons_view.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |
50
+ UIViewAutoresizingFlexibleTopMargin |
51
+ UIViewAutoresizingFlexibleRightMargin |
52
+ UIViewAutoresizingFlexibleBottomMargin
53
+
54
+ left = 0
55
+ top = 0
56
+ self.toggle_button.frame = [[left, top], self.toggle_button.frame.size]
57
+ left += CGRectGetWidth(self.toggle_button.frame) + 10
58
+ self.record_button.frame = [[left, top], self.record_button.frame.size]
59
+ left += CGRectGetWidth(self.record_button.frame) + 10
60
+ self.still_button.frame = [[left, top], self.still_button.frame.size]
61
+ left += CGRectGetWidth(self.still_button.frame) + 10
62
+
63
+ buttons_view.addSubview(self.toggle_button)
64
+ buttons_view.addSubview(self.record_button)
65
+ buttons_view.addSubview(self.still_button)
66
+
67
+ self.view.addSubview(buttons_view)
68
+
69
+ self.update_button_states
70
+
71
+ # Add a single tap gesture to focus on the point tapped, then lock focus
72
+ singleTap = UITapGestureRecognizer.alloc.initWithTarget(self, action:'tapToAutoFocus:')
73
+ singleTap.setDelegate(self)
74
+ singleTap.setNumberOfTapsRequired(1)
75
+ self.viewFinderView.addGestureRecognizer(singleTap)
76
+ end
77
+
78
+ # Auto focus at a particular point. The focus mode will change to locked once
79
+ # the auto focus happens.
80
+ def tapToAutoFocus(gestureRecognizer)
81
+ return unless club.videoInput
82
+
83
+ if club.videoInput.device.isFocusPointOfInterestSupported
84
+ tapPoint = gestureRecognizer.locationInView(viewFinderView)
85
+ convertedFocusPoint = club.convertToPointOfInterestFromViewCoordinates(tapPoint)
86
+ club.autoFocusAtPoint(convertedFocusPoint)
87
+ end
88
+ end
89
+
90
+ # Change to continuous auto focus. The camera will constantly focus at the
91
+ # point choosen.
92
+ def tapToContinouslyAutoFocus(gestureRecognizer)
93
+ return unless club.videoInput
94
+
95
+ if club.videoInput.device.isFocusPointOfInterestSupported
96
+ club.continuousFocusAtPoint(CGPoint.new(0.5, 0.5))
97
+ end
98
+ end
99
+
100
+ def toggleCamera(sender)
101
+ # Toggle between cameras when there is more than one
102
+ club.toggleCamera
103
+
104
+ # Do an initial focus
105
+ club.continuousFocusAtPoint(CGPoint.new(0.5, 0.5))
106
+ end
107
+
108
+ def toggleRecording(sender)
109
+ # Start recording if there isn't a recording running. Stop recording if there is.
110
+ record_button.setEnabled(false)
111
+ unless club.recorder.isRecording
112
+ club.startRecording
113
+ else
114
+ club.stopRecording
115
+ end
116
+ end
117
+
118
+ def captureStillImage(sender)
119
+ return unless still_button.isEnabled
120
+
121
+ # Capture a still image
122
+ still_button.setEnabled(false)
123
+ club.captureStillImageAnimated(true)
124
+ end
125
+
126
+ def update_button_states
127
+ if club.cameraCount > 1
128
+ self.toggle_button.enabled = true
129
+ self.record_button.enabled = true
130
+ self.still_button.enabled = true
131
+ else
132
+ self.toggle_button.enabled = false
133
+
134
+ if club.cameraCount > 0
135
+ self.record_button.enabled = true
136
+ self.still_button.enabled = true
137
+ else
138
+ self.still_button.enabled = false
139
+
140
+ if club.micCount > 0
141
+ self.record_button = true
142
+ else
143
+ self.record_button = false
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ def clubRecordingBegan(club)
150
+ self.record_button.setTitle('Stop', forState:UIControlStateNormal)
151
+ update_button_states
152
+ end
153
+
154
+ def clubRecordingFinished(club)
155
+ self.record_button.setTitle('Record', forState:UIControlStateNormal)
156
+ update_button_states
157
+ end
158
+
159
+ def club(club, stillImageCaptured:image, error:error)
160
+ if image
161
+ club.saveImageToLibrary(image)
162
+ else
163
+ update_button_states
164
+ end
165
+ end
166
+
167
+ def club(club, assetSavedToURL:image, error:error)
168
+ update_button_states
169
+ end
170
+
171
+ def clubDeviceConfigurationChanged(club)
172
+ update_button_states
173
+ end
174
+
175
+ def willRotateToInterfaceOrientation(toInterfaceOrientation, duration:duration)
176
+ super
177
+
178
+ case toInterfaceOrientation
179
+ when UIInterfaceOrientationLandscapeLeft
180
+ new_frame = CGRect.new([0, 0], [480, 320])
181
+ when UIInterfaceOrientationLandscapeRight
182
+ new_frame = CGRect.new([0, 0], [480, 320])
183
+ when UIInterfaceOrientationPortrait
184
+ new_frame = CGRect.new([0, 0], [320, 480])
185
+ when UIInterfaceOrientationPortraitUpsideDown
186
+ new_frame = CGRect.new([0, 0], [320, 480])
187
+ end
188
+
189
+ ### important ###
190
+ rotateCameraTo(new_frame, orientation:toInterfaceOrientation, duration:duration)
191
+ end
192
+
193
+ end
@@ -0,0 +1,69 @@
1
+ class AVClubController < UIViewController
2
+ attr_accessor :club
3
+ attr_accessor :viewFinderView
4
+
5
+ def startInView(view)
6
+ self.viewFinderView = view
7
+
8
+ unless club
9
+ self.club = AVClub.new
10
+ self.club.delegate = self
11
+ self.club.startInView(view)
12
+ end
13
+ end
14
+
15
+ def club(club, didFailWithError:error)
16
+ alertView = UIAlertView.alloc.initWithTitle(error.localizedDescription,
17
+ message:error.localizedFailureReason,
18
+ delegate:nil,
19
+ cancelButtonTitle:'OK',
20
+ otherButtonTitles:nil
21
+ )
22
+ alertView.show
23
+ end
24
+
25
+ def club(club, stillImageCaptured:image, error:error)
26
+ end
27
+
28
+ def club(club, assetSavedToURL:url, error:error)
29
+ end
30
+
31
+ def clubRecordingBegan(club)
32
+ end
33
+
34
+ def clubRecordingFinished(club)
35
+ end
36
+
37
+ def clubDeviceConfigurationChanged(club)
38
+ end
39
+
40
+ def rotateCameraTo(new_frame, orientation:toInterfaceOrientation, duration:duration)
41
+ return unless viewFinderView
42
+
43
+ captureVideoPreviewLayer = nil
44
+ viewFinderView.layer.sublayers.each do |layer|
45
+ if layer.is_a? AVCaptureVideoPreviewLayer
46
+ captureVideoPreviewLayer = layer
47
+ break
48
+ end
49
+ end
50
+ return unless captureVideoPreviewLayer
51
+
52
+ case toInterfaceOrientation
53
+ when UIInterfaceOrientationLandscapeLeft
54
+ rotation = Math::PI / 2
55
+ when UIInterfaceOrientationLandscapeRight
56
+ rotation = -Math::PI / 2
57
+ when UIInterfaceOrientationPortrait
58
+ rotation = 0
59
+ when UIInterfaceOrientationPortraitUpsideDown
60
+ rotation = 2 * Math::PI
61
+ end
62
+
63
+ captureVideoPreviewLayer.masksToBounds = true
64
+ UIView.animateWithDuration(duration, animations:lambda{
65
+ captureVideoPreviewLayer.frame = new_frame
66
+ captureVideoPreviewLayer.orientation = toInterfaceOrientation
67
+ })
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module AVClub
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,35 @@
1
+ unless defined?(Motion::Project::Config)
2
+ raise "The AVClub gem must be required within a RubyMotion project Rakefile."
3
+ end
4
+
5
+
6
+ Motion::Project::App.setup do |app|
7
+ # scans app.files until it finds app/ (the default)
8
+ # if found, it inserts just before those files, otherwise it will insert to
9
+ # the end of the list
10
+ insert_point = 0
11
+ app.files.each_index do |index|
12
+ file = app.files[index]
13
+ if file =~ /^(?:\.\/)?app\//
14
+ # found app/, so stop looking
15
+ break
16
+ end
17
+ insert_point = index + 1
18
+ end
19
+
20
+ Dir.glob(File.join(File.dirname(__FILE__), 'AVClub/**/*.rb')).reverse.each do |file|
21
+ app.files.insert(insert_point, file)
22
+ end
23
+
24
+ app.vendor_project(File.join(File.dirname(__FILE__), '../vendor/AVClub'), :xcode)
25
+
26
+ app.frameworks.concat [
27
+ 'MediaPlayer',
28
+ 'QuartzCore',
29
+ 'CoreVideo',
30
+ 'CoreMedia',
31
+ 'AssetsLibrary',
32
+ 'MobileCoreServices',
33
+ 'AVFoundation',
34
+ ]
35
+ end
@@ -0,0 +1,9 @@
1
+ describe "Application 'AVClub-demo'" do
2
+ before do
3
+ @app = UIApplication.sharedApplication
4
+ end
5
+
6
+ it "has one window" do
7
+ @app.windows.size.should == 1
8
+ end
9
+ end