rays 0.1.16 → 0.1.17
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.
- checksums.yaml +4 -4
- data/.doc/ext/rays/camera.cpp +86 -3
- data/VERSION +1 -1
- data/ext/rays/bitmap.cpp +1 -1
- data/ext/rays/camera.cpp +98 -6
- data/include/rays/camera.h +26 -1
- data/lib/rays/camera.rb +5 -2
- data/rays.gemspec +2 -2
- data/src/ios/bitmap.h +5 -3
- data/src/ios/bitmap.mm +9 -5
- data/src/ios/camera.mm +319 -45
- data/src/ios/helper.h +2 -2
- data/src/osx/bitmap.h +3 -1
- data/src/osx/bitmap.mm +9 -5
- data/src/osx/camera.mm +260 -45
- metadata +6 -6
data/src/ios/helper.h
CHANGED
data/src/osx/bitmap.h
CHANGED
data/src/osx/bitmap.mm
CHANGED
@@ -175,8 +175,12 @@ namespace Rays
|
|
175
175
|
}
|
176
176
|
|
177
177
|
void
|
178
|
-
|
178
|
+
Bitmap_draw_image (
|
179
|
+
Bitmap* bitmap, CGImageRef image,
|
180
|
+
coord x, coord y, coord width, coord height)
|
179
181
|
{
|
182
|
+
if (width == 0 || height == 0) return;
|
183
|
+
|
180
184
|
if (!bitmap || !image)
|
181
185
|
argument_error(__FILE__, __LINE__);
|
182
186
|
|
@@ -184,9 +188,9 @@ namespace Rays
|
|
184
188
|
if (!context)
|
185
189
|
rays_error(__FILE__, __LINE__, "getting CGContext failed.");
|
186
190
|
|
187
|
-
|
188
|
-
|
189
|
-
CGContextDrawImage(context, CGRectMake(
|
191
|
+
if (width < 0) width = (coord) CGImageGetWidth(image);
|
192
|
+
if (height < 0) height = (coord) CGImageGetHeight(image);
|
193
|
+
CGContextDrawImage(context, CGRectMake(x, y, width, height), image);
|
190
194
|
|
191
195
|
Bitmap_set_modified(bitmap);
|
192
196
|
}
|
@@ -265,7 +269,7 @@ namespace Rays
|
|
265
269
|
if (!bmp)
|
266
270
|
rays_error(__FILE__, __LINE__, "invalid bitmap.");
|
267
271
|
|
268
|
-
|
272
|
+
Bitmap_draw_image(&bmp, image);
|
269
273
|
return bmp;
|
270
274
|
}
|
271
275
|
|
data/src/osx/camera.mm
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
|
5
5
|
#import <AVFoundation/AVFoundation.h>
|
6
|
+
#include "rays/exception.h"
|
6
7
|
#include "bitmap.h"
|
7
8
|
|
8
9
|
|
@@ -16,9 +17,9 @@ static int video_input_queue_index = 0;
|
|
16
17
|
@implementation VideoInput
|
17
18
|
|
18
19
|
{
|
19
|
-
AVCaptureSession*
|
20
|
-
dispatch_queue_t
|
21
|
-
CGImageRef
|
20
|
+
AVCaptureSession* session;
|
21
|
+
dispatch_queue_t queue;
|
22
|
+
CGImageRef image;
|
22
23
|
}
|
23
24
|
|
24
25
|
- (id) init
|
@@ -26,9 +27,9 @@ static int video_input_queue_index = 0;
|
|
26
27
|
self = [super init];
|
27
28
|
if (self)
|
28
29
|
{
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
session = nil;
|
31
|
+
queue = nil;
|
32
|
+
image = nil;
|
32
33
|
}
|
33
34
|
return self;
|
34
35
|
}
|
@@ -38,37 +39,37 @@ static int video_input_queue_index = 0;
|
|
38
39
|
[self stop];
|
39
40
|
[self clearImage];
|
40
41
|
|
41
|
-
if (
|
42
|
+
if (queue)
|
42
43
|
{
|
43
|
-
dispatch_release(
|
44
|
-
|
44
|
+
dispatch_release(queue);
|
45
|
+
queue = nil;
|
45
46
|
}
|
46
47
|
|
47
48
|
[super dealloc];
|
48
49
|
}
|
49
50
|
|
50
|
-
- (dispatch_queue_t)
|
51
|
+
- (dispatch_queue_t) getQueue
|
51
52
|
{
|
52
|
-
if (!
|
53
|
+
if (!queue)
|
53
54
|
{
|
54
55
|
auto name = Xot::stringf(
|
55
56
|
"org.xord.RaysVideoInputQueue_%d",
|
56
57
|
video_input_queue_index++);
|
57
|
-
|
58
|
+
queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL);
|
58
59
|
}
|
59
|
-
return
|
60
|
+
return queue;
|
60
61
|
}
|
61
62
|
|
62
|
-
- (BOOL) start
|
63
|
+
- (BOOL) start: (AVCaptureDevice*) device
|
64
|
+
preset: (AVCaptureSessionPreset) preset
|
63
65
|
{
|
64
|
-
|
66
|
+
if (!device) return NO;
|
65
67
|
|
66
|
-
|
67
|
-
session.sessionPreset = AVCaptureSessionPresetHigh;
|
68
|
+
[self stop];
|
68
69
|
|
69
|
-
|
70
|
-
|
71
|
-
|
70
|
+
AVCaptureSession* sess = [[[AVCaptureSession alloc] init] autorelease];
|
71
|
+
if (preset != nil)
|
72
|
+
sess.sessionPreset = preset;
|
72
73
|
|
73
74
|
//device.activeVideoMinFrameDuration = CMTimeMake(1, 30);
|
74
75
|
|
@@ -76,7 +77,7 @@ static int video_input_queue_index = 0;
|
|
76
77
|
AVCaptureDeviceInput* input = [[[AVCaptureDeviceInput alloc]
|
77
78
|
initWithDevice: device error: &error]
|
78
79
|
autorelease];
|
79
|
-
if (!input || error || ![
|
80
|
+
if (!input || error || ![sess canAddInput: input])
|
80
81
|
return NO;
|
81
82
|
|
82
83
|
AVCaptureVideoDataOutput* output =
|
@@ -85,15 +86,15 @@ static int video_input_queue_index = 0;
|
|
85
86
|
(NSString*) kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)
|
86
87
|
};
|
87
88
|
output.alwaysDiscardsLateVideoFrames = YES;
|
88
|
-
[output setSampleBufferDelegate: self queue: self
|
89
|
-
if (![
|
89
|
+
[output setSampleBufferDelegate: self queue: [self getQueue]];
|
90
|
+
if (![sess canAddOutput: output])
|
90
91
|
return NO;
|
91
92
|
|
92
|
-
[
|
93
|
-
[
|
94
|
-
[
|
93
|
+
[sess addInput: input];
|
94
|
+
[sess addOutput: output];
|
95
|
+
[sess startRunning];
|
95
96
|
|
96
|
-
|
97
|
+
session = [sess retain];
|
97
98
|
return YES;
|
98
99
|
}
|
99
100
|
|
@@ -115,35 +116,35 @@ static int video_input_queue_index = 0;
|
|
115
116
|
|
116
117
|
dispatch_async(dispatch_get_main_queue(), ^{
|
117
118
|
[self clearImage];
|
118
|
-
|
119
|
+
image = cgImage;
|
119
120
|
});
|
120
121
|
}
|
121
122
|
|
122
123
|
- (void) stop
|
123
124
|
{
|
124
|
-
if (!
|
125
|
+
if (!session) return;
|
125
126
|
|
126
|
-
[
|
127
|
-
[
|
128
|
-
|
127
|
+
[session stopRunning];
|
128
|
+
[session release];
|
129
|
+
session = nil;
|
129
130
|
}
|
130
131
|
|
131
132
|
- (BOOL) isActive
|
132
133
|
{
|
133
|
-
return
|
134
|
+
return session != nil;
|
134
135
|
}
|
135
136
|
|
136
137
|
- (void) clearImage
|
137
138
|
{
|
138
|
-
if (!
|
139
|
+
if (!image) return;
|
139
140
|
|
140
|
-
CGImageRelease(
|
141
|
-
|
141
|
+
CGImageRelease(image);
|
142
|
+
image = nil;
|
142
143
|
}
|
143
144
|
|
144
145
|
- (CGImageRef) getImage
|
145
146
|
{
|
146
|
-
return
|
147
|
+
return image;
|
147
148
|
}
|
148
149
|
|
149
150
|
@end// VideoInput
|
@@ -156,10 +157,51 @@ namespace Rays
|
|
156
157
|
struct Camera::Data
|
157
158
|
{
|
158
159
|
|
160
|
+
String device_name;
|
161
|
+
|
162
|
+
int min_width = -1, min_height = -1;
|
163
|
+
|
164
|
+
bool resize = false, crop = false;
|
165
|
+
|
159
166
|
mutable Image image;
|
160
167
|
|
161
168
|
VideoInput* video_input = nil;
|
162
169
|
|
170
|
+
AVCaptureSessionPreset get_preset (AVCaptureDevice* device)
|
171
|
+
{
|
172
|
+
int w = min_width, h = min_height;
|
173
|
+
if (w > 0 && h > 0)
|
174
|
+
{
|
175
|
+
#define SUPPORT(x) \
|
176
|
+
[device supportsAVCaptureSessionPreset: AVCaptureSessionPreset##x]
|
177
|
+
|
178
|
+
if (w <= 320 && h <= 240 && SUPPORT(320x240))
|
179
|
+
return AVCaptureSessionPreset320x240;
|
180
|
+
|
181
|
+
if (w <= 352 && h <= 288 && SUPPORT(352x288))
|
182
|
+
return AVCaptureSessionPreset352x288;
|
183
|
+
|
184
|
+
if (w <= 640 && h <= 480 && SUPPORT(640x480))
|
185
|
+
return AVCaptureSessionPreset640x480;
|
186
|
+
|
187
|
+
if (w <= 960 && h <= 540 && SUPPORT(960x540))
|
188
|
+
return AVCaptureSessionPreset960x540;
|
189
|
+
|
190
|
+
if (/*w <= 1280 && h <= 720 &&*/ SUPPORT(1280x720))
|
191
|
+
return AVCaptureSessionPreset1280x720;
|
192
|
+
|
193
|
+
//if (w <= 1920 && h <= 1080 && SUPPORT(1920x1080))
|
194
|
+
// return AVCaptureSessionPreset1920x1080;
|
195
|
+
|
196
|
+
//if (SUPPORT(3840x2160))
|
197
|
+
// return AVCaptureSessionPreset3840x2160;
|
198
|
+
|
199
|
+
#undef SUPPORT
|
200
|
+
}
|
201
|
+
|
202
|
+
return nil;
|
203
|
+
}
|
204
|
+
|
163
205
|
void update_image_from_video_input () const
|
164
206
|
{
|
165
207
|
if (!video_input) return;
|
@@ -167,24 +209,147 @@ namespace Rays
|
|
167
209
|
CGImageRef cgImage = [video_input getImage];
|
168
210
|
if (!cgImage) return;
|
169
211
|
|
170
|
-
|
212
|
+
coord draw_x, draw_y, draw_width, draw_height;
|
213
|
+
int bitmap_width, bitmap_height;
|
214
|
+
get_draw_bounds(
|
215
|
+
&draw_x, &draw_y, &draw_width, &draw_height,
|
216
|
+
&bitmap_width, &bitmap_height,
|
217
|
+
cgImage);
|
218
|
+
|
219
|
+
if (
|
220
|
+
!image ||
|
221
|
+
image.bitmap().width() != bitmap_width ||
|
222
|
+
image.bitmap().height() != bitmap_height)
|
171
223
|
{
|
172
|
-
Bitmap
|
173
|
-
(int) CGImageGetWidth(cgImage),
|
174
|
-
(int) CGImageGetHeight(cgImage));
|
175
|
-
image = Image(bmp);
|
224
|
+
image = Image(Bitmap(bitmap_width, bitmap_height));
|
176
225
|
}
|
177
226
|
|
178
|
-
|
227
|
+
Bitmap_draw_image(
|
228
|
+
&image.bitmap(), cgImage,
|
229
|
+
draw_x, draw_y, draw_width, draw_height);
|
179
230
|
|
180
231
|
[video_input clearImage];
|
181
232
|
}
|
182
233
|
|
234
|
+
void get_draw_bounds (
|
235
|
+
coord* draw_x, coord* draw_y, coord* draw_width, coord* draw_height,
|
236
|
+
int* bitmap_width, int* bitmap_height,
|
237
|
+
CGImageRef image) const
|
238
|
+
{
|
239
|
+
int image_width = (int) CGImageGetWidth(image);
|
240
|
+
int image_height = (int) CGImageGetHeight(image);
|
241
|
+
float image_ratio = (float) image_width / (float) image_height;
|
242
|
+
|
243
|
+
if (resize && min_width > 0 && min_height > 0)
|
244
|
+
{
|
245
|
+
float min_size_ratio = (float) min_width / (float) min_height;
|
246
|
+
if (image_ratio > min_size_ratio)
|
247
|
+
{
|
248
|
+
*draw_width = min_height * image_ratio;
|
249
|
+
*draw_height = min_height;
|
250
|
+
}
|
251
|
+
else
|
252
|
+
{
|
253
|
+
*draw_width = min_width;
|
254
|
+
*draw_height = min_width / image_ratio;
|
255
|
+
}
|
256
|
+
}
|
257
|
+
else if (resize && min_width > 0)
|
258
|
+
{
|
259
|
+
*draw_width = min_width;
|
260
|
+
*draw_height = min_width / image_ratio;
|
261
|
+
}
|
262
|
+
else if (resize && min_height > 0)
|
263
|
+
{
|
264
|
+
*draw_width = min_height * image_ratio;
|
265
|
+
*draw_height = min_height;
|
266
|
+
}
|
267
|
+
else
|
268
|
+
{
|
269
|
+
*draw_width = image_width;
|
270
|
+
*draw_height = image_height;
|
271
|
+
}
|
272
|
+
|
273
|
+
*draw_x = 0;
|
274
|
+
*draw_y = 0;
|
275
|
+
*bitmap_width = *draw_width;
|
276
|
+
*bitmap_height = *draw_height;
|
277
|
+
|
278
|
+
if (crop && min_width > 0)
|
279
|
+
{
|
280
|
+
*draw_x = min_width / 2 - *draw_width / 2;
|
281
|
+
*bitmap_width = min_width;
|
282
|
+
}
|
283
|
+
else if (crop && min_height > 0)
|
284
|
+
{
|
285
|
+
*draw_y = min_height / 2 - *draw_height / 2;
|
286
|
+
*bitmap_height = min_height;
|
287
|
+
}
|
288
|
+
}
|
289
|
+
|
183
290
|
};// Camera::Data
|
184
291
|
|
185
292
|
|
186
|
-
|
293
|
+
static NSArray<AVCaptureDevice*>*
|
294
|
+
get_video_devices ()
|
187
295
|
{
|
296
|
+
NSMutableArray<AVCaptureDevice*>* devices = [NSMutableArray array];
|
297
|
+
for (AVCaptureDevice* d in AVCaptureDevice.devices)
|
298
|
+
{
|
299
|
+
if ([d hasMediaType: AVMediaTypeVideo])
|
300
|
+
[devices addObject: d];
|
301
|
+
}
|
302
|
+
return devices;
|
303
|
+
}
|
304
|
+
|
305
|
+
static AVCaptureDevice*
|
306
|
+
get_default_video_device ()
|
307
|
+
{
|
308
|
+
AVCaptureDevice* device =
|
309
|
+
[AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
|
310
|
+
if (!device)
|
311
|
+
rays_error(__FILE__, __LINE__, "Default camera device is not found.");
|
312
|
+
|
313
|
+
return device;
|
314
|
+
}
|
315
|
+
|
316
|
+
static AVCaptureDevice*
|
317
|
+
get_video_device (const char* name)
|
318
|
+
{
|
319
|
+
if (!name || *name == 0)
|
320
|
+
return get_default_video_device();
|
321
|
+
|
322
|
+
for (AVCaptureDevice* d in get_video_devices())
|
323
|
+
{
|
324
|
+
if (strcmp(name, d.localizedName.UTF8String) == 0)
|
325
|
+
return d;
|
326
|
+
}
|
327
|
+
|
328
|
+
rays_error(__FILE__, __LINE__, "Camera device '%s' is not found.", name);
|
329
|
+
return nil;
|
330
|
+
}
|
331
|
+
|
332
|
+
std::vector<String>
|
333
|
+
get_camera_device_names ()
|
334
|
+
{
|
335
|
+
std::vector<String> names;
|
336
|
+
for (AVCaptureDevice* d in get_video_devices())
|
337
|
+
names.emplace_back(d.localizedName.UTF8String);
|
338
|
+
return names;
|
339
|
+
}
|
340
|
+
|
341
|
+
|
342
|
+
Camera::Camera (
|
343
|
+
const char* device_name,
|
344
|
+
int min_width, int min_height, bool resize, bool crop)
|
345
|
+
{
|
346
|
+
if (device_name)
|
347
|
+
self->device_name = device_name;
|
348
|
+
|
349
|
+
self->min_width = min_width;
|
350
|
+
self->min_height = min_height;
|
351
|
+
self->resize = resize;
|
352
|
+
self->crop = crop;
|
188
353
|
}
|
189
354
|
|
190
355
|
Camera::~Camera ()
|
@@ -197,7 +362,9 @@ namespace Rays
|
|
197
362
|
Camera::start ()
|
198
363
|
{
|
199
364
|
if (!self->video_input) self->video_input = [[VideoInput alloc] init];
|
200
|
-
|
365
|
+
|
366
|
+
AVCaptureDevice* device = get_video_device(self->device_name.c_str());
|
367
|
+
return [self->video_input start: device preset: self->get_preset(device)];
|
201
368
|
}
|
202
369
|
|
203
370
|
void
|
@@ -214,6 +381,54 @@ namespace Rays
|
|
214
381
|
return self->video_input && [self->video_input isActive];
|
215
382
|
}
|
216
383
|
|
384
|
+
void
|
385
|
+
Camera::set_min_width (int width)
|
386
|
+
{
|
387
|
+
self->min_width = width;
|
388
|
+
}
|
389
|
+
|
390
|
+
int
|
391
|
+
Camera::min_width () const
|
392
|
+
{
|
393
|
+
return self->min_width;
|
394
|
+
}
|
395
|
+
|
396
|
+
void
|
397
|
+
Camera::set_min_height (int height)
|
398
|
+
{
|
399
|
+
self->min_height = height;
|
400
|
+
}
|
401
|
+
|
402
|
+
int
|
403
|
+
Camera::min_height () const
|
404
|
+
{
|
405
|
+
return self->min_height;
|
406
|
+
}
|
407
|
+
|
408
|
+
void
|
409
|
+
Camera::set_resize (bool resize)
|
410
|
+
{
|
411
|
+
self->resize = resize;
|
412
|
+
}
|
413
|
+
|
414
|
+
bool
|
415
|
+
Camera::is_resize () const
|
416
|
+
{
|
417
|
+
return self->resize;
|
418
|
+
}
|
419
|
+
|
420
|
+
void
|
421
|
+
Camera::set_crop (bool crop)
|
422
|
+
{
|
423
|
+
self->crop = crop;
|
424
|
+
}
|
425
|
+
|
426
|
+
bool
|
427
|
+
Camera::is_crop () const
|
428
|
+
{
|
429
|
+
return self->crop;
|
430
|
+
}
|
431
|
+
|
217
432
|
const Image*
|
218
433
|
Camera::image () const
|
219
434
|
{
|