Skip to content

Commit

Permalink
Add GetServiceCapabilities for media ver10 (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
waranlogesh authored Jun 26, 2024
1 parent 0a68201 commit 050db0f
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 0 deletions.
44 changes: 44 additions & 0 deletions lib/media/ver10/get_service_capabilities.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
defmodule Onvif.Media.Ver10.GetServiceCapabilities do
import SweetXml
import XmlBuilder

require Logger

alias Onvif.Device

def soap_action, do: "http://www.onvif.org/ver10/media/wsdl/GetServiceCapabilities"

@spec request(Device.t()) :: {:ok, any} | {:error, map()}
def request(device),
do: Onvif.Media.Ver10.Media.request(device, [], __MODULE__)

def request_body do
element(:"s:Body", [element(:"trt:GetServiceCapabilities")])
end

def response(xml_response_body) do
parsed =
xml_response_body
|> parse(namespace_conformant: true, quiet: true)
|> xpath(
~x"//s:Envelope/s:Body/trt:GetServiceCapabilitiesResponse/trt:Capabilities"e
|> add_namespace("s", "http://www.w3.org/2003/05/soap-envelope")
|> add_namespace("trt", "http://www.onvif.org/ver10/media/wsdl")
|> add_namespace("tt", "http://www.onvif.org/ver10/schema")
)
|> Onvif.Media.Ver10.ServiceCapabilities.parse()
|> Onvif.Media.Ver10.ServiceCapabilities.to_struct()

response =
case parsed do
{:ok, sevice} ->
sevice

{:error, changeset} ->
Logger.error("Discarding invalid service capability: #{inspect(changeset)}")
%Onvif.Media.Ver10.ServiceCapabilities{}
end

{:ok, response}
end
end
128 changes: 128 additions & 0 deletions lib/media/ver10/service_capabilities.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
defmodule Onvif.Media.Ver10.ServiceCapabilities do
@moduledoc false

use Ecto.Schema
import Ecto.Changeset
import SweetXml

@primary_key false
@derive Jason.Encoder
@required []
@optional [
:snapshot_uri,
:rotation,
:video_source_mode,
:osd,
:temporary_osd_text,
:exi_compression
]
embedded_schema do
field(:snapshot_uri, :boolean, default: false)
field(:rotation, :boolean, default: false)
field(:video_source_mode, :boolean, default: false)
field(:osd, :boolean, default: false)
field(:temporary_osd_text, :boolean, default: false)
field(:exi_compression, :boolean, default: false)

embeds_one :profile_capabilities, ProfileCapabilities, primary_key: false, on_replace: :update do
@derive Jason.Encoder
field(:maximum_number_of_profiles, :integer)
end

embeds_one :streaming_capabilities, StreamingCapabilities,
primary_key: false,
on_replace: :update do
@derive Jason.Encoder
field(:rtsp_multicast, :boolean, default: false)
field(:rtp_tcp, :boolean, default: false)
field(:rtp_rtsp_tcp, :boolean, default: false)
field(:non_aggregated_control, :boolean, default: false)
field(:no_rtsp_streaming, :boolean, default: false)
end
end

def to_struct(parsed) do
%__MODULE__{}
|> changeset(parsed)
|> apply_action(:validate)
end

@spec to_json(%Onvif.Media.Ver10.Profile.VideoEncoderConfiguration{}) ::
{:error,
%{
:__exception__ => any,
:__struct__ => Jason.EncodeError | Protocol.UndefinedError,
optional(atom) => any
}}
| {:ok, binary}
def to_json(%__MODULE__{} = schema) do
Jason.encode(schema)
end

def changeset(module, attrs) do
module
|> cast(attrs, @required ++ @optional)
|> validate_required(@required)
|> cast_embed(:profile_capabilities, with: &profile_capabilities_changeset/2)
|> cast_embed(:streaming_capabilities,
with: &streaming_capabilities_changeset/2
)
end

def parse(nil), do: nil
def parse([]), do: nil

def parse(doc) do
xmap(
doc,
snapshot_uri: ~x"./@SnapshotUri"so,
rotation: ~x"./@Rotation"so,
video_source_mode: ~x"./@VideoSourceMode"so,
osd: ~x"./@OSD"so,
temporary_osd_text: ~x"./@TemporaryOSDText"so,
exi_compression: ~x"./@EXICompression"so,
profile_capabilities:
~x"./trt:ProfileCapabilities"eo |> transform_by(&parse_profile_capabilities/1),
streaming_capabilities:
~x"./trt:StreamingCapabilities"eo |> transform_by(&parse_streaming_capabilities/1)
)
end

defp profile_capabilities_changeset(module, attrs) do
cast(module, attrs, [:maximum_number_of_profiles])
end

defp streaming_capabilities_changeset(module, attrs) do
cast(module, attrs, [
:rtsp_multicast,
:rtp_tcp,
:rtp_rtsp_tcp,
:non_aggregated_control,
:no_rtsp_streaming
])
end

defp parse_profile_capabilities([]), do: nil
defp parse_profile_capabilities(nil), do: nil

defp parse_profile_capabilities(doc) do
xmap(
doc,
maximum_number_of_profiles: ~x"./@MaximumNumberOfProfiles"i
)
end

defp parse_streaming_capabilities([]), do: nil
defp parse_streaming_capabilities(nil), do: nil

defp parse_streaming_capabilities(doc) do
xmap(
doc,
rtsp_multicast: ~x"./@RTPMulticast "so,
rtp_tcp: ~x"./@RTP_TCP"so,
rtp_rtsp_tcp: ~x"./@RTP_RTSP_TCP"so,
non_aggregated_control: ~x"./@NonAggregateControl"so,
no_rtsp_streaming: ~x"./@NoRTSPStreaming"so
)
end
end
39 changes: 39 additions & 0 deletions test/media/get_service_capabilities_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule Onvif.Media.Ver10.GetServiceCapabilitiesTest do
use ExUnit.Case, async: true

@moduletag capture_log: true

describe "GetServiceCapabilities/1" do
test "should parse with correct values and defaults for non existing attributes" do
xml_response = File.read!("test/media/ver10/fixtures/get_service_capabilities_response.xml")

device = Onvif.Factory.device()

Mimic.expect(Tesla, :request, fn _client, _opts ->
{:ok, %{status: 200, body: xml_response}}
end)

{:ok, service_capabilities} = Onvif.Media.Ver10.GetServiceCapabilities.request(device)

assert service_capabilities == %Onvif.Media.Ver10.ServiceCapabilities{
exi_compression: false,
osd: true,
profile_capabilities: %Onvif.Media.Ver10.ServiceCapabilities.ProfileCapabilities{
maximum_number_of_profiles: 24
},
rotation: false,
snapshot_uri: true,
streaming_capabilities:
%Onvif.Media.Ver10.ServiceCapabilities.StreamingCapabilities{
no_rtsp_streaming: false,
non_aggregated_control: false,
rtp_rtsp_tcp: true,
rtp_tcp: false,
rtsp_multicast: false
},
temporary_osd_text: false,
video_source_mode: false
}
end
end
end
12 changes: 12 additions & 0 deletions test/media/ver10/fixtures/get_service_capabilities_response.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://tempuri.org/tns.xsd" xmlns:tns1="http://www.onvif.org/ver10/topics" xmlns:c14n="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:xmime5="http://tempuri.org/xmime5.xsd" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:tplt="http://www.onvif.org/ver10/plus/schema" xmlns:tt="http://www.onvif.org/ver10/schema" xmlns:wstop="http://docs.oasis-open.org/wsn/t-1" xmlns:wsrfr="http://docs.oasis-open.org/wsrf/r-2" xmlns:wsrfbf="http://docs.oasis-open.org/wsrf/bf-2" xmlns:tanae="http://www.onvif.org/ver20/analytics/wsdl/AnalyticsEngineBinding" xmlns:tanre="http://www.onvif.org/ver20/analytics/wsdl/RuleEngineBinding" xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:tes-cppb="http://www.onvif.org/ver10/events/wsdl/CreatePullPointBinding" xmlns:tes-e="http://www.onvif.org/ver10/events/wsdl/EventBinding" xmlns:tes-nc="http://www.onvif.org/ver10/events/wsdl/NotificationConsumerBinding" xmlns:tes-np="http://www.onvif.org/ver10/events/wsdl/NotificationProducerBinding" xmlns:tes-ppb="http://www.onvif.org/ver10/events/wsdl/PullPointBinding" xmlns:tes-pps="http://www.onvif.org/ver10/events/wsdl/PullPointSubscriptionBinding" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:tes-psmb="http://www.onvif.org/ver10/events/wsdl/PausableSubscriptionManagerBinding" xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2" xmlns:tes-sm="http://www.onvif.org/ver10/events/wsdl/SubscriptionManagerBinding" xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl" xmlns:tpl="http://www.onvif.org/ver10/plus/wsdl" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:tr2="http://www.onvif.org/ver20/media/wsdl" xmlns:trc="http://www.onvif.org/ver10/recording/wsdl" xmlns:trp="http://www.onvif.org/ver10/replay/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:trv="http://www.onvif.org/ver10/receiver/wsdl" xmlns:tse="http://www.onvif.org/ver10/search/wsdl" xmlns:ter="http://www.onvif.org/ver10/error">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<trt:GetServiceCapabilitiesResponse>
<trt:Capabilities OSD="true" VideoSourceMode="false" Rotation="false" SnapshotUri="true">
<trt:ProfileCapabilities MaximumNumberOfProfiles="24"/>
<trt:StreamingCapabilities NoRTSPStreaming="false" NonAggregateControl="false" RTP_RTSP_TCP="true" RTP_TCP="false" RTPMulticast="false"/>
</trt:Capabilities>
</trt:GetServiceCapabilitiesResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

0 comments on commit 050db0f

Please sign in to comment.