Elixir通过Onvif协议控制IP摄像机,扩展ExOnvif的摄像头连续移动功能 ContinuousMove

Elixir 通过Onvif 对IP设备进行控制时,可以使用 ExOnvif 库。ExOnvif官方文档

此文章仅提供了ContinuousMove的控制方式及示例。

Elixir Onvif协议控制IP设备的其他命令,可以参考以下链接

绝对移动 【AbsoluteMove】

调用指定预置位 【GotoPreset】

停止运动 【Stop】

获取预置位列表 【GetPresets】

1. ContinuousMove

ExOnvif官方文档中未给出摄像机连续运动命令,自己按照onvif协议 Onvif协议 扩展的此项功能;

连续移动的命令 ContinuousMove ,属于PTZ服务的一部分,允许客户端向网络摄像机或其他视频监控设备发送指令,以使云台持续平移(Pan)、倾斜(Tilt)、变焦(Zoom)。 这个操作让监控人员能够灵活地追踪移动的目标或调整视角,而不仅仅是跳转到预设的位置。

由以上协议可以看出,控制摄像机连续移动,需要参数2个

  • ProfileToken:ProfileToken指定执行PTZ操作的配置文件标识符,必须先从设备获取有效的ProfileToken。
  • Velocity:PTZ参数,PanTilt定义了平移和倾斜的速度,x轴通常对应水平方向(Pan),y轴对应垂直方向(Tilt)。正值表示正方向移动,负值反之。Zoom定义了变焦速度,值为0通常意味着不改变当前变焦状态。

2. 函数实现

程序实现步骤如下

  • 初始化Device设备
  • 获取Device设备的ProfileToken
  • 控制设备

完整的文件:

erlang 复制代码
defmodule MvOnvif.Action do
  use GenServer

  @moduledoc """
  自定义的Onvif的部分协议
  获取当前状态(exonvif)
  absolute move调用摄像头到指定位置,
  continuous move摄像头连续移动
  调用指定预置位
  停止运动
  """
  import ExOnvif.Utils.XmlBuilder
  import SweetXml
  alias ExOnvif.Device
  alias ExOnvif.Media2
  import ExOnvif.Utils.ApiClient, only: [ptz_request: 4]

  # 初始化device设备
  defp get_device(uri) when not is_nil(uri) do
    %{host: host, userinfo: userinfo} =  URI.parse(uri)
    [user, pw] = String.split(userinfo, ":")
    Device.new("http://" <> host, user, pw);
  end

  defp get_device(uri) do
    :error
  end 
  
  # 获取profiletoken标识符
  defp get_main_stream_profile_token(device) do
    profiles = Media2.get_profiles(device)
    case profiles do
      {:ok, list} -> {:ok, hd(list).reference_token}
      _ -> "something went wrong"
    end
  end

   @spec mv_continuous_move(ExOnvif.Device.t(), ExOnvif.PTZ.AbsoluteMove.t()) :: {:ok, String.t()} | {:error, any()}
  def mv_continuous_move(device, ab_m) do
    body = encode(ab_m)
    ptz_request(device, "ContinuousMove", body, fn resp -> :ok end )
  end
  
	# 按照xml格式编码
  def encode(absolute_move) do
    element(
      "tptz:ContinuousMove",
      [element("tptz:ProfileToken", absolute_move.profile_token),
       element("tptz:Velocity", Vector.encode(absolute_move.speed))]
    )
  end

  # 摄像头连续移动
  def continuous_move_(uri, speed \\ {0.5, 0.5, 0.1}) do
    with {:ok, device} <- get_device(uri),
         {:ok, profile_token} <- get_main_stream_profile_token(device)
      do
      {x, y, z} = speed
      speed =  %ExOnvif.PTZ.Vector{
        pan_tilt: %ExOnvif.PTZ.Vector.PanTilt{
          x: x,
          y: y},
        zoom: z
      }
      abm = ExOnvif.PTZ.AbsoluteMove.new(profile_token, nil, speed)
      mv_continuous_move(device, abm)
    end
  end
end
停止移动 参考连接

3. xml文件示例

连续运动请求的标准xml文件示例
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
    xmlns:wsdl="http://www.onvif.org/ver20/ptz/wsdl"
    xmlns:tt="http://www.onvif.org/ver10/schema">
    
    <SOAP-ENV:Header>
        <!-- 安全认证头 -->
        <Security SOAP-ENV:mustUnderstand="true"
            xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <UsernameToken>
                <Username>admin</Username>
                <Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">
                    <!-- 加密密码(示例值) -->
                    dGhpcyBpcyBub3QgYSByZWFsIHBhc3N3b3JkIEJhc2U2NA==
                </Password>
                <Nonce>eUYbbbyFbcLbcByKUvbcsA==</Nonce>
                <Created>2025-08-14T10:30:00.000Z</Created>
            </UsernameToken>
        </Security>
    </SOAP-ENV:Header>
    
    <SOAP-ENV:Body>
        <wsdl:ContinuousMove>
            <!-- 必填参数:配置集Token -->
            <wsdl:ProfileToken>Profile_1</wsdl:ProfileToken>
            
            <!-- 必填参数:运动速度向量 -->
            <wsdl:Velocity>
                <tt:PanTilt x="0.5" y="0.0" 
                    space="http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace"/>
                <tt:Zoom x="0.0" 
                    space="http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace"/>
            </wsdl:Velocity>
            
            <!-- 可选参数:超时时间(单位:秒) -->
            <!-- <wsdl:Timeout>PT30S</wsdl:Timeout> -->
        </wsdl:ContinuousMove>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
相关推荐
一只游鱼4 小时前
利用keytool实现https协议(生成自签名证书)
网络协议·http·https·keytool
学会煎墙5 小时前
3分钟快速入门WebSocket
网络·websocket·网络协议
码熔burning5 小时前
RPC 和 HTTP 的区别
网络协议·http·rpc
赖龙6 小时前
记录SSL部署,链路不完整问题
网络·网络协议·ssl
吐个泡泡v8 小时前
网络编程基础:一文搞懂 Socket、HTTP、HTTPS、TCP/IP、SSL 的关系
网络·网络协议·http·https·socket·ssl·tcp
Blurpath8 小时前
如何利用静态代理IP优化爬虫策略?从基础到实战的完整指南
爬虫·网络协议·ip代理·住宅代理
liulilittle9 小时前
HTTP简易客户端实现
开发语言·网络·c++·网络协议·http·编程语言
板鸭〈小号〉11 小时前
UDP-Server(2)词典功能
网络·网络协议·udp
Coding_Doggy13 小时前
苍穹外卖Day10 | 订单状态定时处理、来单提醒、客户催单、SpringTask、WebSocket、cron表达式
网络·websocket·网络协议