python通过onvif协议搜索局域网所有摄像头ip,并获取摄像头rtsp,mac地址等相关服务

这里用到camera_discovery模块

pip install camera_discovery
from camera_discovery import CameraDiscovery
camera_ip = CameraDiscovery.ws_discovery()
print(camera_ip)
成功的话会返回一个摄像头ip列表

不过笔者在使用过程中遇到了一个莫名其妙的问题erro 101 network is unreachable(网络不可达)可我ping的通网络,连接也没问题。查询后也没个准确的答案,只知道报错的py文件下有个sock.sendto函数,这个函数发送会有timeout时长,超过就会报上面的错误,我的方法是直接try:下运行sock.sendto函数,报错信息忽略掉,就解决问题了。

try:
    sock.sendto()
except:
    pass

这是Ubuntu的解决方法,Windows下会报hostname的错误,其实也不是这个错误,Windows下没有hostname -i这个命令,所以无法获取到本机ip,直接这样输入就可以了(Ubuntu也同样适用)CameraDiscovery.ws_discovery(‘192.168.123.xxx’),原本以为自己添加ip就可以了,可后面运行时又报了怎么一个错误 AttributeError: module 'select' has no attribute 'poll',查了下是说这个模块只适用于Linux 2.5+,并不支持其他系统。poll机制是Linux内核的一种内核微调机制,由此可以说这个搜索ip的功能只能适用于Ubuntu了,但是如果你已知摄像头ip的话,下面的摄像头信息获取功能你也能在Windows使用

另外此模块还可以对摄像头做其他操作,如获取摄像头mac地址等,官网提供的导入方式如下:

from camera_discovery import CameraONVIF
Class = CameraONVIF(camera_ip, user, password)

上面的导入其实是不对的,导入方式应该改为这样

from camera_discovery.CameraDiscovery import CameraONVIF
camera = CameraONVIF(camera_ip, user, password)
camera.camera_start()#初始化摄像头相关信息,不改源码会出错

这样生成camera对象是成功了,但是初始化相关信息还是会报如下错误

onvif.exceptions.ONVIFError: Unknown error: AnySimpleType.pytonvalue() not implemented

相关获取功能还是不能使用,这个库这是bug多多,还得改改源码,改动其实很简单的,就是 import zeep ,加了个 zeep_pythonvalue 函数 以及 在 camera_start 函数下 加上句 zeep.xsd.simple.AnySimpleType.pythonvalue = self.zeep_pythonvalue,整体改后的CameraONVIF类如下:

import subprocess
from typing import List

import WSDiscovery
from onvif import ONVIFCamera

import zeep #额外加的


def ws_discovery(scope = None) -> List:
    """Discover cameras on network using onvif discovery.

    Returns:
        List: List of ips found in network.
    """
    lst = list()
    if(scope == None):
        cmd = 'hostname -I'
        scope = subprocess.check_output(cmd, shell=True).decode('utf-8')
    wsd = WSDiscovery.WSDiscovery()
    wsd.start()
    ret = wsd.searchServices()
    for service in ret:
        get_ip = str(service.getXAddrs())
        get_types = str(service.getTypes())
        for ip_scope in scope.split():
            result = get_ip.find(ip_scope.split('.')[0] + '.' + ip_scope.split('.')[1])
            if result > 0 and get_types.find('onvif') > 0:
                string_result = get_ip[result:result+13]
                string_result = string_result.split('/')[0]
                lst.append(string_result)
    wsd.stop()
    lst.sort()
    return lst


class CameraONVIF:
    """This class is used to get the information from all cameras discovered on this specific
    network."""

    def __init__(self, ip, user, password):
        """Constructor.

        Args:
            ip (str): Ip of the camera.
            user (str): Onvif login.
            password (str): Onvif password.
        """
        self.cam_ip = ip
        self.cam_user = user
        self.cam_password = password

    def zeep_pythonvalue(self, xmlvalue):	#额外加的
        return xmlvalue
        
    def camera_start(self):
        """Start module.
        """
        mycam = ONVIFCamera(self.cam_ip, 80, self.cam_user, self.cam_password, no_cache = True)
        media = mycam.create_media_service()
        
		zeep.xsd.simple.AnySimpleType.pythonvalue = self.zeep_pythonvalue   #额外加的

        media_profile = media.GetProfiles()[0]
        self.mycam = mycam
        self.camera_media = media
        self.camera_media_profile = media_profile
    
    def get_hostname(self) -> str:
        """Find hostname of camera.

        Returns:
            str: Hostname.
        """
        resp = self.mycam.devicemgmt.GetHostname()
        return resp.Name

    def get_manufacturer(self) -> str:
        """Find manufacturer of camera.

        Returns:
            str: Manufacturer.
        """
        resp = self.mycam.devicemgmt.GetDeviceInformation()
        return resp.Manufacturer

    def get_model(self) -> str:
        """Find model of camera.

        Returns:
            str: Model.
        """
        resp = self.mycam.devicemgmt.GetDeviceInformation()
        return resp.Model

    def get_firmware_version(self) -> str:
        """Find firmware version of camera.

        Returns:
            str: Firmware version.
        """
        resp = self.mycam.devicemgmt.GetDeviceInformation()
        return resp.FirmwareVersion

    def get_mac_address(self) -> str:
        """Find serial number of camera.

        Returns:
            str: Serial number.
        """
        resp = self.mycam.devicemgmt.GetDeviceInformation()
        return resp.SerialNumber

    def get_hardware_id(self) -> str:
        """Find hardware id of camera.

        Returns:
            str: Hardware Id.
        """
        resp = self.mycam.devicemgmt.GetDeviceInformation()
        return resp.HardwareId

    def get_resolutions_available(self) -> List:
        """Find all resolutions of camera.

        Returns:
            tuple: List of resolutions (Width, Height).
        """
        request = self.camera_media.create_type('GetVideoEncoderConfigurationOptions')
        request.ProfileToken = self.camera_media_profile.token
        config = self.camera_media.GetVideoEncoderConfigurationOptions(request)
        return [(res.Width, res.Height) for res in config.H264.ResolutionsAvailable]

    def get_frame_rate_range(self) -> int:
        """Find the frame rate range of camera.

        Returns:
            int: FPS min.
            int: FPS max.
        """
        request = self.camera_media.create_type('GetVideoEncoderConfigurationOptions')
        request.ProfileToken = self.camera_media_profile.token
        config = self.camera_media.GetVideoEncoderConfigurationOptions(request)
        return config.H264.FrameRateRange.Min, config.H264.FrameRateRange.Max

    def get_date(self) -> str:
        """Find date configured on camera.

        Returns:
            str: Date in string.
        """
        datetime = self.mycam.devicemgmt.GetSystemDateAndTime()
        return datetime.UTCDateTime.Date

    def get_time(self) -> str:
        """Find local hour configured on camera.

        Returns:
            str: Hour in string.
        """
        datetime = self.mycam.devicemgmt.GetSystemDateAndTime()
        return datetime.UTCDateTime.Time

    def is_ptz(self) -> bool:
        """Check if camera is PTZ or not.

        Returns:
            bool: Is PTZ or not.
        """
        resp = self.mycam.devicemgmt.GetCapabilities()
        return bool(resp.PTZ)

然而虽然整体模块是能用了,可是里面并没有获取rtsp地址的功能,这可让我太无奈了,因为我本来就是想图省事不想写的,既然没有没办法就写一个了

    def get_rtsp(self):
        obj = self.camera_media.create_type('GetStreamUri')
        obj.StreamSetup = {
     'Stream': 'RTP-Unicast', 'Transport': {
     'Protocol': 'RTSP'}}
        obj.ProfileToken = self.camera_media_profile.token
        res_uri = self.camera_media.GetStreamUri(obj)['Uri']
        return res_uri

把上述函数加到CameraONVIF类中就有获取rtsp流地址的功能了,这里我测了一下有些获取到的地址直接能用,有些还得在ip前面加上用户名和密码,奇奇怪怪。

到此为止这个库就完全可用了,也很奇怪为什么python关于onvif协议调用摄像头的资料那么少,让我耽搁了好多天学习关于onvif的知识

第三次bug更新
ws_discovery函数的ip返回机制更改一下,源代码返回的ip会有的结尾会带:,而且有些摄像头端口并不是默认的80,所以我们将端口也一并返回

def ws_discovery(scope = None) -> List:
    """Discover cameras on network using onvif discovery.

    Returns:
        List: List of ips found in network.
    """
    lst = list()
    if(scope == None):
        cmd = 'hostname -I'
        scope = subprocess.check_output(cmd, shell=True).decode('utf-8')
    wsd = WSDiscovery.WSDiscovery()
    wsd.start()
    ret = wsd.searchServices()
    for service in ret:
        get_ip = str(service.getXAddrs())
        get_types = str(service.getTypes())
        for ip_scope in scope.split():
            result = get_ip.find(ip_scope.split('.')[0] + '.' + ip_scope.split('.')[1])
            if result > 0 and get_types.find('onvif') > 0:
            	#下面是更改的代码
                #string_result = get_ip[result:result+13]
                string_result = get_ip[result:].split('/')[0]
                string_result = string_result.split(':')
                if len(string_result)>1:
                    lst.append([string_result[0],string_result[1]])
                else:
                    lst.append([string_result[0],'80'])
    wsd.stop()
    lst.sort()
    return lst

之后改一下CameraONVIF类的init函数,将port参数也初始化

def __init__(self, ip,user, password, port):
        """Constructor.

        Args:
            ip (str): Ip of the camera.
            user (str): Onvif login.
            password (str): Onvif password.
        """
        self.cam_ip = ip
        self.cam_user = user
        self.cam_password = password
        self.cam_port = port#加入port参数

并在camera_start函数里将默认的80端口改为self.cam_port

mycam = ONVIFCamera(self.cam_ip, self.cam_port, self.cam_user, self.cam_password, no_cache = True)

这样基本上所有ip的rtsp地址你都能获取到了,少数问题是摄像头鉴权问题

你可能感兴趣的:(jetson,nano)