海康sdk捕获码流数据通过JavaCV推成rtmp流的实现思路(PS流转封装RTMP)

码云(Gitee)主页:https://gitee.com/banmajio
github主页:https://github.com/banmajio
个人博客:banmajio’s blog

海康sdk二次开发系列文章
海康sdk捕获码流数据通过JavaCV推成rtmp流的实现思路(PS流转封装RTMP)
海康sdk进行历史回放时,码流数据回调过快问题的解决方法
海康sdk项目(java)部署Linux环境相关问题总结
海康sdk部署Linux环境下无法播放子码流的问题
海康sdk项目部署Linux系统时出现java.lang.UnstisfiedLinkError:jnidispatch(xxx)not found in resource path错误

浏览器不支持flash插件之后,h5播放rtmp直播流的解决方案

【注意】:对于直播来说,可以采用rtsp-rtmp这种方案来实现。但是对于历史回放功能,采用rtsp指令带starttime和endtime参数来进行回放时,因为nvr带宽限制,会出现带宽不足的错误。所以目前没有想到更好的解决方案。故采用海康sdk二次开发,捕获到直播流或者历史流数据,自己转封装推成rtmp。

2020-12-17 修改升级
1.2020-11-17新增的内容中,回放的倍速暂停抓图拖动功能都是基于rtmp的形式做的。因为海康sdk历史录像捕获时是设备全速发送数据,需要自己控制推流速度,使推流速度和播放速度一致才能通过服务精准的做倍速和暂停恢复抓图的功能。但是由于播放速度多多少少会比推流速度慢一点,因此抓图等功能在请求接口后,播放器会有一些延迟。
2.因为上述不够完善,所以目前回放改为hls切片,抓图,倍速,暂停恢复交由客户端(播放器)去做,就可以保证时间点的精准。

2020-11-17 新增
1.抓图接口
2.倍速播放(0.25;0.5;1;2;4;8;16)
3.回放暂停与恢复
4.下载指定时间内的录像文件
5.查询指定时间点内的nvr录像文件列表
6.回放拖动进度条
考虑到开发成本,暂不准备开源,有需要demo的请加Q:1402325991 联系!(收费)

问题分析

通过海康sdk注册回调函数,可以捕获到视频的码流数据。但是因为海康sdk回调的码流数据是ps封装的h264的码流数据,也就是说通过海康sdk可以得到视频的ps流

转码推rtmp

最开始的时候,找到一个demo,是将海康sdk回调函数中将码流数据的byte[]—>BytePointer—>Mat,然后又通过opencv_imgproc.cvtColor()将yv12的Mat转为rgb的Mat,接着将rgb的Mat转为了Frame帧,最后通过FFmpegFrameRecorder.record(Frame)将帧推送到rtmp地址上。虽然这种方式可以实现我们的需求,但是带来的问题是,cpu占用率极高。大概推三路cpu就占满了。查看JavaCV源码发现FFmpegFrameRecorder.record(Frame)方法会对帧进行编解码的动作,然后将Frame转换为AVPacket。CPU高的原因也正是消耗在了编解码的地方。

	this.bPointer = new BytePointer(frameBean.getBuffer().length);
	this.yv12Mat = new Mat(height + height / 2, width, CV_8UC1);
	this.rgbMat = new Mat(height, width, CV_8UC3);
	if (this.converter == null) {
		this.converter = new ToIplImage();
	}
	if (this.matConverter == null) {
		this.matConverter = new ToMat();
	}
	// 图像转码-----↓
	// 填充指针
	this.bPointer.put(frameBean.getBuffer());
	// mat填充
	this.yv12Mat.data(this.bPointer);
	// 转码opencv实现方式
	opencv_imgproc.cvtColor(this.yv12Mat, this.rgbMat, opencv_imgproc.COLOR_YUV2BGR_YV12);
	// 转换为帧
	this.matFrame = this.matConverter.convert(this.rgbMat);
	// 图像转码-----↑
	try {
		this.recorder.record(this.matFrame);
	} catch (Exception e) {
		e.printStackTrace();
	}

PS流转封装

后来了解到,Javacv是可以将PS流转封装为flv格式推到rtmp的。具体的实现思路就是通过Java的管道流,将sdk回调函数中获得的码流数据写入PipedOutputStream中,然后将对应的PipedInputStream当做参数传入到FFmpegFrameGrabber的构造方法中。其余的操作和拉rtsp流推rtmp流大体类似。可以参考JavaCV转封装rtsp到rtmp(无需转码,低资源消耗)
其中要注意的几点就是:
1.管道流PipedInputStream,PipedOutputStream不可以在同一线程下使用否则会造成死锁。
2.管道流是一种阻塞流,PipedOutputStream.write(byte[])会将数据放到PipedInputStream的缓冲区中,当PipedInputStream将这部分数据read()出去后,PipedOutputStream才会继续write。这个缓冲区的大小默认值时1024。也可以自己手动通过下面的这种方式指定缓冲区大小。

	PipedInputStream inputStream = new PipedInputStream(5120);

3.管道流PipedInputStream,PipedOutputStream成对出现,需要将两者建立连接才能正常工作。建立连接有以下两种方式:

	//第一种方式
	PipedInputStream inputStream = new PipedInputStream();
	PipedOutputStream outputStream = new PipedOutputStream(inputStream);

	//第二种方式
	PipedInputStream inputStream = new PipedInputStream();
	PipedOutputStream outputStream = new PipedOutputStream();
	inputStream.connect(outputStream);

4.推流方式和rtsp推流方式几乎相同

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐