热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java实现rstp格式转换使用ffmpeg实现linux命令第一步安装node.js和ffmpeg第二步搭建node.js启动websocket接收服务

java实现rstp格式转换使用ffmpeg实现linux命令第一步安装node.js和ffmpeg第二步搭建node.js启动websocket接收服务第三步java实现


java实现rstp格式转换使用ffmpeg实现linux命令

    • 第一步安装node.js和ffmpeg
    • 第二步搭建node.js启动websocket接收服务
    • 第三步java实现(启动并挂起转换rtsp流)


第一步安装node.js和ffmpeg

安装可以参考网上的这里不一一介绍了

第二步搭建node.js启动websocket接收服务

部分代码:(要完整代码请关注我并私信给我)

var fs = require('fs');
var http = require('http');
var WebSocket &#61; require(&#39;ws&#39;);if (process.argv.length < 3) {console.log(&#39;输入正确参数&#39;);process.exit();
}var stream_secret &#61; process.argv[2];//密码
var stream_port &#61; process.argv[3] || 8081;//ffpeng推送端口
var websocket_port &#61; process.argv[4] || 8082;//前端websocket端口 &#xff0c;比如&#xff1a;8082
var record_stream &#61; false;
var totalSize &#61; 0;

第三步java实现&#xff08;启动并挂起转换rtsp流&#xff09;

首先我们新建一个java项目&#xff0c;可以是普通的java&#xff0c;我这里新建的是springboot看起来比较高大上。


  1. 新建springboot项目
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述next下一步到这里项目新建好了&#xff1b;
  2. 上代码

public class FfmpegTest {//自带map缓存&#xff0c;防止同一个设备机器多次转换导致视频丢帧public static Map<String , Integer> map &#61; new HashMap<>();//线程池&#xff0c;因为开启转换是持续的多线程开启保证主线程不被阻塞ExecutorService es3 &#61; Executors.newCachedThreadPool();&#64;RequestMapping(value &#61; "/hello/{code}",method &#61; RequestMethod.GET)public String test(&#64;PathVariable String code, HttpServletRequest request){//获取IP地址String ipAddress &#61; IpUtil.getIpAddr(request);System.out.println("客户端ip:"&#43;ipAddress);//转换rtsp流String ffmpeg &#61; ffmpeg(code);//获取pidString pid &#61; getPid(code);System.out.println(ffmpeg&#43;"进程号是:"&#43;pid);return ffmpeg&#43;"进程号是:"&#43;pid;}&#64;RequestMapping(value &#61; "/kill/{code}",method &#61; RequestMethod.GET)public String kill(&#64;PathVariable String code){String pid &#61; getPid(code);System.out.println(code&#43;"视频设备进程号是:"&#43;pid);String s &#61; killPid(pid , code);return code&#43;"视频设备进程号是:"&#43;pid&#43;" "&#43;s;}public String ffmpeg(String code) {String returnstr&#61;"";String s&#61;"";List<String> commend &#61; new ArrayList<String>();commend.add("ffmpeg");commend.add("-i");commend.add("rtsp://用户:摄像头密码&#64;192.168.1."&#43;code&#43;":554/h264/ch1/sub/av_stream");commend.add("-q");commend.add("0");commend.add("-f mpegts -codec:v mpeg1video -s 800x600");commend.add("http://192.168.1.142:8081/supersecret/live"&#43;code&#43;"");StringBuffer test&#61;new StringBuffer();for(int i&#61;0;i<commend.size();i&#43;&#43;)test.append(commend.get(i)&#43;" ");System.out.println(test);try {if (map.get(code)!&#61;null && map.get(code)>&#61;1){map.put(code,map.get(code)&#43;1);returnstr&#43;&#61;code&#43;"设备视频已经在转换列表里了";System.out.println("视频已经在转换列表里了");}else{//采用多线程处理防止主线程阻塞&#xff0c;一直等待startStream(test.toString(),code);map.put(code,1);System.out.println("视频转换成功:");returnstr&#43;&#61;code&#43;"设备视频转换成功";}} catch (Exception e) {e.printStackTrace();}return returnstr;}/*** 使用多线程执行linux的ffmpeg命令防止主线程阻塞* &#64;param test 命令* &#64;param code 对应的摄像投设备标志*/private void startStream(String test,String code) {//处理buffer的线程es3.submit(new Runnable() {&#64;Overridepublic void run() {String line &#61; null;try {Runtime rt &#61; Runtime.getRuntime();//执行linux命令Process proc &#61; rt.exec(test.toString());//添加缓存防止多次转换map.put(code,1);//启用多线程消费正常日志防止内存小导致线程阻塞clearStream(proc.getInputStream());//启用多线程消费错误日志防止内存小导致线程阻塞clearStream(proc.getErrorStream());} catch (IOException e) {e.printStackTrace();}}});}/*** 开启多线程消费日志&#xff0c;防止转换缓冲区内存溢出* &#64;param stream 输出流*/private void clearStream (InputStream stream) {//处理buffer的线程es3.submit(new Runnable() {&#64;Overridepublic void run() {String line &#61; null;try (BufferedReader in &#61; new BufferedReader(new InputStreamReader(stream));) {while ((line &#61; in.readLine()) !&#61; null) {System.out.println(line);}} catch (IOException e) {e.printStackTrace();}}});}/*** 根据pid关闭进程* &#64;param pid* &#64;param code* &#64;return*/public static String killPid(String pid , String code) {BufferedReader reader &#61; null;String pidStr&#61;"";try {// 显示所有进程Process process &#61; Runtime.getRuntime().exec("kill -9 " &#43; pid);pidStr&#43;&#61;"成功关闭进程&#xff1a;"&#43;pid;map.put(code,0);} catch (Exception e) {e.printStackTrace();} finally {if (reader !&#61; null) {try {reader.close();} catch (IOException e) {}}}return pidStr;}/***根据名称获取linux启动的进程pid* &#64;param pid 摄像头唯一标志开启进程时包含的标志* &#64;return*/public static String getPid(String pid) {BufferedReader reader &#61; null;try {// 显示所有进程Process process &#61; Runtime.getRuntime().exec("ps -ef");reader &#61; new BufferedReader(new InputStreamReader(process.getInputStream()));String line &#61; null;while ((line &#61; reader.readLine()) !&#61; null) {//根据设备标志获取需要的线程pidif (line.contains("rtsp://用户名:摄像头密码&#64;192.168.1."&#43;pid&#43;":554/h264/ch1/sub")) {//拿到第一个pidString[] strs &#61; line.split("\\s&#43;");return strs[1];}}} catch (Exception e) {e.printStackTrace();} finally {if (reader !&#61; null) {try {reader.close();} catch (IOException e) {}}}return null;}public static void main(String[] args) {}
}

  1. 启动项目&#xff1b;
    启动springboot项目&#xff0c;然后浏览器访问项目我这里路径是&#xff1a; http://localhost:8097/hello/27此时已经把服务开启并且把流推送到websocket项目
    浏览器访问在这里插入图片描述
    文件代码如下

<!DOCTYPE html>
<html>
<head><title>JSMpeg Stream Client</title><style type&#61;"text/css">html, body {text-align: center;}</style></head>
<body><canvas id&#61;"video-canvas1"></canvas><canvas id&#61;"video-canvas2"></canvas><canvas id&#61;"video-canvas3"></canvas><canvas id&#61;"video-canvas4"></canvas><canvas id&#61;"video-canvas5"></canvas><canvas id&#61;"video-canvas6"></canvas><canvas id&#61;"video-canvas7"></canvas><canvas id&#61;"video-canvas8"></canvas><canvas id&#61;"video-canvas9"></canvas><canvas id&#61;"canvas"></canvas><span id&#61;"jietu">截图</span><script type&#61;"text/Javascript" src&#61;"jsmpeg.min.js"></script><script type&#61;"text/Javascript" src&#61;"html2canvas.js"></script><script type&#61;"text/Javascript" src&#61;"canvas2Image.js"></script><script type&#61;"text/Javascript">let canvas1 &#61; document.getElementById(&#39;video-canvas1&#39;);let url1 &#61; &#39;ws://192.168.1.142:8082/live26&#39;;let player1 &#61; new JSMpeg.Player(url1, {canvas: canvas1});let canvas2 &#61; document.getElementById(&#39;video-canvas2&#39;);let url2 &#61; &#39;ws://192.168.1.142:8082/live24&#39;;let player2 &#61; new JSMpeg.Player(url2, {canvas: canvas2});let canvas3 &#61; document.getElementById(&#39;video-canvas3&#39;);let url3 &#61; &#39;ws://192.168.1.142:8082/live27&#39;;let player3 &#61; new JSMpeg.Player(url3, {canvas: canvas3});let canvas4 &#61; document.getElementById(&#39;video-canvas4&#39;);let url4 &#61; &#39;ws://192.168.1.142:8082/live33&#39;;let player4 &#61; new JSMpeg.Player(url4, {canvas: canvas4});let canvas5 &#61; document.getElementById(&#39;video-canvas5&#39;);let url5 &#61; &#39;ws://192.168.1.142:8082/live23&#39;;let player5 &#61; new JSMpeg.Player(url5, {canvas: canvas5});let canvas6 &#61; document.getElementById(&#39;video-canvas6&#39;);let url6 &#61; &#39;ws://192.168.1.142:8082/live30&#39;;let player6 &#61; new JSMpeg.Player(url6, {canvas: canvas6});let jietu &#61; document.getElementById("jietu");jietu.addEventListener("click", function(){/*var cntElem &#61; document.getElementById("video-canvas1");var shareContent &#61; cntElem;//需要截图的包裹的&#xff08;原生的&#xff09;DOM 对象var width &#61; shareContent.offsetWidth; //获取dom 宽度var height &#61; shareContent.offsetHeight; //获取dom 高度var canvas &#61; document.getElementById("canvas"); //创建一个canvas节点var scale &#61; 2; //定义任意放大倍数 支持小数canvas.width &#61; width * scale; //定义canvas 宽度 * 缩放canvas.height &#61; height * scale; //定义canvas高度 *缩放canvas.getContext("2d").scale(scale, scale); //获取context,设置scalevar opts &#61; {scale: scale, // 添加的scale 参数canvas: canvas, //自定义 canvas// logging: true, //日志开关&#xff0c;便于查看html2canvas的内部执行流程width: width, //dom 原始宽度height: height,useCORS: true // 【重要】开启跨域配置};html2canvas(shareContent, opts).then(function (canvas) {var context &#61; canvas.getContext(&#39;2d&#39;);// 【重要】关闭抗锯齿context.mozImageSmoothingEnabled &#61; false;context.webkitImageSmoothingEnabled &#61; false;context.msImageSmoothingEnabled &#61; false;context.imageSmoothingEnabled &#61; false;// 【重要】默认转化的格式为png,也可设置为其他格式var img &#61; Canvas2Image.saveAsPNG(canvas, canvas.width, canvas.height);document.body.appendChild(img);img.css &#61; {"width": canvas.width / 2 &#43; "px","height": canvas.height / 2 &#43; "px","position":"fixed","top":"0","left":"0","opacity":"1","z-index":222}});*/downloadFile(&#39;download&#39;, document.getElementById("video-canvas1").toDataURL());});function downloadFile(fileName, content) {let aLink &#61; document.createElement(&#39;a&#39;);let blob &#61; this.base64ToBlob(content); //new Blob([content]);let evt &#61; document.createEvent("HTMLEvents");evt.initEvent("click", true, true);//initEvent 不加后两个参数在FF下会报错 事件类型&#xff0c;是否冒泡&#xff0c;是否阻止浏览器的默认行为aLink.download &#61; fileName;aLink.href &#61; URL.createObjectURL(blob);// aLink.dispatchEvent(evt);aLink.click()}function base64ToBlob(code) {let parts &#61; code.split(&#39;;base64,&#39;);let contentType &#61; parts[0].split(&#39;:&#39;)[1];let raw &#61; window.atob(parts[1]);let rawLength &#61; raw.length;let uInt8Array &#61; new Uint8Array(rawLength);for (let i &#61; 0; i < rawLength; &#43;&#43;i) {uInt8Array[i] &#61; raw.charCodeAt(i);}return new Blob([uInt8Array], { type: contentType });}/*for (let i &#61; 1; i <&#61; 9; i&#43;&#43;){let canvas &#61; document.getElementById(&#39;video-canvas&#39; &#43; i);let url &#61; &#39;ws://127.0.0.1:8082/live&#39; &#43; i;let player &#61; new JSMpeg.Player(url, {canvas: canvas});}*/</script>
</body>
</html>

  1. 浏览器就可以实时看到监控了&#xff1b;
  2. 注意事项和细节代码注释。觉得还行关注和打赏一下。有需要可以添加我的qq&#xff1a;794129243
    本文是原创未经允许不得用于商业用途涉及法律效应&#xff0c;一概不负责。转载请标明出处。

推荐阅读
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 【系列二】长连接,短连接及WebSocket介绍(含http1.0,1.1,2.0相关)
    前言上一节讲了长轮询和轮询及其实现,这节讲一讲长连接、短连接及webSocket,在讲这些之前,我们先来普及一下http相关的一 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • 本文分享了一位Android开发者多年来对于Android开发所需掌握的技能的笔记,包括架构师基础、高级UI开源框架、Android Framework开发、性能优化、音视频精编源码解析、Flutter学习进阶、微信小程序开发以及百大框架源码解读等方面的知识。文章强调了技术栈和布局的重要性,鼓励开发者做好学习规划和技术布局,以提升自己的竞争力和市场价值。 ... [详细]
author-avatar
ruiqiazhang_236
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有