ffi-hdhomerun 0.1.e8573162d6c7
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/.hgignore +10 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/ffi-hdhomerun.gemspec +22 -0
- data/lib/ffi-hdhomerun.rb +131 -0
- data/lib/ffi/hdhomerun.rb +59 -0
- metadata +62 -0
data/.hgignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ffi-hdhomerun"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ffi-hdhomerun"
|
7
|
+
s.version = "%s.%s" % [ HDHomeRun::VERSION,
|
8
|
+
`hg id -i`.chomp.sub("+", "m") ]
|
9
|
+
s.authors = ["David M. Lary"]
|
10
|
+
s.email = ["dmlary@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/dmlary/ffi-hdhomerun"
|
12
|
+
s.summary = %q{Ruby FFI bindings for libhdhomerun}
|
13
|
+
s.description = %q{Ruby FFI bindings for libhdhomerun}
|
14
|
+
|
15
|
+
s.rubyforge_project = "ffi-hdhomerun"
|
16
|
+
|
17
|
+
s.files = `hg locate`.split("\n")
|
18
|
+
s.test_files = `hg locate -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `hg locate -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.add_dependency "ffi"
|
22
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'ffi/hdhomerun'
|
2
|
+
|
3
|
+
module HDHomeRun
|
4
|
+
VERSION = "0.1"
|
5
|
+
|
6
|
+
class Tuner
|
7
|
+
include FFI::HDHomeRun
|
8
|
+
|
9
|
+
attr_reader :hd, :id, :tuner
|
10
|
+
|
11
|
+
# Initialize new hdhomerun tuner
|
12
|
+
#
|
13
|
+
# Arguments:
|
14
|
+
# [:id] HDHomeRun id (default: "FFFFFFFF")
|
15
|
+
# [:tuner] Tuner number (default: "/tuner0")
|
16
|
+
#
|
17
|
+
def initialize(p={})
|
18
|
+
p = {:id => p} unless p.is_a? Hash
|
19
|
+
p[:id] ||= "FFFFFFFF"
|
20
|
+
p[:tuner] ||= "/tuner0"
|
21
|
+
|
22
|
+
@hd = create_from_str(p[:id].to_s, nil)
|
23
|
+
raise ArgumentError, "Invalid device id: #{id}" if @hd.null?
|
24
|
+
@id = get_device_id_requested(@hd)
|
25
|
+
|
26
|
+
@tuner = p[:tuner]
|
27
|
+
@tuner = "/tuner%d" % @tuner unless @tuner.is_a? String
|
28
|
+
if set_tuner_from_str(@hd, @tuner) <= 0
|
29
|
+
raise RuntimeError, "invalid tuner: %s" % @tuner
|
30
|
+
end
|
31
|
+
|
32
|
+
raise ArgumentError, "Invalid device id: %08X" % id \
|
33
|
+
unless validate_device_id(@id)
|
34
|
+
raise RuntimeError, "Unable to connect to device" \
|
35
|
+
unless get_model_str(@hd)
|
36
|
+
end
|
37
|
+
|
38
|
+
def get(key)
|
39
|
+
value = FFI::MemoryPointer.new(:pointer, 1)
|
40
|
+
error = FFI::MemoryPointer.new(:pointer, 1)
|
41
|
+
|
42
|
+
if get_var(@hd, key, value, error) < 0
|
43
|
+
raise RuntimeError,
|
44
|
+
"communication error sending request to hdhomerun device"
|
45
|
+
end
|
46
|
+
value = value.read_pointer
|
47
|
+
error = error.read_pointer
|
48
|
+
|
49
|
+
raise RuntimeError, error.read_string unless error.null?
|
50
|
+
value.read_string
|
51
|
+
end
|
52
|
+
|
53
|
+
def set(key, value)
|
54
|
+
error = FFI::MemoryPointer.new(:pointer, 1)
|
55
|
+
|
56
|
+
if set_var(@hd, key, value.to_s, nil, error) < 0
|
57
|
+
raise RuntimeError,
|
58
|
+
"communication error sending request to hdhomerun device"
|
59
|
+
end
|
60
|
+
error = error.read_pointer
|
61
|
+
|
62
|
+
raise RuntimeError, error.read_string unless error.null?
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_tuner(key)
|
67
|
+
get("%s/%s" % [@tuner, key.to_s])
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_tuner(key, value)
|
71
|
+
set("%s/%s" % [@tuner, key.to_s], value)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Dynamically define the following getter and setter methods
|
75
|
+
%w{channel channelmap filter program target}.each do |name|
|
76
|
+
define_method name do
|
77
|
+
get_tuner(name)
|
78
|
+
end
|
79
|
+
|
80
|
+
define_method "#{name}=" do |value|
|
81
|
+
set_tuner(name, value)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Dynamically define these as simple getter methods
|
86
|
+
%w{status streaminfo}.each do |name|
|
87
|
+
define_method name do
|
88
|
+
get_tuner(name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def stats
|
93
|
+
ptr = FFI::MemoryPointer.new Stats
|
94
|
+
out = Stats.new ptr;
|
95
|
+
get_video_stats(@hd, out)
|
96
|
+
out
|
97
|
+
end
|
98
|
+
|
99
|
+
def capture(p={}, &block)
|
100
|
+
raise ArgumentError, "no block" unless block_given?
|
101
|
+
raise RuntimeError, "unable to start stream" \
|
102
|
+
if stream_start(@hd) <= 0
|
103
|
+
|
104
|
+
max_delay = (p[:delay] || 0.064).to_f
|
105
|
+
last_yield = 0
|
106
|
+
|
107
|
+
begin
|
108
|
+
len_ptr = FFI::MemoryPointer.new :ulong
|
109
|
+
|
110
|
+
while true do
|
111
|
+
start = Time.now.to_f
|
112
|
+
data = stream_recv(@hd, VIDEO_DATA_BUFFER_SIZE_1S, len_ptr)
|
113
|
+
|
114
|
+
if !data.null? or start - last_yield > 1.0
|
115
|
+
last_yield = start
|
116
|
+
|
117
|
+
len = len_ptr.read_ulong
|
118
|
+
buf = data.read_string(len) if len > 0
|
119
|
+
buf ||= ""
|
120
|
+
yield(buf) or break
|
121
|
+
end
|
122
|
+
|
123
|
+
delay = max_delay - (Time.now.to_f - start)
|
124
|
+
sleep delay if delay > 0
|
125
|
+
end
|
126
|
+
ensure
|
127
|
+
stream_stop(@hd)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module HDHomeRun
|
5
|
+
extend FFI::Library
|
6
|
+
ffi_lib "libhdhomerun.so.1"
|
7
|
+
|
8
|
+
VIDEO_DATA_BUFFER_SIZE_1S = (20000000 / 8)
|
9
|
+
|
10
|
+
attach_function :create_from_str,
|
11
|
+
:hdhomerun_device_create_from_str,
|
12
|
+
[:string, :pointer], :pointer
|
13
|
+
attach_function :get_device_id_requested,
|
14
|
+
:hdhomerun_device_get_device_id_requested,
|
15
|
+
[:pointer], :uint
|
16
|
+
attach_function :validate_device_id,
|
17
|
+
:hdhomerun_discover_validate_device_id,
|
18
|
+
[:uint], :int
|
19
|
+
attach_function :get_model_str,
|
20
|
+
:hdhomerun_device_get_model_str,
|
21
|
+
[:pointer], :string
|
22
|
+
attach_function :get_var,
|
23
|
+
:hdhomerun_device_get_var,
|
24
|
+
[:pointer, :string, :pointer, :pointer], :int
|
25
|
+
attach_function :set_var,
|
26
|
+
:hdhomerun_device_set_var,
|
27
|
+
[:pointer, :string, :string, :pointer, :pointer], :int
|
28
|
+
attach_function :set_tuner_from_str,
|
29
|
+
:hdhomerun_device_set_tuner_from_str,
|
30
|
+
[:pointer, :string], :int
|
31
|
+
attach_function :stream_start,
|
32
|
+
:hdhomerun_device_stream_start,
|
33
|
+
[:pointer], :int
|
34
|
+
attach_function :stream_stop,
|
35
|
+
:hdhomerun_device_stream_stop,
|
36
|
+
[:pointer], :void
|
37
|
+
attach_function :stream_recv,
|
38
|
+
:hdhomerun_device_stream_recv,
|
39
|
+
[:pointer, :ulong, :pointer], :pointer
|
40
|
+
attach_function :get_video_stats,
|
41
|
+
:hdhomerun_device_get_video_stats,
|
42
|
+
[:pointer, :pointer], :void
|
43
|
+
|
44
|
+
class Stats < FFI::Struct
|
45
|
+
layout :packets, :uint,
|
46
|
+
:network_errors, :uint,
|
47
|
+
:transport_errors, :uint,
|
48
|
+
:sequence_errors, :uint,
|
49
|
+
:overflow_errors, :uint
|
50
|
+
|
51
|
+
%w{packets network_errors transport_errors
|
52
|
+
sequence_errors overflow_errors}.each do |name|
|
53
|
+
define_method name do
|
54
|
+
send(:[], name.to_sym)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ffi-hdhomerun
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.e8573162d6c7
|
5
|
+
prerelease: 4
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David M. Lary
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-07 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ffi
|
16
|
+
requirement: &8668500 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *8668500
|
25
|
+
description: Ruby FFI bindings for libhdhomerun
|
26
|
+
email:
|
27
|
+
- dmlary@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .hgignore
|
33
|
+
- Gemfile
|
34
|
+
- Rakefile
|
35
|
+
- ffi-hdhomerun.gemspec
|
36
|
+
- lib/ffi-hdhomerun.rb
|
37
|
+
- lib/ffi/hdhomerun.rb
|
38
|
+
homepage: http://github.com/dmlary/ffi-hdhomerun
|
39
|
+
licenses: []
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>'
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.3.1
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project: ffi-hdhomerun
|
58
|
+
rubygems_version: 1.8.10
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: Ruby FFI bindings for libhdhomerun
|
62
|
+
test_files: []
|