热门标签 | 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;一概不负责。转载请标明出处。

推荐阅读
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • 一、前言2012年毕业,2016年转行,没有一个体面的工作,机缘巧合之下,来到了大连,Java培训,一个全新的领域,迷茫、困惑、漫无目的的努力,转行真的被歧视,真的不行吗?我命由我 ... [详细]
  • 为了让用户体验更好,页面前端往往是通过ajax来进行数据处理;由于浏览器的设计原因每个域名下的连接有 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了OkHttp3的基本使用和特性,包括支持HTTP/2、连接池、GZIP压缩、缓存等功能。同时还提到了OkHttp3的适用平台和源码阅读计划。文章还介绍了OkHttp3的请求/响应API的设计和使用方式,包括阻塞式的同步请求和带回调的异步请求。 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • 微信官方授权及获取OpenId的方法,服务器通过SpringBoot实现
    主要步骤:前端获取到code(wx.login),传入服务器服务器通过参数AppID和AppSecret访问官方接口,获取到OpenId ... [详细]
  • Java如何导入和导出Excel文件的方法和步骤详解
    本文详细介绍了在SpringBoot中使用Java导入和导出Excel文件的方法和步骤,包括添加操作Excel的依赖、自定义注解等。文章还提供了示例代码,并将代码上传至GitHub供访问。 ... [详细]
  • websocket深入,tomcat,jetty服务器使用对比
    转载整理自https:www.zhihu.comquestion20215561http:www.open-open.comlibviewopen1435905714122.ht ... [详细]
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社区 版权所有