Skip to content

Commit

Permalink
Refactor onvif (#546)
Browse files Browse the repository at this point in the history
* refactor onvif page

* refactor onvif discover api endpoint

* delete old onvif code

* delete unused deps

* upgrade onvif dep

* reject link local addresses

* Make ice servers keys atoms

* filter out link local address
  • Loading branch information
gBillal authored Jan 8, 2025
1 parent 4ebba5e commit acaffad
Show file tree
Hide file tree
Showing 25 changed files with 387 additions and 7,819 deletions.
2 changes: 0 additions & 2 deletions nerves_fw/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ config :ex_nvr,

config :ex_nvr, ExNVR.Mailer, adapter: Swoosh.Adapters.Local

config :soap, :globals, version: "1.2"

config :os_mon,
disk_space_check_interval: {:second, 30},
disk_almost_full_threshold: 0.9
Expand Down
1 change: 1 addition & 0 deletions nerves_fw/lib/nerves_fw/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defmodule ExNVR.Nerves.Application do

# List all child processes to be supervised
def children(:host) do
[]
end

def children(_target) do
Expand Down
4 changes: 2 additions & 2 deletions nerves_fw/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ defmodule NervesFw.MixProject do
# version updates, please review their release notes in case
# changes to your application are needed.
{:ex_nvr_system_rpi4,
github: "evercam/ex_nvr_system_rpi4", tag: "v1.29.0", runtime: false, targets: :ex_nvr_rpi4},
github: "evercam/ex_nvr_system_rpi4", tag: "v1.28.1", runtime: false, targets: :ex_nvr_rpi4},
{:ex_nvr_system_rpi5,
github: "evercam/ex_nvr_system_rpi5", tag: "v0.4.0", runtime: false, targets: :ex_nvr_rpi5}
github: "evercam/ex_nvr_system_rpi5", tag: "v0.3.1", runtime: false, targets: :ex_nvr_rpi5}
]
end

Expand Down
8 changes: 4 additions & 4 deletions nerves_fw/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
"ex_libsrtp": {:hex, :ex_libsrtp, "0.7.2", "211bd89c08026943ce71f3e2c0231795b99cee748808ed3ae7b97cd8d2450b6b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.3", [hex: :bundlex, repo: "hexpm", optional: false]}, {:membrane_precompiled_dependency_provider, "~> 0.1.0", [hex: :membrane_precompiled_dependency_provider, repo: "hexpm", optional: false]}, {:unifex, "~> 1.1", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "2e20645d0d739a4ecdcf8d4810a0c198120c8a2f617f2b75b2e2e704d59f492a"},
"ex_m3u8": {:hex, :ex_m3u8, "0.14.2", "3eb17f936e2ca2fdcde11664f3a543e75a94814d928098e050bda5b1e149c021", [:mix], [{:nimble_parsec, "~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "d2a1fb4382a521cce7f966502ecce6187f286ca2852dbb0dcc25dea72f8ba039"},
"ex_mp4": {:hex, :ex_mp4, "0.8.1", "b3ca30388f04ad1ae594783bd93b7f1dc3d75776d540d5f59d93439455f96e2b", [:mix], [{:bunch, "~> 1.6", [hex: :bunch, repo: "hexpm", optional: false]}, {:ratio, "~> 4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:table_rex, "~> 4.0", [hex: :table_rex, repo: "hexpm", optional: true]}], "hexpm", "efd08d7b8c0f03321033bccc8ffc1d1b077d83c7355321ae83c0cd4857ee6b62"},
"ex_nvr_system_rpi4": {:git, "https://github.com/evercam/ex_nvr_system_rpi4.git", "d0231bce4c25058aafc268a2ec8c242df3d0f456", [tag: "v1.29.0"]},
"ex_nvr_system_rpi5": {:git, "https://github.com/evercam/ex_nvr_system_rpi5.git", "1b3dad819fb058391019991695e326ed589e7619", [tag: "v0.4.0"]},
"ex_nvr_system_rpi4": {:git, "https://github.com/evercam/ex_nvr_system_rpi4.git", "b008ffa1399c47e2ae9d9b3761ceb8c0a74d4847", [tag: "v1.28.1"]},
"ex_nvr_system_rpi5": {:git, "https://github.com/evercam/ex_nvr_system_rpi5.git", "965b6af72e7065dc922fcde50d0c24d9a1efc79b", [tag: "v0.3.1"]},
"ex_rtcp": {:hex, :ex_rtcp, "0.4.0", "f9e515462a9581798ff6413583a25174cfd2101c94a2ebee871cca7639886f0a", [:mix], [], "hexpm", "28956602cf210d692fcdaf3f60ca49681634e1deb28ace41246aee61ee22dc3b"},
"ex_rtp": {:hex, :ex_rtp, "0.4.0", "1f1b5c1440a904706011e3afbb41741f5da309ce251cb986690ce9fd82636658", [:mix], [], "hexpm", "0f72d80d5953a62057270040f0f1ee6f955c08eeae82ac659c038001d7d5a790"},
"ex_sdp": {:hex, :ex_sdp, "1.1.1", "1a7b049491e5ec02dad9251c53d960835dc5631321ae978ec331831f3e4f6d5f", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}], "hexpm", "1b13a72ac9c5c695b8824dbdffc671be8cbb4c0d1ccb4ff76a04a6826759f233"},
Expand Down Expand Up @@ -101,7 +101,7 @@
"nerves_pack": {:hex, :nerves_pack, "0.7.1", "f069a9c3cfcde445498aad618af95c846f2ee032a69c2d715a9e5d4f632fb1b5", [:mix], [{:mdns_lite, "> 0.0.0", [hex: :mdns_lite, repo: "hexpm", optional: false]}, {:nerves_motd, "> 0.0.0", [hex: :nerves_motd, repo: "hexpm", optional: false]}, {:nerves_runtime, "> 0.0.0", [hex: :nerves_runtime, repo: "hexpm", optional: false]}, {:nerves_ssh, "> 0.0.0", [hex: :nerves_ssh, repo: "hexpm", optional: false]}, {:nerves_time, "> 0.0.0", [hex: :nerves_time, repo: "hexpm", optional: false]}, {:ring_logger, "> 0.0.0", [hex: :ring_logger, repo: "hexpm", optional: false]}, {:vintage_net, "> 0.0.0", [hex: :vintage_net, repo: "hexpm", optional: false]}, {:vintage_net_direct, "> 0.0.0", [hex: :vintage_net_direct, repo: "hexpm", optional: false]}, {:vintage_net_ethernet, "> 0.0.0", [hex: :vintage_net_ethernet, repo: "hexpm", optional: false]}, {:vintage_net_wifi, "> 0.0.0", [hex: :vintage_net_wifi, repo: "hexpm", optional: false]}], "hexpm", "6b94d38771e3a97c3ed63e62fb5f78b47461208ee162e20bfbcf6e36fd900616"},
"nerves_runtime": {:hex, :nerves_runtime, "0.13.7", "0a7b15d5f55af1b695f7a4a1bd597c2f6101f8cbe7fe7a30743e3e441b7e233f", [:mix], [{:nerves_logging, "~> 0.2.0", [hex: :nerves_logging, repo: "hexpm", optional: false]}, {:nerves_uevent, "~> 0.1.0", [hex: :nerves_uevent, repo: "hexpm", optional: false]}, {:uboot_env, "~> 0.3.0 or ~> 1.0", [hex: :uboot_env, repo: "hexpm", optional: false]}], "hexpm", "6bafb89344709e5405cc7f9b140f91ca76ea3749f0eb3af80d8f8b8c53388b2d"},
"nerves_ssh": {:hex, :nerves_ssh, "1.0.0", "a3683859674224a00aac5ae2a416185ca0129f5c5c6d09ab70a6d98bec79d3d8", [:mix], [{:nerves_runtime, "~> 0.11", [hex: :nerves_runtime, repo: "hexpm", optional: false]}, {:ssh_subsystem_fwup, "~> 0.5", [hex: :ssh_subsystem_fwup, repo: "hexpm", optional: false]}], "hexpm", "2f3bbbb7752bc19aaca19ec9433dd029da728a67f9f497508581602e4cba68f4"},
"nerves_system_br": {:hex, :nerves_system_br, "1.29.1", "afa5337d66e9770d48dc8bf3a4da41f63dcbd1f0b4639d67fa08d449ad752668", [:mix], [], "hexpm", "33f5afe50fa603a8cdb22e281c18fb98de9f3012598af1ab77849d1dbe59747e"},
"nerves_system_br": {:hex, :nerves_system_br, "1.28.3", "979ac2d9ca66e45cb14ffb440a65e691892569070bb2104a9db65cb7a847b9f7", [:mix], [], "hexpm", "4bb6df0823696762c4e81c944fe3e08f46136e982b6bd34258a936b43b389a90"},
"nerves_time": {:hex, :nerves_time, "0.4.8", "50523376d487dfaea9bde973b9160a2ce811ade3ba2533776ef5c2ff4d80c277", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:muontrap, "~> 0.5 or ~> 1.0", [hex: :muontrap, repo: "hexpm", optional: false]}], "hexpm", "84479a25b725c24d1b758eaef3450043868591c8102c0ca1d7cc051a6402dd03"},
"nerves_toolchain_aarch64_nerves_linux_gnu": {:hex, :nerves_toolchain_aarch64_nerves_linux_gnu, "13.2.0", "68fcd2c21c86cceb9545948fae052d72f88b7c7c10205b252dac88559e2a3369", [:mix], [{:nerves, "~> 1.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.10.0", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "f92212606919a062f975e7bd82ed8a1b95bd4864abb3444cd0d5d0e610e94cc5"},
"nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "1.10.0", "c6b35377a0b7a93633a8673a788f1580fe1fa06083374b0e4df36da65828d2ef", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}], "hexpm", "e4ae1a2b84de3502ecac195765819be0ce2834eb276553163a7c03133f1760f1"},
Expand All @@ -113,6 +113,7 @@
"numbers": {:hex, :numbers, "5.2.4", "f123d5bb7f6acc366f8f445e10a32bd403c8469bdbce8ce049e1f0972b607080", [:mix], [{:coerce, "~> 1.0", [hex: :coerce, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "eeccf5c61d5f4922198395bf87a465b6f980b8b862dd22d28198c5e6fab38582"},
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
"one_dhcpd": {:hex, :one_dhcpd, "2.0.3", "224f8a4cd06d73c9e481dd92766d3e831f8dc4d29d2cd50b9459135b32845da0", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "a5902e4ed956bcfaa975bcda7adb2fc35303695a496eafecea69392a26cbf9ff"},
"onvif": {:git, "https://github.com/hammeraj/onvif.git", "1c54a1141c029267677dc50186d0e38a6479287d", [ref: "1c54a11"]},
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
"pbcs": {:hex, :pbcs, "0.1.4", "84c64d6ec826a57821021c03b0db3598ee587ba64430badee77809c2f68a9f8c", [:mix], [], "hexpm", "1f96954e50077e9cfde3cce62da452f4e56906d019ef141c011a96f79137dec0"},
"peep": {:hex, :peep, "3.4.0", "2de27e5f05225df72e4a8a7728dd9c9cc00a02c772c61251eb5d3fe7aa89fffb", [:mix], [{:nimble_options, "~> 1.1", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:plug, "~> 1.16", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry_metrics, "~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "07af52763804eae8f99a92a0fa4728ab7464eb3840671382124f4c52b1fbfbe2"},
Expand All @@ -138,7 +139,6 @@
"shmex": {:hex, :shmex, "0.5.1", "81dd209093416bf6608e66882cb7e676089307448a1afd4fc906c1f7e5b94cf4", [:mix], [{:bunch_native, "~> 0.5.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 1.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "c29f8286891252f64c4e1dac40b217d960f7d58def597c4e606ff8fbe71ceb80"},
"shoehorn": {:hex, :shoehorn, "0.9.2", "7e430e6f27ba4f6519699e54e8aab52520e658d1a11a5ddf01a1af1602416280", [:mix], [], "hexpm", "044353552341925a930681f66595e5d8ed4748d6e4200b8c877a3859016d1a11"},
"slipstream": {:hex, :slipstream, "1.1.3", "26bfd2b91c75bde3eb4f13fdb25940272395a1b04bcbc48b017f63f40fd09b29", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mint_web_socket, "~> 0.2 or ~> 1.0", [hex: :mint_web_socket, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.1 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b1e81cca0369834060077ff43a7e557200dc33a22300e39524a2f5edec9eb023"},
"soap": {:git, "https://github.com/gBillal/soap.git", "07c20d4bbeed5bf244966691105b83c2f73ed108", [branch: "parse-attributes"]},
"ssh_subsystem_fwup": {:hex, :ssh_subsystem_fwup, "0.6.2", "f551804decfa81254eadb6a69e5f905bcf2cb97a2f3069918cb7e55de0210c30", [:mix], [], "hexpm", "7cb8c598b02d95a6aea8888004082de5a74ae977ddfb5b608b1b6b1aaceb2ff3"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"sweet_xml": {:hex, :sweet_xml, "0.7.4", "a8b7e1ce7ecd775c7e8a65d501bc2cd933bff3a9c41ab763f5105688ef485d08", [:mix], [], "hexpm", "e7c4b0bdbf460c928234951def54fe87edf1a170f6896675443279e2dbeba167"},
Expand Down
2 changes: 0 additions & 2 deletions ui/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,6 @@ config :logger, :console,
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason

config :soap, :globals, version: "1.2"

config :os_mon,
disk_space_check_interval: {:second, 30},
disk_almost_full_threshold: 0.9
Expand Down
100 changes: 13 additions & 87 deletions ui/lib/ex_nvr/devices.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ defmodule ExNVR.Devices do
{:ok, Device.t()} | {:error, Ecto.Changeset.t()}
def update_state(%Device{} = device, state), do: __MODULE__.update(device, %{state: state})

@spec list() :: [Device.t()]
@spec list(map()) :: [Device.t()]
def list(params \\ %{}), do: Repo.all(Device.filter(params) |> order_by([d], d.inserted_at))

def ip_cameras(), do: list(type: :ip)
@spec ip_cameras() :: [Device.t()]
def ip_cameras(), do: list(%{type: :ip})

@spec get(binary()) :: Device.t() | nil
def get(device_id), do: Repo.get(Device, device_id)
Expand Down Expand Up @@ -147,20 +149,16 @@ defmodule ExNVR.Devices do
end
end

@spec discover(non_neg_integer()) :: {:ok, map()} | {:error, any()}
def discover(timeout) do
ExNVR.Onvif.discover(timeout: timeout)
end

# fetch IP camera details using ONVIF
@spec fetch_camera_details(binary()) :: map()
@spec fetch_camera_details(binary(), Keyword.t()) :: map()
def fetch_camera_details(url, opts \\ []) do
%{url: url}
|> get_date_time_settings()
|> get_device_information(opts)
|> get_network_interfaces(opts)
|> get_media_profiles(opts)
@spec discover(Keyword.t()) :: [Onvif.Discovery.Probe.t()]
def discover(options) do
Onvif.Discovery.probe(options)
|> Enum.uniq()
|> Enum.map(fn probe ->
# Ignore link local addresses
probe.address
|> Enum.reject(&String.starts_with?(&1, ["http://169.254", "https://169.254"]))
|> then(&Map.put(probe, :address, &1))
end)
end

defp copy_device_file(%Device{type: :file, stream_config: stream_config} = device) do
Expand All @@ -169,78 +167,6 @@ defmodule ExNVR.Devices do

defp copy_device_file(_device), do: :ok

defp get_date_time_settings(camera) do
settings =
case ExNVR.Onvif.get_system_date_and_time(camera.url) do
{:ok, settings} ->
settings

{:error, reason} ->
Logger.error("""
Onvif: could not get date and time from '#{camera.url}'
due to: #{inspect(reason)}
""")

nil
end

Map.put(camera, :date_time_settings, settings)
end

defp get_device_information(camera, opts) do
settings =
case ExNVR.Onvif.get_device_information(camera.url, opts) do
{:ok, infos} ->
infos

{:error, reason} ->
Logger.error("""
Onvif: could not get date and time from '#{camera.url}'
due to: #{inspect(reason)}
""")

nil
end

Map.put(camera, :device_information, settings)
end

defp get_network_interfaces(camera, opts) do
settings =
case ExNVR.Onvif.get_network_interfaces(camera.url, opts) do
{:ok, interfaces} ->
interfaces

{:error, reason} ->
Logger.error("""
Onvif: could not get date and time from '#{camera.url}'
due to: #{inspect(reason)}
""")

nil
end

Map.put(camera, :network_interfaces, settings)
end

defp get_media_profiles(camera, opts) do
with {:ok, %{media: media}} <- ExNVR.Onvif.get_capabilities(camera.url, opts),
{:ok, media_profiles} <- ExNVR.Onvif.get_media_profiles(media.x_addr, opts) do
media_profiles
|> Enum.map(fn profile ->
uri = ExNVR.Onvif.get_media_stream_uri!(media.x_addr, profile.token, opts)
Map.put(profile, :stream_uri, uri)
end)
|> Enum.map(fn profile ->
uri = ExNVR.Onvif.get_media_snapshot_uri!(media.x_addr, profile.token, opts)
Map.put(profile, :snapshot_uri, uri)
end)
|> then(&Map.put(camera, :media_profiles, &1))
else
_other -> Map.put(camera, :media_profiles, [])
end
end

defp url_and_opts(%{url: nil}), do: {:error, :url_not_configured}

defp url_and_opts(device) do
Expand Down
37 changes: 1 addition & 36 deletions ui/lib/ex_nvr/http.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,11 @@ defmodule ExNVR.HTTP do
call(:post, url, body, opts)
end

@spec build_digest_auth(map(), Keyword.t()) :: {:ok, binary()} | {:error, binary()}
def build_digest_auth(resp, opts) do
with digest_opts when is_map(digest_opts) <- digest_auth_opts(resp, opts) do
digest_auth = encode_digest(digest_opts)
{:ok, digest_auth}
end
end

defp call(method, url, body, opts) do
opts = Keyword.merge(opts, method: method, url: url)
do_call(method, body, opts)
end

defp digest_auth_opts(%{headers: headers}, opts) do
headers
|> Enum.map(fn {key, value} -> {String.downcase(key), value} end)
|> Map.new()
|> Map.fetch("www-authenticate")
|> case do
{:ok, ["Digest " <> digest]} ->
build_digest_auth_opts(digest, opts)

{:ok, "Digest " <> digest} ->
build_digest_auth_opts(digest, opts)

_other ->
nil
end
end

defp do_call(method, body, opts) when is_map(body) do
build_request(method, opts) |> Req.request(json: body, auth: auth_from_opts(opts))
end
Expand Down Expand Up @@ -77,7 +52,7 @@ defmodule ExNVR.HTTP do
|> encode_digest()

request
|> Req.Request.put_header("authorization", digest)
|> Req.Request.put_header("Authorization", digest)
|> Req.Request.run_request()
else
_other -> {request, response}
Expand All @@ -86,16 +61,6 @@ defmodule ExNVR.HTTP do

defp digest_auth({request, response}, _credentials), do: {request, response}

defp build_digest_auth_opts(digest, opts) do
build_digest_auth_opts(
digest,
opts[:method],
URI.parse(opts[:url]),
opts[:username],
opts[:password]
)
end

defp build_digest_auth_opts(digest, method, url, username, password) do
%{
nonce: match_pattern("nonce", digest),
Expand Down
Loading

0 comments on commit acaffad

Please sign in to comment.