如题,Java如何通过官方SDK接口获取大华监控的实时视频流帧,有偿
一般大华的sdk有两种方式,一种是把窗口句柄直接传给sdk,画面直接显示,不用管解码显示,最为方便;另外一种是通过注册回调方法,返回数据流,sdk demo里面均有实现,见General_NetSDK_ChnEng_JAVA_Win64_IS_V3.052.0000002.0.R.201103中RealplayEx:
public void realplay(){ lRealHandle= netSdk.CLIENT_RealPlayEx(loginHandle, 0, null, 0); if(lRealHandle.longValue()!=0){ System.out.println("realplay success"); netSdk.CLIENT_SetRealDataCallBackEx(lRealHandle, CbfRealDataCallBackEx.getInstance(),null, 31); } }
/** * 实时监视数据回调函数--扩展(pBuffer内存由SDK内部申请释放) */ private static class CbfRealDataCallBackEx implements NetSDKLib.fRealDataCallBackEx { private CbfRealDataCallBackEx() { } private static class CallBackHolder { private static CbfRealDataCallBackEx instance = new CbfRealDataCallBackEx(); } public static CbfRealDataCallBackEx getInstance() { return CallBackHolder.instance; } @Override public void invoke(LLong lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, int param, Pointer dwUser) { int bInput=0; if(0 != lRealHandle.longValue()) { switch(dwDataType) { case 0: System.out.println("码流大小为" + dwBufSize + "\n" + "码流类型为原始音视频混合数据"); break; case 1: //标准视频数据 break; case 2: //yuv 数据 break; case 3: //pcm 音频数据 break; case 4: //原始音频数据 break; default: break; } } } }
pBuffer 和 dwBufSize
@Override public void invoke(NetSDKLib.LLong lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, int param, Pointer dwUser) { if(0 != lRealHandle.longValue()) { switch(dwDataType) { case 0:
```
```// 原始数据 break; case 1: break; case 2: // yuv 数据 break; case 3: // pcm 音频数据 break; case 4: // 原始音频数据 break; default: // long before = device.getKEEP_ALIVE(); // boolean onLine = new Date().before(new Date(before + Contacts.KEEP_LIVE)); // if ( ! onLine ){ // boolean bRet = LoginModule.netsdk.CLIENT_StopRealPlayEx(lRealHandle); // if(bRet) { // device.getRecordVideo().isExit = true; // log.info("心跳停止,实时预览关闭"); // lRealHandle.setValue(0); // } // } // h264裸码流 if (dwDataType == 1004) { try { this.getOutputStream().write( pBuffer.getByteArray(0,dwBufSize),0,dwBufSize); this.getOutputStream().flush(); } catch (Exception e) { log.error( "管道 h264裸码流 写入失败{}",e.getMessage()); } // } else if (dwDataType == 1005) { // flv流 } break; } } } boolean isConnect = false;
/**
* 管道流连接和推流操作
* TODO: 尝试重新连接和推流操作
*/
public void connetAndPush(String rtmpPath, CameraContext device){
isConnect = false;
try {
outputStream.connect(inputStream);
new Thread(()->{
while (!isConnect){
try {
// Device device = AutoRegister.getDevice(lLoginID); // String rtmpPath = ConfigInfo.getRtpmUrl() + device.getUniCam().getCamId(); // RecordVideo.push(rtmpPath,inputStream,1); log.info("推流地址{}",rtmpPath); // 判断是否推流连接成功 isConnect = true; RecordVideo recordVideo = new RecordVideo(); device.setRecordVideo(recordVideo); recordVideo = recordVideo.fromSources(inputStream).to(rtmpPath,1); recordVideo.record();
byteArrayOS.close();
inputStream.close();
log.info("设备{}因断线,拉流推流关闭",device.getUniCam().getCamId());
} catch (Exception e) {
isConnect = false;
log.error("连接失败,尝试重新连接{}",e.getMessage());
}
}
}).start();
} catch (Exception e) {
log.error( "管道流连接失败{}",e.getMessage());
}
}
public void startByteArrayReaderThread(){
new Thread(() -> {
try {
while (keepRunning) {
// 检查流里面的字节数
if (byteArrayOS.size() > 0) {
byte[] buffer = null;
synchronized (byteArrayOS) {
buffer = byteArrayOS.toByteArray();
byteArrayOS.reset(); // 清除缓冲区
}
// 把提取到的数据发送给PipedOutputStream
outputStream.write(buffer, 0, buffer.length);
} else {
// 没有数据可用,线程进入睡眠状态
// 每隔1秒查看ByteArrayOutputStream检查新数据
Thread.sleep(1000);
}
}
}catch(Exception e) {
// 记录错误或其他处理
log.warn("管道流已关闭{}",e.getMessage());
}
}).start();
}
public OutputStream getOutputStream() {
return byteArrayOS;
}
// 管道流通道
private final PipedOutputStream outputStream = new PipedOutputStream();
private boolean keepRunning = true;
private final ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream() {
public void close() {
keepRunning = false;
try {
super.close();
outputStream.close();
} catch(IOException e) {
// 记录错误或其他处理
log.error( "output流关闭失败 {}",e.getMessage());
}
}
};
private final PipedInputStream inputStream = new PipedInputStream(1024 << 2){
public void close() {
keepRunning = false;
try {
super.close();
} catch(IOException e) {
// 记录错误或其他处理
log.error( "output流关闭失败 写入失败{}",e.getMessage());
}
}
};
卧槽,乱码了?不知道咋整理。复制出来整理一下
可以参考一下这个博主写的思路实现https://blog.csdn.net/zb95731/article/details/114282803
但是我也卡住一个问题了,就是推流的延时很高10s左右去了,应该是个优化问题