• 使用golang对海康sdk进行业务开发


    项目最近需要改造升级:操作海康摄像头(包括登录,拍照,录像)等基本功能。经过一段时间研究后,发现使用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

  • 相关阅读:
    功能测试用例大全
    相对最完整的软件测试工具手册
    测试用例的评审
    黑盒测试学习笔记-(深圳文鹏)
    Llinux:ubuntu常用命令(深圳文鹏)
    HDU-4857(拓扑排序)
    HDU-3665(单源最短路)
    HDU-3661(贪心)
    HDU-2059龟兔赛跑(基础方程DP-遍历之前的所有状态)
    HDU-1047(DP-二进制状态压缩)
  • 原文地址:https://www.cnblogs.com/dust90/p/11447622.html
Copyright © 2020-2023  润新知