1.实时视频流解决方案

目录

1.实时视频流解决方案

2.步骤

1.搭建rtmp+flv服务器

2.java预览demo

3.实时预览

1.配置海康sdk库文件

2.修改FPREVIEW_NEWLINK_CB代码,推流

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版本的。

2.修改FPREVIEW_NEWLINK_CB代码,推流

每当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;}//实时流回调函数/@Overridepublic 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.bytedecojavacpp1.5.7org.bytedecoopencv4.5.5-1.5.7linux-x86_64runtimeorg.bytedecoopenblas0.3.19-1.5.7linux-x86_64runtimeorg.bytedecoffmpeg5.0-1.5.7linux-x86_64runtimeorg.bytedecoopencv4.5.5-1.5.7windows-x86_64testorg.bytedecoopenblas0.3.19-1.5.7windows-x86_64testorg.bytedecoffmpeg5.0-1.5.7windows-x86_64testorg.bytedecojavacv1.5.7org.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码流的MP4FFmpegFrameGrabber 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++;}// 取出的每一帧,都推送到SRSrecorder.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;@AutowiredRedisUtil redisUtil;@AutowiredBreastpieceMapper breastpieceMapper;@AutowiredTaskDetailMapper taskDetailMapper;@AutowiredBreastpieceVideoMapper breastpieceVideoMapper;@Overridepublic 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 */@Slf4jpublic 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版本的sdkHCISUPCMS.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;@Overridepublic 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 && 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;@Slf4jpublic 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(); //预览sessionIDpublic static Map playHandleMap = new HashMap();static int backSessionID = -1;//回放sessionIDstatic 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-1m_lPlayBackListenHandle = hCEhomeStream.NET_ESTREAM_StartListenPlayBack(param);if (m_lPlayBackListenHandle  {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;}//实时流回调函数/@Overridepublic 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;//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 {@Overridepublic 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 {//实时流回调函数@Overridepublic 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 = 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 blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.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 blocke.printStackTrace();}catch (IOException e) {// TODO Auto-generated catch blocke.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();}}}