项目最近需要改造升级:操作海康摄像头(包括登录,拍照,录像)等基本功能。经过一段时间研究后,发现使用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.系统日志的默认分类 系统日志会默认记录在以下地址中,不 ...

  2. vue项目目录结构详解

    项目简介基于 vue.js 的前端开发环境,用于前后端分离后的单页应用开发,可以在开发时使用 ES Next.scss 等最新语言特性.项目包含: 基础库: vue.js.vue-router.vue ...

  3. C#加密解密(AES)

    using System; namespace Encrypt { public class AESHelper { /// <summary> /// 默认密钥-密钥的长度必须是32 / ...

  4. 负载分配—DNS的域名解析

    DNS(Domain Name System)是因特网的一项服务,它作为域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网.人们在通过浏览器访问网站时只需要记住网站的域名即可,而不需 ...

  5. java使用栈计算后缀表达式

    package com.nps.base.xue.DataStructure.stack.utils; import java.util.Scanner; import java.util.Stack ...

  6. CSS3☞transform变换

    transform CSStransform属性允许你旋转,缩放,倾斜或平移给定元素.这是通过修改CSS视觉格式化模型的坐标空间来实现的. DEMO /* Keyword values */ tran ...

  7. 思路重要or技术重要?

    1,思路串通代码的重要性 前段时间,同事在工作上出现一点难题,在技术大佬中看起来算是微不足道的一点小事,由于没有思路,代码也无从下手,他在百度上条框上搜索自己想要的答案,却始终没有比较理想的,大部分的 ...

  8. 【Machine Learning·机器学习】决策树之ID3算法(Iterative Dichotomiser 3)

    目录 1.什么是决策树 2.如何构造一棵决策树? 2.1.基本方法 2.2.评价标准是什么/如何量化评价一个特征的好坏? 2.3.信息熵.信息增益的计算 2.4.决策树构建方法 3.算法总结 @ 1. ...

  9. java之Arrays.asList

    使用Arrays.asList()的原因无非是想将数组或一些元素转为集合,而你得到的集合并不一定是你想要的那个集合. 而一开始asList的设计时用于打印数组而设计的,但jdk1.5开始,有了另一个比 ...

  10. 使用 Docker 生成 Let’s Encrypt 证书

    概念 什么是 Container ? https://www.docker.com/resources/what-container https://www.docker.com/why-docker ...