JavaCv对接海康、大华摄像头SDK取流并转推到RTMP服务器

这篇具有很好参考价值的文章主要介绍了JavaCv对接海康、大华摄像头SDK取流并转推到RTMP服务器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 前言

支持H265转H264编码
本文主要介绍海康、大华SDK取流推流过程,这里就不展示对接海康、大华SDK了
这个是重点 Native.setCallbackThreadInitializer(this, new CallbackThreadInitializer(true, false, "HikRealStream-" + RandomUtil.randomNumbers(8)));
增加回放流速度控制

2. 对接过程 以海康SDK取流推流为例

1. 引入JavaCv Maven依赖,按需引入

   		<!--JavaCV相关依赖-->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>1.5.9</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>6.0-1.5.9</version>
            <classifier>windows-x86_64-gpl</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>6.0-1.5.9</version>
            <classifier>linux-x86_64-gpl</classifier>
        </dependency>

2. 流处理类

/**
 * 推流处理
 *
 * @author lidaofu
 * @since 2023/11/10
 **/
@Slf4j
public class StreamPushHandle {

    private FFmpegFrameGrabber grabber = null;
    private FFmpegFrameRecorder recorder = null;
    private PipedOutputStream outputStream;
    private PipedInputStream inputStream;
    private String pushAddress;
    private SdkStreamClose streamClose;
    private Long handleId;
    private AVPacket avPacket = null;
    private Frame frame = null;
    private boolean isPlayBack=false;
    private double frameRate = 25.0;
 
    

    public StreamPushHandle(String pushAddress, SdkStreamClose streamClose, Boolean isPlayBack) {
        this.pushAddress = pushAddress;
        this.streamClose = streamClose;
        this.outputStream = new PipedOutputStream();
        this.inputStream = new PipedInputStream(256* 1024);
        this.isPlayBack = isPlayBack;
        try {
        	//建立管道连接
            inputStream.connect(outputStream);
        } catch (IOException e) {
            throw new FFmpegException("创建输入管道失败");
        }
    }
    
   /**
     * 设置播放句柄
     *
     * @param handleId
     */
    public void setHandleId(Long handleId) {
        this.handleId = handleId;
    }


     /**
     * 异步接收海康/大华/宇视设备sdk回调实时视频裸流数据
     */
    public void write(byte[] data,int dwBufSize) {
        try {
            outputStream.write(data, 0, dwBufSize);
        } catch (IOException e) {
            throw new FFmpegException("写入数据失败", e);
        }
    }



    /**
     * 推流
     */
    public void push() {
        try {
            FFmpegLogCallback.setLevel(avutil.AV_LOG_ERROR);
            grabber = new FFmpegFrameGrabber(inputStream, 0);
            //有些码率什么可以自己设置、不过没有必要
            grabber.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            // 设置读取的最大数据,单位字节 为了加快首播速度
            grabber.setOption("probesize", "8192");
            // 设置分析的最长时间,单位微秒 为了加快首播速度
            grabber.setOption("analyzeduration", "1000000");
            // 5秒超时 单位微秒
            grabber.setOption("stimeout", "5000000");
            // 5秒超时 单位微秒
            grabber.setOption("rw_timeout", "5000000");
            // 设置缓存大小,提高画质、减少卡顿花屏
            grabber.setOption("buffer_size", "1024000");
            grabber.start();
            // 部分监控设备流信息里携带的帧率为9000,如出现此问题,会导致dts、pts时间戳计算失败,播放器无法播放,故出现错误的帧率时,默认为25帧
            if (grabber.getFrameRate() > 0 && grabber.getFrameRate() < 100) {
                frameRate = grabber.getFrameRate();
            }
            recorder = new FFmpegFrameRecorder(pushAddress, grabber.getImageWidth(), grabber.getImageHeight());
            recorder.setFormat("flv");
            recorder.setInterleaved(true);
            recorder.setVideoOption("preset", "ultrafast");
            recorder.setVideoOption("tune", "zerolatency");
            recorder.setVideoOption("crf", "25");
            recorder.setSampleRate(grabber.getSampleRate());
            recorder.setFrameRate(grabber.getFrameRate());
            recorder.setVideoBitrate(grabber.getVideoBitrate());
            int videoIndex=0;
            AVFormatContext context = grabber.getFormatContext();
            for (int i = 0; i < context.nb_streams(); i++) {
                if (context.streams(i).codecpar().codec_type()==avutil.AVMEDIA_TYPE_VIDEO){
                    videoIndex=i;
                }
            }
            //需要等待的时间
            long waitTime = (long) (1000 / frameRate);
            //h264只需要转封装
            if (grabber.getVideoCodec() == avcodec.AV_CODEC_ID_H264) {
                recorder.start(context);
                 //初次执行时间
                long exStartTime = System.currentTimeMillis();
                while ((avPacket = grabber.grabPacket()) != null) {
                    recorder.recordPacket(avPacket);
                    //如果是回放则使用视频来控制流速度
                    if (isPlayBack && avPacket.stream_index() == videoIndex) {
                        long exEndTime = System.currentTimeMillis();
                        long diffTime = exEndTime - exStartTime;
                        //计算需要等待的时间
                        if (diffTime < waitTime) {
                            Thread.sleep(waitTime - diffTime);
                        }
                        exStartTime = System.currentTimeMillis();
                    }
                }
            } else {
                if (grabber.getAudioChannels() > 0) {
                    recorder.setAudioChannels(grabber.getAudioChannels());
                    recorder.setAudioBitrate(grabber.getAudioBitrate());
                    recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
                }
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                //使用libx264加速解码 注意需要使用gpl版的 不然还是默认是思科的h264编解码器
                recorder.setVideoCodecName("libx264");
                recorder.start();
                //初次执行时间
                long exStartTime = System.currentTimeMillis();
                while ((frame = grabber.grab()) != null) {
                    recorder.record(frame);
                    //如果是回放则使用视频来控制流速度
                    if (isPlayBack && frame.streamIndex == videoIndex) {
                        long exEndTime = System.currentTimeMillis();
                        long diffTime = exEndTime - exStartTime;
                        //计算需要等待的时间
                        if (diffTime < waitTime) {
                            Thread.sleep(waitTime - diffTime);
                        }
                        exStartTime = System.currentTimeMillis();
                    }
                }
            }
        } catch (Exception e) {
            log.warn("【FFMPEG】推送SDK流失败 推流地址:{}", pushAddress);
        } finally {
        	//回调SDK关流
            streamClose.closeStream(handleId);
            try {
                if (recorder != null) {
                    recorder.close();
                }
                if (grabber != null) {
                    grabber.close();
                }
            } catch (Exception e) {
                throw new FFmpegException("关闭取流器失败");
            }
        }
    }


}

3. 注册海康SDK取流回调函数

		//流处理器 HkSdkRequest::stopSdkPlay是关流回调函数的实现
	  	StreamPushHandle streamPushHandle = new StreamPushHandle(pushUrl, HkSdkRequest::stopSdkPlay,fasle);
	  	//SDK实时取流回调
        FRealDataCallBack fRealDataCallBack = new FRealDataCallBack(streamPushHandle);
        HCNetSDK.NET_DVR_PREVIEWINFO netDvrPreviewinfo = new HCNetSDK.NET_DVR_PREVIEWINFO();
        netDvrPreviewinfo.lChannel = channelId;
        netDvrPreviewinfo.dwStreamType = isMain ? 0 : 1;
        netDvrPreviewinfo.bBlocked = 0;
        netDvrPreviewinfo.dwLinkMode = 0;
        netDvrPreviewinfo.byProtoType = 0;
        //开启实时预览及设置回调函数
        long ret = HkSdkClientContext.HCNETSDK.NET_DVR_RealPlay_V40(userId, netDvrPreviewinfo, fRealDataCallBack, null);
        if (ret == -1) {
            log.error("【海康SDK】通过sdk播放视频失败! 错误码:{}", HkSdkClientContext.HCNETSDK.NET_DVR_GetLastError());
            return false;
        }
        //设置播放句柄 为了回调关流用的,懂得都懂
        streamPushHandle.setHandleId(ret);
        //我这里需要同时播放多台设备则需要添加回调函数到map中增加索引防止回调函数被gc回收 如果不需要同时预览可以使用全局final staic回调函数
        REAL_PLAY_STREAM_MAP.put(ret, fRealDataCallBack);

4. 取流回调函数

public class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {

    private StreamPushHandle streamPushHandle;

    private Boolean start = false;


    public FRealDataCallBack(StreamPushHandle streamPushHandle) {
    	//这里是关键,默认回调是一个数据包产生一个线程,因为管道的机制,第一次写入和读取的线程会和管道绑定,如果线程G了管道也会关闭会出现 Write end dead \ Pipe closed错误,所以设置一个线程回调解决这个错误问题,如果不想设置这里可以用队列来解决,这里不详细阐述
    	Native.setCallbackThreadInitializer(this, new CallbackThreadInitializer(true, false, "HikRealStream-" + RandomUtil.randomNumbers(8)));
        this.streamPushHandle = streamPushHandle;
    }

    /**
     * 这个回调方法会使用海康sdk申请的线程来调用 同一个设备也是由不同的线程来提调用
     */
    public void invoke(long lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) {
    	//混合码流
        if (dwDataType == HCNetSDK.NET_DVR_STREAMDATA) {
			 streamPushHandle.write(pBuffer.getByteArray(0L, dwBufSize), dwBufSize);
            if (!start) {
                start = true;
                //这里用的线程池 保证管道读取和写入分别用的是一个独立的线程
                FfmpegThreadPool.execute(streamPushHandle::push);
            }

        }
    }
}
/**
 *  sdk关流回调接口
 *
 * @author lidaofu
 * @since 2023/11/13
 **/
public interface SdkStreamClose {

	 /**
      *  当推流停止或者发生异常会调用此接口 此接口再去调用海康、大华的sdk关流接口
      * @param handle
      */
     void  closeStream(Long handle);
}

3. 小结

上面就是全部对接流程,可能会有问题,但不多,有问题可以wx联系我:L746101210 一起研究。文章来源地址https://www.toymoban.com/news/detail-787026.html

到了这里,关于JavaCv对接海康、大华摄像头SDK取流并转推到RTMP服务器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • JavaCV音视频开发宝典:使用JavaCV读取海康平台或海康网络摄像头sdk回调录像回放视频PS码流并解析预览图像

    JavaCV音视频开发宝典:使用JavaCV读取海康平台或海康网络摄像头sdk回调录像回放视频PS码流并解析预览图像

    《JavaCV音视频开发宝典》专栏目录导航 《JavaCV音视频开发宝典》专栏介绍和目录 ​ 上一章中《JavaCV音视频开发宝典:使用JavaCV读取海康平台或海康网络摄像头sdk回调视频TS流并解析预览图像》已经详细介绍了针对海康SDK实时视频流回调的TS流解析实现,并且也提到了PS流和

    2024年02月16日
    浏览(11)
  • 海康威视摄像头对接SDK实时预览功能和抓拍功能,懒癌福利,可直接CV

    海康威视摄像头对接SDK实时预览功能和抓拍功能,懒癌福利,可直接CV

    最近在新系统的研发中负责了视频监控模块的开发,项目监控设备全部采用海康的摄像头,枪机、球机都有,开发的过程中,有个需求是在前端页面上把摄像头画面进行平铺展示,最开始的方案是通过官方API完成,但是后面发现项目上所有的设备都是不联网的,所以只能转由

    2024年02月02日
    浏览(13)
  • 大华摄像头有问题,海康摄像头也有问题

    买了个大华摄像头,除了抗噪方面效果不好,我是很满意的。前一段时间摄像头启动出了点问题(忘记拔掉SD卡),于是买了个海康的。 大华摄像头是3寸,海康是2寸。视频效果差多了。看来大有大的道理。 更可恨的是,萤石云不支持这个摄像头(说是要插录像机)。买的时

    2024年02月11日
    浏览(13)
  • Java对接大华云睿摄像头(人流统计/客流量统计)

    Java对接大华云睿摄像头(人流统计/客流量统计)

    环境准备(注意区分开发环境和正式服务器环境) 开发环境 win11 + IDEA + spring boot + mysql 正式服务器环境 Linux 下载开发环境和正式环境的SDK SDK下载地址:https://support.dahuatech.com/tools/sdkExploit ** 注意选择对应的语言和操作系统 ** 打开下载的(win64)SDK,查看人流统计部分实现逻

    2024年02月01日
    浏览(10)
  • 海康、大华、tplink监控摄像头和硬盘录像机接入GB28181平台配置细节

    海康、大华、tplink监控摄像头和硬盘录像机接入GB28181平台配置细节

    海康、大华、tplink等各种型号监控摄像头或硬盘录像机(NVR/HVR)接入GB28181平台,配置过程都非常简单明了,但有些细节需要注意,避免走弯路踩泥坑。 首先要说明一点的是,只要监控设备和GB28181平台的网络是连通的,都可以顺畅的接入,不需要为监控设备配置外网地址,更不

    2024年02月07日
    浏览(11)
  • 大华SDK+JAVA+4g网络摄像头进行二次开发

    大华SDK+JAVA+4g网络摄像头进行二次开发

    监控,相信大家都不陌生。现在的监控技术发展迅速,国内以海康威视为首的智能视频监控提供商也层出不穷。现在,这些提供商都已经提供了相应的SDK以及API接口,能够很好的支撑我们进行摄像机的二次开发工作。相信大家都有接触过这么一个需求:利用手机可以自己进行

    2024年01月20日
    浏览(13)
  • 【国标语音对讲】EasyCVR视频汇聚平台海康/大华/宇视摄像头GB28181语音对讲配置

    【国标语音对讲】EasyCVR视频汇聚平台海康/大华/宇视摄像头GB28181语音对讲配置

    近年来,国内视频监控应用发展迅猛,系统接入规模不断扩大,涌现了大量平台提供商,平台提供商的接入协议各不相同,终端制造商需要给每款终端维护提供各种不同平台的软件版本,造成了极大的资源浪费。各地视频大规模建设后,省级、国家级集中调阅,对重特大事件

    2024年04月27日
    浏览(43)
  • 基于海康SDK实现Python调用海康威视网络摄像头

    基于海康SDK实现Python调用海康威视网络摄像头

    本文参考博客,写得很好: Python调用海康威视网络相机之——python调用海康威视C++的SDK Python调用海康威视网络相机C++的SDK 写本文的目的,也是快速复盘,所以没有很详细 保存视频流到本地可参考下一篇:基于海康SDK实现Python保存海康威视网络摄像头拍摄的视频 Windows11 Vis

    2024年02月02日
    浏览(29)
  • vue对接海康摄像头,使用hk3.3(硬盘录像机)开发摄像头分屏翻页操作。

    默认展示4*4规格,分屏之后自己写翻页方法,对摄像头一一展示(1x1, 2x2, 3x3, 4x4),有俩个场景。 1、刚开始默认展示的时候进行分页。 2、点击分屏操作之后进行分页。 思路: 1、拿到所有的通道号,比如有[1, 2, 3…, 100]; 2、 点击分屏之后就会把这个数组变为 1*1[[1], [

    2024年02月13日
    浏览(12)
  • vue2 对接 海康摄像头插件 (视频WEB插件 V1.5.2)

    vue2 对接 海康摄像头插件 (视频WEB插件 V1.5.2)

    前言 海康视频插件v.1.5.2版本运行环境需要安装插件VideoWebPlugin.exe,对浏览器也有兼容性要求,具体看官方文档 对应下载插件 去海康官网下载插件 里面有dome等其他需要用到的 地址: 安装插件 打开下载的文件里的bin文件 安装一下VideoWebPlugin vue脚手架中集成插件 把官方资源

    2024年02月03日
    浏览(14)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包