liusuyi
2026-05-14 9958bc1501d0daee954d9bba383475e55e24ebab
ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java
@@ -5,6 +5,7 @@
import com.ard.common.core.exception.ServiceException;
import com.ard.common.core.exception.device.CameraSDKException;
import com.ard.common.core.utils.StringUtils;
import com.ard.common.core.utils.file.FileMultipartFile;
import com.ard.common.core.utils.file.FileUtils;
import com.ard.common.core.utils.file.MimeTypeUtils;
import com.ard.common.redis.service.RedisService;
@@ -30,7 +31,6 @@
import com.ard.work.sdk.model.PtzScope;
import com.ard.work.sdk.service.CameraSDK;
import com.ard.work.utils.FFmpegUtils;
import com.ard.work.utils.file.MultipartFileUtil;
import com.ard.work.utils.gis.GisUtil;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
@@ -1561,6 +1561,7 @@
        String type = cmd.getType() == null ? "other" : cmd.getType();
        String bucketName = cmd.getBucketName() == null ? "pic" : cmd.getBucketName();
        int userId = cmd.getLoginId().intValue();
        //图片信息
        NET_DVR_JPEGPARA jpeg = new NET_DVR_JPEGPARA();
        //设置图片分辨率
@@ -1570,8 +1571,9 @@
        IntByReference a = new IntByReference();
        //设置图片大小
        ByteBuffer jpegBuffer = ByteBuffer.allocate(1024 * 1024);
        // 抓图到内存,单帧数据捕获并保存成JPEG存放在指定的内存空间中
        // 抓图到内存
        boolean bool = hCNetSDK.NET_DVR_CaptureJPEGPicture_NEW(userId, chanNo, jpeg, jpegBuffer, 1024 * 1024, a);
        if (!bool) {
            int errorCode = hCNetSDK.NET_DVR_GetLastError();
            String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
@@ -1579,19 +1581,46 @@
            log.warn(errorMsg);
            return "";
        }
        log.debug("hikSdk(抓图)-结果状态值(0表示成功):{}", hCNetSDK.NET_DVR_GetLastError());
        byte[] array = jpegBuffer.array();
        // 获取实际图片数据
        int actualLength = a.getValue();
        byte[] imageBytes;
        if (actualLength > 0 && actualLength < 1024 * 1024) {
            imageBytes = new byte[actualLength];
            System.arraycopy(jpegBuffer.array(), 0, imageBytes, 0, actualLength);
        } else {
            imageBytes = jpegBuffer.array();
        }
        //存储到minio
        String objectName = cameraId + "_" + chanNo + ".jpeg";
        InputStream inputStream = new ByteArrayInputStream(array);
        String url = "";
        MultipartFile multipartFile = MultipartFileUtil.inputStreamToMultipartFile(inputStream, objectName,
                MimeTypeUtils.IMAGE_JPEG);
        File tempFile = null;
        try {
            // 创建临时文件
            tempFile = File.createTempFile("snap_", ".jpeg");
            java.nio.file.Files.write(tempFile.toPath(), imageBytes);
            // 使用 FileMultipartFile 转换
            MultipartFile multipartFile = new FileMultipartFile(tempFile, objectName, MimeTypeUtils.IMAGE_JPEG);
        R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
        if (sysFileR.getCode() == R.SUCCESS) {
            url = sysFileR.getData().getUrl();
            log.debug("上传文件成功:{}", url);
        }
        } catch (Exception e) {
            log.error("上传失败", e);
        } finally {
            // 删除临时文件
            if (tempFile != null && tempFile.exists()) {
                tempFile.delete();
            }
        }
        return url;
    }
@@ -1602,6 +1631,7 @@
        Integer chanNo = cmd.getChanNo();
        String type = cmd.getType() == null ? "other" : cmd.getType();
        Integer userId = cmd.getLoginId().intValue();
        //图片信息
        NET_DVR_JPEGPARA jpeg = new NET_DVR_JPEGPARA();
        //设置图片分辨率
@@ -1613,6 +1643,7 @@
        ByteBuffer jpegBuffer = ByteBuffer.allocate(1024 * 1024);
        // 抓图到内存,单帧数据捕获并保存成JPEG存放在指定的内存空间中
        boolean bool = hCNetSDK.NET_DVR_CaptureJPEGPicture_NEW(userId, chanNo, jpeg, jpegBuffer, 1024 * 1024, a);
        if (!bool) {
            int errorCode = hCNetSDK.NET_DVR_GetLastError();
            String errorDesc = SdkErrorCodeEnum.getDescByCode(errorCode);
@@ -1620,22 +1651,50 @@
            log.warn(errorMsg);
            return "";
        }
        byte[] array = jpegBuffer.array();
        InputStream inputStream = new ByteArrayInputStream(array);
        // 获取实际图片数据(只复制有效长度)
        int actualLength = a.getValue();
        byte[] imageBytes;
        if (actualLength > 0 && actualLength < 1024 * 1024) {
            imageBytes = new byte[actualLength];
            System.arraycopy(jpegBuffer.array(), 0, imageBytes, 0, actualLength);
        } else {
            imageBytes = jpegBuffer.array();
        }
        String url = "";
        MultipartFile multipartFile = MultipartFileUtil.inputStreamToMultipartFile(inputStream, objectName,
                MimeTypeUtils.IMAGE_JPEG);
        File tempFile = null;
        try {
            // 创建临时文件
            tempFile = File.createTempFile("snap_", ".jpeg");
            java.nio.file.Files.write(tempFile.toPath(), imageBytes);
            // 使用 FileMultipartFile 转换
            MultipartFile multipartFile = new FileMultipartFile(tempFile, objectName, MimeTypeUtils.IMAGE_JPEG);
        R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
        if (sysFileR.getCode() == R.SUCCESS) {
            url = sysFileR.getData().getUrl();
            log.debug("上传文件成功:{}", url);
        }
        } catch (Exception e) {
            log.error("上传失败", e);
        } finally {
            if (tempFile != null && tempFile.exists()) {
                tempFile.delete();
            }
        }
        return url;
    }
    //短时录像
    @Override
    public String record(CameraCmd cmd) {
        String url = "";
        File tempFile = null;
        try {
            String cameraId = cmd.getCameraId();
            Integer chanNo = cmd.getChanNo();
@@ -1644,29 +1703,32 @@
            String path = FileUtils.createFile(tempDir + "/record/" + name + ".mp4");
            boolean enable = cmd.isEnable();
            Integer userId = cmd.getLoginId().intValue();
            //强制I帧结构体对象
            NET_DVR_I_FRAME netDvrIFrame = new NET_DVR_I_FRAME();   //新建结构体对象
            NET_DVR_I_FRAME netDvrIFrame = new NET_DVR_I_FRAME();
            netDvrIFrame.read();
            netDvrIFrame.dwChannel = chanNo;//因为上文代码中设置了通道号,按照上文中的设置
            netDvrIFrame.dwChannel = chanNo;
            netDvrIFrame.byStreamType = 0;
            netDvrIFrame.dwSize = netDvrIFrame.size();
            netDvrIFrame.write();
            if (!hCNetSDK.NET_DVR_RemoteControl(userId, 3402, netDvrIFrame.getPointer(), netDvrIFrame.dwSize)) {
                log.error("强制I帧 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            }
            //预览参数
            NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO();
            previewInfo.read();
            previewInfo.lChannel = chanNo;
            previewInfo.dwStreamType = 0;//码流类型:0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推
            previewInfo.dwLinkMode = 0;//连接方式:0-TCP方式,1-UDP方式,2-多播方式,3-RTP方式,4-RTP/RTSP,5-RTP/HTTP,6-HRUDP(可靠传输),7
            // -RTSP/HTTPS,8-NPQ
            previewInfo.hPlayWnd = null;//播放窗口的句柄,为NULL表示不解码显示。
            previewInfo.bBlocked = 0;//0- 非阻塞取流,1-阻塞取流
            previewInfo.byNPQMode = 0;//NPQ模式:0-直连模式,1-过流媒体模式
            previewInfo.dwStreamType = 0;
            previewInfo.dwLinkMode = 0;
            previewInfo.hPlayWnd = null;
            previewInfo.bBlocked = 0;
            previewInfo.byNPQMode = 0;
            previewInfo.write();
            String url = "";
            if (enable) {
                // 开始录像
                if (!GlobalVariable.previewMap.containsKey(name)) {
                    int lRealHandle = hCNetSDK.NET_DVR_RealPlay_V40(userId, previewInfo, null, null);
                    if (lRealHandle == -1) {
@@ -1676,6 +1738,7 @@
                    log.debug("取流成功");
                    GlobalVariable.previewMap.put(name, lRealHandle);
                }
                if (!hCNetSDK.NET_DVR_SaveRealData_V30((int) GlobalVariable.previewMap.get(name), 2, path)) {
                    log.error("保存视频文件到临时文件夹失败 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
                    return "";
@@ -1683,24 +1746,40 @@
                log.debug("录像开始");
            } else {
                // 停止录像
                if (GlobalVariable.previewMap.containsKey(name)) {
                    Integer lRealHandle = (Integer) GlobalVariable.previewMap.get(name);
                    hCNetSDK.NET_DVR_StopRealPlay(lRealHandle);
                    GlobalVariable.previewMap.remove(name);
                }
                log.debug("录像停止");
                //存入minio
                // 检查录像文件是否存在
                File videoFile = new File(path);
                if (!videoFile.exists() || videoFile.length() == 0) {
                    log.error("录像文件不存在或为空: {}", path);
                    return "";
                }
                // 存入minio - 使用 FileMultipartFile
                String bucketName = "record";
                String objectName = cameraId + "_" + chanNo;
                MultipartFile multipartFile = MultipartFileUtil.filePathToMultipartFile(path, objectName,
                        MimeTypeUtils.VIDEO_MP4);
                String objectName = cameraId + "_" + chanNo + ".mp4";
                // 创建临时文件对象(实际上 path 已经是文件路径)
                tempFile = new File(path);
                // 使用 FileMultipartFile 转换
                MultipartFile multipartFile = new FileMultipartFile(tempFile, objectName, MimeTypeUtils.VIDEO_MP4);
                R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName);
                if (sysFileR.getCode() == R.SUCCESS) {
                    url = sysFileR.getData().getUrl();
                    log.debug("上传文件成功:{}", url);
                }
            }
            return url;
        } catch (Exception ex) {
            log.error("录像异常:{}", ex.getMessage());
            return "";
@@ -1763,69 +1842,81 @@
            return false;
        }
    }
    @Override
    public String recordStop(CameraCmd cmd) {
        String url = "";
        File videoFile = null;
        File transcodeFile = null;
        try {
            String cameraId = cmd.getCameraId();
            Integer chanNo = cmd.getChanNo();
            String name = cameraId + "_" + chanNo;
            // 本地临时录像地址
            String path = FileUtils.createFile(tempDir + "/record/" + name + ".mp4");
            Integer userId = cmd.getLoginId().intValue();
            //region 强制I帧
            NET_DVR_I_FRAME netDvrIFrame = new NET_DVR_I_FRAME();   //新建结构体对象
            netDvrIFrame.read();
            netDvrIFrame.dwChannel = chanNo;//因为上文代码中设置了通道号,按照上文中的设置
            netDvrIFrame.byStreamType = 0;
            netDvrIFrame.dwSize = netDvrIFrame.size();
            netDvrIFrame.write();
            if (!hCNetSDK.NET_DVR_RemoteControl(userId, 3402, netDvrIFrame.getPointer(), netDvrIFrame.dwSize)) {
                log.error("强制I帧 错误码为:{}", hCNetSDK.NET_DVR_GetLastError());
            }
            //endregion
            //region 预览参数
            NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO();
            previewInfo.read();
            previewInfo.lChannel = chanNo;
            previewInfo.dwStreamType = 0;//码流类型:0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推
            previewInfo.dwLinkMode = 0;//连接方式:0-TCP方式,1-UDP方式,2-多播方式,3-RTP方式,4-RTP/RTSP,5-RTP/HTTP,6-HRUDP(可靠传输),7
            // -RTSP/HTTPS,8-NPQ
            previewInfo.hPlayWnd = null;//播放窗口的句柄,为NULL表示不解码显示。
            previewInfo.bBlocked = 0;//0- 非阻塞取流,1-阻塞取流
            previewInfo.byNPQMode = 0;//NPQ模式:0-直连模式,1-过流媒体模式
            previewInfo.write();
            //endregion
            // 停止录像
            if (GlobalVariable.previewMap.containsKey(name)) {
                Integer lRealHandle = (Integer) GlobalVariable.previewMap.get(name);
                hCNetSDK.NET_DVR_StopRealPlay(lRealHandle);
                GlobalVariable.previewMap.remove(name);
            }
            log.debug("录像停止");
            // 检查原录像文件是否存在
            videoFile = new File(path);
            if (!videoFile.exists() || videoFile.length() == 0) {
                log.error("录像文件不存在或为空: {}", path);
                return "";
            }
            //ffmpeg转码
            StopWatch watchStop = new StopWatch();
            watchStop.start();
            String newpath = FileUtils.createFile(tempDir + "/record/" + name + "_c.mp4");
            transcodeFile = new File(newpath);
            FFmpegUtils.transcodeToMP4(path, newpath);
            watchStop.stop();
            log.warn("转码耗时:{}", watchStop.getTotalTimeMillis());
            //存入minio
            // 检查转码后的文件是否存在
            if (!transcodeFile.exists() || transcodeFile.length() == 0) {
                log.error("转码文件不存在或为空: {}", newpath);
                return "";
            }
            // 存入minio - 使用 FileMultipartFile
            String bucketName = "record";
            String objectName = cameraId + "_" + chanNo;
            String objectName = cameraId + "_" + chanNo + ".mp4";
            String type = cmd.getType();
            MultipartFile multipartFile = MultipartFileUtil.filePathToMultipartFile(newpath, objectName,
                    MimeTypeUtils.VIDEO_MP4);
            MultipartFile multipartFile = new FileMultipartFile(transcodeFile, objectName, MimeTypeUtils.VIDEO_MP4);
            R<SysFile> sysFileR = remoteFileService.upload(multipartFile, bucketName, type);
            if (sysFileR.getCode() == R.SUCCESS) {
                url = sysFileR.getData().getUrl();
                log.info("上传文件成功:{}", url);
            }
            return url;
        } catch (Exception ex) {
            log.error("录像异常:{}", ex.getMessage());
            return "";
        } finally {
            // 清理临时文件
            try {
                if (videoFile != null && videoFile.exists()) {
                    videoFile.delete();
                    log.debug("删除原录像文件: {}", videoFile.getPath());
                }
                if (transcodeFile != null && transcodeFile.exists()) {
                    transcodeFile.delete();
                    log.debug("删除转码文件: {}", transcodeFile.getPath());
                }
            } catch (Exception e) {
                log.warn("删除临时文件失败", e);
            }
        }
    }