海康威视SDK的python端获得设备信息

python端调用海康威视的SDK获得设备的PTZ信息

前言

海康威视的官方文档之中,没有python端获取摄像设备信息的以及调整PTZ信息的代码,因此仿照着其c++的文档写了python的版本。

1.HCNetSDK里的结构体定义

首先我们需要再里面定义设备信息需要用到的结构体,大多都采用的官方定义的结构,额外注意的是,NET_DVR_SET_PTZPOS和NET_DVR_GET_PTZPOS分别是设置和获取PTZ的参数。以及NET_DVR_PTZPOS这个类,里面包含了PTZ的结构体。

# coding=utf-8

import ctypes
import sys
from ctypes import *
# 回调函数类型定义
if 'linux' in sys.platform:
    fun_ctype = CFUNCTYPE
else:
    fun_ctype = WINFUNCTYPE
# 云台控制命令
LIGHT_PWRON = 2  # 接通灯光电源
WIPER_PWRON = 3  # 接通雨刷开关
FAN_PWRON = 4  # 接通风扇开关
HEATER_PWRON = 5  # 接通加热器开关
AUX_PWRON1 = 6  # 接通辅助设备开关
AUX_PWRON2 = 7  # 接通辅助设备开关
ZOOM_IN = 11  # 焦距变大(倍率变大)
ZOOM_OUT = 12  # 焦距变小(倍率变小)
FOCUS_NEAR = 13  # 焦点前调
FOCUS_FAR = 14  # 焦点后调
IRIS_OPEN = 15  # 光圈扩大
IRIS_CLOSE = 16  # 光圈缩小
TILT_UP = 21  # 云台上仰
TILT_DOWN = 22  # 云台下俯
PAN_LEFT = 23  # 云台左转
PAN_RIGHT = 24  # 云台右转
UP_LEFT = 25  # 云台上仰和左转
UP_RIGHT = 26  # 云台上仰和右转
DOWN_LEFT = 27  # 云台下俯和左转
DOWN_RIGHT = 28  # 云台下俯和右转
PAN_AUTO = 29  # 云台左右自动扫描
TILT_DOWN_ZOOM_IN = 58  # 云台下俯和焦距变大(倍率变大)
TILT_DOWN_ZOOM_OUT = 59  # 云台下俯和焦距变小(倍率变小)
PAN_LEFT_ZOOM_IN = 60  # 云台左转和焦距变大(倍率变大)
PAN_LEFT_ZOOM_OUT = 61  # 云台左转和焦距变小(倍率变小)
PAN_RIGHT_ZOOM_IN = 62  # 云台右转和焦距变大(倍率变大)
PAN_RIGHT_ZOOM_OUT = 63  # 云台右转和焦距变小(倍率变小)
UP_LEFT_ZOOM_IN = 64  # 云台上仰和左转和焦距变大(倍率变大)
UP_LEFT_ZOOM_OUT = 65  # 云台上仰和左转和焦距变小(倍率变小)
UP_RIGHT_ZOOM_IN = 66  # 云台上仰和右转和焦距变大(倍率变大)
UP_RIGHT_ZOOM_OUT = 67  # 云台上仰和右转和焦距变小(倍率变小)
DOWN_LEFT_ZOOM_IN = 68  # 云台下俯和左转和焦距变大(倍率变大)
DOWN_LEFT_ZOOM_OUT = 69  # 云台下俯和左转和焦距变小(倍率变小)
DOWN_RIGHT_ZOOM_IN = 70  # 云台下俯和右转和焦距变大(倍率变大)
DOWN_RIGHT_ZOOM_OUT = 71  # 云台下俯和右转和焦距变小(倍率变小)
TILT_UP_ZOOM_IN = 72  # 云台上仰和焦距变大(倍率变大)
TILT_UP_ZOOM_OUT = 73  # 云台上仰和焦距变小(倍率变小)
NET_DVR_SET_PTZPOS = 292  # 云台设置PTZ位置
NET_DVR_GET_PTZPOS = 293  # 云台获取PTZ位置
NET_DVR_GET_PTZSCOPE = 294  # 云台获取PTZ范围

# 码流回调数据类型
NET_DVR_SYSHEAD = 1
NET_DVR_STREAMDATA = 2
NET_DVR_AUDIOSTREAMDATA = 3
NET_DVR_PRIVATE_DATA = 112


# 设备参数结构体 V30
class NET_DVR_DEVICEINFO_V30(ctypes.Structure):
    _fields_ = [
        ("sSerialNumber", c_byte * 48),  # 序列号
        ("byAlarmInPortNum", c_byte),  # 模拟报警输入个数
        ("byAlarmOutPortNum", c_byte),  # 模拟报警输出个数
        ("byDiskNum", c_byte),  # 硬盘个数
        ("byDVRType", c_byte),  # 设备类型
        ("byChanNum", c_byte),  # 设备模拟通道个数,数字(IP)通道最大个数为byIPChanNum + byHighDChanNum*256
        ("byStartChan", c_byte),  # 模拟通道的起始通道号,从1开始。数字通道的起始通道号见下面参数byStartDChan
        ("byAudioChanNum", c_byte),  # 设备语音对讲通道数
        ("byIPChanNum", c_byte),  # 设备最大数字通道个数,低8位,高8位见byHighDChanNum
        ("byZeroChanNum", c_byte),  # 零通道编码个数
        ("byMainProto", c_byte),  # 主码流传输协议类型:0- private,1- rtsp,2- 同时支持私有协议和rtsp协议取流(默认采用私有协议取流)
        ("bySubProto", c_byte),  # 子码流传输协议类型:0- private,1- rtsp,2- 同时支持私有协议和rtsp协议取流(默认采用私有协议取流)
        ("bySupport", c_byte),  # 能力,位与结果为0表示不支持,1表示支持
        # bySupport & 0x1,表示是否支持智能搜索
        # bySupport & 0x2,表示是否支持备份
        # bySupport & 0x4,表示是否支持压缩参数能力获取
        # bySupport & 0x8, 表示是否支持双网卡
        # bySupport & 0x10, 表示支持远程SADP
        # bySupport & 0x20, 表示支持Raid卡功能
        # bySupport & 0x40, 表示支持IPSAN目录查找
        # bySupport & 0x80, 表示支持rtp over rtsp
        ("bySupport1", c_byte),  # 能力集扩充,位与结果为0表示不支持,1表示支持
        # bySupport1 & 0x1, 表示是否支持snmp v30
        # bySupport1 & 0x2, 表示是否支持区分回放和下载
        # bySupport1 & 0x4, 表示是否支持布防优先级
        # bySupport1 & 0x8, 表示智能设备是否支持布防时间段扩展
        # bySupport1 & 0x10,表示是否支持多磁盘数(超过33个)
        # bySupport1 & 0x20,表示是否支持rtsp over http
        # bySupport1 & 0x80,表示是否支持车牌新报警信息,且还表示是否支持NET_DVR_IPPARACFG_V40配置
        ("bySupport2", c_byte),  # 能力集扩充,位与结果为0表示不支持,1表示支持
        # bySupport2 & 0x1, 表示解码器是否支持通过URL取流解码
        # bySupport2 & 0x2, 表示是否支持FTPV40
        # bySupport2 & 0x4, 表示是否支持ANR(断网录像)
        # bySupport2 & 0x20, 表示是否支持单独获取设备状态子项
        # bySupport2 & 0x40, 表示是否是码流加密设备
        ("wDevType", c_uint16),  # 设备型号,详见下文列表
        ("bySupport3", c_byte),  # 能力集扩展,位与结果:0- 不支持,1- 支持
        # bySupport3 & 0x1, 表示是否支持多码流
        # bySupport3 & 0x4, 表示是否支持按组配置,具体包含通道图像参数、报警输入参数、IP报警输入/输出接入参数、用户参数、设备工作状态、JPEG抓图、定时和时间抓图、硬盘盘组管理等
        # bySupport3 & 0x20, 表示是否支持通过DDNS域名解析取流
        ("byMultiStreamProto", c_byte),  # 是否支持多码流,按位表示,位与结果:0-不支持,1-支持
        # byMultiStreamProto & 0x1, 表示是否支持码流3
        # byMultiStreamProto & 0x2, 表示是否支持码流4
        # byMultiStreamProto & 0x40,表示是否支持主码流
        # byMultiStreamProto & 0x80,表示是否支持子码流
        ("byStartDChan", c_byte),  # 起始数字通道号,0表示无数字通道,比如DVR或IPC
        ("byStartDTalkChan", c_byte),  # 起始数字对讲通道号,区别于模拟对讲通道号,0表示无数字对讲通道
        ("byHighDChanNum", c_byte),  # 数字通道个数,高8位
        ("bySupport4", c_byte),  # 能力集扩展,按位表示,位与结果:0- 不支持,1- 支持
        # bySupport4 & 0x01, 表示是否所有码流类型同时支持RTSP和私有协议
        # bySupport4 & 0x10, 表示是否支持域名方式挂载网络硬盘
        ("byLanguageType", c_byte),  # 支持语种能力,按位表示,位与结果:0- 不支持,1- 支持
        # byLanguageType ==0,表示老设备,不支持该字段
        # byLanguageType & 0x1,表示是否支持中文
        # byLanguageType & 0x2,表示是否支持英文
        ("byVoiceInChanNum", c_byte),  # 音频输入通道数
        ("byStartVoiceInChanNo", c_byte),  # 音频输入起始通道号,0表示无效
        ("bySupport5", c_byte),  # 按位表示,0-不支持,1-支持,bit0-支持多码流
        ("bySupport6", c_byte),  # 按位表示,0-不支持,1-支持
        # bySupport6 & 0x1  表示设备是否支持压缩
        # bySupport6 & 0x2  表示是否支持流ID方式配置流来源扩展命令,DVR_SET_STREAM_SRC_INFO_V40
        # bySupport6 & 0x4  表示是否支持事件搜索V40接口
        # bySupport6 & 0x8  表示是否支持扩展智能侦测配置命令
        # bySupport6 & 0x40 表示图片查询结果V40扩展
        ("byMirrorChanNum", c_byte),  # 镜像通道个数,录播主机中用于表示导播通道
        ("wStartMirrorChanNo", c_uint16),  # 起始镜像通道号
        ("bySupport7", c_byte),  # 能力,按位表示,0-不支持,1-支持
        # bySupport7 & 0x1  表示设备是否支持NET_VCA_RULECFG_V42扩展
        # bySupport7 & 0x2  表示设备是否支持IPC HVT 模式扩展
        # bySupport7 & 0x04 表示设备是否支持返回锁定时间
        # bySupport7 & 0x08 表示设置云台PTZ位置时,是否支持带通道号
        # bySupport7 & 0x10 表示设备是否支持双系统升级备份
        # bySupport7 & 0x20 表示设备是否支持OSD字符叠加V50
        # bySupport7 & 0x40 表示设备是否支持主从跟踪(从摄像机)
        # bySupport7 & 0x80 表示设备是否支持报文加密
        ("byRes2", c_byte)]  # 保留,置为0


LPNET_DVR_DEVICEINFO_V30 = POINTER(NET_DVR_DEVICEINFO_V30)


# 设备参数结构体 V40
class NET_DVR_DEVICEINFO_V40(ctypes.Structure):
    _fields_ = [
        ('struDeviceV30', NET_DVR_DEVICEINFO_V30),  # 设备信息
        ('bySupportLock', c_byte),  # 设备支持锁定功能,该字段由SDK根据设备返回值来赋值的。bySupportLock为1时,dwSurplusLockTime和byRetryLoginTime有效
        ('byRetryLoginTime', c_byte),  # 剩余可尝试登陆的次数,用户名,密码错误时,此参数有效
        ('byPasswordLevel', c_byte),  # admin密码安全等级
        ('byProxyType', c_byte),  # 代理类型,0-不使用代理, 1-使用socks5代理, 2-使用EHome代理
        ('dwSurplusLockTime', c_uint32),  # 剩余时间,单位秒,用户锁定时,此参数有效
        ('byCharEncodeType', c_byte),  # 字符编码类型
        ('bySupportDev5', c_byte),  # 支持v50版本的设备参数获取,设备名称和设备类型名称长度扩展为64字节
        ('bySupport', c_byte),  # 能力集扩展,位与结果:0- 不支持,1- 支持
        ('byLoginMode', c_byte),  # 登录模式:0- Private登录,1- ISAPI登录
        ('dwOEMCode', c_uint32),  # OEM Code
        ('iResidualValidity', c_uint32),  # 该用户密码剩余有效天数,单位:天,返回负值,表示密码已经超期使用,例如“-3表示密码已经超期使用3天”
        ('byResidualValidity', c_byte),  # iResidualValidity字段是否有效,0-无效,1-有效
        ('bySingleStartDTalkChan', c_byte),  # 独立音轨接入的设备,起始接入通道号,0-为保留字节,无实际含义,音轨通道号不能从0开始
        ('bySingleDTalkChanNums', c_byte),  # 独立音轨接入的设备的通道总数,0-表示不支持
        ('byPassWordResetLevel', c_byte),  # 0-无效,
        # 1- 管理员创建一个非管理员用户为其设置密码,该非管理员用户正确登录设备后要提示“请修改初始登录密码”,未修改的情况下,用户每次登入都会进行提醒;
        # 2- 当非管理员用户的密码被管理员修改,该非管理员用户再次正确登录设备后,需要提示“请重新设置登录密码”,未修改的情况下,用户每次登入都会进行提醒。
        ('bySupportStreamEncrypt', c_byte),  # 能力集扩展,位与结果:0- 不支持,1- 支持
        # bySupportStreamEncrypt & 0x1 表示是否支持RTP/TLS取流
        # bySupportStreamEncrypt & 0x2 表示是否支持SRTP/UDP取流
        # bySupportStreamEncrypt & 0x4 表示是否支持SRTP/MULTICAST取流
        ('byMarketType', c_byte),  # 0-无效(未知类型),1-经销型,2-行业型
        ('byRes2', c_byte * 238)  # 保留,置为0
    ]


LPNET_DVR_DEVICEINFO_V40 = POINTER(NET_DVR_DEVICEINFO_V40)

# 异步登录回调函数
fLoginResultCallBack = CFUNCTYPE(None, c_uint32, c_uint32, LPNET_DVR_DEVICEINFO_V30, c_void_p)


# NET_DVR_Login_V40()参数
class NET_DVR_USER_LOGIN_INFO(Structure):
    _fields_ = [
        ("sDeviceAddress", c_char * 129),  # 设备地址,IP 或者普通域名
        ("byUseTransport", c_byte),  # 是否启用能力集透传:0- 不启用透传,默认;1- 启用透传
        ("wPort", c_uint16),  # 设备端口号,例如:8000
        ("sUserName", c_char * 64),  # 登录用户名,例如:admin
        ("sPassword", c_char * 64),  # 登录密码,例如:12345
        ("cbLoginResult", fLoginResultCallBack),  # 登录状态回调函数,bUseAsynLogin 为1时有效
        ("pUser", c_void_p),  # 用户数据
        ("bUseAsynLogin", c_uint32),  # 是否异步登录:0- 否,1- 是
        ("byProxyType", c_byte),  # 0:不使用代理,1:使用标准代理,2:使用EHome代理
        ("byUseUTCTime", c_byte),
        # 0-不进行转换,默认,1-接口上输入输出全部使用UTC时间,SDK完成UTC时间与设备时区的转换,2-接口上输入输出全部使用平台本地时间,SDK完成平台本地时间与设备时区的转换
        ("byLoginMode", c_byte),  # 0-Private 1-ISAPI 2-自适应
        ("byHttps", c_byte),  # 0-不适用tls,1-使用tls 2-自适应
        ("iProxyID", c_uint32),  # 代理服务器序号,添加代理服务器信息时,相对应的服务器数组下表值
        ("byVerifyMode", c_byte),  # 认证方式,0-不认证,1-双向认证,2-单向认证;认证仅在使用TLS的时候生效;
        ("byRes2", c_byte * 119)]


LPNET_DVR_USER_LOGIN_INFO = POINTER(NET_DVR_USER_LOGIN_INFO)
# PTZ参数的结构
class NET_DVR_PTZPOS(Structure):
    _fields_ = [
        ("wAction", ctypes.c_uint16),
        ("wPanPos", ctypes.c_uint16),
        ("wTiltPos", ctypes.c_uint16),
        ("wZoomPos", ctypes.c_uint16)]
LPNET_DVR_PTZPOS = POINTER(NET_DVR_PTZPOS)
# 光学变倍结构体
class NET_DVR_FOCUSMODE_CFG(ctypes.Structure):
    _fields_ = [
        ("dwSize", ctypes.c_uint16),
        ("byFocusMode", ctypes.c_byte),
        ("byAutoFocusMode", ctypes.c_byte),
        ("wMinFocusDistance", ctypes.c_uint16),
        ("byZoomSpeedLevel", ctypes.c_byte),
        ("byFocusSpeedLevel", ctypes.c_byte),
        ("byOpticalZoom", ctypes.c_byte),
        ("byDigtitalZoom", ctypes.c_byte),
        ("fOpticalZoomLevel", ctypes.c_float),
        ("dwFocusPos", ctypes.c_uint16),
        ("byFocusDefinitionDisplay", ctypes.c_byte),
        ("byFocusSensitivity", ctypes.c_byte),
        ("byRes1", ctypes.c_byte * 2),
        ("dwRelativeFocusPos", ctypes.c_uint16),
        ("byRes", ctypes.c_byte * 48), ]


FOCUSMODE_CFG = POINTER(NET_DVR_FOCUSMODE_CFG)


# 组件库加载路径信息
class NET_DVR_LOCAL_SDK_PATH(Structure):
    pass


LPNET_DVR_LOCAL_SDK_PATH = POINTER(NET_DVR_LOCAL_SDK_PATH)
NET_DVR_LOCAL_SDK_PATH._fields_ = [
    ('sPath', c_char * 256),  # 组件库地址
    ('byRes', c_byte * 128),
]


# 定义预览参数结构体
class NET_DVR_PREVIEWINFO(Structure):
    pass


LPNET_DVR_PREVIEWINFO = POINTER(NET_DVR_PREVIEWINFO)
NET_DVR_PREVIEWINFO._fields_ = [
    ('lChannel', c_uint32),  # 通道号
    ('dwStreamType', c_uint32),  # 码流类型,0-主码流,1-子码流,2-码流3,3-码流4, 4-码流5,5-码流6,7-码流7,8-码流8,9-码流9,10-码流10
    ('dwLinkMode', c_uint32),  # 0:TCP方式,1:UDP方式,2:多播方式,3 - RTP方式,4-RTP/RTSP,5-RSTP/HTTP ,6- HRUDP(可靠传输) ,7-RTSP/HTTPS
    ('hPlayWnd', c_uint32),  # 播放窗口的句柄,为NULL表示不播放图象
    ('bBlocked', c_uint32),  # 0-非阻塞取流, 1-阻塞取流, 如果阻塞SDK内部connect失败将会有5s的超时才能够返回,不适合于轮询取流操作
    ('bPassbackRecord', c_uint32),  # 0-不启用录像回传,1启用录像回传
    ('byPreviewMode', c_ubyte),  # 预览模式,0-正常预览,1-延迟预览
    ('byStreamID', c_ubyte * 32),  # 流ID,lChannel为0xffffffff时启用此参数
    ('byProtoType', c_ubyte),  # 应用层取流协议,0-私有协议,1-RTSP协议,
    # 2-SRTP码流加密(对应此结构体中dwLinkMode 字段,支持如下方式, 为1,表示udp传输方式,信令走TLS加密,码流走SRTP加密,为2,表示多播传输方式,信令走TLS加密,码流走SRTP加密)
    ('byRes1', c_ubyte),
    ('byVideoCodingType', c_ubyte),  # 码流数据编解码类型 0-通用编码数据 1-热成像探测器产生的原始数据
    ('dwDisplayBufNum', c_uint32),  # 播放库播放缓冲区最大缓冲帧数,范围1-50,置0时默认为1
    ('byNPQMode', c_ubyte),  # NPQ是直连模式,还是过流媒体:0-直连 1-过流媒体
    ('byRecvMetaData', c_ubyte),  # 是否接收metadata数据
    # 设备是否支持该功能通过GET /ISAPI/System/capabilities 中DeviceCap.SysCap.isSupportMetadata是否存在且为true
    ('byDataType', c_ubyte),  # 数据类型,0-码流数据,1-音频数据
    ('byRes', c_ubyte * 213),
]


# 定义JPEG图像信息结构体
class NET_DVR_JPEGPARA(Structure):
    pass


LPNET_DVR_JPEGPARA = POINTER(NET_DVR_JPEGPARA)
NET_DVR_JPEGPARA._fields_ = [
    ('wPicSize', c_ushort),
    ('wPicQuality', c_ushort),
]


# 叠加字符
class NET_DVR_SHOWSTRINGINFO(Structure):
    pass


LPNET_DVR_SHOWSTRINGINFO = POINTER(NET_DVR_SHOWSTRINGINFO)
NET_DVR_SHOWSTRINGINFO._fields_ = [
    ('wShowString', c_ushort),
    ('wStringSize', c_ushort),
    ('wShowStringTopLeftX', c_ushort),
    ('wShowStringTopLeftY', c_ushort),
    ('sString', c_ubyte * 44),
]


# 叠加字符
class NET_DVR_SHOWSTRING_V30(Structure):
    pass


LPNET_DVR_SHOWSTRING_V30 = POINTER(NET_DVR_SHOWSTRING_V30)
NET_DVR_SHOWSTRING_V30._fields_ = [
    ('dwSize', c_uint32),
    ('struStringInfo', NET_DVR_SHOWSTRINGINFO * 8),
]


# 透传接口输出参数结构体
class NET_DVR_XML_CONFIG_OUTPUT(Structure):
    pass


LPNET_DVR_XML_CONFIG_OUTPUT = POINTER(NET_DVR_XML_CONFIG_OUTPUT)
NET_DVR_XML_CONFIG_OUTPUT._fields_ = [
    ('dwSize', c_uint32),
    ('lpOutBuffer', c_void_p),
    ('dwOutBufferSize', c_uint32),
    ('dwReturnedXMLSize', c_uint32),
    ('lpStatusBuffer', c_void_p),
    ('dwStatusSize', c_uint32),
    ('byRes', c_ubyte * 32)
]


# 透传接口输入参数结构体
class NET_DVR_XML_CONFIG_INPUT(Structure):
    pass


LPNET_DVR_XML_CONFIG_INPUT = POINTER(NET_DVR_XML_CONFIG_INPUT)
NET_DVR_XML_CONFIG_INPUT._fields_ = [
    ('dwSize', c_uint32),
    ('lpRequestUrl', c_void_p),
    ('dwRequestUrlLen', c_uint32),
    ('lpInBuffer', c_void_p),
    ('dwInBufferSize', c_uint32),
    ('dwRecvTimeOut', c_uint32),
    ('byForceEncrpt', c_ubyte),
    ('byNumOfMultiPart', c_ubyte),
    ('byRes', c_ubyte * 30)
]
# 码流回调函数
REALDATACALLBACK = fun_ctype(None, c_long, c_ulong, POINTER(c_ubyte), c_ulong, c_void_p)

2.主代码调用NET_DVR_GetDVRConfig

在代码之中增加获取设备信息的NET_DVR_GetDVRConfig代码,按照起格式传递相应的结构体,来获取其设备信息。结构登录用NET_DVR_DEVICEINFO_V30和NET_DVR_Login_V40()结构,都可以返回设备信息。

# coding=utf-8
import os
import platform
import tkinter
from tkinter import *
from HCNetSDK import *
from PlayCtrl import *
from time import sleep

# 登录的设备信息
DEV_IP = create_string_buffer(b'10.17.35.41')
DEV_PORT = 8000
DEV_USER_NAME = create_string_buffer(b'admin')
DEV_PASSWORD = create_string_buffer(b'abcd1234')

WINDOWS_FLAG = True
win = None  # 预览窗口
funcRealDataCallBack_V30 = None  # 实时预览回调函数,需要定义为全局的

PlayCtrl_Port = c_long(-1)  # 播放句柄
Playctrldll = None  # 播放库
FuncDecCB = None  # 播放库解码回调函数,需要定义为全局的



def HexToDecMa(x):
    # 十进制转换为十六进制
    x = int(hex(x)[2:].upper()) // 10
    return x


def DEC2HEX_doc(x):
    # 十六进制转化为十进制
    x = int(str(x * 10), 16)
    return x

# 获取当前系统环境
def GetPlatform():
    sysstr = platform.system()
    print('' + sysstr)
    if sysstr != "Windows":
        global WINDOWS_FLAG
        WINDOWS_FLAG = False

# 设置SDK初始化依赖库路径
def SetSDKInitCfg():
    # 设置HCNetSDKCom组件库和SSL库加载路径
    # print(os.getcwd())
    if WINDOWS_FLAG:
        strPath = os.getcwd().encode('gbk')
        sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
        sdk_ComPath.sPath = strPath
        Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
        Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'\libcrypto-1_1-x64.dll'))
        Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'\libssl-1_1-x64.dll'))
    else:
        strPath = os.getcwd().encode('utf-8')
        sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
        sdk_ComPath.sPath = strPath
        Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
        Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'/libcrypto.so.1.1'))
        Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'/libssl.so.1.1'))

def LoginDev(Objdll):
    # 登录注册设备
    device_info = NET_DVR_DEVICEINFO_V30()
    lUserId = Objdll.NET_DVR_Login_V30(DEV_IP, DEV_PORT, DEV_USER_NAME, DEV_PASSWORD, byref(device_info))
    return (lUserId, device_info)
    
def DecCBFun(nPort, pBuf, nSize, pFrameInfo, nUser, nReserved2):
    # 解码回调函数
    if pFrameInfo.contents.nType == 3:
        # 解码返回视频YUV数据,将YUV数据转成jpg图片保存到本地
        # 如果有耗时处理,需要将解码数据拷贝到回调函数外面的其他线程里面处理,避免阻塞回调导致解码丢帧
        sFileName = ('../../pic/test_stamp[%d].jpg' % pFrameInfo.contents.nStamp)
        nWidth = pFrameInfo.contents.nWidth
        nHeight = pFrameInfo.contents.nHeight
        nType = pFrameInfo.contents.nType
        dwFrameNum = pFrameInfo.contents.dwFrameNum
        nStamp = pFrameInfo.contents.nStamp
        print(nWidth, nHeight, nType, dwFrameNum, nStamp, sFileName)

        lRet = Playctrldll.PlayM4_ConvertToJpegFile(pBuf, nSize, nWidth, nHeight, nType, c_char_p(sFileName.encode()))
        if lRet == 0:
            print('PlayM4_ConvertToJpegFile fail, error code is:', Playctrldll.PlayM4_GetLastError(nPort))
        else:
            print('PlayM4_ConvertToJpegFile success')

def RealDataCallBack_V30(lPlayHandle, dwDataType, pBuffer, dwBufSize, pUser):
    # 码流回调函数
    if dwDataType == NET_DVR_SYSHEAD:
        # 设置流播放模式
        Playctrldll.PlayM4_SetStreamOpenMode(PlayCtrl_Port, 0)
        # 打开码流,送入40字节系统头数据
        if Playctrldll.PlayM4_OpenStream(PlayCtrl_Port, pBuffer, dwBufSize, 1024 * 1024):
            # 设置解码回调,可以返回解码后YUV视频数据
            global FuncDecCB
            FuncDecCB = DECCBFUNWIN(DecCBFun)
            Playctrldll.PlayM4_SetDecCallBackExMend(PlayCtrl_Port, FuncDecCB, None, 0, None)
            # 开始解码播放
            if Playctrldll.PlayM4_Play(PlayCtrl_Port, cv.winfo_id()):
                print(u'播放库播放成功')
            else:
                print(u'播放库播放失败')
        else:
            print(u'播放库打开流失败')
    elif dwDataType == NET_DVR_STREAMDATA:
        Playctrldll.PlayM4_InputData(PlayCtrl_Port, pBuffer, dwBufSize)
    else:
        print(u'其他数据,长度:', dwBufSize)

def OpenPreview(Objdll, lUserId, callbackFun):
    '''
    打开预览
    '''
    preview_info = NET_DVR_PREVIEWINFO()
    preview_info.hPlayWnd = 0
    preview_info.lChannel = 1  # 通道号
    preview_info.dwStreamType = 0  # 主码流
    preview_info.dwLinkMode = 0  # TCP
    preview_info.bBlocked = 1  # 阻塞取流

    # 开始预览并且设置回调函数回调获取实时流数据
    lRealPlayHandle = Objdll.NET_DVR_RealPlay_V40(lUserId, byref(preview_info), callbackFun, None)
    return lRealPlayHandle

def InputData(fileMp4, Playctrldll):
    while True:
        pFileData = fileMp4.read(4096)
        if pFileData is None:
            break

        if not Playctrldll.PlayM4_InputData(PlayCtrl_Port, pFileData, len(pFileData)):
            break


if __name__ == '__main__':
    # 创建窗口
    win = tkinter.Tk()
    # 固定窗口大小
    win.resizable(0, 0)
    win.overrideredirect(True)

    sw = win.winfo_screenwidth()
    # 得到屏幕宽度
    sh = win.winfo_screenheight()
    # 得到屏幕高度

    # 窗口宽高
    ww = 512
    wh = 384
    x = (sw - ww) / 2
    y = (sh - wh) / 2
    win.geometry("%dx%d+%d+%d" % (ww, wh, x, y))

    # 创建退出按键
    b = Button(win, text='退出', command=win.quit)
    b.pack()
    # 创建一个Canvas,设置其背景色为白色
    cv = tkinter.Canvas(win, bg='white', width=ww, height=wh)
    cv.pack()

    # 获取系统平台
    GetPlatform()

    # 加载库,先加载依赖库
    if WINDOWS_FLAG:
        os.chdir(r'./lib/win')
        Objdll = ctypes.CDLL(r'./HCNetSDK.dll')  # 加载网络库
        Playctrldll = ctypes.CDLL(r'./PlayCtrl.dll')  # 加载播放库
    else:
        os.chdir(r'./lib/linux')
        Objdll = cdll.LoadLibrary(r'./libhcnetsdk.so')
        Playctrldll = cdll.LoadLibrary(r'./libPlayCtrl.so')

    SetSDKInitCfg()  # 设置组件库和SSL库加载路径

    # 初始化DLL
    Objdll.NET_DVR_Init()
    # 启用SDK写日志
    Objdll.NET_DVR_SetLogToFile(3, bytes('./SdkLog_Python/', encoding="utf-8"), False)

    # 获取一个播放句柄
    if not Playctrldll.PlayM4_GetPort(byref(PlayCtrl_Port)):
        print(u'获取播放库句柄失败')

    # 登录设备
    (lUserId, device_info) = LoginDev(Objdll)
    if lUserId < 0:
        err = Objdll.NET_DVR_GetLastError()
        print('Login device fail, error code is: %d' % Objdll.NET_DVR_GetLastError())
        # 释放资源
        Objdll.NET_DVR_Cleanup()
        exit()

    # 定义码流回调函数
    funcRealDataCallBack_V30 = REALDATACALLBACK(RealDataCallBack_V30)
    # 开启预览
    lRealPlayHandle = OpenPreview(Objdll, lUserId, funcRealDataCallBack_V30)
    if lRealPlayHandle < 0:
        print('Open preview fail, error code is: %d' % Objdll.NET_DVR_GetLastError())
        # 登出设备
        Objdll.NET_DVR_Logout(lUserId)
        # 释放资源
        Objdll.NET_DVR_Cleanup()
        exit()

    # show Windows
    win.mainloop()
    # 获得PTZ的位置信息
    dw_returned = ctypes.c_uint16(0)
    #创建一个PTZ的结构体
    ptzpos = NET_DVR_PTZPOS()

    res = Objdll.NET_DVR_GetDVRConfig(lUserId, NET_DVR_GET_PTZPOS, 1, byref(ptzpos), 255, byref(dw_returned))
    if not res:
        print('获取PTZ坐标信息失败')

    TempPosture = [0, 0, 0]
    TempPosture[0] = HexToDecMa(ptzpos.wPanPos)
    TempPosture[1] = HexToDecMa(ptzpos.wTiltPos)
    TempPosture[2] = HexToDecMa(ptzpos.wZoomPos)
    print("此时P的角度为:" + str(TempPosture[0]))
    print("此时T的角度为:" + str(TempPosture[1]))
    print("此时T的角度为:" + str(TempPosture[2]))
    # 水平方向扭转30度
    TempPosture[0] = TempPosture[0] + 30
    # 上下扭转10度
    TempPosture[1] = TempPosture[1] + 10
    # 焦距增大1
    TempPosture[2] = TempPosture[2] + 1
    # 水平扭转最多为360,最小为0,因为超过和小于限定值需要判断
    if TempPosture[0] > 360:
        TempPosture[0] -= 360
    elif TempPosture[0] < 0:
        TempPosture[0] += 360
    # 上下转动范围是0-180
    if TempPosture[1] > 180:
        TempPosture[1] -= 180
    elif TempPosture[1] < 0:
        TempPosture[1] += 180
	# 变焦的倍数为1-23
    if TempPosture[2] <= 0:
        TempPosture[2] = 1
    ptzpos.wPanPos = DEC2HEX_doc(TempPosture[0])
    ptzpos.wTiltPos = DEC2HEX_doc(TempPosture[1])
    ptzpos.wZoomPos = DEC2HEX_doc(TempPosture[2])
    # 变更PTZ的值
    res = Objdll.NET_DVR_SetDVRConfig(lUserId, NET_DVR_SET_PTZPOS, 1, byref(ptzpos), 255, byref(dw_returned))
  	TempPosture[0] = HexToDecMa(ptzpos.wPanPos)
    TempPosture[1] = HexToDecMa(ptzpos.wTiltPos)
    TempPosture[2] = HexToDecMa(ptzpos.wZoomPos)
    print("此时P的角度为:" + str(TempPosture[0]))
    print("此时T的角度为:" + str(TempPosture[1]))
    print("此时T的角度为:" + str(TempPosture[2]))
    
    if not res:
        print('设置PTZ值失败')

    # 关闭预览
    Objdll.NET_DVR_StopRealPlay(lRealPlayHandle)

    # 停止解码,释放播放库资源
    if PlayCtrl_Port.value > -1:
        Playctrldll.PlayM4_Stop(PlayCtrl_Port)
        Playctrldll.PlayM4_CloseStream(PlayCtrl_Port)
        Playctrldll.PlayM4_FreePort(PlayCtrl_Port)
        PlayCtrl_Port = c_long(-1)

    # 登出设备
    Objdll.NET_DVR_Logout(lUserId)

    # 释放资源
    Objdll.NET_DVR_Cleanup()

其余用的库都是官方自带的,缺少的话,去海康威视SDK官方下载即可。实际显示的PTZ值是获取到的十六进制值的十分之一,因此需要首先将获得的十进制值转化为十六进制,然后取其十分之一则是最后的角度信息。

猜你喜欢

转载自blog.csdn.net/qq_43276926/article/details/126430956