欢迎进入jdb电子官方网站!

咨询热线:400-156-4567

web实时预览功能开发 java 海康sdk nvr

发布时间:2024-01-27 21:04:51

1.实时视频流解决方案

目录

1.实时视频流解决方案

2.步骤

1.搭建rtmp+flv服务器

2.java预览demo

3.实时预览

1.配置海康sdk库文件

3.修改FPREVIEW_DATA_CB代码,取流

4.javacv的推流

3.部分代码

1.启动项目初始化cms,stream的代码

2.cms代码

3.stream代码



1.前端调用后台接口,
2.后台接口调用海康sdk开启具体的摄像头监听,获取摄像头实时流数据
3.建立管道,海康sdk监听线程和javacv推流线程共享管道
4.sdk线程向管道输出流写数据,javacv推流线程向rtmp发送管道数据。即sdk生产者,javacv是消费者
5.nginx+rtmp+flv模块接收到视频流,可以通过http的路径在浏览器访问

前端->后台->海康sdk—>管道->javacv->nginx+rtmp+flv->http路径

2.步骤

1.搭建rtmp+flv服务器

windows系统下,自己编译比较麻烦,这边有大佬编译好的windows中obs+nginx-http-flv-module的流媒体服务搭建_windows nginx集成http-flv_OneCodeSolvesAll的博客-CSDN博客个人编译好的:链接:https://pan.baidu.com/s/1XKkyYvK5W6yZA1w2mPaAfg
提取码:3ug6

linux环境下,下载nginx-http-flv-module(包含nginx-rtmp-module),添加到nginx重载配置就行,网上有很多教程。

如果不需要flv,也可以单独下载nginx-rtmp-module,添加到nginx重载配置。

2.java预览demo

1.登录海康平台海康开放平台

选择硬件产品的isup sdk下载

2.解压之后

 java的这个demo直接打开,修改一下nvr信息,包括ip端口就可以直接预览。

3.实时预览

1.配置海康sdk库文件

海康文档lib文件夹下的文件就是库文件,JavaISUPDemo\lib下也有,需要注意文档是win还是linux版本的。

每当NET_ECMS_StartGetRealStreamV11,开启预览时,FPREVIEW_NEWLINK_CB便会监听到预览请求。

创建管道,分别用于取流推流。异步开始调用javacv的推流方法。

PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream();
Stream.streamMap.put("stream:"+lLinkHandle,pos);
pos.connect(pis);
CompletableFuture.runAsync(()-> {
    try {
         PushUtil.grabAndPushRtmp(pis,"rtmp://"+rtmphost+"/live/"+userId+"_"+ dwChannelNo);
    } catch (Exception e) {
         e.printStackTrace();
    }
}, Executors.newFixedThreadPool(1));

3.修改FPREVIEW_DATA_CB代码,取流

每当监听到视频流时,将视频流写入pos管道。注意此处不能使用redis,redis会严重影响此处代码性能,导致取流失败。每监听一次,是一个新的线程。第一块注释的代码是调试时使用,调试完之后要删掉。第二块注释代码pos也可以存到map中,使用时从map取,此处直接构造方法传进来也可以。

    public class FPREVIEW_DATA_CB implements HCISUPStream.PREVIEW_DATA_CB {

        private PipedOutputStream pos;
        public FPREVIEW_DATA_CB(PipedOutputStream pos) {
            this.pos = pos;
        }

        //实时流回调函数/
        @Override
        public void invoke(int iPreviewHandle, HCISUPStream.NET_EHOME_PREVIEW_CB_MSG pPreviewCBMsg, Pointer pUserData) throws Exception {
//            if (Count == 500) {//降低打印频率
//                log.info("FPREVIEW_DATA_CB callback, iPreviewHandle:{}", iPreviewHandle);
//                log.info("FPREVIEW_DATA_CB callback, data length:" + pPreviewCBMsg.dwDataLen);
//                Count = 0;
//            }
//            Count++;

            long offset = 0;
            ByteBuffer buffers = pPreviewCBMsg.pRecvdata.getByteBuffer(offset, pPreviewCBMsg.dwDataLen);
            byte[] bytes = new byte[pPreviewCBMsg.dwDataLen];
            buffers.rewind();
            buffers.get(bytes);

            try{
                //2.将数据读入到内存空间,此处不可用redis,否则执行时间是当前10倍
//                PipedOutputStream pos = Stream.streamMap.get("stream:"+iPreviewHandle);
                pos.write(bytes);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

4.javacv的推流

关于2.提到的javacv推流,linux和windows环境所需要的依赖包不同。

以下,test的是本地windows环境使用的,runtime是线上使用的,可不写,因为默认就是runtime。



    org.bytedeco
    javacpp
    1.5.7


    org.bytedeco
    opencv
    4.5.5-1.5.7
    linux-x86_64
    runtime


    org.bytedeco
    openblas
    0.3.19-1.5.7
    linux-x86_64
    runtime


    org.bytedeco
    ffmpeg
    5.0-1.5.7
    linux-x86_64
    runtime



    org.bytedeco
    opencv
    4.5.5-1.5.7
    windows-x86_64
    test


    org.bytedeco
    openblas
    0.3.19-1.5.7
    windows-x86_64
    test


    org.bytedeco
    ffmpeg
    5.0-1.5.7
    windows-x86_64
    test


    org.bytedeco
    javacv
    1.5.7
    
        
            org.bytedeco.javacpp-presets
            *
        
    

推流代码原理就是,javacv抓取帧,根据帧率算出间隔时间推送到rtmp服务器。

package com.ei.ambulance.util.video;

import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avformat.AVStream;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.FFmpegLogCallback;
import org.bytedeco.javacv.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.PipedInputStream;

/**
 * @author willzhao
 * @version 1.0
 * @description 读取指定的mp4文件,推送到SRS服务器
 * @date 2021/11/19 8:49
 */
public class PushUtil {
    private static final Logger log = LoggerFactory.getLogger(PushUtil.class);

    /**
     * 推送到SRS服务器
     *
     * @param pis
     * @param pushAddress 推流地址
     * @throws Exception
     */
    public static void grabAndPushRtmp(PipedInputStream pis, String pushAddress) throws Exception {
        Thread.sleep(500);
        // ffmepg日志级别
        avutil.av_log_set_level(avutil.AV_LOG_ERROR);
        FFmpegLogCallback.set();
        // 实例化帧抓取器对象,将文件路径传入
        // 1.直接以文件形式实例化帧抓取器,此方式可以推送h264,ps码流格式的MP4
//        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(MP4_FILE_PATH);
        // 2.从文件中取流实例化帧抓取器,此方式只能推送ps码流的MP4
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(pis,0);

        long startTime = System.currentTimeMillis();

        log.info("开始初始化帧抓取器");
        // 下面两行设置可加可不加
        grabber.setOption("analyzeduration", "1000000");
//        grabber.setFormat("h264");
        // 初始化帧抓取器,例如数据结构(时间戳、编码器上下文、帧对象等),
        // 如果入参等于true,还会调用avformat_find_stream_info方法获取流的信息,放入AVFormatContext类型的成员变量oc中
        grabber.start(true);
//        grabber.startUnsafe(true); // 也可以使用此方法
        log.info("帧抓取器初始化完成,耗时[{}]毫秒", System.currentTimeMillis()-startTime);

        // grabber.start方法中,初始化的解码器信息存在放在grabber的成员变量oc中
        AVFormatContext avFormatContext = grabber.getFormatContext();

        // 文件内有几个媒体流(一般是视频流+音频流)
        int streamNum = avFormatContext.nb_streams();

        // 没有媒体流就不用继续了
        if (streamNum<1) {
            log.error("文件内不存在媒体流");
            return;
        }

        // 取得视频的帧率
        int frameRate = (int)grabber.getVideoFrameRate();

        log.info("视频帧率[{}],视频时长[{}]秒,媒体流数量[{}]",
                frameRate,
                avFormatContext.duration()/1000000,
                avFormatContext.nb_streams());

        // 遍历每一个流,检查其类型
        for (int i=0; i< streamNum; i++) {
            AVStream avStream = avFormatContext.streams(i);
            AVCodecParameters avCodecParameters = avStream.codecpar();
            log.info("流的索引[{}],编码器类型[{}],编码器ID[{}]", i, avCodecParameters.codec_type(), avCodecParameters.codec_id());
        }

        // 视频宽度
        int frameWidth = grabber.getImageWidth();
        // 视频高度
        int frameHeight = grabber.getImageHeight();
        // 音频通道数量
        int audioChannels = grabber.getAudioChannels();

        log.info("视频宽度[{}],视频高度[{}],音频通道数[{}]",
                frameWidth,
                frameHeight,
                audioChannels);

        // 实例化FFmpegFrameRecorder,将SRS的推送地址传入
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(pushAddress,
                frameWidth,
                frameHeight,
                audioChannels);

        // 设置编码格式
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);

        // 设置封装格式
        recorder.setFormat("flv");

        // 一秒内的帧数
        recorder.setFrameRate(frameRate);

        // 两个关键帧之间的帧数
        recorder.setGopSize(frameRate);

        // 设置音频通道数,与视频源的通道数相等
        recorder.setAudioChannels(grabber.getAudioChannels());

        startTime = System.currentTimeMillis();
        log.info("开始初始化帧抓取器");

        // 初始化帧录制器,例如数据结构(音频流、视频流指针,编码器),
        // 调用av_guess_format方法,确定视频输出时的封装方式,
        // 媒体上下文对象的内存分配,
        // 编码器的各项参数设置
        recorder.start();

        log.info("帧录制初始化完成,耗时[{}]毫秒", System.currentTimeMillis()-startTime);

        Frame frame;

        startTime = System.currentTimeMillis();

        log.info("开始推流");

        long videoTS = 0;

        int videoFrameNum = 0;
        int audioFrameNum = 0;
        int dataFrameNum = 0;

        // 假设一秒钟15帧,那么两帧间隔就是(1000/15)毫秒
        int interVal = 1000/frameRate;
        // 发送完一帧后sleep的时间,不能完全等于(1000/frameRate),不然会卡顿,
        // 要更小一些,这里取八分之一
        interVal/=8;

        // 持续从视频源取帧
        while (null!=(frame=grabber.grab())) {
            videoTS = 1000 * (System.currentTimeMillis() - startTime);

            // 时间戳
            recorder.setTimestamp(videoTS);

            // 有图像,就把视频帧加一
            if (null!=frame.image) {
                videoFrameNum++;
            }

            // 有声音,就把音频帧加一
            if (null!=frame.samples) {
                audioFrameNum++;
            }

            // 有数据,就把数据帧加一
            if (null!=frame.data) {
                dataFrameNum++;
            }

            // 取出的每一帧,都推送到SRS
            recorder.record(frame);

            // 停顿一下再推送
            Thread.sleep(interVal);
        }

        log.info("推送完成,视频帧[{}],音频帧[{}],数据帧[{}],耗时[{}]秒",
                videoFrameNum,
                audioFrameNum,
                dataFrameNum,
                (System.currentTimeMillis()-startTime)/1000);

        // 关闭帧录制器
        recorder.close();
        // 关闭帧抓取器
        grabber.stop();
        grabber.close();
        pis.close();
    }

}

其中,Thread.sleep(500);是必要的。要保证此线程执行到后面代码时,管道中必须存在流,这样开始初始化帧抓取器才能成功。即取流线程必须先于推流线程执行。否则会报错。

3.部分代码

代码中涉及到部分业务代码,按需修改。

1.启动项目初始化cms,stream的代码

package com.ei.ambulance;

import com.ei.ambulance.mapper.BreastpieceMapper;
import com.ei.ambulance.mapper.BreastpieceVideoMapper;
import com.ei.ambulance.mapper.TaskDetailMapper;
import com.ei.ambulance.util.RedisUtil;
import com.ei.ambulance.util.cacheMap.CacheMap;
import com.ei.ambulance.util.isup.Cms;
import com.ei.ambulance.util.isup.Stream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author zhangyd
 * @date 2022/9/20
 */
@Component
@Slf4j
@Order(1)
public class ISUPIniter implements CommandLineRunner {

    @Value("${hik.cmsServerIP}")
    String cmsServerIp;

    @Value("${hik.cmsServerPort}")
    String cmsServerPort;

    @Value("${hik.voiceSmsServerIP}")
    String voiceSmsServerIp;

    @Value("${hik.voiceSmsServerPort}")
    String voiceSmsServerPort;

    @Value("${hik.breastpieceVideoPath}")
    String breastpieceVideoPath;

    @Value("${rtmp.rtmphost}")
    private String rtmphost;

    @Value("${hik.smsServerIP}")
    String smsServerIP;

    @Value("${hik.smsServerPort}")
    String smsServerPort;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    BreastpieceMapper breastpieceMapper;

    @Autowired
    TaskDetailMapper taskDetailMapper;

    @Autowired
    BreastpieceVideoMapper breastpieceVideoMapper;

    @Override
    public void run(String... args) throws Exception {
        Cms cms = new Cms();
        cms.redisUtil = redisUtil;
        cms.breastpieceMapper = breastpieceMapper;
        cms.taskDetailMapper = taskDetailMapper;
        cms.breastpieceVideoMapper = breastpieceVideoMapper;
        cms.cMS_Init();
        cms.startCmsListen(cmsServerIp, cmsServerPort, breastpieceVideoPath);

        Stream stream = new Stream();
        stream.eStream_Init();
        stream.startRealPlayListen(smsServerIP, smsServerPort);
        Stream.redisUtil = redisUtil;
        Stream.rtmphost = rtmphost;
        CacheMap.put("stream", stream);
        CacheMap.put("cms", cms);
        redisUtil.deletePattern("video:*");
    }
}

2.cms代码

package com.ei.ambulance.util.isup;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ei.ambulance.mapper.BreastpieceMapper;
import com.ei.ambulance.mapper.BreastpieceVideoMapper;
import com.ei.ambulance.mapper.TaskDetailMapper;
import com.ei.ambulance.model.Breastpiece;
import com.ei.ambulance.model.BreastpieceVideo;
import com.ei.ambulance.model.ambulanceManage.TaskDetail;
import com.ei.ambulance.util.RedisUtil;
import com.ei.ambulance.util.Xml2JsonUtil;
import com.ei.ambulance.util.cacheMap.CacheMap;
import com.ei.ambulance.util.hik.OsSelect;
import com.ei.ambulance.vo.video.VideoFileInfoVo;
import com.ei.ambulance.vo.video.VideoFilesReq;
import com.sun.istack.internal.NotNull;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author zhangyd
 * @date 2022/9/20
 */
@Slf4j
public class Cms {

    public static Map userLoginMap  = new HashMap<>();

    public RedisUtil redisUtil;

    public BreastpieceMapper breastpieceMapper;

    public TaskDetailMapper taskDetailMapper;

    public BreastpieceVideoMapper breastpieceVideoMapper;

    public static HCISUPCMS hCEhomeCMS = null;
    //CMS监听句柄
    public static int CmsHandle = -1;
    //注册回调函数实现
    static FRegisterCallBack fRegisterCallBack;
    HCISUPCMS.NET_EHOME_CMS_LISTEN_PARAM struCMSListenPara = new HCISUPCMS.NET_EHOME_CMS_LISTEN_PARAM();


    /**
     * 根据不同操作系统选择不同的库文件和库路径
     *
     * @return
     */
    private static boolean createSDKInstance() {
        if (hCEhomeCMS == null) {
            synchronized (HCISUPCMS.class) {
                String strDllPath = "";
                try {
                    if (OsSelect.isWindows()) {
                        //win系统加载库路径(路径不要带中文)
                        strDllPath = System.getProperty("user.dir") + "\\lib\\isupsdk\\HCISUPCMS.dll";
                    } else if (OsSelect.isLinux()) {
                        //Linux系统加载库路径(路径不要带中文)
                        strDllPath = System.getProperty("user.dir") + "/lib/isupsdkLinux/libHCISUPCMS.so";
                        log.info("===============1{}", strDllPath);
//                        strDllPath = "/usr/soft/java-service/ambulance/lib/isupsdkLinux/libHCISUPCMS.so";
                    }
                    hCEhomeCMS = (HCISUPCMS) Native.loadLibrary(strDllPath, HCISUPCMS.class);
                } catch (Exception ex) {
                    log.info("loadLibrary: " + strDllPath + " Error: " + ex.getMessage());
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * cms服务初始化,开启监听
     *
     * @throws IOException
     */
    public void cMS_Init() throws IOException {

        if (hCEhomeCMS == null) {
            if (!createSDKInstance()) {
                log.info("Load CMS SDK fail");
                return;
            }
        }
        if (OsSelect.isWindows()) {
            HCISUPCMS.BYTE_ARRAY ptrByteArrayCrypto = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathCrypto = System.getProperty("user.dir") + "\\lib\\isupsdk\\libeay32.dll"; //Linux版本是libcrypto.so库文件的路径
            System.arraycopy(strPathCrypto.getBytes(), 0, ptrByteArrayCrypto.byValue, 0, strPathCrypto.length());
            ptrByteArrayCrypto.write();
            hCEhomeCMS.NET_ECMS_SetSDKInitCfg(0, ptrByteArrayCrypto.getPointer());

            //设置libssl.so所在路径
            HCISUPCMS.BYTE_ARRAY ptrByteArraySsl = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathSsl = System.getProperty("user.dir") + "\\lib\\isupsdk\\ssleay32.dll";    //Linux版本是libssl.so库文件的路径
            System.arraycopy(strPathSsl.getBytes(), 0, ptrByteArraySsl.byValue, 0, strPathSsl.length());
            ptrByteArraySsl.write();
            hCEhomeCMS.NET_ECMS_SetSDKInitCfg(1, ptrByteArraySsl.getPointer());
            //注册服务初始化
            boolean binit = hCEhomeCMS.NET_ECMS_Init();
            //设置HCAapSDKCom组件库文件夹所在路径
            HCISUPCMS.BYTE_ARRAY ptrByteArrayCom = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathCom = System.getProperty("user.dir") + "\\lib\\isupsdk\\HCAapSDKCom";        //只支持绝对路径,建议使用英文路径
            System.arraycopy(strPathCom.getBytes(), 0, ptrByteArrayCom.byValue, 0, strPathCom.length());
            ptrByteArrayCom.write();
            hCEhomeCMS.NET_ECMS_SetSDKLocalCfg(5, ptrByteArrayCom.getPointer());

        } else if (OsSelect.isLinux()) {
            //todo linux版本的sdk
            HCISUPCMS.BYTE_ARRAY ptrByteArrayCrypto = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathCrypto = System.getProperty("user.dir") + "/lib/isupsdkLinux/libcrypto.so"; //Linux版本是libcrypto.so库文件的路径
            log.info("===============2{}", strPathCrypto);
            System.arraycopy(strPathCrypto.getBytes(), 0, ptrByteArrayCrypto.byValue, 0, strPathCrypto.length());
            ptrByteArrayCrypto.write();
            hCEhomeCMS.NET_ECMS_SetSDKInitCfg(0, ptrByteArrayCrypto.getPointer());

            //设置libssl.so所在路径
            HCISUPCMS.BYTE_ARRAY ptrByteArraySsl = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathSsl = System.getProperty("user.dir") + "/lib/isupsdkLinux/libssl.so";    //Linux版本是libssl.so库文件的路径
            log.info("===============3{}", strPathSsl);
            System.arraycopy(strPathSsl.getBytes(), 0, ptrByteArraySsl.byValue, 0, strPathSsl.length());
            ptrByteArraySsl.write();
            hCEhomeCMS.NET_ECMS_SetSDKInitCfg(1, ptrByteArraySsl.getPointer());
            //注册服务初始化
            boolean binit = hCEhomeCMS.NET_ECMS_Init();
            //设置HCAapSDKCom组件库文件夹所在路径
            HCISUPCMS.BYTE_ARRAY ptrByteArrayCom = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathCom = System.getProperty("user.dir") + "/lib/isupsdkLinux/HCAapSDKCom/";        //只支持绝对路径,建议使用英文路径
            log.info("===============4{}", strPathCom);
            System.arraycopy(strPathCom.getBytes(), 0, ptrByteArrayCom.byValue, 0, strPathCom.length());
            ptrByteArrayCom.write();
            hCEhomeCMS.NET_ECMS_SetSDKLocalCfg(5, ptrByteArrayCom.getPointer());

        }
        hCEhomeCMS.NET_ECMS_SetLogToFile(3, System.getProperty("user.dir") + "/EHomeSDKLog", false);
    }

    public void startCmsListen(String cmsServerIp, String cmsServerPort, String breastpieceVideoPath) {
        if (fRegisterCallBack == null) {
            fRegisterCallBack = new FRegisterCallBack();
            fRegisterCallBack.ip = cmsServerIp;
            fRegisterCallBack.port = "8007";
            fRegisterCallBack.breastpieceVideoPath = breastpieceVideoPath;
        }
        System.arraycopy(cmsServerIp.getBytes(), 0, struCMSListenPara.struAddress.szIP, 0, cmsServerIp.length());
        struCMSListenPara.struAddress.wPort = Short.parseShort(cmsServerPort);
        struCMSListenPara.fnCB = fRegisterCallBack;
        struCMSListenPara.write();
        //启动监听,接收设备注册信息
        CmsHandle = hCEhomeCMS.NET_ECMS_StartListen(struCMSListenPara);
        if (CmsHandle < -1) {
            log.info("NET_ECMS_StartListen failed, error code:" + hCEhomeCMS.NET_ECMS_GetLastError());
            hCEhomeCMS.NET_ECMS_Fini();
            return;
        }
        String cmsListenInfo = new String(struCMSListenPara.struAddress.szIP).trim() + "_" + struCMSListenPara.struAddress.wPort;
        log.info("注册服务器:" + cmsListenInfo + ",NET_ECMS_StartListen succeed!\n");
    }

    public boolean setHeart(int lUserID, long dwKeepAliveSec, long dwTimeOutCount) {
        boolean result = hCEhomeCMS.NET_ECMS_SetAliveTimeout(lUserID, dwKeepAliveSec, dwTimeOutCount);
        if (result) {
            log.info("device NET_ECMS_SetAliveTimeout success");
        } else {
            log.info("device NET_ECMS_SetAliveTimeout failed, error code: {}", hCEhomeCMS.NET_ECMS_GetLastError());
        }
        return result;
    }

    public String channels(int channel, int lUserID) throws DocumentException {
        HCISUPCMS.NET_EHOME_PTXML_PARAM m_struParam = new HCISUPCMS.NET_EHOME_PTXML_PARAM();
        m_struParam.read();
        String url = "GET /ISAPI/ContentMgmt/InputProxy/channels";
        HCISUPCMS.BYTE_ARRAY ptrUrl = new HCISUPCMS.BYTE_ARRAY(1024);
        System.arraycopy(url.getBytes(), 0, ptrUrl.byValue, 0, url.length());
        ptrUrl.write();
        m_struParam.pRequestUrl = ptrUrl.getPointer();
        m_struParam.dwRequestUrlLen = url.length();

        HCISUPCMS.BYTE_ARRAY ptrOutByte = new HCISUPCMS.BYTE_ARRAY(100 * 1024);
        m_struParam.pOutBuffer = ptrOutByte.getPointer();
        m_struParam.dwOutSize = 100 * 1024;
        m_struParam.write();

        if (!hCEhomeCMS.NET_ECMS_ISAPIPassThrough(lUserID, m_struParam)) {
            int iErr = hCEhomeCMS.NET_ECMS_GetLastError();
            log.info("NET_ECMS_ISAPIPassThrough failed, error:{}", iErr);
            m_struParam.read();
            ptrOutByte.read();
            log.info("ptrOutByte:{}", new String(ptrOutByte.byValue).trim());
        }
        String xml = new String(m_struParam.pOutBuffer.getByteArray(0, m_struParam.dwOutSize));
        xml = "\n" + xml;
        JSONObject result = Xml2JsonUtil.xml2Json(xml);
        JSONArray jsonArray = result.getJSONArray("inputproxychannel");
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject j = jsonArray.getJSONObject(i);
            if (j.getInteger("id") == channel) {
                return j.getJSONObject("sourceinputportdescriptor").getString("ipaddress");
            }
        }
        return null;
    }

    public List cameraList(int lUserID) throws DocumentException {
        HCISUPCMS.NET_EHOME_PTXML_PARAM m_struParam = new HCISUPCMS.NET_EHOME_PTXML_PARAM();
        m_struParam.read();
        String url = "GET /ISAPI/ContentMgmt/InputProxy/channels";
        HCISUPCMS.BYTE_ARRAY ptrUrl = new HCISUPCMS.BYTE_ARRAY(1024);
        System.arraycopy(url.getBytes(), 0, ptrUrl.byValue, 0, url.length());
        ptrUrl.write();
        m_struParam.pRequestUrl = ptrUrl.getPointer();
        m_struParam.dwRequestUrlLen = url.length();

        HCISUPCMS.BYTE_ARRAY ptrOutByte = new HCISUPCMS.BYTE_ARRAY(100 * 1024);
        m_struParam.pOutBuffer = ptrOutByte.getPointer();
        m_struParam.dwOutSize = 100 * 1024;
        m_struParam.write();

        if (!hCEhomeCMS.NET_ECMS_ISAPIPassThrough(lUserID, m_struParam)) {
            int iErr = hCEhomeCMS.NET_ECMS_GetLastError();
            log.info("NET_ECMS_ISAPIPassThrough failed, error:{}", iErr);
            m_struParam.read();
            ptrOutByte.read();
            log.info("ptrOutByte:{}", new String(ptrOutByte.byValue).trim());
        }
        String xml = new String(m_struParam.pOutBuffer.getByteArray(0, m_struParam.dwOutSize));
        xml = "\n" + xml;
        JSONObject resultJson = Xml2JsonUtil.xml2Json(xml);
        JSONArray jsonArray = resultJson.getJSONArray("inputproxychannel");
        List result = new ArrayList<>();
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject j = jsonArray.getJSONObject(i);
            result.add(j.getInteger("id"));
        }
        return result;
    }

    public void playBack(int lUserID, String ip, String port, @NotNull Date start, @NotNull Date end, String fileName, Integer taskId, Integer breastpieceId) {
        HCISUPCMS.NET_EHOME_PLAYBACK_INFO_IN pPlaybackInfoIn = new HCISUPCMS.NET_EHOME_PLAYBACK_INFO_IN();
        pPlaybackInfoIn.read();
        pPlaybackInfoIn.dwSize = pPlaybackInfoIn.size();
        pPlaybackInfoIn.dwChannel = 1;

        pPlaybackInfoIn.byPlayBackMode = 1;
        pPlaybackInfoIn.unionPlayBackMode.setType(HCISUPCMS.NET_EHOME_PLAYBACKBYTIME.class);
        Calendar cal = Calendar.getInstance();
        cal.setTime(start);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStartTime.wYear = (short) cal.get(Calendar.YEAR);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStartTime.byMonth = (byte) (cal.get(Calendar.MONTH) + 1);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStartTime.byDay = (byte) cal.get(Calendar.DAY_OF_MONTH);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStartTime.byHour = (byte) cal.get(Calendar.HOUR_OF_DAY);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStartTime.byMinute = (byte) cal.get(Calendar.MINUTE);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStartTime.bySecond = (byte) cal.get(Calendar.SECOND);

        cal.setTime(end);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStopTime.wYear = (short) cal.get(Calendar.YEAR);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStopTime.byMonth = (byte) (cal.get(Calendar.MONTH) + 1);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStopTime.byDay = (byte) cal.get(Calendar.DAY_OF_MONTH);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStopTime.byHour = (byte) cal.get(Calendar.HOUR_OF_DAY);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStopTime.byMinute = (byte) cal.get(Calendar.MINUTE);
        pPlaybackInfoIn.unionPlayBackMode.struPlayBackbyTime.struStopTime.bySecond = (byte) cal.get(Calendar.SECOND);

        System.arraycopy(ip.getBytes(), 0, pPlaybackInfoIn.struStreamSever.szIP, 0, ip.length());
        pPlaybackInfoIn.struStreamSever.wPort = Short.parseShort(port);
        pPlaybackInfoIn.write();
        HCISUPCMS.NET_EHOME_PLAYBACK_INFO_OUT pPlaybackInfoOut = new HCISUPCMS.NET_EHOME_PLAYBACK_INFO_OUT();
        pPlaybackInfoOut.write();
        log.info("NET_ECMS_StartPlayBack接口参数:{}", pPlaybackInfoIn);
        if (hCEhomeCMS.NET_ECMS_StartPlayBack(lUserID, pPlaybackInfoIn, pPlaybackInfoOut)) {
            pPlaybackInfoOut.read();
            log.info("NET_ECMS_StartPlayBack success");
        } else {
            log.info("NET_ECMS_StartPlayBack failed, error code: {}", hCEhomeCMS.NET_ECMS_GetLastError());
            return;
        }
        HCISUPCMS.NET_EHOME_PUSHPLAYBACK_IN m_struPushPlayBackIn = new HCISUPCMS.NET_EHOME_PUSHPLAYBACK_IN();
        m_struPushPlayBackIn.read();
        m_struPushPlayBackIn.dwSize = m_struPushPlayBackIn.size();
        m_struPushPlayBackIn.lSessionID = pPlaybackInfoOut.lSessionID;
        m_struPushPlayBackIn.write();

        HCISUPCMS.NET_EHOME_PUSHPLAYBACK_OUT m_struPushPlayBackOut = new HCISUPCMS.NET_EHOME_PUSHPLAYBACK_OUT();
        m_struPushPlayBackOut.read();
        m_struPushPlayBackOut.dwSize = m_struPushPlayBackOut.size();
        m_struPushPlayBackOut.write();
        log.info("NET_ECMS_StartPushPlayBack接口参数:{}", m_struPushPlayBackIn);
        if (hCEhomeCMS.NET_ECMS_StartPushPlayBack(lUserID, m_struPushPlayBackIn, m_struPushPlayBackOut)) {
            log.info("NET_ECMS_StartPushPlayBack success");
            redisUtil.set("downloadHandle:" + m_struPushPlayBackOut.lHandle, fileName);
            BreastpieceVideo breastpieceVideo = new BreastpieceVideo();
            String[] names = fileName.split(OsSelect.isLinux()? "/" : "\\\\");
            breastpieceVideo.setTaskId(taskId);
            breastpieceVideo.setBreastpieceId(breastpieceId);
            breastpieceVideo.setName(names[names.length - 1] + ".mp4");
            breastpieceVideo.setAddress("http://47.101.185.231/ambulance-pc/video/breast/");
            breastpieceVideo.setStartTime(start);
            breastpieceVideo.setEndTime(end);
            breastpieceVideoMapper.insert(breastpieceVideo);
        } else {
            log.info("NET_ECMS_StartPushPlayBack failed, error code: {}", hCEhomeCMS.NET_ECMS_GetLastError());
        }

    }

    //注册回调函数
    public class FRegisterCallBack implements HCISUPCMS.DEVICE_REGISTER_CB {
        public String ip, port, breastpieceVideoPath;


        @Override
        public boolean invoke(int lUserID, int dwDataType, Pointer pOutBuffer, int dwOutLen, Pointer pInBuffer, int dwInLen, Pointer pUser) {
            log.info("FRegisterCallBack, dwDataType:" + dwDataType + ", lUserID:" + lUserID);
            HCISUPCMS.NET_EHOME_DEV_REG_INFO_V12 strDevRegInfo;
            Pointer pDevRegInfo;
            if (dwDataType == 0 || dwDataType == 7) {
                strDevRegInfo = new HCISUPCMS.NET_EHOME_DEV_REG_INFO_V12();
                strDevRegInfo.write();
                pDevRegInfo = strDevRegInfo.getPointer();
                pDevRegInfo.write(0, pOutBuffer.getByteArray(0, strDevRegInfo.size()), 0, strDevRegInfo.size());
                strDevRegInfo.read();
                HCISUPCMS.NET_EHOME_SERVER_INFO_V50 strEhomeServerInfo = new HCISUPCMS.NET_EHOME_SERVER_INFO_V50();
                strEhomeServerInfo.read();
                byte[] byCmsIP = new byte[0];
                log.info(new String(byCmsIP));
                String nvrCode = new String(strDevRegInfo.struRegInfo.byDeviceID).trim();
                String nvrIp = new String(strDevRegInfo.struRegInfo.struDevAdd.szIP).trim();
                log.info("Device online, DeviceID is:" + nvrCode);
                log.info("Device online, DeviceIP is:" + nvrIp);
                // 设备注册上线标志
                userLoginMap.put(nvrCode,lUserID);
                redisUtil.set("ip:" + nvrCode, nvrIp);
                redisUtil.set("lUserID:" + nvrCode, lUserID);
                redisUtil.set("carNo:" + lUserID, nvrCode);
                if (nvrCode.startsWith("xp")) {
                    UpdateWrapper updateWrapper = new UpdateWrapper<>();
                    updateWrapper.eq("name", nvrCode);
                    Breastpiece breastpiece = new Breastpiece();
                    breastpiece.setIsOnline(1);
                    breastpieceMapper.update(breastpiece, updateWrapper);
                    QueryWrapper queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("name", nvrCode);
                    Breastpiece breastpieceL = breastpieceMapper.selectOne(queryWrapper);
                    //异步配置心跳监听。100秒没有收到心跳,设备离线
                    ExecutorService executorService = Executors.newSingleThreadExecutor();
                    executorService.submit(() -> {
                        try {
                            Thread.sleep(2000);
                            boolean heart = hCEhomeCMS.NET_ECMS_SetAliveTimeout(lUserID, 100L, 1L);
                            if (heart) {
                                log.info("device {} NET_ECMS_SetAliveTimeout success", nvrCode);
                            } else {
                                log.info("device {} NET_ECMS_SetAliveTimeout failed, error code: {}", nvrCode, hCEhomeCMS.NET_ECMS_GetLastError());
                            }
                            Stream stream = CacheMap.get("stream");

                            stream.startListenPlayBack(ip, port);
                            List taskList = taskDetailMapper.selectListByDeviceCode(nvrCode);
                            if (taskList != null && taskList.size() > 0) {
                                taskList.forEach(t -> {
                                    QueryWrapper wrapper = new QueryWrapper<>();
                                    wrapper.eq("task_id", t.getId());
                                    Integer c = breastpieceVideoMapper.selectCount(wrapper);
                                    if (c <= 0) {
                                        playBack(lUserID, "47.101.185.231", port, t.getReceiveTime(), t.getDeliveryTime(), breastpieceVideoPath + t.getId(), t.getId(), breastpieceL.getId());
                                    }
                                });
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    });
                    executorService.shutdown();
                }
                return true;
            } else if (dwDataType == 3) {
                strDevRegInfo = new HCISUPCMS.NET_EHOME_DEV_REG_INFO_V12();
                strDevRegInfo.write();
                pDevRegInfo = strDevRegInfo.getPointer();
                pDevRegInfo.write(0, pOutBuffer.getByteArray(0, strDevRegInfo.size()), 0, strDevRegInfo.size());
                strDevRegInfo.read();
                String szEHomeKey = "12345678";
                byte[] bs = szEHomeKey.getBytes();
                pInBuffer.write(0, bs, 0, szEHomeKey.length());
            } else if (dwDataType == 4) {
                strDevRegInfo = new HCISUPCMS.NET_EHOME_DEV_REG_INFO_V12();
                strDevRegInfo.write();
                pDevRegInfo = strDevRegInfo.getPointer();
                pDevRegInfo.write(0, pOutBuffer.getByteArray(0, strDevRegInfo.size()), 0, strDevRegInfo.size());
                strDevRegInfo.read();

                log.info("byDeviceID:" + new String(strDevRegInfo.struRegInfo.byDeviceID).trim());
                log.info("bySessionKey:" + new String(strDevRegInfo.struRegInfo.bySessionKey).trim());

                HCISUPCMS.NET_EHOME_DEV_SESSIONKEY struSessionKey = new HCISUPCMS.NET_EHOME_DEV_SESSIONKEY();
                System.arraycopy(strDevRegInfo.struRegInfo.byDeviceID, 0, struSessionKey.sDeviceID, 0, strDevRegInfo.struRegInfo.byDeviceID.length);
                System.arraycopy(strDevRegInfo.struRegInfo.bySessionKey, 0, struSessionKey.sSessionKey, 0, strDevRegInfo.struRegInfo.bySessionKey.length);
                struSessionKey.write();

                Pointer pSessionKey = struSessionKey.getPointer();

                hCEhomeCMS.NET_ECMS_SetDeviceSessionKey(pSessionKey);
//                mHCEHomeAlarm.NET_EALARM_SetDeviceSessionKey(pSessionKey);
            } else if (dwDataType == 5) {
                String dasInfo = "{\n" +
                        "    \"Type\":\"DAS\",\n" +
                        "    \"DasInfo\":{\n" +
                        "        \"Address\":\"47.101.185.231\",\n" +
                        "        \"Domain\":\"\",\n" +
                        "        \"ServerID\":\"\",\n" +
                        "        \"Port\":20000,\n" +
                        "        \"UdpPort\":\n" +
                        "    }\n" +
                        "}";
                byte[] bs1 = dasInfo.getBytes();
                pInBuffer.write(0, bs1, 0, dasInfo.length());
            } else if (dwDataType == 1) {
                //设备离线
                String deviceName = redisUtil.get("carNo:" + lUserID);
                log.info("设备{}离线", deviceName);
                if (deviceName.startsWith("xp")) {
                    //胸牌
                    UpdateWrapper updateWrapper = new UpdateWrapper<>();
                    updateWrapper.eq("name", deviceName);
                    Breastpiece breastpiece = new Breastpiece();
                    breastpiece.setIsOnline(0);
                    breastpieceMapper.update(breastpiece, updateWrapper);
                }
                redisUtil.delete("carNo:" + lUserID);
                redisUtil.delete("ip:" + deviceName);
                redisUtil.delete("lUserID:" + deviceName);
                userLoginMap.remove(deviceName);
            } else if (dwDataType == 8) {
                //心跳
                log.info("__心跳___");
                log.info("FRegisterCallBack default type:" + dwDataType);
            } else {
                log.info("FRegisterCallBack default type:" + dwDataType);
            }
            return true;
        }
    }

    public List findFile(int lLoginID, VideoFilesReq req) throws InterruptedException {
        List result = new ArrayList<>();
        HCISUPCMS.NET_EHOME_REC_FILE_COND strufindCond = new HCISUPCMS.NET_EHOME_REC_FILE_COND();
        strufindCond.dwChannel = 1;
        strufindCond.dwRecType = 0xff;
        strufindCond.dwStartIndex = 0;
        strufindCond.dwMaxFileCountPer = 10;

        Calendar c = Calendar.getInstance();
        c.setTime(req.getStart());
        strufindCond.struStartTime.wYear = (short) c.get(Calendar.YEAR);
        strufindCond.struStartTime.byMonth = (byte) (c.get(Calendar.MONTH) + 1);
        strufindCond.struStartTime.byDay = (byte) c.get(Calendar.DAY_OF_MONTH);
        strufindCond.struStartTime.byHour = (byte) c.get(Calendar.HOUR_OF_DAY);
        strufindCond.struStartTime.byMinute = (byte) c.get(Calendar.MINUTE);
        strufindCond.struStartTime.bySecond = (byte) c.get(Calendar.SECOND);
        c.setTime(req.getEnd());
        strufindCond.struStopTime.wYear = (short) c.get(Calendar.YEAR);
        strufindCond.struStopTime.byMonth = (byte) (c.get(Calendar.MONTH) + 1);
        strufindCond.struStopTime.byDay = (byte) c.get(Calendar.DAY_OF_MONTH);
        strufindCond.struStopTime.byHour = (byte) c.get(Calendar.HOUR_OF_DAY);
        strufindCond.struStopTime.byMinute = (byte) c.get(Calendar.MINUTE);
        strufindCond.struStopTime.bySecond = (byte) c.get(Calendar.SECOND);

        int lHandle = hCEhomeCMS.NET_ECMS_StartFindFile_V11(lLoginID, 0, strufindCond, strufindCond.size());
        if (lHandle < 0) {
            log.info("NET_ECMS_StartFindFile_V11 failed, error code: {}", hCEhomeCMS.NET_ECMS_GetLastError());
        } else {
            log.info("NET_ECMS_StartFindFile_V11 success!");
            while (true) {
                HCISUPCMS.NET_EHOME_REC_FILE struFileInfo = new HCISUPCMS.NET_EHOME_REC_FILE();
                int lRet = hCEhomeCMS.NET_ECMS_FindNextFile_V11(lHandle, struFileInfo, struFileInfo.size());
                if (lRet == 1000) {
                    //保存获取到的文件
                    VideoFileInfoVo vo = new VideoFileInfoVo();
                    vo.setFileName(new String(struFileInfo.sFileName));
                    log.info(vo.getFileName());
                    String fileSize;
                    if (struFileInfo.dwFileSize / 1024 == 0) {
                        fileSize = String.valueOf(struFileInfo.dwFileSize);
                    } else if (struFileInfo.dwFileSize / 1024 > 0 && struFileInfo.dwFileSize / (1024 * 1024) == 0) {
                        fileSize = struFileInfo.dwFileSize / 1024 + "K";
                    } else {
                        fileSize = struFileInfo.dwFileSize / (1024 * 1024) + "M";
                    }
                    vo.setSize(fileSize);
                    vo.setStart(String.format("%04d-%02d-%02d %02d:%02d:%02d", struFileInfo.struStartTime.wYear, struFileInfo.struStartTime.byMonth, struFileInfo.struStartTime.byDay, struFileInfo.struStartTime.byHour, struFileInfo.struStartTime.byMinute, struFileInfo.struStartTime.bySecond));
                    vo.setEnd(String.format("%04d-%02d-%02d %02d:%02d:%02d", struFileInfo.struStopTime.wYear, struFileInfo.struStopTime.byMonth, struFileInfo.struStopTime.byDay, struFileInfo.struStopTime.byHour, struFileInfo.struStopTime.byMinute, struFileInfo.struStopTime.bySecond));
                    result.add(vo);
                } else if (lRet == 1002) {
                    Thread.sleep(5L);
                } else {
                    break;
                }
            }
            hCEhomeCMS.NET_ECMS_StopFindFile(lHandle);

        }
        return result;
    }

}

3.stream代码

package com.ei.ambulance.util.isup;

import com.ei.ambulance.util.RedisUtil;
import com.ei.ambulance.util.hik.HCNetSDK;
import com.ei.ambulance.util.hik.OsSelect;
import com.ei.ambulance.util.video.PushUtil;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;

@Slf4j
public class Stream {
    public static Map streamMap = new HashMap<>();
    public static RedisUtil redisUtil;
    public static String rtmphost;
    public static HCISUPStream hCEhomeStream = null;
    public static PlayCtrl playCtrl = null;
    static int m_lPlayBackLinkHandle = -1;   //回放句柄
    public static int m_lPlayBackListenHandle = -1; //回放监听句柄

    public static int StreamHandle = -1;   //预览监听句柄
    public static Map sessionMap = new HashMap<>(); //预览sessionID
    public static Map playHandleMap = new HashMap<>();
    static int backSessionID = -1;  //回放sessionID
    static int Count = 0;
    static int iCount = 0;
    static IntByReference m_lPort = new IntByReference(-1);//回调预览时播放库端口指针

    HCISUPStream.NET_EHOME_PLAYBACK_LISTEN_PARAM struPlayBackListen = new HCISUPStream.NET_EHOME_PLAYBACK_LISTEN_PARAM();
//    HCISUPStream.NET_EHOME_LISTEN_PREVIEW_CFG struPreviewListen = new HCISUPStream.NET_EHOME_LISTEN_PREVIEW_CFG();
    static FPREVIEW_NEWLINK_CB fPREVIEW_NEWLINK_CB;//预览监听回调函数实现
//    static FPREVIEW_DATA_CB fPREVIEW_DATA_CB;//预览回调函数实现
    public static Map dataMap = new HashMap<>();


    static PLAYBACK_NEWLINK_CB fPLAYBACK_NEWLINK_CB; //回放监听回调函数实现
    static PLAYBACK_DATA_CB fPLAYBACK_DATA_CB;   //回放回调实现

    /**
     * 动态库加载
     *
     * @return
     */
    private static boolean CreateSDKInstance() {
        if (hCEhomeStream == null) {
            synchronized (HCISUPStream.class) {
                String strDllPath = "";
                try {
                    if (OsSelect.isWindows())
                        //win系统加载库路径
                        strDllPath = System.getProperty("user.dir") + "\\lib\\isupsdk\\HCISUPStream.dll";

                    else if (OsSelect.isLinux())
                        //Linux系统加载库路径
                        strDllPath = System.getProperty("user.dir") + "/lib/isupsdkLinux/libHCISUPStream.so";
                    hCEhomeStream = (HCISUPStream) Native.loadLibrary(strDllPath, HCISUPStream.class);
                } catch (Exception ex) {
                    log.info("loadLibrary: " + strDllPath + " Error: " + ex.getMessage());
                    return false;
                }
            }
        }
        return true;
    }

    public void eStream_Init() {
        if (hCEhomeStream == null) {
            if (!CreateSDKInstance()) {
                log.info("Load Stream SDK fail");
                return;
            } else {
                log.info("Load Stream SDK success");
            }
        }
//        if (playCtrl == null) {
//            if (!CreatePlayInstance()) {
//                log.info("Load PlayCtrl fail");
//                return;
//            } else {
//                log.info("Load PlayCtrl SDK success");
//            }
//
//        }
        if (OsSelect.isWindows()) {
            HCISUPCMS.BYTE_ARRAY ptrByteArrayCrypto = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathCrypto = System.getProperty("user.dir") + "\\lib\\isupsdk\\libeay32.dll"; //Linux版本是libcrypto.so库文件的路径
            System.arraycopy(strPathCrypto.getBytes(), 0, ptrByteArrayCrypto.byValue, 0, strPathCrypto.length());
            ptrByteArrayCrypto.write();
            if (!hCEhomeStream.NET_ESTREAM_SetSDKInitCfg(0, ptrByteArrayCrypto.getPointer())) {
                log.info("NET_ESTREAM_SetSDKInitCfg 0 failed, error:" + hCEhomeStream.NET_ESTREAM_GetLastError());
            }
            HCISUPCMS.BYTE_ARRAY ptrByteArraySsl = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathSsl = System.getProperty("user.dir") + "\\lib\\isupsdk\\ssleay32.dll";    //Linux版本是libssl.so库文件的路径
            System.arraycopy(strPathSsl.getBytes(), 0, ptrByteArraySsl.byValue, 0, strPathSsl.length());
            ptrByteArraySsl.write();
            if (!hCEhomeStream.NET_ESTREAM_SetSDKInitCfg(1, ptrByteArraySsl.getPointer())) {
                log.info("NET_ESTREAM_SetSDKInitCfg 1 failed, error:" + hCEhomeStream.NET_ESTREAM_GetLastError());
            }
            //流媒体初始化
            hCEhomeStream.NET_ESTREAM_Init();
            //设置HCAapSDKCom组件库文件夹所在路径
            HCISUPCMS.BYTE_ARRAY ptrByteArrayCom = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathCom = System.getProperty("user.dir") + "\\lib\\isupsdk\\HCAapSDKCom";      //只支持绝对路径,建议使用英文路径
            System.arraycopy(strPathCom.getBytes(), 0, ptrByteArrayCom.byValue, 0, strPathCom.length());
            ptrByteArrayCom.write();
            if (!hCEhomeStream.NET_ESTREAM_SetSDKLocalCfg(5, ptrByteArrayCom.getPointer())) {
                log.info("NET_ESTREAM_SetSDKLocalCfg 5 failed, error:" + hCEhomeStream.NET_ESTREAM_GetLastError());
            }
            hCEhomeStream.NET_ESTREAM_SetLogToFile(3, System.getProperty("user.dir") + "/EHomeSDKLog", false);
        } else if (OsSelect.isLinux()) {
            //设置libcrypto.so所在路径
            HCISUPCMS.BYTE_ARRAY ptrByteArrayCrypto = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathCrypto = System.getProperty("user.dir") + "/lib/libcrypto.so"; //Linux版本是libcrypto.so库文件的路径
            System.arraycopy(strPathCrypto.getBytes(), 0, ptrByteArrayCrypto.byValue, 0, strPathCrypto.length());
            ptrByteArrayCrypto.write();
            if (!hCEhomeStream.NET_ESTREAM_SetSDKInitCfg(0, ptrByteArrayCrypto.getPointer())) {
                log.info("NET_ESTREAM_SetSDKInitCfg 0 failed, error:" + hCEhomeStream.NET_ESTREAM_GetLastError());
            }
            //设置libssl.so所在路径
            HCISUPCMS.BYTE_ARRAY ptrByteArraySsl = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathSsl = System.getProperty("user.dir") + "/lib/libssl.so";    //Linux版本是libssl.so库文件的路径
            System.arraycopy(strPathSsl.getBytes(), 0, ptrByteArraySsl.byValue, 0, strPathSsl.length());
            ptrByteArraySsl.write();
            if (!hCEhomeStream.NET_ESTREAM_SetSDKInitCfg(1, ptrByteArraySsl.getPointer())) {
                log.info("NET_ESTREAM_SetSDKInitCfg 1 failed, error:" + hCEhomeStream.NET_ESTREAM_GetLastError());
            }
            hCEhomeStream.NET_ESTREAM_Init();
            //设置HCAapSDKCom组件库文件夹所在路径
            HCISUPCMS.BYTE_ARRAY ptrByteArrayCom = new HCISUPCMS.BYTE_ARRAY(256);
            String strPathCom = System.getProperty("user.dir") + "/lib/HCAapSDKCom/";      //只支持绝对路径,建议使用英文路径
            System.arraycopy(strPathCom.getBytes(), 0, ptrByteArrayCom.byValue, 0, strPathCom.length());
            ptrByteArrayCom.write();
            if (!hCEhomeStream.NET_ESTREAM_SetSDKLocalCfg(5, ptrByteArrayCom.getPointer())) {
                log.info("NET_ESTREAM_SetSDKLocalCfg 5 failed, error:" + hCEhomeStream.NET_ESTREAM_GetLastError());
            }
            hCEhomeStream.NET_ESTREAM_SetLogToFile(3, System.getProperty("user.dir") + "/EHomeSDKLog", false);
        }

    }

    public void startRealPlayListen(String smsServerListenIP, String smsServerListenPort) {
        //预览监听
        if (fPREVIEW_NEWLINK_CB == null) {
            fPREVIEW_NEWLINK_CB = new FPREVIEW_NEWLINK_CB();
        }
        HCISUPStream.NET_EHOME_LISTEN_PREVIEW_CFG struPreviewListen = new HCISUPStream.NET_EHOME_LISTEN_PREVIEW_CFG();
        System.arraycopy(smsServerListenIP.getBytes(), 0, struPreviewListen.struIPAdress.szIP, 0, smsServerListenIP.length());
        struPreviewListen.struIPAdress.wPort = Short.parseShort(smsServerListenPort); //流媒体服务器监听端口
        struPreviewListen.fnNewLinkCB = fPREVIEW_NEWLINK_CB; //预览连接请求回调函数
        struPreviewListen.pUser = null;
        struPreviewListen.byLinkMode = 0; //0- TCP方式,1- UDP方式
        struPreviewListen.write();

        if (StreamHandle < 0) {
            StreamHandle = hCEhomeStream.NET_ESTREAM_StartListenPreview(struPreviewListen);
            if (StreamHandle == -1) {

                System.out.println("NET_ESTREAM_StartListenPreview failed, error code:" + hCEhomeStream.NET_ESTREAM_GetLastError());
                hCEhomeStream.NET_ESTREAM_Fini();
                return;
            } else {
                String StreamListenInfo = new String(struPreviewListen.struIPAdress.szIP).trim() + "_" + struPreviewListen.struIPAdress.wPort;
                System.out.println("流媒体服务:" + StreamListenInfo + ",NET_ESTREAM_StartListenPreview succeed");
            }
        }
    }


    /**
     * 开启预览,
     *
     * @param carId
     * @param lChannel 预览通道号
     */
    public void realPlay(int lLoginId, String carId, int lChannel, String smsServerIP, String smsServerPort) throws Exception {
//        JPanelDemo.jRealWinInit();
        HCISUPCMS.NET_EHOME_PREVIEWINFO_IN_V11 struPreviewIn = new HCISUPCMS.NET_EHOME_PREVIEWINFO_IN_V11();
        struPreviewIn.iChannel = lChannel; //通道号
        struPreviewIn.dwLinkMode = 0; //0- TCP方式,1- UDP方式
        struPreviewIn.dwStreamType = 0; //码流类型:0- 主码流,1- 子码流, 2- 第三码流
        struPreviewIn.struStreamSever.szIP = smsServerIP.getBytes();//流媒体服务器IP地址,公网地址
        struPreviewIn.struStreamSever.wPort = Short.parseShort(smsServerPort); //流媒体服务器端口,需要跟服务器启动监听端口一致
        struPreviewIn.write();
        //预览请求
        HCISUPCMS.NET_EHOME_PREVIEWINFO_OUT struPreviewOut = new HCISUPCMS.NET_EHOME_PREVIEWINFO_OUT();
        boolean getRS = Cms.hCEhomeCMS.NET_ECMS_StartGetRealStreamV11(lLoginId, struPreviewIn, struPreviewOut);
//        boolean getRS = Cms.hCEhomeCMS.NET_ECMS_StartGetRealStream(Cms.lLoginID, struPreviewIn, struPreviewOut);
        //Thread.sleep(10000);
        if (!getRS) {
            log.error("NET_ECMS_StartGetRealStream failed, error code:{}", Cms.hCEhomeCMS.NET_ECMS_GetLastError());
            throw new Exception("摄像头不存在");
        } else {
            struPreviewOut.read();
            log.info("NET_ECMS_StartGetRealStream succeed, sessionID:{}", struPreviewOut.lSessionID);
            sessionMap.put("session:"+carId+"_"+lChannel,struPreviewOut.lSessionID);
        }
        HCISUPCMS.NET_EHOME_PUSHSTREAM_IN struPushInfoIn = new HCISUPCMS.NET_EHOME_PUSHSTREAM_IN();
        struPushInfoIn.read();
        struPushInfoIn.dwSize = struPushInfoIn.size();
        struPushInfoIn.lSessionID = sessionMap.get("session:"+carId+"_"+lChannel);
        struPushInfoIn.write();
        HCISUPCMS.NET_EHOME_PUSHSTREAM_OUT struPushInfoOut = new HCISUPCMS.NET_EHOME_PUSHSTREAM_OUT();
        struPushInfoOut.read();
        struPushInfoOut.dwSize = struPushInfoOut.size();
        struPushInfoOut.write();
        if (!Cms.hCEhomeCMS.NET_ECMS_StartPushRealStream(lLoginId, struPushInfoIn, struPushInfoOut)) {
            sessionMap.remove("session:"+carId+"_"+lChannel);
            log.error("NET_ECMS_StartPushRealStream failed, error code:" + Cms.hCEhomeCMS.NET_ECMS_GetLastError());
        } else {
            log.info("NET_ECMS_StartPushRealStream succeed, sessionID:" + struPushInfoIn.lSessionID);
        }
    }

    /**
     * 停止预览,Stream服务停止实时流转发,CMS向设备发送停止预览请求
     */
    public void StopRealPlay(String carNo, Integer channel) {

        Integer lPreviewHandle = playHandleMap.get("realPlayHandle:" + carNo+channel);
        if (lPreviewHandle == null) {
            return;
        }
        if (!hCEhomeStream.NET_ESTREAM_StopPreview(lPreviewHandle)) {
            System.out.println("NET_ESTREAM_StopPreview failed,err = " + hCEhomeStream.NET_ESTREAM_GetLastError());
            return;
        }

        Integer loginId = Cms.userLoginMap.get(carNo);
        if (loginId == null) {
            return;
        }
        Integer sessionId = sessionMap.get("session:"+carNo+"_"+channel);
        if (sessionId == null) {
            return;
        }

        if (!Cms.hCEhomeCMS.NET_ECMS_StopGetRealStream(loginId, sessionId)) {
            log.info("NET_ECMS_StopGetRealStream failed,err = " + Cms.hCEhomeCMS.NET_ECMS_GetLastError());
            return;
        }
        log.info("停止Stream的实时流转发");
        Stream.dataMap.remove(carNo + "_" + channel);
        Stream.sessionMap.remove("session:"+carNo+"_"+channel);
        PipedOutputStream pos = streamMap.get("stream:" + lPreviewHandle);
        if (pos != null) {
            try {
                pos.close();
            } catch (IOException exception) {
                exception.printStackTrace();
            }
            Stream.streamMap.remove("stream:"+ lPreviewHandle);
            Stream.playHandleMap.remove("realPlayHandle:" + carNo+channel);
        }
        log.info("CMS发送预览停止请求");
    }

    /**
     * 播放库加载
     *
     * @return
     */
    private static boolean CreatePlayInstance() {
        if (playCtrl == null) {
            synchronized (PlayCtrl.class) {
                String strPlayPath = "";
                try {
                    if (OsSelect.isWindows())
                        //win系统加载库路径(路径不要带中文)
                        strPlayPath = System.getProperty("user.dir") + "\\lib\\isupsdk\\PlayCtrl.dll";
                    else if (OsSelect.isLinux())
                        //Linux系统加载库路径(路径不要带中文)
                        strPlayPath = System.getProperty("user.dir") + "/lib/libPlayCtrl.so";
                    playCtrl = (PlayCtrl) Native.loadLibrary(strPlayPath, PlayCtrl.class);

                } catch (Exception ex) {
                    log.info("loadLibrary: " + strPlayPath + " Error: " + ex.getMessage());
                    return false;
                }
            }
        }
        return true;
    }

    public void startListenPlayBack(String ip, String port) {
        if (fPLAYBACK_NEWLINK_CB == null) {
            fPLAYBACK_NEWLINK_CB = new PLAYBACK_NEWLINK_CB();
        }
        HCISUPStream.NET_EHOME_PLAYBACK_LISTEN_PARAM param = new HCISUPStream.NET_EHOME_PLAYBACK_LISTEN_PARAM();
        System.arraycopy(ip.getBytes(), 0, param.struIPAdress.szIP, 0, ip.length());
        param.struIPAdress.wPort = Short.parseShort(port);
        param.fnNewLinkCB = fPLAYBACK_NEWLINK_CB;
        param.byLinkMode = 0;//tcp-0;udp-1
        m_lPlayBackListenHandle = hCEhomeStream.NET_ESTREAM_StartListenPlayBack(param);
        if (m_lPlayBackListenHandle < -1) {
            log.info("NET_ESTREAM_StartListenPlayBack failed, error code: {}", hCEhomeStream.NET_ESTREAM_GetLastError());
        } else {
            log.info("回放服务:{}_{}, NET_ESTREAM_StartListenPlayBack success", ip, port);
        }

    }


    public class FPREVIEW_NEWLINK_CB implements HCISUPStream.PREVIEW_NEWLINK_CB {

        @Override
        public boolean invoke(int lLinkHandle, HCISUPStream.NET_EHOME_NEWLINK_CB_MSG pNewLinkCBMsg, Pointer pUserData) throws IOException {
            // 监听,每realPlay调用时,此方法监听到,开始执行
            String userId = new String(pNewLinkCBMsg.szDeviceID).trim();
            int dwChannelNo = pNewLinkCBMsg.dwChannelNo;

            PipedInputStream pis = new PipedInputStream();
            PipedOutputStream pos = new PipedOutputStream();
            Stream.streamMap.put("stream:"+lLinkHandle,pos);
            pos.connect(pis);
            CompletableFuture.runAsync(()-> {
                try {
                    PushUtil.grabAndPushRtmp(pis,"rtmp://"+rtmphost+"/live/"+userId+"_"+ dwChannelNo);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, Executors.newFixedThreadPool(1));

            log.info("FPREVIEW_NEWLINK_CB callback");
            log.info("FPREVIEW_NEWLINK_CB callback byStreamType : {}", pNewLinkCBMsg.byStreamType);
            //预览数据回调参数
            playHandleMap.put("realPlayHandle:" + userId + dwChannelNo , lLinkHandle );
            HCISUPStream.NET_EHOME_PREVIEW_DATA_CB_PARAM struDataCB = new HCISUPStream.NET_EHOME_PREVIEW_DATA_CB_PARAM();

            FPREVIEW_DATA_CB fPREVIEW_DATA_CB = dataMap.get(userId + "_" + dwChannelNo);
            if (fPREVIEW_DATA_CB == null) {
                fPREVIEW_DATA_CB = new FPREVIEW_DATA_CB(pos);
                dataMap.put(userId + "_" + dwChannelNo,fPREVIEW_DATA_CB);
            }
            struDataCB.fnPreviewDataCB = fPREVIEW_DATA_CB;

            if (!hCEhomeStream.NET_ESTREAM_SetPreviewDataCB(lLinkHandle, struDataCB)) {
                log.info("NET_ESTREAM_SetPreviewDataCB failed err::" + hCEhomeStream.NET_ESTREAM_GetLastError());
                return false;
            }
            return true;
        }
    }

    public class FPREVIEW_DATA_CB implements HCISUPStream.PREVIEW_DATA_CB {

        private PipedOutputStream pos;
        public FPREVIEW_DATA_CB(PipedOutputStream pos) {
            this.pos = pos;
        }

        //实时流回调函数/
        @Override
        public void invoke(int iPreviewHandle, HCISUPStream.NET_EHOME_PREVIEW_CB_MSG pPreviewCBMsg, Pointer pUserData) throws Exception {
//            if (Count == 500) {//降低打印频率
//                log.info("FPREVIEW_DATA_CB callback, iPreviewHandle:{}", iPreviewHandle);
//                log.info("FPREVIEW_DATA_CB callback, data length:" + pPreviewCBMsg.dwDataLen);
//                Count = 0;
//            }
//            Count++;

            long offset = 0;
            ByteBuffer buffers = pPreviewCBMsg.pRecvdata.getByteBuffer(offset, pPreviewCBMsg.dwDataLen);
            byte[] bytes = new byte[pPreviewCBMsg.dwDataLen];
            buffers.rewind();
            buffers.get(bytes);

            try{
                //2.将数据读入到内存空间,此处不可用redis,否则执行时间是当前10倍
//                PipedOutputStream pos = Stream.streamMap.get("stream:"+iPreviewHandle);
                pos.write(bytes);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    byte[] allEsBytes = null;

    public void writeESH264(final byte[] outputData) throws IOException {
        if (outputData.length <= 0) {
            return;
        }
        if ((outputData[0] & 0xff) == 0x00
                && (outputData[1] & 0xff) == 0x00
                && (outputData[2] & 0xff) == 0x01
                && (outputData[3] & 0xff) == 0xBA) {
            if (allEsBytes != null && allEsBytes.length > 0) {
                return;
//                allEsBytes = null;
            }
        }
        if ((outputData[0] & 0xff) == 0x00
                && (outputData[1] & 0xff) == 0x00
                && (outputData[2] & 0xff) == 0x01
                && (outputData[3] & 0xff) == 0xE0) {
            int from = 9 + outputData[8] & 0xff;
            int len = outputData.length - 9 - (outputData[8] & 0xff);

            byte[] esBytes = new byte[len];
            System.arraycopy(outputData, from, esBytes, 0, len);

            if (allEsBytes == null) {
                allEsBytes = esBytes;
            } else {
                byte[] newEsBytes = new byte[allEsBytes.length + esBytes.length];
                System.arraycopy(allEsBytes, 0, newEsBytes, 0, allEsBytes.length);
                System.arraycopy(esBytes, 0, newEsBytes, allEsBytes.length, esBytes.length);
                allEsBytes = newEsBytes;
            }
        } else {
            allEsBytes = outputData;
        }
    }

    void ffmpegConvetor(String videoInputPath, String videoOutPath) throws Exception {
        if (OsSelect.isWindows()) {
            String ffmpegExe = "E:\\chromeDowload\\ffmpeg.exe";
            List command = new ArrayList<>();
            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) {
                e.printStackTrace();
            }
            // 使用这种方式会在瞬间大量消耗CPU和内存等系统资源,所以这里我们需要对流进行处理
            try {
                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();
                }
            } catch (NullPointerException e) {
                e.printStackTrace();
            }
        } else if (OsSelect.isLinux()) {
            //todo linux的ffmpeg使用
        }
    }


    public static class PLAYBACK_NEWLINK_CB implements HCISUPStream.PLAYBACK_NEWLINK_CB {
        @Override
        public boolean invoke(int lPlayBackLinkHandle, HCISUPStream.NET_EHOME_PLAYBACK_NEWLINK_CB_INFO pNewLinkCBInfo, Pointer pUserData) {
            pNewLinkCBInfo.read();
            log.info("PLAYBACK_NEWLINK_CB callback, szDeviceID:" + new String(pNewLinkCBInfo.szDeviceID).trim()
                    + ",lSessionID:" + pNewLinkCBInfo.lSessionID + ",dwChannelNo:" + pNewLinkCBInfo.dwChannelNo);
            m_lPlayBackLinkHandle = lPlayBackLinkHandle;
            HCISUPStream.NET_EHOME_PLAYBACK_DATA_CB_PARAM struCBParam = new HCISUPStream.NET_EHOME_PLAYBACK_DATA_CB_PARAM();
            //预览数据回调参数
            if (fPLAYBACK_DATA_CB == null) {
                fPLAYBACK_DATA_CB = new PLAYBACK_DATA_CB();
            }
//            pNewLinkCBInfo.fnPlayBackDataCB = fPLAYBACK_DATA_CB;
//            pNewLinkCBInfo.byStreamFormat = 0;
            struCBParam.fnPlayBackDataCB = fPLAYBACK_DATA_CB;
            struCBParam.byStreamFormat = 0;
            struCBParam.write();
//            pNewLinkCBInfo.write();
            if (!hCEhomeStream.NET_ESTREAM_SetPlayBackDataCB(lPlayBackLinkHandle, struCBParam)) {
                log.info("NET_ESTREAM_SetPlayBackDataCB failed");
            }
            return true;
        }
    }

    public static class PLAYBACK_DATA_CB implements HCISUPStream.PLAYBACK_DATA_CB {
        //实时流回调函数
        @Override
        public boolean invoke(int iPlayBackLinkHandle, HCISUPStream.NET_EHOME_PLAYBACK_DATA_CB_INFO pDataCBInfo, Pointer pUserData) {
            if (iCount == 500) {//降低打印频率
                log.info("PLAYBACK_DATA_CB callback , dwDataLen:" + pDataCBInfo.dwDataLen + ",dwType:" + pDataCBInfo.dwType);
                iCount = 0;
            }
            iCount++;
            //播放库SDK解码显示在win窗口上,
            switch (pDataCBInfo.dwType) {
                case HCNetSDK.NET_DVR_SYSHEAD: //系统头
//                    boolean b_port = playCtrl.PlayM4_GetPort(m_lPort);
//                    if (b_port == false) //获取播放库未使用的通道号
//                    {
//                        break;
//                    }
//                    if (pDataCBInfo.dwDataLen > 0) {
//                        if (!playCtrl.PlayM4_SetOverlayMode(m_lPort.getValue(), false, 0)) {
//                            break;
//                        }
//
//                        if (!playCtrl.PlayM4_SetStreamOpenMode(m_lPort.getValue(), PlayCtrl.STREAME_FILE))  //设置文件流播放模式
//                        {
//                            break;
//                        }
//
//                        if (!playCtrl.PlayM4_OpenStream(m_lPort.getValue(), pDataCBInfo.pData, pDataCBInfo.dwDataLen, 2 * 1024 * 1024)) //打开流接口
//                        {
//                            break;
//                        }
//                        W32API.HWND hwnd = new W32API.HWND(Native.getComponentPointer(IsupTest.panelRealplay));
//                        if (!playCtrl.PlayM4_Play(m_lPort.getValue(), hwnd)) //播放开始
//                        {
//                            break;
//                        }
//                    }
                case HCNetSDK.NET_DVR_STREAMDATA:   //码流数据
                    if (pDataCBInfo.dwDataLen > 0) {
//                        for (int i = 0; i < 1000; i++) {
//                            boolean bRet = playCtrl.PlayM4_InputData(m_lPort.getValue(), pDataCBInfo.pData, pDataCBInfo.dwDataLen);
//                            if (!bRet) {
//                                if (i >= 999) {
//                                    log.info("PlayM4_InputData,failed err:" + playCtrl.PlayM4_GetLastError(m_lPort.getValue()));
//                                }
//                                try {
//                                    Thread.sleep(1000);
//                                } catch (InterruptedException e) {
//                                    e.printStackTrace();
//                                }
//                            } else {
//                                break;
//                            }
//                        }
                        try {
                            String fileName = redisUtil.get("downloadHandle:" + iPlayBackLinkHandle);
                            FileOutputStream m_file = new FileOutputStream(fileName, true);
                            long offset = 0;
                            ByteBuffer buffers = pDataCBInfo.pData.getByteBuffer(offset, pDataCBInfo.dwDataLen);
                            byte[] bytes = new byte[pDataCBInfo.dwDataLen];
                            buffers.rewind();
                            buffers.get(bytes);
                            m_file.write(bytes);
                            m_file.close();
//                           if (pDataCBInfo.dwDataLen < 1024)
//                                VideoUtil.convetor(fileName, fileName + ".mp4");

                        } catch (FileNotFoundException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                //如果仅需保存回放的视频流文件,参考一下注释的代码
			/*try {

				m_file = new FileOutputStream(file, true);
				long offset = 0;
	             ByteBuffer buffers = pDataCBInfo.pData.getByteBuffer(offset, pDataCBInfo.dwDataLen);
	             byte [] bytes = new byte[pDataCBInfo.dwDataLen];
	             buffers.rewind();
	             buffers.get(bytes);
	             m_file.write(bytes);
	             m_file.close();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}*/
                case 3: //结束
                    String fileName = redisUtil.get("downloadHandle:" + iPlayBackLinkHandle);
                    try {
                        if (OsSelect.isLinux()) {
                            convetorLinux(fileName, fileName + ".mp4");
                        } else {
                            convetorWindows(fileName, fileName + ".mp4");
                        }
                        break;
                    }catch (Exception ex) {
                        ex.printStackTrace();
                    }
            }
            return true;
        }
    }

    public static void convetorWindows(String videoInputPath, String videoOutPath) throws Exception{
        String ffmpegExe = "E:\\software\\ffmpeg-5.0.1-essentials_build\\bin\\ffmpeg.exe";
        List command = new ArrayList<>();
        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) {
            e.printStackTrace();
        }
        // 使用这种方式会在瞬间大量消耗CPU和内存等系统资源,所以这里我们需要对流进行处理
        try {
            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();
            }
            Path p = Paths.get(videoInputPath);
            Files.delete(p);
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
    }

    public static void convetorLinux(String videoInputPath, String videoOutPath) throws Exception {
        String ffmpegExe = "/usr/local/ffmpeg/bin/ffmpeg";
        String videoCommend = ffmpegExe + " -i " +  videoInputPath + " -c copy -an "  + videoOutPath;
        try {
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec(videoCommend);
            InputStream stderr = proc.getErrorStream();
            InputStreamReader isr = new InputStreamReader(stderr);
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null){
            }
            if (br != null) {
                br.close();
            }
            if (isr != null) {
                isr.close();
            }
            if (stderr != null) {
                stderr.close();
            }
            int exitVal = proc.waitFor();
            Path p = Paths.get(videoInputPath);
            Files.delete(p);
        } catch (Throwable t) {
            t.printStackTrace();
        }

    }


}