ffi-libfreenect 0.1.1
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.
- data/History.txt +5 -0
- data/LICENSE.txt +23 -0
- data/README.rdoc +86 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/examples/record.rb +76 -0
- data/examples/tilt_led.rb +105 -0
- data/examples/tilt_nod.rb +28 -0
- data/examples/video_snapshot.rb +61 -0
- data/lib/ffi/freenect.rb +170 -0
- data/lib/freenect.rb +40 -0
- data/lib/freenect/context.rb +66 -0
- data/lib/freenect/device.rb +210 -0
- data/lib/freenect/sync.rb +93 -0
- data/spec/context_spec.rb +61 -0
- data/spec/device_spec.rb +97 -0
- data/spec/freenect_spec.rb +90 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +11 -0
- metadata +113 -0
data/lib/freenect.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
# we may one day have a native extension for bindings... for now only
|
3
|
+
# ffi/freenect exists
|
4
|
+
require 'ffi/freenect'
|
5
|
+
require 'freenect/context'
|
6
|
+
require 'freenect/device'
|
7
|
+
|
8
|
+
module Freenect
|
9
|
+
include FFI::Freenect
|
10
|
+
|
11
|
+
def self.init(*args)
|
12
|
+
Context.new(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.lookup_video_format(fmt)
|
16
|
+
return (fmt.is_a?(Numeric) ? fmt : FFI::Freenect::VIDEO_FORMATS[fmt])
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.lookup_video_size(fmt)
|
20
|
+
l_fmt = (fmt.is_a?(Numeric) ? FFI::Freenect::VIDEO_FORMATS[fmt] : fmt)
|
21
|
+
if l_fmt.nil? or (sz = FFI::Freenect::VIDEO_SIZES[l_fmt]).nil?
|
22
|
+
return nil
|
23
|
+
else
|
24
|
+
return sz
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.lookup_depth_format(fmt)
|
29
|
+
return (fmt.is_a?(Numeric) ? fmt : FFI::Freenect::DEPTH_FORMATS[fmt])
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.lookup_depth_size(fmt)
|
33
|
+
l_fmt = (fmt.is_a?(Numeric) ? FFI::Freenect::DEPTH_FORMATS[fmt] : fmt)
|
34
|
+
if l_fmt.nil? or (sz = FFI::Freenect::DEPTH_SIZES[l_fmt]).nil?
|
35
|
+
return nil
|
36
|
+
else
|
37
|
+
return sz
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'ffi/freenect'
|
2
|
+
require 'freenect/device'
|
3
|
+
|
4
|
+
module Freenect
|
5
|
+
class ContextError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class Context
|
9
|
+
def initialize(usb_ctx=nil)
|
10
|
+
ctx_p = FFI::MemoryPointer.new(:pointer)
|
11
|
+
if ::FFI::Freenect.freenect_init(ctx_p, usb_ctx) != 0
|
12
|
+
raise ContextError, "freenect_init() returned nonzero"
|
13
|
+
elsif ctx_p.null?
|
14
|
+
raise ContextError, "freenect_init() produced a NULL context"
|
15
|
+
end
|
16
|
+
@ctx = ctx_p.read_pointer
|
17
|
+
end
|
18
|
+
|
19
|
+
def context
|
20
|
+
if @ctx_closed
|
21
|
+
raise ContextError, "This context has been shut down and can no longer be used"
|
22
|
+
else
|
23
|
+
return @ctx
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def num_devices
|
28
|
+
::FFI::Freenect.freenect_num_devices(self.context)
|
29
|
+
end
|
30
|
+
|
31
|
+
def open_device(idx)
|
32
|
+
return Device.new(self, idx)
|
33
|
+
end
|
34
|
+
|
35
|
+
alias [] open_device
|
36
|
+
|
37
|
+
def set_log_level(loglevel)
|
38
|
+
::FFI::Freenect.freenect_set_log_level(self.context, loglevel)
|
39
|
+
end
|
40
|
+
|
41
|
+
alias log_level= set_log_level
|
42
|
+
|
43
|
+
def set_log_callback(&block)
|
44
|
+
::FFI::Freenect.freenect_set_log_callback(self.context, block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_events
|
48
|
+
::FFI::Freenect.freenect_process_events(self.context)
|
49
|
+
end
|
50
|
+
|
51
|
+
def close
|
52
|
+
unless closed?
|
53
|
+
if ::FFI::Freenect.freenect_shutdown(@ctx) != 0
|
54
|
+
raise ContextError, "freenect_shutdown() returned nonzero"
|
55
|
+
end
|
56
|
+
@ctx_closed = true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
alias shutdown close
|
61
|
+
|
62
|
+
def closed?
|
63
|
+
@ctx_closed == true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
|
2
|
+
require 'ffi/freenect'
|
3
|
+
require 'freenect/context'
|
4
|
+
|
5
|
+
module Freenect
|
6
|
+
RawTiltState = FFI::Freenect::RawTiltState
|
7
|
+
|
8
|
+
class DeviceError < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
class Device
|
12
|
+
# Returns a device object tracked by its ruby object reference ID stored
|
13
|
+
# in user data.
|
14
|
+
#
|
15
|
+
# This method is intended for internal use.
|
16
|
+
def self.by_reference(devp)
|
17
|
+
unless devp.null? or (refp=FFI::Freenect.freenect_get_user(devp)).null?
|
18
|
+
obj=ObjectSpace._id2ref(refp.read_long_long)
|
19
|
+
return obj if obj.is_a?(Device)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(ctx, idx)
|
24
|
+
dev_p = ::FFI::MemoryPointer.new(:pointer)
|
25
|
+
@ctx = ctx
|
26
|
+
|
27
|
+
if ::FFI::Freenect.freenect_open_device(@ctx.context, dev_p, idx) != 0
|
28
|
+
raise DeviceError, "unable to open device #{idx} from #{ctx.inspect}"
|
29
|
+
end
|
30
|
+
|
31
|
+
@dev = dev_p.read_pointer
|
32
|
+
save_object_id!()
|
33
|
+
end
|
34
|
+
|
35
|
+
def closed?
|
36
|
+
@ctx.closed? or (@dev_closed == true)
|
37
|
+
end
|
38
|
+
|
39
|
+
def close
|
40
|
+
unless closed?
|
41
|
+
if ::FFI::Freenect.freenect_close_device(@dev) == 0
|
42
|
+
@dev_closed = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def device
|
48
|
+
if closed?
|
49
|
+
raise DeviceError, "this device is closed and can no longer be used"
|
50
|
+
else
|
51
|
+
return @dev
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def context
|
56
|
+
@ctx
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_tilt_state
|
60
|
+
unless (p=::FFI::Freenect.freenect_get_tilt_state(self.device)).null?
|
61
|
+
return RawTiltState.new(p)
|
62
|
+
else
|
63
|
+
raise DeviceError, "freenect_get_tilt_state() returned a NULL tilt_state"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
alias tilt_state get_tilt_state
|
68
|
+
|
69
|
+
# Returns the current tilt angle
|
70
|
+
def get_tilt_degrees
|
71
|
+
::FFI::Freenect.freenect_get_tilt_degs(self.device)
|
72
|
+
end
|
73
|
+
|
74
|
+
alias tilt get_tilt_degrees
|
75
|
+
|
76
|
+
# Sets the tilt angle.
|
77
|
+
# Maximum tilt angle range is between +30 and -30
|
78
|
+
def set_tilt_degrees(angle)
|
79
|
+
::FFI::Freenect.freenect_set_tilt_degs(self.device, angle)
|
80
|
+
return(update_tilt_state() < 0) # based on libfreenect error cond. as of 12-21-10
|
81
|
+
end
|
82
|
+
|
83
|
+
alias tilt= set_tilt_degrees
|
84
|
+
|
85
|
+
# Defines a handler for depth events.
|
86
|
+
#
|
87
|
+
# @yield [device, depth_buf, timestamp]
|
88
|
+
# @yieldparam device A pointer to the device that generated the event.
|
89
|
+
# @yieldparam depth_buf A pointer to the buffer containing the depth data.
|
90
|
+
# @yieldparam timestamp A timestamp for the event?
|
91
|
+
def set_depth_callback(&block)
|
92
|
+
@depth_callback = block
|
93
|
+
::FFI::Freenect.freenect_set_depth_callback(self.device, @depth_callback)
|
94
|
+
end
|
95
|
+
|
96
|
+
alias on_depth set_depth_callback
|
97
|
+
|
98
|
+
# Defines a handler for video events.
|
99
|
+
#
|
100
|
+
# @yield [device, video_buf, timestamp]
|
101
|
+
# @yieldparam device A pointer to the device that generated the event.
|
102
|
+
# @yieldparam video_buf A pointer to the buffer containing the video data.
|
103
|
+
# @yieldparam timestamp A timestamp for the event?
|
104
|
+
def set_video_callback(&block)
|
105
|
+
@video_callback = block
|
106
|
+
::FFI::Freenect.freenect_set_video_callback(self.device, @video_callback)
|
107
|
+
end
|
108
|
+
|
109
|
+
alias on_video set_video_callback
|
110
|
+
|
111
|
+
def start_depth
|
112
|
+
unless(::FFI::Freenect.freenect_start_depth(self.device) == 0)
|
113
|
+
raise DeviceError, "Error in freenect_start_depth()"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def stop_depth
|
118
|
+
unless(::FFI::Freenect.freenect_stop_depth(self.device) == 0)
|
119
|
+
raise DeviceError, "Error in freenect_stop_depth()"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def start_video
|
124
|
+
unless(::FFI::Freenect.freenect_start_video(self.device) == 0)
|
125
|
+
raise DeviceError, "Error in freenect_start_video()"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def stop_video
|
130
|
+
unless(::FFI::Freenect.freenect_stop_video(self.device) == 0)
|
131
|
+
raise DeviceError, "Error in freenect_stop_video()"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def set_depth_format(fmt)
|
136
|
+
l_fmt = fmt.is_a?(Numeric)? fmt : Freenect::DEPTH_FORMATS[fmt]
|
137
|
+
ret = ::FFI::Freenect.freenect_set_depth_format(self.device, l_fmt)
|
138
|
+
if (ret== 0)
|
139
|
+
@depth_format = fmt
|
140
|
+
else
|
141
|
+
raise DeviceError, "Error calling freenect_set_depth_format(self, #{fmt})"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
alias depth_format= set_depth_format
|
146
|
+
|
147
|
+
# returns the symbolic constant for the current depth format
|
148
|
+
def depth_format
|
149
|
+
(@depth_format.is_a?(Numeric))? Freenect::DEPTH_FORMATS[@depth_format] : @depth_format
|
150
|
+
end
|
151
|
+
|
152
|
+
# Sets the video format to one of the following accepted values:
|
153
|
+
#
|
154
|
+
def set_video_format(fmt)
|
155
|
+
l_fmt = fmt.is_a?(Numeric)? fmt : Freenect::VIDEO_FORMATS[fmt]
|
156
|
+
ret = ::FFI::Freenect.freenect_set_video_format(self.device, l_fmt)
|
157
|
+
if (ret== 0)
|
158
|
+
@video_format = fmt
|
159
|
+
else
|
160
|
+
raise DeviceError, "Error calling freenect_set_video_format(self, #{fmt})"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
alias video_format= set_video_format
|
165
|
+
|
166
|
+
def video_format
|
167
|
+
(@video_format.is_a?(Numeric))? ::Freenect::VIDEO_FORMATS[@video_format] : @video_format
|
168
|
+
end
|
169
|
+
|
170
|
+
# Sets the led to one of the following accepted values:
|
171
|
+
# :off, Freenect::LED_OFF
|
172
|
+
# :green, Freenect::LED_GREEN
|
173
|
+
# :red, Freenect::LED_RED
|
174
|
+
# :yellow, Freenect::LED_YELLOW
|
175
|
+
# :blink_yellow, Freenect::LED_BLINK_YELLOW
|
176
|
+
# :blink_green, Freenect::LED_BLINK_GREEN
|
177
|
+
# :blink_red_yellow, Freenect::LED_BLINK_RED_YELLOW
|
178
|
+
#
|
179
|
+
# Either the symbol or numeric constant can be specified.
|
180
|
+
def set_led(mode)
|
181
|
+
return(::FFI::Freenect.freenect_set_led(self.device, mode) == 0)
|
182
|
+
end
|
183
|
+
|
184
|
+
alias led= set_led
|
185
|
+
|
186
|
+
def reference_id
|
187
|
+
unless (p=::FFI::Freenect.freenect_get_user(device)).null?
|
188
|
+
p.read_long_long
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
def set_depth_buffer(buf)
|
194
|
+
end
|
195
|
+
|
196
|
+
def set_video_buffer(buf)
|
197
|
+
end
|
198
|
+
|
199
|
+
def save_object_id!
|
200
|
+
objid_p = FFI::MemoryPointer.new(:long_long)
|
201
|
+
objid_p.write_long_long(self.object_id)
|
202
|
+
::FFI::Freenect.freenect_set_user(self.device, objid_p)
|
203
|
+
end
|
204
|
+
|
205
|
+
def update_tilt_state
|
206
|
+
::FFI::Freenect.freenect_update_tilt_state(self.device)
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
|
2
|
+
require 'freenect'
|
3
|
+
|
4
|
+
module Freenect
|
5
|
+
module Sync
|
6
|
+
class FormatError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
# Synchronous video function (starts the runloop if it isn't running)
|
10
|
+
#
|
11
|
+
# @param idx
|
12
|
+
# Device index. Default: 0
|
13
|
+
#
|
14
|
+
# @param fmt
|
15
|
+
# Video Format. See FFI::Freenect::VIDEO_FORMATS. Default is :rgb
|
16
|
+
#
|
17
|
+
# @return [Numeric, String]
|
18
|
+
# Returns an array containing a numeric timestamp and the video buffer
|
19
|
+
# snapshot with a size based on the requested video format.
|
20
|
+
#
|
21
|
+
# @raise FormatError
|
22
|
+
# An exception is raised if an invalid format is specified.
|
23
|
+
#
|
24
|
+
# @raise RuntimeError
|
25
|
+
# An exception is raised if an unknown error occurs in the
|
26
|
+
# freenect_sync_get_video function
|
27
|
+
#
|
28
|
+
def self.get_video(idx=nil, fmt=nil)
|
29
|
+
idx ||= 0
|
30
|
+
fmt ||= :rgb
|
31
|
+
|
32
|
+
if (buf_size = Freenect.lookup_video_size(fmt)).nil?
|
33
|
+
raise(FormatError, "Invalid video format: #{fmt.inspect}")
|
34
|
+
end
|
35
|
+
|
36
|
+
video_p = FFI::MemoryPointer.new(buf_size)
|
37
|
+
timestamp_p = FFI::MemoryPointer.new(:uint32)
|
38
|
+
|
39
|
+
ret = ::FFI::Freenect.freenect_sync_get_video(video_p, timestamp_p, idx, Freenect.lookup_video_format(fmt))
|
40
|
+
|
41
|
+
if ret != 0
|
42
|
+
raise("Unknown error in freenect_sync_get_video()") # TODO is errno set or something here?
|
43
|
+
else
|
44
|
+
return [timestamp_p.read_int, video_p.read_string_length(buf_size)]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Synchronous depth function (starts the runloop if it isn't running)
|
49
|
+
#
|
50
|
+
# @param idx
|
51
|
+
# Device index. Default: 0
|
52
|
+
#
|
53
|
+
# @param fmt
|
54
|
+
# Video Format. See FFI::Freenect::DEPTH_FORMATS. Default is :rgb
|
55
|
+
#
|
56
|
+
# @return [Numeric, String]
|
57
|
+
# Returns an array containing a numeric timestamp and the depth buffer
|
58
|
+
# snapshot with a size based on the requested video format.
|
59
|
+
#
|
60
|
+
# @raise FormatError
|
61
|
+
# An exception is raised if an invalid format is specified.
|
62
|
+
#
|
63
|
+
# @raise RuntimeError
|
64
|
+
# An exception is raised if an unknown error occurs in the
|
65
|
+
# freenect_sync_get_video function
|
66
|
+
#
|
67
|
+
def self.get_depth(idx=0, fmt=:depth_11bit)
|
68
|
+
idx ||= 0
|
69
|
+
fmt ||= :depth_11bit
|
70
|
+
|
71
|
+
if (buf_size = Freenect.lookup_depth_size(fmt)).nil?
|
72
|
+
raise(FormatError, "Invalid depth format: #{fmt.inspect}")
|
73
|
+
end
|
74
|
+
|
75
|
+
depth_p = FFI::MemoryPointer.new(buf_size)
|
76
|
+
timestamp_p = FFI::MemoryPointer.new(:uint32)
|
77
|
+
|
78
|
+
ret = ::FFI::Freenect.freenect_sync_get_depth(depth_p, timestamp_p, idx, Freenect.lookup_depth_format(fmt))
|
79
|
+
if ret != 0
|
80
|
+
raise("Unknown error in freenect_sync_get_depth()") # TODO is errno set or something here?
|
81
|
+
else
|
82
|
+
return [timestamp_p.read_int, video_p.read_string_length(buf_size)]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Stops the sync runloop
|
87
|
+
def self.stop
|
88
|
+
FFI::Freenect.freenect_sync_stop()
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Freenect::Context do
|
5
|
+
before(:all) { @ctx = Freenect::Context.new() }
|
6
|
+
after(:all) { @ctx.close if @ctx }
|
7
|
+
|
8
|
+
it "should initialize a context" do
|
9
|
+
@ctx.should be_kind_of(Freenect::Context)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should indicate the number of devices attached" do
|
13
|
+
@ctx.num_devices.should >= 0
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should open a device when one is connected" do
|
17
|
+
if @ctx.num_devices > 0
|
18
|
+
dev = @ctx[0]
|
19
|
+
dev.should be_kind_of(Freenect::Device)
|
20
|
+
lambda { dev.close }.should_not raise_error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise an exception when an invalid device index is opened" do
|
25
|
+
lambda { @ctx[@ctx.num_devices + 1] }.should raise_error(StandardError)
|
26
|
+
lambda { @ctx.open_device(@ctx.num_devices + 1) }.should raise_error(StandardError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should allow a libfreenect log level to be set using symbols or constants" do
|
30
|
+
(@ctx.log_level = :fatal).should == :fatal
|
31
|
+
(@ctx.log_level = Freenect::LOG_FATAL).should == 0
|
32
|
+
|
33
|
+
(@ctx.log_level = :error).should == :error
|
34
|
+
(@ctx.log_level = Freenect::LOG_ERROR).should == 1
|
35
|
+
|
36
|
+
(@ctx.log_level = :warning).should == :warning
|
37
|
+
(@ctx.log_level = Freenect::LOG_WARNING).should == 2
|
38
|
+
|
39
|
+
(@ctx.log_level = :notice).should == :notice
|
40
|
+
(@ctx.log_level = Freenect::LOG_NOTICE).should == 3
|
41
|
+
|
42
|
+
(@ctx.log_level = :info).should == :info
|
43
|
+
(@ctx.log_level = Freenect::LOG_INFO).should == 4
|
44
|
+
|
45
|
+
(@ctx.log_level = :debug).should == :debug
|
46
|
+
(@ctx.log_level = Freenect::LOG_DEBUG).should == 5
|
47
|
+
|
48
|
+
(@ctx.log_level = :spew).should == :spew
|
49
|
+
(@ctx.log_level = Freenect::LOG_SPEW).should == 6
|
50
|
+
|
51
|
+
(@ctx.log_level = :flood).should == :flood
|
52
|
+
(@ctx.log_level = Freenect::LOG_FLOOD).should == 7
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should allow a log callback to be set" do
|
56
|
+
@ctx.set_log_callback {|a,b,c| p [a,b,c] }
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
end
|
61
|
+
|