motion-capture 1.3.1 → 1.4.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.
Files changed (4) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +37 -4
  3. data/lib/project/motion-capture.rb +140 -22
  4. metadata +7 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a40129aa5ea8c985e6089e6db40906b56f00bb3c
4
- data.tar.gz: 5b4726c722107daad383f37b2e520418beb3a5fa
2
+ SHA256:
3
+ metadata.gz: ee51432dacdc318b8b687203b598709f6d69e549709f43390628e6ab6b4f6b7b
4
+ data.tar.gz: 7df7fe7e9d0fc9fa21cf315edd714213991e75e4ba34cbc0f27d0a83e983ec79
5
5
  SHA512:
6
- metadata.gz: 473b932901836198ddc185c257435834fccfd9a709e3d6d47a5fc048ac18c725812da2b5d4df88d142263787be86fcd29f54bf50f1700b507eb4b918f53d211f
7
- data.tar.gz: 988ce6e872578fe0df289dd76543a3bfb87de881d36e0c7a31b2481aad65a6f6ad29facc9c30f4487ba2c1e84a892abd8e0bb4f16998af41fd0fd314de64219b
6
+ metadata.gz: 89a3887fa974c95d0e2d37a3c36b90f842bc8d52d0f5e3a504df3e307759ca7bd2a62b7c7115305bc55b36d084955ce4a481cbc3292d0dc8bc47bcf5de22ef1c
7
+ data.tar.gz: 61c711ba4823f72564e0d21f86d21f4a8fc73f5846f0ffa9f6a9c0eb8c7b9aacbb11c2b1cdd2442b47f6a4a64f8b92353453ac180f4881d437eecf024f878da5
data/README.md CHANGED
@@ -7,9 +7,9 @@ Camera support for custom camera controllers
7
7
  ``` ruby
8
8
  motion_capture = Motion::Capture.new
9
9
  motion_capture = Motion::Capture.new(device: :front) # specify camera
10
+ motion_capture = Motion::Capture.new(preset: AVCaptureSessionPreset640x480) # specify a different preset (defaults to high resolution photo)
10
11
 
11
- preview = motion_capture.capture_preview_view(frame: view.bounds)
12
- view.addSubview(preview) # UIView containing AVCaptureVideoPreviewLayer
12
+ motion_capture.attach(view) # apply a AVCaptureVideoPreviewLayer to the specified view
13
13
 
14
14
  motion_capture.toggle_camera # Switch between front/rear cameras
15
15
  motion_capture.toggle_flash # Switch bettwen flash on/off
@@ -21,15 +21,34 @@ motion_capture.use_camera(:default)
21
21
  motion_capture.use_camera(:front)
22
22
  motion_capture.use_camera(:rear)
23
23
 
24
+ # When you're ready to start the capture session:
25
+ motion_capture.start!
26
+
27
+ # Capturing Single Photos
28
+
24
29
  motion_capture.capture do |image_data|
25
30
  # Use NSData
26
31
  end
27
32
 
28
- motion_capture.capture_and_save do |asset_url|
29
- # Use NSURL
33
+ motion_capture.capture_image do |image|
34
+ # Use UIImage
35
+ end
36
+
37
+ # Saving captured images to the Photos library
38
+
39
+ motion_capture.capture_and_save do |image_data, asset_url|
40
+ # Use NSData and NSURL
41
+ end
42
+
43
+ motion_capture.capture_image_and_save do |image, asset_url|
44
+ # Use UIImage and NSURL
30
45
  end
46
+
47
+ # When you're done using the camera and are ready to stop the capture session:
48
+ motion_capture.stop!
31
49
  ```
32
50
 
51
+
33
52
  ## Setup
34
53
 
35
54
  Add this line to your application's Gemfile:
@@ -44,6 +63,20 @@ Or install it yourself as:
44
63
 
45
64
  $ gem install motion-capture
46
65
 
66
+ Add the necessary frameworks to your app configuration in your Rakefile:
67
+
68
+ app.frameworks << 'AVFoundation'
69
+ app.frameworks << 'Photos' # if you will be saving to the Photo library and targeting iOS 8+
70
+ app.frameworks << 'AssetsLibrary' # if you will be targeting iOS 4-7
71
+
72
+ Then update your app configuration in your Rakefile to specify the message that will be displayed when asking the user for permission to use the camera:
73
+
74
+ app.info_plist['NSCameraUsageDescription'] = 'Camera will be used for taking your profile photo.'
75
+
76
+ If you will be saving photos to the Photo Library, you will also need to specify the message that will be displayed to the user:
77
+
78
+ app.info_plist['NSPhotoLibraryUsageDescription'] = 'Photos taken will be saved to your library.'
79
+
47
80
  ## Contributing
48
81
 
49
82
  1. Fork it
@@ -8,18 +8,55 @@ module Motion; class Capture
8
8
  @options = options
9
9
  end
10
10
 
11
- def on_error(block)
11
+ def on_error(&block)
12
12
  @error_callback = block
13
13
  end
14
14
 
15
- def start!(preset = AVCaptureSessionPresetPhoto)
15
+ def start!
16
+ return if session.running?
17
+ if defined?(AVCapturePhotoOutput) # iOS 10+
18
+ @starting = true
19
+ authorize_camera do |success|
20
+ if success
21
+ Dispatch::Queue.new('motion-capture').async do
22
+ configure_session
23
+ session.startRunning
24
+ @starting = false
25
+ end
26
+ else
27
+ @starting = false
28
+ end
29
+ end
30
+ else # iOS 4-9
31
+ configure_session
32
+ session.startRunning
33
+ end
34
+ end
35
+
36
+ def authorize_camera(&block)
37
+ AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: -> (success) {
38
+ block.call(success)
39
+ })
40
+ end
41
+
42
+ def configure_session
43
+ session.beginConfiguration
44
+
45
+ set_preset(options.fetch(:preset, AVCaptureSessionPresetPhoto))
46
+
16
47
  use_camera(options.fetch(:device, :default))
17
48
 
18
- set_preset(preset)
49
+ if defined?(AVCapturePhotoOutput) # iOS 10+
50
+ add_output(photo_output)
51
+ else # iOS 4-9
52
+ add_output(still_image_output)
53
+ end
19
54
 
20
- add_ouput(still_image_output)
55
+ session.commitConfiguration
56
+ end
21
57
 
22
- session.startRunning
58
+ def running?
59
+ @session && session.running?
23
60
  end
24
61
 
25
62
  def stop!
@@ -31,30 +68,66 @@ module Motion; class Capture
31
68
  preview_layer.removeFromSuperlayer if preview_layer && preview_layer.superlayer
32
69
 
33
70
  @still_image_output = nil
71
+ @photo_output = nil
34
72
  @session = nil
35
73
  @preview_layer = nil
36
74
  end
37
75
 
38
- def running?
39
- @session && session.running?
76
+ def capture(&block)
77
+ if defined?(AVCapturePhotoOutput) # iOS 10+
78
+ Dispatch::Queue.new('motion-capture').async do
79
+ ensure_running_session do
80
+ update_video_orientation!
81
+ @capture_callback = block
82
+ capture_settings = AVCapturePhotoSettings.photoSettingsWithFormat(AVVideoCodecKey => AVVideoCodecJPEG)
83
+ photo_output.capturePhotoWithSettings(capture_settings, delegate: self)
84
+ end
85
+ end
86
+ else # iOS 4-9
87
+ still_image_output.captureStillImageAsynchronouslyFromConnection(still_image_connection, completionHandler: -> (buffer, error) {
88
+ if error
89
+ error_callback.call(error)
90
+ else
91
+ image_data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
92
+ block.call(image_data)
93
+ end
94
+ })
95
+ end
40
96
  end
41
97
 
42
- def capture(&block)
43
- still_image_output.captureStillImageAsynchronouslyFromConnection(still_image_connection, completionHandler: -> (buffer, error) {
44
- if error
45
- error_callback.call(error)
46
- else
47
- image_data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
98
+ def ensure_running_session(&block)
99
+ start! unless @starting || session.running?
100
+ while @starting || !session.running?
101
+ # wait for session to start...
102
+ end
103
+ block.call
104
+ end
48
105
 
49
- block.call(image_data)
50
- end
51
- })
106
+ # iOS 11+ AVCapturePhotoCaptureDelegate method
107
+ def captureOutput(output, didFinishProcessingPhoto: photo, error: error)
108
+ if error
109
+ error_callback.call(error)
110
+ else
111
+ @capture_callback.call(photo.fileDataRepresentation)
112
+ end
113
+ end
114
+
115
+ # iOS 10 AVCapturePhotoCaptureDelegate method
116
+ def captureOutput(output, didFinishProcessingPhotoSampleBuffer: photo_sample_buffer, previewPhotoSampleBuffer: preview_photo_sample_buffer, resolvedSettings: resolved_settings, bracketSettings: bracket_settings, error: error)
117
+ if error
118
+ error_callback.call(error)
119
+ else
120
+ jpeg_data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(
121
+ forJPEGSampleBuffer: photo_sample_buffer,
122
+ previewPhotoSampleBuffer: preview_photo_sample_buffer
123
+ )
124
+ @capture_callback.call(jpeg_data)
125
+ end
52
126
  end
53
127
 
54
128
  def capture_image(&block)
55
129
  capture do |jpeg_data|
56
130
  image = UIImage.imageWithData(jpeg_data)
57
-
58
131
  block.call(image)
59
132
  end
60
133
  end
@@ -71,18 +144,40 @@ module Motion; class Capture
71
144
  capture do |jpeg_data|
72
145
  save_data(jpeg_data) do |asset_url|
73
146
  image = UIImage.imageWithData(jpeg_data)
74
-
75
147
  block.call(image, asset_url)
76
148
  end
77
149
  end
78
150
  end
79
151
 
80
152
  def save_data(jpeg_data, &block)
153
+ if defined?(PHPhotoLibrary) # iOS 8+
154
+ save_to_photo_library(jpeg_data, &block)
155
+ else # iOS 4-8
156
+ save_to_assets_library(jpeg_data, &block)
157
+ end
158
+ end
159
+
160
+ # iOS 4-8
161
+ def save_to_assets_library(jpeg_data, &block)
81
162
  assets_library.writeImageDataToSavedPhotosAlbum(jpeg_data, metadata: nil, completionBlock: -> (asset_url, error) {
82
163
  error ? error_callback.call(error) : block.call(asset_url)
83
164
  })
84
165
  end
85
166
 
167
+ # iOS 8+
168
+ def save_to_photo_library(jpeg_data, &block)
169
+ photo_library.performChanges(-> {
170
+ image = UIImage.imageWithData(jpeg_data)
171
+ PHAssetChangeRequest.creationRequestForAssetFromImage(image)
172
+ }, completionHandler: -> (success, error) {
173
+ if error
174
+ error_callback.call(error)
175
+ else
176
+ block.call(nil) # asset url is not returned in completion block
177
+ end
178
+ })
179
+ end
180
+
86
181
  def attach(view, options = {})
87
182
  @preview_layer = preview_layer_for_view(view, options)
88
183
 
@@ -124,13 +219,14 @@ module Motion; class Capture
124
219
  end
125
220
 
126
221
  def preset
127
- session.captureSession if @session
222
+ session.sessionPreset if @session
128
223
  end
129
224
 
130
225
  def flash
131
226
  device.flashMode if @device
132
227
  end
133
228
 
229
+ # iOS 4-9
134
230
  def set_flash(mode = :auto)
135
231
  configure_with_lock { device.flashMode = FLASH_MODES[mode] } if flash_mode_available?(mode)
136
232
  end
@@ -145,6 +241,7 @@ module Motion; class Capture
145
241
  @error_callback ||= -> (error) { p "An error occurred: #{error.localizedDescription}." }
146
242
  end
147
243
 
244
+ # iOS 4-9
148
245
  def still_image_connection
149
246
  still_image_output.connectionWithMediaType(AVMediaTypeVideo).tap do |connection|
150
247
  device_orientation = UIDevice.currentDevice.orientation
@@ -154,6 +251,16 @@ module Motion; class Capture
154
251
  end
155
252
  end
156
253
 
254
+ # iOS 10+
255
+ def update_video_orientation!
256
+ photo_output.connectionWithMediaType(AVMediaTypeVideo).tap do |connection|
257
+ device_orientation = UIDevice.currentDevice.orientation
258
+ video_orientation = orientation_mapping.fetch(device_orientation, AVCaptureVideoOrientationPortrait)
259
+
260
+ connection.setVideoOrientation(video_orientation) if connection.videoOrientationSupported?
261
+ end
262
+ end
263
+
157
264
  def orientation_mapping
158
265
  { UIDeviceOrientationPortrait => AVCaptureVideoOrientationPortrait,
159
266
  UIDeviceOrientationPortraitUpsideDown => AVCaptureVideoOrientationPortraitUpsideDown,
@@ -161,10 +268,16 @@ module Motion; class Capture
161
268
  UIDeviceOrientationLandscapeLeft => AVCaptureVideoOrientationLandscapeRight }
162
269
  end
163
270
 
271
+ # iOS 4-8
164
272
  def assets_library
165
273
  @assets_library ||= ALAssetsLibrary.alloc.init
166
274
  end
167
275
 
276
+ # iOS 8+
277
+ def photo_library
278
+ @photo_library ||= PHPhotoLibrary.sharedPhotoLibrary
279
+ end
280
+
168
281
  def configure_with_lock(&block)
169
282
  error_pointer = Pointer.new(:object)
170
283
 
@@ -213,7 +326,7 @@ module Motion; class Capture
213
326
  session.addInput(input) if session.canAddInput(input)
214
327
  end
215
328
 
216
- def add_ouput(output)
329
+ def add_output(output)
217
330
  session.addOutput(output) if session.canAddOutput(output)
218
331
  end
219
332
 
@@ -260,11 +373,16 @@ module Motion; class Capture
260
373
  @session ||= AVCaptureSession.alloc.init
261
374
  end
262
375
 
376
+ # iOS 4-9
263
377
  def still_image_output
264
378
  @still_image_output ||= AVCaptureStillImageOutput.alloc.init.tap do |output|
265
- settings = { 'AVVideoCodeKey' => AVVideoCodecJPEG }
266
-
379
+ settings = { 'AVVideoCodecKey' => AVVideoCodecJPEG }
267
380
  output.setOutputSettings(settings)
268
381
  end
269
382
  end
383
+
384
+ # iOS 10+
385
+ def photo_output
386
+ @photo_output ||= AVCapturePhotoOutput.alloc.init
387
+ end
270
388
  end; end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion-capture
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Devon Blandin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-29 00:00:00.000000000 Z
11
+ date: 2018-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  description: Easily create custom camera controllers
@@ -44,17 +44,17 @@ require_paths:
44
44
  - lib
45
45
  required_ruby_version: !ruby/object:Gem::Requirement
46
46
  requirements:
47
- - - '>='
47
+ - - ">="
48
48
  - !ruby/object:Gem::Version
49
49
  version: '0'
50
50
  required_rubygems_version: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  requirements: []
56
56
  rubyforge_project:
57
- rubygems_version: 2.0.3
57
+ rubygems_version: 2.7.6
58
58
  signing_key:
59
59
  specification_version: 4
60
60
  summary: RubyMotion AVFoundation wrapper