ffi-hdhomerun 0.3.100b91730e74 → 0.4

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,87 @@
1
+ ffi-hdhomerun
2
+ =============
3
+ http://bitbucket.org/dmlary/ffi-hdhomerun
4
+
5
+ Description
6
+ -----------
7
+ Ruby-FFI bindings and wrappers for libhdhomerun.
8
+
9
+ Features
10
+ --------
11
+ Allows the user to capture video from an HDHomeRun device within Ruby.
12
+
13
+ What's missing
14
+ --------------
15
+ * Channel scan
16
+ * Documentation
17
+
18
+ Usage
19
+ -----
20
+ require 'ffi-hdhomerun'
21
+
22
+ # Dump the list of tuners on the network
23
+ HDHomeRun.discover.each do |hash|
24
+ puts "hdhomerun device %s found at %s" % [ hash[:id], hash[:ip_addr] ]
25
+ end
26
+
27
+ # Allocate a tuner
28
+ tuner = HDHomeRun::Tuner.new(:id => 'FFFFFFFF', :tuner => 0)
29
+
30
+ # Set the channel and optionally the program
31
+ tuner.channel = 20
32
+ tuner.program = 3
33
+
34
+ # Capture some data
35
+ File.open("capture.ts", "w") do |file|
36
+ puts "Capturing (^C to stop):"
37
+ tuner.capture do |buf|
38
+ file.write(buf)
39
+ STDOUT.write(buf.size > 0 ? "." : "?")
40
+ STDOUT.flush
41
+ end
42
+ end
43
+
44
+ Requirements
45
+ ------------
46
+ * libhdhomerun >= 1.0
47
+ * ffi >= 1.0
48
+
49
+ FFI_HDHOMERUN_OLD_DEVICE_STRUCT
50
+ -------------------------------
51
+ libhdhomerun made a change to the device structure used for discovery
52
+ sometime between version 20100121 and 20110323. They added a new field
53
+ caller tuner_count, but the filename of the hdhomerun library was not
54
+ incremented in ubuntu. As a result, on older releases such as Lucid, users
55
+ of this gem will have to set FFI_HDHOMERUN_OLD_DEVICE_STRUCT in their
56
+ environment to get proper results from the HDHomeRun.discover() method.
57
+ This environment variable only needs to be set if you're planning on
58
+ using the discovery method.
59
+
60
+ Copyright
61
+ ---------
62
+ Copyright (c) 2011,2012 David M. Lary
63
+ All rights reserved.
64
+
65
+ License
66
+ -------
67
+ Redistribution and use in source and binary forms, with or without
68
+ modification, are permitted provided that the following conditions are met:
69
+
70
+ * Redistributions of source code must retain the above copyright
71
+ notice, this list of conditions and the following disclaimer.
72
+ * Redistributions in binary form must reproduce the above copyright
73
+ notice, this list of conditions and the following disclaimer in the
74
+ documentation and/or other materials provided with the distribution.
75
+ * The name of the author may not be used to endorse or promote products
76
+ derived from this software without specific prior written permission.
77
+
78
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
79
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
80
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
81
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
82
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
83
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
84
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
85
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
86
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
87
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/Rakefile CHANGED
@@ -1 +1,5 @@
1
1
  require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run all RSpec tests"
5
+ RSpec::Core::RakeTask.new(:spec)
@@ -4,8 +4,7 @@ require "ffi-hdhomerun"
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "ffi-hdhomerun"
7
- s.version = "%s.%s" % [ HDHomeRun::VERSION,
8
- `hg id -i`.chomp.sub("+", "m") ]
7
+ s.version = "%s" % [ HDHomeRun::VERSION ]
9
8
  s.authors = ["David M. Lary"]
10
9
  s.email = ["dmlary@gmail.com"]
11
10
  s.homepage = "http://bitbucket.org/dmlary/ffi-hdhomerun"
@@ -1,10 +1,166 @@
1
1
  require 'ffi/hdhomerun'
2
+ require 'pry'
2
3
 
3
4
  #
4
5
  # Documentation can be found under HDHomeRun::Tuner
5
6
  #
6
7
  module HDHomeRun
7
- VERSION = "0.3"
8
+ VERSION = "0.4"
9
+ ERROR_STRING_UNKNOWN_VARIABLE = "ERROR: unknown getset variable"
10
+ MAX_TUNERS = 8
11
+
12
+ class ConnectionError < Exception; end
13
+ class UnknownVariableError < Exception; end
14
+
15
+ #
16
+ # Discover tuners on the network.
17
+ #
18
+ # Warning: This function will return invalid data
19
+ #
20
+ # Returns an array of hashes with the following fields:
21
+ # :id -- device id (string)
22
+ # :type -- device type (int)
23
+ # :ip_addr -- ip address (IPAddr)
24
+ # :tuner_count -- number of tuners on the device (int)
25
+ #
26
+ def self.discover
27
+ results_ptr = FFI::MemoryPointer.new(FFI::HDHomeRun::Device, 64)
28
+ count = FFI::HDHomeRun::discover_find_devices_custom(
29
+ FFI::HDHomeRun::DEVICE_ID_WILDCARD,
30
+ FFI::HDHomeRun::DEVICE_TYPE_TUNER,
31
+ FFI::HDHomeRun::DEVICE_ID_WILDCARD,
32
+ results_ptr,
33
+ 64)
34
+ raise "error sending discover request" if count < 0
35
+
36
+ # Loop through the results and construct an array of hashes
37
+ # as our output.
38
+ (0..count-1).map do |i|
39
+
40
+ # construct our hash
41
+ p = FFI::HDHomeRun::Device.new(results_ptr[i])
42
+ out = { :id => "%08X" % p.device_id,
43
+ :type => p.device_type,
44
+ :ip_addr => p.ip_addr }
45
+
46
+ # Some versions of the library don't support tuner_count
47
+ out.merge!({ :tuner_count => p.tuner_count }) if
48
+ p.respond_to? :tuner_count
49
+
50
+ out
51
+ end
52
+ end
53
+
54
+ # HDHomeRun device wrapper.
55
+ class Device
56
+ include FFI::HDHomeRun
57
+
58
+ attr_reader :id
59
+
60
+ # Initialize new hdhomerun device
61
+ #
62
+ # Arguments:
63
+ # [:id] HDHomeRun id (default "FFFFFFFF")
64
+ #
65
+ # Create a device instance for any HDHomeRun device on the network:
66
+ # device = HDHomeRun::Device.new
67
+ #
68
+ # Create a device instance for a specific HDHomeRun device on the network:
69
+ # device = HDHomeRun::Device.new "FEEDFACE"
70
+ #
71
+ def initialize(p={})
72
+ p = {:id => p} unless p.is_a? Hash
73
+ p[:id] ||= "FFFFFFFF"
74
+ p[:id] = "%08X" % p[:id] if p[:id].is_a? Fixnum
75
+
76
+ @hd = create_from_str(p[:id].to_s, nil)
77
+ raise ArgumentError, "Invalid device id: %s" % p[:id] if @hd.null?
78
+ @id = get_device_id_requested(@hd)
79
+ raise ArgumentError, "Invalid device id: %08X" % id \
80
+ unless validate_device_id(@id)
81
+ raise ConnectionError, "Unable to connect to device" \
82
+ unless get_model_str(@hd)
83
+ end
84
+
85
+ #
86
+ # Get a device variable; equivalent to:
87
+ # hdhomerun_config <id> get <key>
88
+ #
89
+ def get(key)
90
+ value = FFI::MemoryPointer.new(:pointer, 1)
91
+ error = FFI::MemoryPointer.new(:pointer, 1)
92
+
93
+ if get_var(@hd, key, value, error) < 0
94
+ raise ConnectionError,
95
+ "communication error sending request to hdhomerun device"
96
+ end
97
+ value = value.read_pointer
98
+ error = error.read_pointer
99
+
100
+ # Return the value if we didn't get an error.
101
+ return value.read_string if error.null?
102
+
103
+ # Throw a useful exception if this get was for an unknown variable.
104
+ error = error.read_string
105
+ raise UnknownVariableError, "unknown variable '%s'" % key if
106
+ error == ERROR_STRING_UNKNOWN_VARIABLE
107
+
108
+ # otherwise, it's a generic runtime error.
109
+ raise RuntimeError, error
110
+ end
111
+
112
+ #
113
+ # Set a device variable; equivalent to:
114
+ # hdhomerun_config <id> set <key> <value>
115
+ #
116
+ def set(key, value)
117
+ error = FFI::MemoryPointer.new(:pointer, 1)
118
+
119
+ if set_var(@hd, key, value.to_s, nil, error) < 0
120
+ raise ConnectionError,
121
+ "communication error sending request to hdhomerun device"
122
+ end
123
+ error = error.read_pointer
124
+
125
+ return true if error.null?
126
+
127
+ # Throw a useful exception if this get was for an unknown variable.
128
+ error = error.read_string
129
+ raise UnknownVariableError, "unknown variable '%s'" % key if
130
+ error == ERROR_STRING_UNKNOWN_VARIABLE
131
+
132
+ # otherwise, it's a generic runtime error.
133
+ raise RuntimeError, error
134
+ end
135
+
136
+ # Get the number of tuners on this device.
137
+ def tuner_count
138
+ @tuner_count ||= begin
139
+ MAX_TUNERS.times do |tuner|
140
+ begin
141
+ get "/tuner%d/debug" % tuner
142
+ rescue UnknownVariableError
143
+ break tuner
144
+ end
145
+ end or raise RuntimeError, "Unable to detect tuner count"
146
+ end
147
+ end
148
+
149
+ # Get an array of tuners for this device.
150
+ def tuners
151
+ @tuners ||= tuner_count.times.map do |tuner|
152
+ Tuner.new(:id => @id, :tuner => tuner)
153
+ end
154
+ end
155
+
156
+ def id
157
+ "%08X" % @id
158
+ end
159
+
160
+ def to_s
161
+ "<%s:0x%08x @id=%08X>" % [ self.class, object_id, @id ]
162
+ end
163
+ end
8
164
 
9
165
  # HDHomeRun tuner wrapper.
10
166
  #
@@ -33,10 +189,11 @@ module HDHomeRun
33
189
  # end
34
190
  # end
35
191
  #
36
- class Tuner
192
+ class Tuner < Device
193
+ extend Forwardable
37
194
  include FFI::HDHomeRun
38
195
 
39
- attr_reader :hd, :id, :tuner
196
+ attr_reader :tuner
40
197
 
41
198
  # Initialize new hdhomerun tuner
42
199
  #
@@ -46,32 +203,20 @@ module HDHomeRun
46
203
  #
47
204
  # Create a tuner instance for the first tuner on any HDHomeRun box
48
205
  # on the network:
49
- # tuner => HDHomeRun::Tuner.new(0)
206
+ # tuner => HDHomeRun::Tuner.new
50
207
  #
51
208
  # Create a tuner instance for the second tuner on the HDHomeRun box
52
209
  # with id FE92EBD0
53
210
  # tuner = HDHomeRun::Tuner.new(:id => "FE92EBD0", :tuner => 1)
54
211
  #
55
212
  def initialize(p={})
56
- p = {:id => p} unless p.is_a? Hash
57
- p[:id] ||= "FFFFFFFF"
58
- p[:tuner] ||= 0
59
- p[:tuner] = "/tuner%d" % p[:tuner] if p[:tuner].is_a? Fixnum
60
-
61
- @hd = create_from_str(p[:id].to_s, nil)
62
- raise ArgumentError, "Invalid device id: #{id}" if @hd.null?
63
- @id = get_device_id_requested(@hd)
213
+ super(p)
214
+ @tuner = p[:tuner] || 0
215
+ @tuner = "/tuner%d" % @tuner if @tuner.is_a? Fixnum
64
216
 
65
- @tuner = p[:tuner]
66
- @tuner = "/tuner%d" % @tuner unless @tuner.is_a? String
67
217
  if set_tuner_from_str(@hd, @tuner) <= 0
68
218
  raise RuntimeError, "invalid tuner: %s" % @tuner
69
219
  end
70
-
71
- raise ArgumentError, "Invalid device id: %08X" % id \
72
- unless validate_device_id(@id)
73
- raise RuntimeError, "Unable to connect to device" \
74
- unless get_model_str(@hd)
75
220
  end
76
221
 
77
222
  # Dynamically define the following getter and setter methods
@@ -86,7 +231,7 @@ module HDHomeRun
86
231
  end
87
232
 
88
233
  # Dynamically define these as simple getter methods
89
- %w{status streaminfo}.each do |name|
234
+ %w{status streaminfo debug}.each do |name|
90
235
  define_method name do
91
236
  get_tuner(name)
92
237
  end
@@ -147,35 +292,12 @@ module HDHomeRun
147
292
  end
148
293
  end
149
294
 
150
- private
151
-
152
- def get(key)
153
- value = FFI::MemoryPointer.new(:pointer, 1)
154
- error = FFI::MemoryPointer.new(:pointer, 1)
155
-
156
- if get_var(@hd, key, value, error) < 0
157
- raise RuntimeError,
158
- "communication error sending request to hdhomerun device"
159
- end
160
- value = value.read_pointer
161
- error = error.read_pointer
162
-
163
- raise RuntimeError, error.read_string unless error.null?
164
- value.read_string
295
+ def to_s
296
+ "<%s:0x%08x @id=%08X, @tuner=%s>" %
297
+ [ self.class, object_id, @id, @tuner ]
165
298
  end
166
299
 
167
- def set(key, value)
168
- error = FFI::MemoryPointer.new(:pointer, 1)
169
-
170
- if set_var(@hd, key, value.to_s, nil, error) < 0
171
- raise RuntimeError,
172
- "communication error sending request to hdhomerun device"
173
- end
174
- error = error.read_pointer
175
-
176
- raise RuntimeError, error.read_string unless error.null?
177
- true
178
- end
300
+ private
179
301
 
180
302
  def get_tuner(key)
181
303
  get("%s/%s" % [@tuner, key.to_s])
@@ -1,4 +1,5 @@
1
1
  require 'ffi'
2
+ require 'ipaddr'
2
3
 
3
4
  module FFI # :nodoc:
4
5
  module HDHomeRun
@@ -6,6 +7,8 @@ module FFI # :nodoc:
6
7
  ffi_lib "libhdhomerun.so.1"
7
8
 
8
9
  VIDEO_DATA_BUFFER_SIZE_1S = (20000000 / 8)
10
+ DEVICE_TYPE_TUNER = 0x00000001
11
+ DEVICE_ID_WILDCARD = 0xFFFFFFFF
9
12
 
10
13
  attach_function :create_from_str,
11
14
  :hdhomerun_device_create_from_str,
@@ -40,6 +43,10 @@ module FFI # :nodoc:
40
43
  attach_function :get_video_stats,
41
44
  :hdhomerun_device_get_video_stats,
42
45
  [:pointer, :pointer], :void
46
+ attach_function :discover_find_devices_custom,
47
+ :hdhomerun_discover_find_devices_custom,
48
+ [ :uint32, :uint32, :uint32, :pointer, :int ],
49
+ :int
43
50
 
44
51
  # HDHomeRun capture statistics for a given tuner
45
52
  #
@@ -63,5 +70,44 @@ module FFI # :nodoc:
63
70
  end
64
71
  end
65
72
  end
73
+
74
+ # Discover device structure
75
+ #
76
+ # Silicon dust changed this structure at some point between
77
+ # revision 20100121 and 20110323, but the version number in the
78
+ # library filename was not changed by package maintainers for
79
+ # ubuntu. As a result, there's no simple way to determine which
80
+ # structure we should use. To work around this, the gem will
81
+ # assume the library has the newer structure unless the
82
+ # FFI_HDHOMERUN_OLD_DEVICE_STRUCT environment variable has been
83
+ # set.
84
+ #
85
+ class Device < FFI::Struct
86
+ if ENV['FFI_HDHOMERUN_OLD_DEVICE_STRUCT']
87
+ layout :ip_addr, :uint32,
88
+ :device_type, :uint32,
89
+ :device_id, :uint32
90
+ else
91
+ # Newer layout has support for :tuner_count
92
+ layout :ip_addr, :uint32,
93
+ :device_type, :uint32,
94
+ :device_id, :uint32,
95
+ :tuner_count, :uint8
96
+
97
+ define_method :tuner_count do
98
+ send(:[], :tuner_count)
99
+ end
100
+ end
101
+
102
+ %w{device_type device_id}.each do |name|
103
+ define_method name do
104
+ send(:[], name.to_sym)
105
+ end
106
+ end
107
+
108
+ def ip_addr
109
+ IPAddr.new(self[:ip_addr], Socket::AF_INET)
110
+ end
111
+ end
66
112
  end
67
113
  end
@@ -0,0 +1,104 @@
1
+ require 'ffi-hdhomerun'
2
+ require 'timeout'
3
+
4
+ describe HDHomeRun do
5
+ it "can find hdhomerun devices on the network" do
6
+ devices = HDHomeRun.discover
7
+ devices.empty?.should eq(false)
8
+ end
9
+ end
10
+
11
+ describe HDHomeRun::Device do
12
+ before :each do
13
+ @device = HDHomeRun::Device.new
14
+ end
15
+
16
+ it "can connect to any device" do
17
+ HDHomeRun::Device.new
18
+ end
19
+
20
+ it "throws exception on invalid name" do
21
+ lambda { HDHomeRun::Device.new("WOOF WOOF, I'm a puppy!") } \
22
+ .should raise_error(ArgumentError)
23
+ end
24
+
25
+ it "throws exception on unreachable device" do
26
+ lambda { HDHomeRun::Device.new("FEEDFACE") } \
27
+ .should raise_error(HDHomeRun::ConnectionError)
28
+ end
29
+
30
+ it "can get device variables" do
31
+ str = @device.get("/sys/debug")
32
+ str.nil?.should eq(false)
33
+ end
34
+
35
+ it "can determine the correct number of tuners" do
36
+ count1 = @device.tuner_count
37
+ count2 = @device.get("/sys/debug").split(/\n/) \
38
+ .count { |l| l =~ /^t\d+: / }
39
+ count1.should eq(count2)
40
+ end
41
+
42
+ it "can return a list of tuners" do
43
+ tuners = @device.tuners
44
+ tuners.empty?.should eq(false)
45
+ tuners.each do |t|
46
+ t.channel.should eq(@device.get("%s/channel" % t.tuner))
47
+ end
48
+ end
49
+ end
50
+
51
+ describe HDHomeRun::Tuner do
52
+ before :all do
53
+ @tuner = HDHomeRun.discover.map do |device|
54
+ HDHomeRun::Device.new(device[:id]).tuners
55
+ end.flatten.find do |tuner|
56
+ tuner.target == "none"
57
+ end
58
+ @tuner.nil?.should eq(false)
59
+
60
+ @device = HDHomeRun::Device.new(@tuner.id)
61
+
62
+ # Pull the test channel from the environment
63
+ @channel = ENV['FFI_HDHOMERUN_TEST_CHANNEL']
64
+
65
+ # Failing that, do a channel scan to find a channel with strong signal.
66
+ @channel ||= (2..69).to_a.find do |channel|
67
+ @tuner.channel = channel
68
+ sleep 0.75
69
+ @tuner.status !~ / lock=none /
70
+ end
71
+
72
+ fail "Unable to find good channel for tests. " +
73
+ "Please set the environment variable FFI_HDHOMERUN_TEST_CHANNEL " +
74
+ "to a channel that comes in clearly on your tuner" unless @channel
75
+ end
76
+
77
+ it "can set and get the channel" do
78
+ @tuner.channel = "none"
79
+ @tuner.channel.should eq("none")
80
+ @device.get("%s/channel" % @tuner.tuner).should eq("none")
81
+
82
+ channel = "auto:%d" % @channel
83
+ @tuner.channel = channel
84
+ @tuner.channel.should eq(channel)
85
+ @device.get("%s/channel" % @tuner.tuner).should eq(channel)
86
+ end
87
+
88
+ it "can capture from the tuner" do
89
+ @tuner.channel = "auto:%d" % @channel
90
+
91
+ bytes = 0
92
+ calls = 25
93
+ Timeout::timeout(2) do
94
+ @tuner.capture do |buf|
95
+ bytes += buf.size
96
+ calls -= 1
97
+ break if calls == 0
98
+ end
99
+ end
100
+
101
+ bytes.size.should be > 0
102
+ end
103
+ end
104
+
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ffi-hdhomerun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.100b91730e74
5
- prerelease: 7
4
+ version: '0.4'
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - David M. Lary
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-03 00:00:00.000000000Z
12
+ date: 2012-07-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi
16
- requirement: &12302400 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,12 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *12302400
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  description: Ruby FFI bindings for libhdhomerun
26
31
  email:
27
32
  - dmlary@gmail.com
@@ -31,11 +36,12 @@ extra_rdoc_files: []
31
36
  files:
32
37
  - .hgignore
33
38
  - Gemfile
34
- - README.txt
39
+ - README.md
35
40
  - Rakefile
36
41
  - ffi-hdhomerun.gemspec
37
42
  - lib/ffi-hdhomerun.rb
38
43
  - lib/ffi/hdhomerun.rb
44
+ - spec/hdhomerun_spec.rb
39
45
  homepage: http://bitbucket.org/dmlary/ffi-hdhomerun
40
46
  licenses: []
41
47
  post_install_message:
@@ -51,13 +57,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
51
57
  required_rubygems_version: !ruby/object:Gem::Requirement
52
58
  none: false
53
59
  requirements:
54
- - - ! '>'
60
+ - - ! '>='
55
61
  - !ruby/object:Gem::Version
56
- version: 1.3.1
62
+ version: '0'
57
63
  requirements: []
58
64
  rubyforge_project: ffi-hdhomerun
59
- rubygems_version: 1.8.10
65
+ rubygems_version: 1.8.24
60
66
  signing_key:
61
67
  specification_version: 3
62
68
  summary: Ruby FFI bindings for libhdhomerun
63
- test_files: []
69
+ test_files:
70
+ - spec/hdhomerun_spec.rb
data/README.txt DELETED
@@ -1,62 +0,0 @@
1
- = ffi-hdhomerun
2
- http://bitbucket.org/dmlary/ffi-hdhomerun
3
-
4
- == Description
5
- Ruby-FFI bindings and wrappers for libhdhomerun.
6
-
7
- == Features
8
- Allows the user to capture video from an HDHomeRun device within Ruby.
9
-
10
- == What's missing
11
- * Discovery
12
- * Documentation
13
-
14
- == Usage
15
- require 'ffi-hdhomerun'
16
-
17
- # Allocate a tuner
18
- tuner = HDHomeRun::Tuner.new(:id => 'FFFFFFFF', :tuner => 0)
19
-
20
- # Set the channel and optionally the program
21
- tuner.channel = 20
22
- tuner.program = 3
23
-
24
- # Capture some data
25
- File.open("capture.ts", "w") do |file|
26
- puts "Capturing (^C to stop):"
27
- tuner.capture do |buf|
28
- file.write(buf)
29
- STDOUT.write(buf.size > 0 ? "." : "?")
30
- STDOUT.flush
31
- end
32
- end
33
-
34
- == Requirements
35
- - libhdhomerun >= 1.0
36
- - ffi >= 1.0
37
-
38
- == Copyright
39
- Copyright (c) 2011, David M. Lary
40
- All rights reserved.
41
-
42
- == License
43
- Redistribution and use in source and binary forms, with or without
44
- modification, are permitted provided that the following conditions are met:
45
- * Redistributions of source code must retain the above copyright
46
- notice, this list of conditions and the following disclaimer.
47
- * Redistributions in binary form must reproduce the above copyright
48
- notice, this list of conditions and the following disclaimer in the
49
- documentation and/or other materials provided with the distribution.
50
- * The name of the author may not be used to endorse or promote products
51
- derived from this software without specific prior written permission.
52
-
53
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
54
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
57
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
58
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
59
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
60
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
61
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
62
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.