java接入海康威视摄像头sdk后,如何推流给前端?

海康提供的demo用的Java Swing开发的GUI程序,怎么把这个视频转到前端vue项目,后端是怎么生成流地址?


之前贴的代码我删了,我重新整理了一下,有懂得大佬来指导一下
开发背景:目前开发环境是摄像头直接连接我本地电脑做开发,springboot框架,不走云视频,本地调海康的SDK得到视频流
我的想法:本地搭建一个流媒体服务ZLMediaKit,海康回调的视频流我通过java代码推到这个流媒体服务上,然后前端vue再去从这个流媒体服务上的rtsp地址直接拉流?
下面是业务类,项目启动时调用,ClientHikVision是从海康demo整理出来的

@Service

public class EquipmentHikVisionServiceImpl implements EquipmentHikVisionService {

@Override

@PostConstruct

public void register() {

ClientHikVision clientHikVision = new ClientHikVision();

clientHikVision.initPipedStream();

clientHikVision.clientInit();

clientHikVision.action();

}

}

ClientHikVision太长了,我贴出部分,这里基本都是初始化,连接设备都没问题

public class ClientHikVision {

ExecutorService executor = Executors.newFixedThreadPool(5);

FFmpegFrameGrabber grabber = null;

FFmpegFrameRecorder recorder = null;

PipedInputStream inputStream = new PipedInputStream();

PipedOutputStream outputStream = new PipedOutputStream();

String pushAddress = "rtsp://127.0.0.1:554/live/1";

public void initData(){

m_lPort= new IntByReference(-1);

fRealDataCallBack = new FRealDataCallBack();

fExceptionCallBack = new FExceptionCallBack_Imp();

}

public void initPipedStream(){

// inputStream = new PipedInputStream();

// outputStream = new PipedOutputStream();

try {

inputStream.connect(outputStream);

System.out.println("Piped设置连接成功");

} catch (IOException e) {

throw new RuntimeException(e);

}

}

public void clientInit() {

if (hCNetSDK == null && playControl == null) {

if (!CreateSDKInstance()) {

System.out.println("Load SDK fail");

return;

}

if (!CreatePlayInstance()) {

System.out.println("Load PlayCtrl fail");

return;

}

}

//linux系统建议调用以下接口加载组件库

if (osSelect.isLinux()) {

HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256);

HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256);

//这里是库的绝对路径,请根据实际情况修改,注意改路径必须有访问权限

String strPath1 = System.getProperty("user.dir") + "/lib/libcrypto.so.1.1";

String strPath2 = System.getProperty("user.dir") + "/lib/libssl.so.1.1";

System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length());

ptrByteArray1.write();

hCNetSDK.NET_DVR_SetSDKInitCfg(3, ptrByteArray1.getPointer());

System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length());

ptrByteArray2.write();

hCNetSDK.NET_DVR_SetSDKInitCfg(4, ptrByteArray2.getPointer());

String strPathCom = System.getProperty("user.dir") + "/lib/";

HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH();

System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length());

struComPath.write();

hCNetSDK.NET_DVR_SetSDKInitCfg(2, struComPath.getPointer());

}

boolean initSuc = hCNetSDK.NET_DVR_Init();

if (!initSuc) {

throw new CommonException(9001, "初始化失败");

}

if (fExceptionCallBack == null) {

fExceptionCallBack = new FExceptionCallBack_Imp();

}

Pointer pUser = null;

if (!hCNetSDK.NET_DVR_SetExceptionCallBack_V30(0, 0, fExceptionCallBack, pUser)) {

return;

}

System.out.println("设置告警回调成功");

hCNetSDK.NET_DVR_SetLogToFile(3, "./sdklog", false);

register();

}

public void register() {

//注册之前先注销已注册的用户,预览情况下不可注销

if (bRealPlay) {

throw new CommonException(9001, "注册新用户请先停止当前预览!");

}

if (lUserID > -1) {

//先注销

hCNetSDK.NET_DVR_Logout_V30(lUserID);

lUserID = -1;

}

//注册

m_sDeviceIP = EquipmentConstants.IP;//设备ip地址

m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30();

int iPort = EquipmentConstants.PORT;

lUserID = hCNetSDK.NET_DVR_Login_V30(m_sDeviceIP,

(short) iPort, EquipmentConstants.ACCOUNT, EquipmentConstants.PSW, m_strDeviceInfo);

long userID = lUserID;

if (userID == -1) {

m_sDeviceIP = "";//登录未成功,IP置为空

int error;

error = hCNetSDK.NET_DVR_GetLastError();

throw new CommonException(9001, "注册失败,错误码:" + error);

} else {

initData();

}

}

public void action(){

if (lUserID == -1) {

throw new CommonException(9001,"请先注册");

}

//如果预览窗口没打开,不在预览

if (bRealPlay == false) {

//获取窗口句柄

// W32API.HWND hwnd = new W32API.HWND(Native.getComponentPointer(panelRealplay));

//获取通道号

int iChannelNum = 1;//通道号

// m_strClientInfo = new HCNetSDK.NET_DVR_CLIENTINFO();

// m_strClientInfo.lChannel = new NativeLong(iChannelNum);

HCNetSDK.NET_DVR_PREVIEWINFO strClientInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();

strClientInfo.read();

// strClientInfo.hPlayWnd = null; //窗口句柄,从回调取流不显示一般设置为空

strClientInfo.lChannel = iChannelNum; //通道号

strClientInfo.dwStreamType=0; //0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推

strClientInfo.dwLinkMode=0; //连接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4- RTP/RTSP,5- RTP/HTTP,6- HRUDP(可靠传输) ,7- RTSP/HTTPS,8- NPQ

strClientInfo.bBlocked=1; //0- 非阻塞取流,1- 阻塞取流

//在此判断是否回调预览,0,不回调 1 回调

//回调------------

strClientInfo.hPlayWnd = null;

strClientInfo.write();

lPreviewHandle = hCNetSDK.NET_DVR_RealPlay_V40(lUserID,

strClientInfo, fRealDataCallBack, null);

if (lPreviewHandle<=-1)

{

int error;

error=hCNetSDK.NET_DVR_GetLastError();

throw new CommonException(9001,"预览失败,错误码:"+error);

}

//------------

long previewSucValue = lPreviewHandle;

//预览失败时:

if (previewSucValue == -1) {

int error;

error=hCNetSDK.NET_DVR_GetLastError();

throw new CommonException(9001,"预览失败,错误码:"+error);

}

//预览成功的操作

bRealPlay = true;

//显示云台控制窗口

}

//如果在预览,停止预览,关闭窗口

else {

hCNetSDK.NET_DVR_StopRealPlay(lPreviewHandle);

bRealPlay = false;

if (m_lPort.getValue() != -1) {

playControl.PlayM4_Stop(m_lPort.getValue());

m_lPort.setValue(-1);

}

panelRealplay.repaint();

}

}

下面是海康demo里预览回调的方法,这个方法也在ClientHikVision类里,我单独贴出来了,System.out.println(Arrays.toString(bytes));这行也打印出来了,这个应该是原始的流数据吧

/******************************************************************************

* 内部类: FRealDataCallBack

* 实现预览回调数据

******************************************************************************/

class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {

//预览回调

public void invoke(int lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) {

// W32API.HWND hwnd = new W32API.HWND(Native.getComponentPointer(panelRealplay));

// System.out.println("【dwDataType:】"+dwDataType);

if (dwDataType == HCNetSDK.NET_DVR_STREAMDATA) { //码流数据

// System.out.println("【dwBufSize:】"+dwBufSize);

if (dwBufSize > 0) {

long offset = 0;

ByteBuffer buffers = pBuffer.getPointer().getByteBuffer(offset, dwBufSize);

byte[] bytes = new byte[dwBufSize];

buffers.rewind();

buffers.get(bytes);

System.out.println(Arrays.toString(bytes));

executor.execute(() -> {

push(bytes,dwBufSize);

});

// if (!playControl.PlayM4_InputData(m_lPort.getValue(), pBuffer, dwBufSize)) //输入流数据

// {

// break;

// }

}

}

}

}

java接入海康威视摄像头sdk后,如何推流给前端?
上面的数据流写入到管道里,后面代码我不知道写的对不对,走了一个handle方法,grabber = new FFmpegFrameGrabber(inputStream,0),这里应该是消费管道中的数据流。这里用的ZLMediaKit做流媒体服务,我已经编译好启动了,但是不知道该怎么用,这个pushAddress地址我不知道写的对不对。里面的grabber.startUnsafe()方法每次调用都会阻塞,本来是grabber.start()方法的,两种方式都会阻塞,

public void push(byte[] data, int size) {

try {

outputStream.write(data, 0, size);

handle();

} catch (IOException | InterruptedException e) {

throw new RuntimeException(e);

}

}

public void handle() throws InterruptedException, IOException {

System.out.println("-----开始处理流数据-----");

grabber = new FFmpegFrameGrabber(inputStream,0);

grabber.setOption("rtsp_transport", "tcp");

grabber.setOption("stimeout", "2000000");

grabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);

grabber.setVideoCodec(avcodec.AV_CODEC_ID_H264);

grabber.setAudioStream(Integer.MIN_VALUE);

grabber.setFormat("mpeg");

long stime = System.currentTimeMillis();

System.out.println("------1111111111------");

System.out.println(inputStream.available());

// 检测回调函数书否有数据流产生,防止avformat_open_input函数阻塞

do {

Thread.sleep(100);

if (System.currentTimeMillis() - stime > 2000) {

System.out.println(("-----SDK回调无视频流产生------"));

return;

}

}while (inputStream.available() <= 0);

System.out.println("------222222222------");

// do {

// Thread.sleep(100);

// if (System.currentTimeMillis() - stime > 2000) {

// System.out.println(("-----SDK回调无视频流产生------"));

// return;

// }

// } while (inputStream.available() != 2048);

// 只打印错误日志

avutil.av_log_set_level(avutil.AV_LOG_QUIET);

FFmpegLogCallback.set();

System.out.println(grabber.toString());

grabber.startUnsafe();

System.out.println(("--------开始推送视频流---------"));

recorder = new FFmpegFrameRecorder(pushAddress, grabber.getImageWidth(),grabber.getImageHeight(), grabber.getAudioChannels());

recorder.setInterleaved(true);

// 画质参数

recorder.setVideoOption("crf", "28");

// H264编/解码器

recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);

recorder.setVideoBitrate(grabber.getVideoBitrate());

// 封装flv格式

recorder.setFormat("flv");

// 视频帧率,最低保证25

recorder.setFrameRate(25);

// 关键帧间隔 一般与帧率相同或者是帧率的两倍

recorder.setGopSize(50);

// yuv420p

recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);

recorder.startUnsafe();

int count = 0;

Frame frame;

while (grabber.hasVideo() && (frame = grabber.grab()) != null) {

count++;

if (count % 100 == 0) {

System.out.println("推送视频帧次数:"+count);

}

if (frame.samples != null) {

System.out.println("检测到音频");

}

recorder.record(frame);

}

if (grabber != null) {

grabber.stop();

grabber.release();

}

if (recorder != null) {

recorder.stop();

recorder.release();

}

}

java接入海康威视摄像头sdk后,如何推流给前端?
System.out.println(grabber.toString());grabber.startUnsafe();这两行代码都走了,但是到这就停住阻塞不动了,麻烦大佬帮忙看下
java接入海康威视摄像头sdk后,如何推流给前端?

参考资料 转:
大华、海康SDK对接,使用javacv+流媒体服务实现实时播放和回放
JavaCV中FFmpegFrameGrabber调用start()方法时出现阻塞的解决办法


回答:

海康一般都是rtsp的流,后端需要用ffmpeg进行转流为flv格式推给前端


回答:

播放这种功能不都是前台直接对接的吗,从后台转一下的意义是啥


回答:

我也和你一样,我现在也只是拿到了byte[] 但是我不知道该怎么转成前端可以用的RTSP格式 这个怎么弄啊


回答:

前端请求接口,接口使用异步方式不结束,然后使用FFmpegFrameGrabber读取RTSP,结果交给FFmpegFrameRecorder。FFmpegFrameRecorder转码成flv,结果交给下面的类,下面的类会解析byte数组,将flv包源源不断写给响应流,就像文件下载形式一样,前端flv播放器拿到flv包就会解析播放,斗鱼虎牙都是http-flv这种方案。
java接入海康威视摄像头sdk后,如何推流给前端?
java接入海康威视摄像头sdk后,如何推流给前端?

以上是 java接入海康威视摄像头sdk后,如何推流给前端? 的全部内容, 来源链接: utcz.com/p/935312.html

回到顶部