ruby_onvif_client 0.0.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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/lib/ruby_onvif_client/action.rb +31 -0
- data/lib/ruby_onvif_client/client.rb +44 -0
- data/lib/ruby_onvif_client/device_discovery.rb +36 -0
- data/lib/ruby_onvif_client/device_management/get_capabilities.rb +118 -0
- data/lib/ruby_onvif_client/device_management/get_device_information.rb +30 -0
- data/lib/ruby_onvif_client/device_management/get_network_interfaces.rb +131 -0
- data/lib/ruby_onvif_client/device_management/get_network_protocols.rb +32 -0
- data/lib/ruby_onvif_client/device_management/get_system_date_and_time.rb +60 -0
- data/lib/ruby_onvif_client/device_management/get_users.rb +32 -0
- data/lib/ruby_onvif_client/device_management/set_network_interfaces.rb +83 -0
- data/lib/ruby_onvif_client/device_management/set_system_date_and_time.rb +48 -0
- data/lib/ruby_onvif_client/device_management/set_system_factory_default.rb +21 -0
- data/lib/ruby_onvif_client/device_management/system_reboot.rb +25 -0
- data/lib/ruby_onvif_client/device_management.rb +10 -0
- data/lib/ruby_onvif_client/media/get_audio_encoder_configuration.rb +48 -0
- data/lib/ruby_onvif_client/media/get_audio_encoder_configuration_options.rb +57 -0
- data/lib/ruby_onvif_client/media/get_audio_encoder_configurations.rb +49 -0
- data/lib/ruby_onvif_client/media/get_audio_source_configurations.rb +32 -0
- data/lib/ruby_onvif_client/media/get_stream_uri.rb +58 -0
- data/lib/ruby_onvif_client/media/get_video_encoder_configuration.rb +104 -0
- data/lib/ruby_onvif_client/media/get_video_encoder_configuration_options.rb +95 -0
- data/lib/ruby_onvif_client/media/get_video_source_configuration.rb +55 -0
- data/lib/ruby_onvif_client/media/get_video_source_configurations.rb +40 -0
- data/lib/ruby_onvif_client/media.rb +10 -0
- data/lib/ruby_onvif_client/message.rb +41 -0
- data/lib/ruby_onvif_client/path.rb +5 -0
- data/lib/ruby_onvif_client/service.rb +37 -0
- data/lib/ruby_onvif_client.rb +3 -0
- data/ruby-onvif-client.gemspec +20 -0
- metadata +143 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module DeviceManagementAction
|
|
5
|
+
class SetSystemFactoryDefault < Action
|
|
6
|
+
# default_type = 'Hard' // 'Hard', 'Soft' FactoryDefault
|
|
7
|
+
def run default_type, cb
|
|
8
|
+
message = Message.new namespaces: {:'xmlns:sch' => 'http://www.onvif.org/ver10/schema'}
|
|
9
|
+
message.body = ->(xml) do
|
|
10
|
+
xml.wsdl(:SetSystemFactoryDefault) do
|
|
11
|
+
xml.wsdl :FactoryDefault, default_type
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
send_message message do |success, result|
|
|
15
|
+
#????
|
|
16
|
+
callback cb, success, result
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module DeviceManagementAction
|
|
5
|
+
class SystemReboot < Action
|
|
6
|
+
def run cb
|
|
7
|
+
message = Message.new
|
|
8
|
+
message.body = ->(xml) do
|
|
9
|
+
xml.wsdl(:SystemReboot)
|
|
10
|
+
end
|
|
11
|
+
send_message message do |success, result|
|
|
12
|
+
if success
|
|
13
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
14
|
+
msg = {
|
|
15
|
+
msg: value(xml_doc, '//tds:Message')
|
|
16
|
+
}
|
|
17
|
+
callback cb, success, msg
|
|
18
|
+
else
|
|
19
|
+
callback cb, success, result
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module MediaAction
|
|
5
|
+
class GetAudioEncoderConfiguration < Action
|
|
6
|
+
# c_token 的结构
|
|
7
|
+
# c_token //[ReferenceToken] Token of the requested audio encoder configuration.
|
|
8
|
+
#
|
|
9
|
+
def run c_token, cb
|
|
10
|
+
message = Message.new
|
|
11
|
+
message.body = ->(xml) do
|
|
12
|
+
xml.wsdl(:GetAudioEncoderConfiguration) do
|
|
13
|
+
xml.wsdl :ConfigurationToken, c_token
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
send_message message do |success, result|
|
|
17
|
+
if success
|
|
18
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
19
|
+
success_result = {
|
|
20
|
+
configuration: {
|
|
21
|
+
name: value(xml_doc, '//tt:name'),
|
|
22
|
+
use_count: value(xml_doc, '//tt:UseCount'),
|
|
23
|
+
token: attribute(xml_doc, 'token'),
|
|
24
|
+
encoding: value(xml_doc, '//tt:Encoding'),
|
|
25
|
+
bitrate: value(xml_doc, '//tt:Bitrate'),
|
|
26
|
+
sample_rate: value(xml_doc, '//tt:SampleRate'),
|
|
27
|
+
multicast: {
|
|
28
|
+
address: {
|
|
29
|
+
type: value(xml_doc, '//tt:Type'),
|
|
30
|
+
ipv4_address: value(xml_doc, '//tt:IPv4Address'),
|
|
31
|
+
ipv6_address: value(xml_doc, '//tt:IPv6Address')
|
|
32
|
+
},
|
|
33
|
+
port: value(xml_doc, "//tt:Port"),
|
|
34
|
+
ttl: value(xml_doc, "//tt:TTL"),
|
|
35
|
+
auto_start: value(xml_doc, "//tt:AutoStart")
|
|
36
|
+
},
|
|
37
|
+
session_timeout: value(xml_doc, '//tt:SessionTimeout')
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
callback cb, success, success_result
|
|
41
|
+
else
|
|
42
|
+
callback cb, success, result
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module MediaAction
|
|
5
|
+
class GetAudioEncoderConfigurationOptions < Action
|
|
6
|
+
# options 的结构
|
|
7
|
+
# {
|
|
8
|
+
# c_token: "xxxxxxx", //[ReferenceToken] Optional audio encoder configuration token that specifies an existing configuration that the options are intended for.
|
|
9
|
+
# p_token: "xxxxxxx" //[ReferenceToken] Optional ProfileToken that specifies an existing media profile that the options shall be compatible with.
|
|
10
|
+
# }
|
|
11
|
+
def run options, cb
|
|
12
|
+
message = Message.new
|
|
13
|
+
message.body = ->(xml) do
|
|
14
|
+
xml.wsdl(:GetAudioEncoderConfigurationOptions) do
|
|
15
|
+
xml.wsdl :ConfigurationToken, options["c_token"]
|
|
16
|
+
xml.wsdl :ProfileToken, options["p_token"]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
send_message message do |success, result|
|
|
20
|
+
if success
|
|
21
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
22
|
+
node_options = []
|
|
23
|
+
xml_doc.xpath('//trt:Options//tt:Options').each do |node|
|
|
24
|
+
this_options = {encoding: value(node, 'tt:Encoding')}
|
|
25
|
+
bitrate = node.at_xpath("tt:BitrateList")
|
|
26
|
+
unless bitrate.nil?
|
|
27
|
+
bitrate_list = []
|
|
28
|
+
node.xpath('tt:BitrateList//tt:Items').each do |item|
|
|
29
|
+
bitrate_list << {
|
|
30
|
+
items: item.content
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
this_options["bitrate_list"] = bitrate_list
|
|
34
|
+
end
|
|
35
|
+
sample_rate = node.at_xpath("tt:SampleRateList")
|
|
36
|
+
unless sample_rate.nil?
|
|
37
|
+
sample_rate_list = []
|
|
38
|
+
node.xpath('tt:SampleRateList//tt:Items').each do |item|
|
|
39
|
+
sample_rate_list << {
|
|
40
|
+
items: item.content
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
this_options["sample_rate_list"] = sample_rate_list
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
node_options << this_options
|
|
48
|
+
end
|
|
49
|
+
callback cb, success, node_options
|
|
50
|
+
else
|
|
51
|
+
callback cb, success, result
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module MediaAction
|
|
5
|
+
class GetAudioEncoderConfigurations < Action
|
|
6
|
+
def run cb
|
|
7
|
+
message = Message.new
|
|
8
|
+
message.body = ->(xml) do
|
|
9
|
+
xml.wsdl(:GetAudioEncoderConfigurations)
|
|
10
|
+
end
|
|
11
|
+
send_message message do |success, result|
|
|
12
|
+
if success
|
|
13
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
14
|
+
configurations = []
|
|
15
|
+
xml_doc.xpath('//trt:Configurations').each do |node|
|
|
16
|
+
bounds = node.xpath('tt:Bounds')
|
|
17
|
+
multicast = node.xpath('tt:Multicast')
|
|
18
|
+
address = multicast.xpath('tt:Address')
|
|
19
|
+
configuration = {
|
|
20
|
+
name: value(node, 'tt:Name'),
|
|
21
|
+
use_count: value(node, 'tt:UseCount'),
|
|
22
|
+
token: attribute(node, 'token'),
|
|
23
|
+
encoding: value(node, 'tt:Encoding'),
|
|
24
|
+
bitrate: value(node, 'tt:Bitrate'),
|
|
25
|
+
sample_rate: value(node, 'tt:SampleRate'),
|
|
26
|
+
|
|
27
|
+
multicast: {
|
|
28
|
+
address: {
|
|
29
|
+
type: value(address, 'tt:Type'),
|
|
30
|
+
ipv4_address: value(address, 'tt:IPv4Address'),
|
|
31
|
+
ipv6_address: value(address, 'tt:IPv6Address')
|
|
32
|
+
},
|
|
33
|
+
port: value(multicast, "tt:Port"),
|
|
34
|
+
ttl: value(multicast, "tt:TTL"),
|
|
35
|
+
auto_start: value(multicast, "tt:AutoStart")
|
|
36
|
+
},
|
|
37
|
+
session_timeout: value(node, 'tt:SessionTimeout')
|
|
38
|
+
}
|
|
39
|
+
configurations << configuration
|
|
40
|
+
end
|
|
41
|
+
callback cb, success, configurations
|
|
42
|
+
else
|
|
43
|
+
callback cb, success, result
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module MediaAction
|
|
5
|
+
class GetAudioSourceConfigurations < Action
|
|
6
|
+
def run cb
|
|
7
|
+
message = Message.new
|
|
8
|
+
message.body = ->(xml) do
|
|
9
|
+
xml.wsdl(:GetAudioSourceConfigurations)
|
|
10
|
+
end
|
|
11
|
+
send_message message do |success, result|
|
|
12
|
+
if success
|
|
13
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
14
|
+
configurations = []
|
|
15
|
+
xml_doc.xpath('//trt:Configurations').each do |node|
|
|
16
|
+
configuration = {
|
|
17
|
+
name: value(node, 'tt:Name'),
|
|
18
|
+
use_count: value(node, 'tt:UseCount'),
|
|
19
|
+
token: attribute(node, 'token'),
|
|
20
|
+
source_token: value(xml_doc, '//tt:SourceToken')
|
|
21
|
+
}
|
|
22
|
+
configurations << configuration
|
|
23
|
+
end
|
|
24
|
+
callback cb, success, configurations
|
|
25
|
+
else
|
|
26
|
+
callback cb, success, result
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module MediaAction
|
|
5
|
+
class GetStreamUri < Action
|
|
6
|
+
# options 的结构
|
|
7
|
+
# {
|
|
8
|
+
# stream_setup: {
|
|
9
|
+
# stream: 'RTP-Unicast', //'RTP-Unicast', 'RTP-Multicast'
|
|
10
|
+
# transport: {
|
|
11
|
+
# protocol: 'UDP', //'UDP', 'TCP', 'RTSP', 'HTTP'
|
|
12
|
+
# tunnel:{
|
|
13
|
+
# protocol: 'TCP', //'UDP', 'TCP', 'RTSP', 'HTTP'
|
|
14
|
+
# }
|
|
15
|
+
# }
|
|
16
|
+
# }
|
|
17
|
+
# profile_token: "xxxxxxx" // [ReferenceToken]
|
|
18
|
+
# }
|
|
19
|
+
def run options, cb
|
|
20
|
+
message = Message.new namespaces: {:'xmlns:sch' => 'http://www.onvif.org/ver10/schema'}
|
|
21
|
+
message.body = ->(xml) do
|
|
22
|
+
xml.wsdl(:GetStreamUri) do
|
|
23
|
+
xml.wsdl(:StreamSetup) do
|
|
24
|
+
xml.sch :Stream, options["stream_setup"]["stream"]
|
|
25
|
+
xml.sch :Transport do
|
|
26
|
+
xml.sch :Protocol, options["stream_setup"]["transport"]["protocol"]
|
|
27
|
+
xml.sch :Tunnel do
|
|
28
|
+
tunnel = options["stream_setup"]["transport"]["tunnel"]
|
|
29
|
+
unless tunnel.nil?
|
|
30
|
+
xml.sch :Protocol,options["stream_setup"]["transport"]["tunnel"]["protocol"]
|
|
31
|
+
xml.sch :Tunnel,options["stream_setup"]["transport"]["tunnel"]["tunnel"]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
xml.wsdl :ProfileToken, options["profile_token"]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
send_message message do |success, result|
|
|
40
|
+
if success
|
|
41
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
42
|
+
media_uri = {
|
|
43
|
+
media_uri: {
|
|
44
|
+
uri: value(xml_doc, '//tt:Uri'),
|
|
45
|
+
invalid_after_connect: value(xml_doc, '//tt:InvalidAfterConnect'),
|
|
46
|
+
invalid_after_reboot: value(xml_doc, '//tt:InvalidAfterReboot'),
|
|
47
|
+
timeout: value(xml_doc, '//tt:Timeout')
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
callback cb, success, media_uri
|
|
51
|
+
else
|
|
52
|
+
callback cb, success, result
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module MediaAction
|
|
5
|
+
class GetVideoEncoderConfiguration < Action
|
|
6
|
+
# c_token 的结构 // [ReferenceToken] Token of the requested video encoder configuration.
|
|
7
|
+
def run c_token, cb
|
|
8
|
+
message = Message.new
|
|
9
|
+
message.body = ->(xml) do
|
|
10
|
+
xml.wsdl(:GetVideoEncoderConfiguration) do
|
|
11
|
+
xml.wsdl :ConfigurationToken, c_token
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
send_message message do |success, result|
|
|
15
|
+
if success
|
|
16
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
17
|
+
cfr = xml_doc.at_xpath("//trt:Configuration")
|
|
18
|
+
rcl = xml_doc.at_xpath("//tt:RateControl")
|
|
19
|
+
configuration = {
|
|
20
|
+
name: value(xml_doc, "//tt:Name"),
|
|
21
|
+
use_count: value(xml_doc, "//tt:UseCount"),
|
|
22
|
+
token: attribute(cfr,"token"),
|
|
23
|
+
encoding: value(xml_doc, "//tt:Encoding"),
|
|
24
|
+
resolution: {
|
|
25
|
+
width: value(_get_node(xml_doc, "//tt:Resolution"), "//tt:Width"),
|
|
26
|
+
height: value(_get_node(xml_doc, "//tt:Resolution"), "//tt:Height")
|
|
27
|
+
},
|
|
28
|
+
quality: value(xml_doc, "//tt:Quality"),
|
|
29
|
+
rateControl: {
|
|
30
|
+
frame_rate_limit: value(rcl, "tt:FrameRateLimit"),
|
|
31
|
+
encoding_interval: value(rcl, "tt:EncodingInterval"),
|
|
32
|
+
bitrate_limit: value(rcl, "tt:BitrateLimit")
|
|
33
|
+
},
|
|
34
|
+
mpeg4: {
|
|
35
|
+
gov_length: value(_get_node(xml_doc, "//tt:MPEG4"), "//tt:GovLength"),
|
|
36
|
+
mpeg4_profile: value(_get_node(xml_doc, "//tt:MPEG4"), "//tt:Mpeg4Profile")
|
|
37
|
+
},
|
|
38
|
+
h264: {
|
|
39
|
+
gov_length: value(_get_node(xml_doc, "//tt:H264"), "//tt:GovLength"),
|
|
40
|
+
h264_profile: value(_get_node(xml_doc, "//tt:H264"), "//tt:H264Profile")
|
|
41
|
+
},
|
|
42
|
+
multicast: {
|
|
43
|
+
address: {
|
|
44
|
+
type: value(_get_node(xml_doc, "//tt:Multicast//tt:Address"), '//tt:Type'),
|
|
45
|
+
ipv4_address: value(_get_node(xml_doc, "//tt:Multicast//tt:Address"), '//tt:IPv4Address'),
|
|
46
|
+
ipv6_address: value(_get_node(xml_doc, "//tt:Multicast//tt:Address"), '//tt:IPv6Address')
|
|
47
|
+
},
|
|
48
|
+
port: value(_get_node(xml_doc, "//tt:Multicast"), "tt:Port"),
|
|
49
|
+
ttl: value(_get_node(xml_doc, "//tt:Multicast"), "tt:TTL"),
|
|
50
|
+
auto_start: value(_get_node(xml_doc, "//tt:Multicast"), "tt:AutoStart")
|
|
51
|
+
},
|
|
52
|
+
session_timeout: value(xml_doc, "//tt:SessionTimeout")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
callback cb, success, configuration
|
|
56
|
+
else
|
|
57
|
+
callback cb, success, result
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
def _get_profiles_supported xml_doc, parent_name
|
|
62
|
+
this_node = xml_doc.at_xpath(parent_name)
|
|
63
|
+
result_val = []
|
|
64
|
+
this_node.each do |node|
|
|
65
|
+
result_val << node.content
|
|
66
|
+
end
|
|
67
|
+
return result_val
|
|
68
|
+
end
|
|
69
|
+
def _get_node parent_node, node_name
|
|
70
|
+
parent_node.at_xpath(node_name)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def _get_each_val xml_doc, parent_name
|
|
74
|
+
result_val = []
|
|
75
|
+
xml_doc.each do |node|
|
|
76
|
+
result_val << _get_width_height(node, parent_name)
|
|
77
|
+
end
|
|
78
|
+
return result_val
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def _get_min_max xml_doc, parent_name
|
|
82
|
+
this_node = xml_doc
|
|
83
|
+
unless parent_name.nil?
|
|
84
|
+
this_node = xml_doc.at_xpath(parent_name)
|
|
85
|
+
end
|
|
86
|
+
return {
|
|
87
|
+
min: value(this_node, "tt:Min"),
|
|
88
|
+
max: value(this_node, "tt:Max")
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def _get_width_height xml_doc, parent_name
|
|
93
|
+
this_node = xml_doc
|
|
94
|
+
unless parent_name.nil?
|
|
95
|
+
this_node = xml_doc.at_xpath(parent_name)
|
|
96
|
+
end
|
|
97
|
+
return {
|
|
98
|
+
width: value(this_node, "tt:Width"),
|
|
99
|
+
height: value(this_node, "tt:Height")
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module MediaAction
|
|
5
|
+
class GetVideoEncoderConfigurationOptions < Action
|
|
6
|
+
# options 的结构
|
|
7
|
+
# {
|
|
8
|
+
# c_token: "xxxxxxx", //[ReferenceToken] Optional audio encoder configuration token that specifies an existing configuration that the options are intended for.
|
|
9
|
+
# p_token: "xxxxxxx" //[ReferenceToken] Optional ProfileToken that specifies an existing media profile that the options shall be compatible with.
|
|
10
|
+
# }
|
|
11
|
+
def run options, cb
|
|
12
|
+
message = Message.new
|
|
13
|
+
message.body = ->(xml) do
|
|
14
|
+
xml.wsdl(:GetVideoEncoderConfigurationOptions) do
|
|
15
|
+
xml.wsdl :ConfigurationToken, options["c_token"]
|
|
16
|
+
xml.wsdl :ProfileToken, options["p_token"]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
send_message message do |success, result|
|
|
20
|
+
if success
|
|
21
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
22
|
+
options = {
|
|
23
|
+
quality_range: _get_min_max(_get_node(xml_doc, "//tt:QualityRange")),
|
|
24
|
+
jpeg: {
|
|
25
|
+
resolutions_available: _get_each_val(_get_node(xml_doc, "//tt:JPEG"), "//tt:ResolutionsAvailable"),
|
|
26
|
+
frame_rate_range: _get_min_max(_get_node(xml_doc, "//tt:JPEG"), "//tt:FrameRateRange"),
|
|
27
|
+
rncoding_interval_range: _get_min_max(_get_node(xml_doc, "//tt:JPEG"), "//tt:FrameRateRange")
|
|
28
|
+
},
|
|
29
|
+
mpeg4: {
|
|
30
|
+
resolutions_available: _get_each_val(_get_node(xml_doc, "//tt:MPEG4"), "//tt:ResolutionsAvailable"),
|
|
31
|
+
gov_length_range: _get_min_max(_get_node(xml_doc, "//tt:MPEG4"), "//tt:GovLengthRange"),
|
|
32
|
+
frame_rate_range: _get_min_max(_get_node(xml_doc, "//tt:MPEG4"), "//tt:FrameRateRange"),
|
|
33
|
+
rncoding_interval_range: _get_min_max(_get_node(xml_doc, "//tt:MPEG4"), "//tt:EncodingIntervalRange"),
|
|
34
|
+
mpeg4_profiles_supported: _get_profiles_supported(_get_node(xml_doc, "//tt:MPEG4"), "//tt:Mpeg4ProfilesSupported")
|
|
35
|
+
},
|
|
36
|
+
h264: {
|
|
37
|
+
resolutions_available: _get_each_val(_get_node(xml_doc, "//tt:h264"), "//tt:ResolutionsAvailable"),
|
|
38
|
+
gov_length_range: _get_min_max(_get_node(xml_doc, "//tt:h264"), "//tt:GovLengthRange"),
|
|
39
|
+
frame_rate_range: _get_min_max(_get_node(xml_doc, "//tt:h264"), "//tt:FrameRateRange"),
|
|
40
|
+
rncoding_interval_range: _get_min_max(_get_node(xml_doc, "//tt:h264"), "//tt:EncodingIntervalRange"),
|
|
41
|
+
h264_profiles_supported: _get_profiles_supported(_get_node(xml_doc, "//tt:h264"), "//tt:H264ProfilesSupported")
|
|
42
|
+
},
|
|
43
|
+
extension: ""
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
callback cb, success, options
|
|
47
|
+
else
|
|
48
|
+
callback cb, success, result
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
def _get_profiles_supported xml_doc, parent_name
|
|
53
|
+
this_node = xml_doc.at_xpath(parent_name)
|
|
54
|
+
result_val = []
|
|
55
|
+
this_node.each do |node|
|
|
56
|
+
result_val << node.content
|
|
57
|
+
end
|
|
58
|
+
return result_val
|
|
59
|
+
end
|
|
60
|
+
def _get_node parent_node, node_name
|
|
61
|
+
parent_node.at_xpath(node_name)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def _get_each_val xml_doc, parent_name
|
|
65
|
+
result_val = []
|
|
66
|
+
xml_doc.each do |node|
|
|
67
|
+
result_val << _get_width_height(node, parent_name)
|
|
68
|
+
end
|
|
69
|
+
return result_val
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def _get_min_max xml_doc, parent_name
|
|
73
|
+
this_node = xml_doc
|
|
74
|
+
unless parent_name.nil?
|
|
75
|
+
this_node = xml_doc.at_xpath(parent_name)
|
|
76
|
+
end
|
|
77
|
+
return {
|
|
78
|
+
min: value(this_node, "tt:Min"),
|
|
79
|
+
max: value(this_node, "tt:Max")
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def _get_width_height xml_doc, parent_name
|
|
84
|
+
this_node = xml_doc
|
|
85
|
+
unless parent_name.nil?
|
|
86
|
+
this_node = xml_doc.at_xpath(parent_name)
|
|
87
|
+
end
|
|
88
|
+
return {
|
|
89
|
+
width: value(this_node, "tt:Width"),
|
|
90
|
+
height: value(this_node, "tt:Height")
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module MediaAction
|
|
5
|
+
class GetVideoSourceConfiguration < Action
|
|
6
|
+
# c_token 的结构
|
|
7
|
+
# c_token //[ReferenceToken] Token of the requested audio encoder configuration.
|
|
8
|
+
#
|
|
9
|
+
def run c_token, cb
|
|
10
|
+
message = Message.new
|
|
11
|
+
message.body = ->(xml) do
|
|
12
|
+
xml.wsdl(:GetVideoSourceConfiguration) do
|
|
13
|
+
xml.wsdl :ConfigurationToken, c_token
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
send_message message do |success, result|
|
|
17
|
+
if success
|
|
18
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
19
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
20
|
+
configurations = []
|
|
21
|
+
xml_doc.xpath('//trt:Configurations').each do |node|
|
|
22
|
+
bounds = node.xpath('tt:Bounds')
|
|
23
|
+
multicast = node.xpath('tt:Multicast')
|
|
24
|
+
address = multicast.xpath('tt:Address')
|
|
25
|
+
configuration = {
|
|
26
|
+
name: value(node, 'tt:Name'),
|
|
27
|
+
use_count: value(node, 'tt:UseCount'),
|
|
28
|
+
token: attribute(node, 'token'),
|
|
29
|
+
encoding: value(node, 'tt:Encoding'),
|
|
30
|
+
bitrate: value(node, 'tt:Bitrate'),
|
|
31
|
+
sample_rate: value(node, 'tt:SampleRate'),
|
|
32
|
+
|
|
33
|
+
multicast: {
|
|
34
|
+
address: {
|
|
35
|
+
type: value(address, 'tt:Type'),
|
|
36
|
+
ipv4_address: value(address, 'tt:IPv4Address'),
|
|
37
|
+
ipv6_address: value(address, 'tt:IPv6Address')
|
|
38
|
+
},
|
|
39
|
+
port: value(multicast, "tt:Port"),
|
|
40
|
+
ttl: value(multicast, "tt:TTL"),
|
|
41
|
+
auto_start: value(multicast, "tt:AutoStart")
|
|
42
|
+
},
|
|
43
|
+
session_timeout: value(node, 'tt:SessionTimeout')
|
|
44
|
+
}
|
|
45
|
+
configurations << configuration
|
|
46
|
+
end
|
|
47
|
+
callback cb, success, configurations
|
|
48
|
+
else
|
|
49
|
+
callback cb, success, result
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative '../action'
|
|
2
|
+
|
|
3
|
+
module ONVIF
|
|
4
|
+
module MediaAction
|
|
5
|
+
class GetVideoSourceConfigurations < Action
|
|
6
|
+
def run cb
|
|
7
|
+
message = Message.new
|
|
8
|
+
message.body = ->(xml) do
|
|
9
|
+
xml.wsdl(:GetVideoSourceConfigurations)
|
|
10
|
+
end
|
|
11
|
+
send_message message do |success, result|
|
|
12
|
+
if success
|
|
13
|
+
xml_doc = Nokogiri::XML(result[:content])
|
|
14
|
+
configurations = []
|
|
15
|
+
xml_doc.xpath('//trt:Configurations').each do |node|
|
|
16
|
+
bounds = node.at_xpath('tt:Bounds')
|
|
17
|
+
configuration = {
|
|
18
|
+
name: value(node, 'tt:Name'),
|
|
19
|
+
use_count: value(node, 'tt:UseCount'),
|
|
20
|
+
token: attribute(node, 'token'),
|
|
21
|
+
source_token: value(node, 'tt:SourceToken'),
|
|
22
|
+
bounds: {
|
|
23
|
+
x: attribute(bounds, "x"),
|
|
24
|
+
y: attribute(bounds, "y"),
|
|
25
|
+
width: attribute(bounds, "width"),
|
|
26
|
+
height: attribute(bounds, "height")
|
|
27
|
+
},
|
|
28
|
+
extension: ""
|
|
29
|
+
}
|
|
30
|
+
configurations << configuration
|
|
31
|
+
end
|
|
32
|
+
callback cb, success, configurations
|
|
33
|
+
else
|
|
34
|
+
callback cb, success, result
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'nokogiri'
|
|
2
|
+
require "builder"
|
|
3
|
+
require "akami"
|
|
4
|
+
|
|
5
|
+
module ONVIF
|
|
6
|
+
class Message
|
|
7
|
+
attr_writer :body
|
|
8
|
+
|
|
9
|
+
def initialize options = {}
|
|
10
|
+
@options = {
|
|
11
|
+
username: 'admin',
|
|
12
|
+
password: 'admin',
|
|
13
|
+
namespaces: {}
|
|
14
|
+
}.merge(options)
|
|
15
|
+
|
|
16
|
+
@namespaces = {
|
|
17
|
+
:'xmlns:soap' => "http://www.w3.org/2003/05/soap-envelope",
|
|
18
|
+
:'xmlns:wsdl' => "http://www.onvif.org/ver10/device/wsdl"
|
|
19
|
+
}.merge(@options[:namespaces])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def header
|
|
23
|
+
wsse = Akami.wsse
|
|
24
|
+
wsse.credentials(@options[:username], @options[:password])
|
|
25
|
+
wsse.created_at = Time.now
|
|
26
|
+
wsse.to_xml
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_s
|
|
30
|
+
Builder::XmlMarkup.new(indent: 4).soap(:Envelope, @namespaces) do |xml|
|
|
31
|
+
xml.soap(:Header) do
|
|
32
|
+
xml << header
|
|
33
|
+
end
|
|
34
|
+
xml.soap(:Body) do
|
|
35
|
+
@body.call(xml) if @body.class == Proc
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|