Elixir通过Onvif协议控制IP摄像机,ExOnvif库给视频流叠加字符

Elixir 通过 ExOnvif 库,Onvif 协议可以控制IP摄像机等设备,这篇文章记录:使用ExOnvif库,给视频流叠加文字,使用ExOnvif库的接口模块:ExOnvif.Media、ExOnvif.Media2。
ExOnvif官方文档

此文章内容:视频流叠加文字,关于Elixir通过ONVIF协议实现PTZ控制、视频流获取等指令,可以查看我的其他文章。点击查看主页

1. Media2和Media的核心关系:演进与替代

在 Onvif 协议中,Media2 是 Media 的升级版,

复制代码
Media (Profile S): 是ONVIF基础版媒体服务,最早在ONVIF核心规范中定义。
	它提供了基本的视频流获取、快照、音频、视频编码配置等功能。
	绝大多数支持ONVIF的旧设备都实现了此服务。

Media2 (Profile T): 是ONVIF第二代媒体服务,在 media 的基础上进行了重构和大幅增强。
	它是 Profile T 强制要求的标准服务。
	新发布的、功能更丰富的设备(尤其是支持H.265等新编码的设备)
	通常会同时支持 media 和 media2 或仅支持 media2。

关系总结: media2 并非要完全废弃 media,而是在保持向后兼容性的同时,提供了一个更先进的替代方案。两者在网络上可以共存。

2. 获取OSD

获取OSD有两种方式

  • get_osd(device, token) # get the osd by token
  • get_osds(device) # get the osds

设置OSD的方式

  • set_osd(device, osd) #set the osd
  • create_osd(device, osd) # create the osd

更新逻辑

  • 首先先通过get_osds函数获取所有的osds,
  • 取到需要更新的osd的token
  • 调用set或create函数,修改或创建新的osd text

3. 完整的代码示例

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

  # 初始化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

 # 获取文字叠加
  def get_osds(uri) do
    with {:ok, device} <- get_device(uri) do
      ExOnvif.Media.get_osds(device)
    end
  end
		
	# 创建/更新 文字叠加
  def create_osd({ip, username, password}, text) do
    with {:ok, device} <- get_device(ip, username, password),
         {:ok, profile_token} <- get_main_stream_profile_token(device),
         {:ok, source} <- ExOnvif.Media2.get_video_source_configurations(device, [profile_token: profile_token]),
         {:ok, osd_list} <- ExOnvif.Media.get_osds(device)
      do
      %{source_token: source_token} = hd(source) #默认取主视频流
      if length(osd_list) > 2 do  # 我取的是第三个osd
        %{token: osd_token} = List.last(osd_list)
        osd = make_osd(source_token, text, osd_token)
        ExOnvif.Media.set_osd(device, osd)
      else
        osd = make_osd(source_token, text)
        ExOnvif.Media.create_osd(device, osd)
      end
    end
  end
  
	# %ExOnvif.Media.OSD实例
  defp make_osd(source_token, text \\ "", token \\ nil) do
    %ExOnvif.Media.OSD{
      token: token,
      video_source_configuration_token: source_token,
      text_string:  %ExOnvif.Media.OSD.TextString{
        is_persistent_text: true,
        type: :plain,
        plain_text: text
      },
      type: :text,
      position: %ExOnvif.Media.OSD.Position{
        type: :upper_left,
        pos: %{x: 21, y: 1}
      }
    }
  end
end

4. xml文件示例

获取osds的xml
erlang 复制代码
<wsdl:GetOSDs>
    <wsdl:ConfigurationToken>VideoSourceToken_1</wsdl:ConfigurationToken>
</wsdl:GetOSDs>
修改osd的xml
erlang 复制代码
<SOAP-ENV:Body>
    <wsdl:SetOSD>
        <wsdl:OSDToken>OSDToken_001</wsdl:OSDToken> <!-- 要修改的OSD令牌 -->
        <wsdl:OSD>
            <tt:Position>
                <tt:Pos>
                    <tt:x>0.85</tt:x>
                    <tt:y>0.05</tt:y>
                </tt:Pos>
            </tt:Position>
            <tt:TextString>
                <tt:FontSize>16</tt:FontSize>
                <tt:FontColor>0xFF0000</tt:FontColor> <!-- 改为红色 -->
                <tt:PlainText>MAIN GATE - CAM01</tt:PlainText>
            </tt:TextString>
        </wsdl:OSD>
    </wsdl:SetOSD>
</SOAP-ENV:Body>
创建osd的xml
erlang 复制代码
<?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/device/wsdl"
    xmlns:tt="http://www.onvif.org/ver10/schema">
    
    <SOAP-ENV:Header>
        <!-- 安全认证头(同前) -->
    </SOAP-ENV:Header>
    
    <SOAP-ENV:Body>
        <wsdl:CreateOSD>
            <wsdl:OSD>
                <tt:VideoSourceConfigurationToken>VideoSourceToken_1</tt:VideoSourceConfigurationToken>
                <tt:Type>Text</tt:Type>
                <tt:Position>
                    <tt:Type>Custom</tt:Type> <!-- 或 UpperLeft/UpperRight/LowerLeft/LowerRight -->
                    <tt:Pos>
                        <tt:x>0.8</tt:x> <!-- 0-1.0 水平位置 -->
                        <tt:y>0.1</tt:y> <!-- 0-1.0 垂直位置 -->
                    </tt:Pos>
                </tt:Position>
                <tt:TextString>
                    <tt:Type>Plain</tt:Type> <!-- 或 Date/Time/DateAndTime -->
                    <tt:DateFormat>yyyy-MM-dd</tt:DateFormat>
                    <tt:TimeFormat>HH:mm:ss</tt:TimeFormat>
                    <tt:FontSize>14</tt:FontSize>
                    <tt:FontColor>0x00FF00</tt:FontColor> <!-- RGB 格式: 0xRRGGBB -->
                    <tt:BackgroundColor>0x000000</tt:BackgroundColor>
                    <tt:PlainText>Camera 01 - Main Gate</tt:PlainText>
                    <tt:Extension>
                        <tt:IsPersistentText>true</tt:IsPersistentText>
                    </tt:Extension>
                </tt:TextString>
            </wsdl:OSD>
        </wsdl:CreateOSD>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
相关推荐
eqwaak07 小时前
Matplotlib 动态显示详解:技术深度与创新思考
网络·python·网络协议·tcp/ip·语言模型·matplotlib
qq_401700418 小时前
Qt UDP 网络编程详解
网络·网络协议·udp
油泼辣子多加8 小时前
HTTP 请求体格式详解
网络·网络协议·http
oldking呐呐9 小时前
【CS144】【计网】第一周 check0
网络协议
bantinghy10 小时前
RPC内核细节(转载)
linux·服务器·网络·网络协议·rpc
ZPC821011 小时前
scp 网间拷贝
网络协议·tcp/ip·ssl·信息与通信
liulilittle18 小时前
OPENPPP2 —— IP标准校验和算法深度剖析:从原理到SSE2优化实现
网络·c++·网络协议·tcp/ip·算法·ip·通信
阿昭L21 小时前
HTTP原理
网络·网络协议·http
zhao3266857511 天前
2025年代理IP三强横评:LoongProxy、神龙海外动态IP代理、全民HTTP怎么选?看完这篇不踩坑
网络协议·tcp/ip·http