别问我为什么要用java调用ffmpeg处理视频。主要是需要按需压缩,按需压缩。
1.为什么要压缩?
有几百路摄像头,摄像头的数据都是高清分辨率,达到了惊人的4096x2160,帧分辨率25。需要在页面同时展示4路,每路4m,一个页面需要同时占用16m。
其次客户端播放窗口只有400像素,通过客户端压缩,页面非常卡顿,一顿一顿的。
为什么使用ffmpeg压缩,ffmpeg是c++写的相对来说效率高,而且支持拉流。
于是想到的方法是按需压缩。
之前只有8路摄像头, 方案简单粗暴, 服务器实时拉流,处理后放在服务器等待客户端播放。但是大部分时间,浏览器不需要看摄像头,偶尔看一下,虽然是内网处理,但是仍然浪费了服务器资源和带宽。
最进突然要接入几百路摄像头,于是想到了按需压缩。打开摄像头看视频的时候再拉流压缩。
按需压缩就是等浏览器像服务器发起请求的时候,再拉流压缩。考虑到源摄像头有可能是坏的,第一次返回404给客户端。客户端发起重试,返回压流后的流媒体。
调研了前端播放器 videojs,支持重试,于是服务器开工。
服务器想到的使用java需要多线程处理。一个线程负责监控客户端播放请求,一个线程负责心跳检查。
程序逻辑很简单。 当有请求来的时候,调起拉流压流daemon,当然还需要溢出判断,统一服务器限制不能超过50路,50路服务器处理不过来,处理视频太耗资源了。还有就是锁机制,存在n多客户端,同时播放的需求,实际只需要压缩1路即可,其他客户端共用这一路,用memcached或者redis 解决这些很简单。对于架构来说,越简单,越可靠,为此引入memcached或者redis个人觉得性能和维护成本 ,都不值得,于是就在java程序里面实现了。
另外一个心跳检查,就是检查上面那个线程的数据,超过一定时间关闭拉流压缩,节省服务器资源。
代码写好后,一切都按照预想的成果,开心而又愉快的运行。为了避免手工操作,写了一个定时脚本,监控并启动 这个java程序,昨天晚上启动,今天早上到公司一看,芭比Q了,java程序在运行,视频播放不出来。。。。
杀掉进程在xshell里面启动java 程序,播放正常。 但是通过crond 自动启动脚本,大约过2分钟,某一路视频无法播放。以为是心跳检查程序把 进程杀掉了。
然后又重写代码,把进程和超时队列单独分开,也没有解决,又重新封装线程,还是这样。
在xshell里面为什么正常,很奇怪。 于是写了一个简单的daemon 程序,发现正常了。唯一的区别就是 ffmpeg 执行的时候,会输出很多消息,而且java调用c++程序,c++不能挂在后台,我强制在ffmpeg程序后面加& ,ffmpeg 被java终止了。
找到问题就好解决了,把ffmpeg放在sh脚本里面,通过shell脚本把所有的输出 输出到null终端,ffmpeg */hls/aa >/dev/null 2>&1
原因就是: 输出流会阻塞进程执行。
Java进程执行有一个输入流,两个输出流(相对于外部程序)。当两个输出流有内容输出,而Java执行程序没有及时清空输出流时就会阻塞进程。
java下是通过下面的方式调用的。
今天晚上,再跑一个晚上,希望程序正常,程序正常,还需要播放器封装,还需要部署流媒体服务器和压流的代码 脚本和文档。
今天晚上,跑一个晚上,明天不正常,估计五一都没法安心的过了。经过改造后,不会因为网络播放卡,消耗带宽高,视频不清晰(浏览器压缩导致),播放不流畅了,也不会因为网络中断,导致延时过大了。
单路从4m压缩到了400k,带宽缩小10倍,希望正常,不然劳动白费,也对不起我掉的头发啊。