项目最近需要改造升级:操作海康摄像头(包括登录,拍照,录像)等基本功能。经过一段时间研究后,发现使用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. Unity3D热更新之LuaFramework篇[07]--怎么让unity对象绑定Lua脚本

    前言 在上一篇文章 Unity3D热更新之LuaFramework篇[06]--Lua中是怎么实现脚本生命周期的 中,我分析了由LuaBehaviour来实现lua脚本生命周期的方法. 但在实际使用中 ...

  2. C#七种设计原则

    在C#中有七种设计原则 分别是 1.开闭原则(Open-Closed Principle, OCP) 2.单一职责原则(Single Responsibility Principle) 3.里氏替换原 ...

  3. Reporting报表开发知识合并[个人原创]

    [个人原创] ,转发请声明原文链接 了解 a)      SSRS全称 SQL Server Reporting Services,是依赖于数据库运行的,是微软开发的重量级别的BI产品 b)      ...

  4. hdoj 4762 Cut the Cake

    题意很简单就不说了. 解题的关键就是这个公式 answer=n/(m^(n-1)); 要用到大数的乘法.然后java水过. import java.util.*; import java.math.* ...

  5. golang-http 请求---设置header与直接发

    背景 现在各种软件到处都是,写代码难免有时候需要http 调用其他的接口. 其实这个东西还挺常用,虽然很简单,但是写的时候 又忘,就像是提笔忘字,索性总结一下吧. 不需要设置header属性的http ...

  6. Extjs的使用总结笔记

    一:Extjs自带验证 1.alpha //只能输入字母,无法输入其他(如数字,特殊符号等) 2.alphanum//只能输入字母和数字,无法输入其他 3.email//email验证,要求的格式是& ...

  7. kubeproxy源码分析

    kubernetes离线安装包,仅需三步 kube-proxy源码解析 ipvs相对于iptables模式具备较高的性能与稳定性, 本文讲以此模式的源码解析为主,如果想去了解iptables模式的原理 ...

  8. 用HTML5的Audio标签做一个歌词同步的效果

    HTML5出来这么久了,但是关于它里面的audio标签也就用过那么一次,当然还仅仅只是把这个标签插入到了页面中.这次呢就刚好趁着帮朋友做几个页面,拿这个audio标签来练练手. 首先你需要向页面中插入 ...

  9. Of efficiency and methodology

    There are only too many articles and books which pertains to the discussion of efficiency and method ...

  10. JAVA基础知识(二):List接口、ArrayList类和LinkedList类

    List接口继承了Collection接口,位于java.util包中.它包含Collection接口的所有方法,外加其他一些方法(具体实现参考源码),比较重要的有: anyType get(int ...