ffi-gphoto2 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -1
  3. data/Rakefile +2 -0
  4. data/examples/intervalometer.rb +9 -7
  5. data/examples/list_config.rb +3 -5
  6. data/examples/live_view.rb +6 -12
  7. data/examples/record_movie.rb +10 -13
  8. data/ffi-gphoto2.gemspec +2 -1
  9. data/lib/ffi/gphoto2_port.rb +2 -2
  10. data/lib/gphoto2/camera/capture.rb +59 -0
  11. data/lib/gphoto2/camera/configuration.rb +136 -0
  12. data/lib/gphoto2/camera/event.rb +56 -0
  13. data/lib/gphoto2/camera/filesystem.rb +44 -0
  14. data/lib/gphoto2/camera.rb +95 -166
  15. data/lib/gphoto2/camera_abilities.rb +6 -6
  16. data/lib/gphoto2/camera_abilities_list.rb +7 -6
  17. data/lib/gphoto2/camera_event.rb +1 -8
  18. data/lib/gphoto2/camera_file.rb +15 -5
  19. data/lib/gphoto2/camera_file_path.rb +3 -5
  20. data/lib/gphoto2/camera_folder.rb +11 -0
  21. data/lib/gphoto2/camera_list.rb +3 -10
  22. data/lib/gphoto2/camera_widgets/camera_widget.rb +27 -14
  23. data/lib/gphoto2/camera_widgets/radio_camera_widget.rb +1 -0
  24. data/lib/gphoto2/camera_widgets/range_camera_widget.rb +1 -0
  25. data/lib/gphoto2/context.rb +2 -6
  26. data/lib/gphoto2/entry.rb +2 -0
  27. data/lib/gphoto2/port_info.rb +8 -6
  28. data/lib/gphoto2/port_info_list.rb +5 -6
  29. data/lib/gphoto2/port_result.rb +2 -0
  30. data/lib/gphoto2/struct.rb +11 -0
  31. data/lib/gphoto2/version.rb +1 -1
  32. data/lib/gphoto2.rb +13 -0
  33. data/spec/gphoto2/camera_abilities_list_spec.rb +5 -5
  34. data/spec/gphoto2/camera_abilities_spec.rb +7 -7
  35. data/spec/gphoto2/camera_file_path_spec.rb +2 -2
  36. data/spec/gphoto2/camera_file_spec.rb +7 -7
  37. data/spec/gphoto2/camera_folder_spec.rb +3 -3
  38. data/spec/gphoto2/camera_list_spec.rb +3 -3
  39. data/spec/gphoto2/camera_spec.rb +37 -272
  40. data/spec/gphoto2/camera_widgets/date_camera_widget_spec.rb +1 -1
  41. data/spec/gphoto2/camera_widgets/radio_camera_widget_spec.rb +4 -4
  42. data/spec/gphoto2/camera_widgets/range_camera_widget_spec.rb +2 -2
  43. data/spec/gphoto2/camera_widgets/text_camera_widget_spec.rb +1 -1
  44. data/spec/gphoto2/camera_widgets/toggle_camera_widget_spec.rb +2 -2
  45. data/spec/gphoto2/context_spec.rb +1 -1
  46. data/spec/gphoto2/entry_spec.rb +2 -2
  47. data/spec/gphoto2/port_info_list_spec.rb +4 -4
  48. data/spec/gphoto2/port_info_spec.rb +9 -10
  49. data/spec/spec_helper.rb +0 -7
  50. data/spec/support/shared_examples/camera/capture_examples.rb +41 -0
  51. data/spec/support/shared_examples/camera/configuration_examples.rb +161 -0
  52. data/spec/support/shared_examples/camera/event_examples.rb +39 -0
  53. data/spec/support/shared_examples/camera/filesystem_examples.rb +46 -0
  54. data/spec/support/shared_examples/camera_widget_examples.rb +13 -13
  55. metadata +32 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3b4805a380f8f02eb6694864ac81a98762bf7b46
4
- data.tar.gz: 9e902a992bfd63ea3c34cb86309d7b0856f5790f
3
+ metadata.gz: 06a37e1529e44e76317ee65554d7c1ddacea1cec
4
+ data.tar.gz: fd9cb5732c86302822891de0530a3b6dd822c078
5
5
  SHA512:
6
- metadata.gz: a661c2b35d4437e187d1b70841819b968c78fe4adef6a13921872c2e539c8bca320b07f1aad88ed965848d88ea746a97ff19a0162aff3ece0cea4488d2c7d67d
7
- data.tar.gz: fd8d1791990063bfe0945774a61dd3d0c287ee1cfd554c24ff2b2e528ec88071013b1d7bde689d2abc0cf922866ba1ca597d1af9c9d326b60d3f84af6edd9f68
6
+ metadata.gz: 63f6a9cc345afbc0c9e90fd4396927d054e24825c6579de635e0a12ab97fce77606960c0c9a79771b005b08e91537a262decef0e7015c3aa60010fa51b1bed3b
7
+ data.tar.gz: e1cd250dc551227e6425991e8aabe5e1dcb92452186caa6f33980081784f33c5733e96aadb99806abca94697c3090e2cd898dc6fa140d582655b0f2155ada2fc
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --color
2
- --format progress
2
+ --warnings
3
+ --require spec_helper
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'yard'
3
4
 
4
5
  RSpec::Core::RakeTask.new('spec')
6
+ YARD::Rake::YardocTask.new
5
7
 
6
8
  task default: :spec
@@ -1,12 +1,14 @@
1
1
  require 'gphoto2'
2
2
 
3
- camera = GPhoto2::Camera.first
3
+ # Take a photo every 10 seconds for 2 hours.
4
4
 
5
- six_hours = 60 * 60 * 6
6
- stop_time = Time.now + six_hours
5
+ interval = 10 # seconds
6
+ two_hours = 60 * 60 * 2 # (2 hours)
7
+ stop_time = Time.now + two_hours
7
8
 
8
- # take a photo every 30 seconds for 6 hours
9
- until Time.now >= stop_time
10
- camera.capture.save
11
- sleep 30
9
+ GPhoto2::Camera.first do |camera|
10
+ until Time.now >= stop_time
11
+ camera.capture.save
12
+ sleep interval
13
+ end
12
14
  end
@@ -1,5 +1,7 @@
1
1
  require 'gphoto2'
2
2
 
3
+ # List all configuration values in tree form.
4
+
3
5
  def visit(widget, level = 0)
4
6
  indent = ' ' * level
5
7
 
@@ -28,10 +30,6 @@ def visit(widget, level = 0)
28
30
  end
29
31
  end
30
32
 
31
- camera = GPhoto2::Camera.first
32
-
33
- begin
33
+ GPhoto2::Camera.first do |camera|
34
34
  visit(camera.window)
35
- ensure
36
- camera.finalize
37
35
  end
@@ -1,21 +1,15 @@
1
+ require 'gphoto2'
2
+
1
3
  # Run and pipe to a video player that can demux raw mjpeg. For example,
2
4
  #
3
- # ruby live_view.rb | mpv --demuxer=+lavf --demuxer-lavf-format=mjpeg --demuxer-lavf-analyzeduration=0.1 -
5
+ # ruby live_view.rb | mpv --demuxer-lavf-format=mjpeg -
4
6
 
5
- require 'gphoto2'
6
-
7
- # automatically flush the io buffer
7
+ # Automatically flush the IO buffer.
8
8
  STDOUT.sync = true
9
9
 
10
- camera = GPhoto2::Camera.first
11
-
12
- begin
10
+ GPhoto2::Camera.first do |camera|
13
11
  loop do
14
- # write the preview image to stdout
12
+ # Write the preview image to stdout.
15
13
  print camera.preview.data
16
14
  end
17
- ensure
18
- # make sure to close (or #exit) the camera
19
- # otherwise, the mirror will stay up
20
- camera.finalize
21
15
  end
@@ -1,28 +1,25 @@
1
+ require 'gphoto2'
2
+
1
3
  # The camera must have a `movie` config key for this to work.
2
4
  #
3
5
  # Unlike capturing photos, which typically save to internal memory, videos
4
6
  # save to the memory card. The next `file_added` event from the camera after
5
7
  # stopping the recording will contain data pointing to the video file.
6
8
 
7
- require 'gphoto2'
8
-
9
- camera = GPhoto2::Camera.first
10
-
11
- begin
12
- # start recording
9
+ GPhoto2::Camera.first do |camera|
10
+ # Start recording.
13
11
  camera.update(movie: true)
14
12
 
15
- # record for ~10 seconds
13
+ # Record for ~10 seconds.
16
14
  sleep 10
17
15
 
18
- # stop recording
16
+ # Stop recording.
19
17
  camera.update(movie: false)
20
18
 
21
- # block until the camera finishes with the file
19
+ # Block until the camera finishes with the file.
22
20
  event = camera.wait_for(:file_added)
23
21
 
24
- # the event data has a camera file that can be saved
25
- event.data.save
26
- ensure
27
- camera.finalize
22
+ # The event data has a camera file that can be saved.
23
+ file = event.data
24
+ file.save
28
25
  end
data/ffi-gphoto2.gemspec CHANGED
@@ -24,7 +24,8 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_development_dependency 'bundler', '~> 1.3'
26
26
  spec.add_development_dependency 'rake'
27
- spec.add_development_dependency 'rspec', '~> 2.14.1'
27
+ spec.add_development_dependency 'rspec', '~> 3.0.0'
28
+ spec.add_development_dependency 'yard', '~> 0.8.7'
28
29
 
29
30
  spec.add_dependency 'ffi', '~> 1.9.0'
30
31
  end
@@ -16,8 +16,8 @@ module FFI
16
16
 
17
17
  # libgphoto2_port/gphoto2/gphoto2-port-info-list.h
18
18
  attach_function :gp_port_info_get_name, [GPPortInfo, :pointer], :int
19
- attach_function :gp_port_info_get_path, [GPPortInfo, :pointer], :int
20
- attach_function :gp_port_info_get_type, [GPPortInfo, :pointer], :int
19
+ attach_function :gp_port_info_get_path, [GPPortInfo, :pointer], :int
20
+ attach_function :gp_port_info_get_type, [GPPortInfo, :pointer], :int
21
21
 
22
22
  attach_function :gp_port_info_list_new, [:pointer], :int
23
23
  attach_function :gp_port_info_list_free, [GPPortInfoList.by_ref], :int
@@ -0,0 +1,59 @@
1
+ module GPhoto2
2
+ class Camera
3
+ module Capture
4
+ # @example
5
+ # # Take a photo.
6
+ # file = camera.capture
7
+ #
8
+ # # And save it to the current working directory.
9
+ # file.save
10
+ #
11
+ # @param [CameraCaptureType] type
12
+ # @return [GPhoto2::CameraFile]
13
+ def capture(type = :image)
14
+ save
15
+ path = _capture(type)
16
+ CameraFile.new(self, path.folder, path.name)
17
+ end
18
+
19
+ # Captures a preview from the camera.
20
+ #
21
+ # Previews are not stored on the camera but are returned as data in a
22
+ # {GPhoto2::CameraFile}.
23
+ #
24
+ # @example
25
+ # # Capturing a preview is like using `Camera#capture`.
26
+ # file = camera.preview
27
+ #
28
+ # # The resulting file will have neither a folder nor name.
29
+ # file.preview?
30
+ # # => true
31
+ #
32
+ # # But it will contain image data from the camera.
33
+ # file.data
34
+ # # => "\xFF\xD8\xFF\xDB\x00\x84\x00\x06..."
35
+ #
36
+ # @return [GPhoto2::CameraFile]
37
+ def preview
38
+ save
39
+ capture_preview
40
+ end
41
+
42
+ private
43
+
44
+ def _capture(type)
45
+ path = CameraFilePath.new
46
+ rc = gp_camera_capture(ptr, type, path.ptr, context.ptr)
47
+ GPhoto2.check!(rc)
48
+ path
49
+ end
50
+
51
+ def capture_preview
52
+ file = CameraFile.new(self)
53
+ rc = gp_camera_capture_preview(ptr, file.ptr, context.ptr)
54
+ GPhoto2.check!(rc)
55
+ file
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,136 @@
1
+ module GPhoto2
2
+ class Camera
3
+ module Configuration
4
+ # @param [String] model
5
+ # @param [String] port
6
+ def initialize(model, port)
7
+ reset
8
+ end
9
+
10
+ # @return [WindowCameraWidget]
11
+ def window
12
+ @window ||= get_config
13
+ end
14
+
15
+ # @example
16
+ # # List camera configuration keys.
17
+ # camera.config.keys
18
+ # # => ['autofocusdrive', 'manualfocusdrive', 'controlmode', ...]
19
+ #
20
+ # @return [Hash<String,GPhoto2::CameraWidget>] a flat map of camera
21
+ # configuration widgets
22
+ # @see #[]
23
+ # @see #[]=
24
+ def config
25
+ @config ||= window.flatten
26
+ end
27
+
28
+ # Reloads the camera configuration.
29
+ #
30
+ # All unsaved changes will be lost.
31
+ #
32
+ # @return [void]
33
+ def reload
34
+ @window.finalize if @window
35
+ reset
36
+ config
37
+ end
38
+
39
+ # @example
40
+ # camera['whitebalance'].to_s
41
+ # # => "Automatic"
42
+ #
43
+ # @param [#to_s] key
44
+ # @return [GPhoto2::CameraWidget] the widget identified by `key`
45
+ def [](key)
46
+ config[key.to_s]
47
+ end
48
+
49
+ # Updates the attribute identified by `key` with the specified `value`.
50
+ #
51
+ # This marks the configuration as "dirty", meaning a call to {#save} is
52
+ # needed to actually update the configuration on the camera.
53
+ #
54
+ # @example
55
+ # camera['iso'] = 800
56
+ # camera['f-number'] = 'f/2.8'
57
+ # camera['shutterspeed2'] = '1/60'
58
+ #
59
+ # @param [#to_s] key
60
+ # @param [Object] value
61
+ # @return [Object]
62
+ def []=(key, value)
63
+ self[key].value = value
64
+ @dirty = true
65
+ value
66
+ end
67
+
68
+ # Updates the configuration on the camera.
69
+ #
70
+ # @return [Boolean] whether setting the configuration was attempted
71
+ def save
72
+ return false unless dirty?
73
+ set_config
74
+ @dirty = false
75
+ true
76
+ end
77
+
78
+ # Updates the attributes of the camera from the given Hash and saves the
79
+ # configuration.
80
+ #
81
+ # @example
82
+ # camera['iso'] # => 800
83
+ # camera['shutterspeed2'] # => "1/30"
84
+ #
85
+ # camera.update(iso: 400, shutterspeed2: '1/60')
86
+ #
87
+ # camera['iso'] # => 400
88
+ # camera['shutterspeed2'] # => "1/60"
89
+ #
90
+ # @param [Hash<String,Object>] attributes
91
+ # @return [Boolean] whether the configuration saved
92
+ def update(attributes = {})
93
+ attributes.each do |key, value|
94
+ self[key] = value
95
+ end
96
+
97
+ save
98
+ end
99
+
100
+ # @example
101
+ # camera.dirty?
102
+ # # => false
103
+ #
104
+ # camera['iso'] = 400
105
+ #
106
+ # camera.dirty?
107
+ # # => true
108
+ #
109
+ # @return [Boolean] whether attributes have been changed
110
+ def dirty?
111
+ @dirty
112
+ end
113
+
114
+ private
115
+
116
+ def reset
117
+ @window = nil
118
+ @config = nil
119
+ @dirty = false
120
+ end
121
+
122
+ def get_config
123
+ widget_ptr = FFI::MemoryPointer.new(FFI::GPhoto2::CameraWidget)
124
+ rc = gp_camera_get_config(ptr, widget_ptr, context.ptr)
125
+ GPhoto2.check!(rc)
126
+ widget = FFI::GPhoto2::CameraWidget.new(widget_ptr.read_pointer)
127
+ CameraWidget.factory(widget)
128
+ end
129
+
130
+ def set_config
131
+ rc = gp_camera_set_config(ptr, window.ptr, context.ptr)
132
+ GPhoto2.check!(rc)
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,56 @@
1
+ module GPhoto2
2
+ class Camera
3
+ module Event
4
+ # @param [Integer] timeout time to wait in milliseconds
5
+ # @return [CameraEvent]
6
+ def wait(timeout = 2000)
7
+ wait_for_event(timeout)
8
+ end
9
+
10
+ # @param [CameraEventType] event_type
11
+ # @return [CameraEvent]
12
+ def wait_for(event_type)
13
+ begin
14
+ event = wait
15
+ end until event.type == event_type
16
+
17
+ event
18
+ end
19
+
20
+ private
21
+
22
+ def wait_for_event(timeout)
23
+ # assume CameraEventType is an int
24
+ type = FFI::MemoryPointer.new(:int)
25
+
26
+ data = FFI::MemoryPointer.new(:pointer)
27
+ data_ptr = FFI::MemoryPointer.new(:pointer)
28
+ data_ptr.write_pointer(data)
29
+
30
+ rc = gp_camera_wait_for_event(ptr, timeout, type, data_ptr, context.ptr)
31
+ GPhoto2.check!(rc)
32
+
33
+ type = CameraEventType[type.read_int]
34
+ data = data_ptr.read_pointer
35
+
36
+ data =
37
+ case type
38
+ when :unknown
39
+ data.null? ? nil : data.read_string
40
+ when :file_added
41
+ path_ptr = FFI::GPhoto2::CameraFilePath.new(data)
42
+ path = CameraFilePath.new(path_ptr)
43
+ CameraFile.new(self, path.folder, path.name)
44
+ when :folder_added
45
+ path_ptr = FFI::GPhoto2::CameraFilePath.new(data)
46
+ path = CameraFilePath.new(path_ptr)
47
+ CameraFolder.new(self, '%s/%s' % [path.folder, path.name])
48
+ else
49
+ nil
50
+ end
51
+
52
+ CameraEvent.new(type, data)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,44 @@
1
+ module GPhoto2
2
+ class Camera
3
+ module Filesystem
4
+ # @example
5
+ # # Get a list of filenames in a path.
6
+ # folder = camera/'store_00010001/DCIM/100D5100'
7
+ # folder.files.map(&:name)
8
+ # # => ["DSC_0001.JPG", "DSC_0002.JPG", ...]
9
+ #
10
+ # @param [String] root
11
+ # @return [CameraFolder]
12
+ def filesystem(root = '/')
13
+ root = "/#{root}" if root[0] != '/'
14
+ CameraFolder.new(self, root)
15
+ end
16
+ alias_method :/, :filesystem
17
+
18
+ # @param [CameraFile] file
19
+ # @return [CameraFile]
20
+ def file(file)
21
+ file_get(file)
22
+ end
23
+
24
+ # @param [CameraFile] file
25
+ # @return [void]
26
+ def delete(file)
27
+ file_delete(file)
28
+ end
29
+
30
+ private
31
+
32
+ def file_get(file, type = :normal)
33
+ rc = gp_camera_file_get(ptr, file.folder, file.name, type, file.ptr, context.ptr)
34
+ GPhoto2.check!(rc)
35
+ file
36
+ end
37
+
38
+ def file_delete(file)
39
+ rc = gp_camera_file_delete(ptr, file.folder, file.name, context.ptr)
40
+ GPhoto2.check!(rc)
41
+ end
42
+ end
43
+ end
44
+ end