目录
准备工作
开发环境信息
改写 HCNetSDK.h 头文件
开发过程
基本数据类型转换
业务开发
参考
项目最近需要改造升级: 操作海康摄像头 (包括登录, 拍照, 录像) 等基本功能. 经过一段时间研究后, 发现使用 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 使用总结
来源: https://www.cnblogs.com/dust90/p/11447622.html