• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 海康威視SDK基于JAVA二次開發

    標簽: java  

    項目背景

    項目中用到了海康威視的攝像機視頻服務器。項目要求,要將海康威視的攝像視頻同步按時間至我們自己的服務器,并且在項目中記錄文件信息。

    項目環境

    • SpringBoot + JDK1.8
    • 海康威視設備型號:DS-7608N-K2/8P

    引入SDK

    首先在海康衛視下載對應的SDK包。

    官網下載地址:https://www.hikvision.com/cn/download_61.html

    解壓下載的SDK文件,如下圖:
    在這里插入圖片描述

    • Demo示例:其中有java的一個demo,大家也可以參考一下
    • 開發文檔:包含了海康威視不同設備的SDK對接文檔
    • 庫文件:需要部分dll文件引用,通過dll文件去調用海康威視的服務器,需要的文件下文詳細介紹

    打開我們的SpringBoot項目

    • 首先將demo示例中java例子的HCNetSDK.java文件拷貝至我們項目,項目位置沒有特殊要求。

    • 然后將庫文件中的HCNetSDKCom文件夾及下面的所有文件、AudioRender.dll、HCCore.dll、HCNetSDK.dll、PlayCtrl.dll、SuperRender.dll文件放置指定的文件夾
      在這里插入圖片描述

    • 修改HCNetSDK.java文件的HCNetSDK加載dll文件的位置。如下:D:\HKSDK\HCNetSDK為dll文件存放的目錄。

     HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("D:\\HKSDK\\HCNetSDK", HCNetSDK.class);
    
    • pom文件引入SDK二次開發所需要的jar包。jar包均手動引入,不是通過maven網上下載。官方示例中的javaDemo中有相應的jar包。
    <dependency>
        <groupId>net.java.jna</groupId>
        <artifactId>jna</artifactId>
        <version>1.0.0</version>
    </dependency>
    <dependency>
        <groupId>net.java.jna</groupId>
        <artifactId>examples</artifactId>
        <version>1.0.0</version>
    </dependency>
    

    編寫程序代碼

    import com.sun.jna.NativeLong;
    import com.sun.jna.ptr.IntByReference;
    import com.urgentlogistic.util.HCNetSDK;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.scheduling.annotation.Scheduled;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Timer;
    
    /**
     * @author ZhangFZ
     * @date 2020/4/20 20:23
     **/
    public class VideoDowload {
    
        private static Logger logger = LoggerFactory.getLogger(VideoDowload.class);
        private static HCNetSDK hcNetSDK = HCNetSDK.INSTANCE;
        private NativeLong userId;//用戶句柄
        private NativeLong loadHandle;//下載句柄
        private Timer downloadTimer;
    
        /**
         * 按時間下載視頻
         */
        private boolean downloadVideo(Dvr dvr, Date startTime, Date endTime, String filePath, int channel) {
            boolean initFlag = hcNetSDK.NET_DVR_Init();
            if (!initFlag) { //返回值為布爾值 fasle初始化失敗
                logger.warn("hksdk(視頻)-海康sdk初始化失敗!");
                return false;
            }
            HCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30();
            userId = hcNetSDK.NET_DVR_Login_V30(dvr.getDvrip(), (short) dvr.getDvrport(), dvr.getDvrusername(), dvr.getDvrpassword(), deviceInfo);
            logger.info("hksdk(視頻)-登錄海康錄像機信息,狀態值:" + hcNetSDK.NET_DVR_GetLastError());
            long lUserId = userId.longValue();
            if (lUserId == -1) {
                logger.warn("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);
                logger.info("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;
                            logger.info("hksdk(視頻)-視頻下載進度:" + "==" + produce + "%");
                        }
                        if (produce == 100) {//下載成功
                            hcNetSDK.NET_DVR_StopGetFile(loadHandle);
                            loadHandle.setValue(-1);
                            hcNetSDK.NET_DVR_Logout(userId);//退出錄像機
                            logger.info("hksdk(視頻)-退出狀態" + hcNetSDK.NET_DVR_GetLastError());
                            //hcNetSDK.NET_DVR_Cleanup();
                            return true;
                        }
                        if (produce > 100) {//下載失敗
                            hcNetSDK.NET_DVR_StopGetFile(loadHandle);
                            loadHandle.setValue(-1);
                            logger.warn("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("20200422170000");   //開始時間
                endTime = sdf.parse("20200422180000");      //結束時間
            } catch (ParseException e) {
                e.printStackTrace();
            }
            VideoDowload test = new VideoDowload();
            Dvr dvr = new Dvr("http://192.168.0.167",80,"admin","123456");
            int channel = 33;//通道
            System.out.print(test.downloadVideo(dvr, startTime, endTime, "D:\\testhk\\test.mp4", channel));
        }
    }
    

    Dvr類:

    import lombok.Data;
    
    /**
     * @author ZhangFZ
     * @date 2020/4/21 9:32
     **/
    @Data
    public class Dvr {
    
        // 視頻服務器ip地址
        private String dvrip;
        // 視頻服務器端口號
        private int dvrport;
        // 視頻服務器用戶名
        private String dvrusername;
        // 視頻服務器密碼
        private String dvrpassword;
    
        public Dvr(String dvrip, int dvrport, String dvrusername, String dvrpassword) {
            this.dvrip = dvrip;
            this.dvrport = dvrport;
            this.dvrusername = dvrusername;
            this.dvrpassword = dvrpassword;
        }
    }
    

    注意事項:

    • 自己的服務器信息請手動修改,保存目錄等信息請手動修改
    • channel通道號是32起,比如你的通道為1,這里需要寫33,原因咨詢海康威視的技術。
    • 這里只是一個簡單的demo,具體操作根據自己業務的實際情況來。

    下圖是定時5分鐘取一次數據的結果

    視頻無法播放的問題

    因為你的音頻不是mpeg4容器支持的音頻格式。通過mediainfo分析可知,你的音頻格式是pcm_alaw,并且還有一行警告信息:
    FileExtension_Invalid : mpeg mpg m2p vob vro pss evo
    你的視頻格式應該是MPEG-PS格式,但是后綴卻是mp4,其實是一個非法的MP4。mediainfo還會告訴你合法的后綴應該是上述那幾個。由于你的音頻格式根本不被瀏覽器支持,但是視頻格式是被瀏覽器支持的(H.264/AVC)。那么解決方案其實也簡單,抽調音頻即可。參考ffmpeg命令行(不轉碼,直接復制流):
    ffmpeg -i demo.mp4 -c copy -an demp_enc.mp4
    速度很快,使用-an參數屏蔽掉音頻流,將封裝格式轉為mp4。再次用mediainfo查看,格式已經顯示MPEG-4,是一個標準的mp4容器封裝了,在當今主流的瀏覽器都能直接播放。

    下載ffmpeg解碼工具官網下載ffmpeg,解壓安裝找到ffmpeg.exe。

    添加一下工具類

    // ffmpeg.exe存放的位置
    private static String ffmpegEXE = "D:\\software\\ffmpg\\bin\\ffmpeg.exe";
    
    //ffmpeg -i demo.mp4 -c copy -an demp_enc.mp4
    public static void convetor(String videoInputPath, String videoOutPath) throws Exception {
        List<String> command = new ArrayList<String>();
        command.add(ffmpegEXE);
        command.add("-i");
        command.add(videoInputPath);
        command.add("-c");
        command.add("copy");
        command.add("-an");
        command.add(videoOutPath);
        ProcessBuilder builder = new ProcessBuilder(command);
        Process process = null;
        try {
            process = builder.start();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 使用這種方式會在瞬間大量消耗CPU和內存等系統資源,所以這里我們需要對流進行處理
        InputStream errorStream = process.getErrorStream();
        InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
        BufferedReader br = new BufferedReader(inputStreamReader);
        String line = "";
        while ((line = br.readLine()) != null) {
        }
        if (br != null) {
            br.close();
        }
        if (inputStreamReader != null) {
            inputStreamReader.close();
        }
        if (errorStream != null) {
            errorStream.close();
        }
    
    }
    

    下載視頻成功后,調用該方法進行轉碼,修改VideoDowload.java代碼為以下:

    if (produce == 100) {//下載成功
        hcNetSDK.NET_DVR_StopGetFile(loadHandle);
        loadHandle.setValue(-1);
        hcNetSDK.NET_DVR_Logout(userId);//退出錄像機
        logger.info("hksdk(視頻)-退出狀態" + hcNetSDK.NET_DVR_GetLastError());
        hcNetSDK.NET_DVR_Cleanup();
    
        Media media = new Media();
        media.setDisasterId(disasterId);
        media.setType(MediaType.VIDEO);
        media.setCreateDate(new Date());
        media.setUploadTime(new Date());
        media.setAttachmentName("z" + fileName);
        media.setIsDeleted(false);
        media.setUpdate_rhtx(1);
        media.setFlag("OTHER");
        media.setNewsFeedId(0);
        String http_path = restUrlLocalhost;
        media.setUrl(http_path + "/urgentlogistic/file/mp4/" + newDate + "/z" +fileName);
        mediaService.create(media);
    
        try {
            // 視頻進行轉碼
            convetor(filePath + fileName,filePath + "z" +fileName);
            File file = new File(filePath + fileName);
            // 如果文件路徑所對應的文件存在,并且是一個文件,則直接刪除
            file.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        return true;
    }
    

    運行完就會多一個視頻文件,然后就可以用通用播放器播放這個新的視頻了,至于老的視頻任憑自己需求處理掉就行啦

    最后在附上官方開發人員的郵箱 [email protected]

    如果對您有幫助,請點個贊,關注下小編再走喲,有什么問題可以在評論區留言,經常在線,看到一定回復。

    本章到此結束。后續文章會陸續更新,文檔會同步在CSDN和GitHub保持同步更新。

    CSDN:https://blog.csdn.net/qq_34988304/category_8820134.html

    Github文檔:https://github.com/hack-feng/Java-Notes/tree/master/src/note/SpringCloud

    版權聲明:本文為qq_34988304原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_34988304/article/details/105842490

    智能推薦

    關于海康威視網絡攝像機二次開發問題

    最近一個星期一直在研究海康威視的網絡攝像機二次開發問題,糾結了很久。今天終于走通了一部分,記下來希望能夠幫助到更多人。現在是用的  VS2013 + opencv 進行測試。 首先推薦兩篇對我幫助很大的博文:                http://blog.csdn.net/wanghuiqi2008/...

    海康威視攝像頭web二次開發(angular)

    海康威視官網有提供二次開發的web開發包,主要由js,html組成,但是開發包更新迭代太快了,甲方的攝像頭還是老版本,幸好甲方提供了它們的開發包(內涵demo,doc)。 測試環境 在web包里面里面有demo>cn文件夾,內含js、html、css文件,直接點擊html文件,在瀏覽器中打開,可以直接使用,輸入ip,port,username,password,點擊登錄,點擊預覽,可以查看對...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    猜你喜歡

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    styled-components —— React 中的 CSS 最佳實踐

    https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...

    精品国产乱码久久久久久蜜桃不卡