Desarrollo de función de vista previa web en tiempo real java Hikvision sdk nvr

1. Solución de transmisión de video en tiempo real

Tabla de contenido

1. Solución de transmisión de video en tiempo real

2. Pasos

1. Construya el servidor rtmp+flv

2. Demostración de vista previa de Java

3. Vista previa en tiempo real

1. Configure el archivo de la biblioteca sdk de Hikvision

3. Modifique el código FPREVIEW_DATA_CB para obtener la transmisión

4. Empuje el flujo de javacv

3. Parte del código

1. Inicie el cms de inicialización del proyecto, código de flujo

2. código cms

3. código de transmisión



1. El front-end llama a la interfaz de fondo
2. La interfaz de fondo llama a Hikvision sdk para iniciar un monitoreo específico de la cámara y obtiene los datos de transmisión en tiempo real de la cámara
3. Establece una canalización y el hilo de monitoreo de Hikvision sdk y javacv el subproceso de transmisión
comparte la canalización 4. El subproceso sdk genera datos de escritura de secuencia de canalización, el subproceso de transmisión javacv envía datos de canalización a rtmp. Es decir, el productor SDK, javacv es el consumidor
5. El módulo nginx+rtmp+flv recibe el flujo de video, al que se puede acceder en el navegador a través de la ruta de http

Front-end->Fondo->Hikang sdk->Pipeline->javacv->nginx+rtmp+flv->ruta http

2. Pasos

1. Construya el servidor rtmp+flv

Bajo el sistema de Windows, es más problemático compilarlo usted mismo. Aquí está la construcción del servicio de transmisión de medios de obs+nginx-http-flv-module en Windows compilado por los grandes chicos_windows nginx integra http-flv_OneCodeSolvesAll's blog-CSDN blog Compilado personalmente: Enlace: https://pan.baidu.com/s/1XKkyYvK5W6yZA1w2mPaAfg
Código de extracción: 3ug6

En el entorno Linux, descargue nginx-http-flv-module (incluido nginx- rtmp-module ) y agréguelo a la configuración de sobrecarga de nginx. Hay muchos tutoriales en Internet.

Si no necesita flv, también puede descargar nginx- rtmp-module por separado y agregarlo a la configuración de sobrecarga de nginx.

2. Demostración de vista previa de Java

1. Inicie sesión en la plataforma Haikang Plataforma abierta Haikang

Seleccione la descarga isup sdk del producto de hardware

2. Después de la descompresión

 Abra esta demostración de Java directamente, modifique la información de nvr, incluido el puerto IP, y podrá obtener una vista previa directamente.

3. Vista previa en tiempo real

1. Configure el archivo de la biblioteca sdk de Hikvision

Los archivos en la carpeta lib de la documentación de Haikang son archivos de biblioteca, y también están en JavaISUPDemo\lib. Debe prestar atención a si el documento es una versión win o linux.

Siempre que NET_ECMS_StartGetRealStreamV11 inicie la vista previa, FPREVIEW_NEWLINK_CB escuchará la solicitud de vista previa.

Cree tuberías para obtener y enviar flujos respectivamente. Comience a llamar asincrónicamente al método de transmisión de 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. Modifique el código FPREVIEW_DATA_CB para obtener la transmisión

Cada vez que se monitorea una transmisión de video, escriba la transmisión de video en la canalización pos. Tenga en cuenta que redis no se puede usar aquí. Redis afectará seriamente el rendimiento del código aquí, lo que provocará que no se obtengan flujos. Cada vez que escuchas, es un hilo nuevo. El primer bloque de código comentado se usa durante la depuración y debe eliminarse después de la depuración. La segunda parte del código de comentario pos también se puede almacenar en el mapa, y se puede obtener del mapa cuando se usa, y también se puede pasar directamente por el método de construcción aquí.

    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. Empuje el flujo de javacv

Con respecto a la transmisión javacv mencionada en 2., los paquetes de dependencia requeridos para los entornos de Linux y Windows son diferentes.

A continuación, la prueba se usa en el entorno de Windows local y el tiempo de ejecución se usa en línea, por lo que no necesita escribirlo, porque el valor predeterminado es el tiempo de ejecución.

<!-- Linux x86_64 使用 --> 
<dependencia> 
    <groupId>org.bytedeco</groupId> 
    <artifactId>javacpp</artifactId> 
    <version>1.5.7</version> 
</dependency> 
<dependency> 
    <groupId >org.bytedeco</groupId> 
    <artifactId>opencv</artifactId> 
    <version>4.5.5-1.5.7</version> 
    <classifier>linux-x86_64</classifier> 
    <scope>runtime</scope> 
</ dependencia> 
<dependencia> 
    <groupId>org.bytedeco</groupId> 
    <artifactId>openblas</artifactId> 
    <version>0.3.19-1.5.7</versión> 
    <clasificador>linux-x86_64</clasificador> 
    <ámbito>tiempo de ejecución</ámbito> 
</dependencia> 
<dependencia>
    <groupId>org.bytedeco</groupId> 
    <artifactId>ffmpeg</artifactId> 
    <version>5.0-1.5.7</version> 
    <classifier>linux-x86_64</classifier> 
    <scope>runtime</scope> 
</ dependencia> 
<!-- Windows x86_64 使用 --> 
<dependencia> 
    <groupId>org.bytedeco</groupId> 
    <artifactId>opencv</artifactId> 
    <version>4.5.5-1.5.7</version> 
    <classifier> windows-x86_64</classifier> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>org.bytedeco</groupId> 
    <artifactId>openblas</artifactId> 
    <version>0.3.19-1.5.7</version>  
    <classifier>windows-x86_64</classifier>
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>org.bytedeco</groupId> 
    <artifactId>ffmpeg</artifactId> 
    <version>5.0-1.5.7</version> 
    <classifier>windows-x86_64</classifier > 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>org.bytedeco</groupId> 
    <artifactId>javacv</artifactId> 
    <version>1.5.7</version> 
    <exclusions> 
        <exclusion> 
            <groupId>org.bytedeco.javacpp-presets</groupId> 
            <artifactId>*</artifactId> 
        </exclusion> 
    </exclusions> 
</dependency>

El principio del código de transmisión es que javacv toma el cuadro, calcula el intervalo de tiempo de acuerdo con la velocidad de cuadro y lo envía al servidor 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();
    }

}

Entre ellos, Thread.sleep(500); es necesario. Para garantizar que este subproceso se ejecute en el siguiente código, debe haber una secuencia en la canalización, de modo que la inicialización del capturador de fotogramas pueda realizarse correctamente. El subproceso de transmisión instantánea debe ejecutarse antes que el subproceso de inserción. De lo contrario, se informará un error.

3. Parte del código

Parte del código comercial está involucrado en el código, así que modifíquelo según sea necesario.

1. Inicie el cms de inicialización del proyecto, código de flujo

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. código 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<String,Integer> 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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<Integer> 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml;
        JSONObject resultJson = Xml2JsonUtil.xml2Json(xml);
        JSONArray jsonArray = resultJson.getJSONArray("inputproxychannel");
        List<Integer> 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<Breastpiece> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.eq("name", nvrCode);
                    Breastpiece breastpiece = new Breastpiece();
                    breastpiece.setIsOnline(1);
                    breastpieceMapper.update(breastpiece, updateWrapper);
                    QueryWrapper<Breastpiece> 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<TaskDetail> taskList = taskDetailMapper.selectListByDeviceCode(nvrCode);
                            if (taskList != null && taskList.size() > 0) {
                                taskList.forEach(t -> {
                                    QueryWrapper<BreastpieceVideo> 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<Breastpiece> 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<VideoFileInfoVo> findFile(int lLoginID, VideoFilesReq req) throws InterruptedException {
        List<VideoFileInfoVo> 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. código de transmisión

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<String,PipedOutputStream> 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<String,Integer> sessionMap = new HashMap<>(); //预览sessionID
    public static Map<String,Integer> 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<String,FPREVIEW_DATA_CB> 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<String> 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<String> 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();
        }

    }


}



Supongo que te gusta

Origin blog.csdn.net/Spring_possible/article/details/130593848
Recomendado
Clasificación