Bootstrap

海康威视SDK二次开发JAVA(SpringBoot)

功能背景:

项目里面使用到海康威视摄像头录播课堂,之后取出视频分享。

推荐内容

项目环境:

SpringBoot、JDK1.8、IntelliJ IDEA 2020.1、SDK V6.1、FFMPEG、康威视摄像头、录像机。

详细步骤:
一、引入SDK (海康威视 [官网入口] ),下载对应自己服务器版本的SDK,我选择的是《设备网络SDK_Win64 V6.1.6.45_build20210302》。
在这里插入图片描述
二、下载SDK解压后,如图:
在这里插入图片描述

  • Demo示例 :里面有个java Demo可以提供参考。
  • 开发文档:设备网络SDK使用手册.chm这个文档帮助大,其它我看不习惯。
  • 库文件:dll文件引用。
  • 头文件:非java使用观看的文件。
  • HCNetSDKCom文件夹必须加载到工程.txt :说明文件。

三、库文件准备,打开SDK解压文件夹找到【库文件】里的HCNetSDK.dll、HCCore.dll、PlayCtrl.dll、SuperRender.dll、AudioRender.dll、HCNetSDKCom文件夹、ssleay32.dll、libeay32.dll、hlog.dll、hpr.dll、zlib1.dll、log4cxx.properties等文件均要加载到程序里面,【HCNetSDKCom文件夹】(包含里面的功能组件dll库文件)需要和HCNetSDK.dll、HCCore.dll一起加载,放在同一个目录下,且HCNetSDKCom文件夹名不能修改。拷贝到D盘自己创建的文件夹内。

四、新创建一个SpringBoot项目,pom文件引入SDK二次开发所需要的jar包,手动引入可以拷贝下载的SDK JAVA Demo里面有。

  • 手动引入方式拷贝CH-HCNetSDKV6.1.6.45_build20210302_win64\Demo示例\4- Java 开发示例\1-ClientDemo\ClientDemo-NetBeansPro 下的 jna.jar、examples.jar
  • maven
<!--海康威视-->
 <dependency>
     <groupId>net.java.jna</groupId>
     <artifactId>jna</artifactId>
     <version>1.0.0</version>
 </dependency>
 <dependency>
     <groupId>net.java.examples</groupId>
     <artifactId>examples</artifactId>
     <version>1.0.0</version>
 </dependency>

五、找到下载JAVA Demo中的HCNetSDK.java文件,拷贝到自己新建的项目里面,修改dll引入路径。

HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("D:\\Hk\\HCNetSDK.dll",HCNetSDK.class);

六、创建类 DVRLogin.java

import lombok.Data;

/**
 * 登录录像机对象
 */
@Data
public class DVRLogin {
    /**
     * 视频服务器ip地址
     */
    private String ip;
    /**
     * 视频服务器端口号
     */
    private int report;
    /**
     * 视频服务器用户名
     */
    private String userName;
    /**
     * 视频服务器密码
     */
    private String password;

    public DVRLogin() {
    }

    public DVRLogin(String ip, int report, String userName, String password) {
        this.ip = ip;
        this.report = report;
        this.userName = userName;
        this.password = password;
    }
}

七、创建测试类 VideoDowload.java

import com.sun.jna.NativeLong;
import com.sun.jna.ptr.IntByReference;

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import static com.xzl.hk.FfmpegUtil.executeCodecs;

public class VideoDowload {

    private static HCNetSDK hcNetSDK = HCNetSDK.INSTANCE;
    private NativeLong userId;//用户句柄
    private NativeLong loadHandle;//下载句柄

    /**
     * 按时间下载视频
     */
    private boolean downloadVideo(DVRLogin DVRLogin, Date startTime, Date endTime, String filePath, int channel) {
        boolean initFlag = hcNetSDK.NET_DVR_Init();
        if (!initFlag) { //返回值为布尔值 fasle初始化失败
            System.err.println("hksdk(视频)-海康sdk初始化失败!");
            return false;
        }
        HCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30();
        userId = hcNetSDK.NET_DVR_Login_V30(DVRLogin.getIp(), (short) DVRLogin.getReport(), DVRLogin.getUserName(), DVRLogin.getPassword(), deviceInfo);
        System.out.println("hksdk(视频)-登录海康录像机信息,状态值:" + hcNetSDK.NET_DVR_GetLastError());
        long lUserId = userId.longValue();
        if (lUserId == -1) {
             System.err.println("hksdk(视频)-海康sdk登录失败!");
            return false;
        }
        loadHandle = new NativeLong(-1);
        if (loadHandle.intValue() == -1) {
            loadHandle = hcNetSDK.NET_DVR_GetFileByTime(userId, new NativeLong(channel), getHkTime(startTime), getHkTime(endTime), filePath);
            System.out.println("hksdk(视频)-获取播放句柄信息,状态值:" + hcNetSDK.NET_DVR_GetLastError());
            if (loadHandle.intValue() >= 0) {
                // 判断文件夹是否存在
                File files = new File(filePath);
                if (!files.exists()) {
                    files.mkdirs();
                }
                boolean downloadFlag = hcNetSDK.NET_DVR_PlayBackControl(loadHandle, hcNetSDK.NET_DVR_PLAYSTART, 0, null);
                int tmp = -1;
                IntByReference pos = new IntByReference();
                while (true) {
                    boolean backFlag = hcNetSDK.NET_DVR_PlayBackControl(loadHandle, hcNetSDK.NET_DVR_PLAYGETPOS, 0, pos);
                    if (!backFlag) {//防止单个线程死循环
                        return downloadFlag;
                    }
                    int produce = pos.getValue();
                    if ((produce % 10) == 0 && tmp != produce) {//输出进度
                        tmp = produce;
                        System.out.println("hksdk(视频)-视频下载进度:" + "==" + produce + "%");
                    }
                    if (produce == 100) {//下载成功
                        hcNetSDK.NET_DVR_StopGetFile(loadHandle);
                        loadHandle.setValue(-1);
                        hcNetSDK.NET_DVR_Logout(userId);//退出录像机
                        System.out.println("hksdk(视频)-退出状态" + hcNetSDK.NET_DVR_GetLastError());
                        hcNetSDK.NET_DVR_Cleanup();

                        try {
                            // 视频进行转码
                            executeCodecs();
                            File file = new File(filePath);
                            // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
                            file.delete();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return true;
                    }
                    if (produce > 100) {//下载失败
                        hcNetSDK.NET_DVR_StopGetFile(loadHandle);
                        loadHandle.setValue(-1);
                         System.err.println("hksdk(视频)-海康sdk由于网络原因或DVR忙,下载异常终止!错误原因:" + hcNetSDK.NET_DVR_GetLastError());
                        hcNetSDK.NET_DVR_Logout(userId);//退出录像机
                        logger.info("hksdk(视频)-退出状态"+hcNetSDK.NET_DVR_GetLastError());
                        return false;
                    }
                }
            } else {
                System.out.println("hksdk(视频)-下载失败" + hcNetSDK.NET_DVR_GetLastError());
                return false;
            }
        }
        return false;
    }

    /**
     * 获取海康录像机格式的时间
     */
    private HCNetSDK.NET_DVR_TIME getHkTime(Date time) {
        HCNetSDK.NET_DVR_TIME structTime = new HCNetSDK.NET_DVR_TIME();
        String str = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(time);
        String[] times = str.split("-");
        structTime.dwYear = Integer.parseInt(times[0]);
        structTime.dwMonth = Integer.parseInt(times[1]);
        structTime.dwDay = Integer.parseInt(times[2]);
        structTime.dwHour = Integer.parseInt(times[3]);
        structTime.dwMinute = Integer.parseInt(times[4]);
        structTime.dwSecond = Integer.parseInt(times[5]);
        return structTime;
    }

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        Date startTime = null;
        Date endTime = null;
        try {
            startTime = sdf.parse("20211101113000");   //开始时间
            endTime = sdf.parse("20211101113035");      //结束时间
        } catch (ParseException e) {
            e.printStackTrace();
        }
        VideoDowload test = new VideoDowload();
        DVRLogin DVRLogin = new DVRLogin("192.168.1.29", 8000, "admin", "abc123456");
        int channel = 33;//通道
        System.out.print(test.downloadVideo(DVRLogin, startTime, endTime, "D:\\testhk\\test_other.mp4", channel));
        try {
            executeCodecs();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

八、创建两个工具类FfmpegUtil.java、StreamGobbler.java。

FfmpegUtil.java,解决Jna抓取的视频文件不能播放问题,视频格式是MPEG-PS,需要转文件。

import java.io.IOException;
public class FfmpegUtil {
    // ffmpeg.exe存放的位置
    private static String ffmpegEXE = "D:\\tools\\ffmpeg\\bin\\ffmpeg.exe";

    public static void executeCodecs() {
        // 创建一个List集合来保存转换视频文件为flv格式的命令
        // 拼接命令
        String command = ffmpegEXE + " -i D:\\testhk\\test_other.mp4 -y -vol 1800 -ar 44100 -r 29.97 -s 1920x1080 D:\\testhk\\demp_other_encan1.mp4";
        // 执行命令行
        try {
            Process process = Runtime.getRuntime().exec(command);

            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR");
            StreamGobbler outGobbler = new StreamGobbler(process.getInputStream(), "STDOUT");
            // kick off stderr
            errorGobbler.start();
            outGobbler.start();

            try {
                int i = process.waitFor();
                if (i == 0) {
                    System.out.println("进程完成.");
                } else {
                    System.out.println("进程失败.");
                }
            } catch (InterruptedException e) {
                System.out.println("进程失败!");
                e.printStackTrace();
            }

            int i = process.exitValue();  //接收执行完毕的返回值
            if (i == 0) {
                System.out.println("执行完成.");
            } else {
                System.out.println("执行失败.");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            executeCodecs();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

StreamGobbler.java 解决process.waitFor();阻塞进程。

import lombok.SneakyThrows;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
/**
 * 用于处理Runtime.getRuntime().exec产生的错误流及输出流
 */
public class StreamGobbler extends Thread {
    InputStream is;
    String type;
    OutputStream os;
    StreamGobbler(InputStream is, String type) {
        this(is, type, null);
    }

    StreamGobbler(InputStream is, String type, OutputStream redirect) {
        this.is = is;
        this.type = type;
        this.os = redirect;
    }

    @SneakyThrows
    public void run() {
        InputStreamReader isr = null;
        BufferedReader br = null;
        PrintWriter pw = null;
        try {
            if (os != null)
                pw = new PrintWriter(os);

            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null) {
                if (pw != null)
                    pw.println(line);
                System.out.println(type + ">" + line);
            }

            if (pw != null)
                pw.flush();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            if (pw != null) {
                pw.close();
            }
            if (br != null) {
                br.close();
            }
            if (isr != null) {
                isr.close();
            }
        }
    }
}

注意:上面内容中使用到 ffmpeg 安装教程。

;