Java调用海康SDK读取图像

2023-02-26,,

前言

视频识别的一个项目,前期采用Java调用OpenCV读取的方案,然后发送到Redis服务器,由于OpenCV采图像不稳定,需要研究使用海康SDK读取图像。由于本人海康SDK的demo已成功运行,这里直接删除了demo里不需要的东西,demo正常运行也会附到本文。
海康windows64SDK下载
opencv-4-4-0下载
代码示例下载

1、加载动态链接库dll

下载海康SDK,并放到指定文件夹

HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("D:\\Java\\HCNetSDK\\库文件\\HCNetSDK.dll", HCNetSDK.class);

PlayCtrl INSTANCE = (PlayCtrl) Native.loadLibrary("D:\\Java\\HCNetSDK\\库文件\\PlayCtrl.dll", PlayCtrl.class);

这里采用了直接加载dll的方式,没有用eclipse加载lib文件的方式,HCNetSDK用于登录、预览,PlayCtrl用于播放,OpenCV 4.4.0

2、调用逻辑

先调用NET_DVR_Init方法进行初始化,调用NET_DVR_Login_V30进行登录,调用NET_DVR_RealPlay_V30进行预览

lPreviewHandle = hCNetSDK.NET_DVR_RealPlay_V30(lUserID, m_strClientInfo, fRealDataCallBack, null, true);

这里需要fRealDataCallBack预览回调,是一个参数,需要自定义一个类去实现一个接口,

case HCNetSDK.NET_DVR_SYSHEAD是预览时第一次会进入系统头这个逻辑,相当于配置一下,这里是配置了PlayM4_SetDecCallBackSDK解码回调,这里需要一个fDecCallBackSDK解码回调函数,在下面附上

case HCNetSDK.NET_DVR_STREAMDATA:是上面配置完,下一次就会进入这个case语句,这个逻辑是放入码流数据

    class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {
        public void invoke(NativeLong lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) {
            switch (dwDataType) {
                case HCNetSDK.NET_DVR_SYSHEAD: //系统头
                    System.out.println(logHead + "FRealDataCallBack 系统头");
                    if (!playControl.PlayM4_GetPort(m_lPort)) //获取播放库未使用的通道号
                    {
                        break;
                    }
                    if (dwBufSize > 0) {
                        if (!playControl.PlayM4_SetDecCallBack(m_lPort.getValue(), fDecCallBack))  //设置解码回调
                        {
                            break;
                        }
                        // 设置解码回调函数 解码且显示
                        // if (!playControl.PlayM4_SetDecCallBackEx(m_lPort.getValue(), fDecCallBack, null, null)) {
                        //     break;
                        // }
                        if (!playControl.PlayM4_SetStreamOpenMode(m_lPort.getValue(), PlayCtrl.STREAME_REALTIME))  //设置实时流播放模式
                        {
                            break;
                        }

                        if (!playControl.PlayM4_OpenStream(m_lPort.getValue(), pBuffer, dwBufSize, 1024 * 1024)) //打开流接口
                        {
                            break;
                        }
                        if (!playControl.PlayM4_Play(m_lPort.getValue(), null)) //播放开始
                        {
                            break;
                        }
                    }
                case HCNetSDK.NET_DVR_STREAMDATA:   //码流数据
                    if (!playControl.PlayM4_InputData(m_lPort.getValue(), pBuffer, dwBufSize)) {
                        break;
                    }
            }
        }
    }

fDecCallBackSDK解码回调函数如下,解码回调函数里面的数据,是sdk软解码以后的数据,可以直接拿来处理,不过里面的代码需要在40ms里处理完毕,不然会产生很大的延迟

    class FDecCallBack implements PlayCtrl.DecCallBack {
        @Override
        public void invoke(NativeLong nPort, ByteByReference pBuffer, NativeLong nSize, PlayCtrl.FRAME_INFO frameInfo, NativeLong nReserved1, NativeLong nReserved2) {
            if (++count % 6 == 0) {
                try {
                    handle(pBuffer, nSize.intValue(), frameInfo);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        //这样在回调函数DecCallBack 中可以得到视音频数据,其中视频数据是YV12格式的,音频数据是PCM格式的。
        public void handle(ByteByReference pBuffer, int dwBufSize, PlayCtrl.FRAME_INFO frameInfo) {
            src = null;
            dst = null;
            bufferedImage = null;
            boolean isLog = new Date().getSeconds() % 10 == 0;
            String printStr = null;
            if (isLog) {
                stopWatch.reset();
                stopWatch.start();
            }

            int width = frameInfo.nWidth.intValue();
            int height = frameInfo.nHeight.intValue();
            byte[] byteArray = pBuffer.getPointer().getByteArray(0, dwBufSize);

            src = new Mat(height + height / 2, width, CvType.CV_8UC1);
            src.put(0, 0, byteArray);
            if (isLog) {
                stopWatch.split();
                printStr = "";
                printStr += logHead + "put " + stopWatch.getSplitTime() + "\r\n";
            }

            dst = new Mat(height, width, CvType.CV_8UC3);
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_YUV2BGR_YV12);
            if (isLog) {
                stopWatch.split();
                printStr += logHead + "cvtColor " + stopWatch.getSplitTime() + "\r\n";
            }

            bufferedImage = Mat2BufImg.Mat2BufImg(dst);
            if (isLog) {
                stopWatch.split();
                printStr += logHead + "Mat2BufImg " + stopWatch.getSplitTime() + "\r\n";
            }

            String imgHex = Mat2BufImg.bufImgToBase64(bufferedImage, "jpg");
            if (isLog) {
                stopWatch.split();
                printStr += logHead + "bufImgToBase64 " + stopWatch.getSplitTime() + "\r\n";
            }

            Jedis jedis = JedisUtil.getJedis();
            jedis.set(ip, imgHex);
            JedisUtil.close(jedis);
            if (isLog) {
                stopWatch.split();
                printStr += logHead + "set " + stopWatch.getSplitTime() + "\r\n";
                System.out.println(printStr);
            }
        }
    }

3、结尾

大体流程就是这样子,要不我画一张流程图哇,哈哈