项目最近需要改造升级:操作海康摄像头(包括登录,拍照,录像)等基本功能。经过一段时间研究后,发现使用golang的cgo来进行开发,甚是方便,不用考虑生成多余的golang代码,直接调用海康sdk中的函数代码。


准备工作

开发环境信息

Windows10下进行开发,使用海康sdk是CH-HCNetSDKV6.0.2.35_build20190411_Win64版本。go版本号go1.12.7

改写HCNetSDK.h头文件

海康威视提供的头文件是不能被cgo所识别的,而cgo是不能使用C++相关的东西的,比如标准库或者C++的面向对象特性,导致其会疯狂的报语法错误.

查询资料后得知,该头文件中有以下情况,就不能通过编译:

  • 注释里面套注释,例如这样的//这里是注释1 /*这里是注释2*/
  • #define xxx时,若后面函数被xxx修饰,当xxx无对应的值而仅仅是被定义的时候;
  • c++语法,例如联合嵌套在C++中是不支持的,c++的bool类型等

在开发的时候,发现原HCNetSDK.h文件里面有五万多行,如果全部的改造,那么会花费大量的时间。在c++开发的同事的建议下:只取出与开发功能相关的代码进行改造(改造为cgo可以识别的代码)。

改造规则如下:

  • 去掉所有注释
  • 去掉函数前面的NET_DVR_API__std
  • 去掉CALLBACK
  • 为没有tag的结构体加上tag前缀
  • 删除无实现的函数

开发过程

基本数据类型转换

由于在开发过程中涉及到基本的golang和c的数据类型转换,查阅资料后,转换对应关系如下:

C语言类型 CGO类型 Go语言类型
char C.char byte
singed char C.schar int8
unsigned char C.uchar uint8
short C.short int16
unsigned short C.ushort uint16
int C.int int32
unsigned int C.uint uint32
long C.long int32
unsigned long C.ulong uint32
long long int C.longlong int64
unsigned long long int C.ulonglong uint64
float C.float float32
double C.double float64
size_t C.size_t uint

注意 C 中的整形比如 int 在标准中是没有定义具体字长的,但一般默认认为是 4 字节,对应 CGO 类型中 C.int 则明确定义了字长是 4 ,但 golang 中的 int 字长则是 8 ,因此对应的 golang 类型不是 int 而是 int32 。为了避免误用,C 代码最好使用 C99 标准的数值类型,对应的转换关系如下:

C语言类型 CGO类型 Go语言类型
int8_t C.int8_t int8
uint8_t C.uint8_t uint8
int16_t C.int16_t int16
uint16_t C.uint16_t uint16
int32_t C.int32_t int32
uint32_t C.uint32_t uint32
int64_t C.int64_t int64
uint64_t C.uint64_t uint64

业务开发

HCNetSDK.go

package main

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. -lHCCore
#cgo LDFLAGS: -L. -lHCNetSDK
#include "HCNetSDK.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> */
import "C"
import (
"errors"
"fmt"
"time"
"unsafe"
) // 是否有错误
func isErr(oper string) error {
errno := int64(C.NET_DVR_GetLastError())
if errno > 0 {
reMsg := fmt.Sprintf("%s摄像头失败,失败代码号:%d", oper, errno)
return errors.New(reMsg)
}
return nil
} // 初始化海康摄像头
func Init() (err error) {
C.NET_DVR_Init()
if err = isErr("Init"); err != nil {
return
}
// 设置连接时间
C.NET_DVR_SetConnectTime(C.DWORD(2000), C.DWORD(1))
if err = isErr("SetConnectTime"); err != nil {
return
}
return nil
} // 登录摄像头
func Login() (int64,error) {
var deviceinfoV30 C.NET_DVR_DEVICEINFO_V30
c_ip := C.CString("192.168.1.64")
defer C.free(unsafe.Pointer(c_ip)) c_login := C.CString("admin")
defer C.free(unsafe.Pointer(c_login)) c_password := C.CString("admin")
defer C.free(unsafe.Pointer(c_password)) msgId := C.NET_DVR_Login_V30(c_ip,C.WORD(8080),c_login,c_password,
(*C.NET_DVR_DEVICEINFO_V30)(unsafe.Pointer(&deviceinfoV30)),
) if int64(msgId) < 0 {
if err := isErr("Login"); err != nil {
return -1,err
}
return -1,errors.New("登录摄像头失败")
}
return int64(msgId),nil
} // 退出摄像头登录
// uid:摄像头登录成功的id
func Logout(uid int64) error {
C.NET_DVR_Logout_V30(C.LONG(uid))
if err := isErr("Logout"); err != nil {
return err
}
return nil
} // 播放视频
// uid:摄像头登录成功的id
// 返回播放视频标识 pid
func Play(uid int64)(int64, error) {
var pDetectInfo C.NET_DVR_CLIENTINFO
pDetectInfo.lChannel = C.LONG(1)
pid := C.NET_DVR_RealPlay_V30(C.LONG(uid),(*C.NET_DVR_CLIENTINFO)(unsafe.Pointer(&pDetectInfo)),nil,nil,C.BOOL(1))
if int64(pid) < 0 {
if err := isErr("Play"); err != nil {
return -1,err
}
return -1,errors.New("播放失败")
} return int64(pid),nil
} // 抓拍
func Capture(uid int64) (string, error){
picPath := "D:\\" + time.Now().Format("20060102150405") + ".jpeg" var jpegpara C.NET_DVR_JPEGPARA
var lChannel uint32 = 1
c_path := C.CString(picPath)
defer C.free(unsafe.Pointer(c_path))
msgId := C.NET_DVR_CaptureJPEGPicture(C.LONG(uid), C.LONG(lChannel),
(*C.NET_DVR_JPEGPARA)(unsafe.Pointer(&jpegpara)),
c_path,
) if int64(msgId) < 0 {
if err := isErr("Capture"); err != nil {
return "",err
}
return "",errors.New("抓拍失败")
}
return picPath,nil
} // 停止相机
// pid 播放标识符
func PtzStop(pid int64) error {
msgId := C.NET_DVR_StopRealPlay(C.LONG(pid))
if int64(msgId) < 0 {
if err := isErr("PtzStop"); err != nil {
return err
}
return errors.New("停止相机失败")
}
return nil
} func main() {
var err error
err = Init()
defer Close()
if err != nil {
log.Fatal(err.Error())
} var uid int64
if uid,err = Login();err != nil {
log.Fatal(err.Error())
} var picPath string
if picPath,err = Capture(uid);err != nil {
log.Fatal(err.Error())
}
log.Println("图片路径:",picPath) var pid int64
if pid,err = Play(uid);err != nil {
log.Fatal(err.Error())
} if err = PtzStop(pid);err != nil {
log.Fatal(err.Error())
} if err = Logout(uid);err != nil {
log.Fatal(err.Error())
} }

Makefile

export CGO_ENABLED=1
export WDIR=${PWD} all: windows windows:
CGO_LDFLAGS_ALLOW=".*" CGO_CFLAGS="-I${WDIR}/include" CGO_LDFLAGS="-L${WDIR}/lib/Windows -Wl,--enable-stdcall-fixup,-rpath=${WDIR}/lib/Windows -lHCNetSDK" GOOS=windows CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -ldflags "-s -w" -o build/Windows/hk.exe src/HCNetSDK.go
cp lib/Windows/HCNetSDK.dll build/Windows/
cp lib/Windows/HCCore.dll build/Windows/
cp -r lib/Windows/HCNetSDKCom/ build/Windows/ clean:
rm -r build/

通过make命令该文件即可。(注意海康开发文档中的说明)


参考

SWIG编译海康威视SDK 使用golang

golang cgo 使用总结

hikavision-recover

使用golang对海康sdk进行业务开发的更多相关文章

  1. golang调用海康sdk

    git地址:https://gitee.com/mimo431/hcnet-sdk_golang 网络不太流畅,先传gitee上 参考链接: https://www.cnblogs.com/dust9 ...

  2. 海康SDK编程指南(C#二次开发版本)

    海康SDK编程指南 目前使用的海康SDK包括IPC_SDK(硬件设备),Plat_SDK(平台),其中两套SDK都需单独调用海康播放库PlayCtrl.dll来解码视频流,返回视频信息和角度信息.本文 ...

  3. 海康SDK编程指南

    转至心澄欲遣 目前使用的海康SDK包括IPC_SDK(硬件设备),Plat_SDK(平台),其中两套SDK都需单独调用海康播放库PlayCtrl.dll来解码视频流,返回视频信息和角度信息.本文仅对视 ...

  4. 海康SDK JAVA版本调用步骤及问题介绍

    一.前言 本文为海康SDK JAVA版本Demo的介绍,采用Eclipse运行,以及一些问题记录. 海康SDK版本:SDK_Win32 Eclipse版本:Mars2.0 JDK版本:1.8.0_15 ...

  5. 封装海康SDK出现无法加载 DLL“..\bin\HCNetSDK.dll”: 找不到指定的模块

    今天在封装海康设备的时候出现了这么一个问题,在初始化的时候提升无法加载 DLL“..\bin\HCNetSDK.dll”: 找不到指定的模块. 在网上查找了几个方法,并不是很靠谱,于是从源头找找,是什 ...

  6. C#制作ActiveX控件中调用海康SDK的问题

    事情是这样的,有一台海康威视的摄像头,客户需要一个ActiveX控件嵌入到网页中,通过点击按钮开始录制和结束录制来进行视频的录制和保存,关于海康摄像头的二次开发在此就不多说了,可以参考SDK中的说明. ...

  7. 海康sdk

    package com.hikvision.artemis.sdk.util; import java.util.Collections; import java.util.Iterator; imp ...

  8. 使用c#封装海康SDK出现无法加载 DLL“..\bin\HCNetSDK.dll”: 找不到指定的模块

    最近在研究网络摄像头的二次开发,测试了一款海康威视的网络摄像头,程序调试的时候,出现如题的报错. 调试随机自带的demo时,程序运行正常,但当把该程序引入到我自己的程序中时,就开始报错.根据开发软件包 ...

  9. 海康相机SDK二次开发只有视频无声音问题

    海康SDK相信做企业开发的的同仁,在项目中经常会用到,毕竟使用范围这么广. 本次就开发遇到的奇葩问题来说明一下我们的解决方案. 场景 虽然海康有4200客户端,但是对于高度定制化的项目,肯定不能再使用 ...

随机推荐

  1. linux初学者-系统启动故障篇

    linux初学者-系统启动故障篇 在系统的操作中,有时会不小心误删或者操作失误使得系统启动不起来,下文将列举几种常见的系统启动失败的情况及解决的办法. 1.删除或者覆盖mbr的446个字节 mbr的4 ...

  2. centos7下yum方式安装MySQL5.7

    前言: MySQL作为一款免费.开源数据库产品,已经问世就饱受关注,很多中小企业甚至是大企业都钟爱MySQL,随着大数据的不断发展,我们接触的信息量也越来越多,虽然NoSQL是大数据的宠儿,但MySQ ...

  3. jsp对数据库的使用

    JDBC由Sun公司制定,全称JavaDataBase Connectivity,是一种可以执行SQL语句并可返回结果的javaAPI,支持多种关系型数据库,封装在Java.sql.* 它的具体位置在 ...

  4. TreeSet类的排序

    TreeSet支持两种排序方法:自然排序和定制排序.TreeSet默认采用自然排序. 1.自然排序 TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间大小关系, ...

  5. win10虚拟机搭建Hadoop集群(已完结)

    1 在虚拟机安装 Ubuntu 2 安装网络工具 Ubuntu最小化安装没有 ifconfig命令 sudo apt-get install net-tools 3 Ubuntu修改网卡名字 修改网卡 ...

  6. 【JDK】JDK源码分析-ArrayList

    概述 ArrayList 是 List 接口的一个实现类,也是 Java 中最常用的容器实现类之一,可以把它理解为「可变数组」. 我们知道,Java 中的数组初始化时需要指定长度,而且指定后不能改变. ...

  7. 如何确定FPGA电路中DDR4的Speed bin 是否兼容?

    原创 by DeeZeng DDR4 是否兼容,拿更快速度的DDR4,是否可以不改FPGA工程,直接换料就能直接用? 实际工作中,经常会碰到因为DDR3/4 或其他料件换料了,需要判断FPGA工程中I ...

  8. 详细分享TortoiseGit配置密钥的方法

    详细分享TortoiseGit配置密钥的方法 TortoiseGit 使用扩展名为ppk的密钥,而不是ssh-keygen生成的rsa密钥.使用命令ssh-keygen -C "邮箱地址&q ...

  9. 夯实Java基础(六)——包装类

    1.包装类简介 我们都知道Java是面向对象编程语言,包含了8种基本数据类型,但是这8种基本数据类型并不支持面向对象的特征,它们既不是类,也不能调用方法.这在实际使用时存在很多的不便,比如int类型需 ...

  10. 大白话5分钟带你走进人工智能-第32节集成学习之最通俗理解XGBoost原理和过程

    目录 1.回顾: 1.1 有监督学习中的相关概念 1.2  回归树概念 1.3 树的优点 2.怎么训练模型: 2.1 案例引入 2.2 XGBoost目标函数求解 3.XGBoost中正则项的显式表达 ...