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

推荐阅读
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • Java 中 Writer flush()方法,示例 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 深入解析 Spring Security 用户认证机制
    本文将详细介绍 Spring Security 中用户登录认证的核心流程,重点分析 AbstractAuthenticationProcessingFilter 和 AuthenticationManager 的工作原理。通过理解这些组件的实现,读者可以更好地掌握 Spring Security 的认证机制。 ... [详细]
  • 本文介绍如何在Node.js环境中执行Powershell脚本,并详细说明了通过子进程处理命令输出和错误信息的具体步骤。 ... [详细]
  • 在项目部署后,Node.js 进程可能会遇到不可预见的错误并崩溃。为了及时通知开发人员进行问题排查,我们可以利用 nodemailer 插件来发送邮件提醒。本文将详细介绍如何配置和使用 nodemailer 实现这一功能。 ... [详细]
  • 本文详细解析了Java中hashCode()和equals()方法的实现原理及其在哈希表结构中的应用,探讨了两者之间的关系及其实现时需要注意的问题。 ... [详细]
  • 本文介绍了如何在 Node.js 中使用 `setDefaultEncoding` 方法为可写流设置默认编码,并提供了详细的语法说明和示例代码。 ... [详细]
  • 获得头条Offer后,我感激的七个技术公众号
    是否感觉订阅的公众号过多,浏览时缺乏目标性,未能获取实质性的知识?本文将介绍如何精简公众号列表,提升信息吸收效率,并推荐几个高质量的技术公众号。 ... [详细]
  • 本文详细分析了Hive在启动过程中遇到的权限拒绝错误,并提供了多种解决方案,包括调整文件权限、用户组设置以及环境变量配置等。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
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社区 版权所有