前情提要:之前利用websocket解析过https://blog.csdn.net/IT_CREATE/article/details/105625858?spm=1001.2014.3001.5501,不过由于是处理图片帧的方式,导致前端不能播放声音,同时多开窗口分流后影响了图片的刷新率,所以改用当前方式进行解析,效率得到了提高,同时更加合理
展示效果

码云地址:https://gitee.com/dxl96/video-service

1、首先我们需要引入相关的jar包,javacv相关

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.1.RELEASE</version><relativePath/> <!-- lookup parent from repository --> </parent><groupId>com.de</groupId><artifactId>videoservice</artifactId><version>0.0.1-SNAPSHOT</version><name>videoservice</name><description>视频服务</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><commons.io.version>2.5</commons.io.version><commons.fileupload.version>1.3.3</commons.fileupload.version><hutool.version>4.6.4</hutool.version><fastjson.version>1.2.47</fastjson.version><lang3.version>3.9</lang3.version><jsckson.version>2.10.3</jsckson.version><javacv.version>1.5.1</javacv.version> </properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>${commons.io.version}</version></dependency><!--文件上传工具类 --><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>${commons.fileupload.version}</version></dependency><!-- 阿里JSON解析器 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><!--好用的工具集--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>${hutool.version}</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>${lang3.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>${javacv.version}</version><type>pom</type></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>${java.version}</source><target>${java.version}</target><encoding>${project.build.sourceEncoding}</encoding></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration><skipTests>true</skipTests></configuration></plugin></plugins></build></project>

2、编写javacv转flv(MediaVideoTransfer.java)

package com.de.rtsp;import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;import java.io.OutputStream;/*** 转换rtsp为flv** @author IT_CREATE* @date 2021/6/8 12:00:00*/
@Slf4j
public class MediaVideoTransfer {@Setterprivate OutputStream outputStream;@Setterprivate String rtspUrl;@Setterprivate String rtspTransportType;private FFmpegFrameGrabber grabber;private FFmpegFrameRecorder recorder;private boolean isStart = false;/*** 开启获取rtsp流*/public void live() {log.info("连接rtsp:" + rtspUrl + ",开始创建grabber");boolean isSuccess = createGrabber(rtspUrl);if (isSuccess) {log.info("创建grabber成功");} else {log.info("创建grabber失败");}startCameraPush();}/*** 构造视频抓取器** @param rtsp 拉流地址* @return 创建成功与否*/private boolean createGrabber(String rtsp) {// 获取视频源try {grabber = FFmpegFrameGrabber.createDefault(rtsp);grabber.setOption("rtsp_transport", rtspTransportType);grabber.start();isStart = true;recorder = new FFmpegFrameRecorder(outputStream, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());//avcodec.AV_CODEC_ID_H264  //AV_CODEC_ID_MPEG4recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);recorder.setFormat("flv");recorder.setFrameRate(grabber.getFrameRate());recorder.setSampleRate(grabber.getSampleRate());recorder.setAudioChannels(grabber.getAudioChannels());recorder.setFrameRate(grabber.getFrameRate());return true;} catch (FrameGrabber.Exception e) {log.error("创建解析rtsp FFmpegFrameGrabber 失败");log.error("create rtsp FFmpegFrameGrabber exception: ", e);stop();reset();return false;}}/*** 推送图片(摄像机直播)*/private void startCameraPush() {if (grabber == null) {log.info("重试连接rtsp:" + rtspUrl + ",开始创建grabber");boolean isSuccess = createGrabber(rtspUrl);if (isSuccess) {log.info("创建grabber成功");} else {log.info("创建grabber失败");}}try {if (grabber != null) {recorder.start();Frame frame;while (isStart && (frame = grabber.grabFrame()) != null) {recorder.setTimestamp(grabber.getTimestamp());recorder.record(frame);}stop();reset();}} catch (FrameGrabber.Exception | RuntimeException | FrameRecorder.Exception e) {log.error(e.getMessage(), e);stop();reset();}}private void stop() {try {if (recorder != null) {recorder.stop();recorder.release();}if (grabber != null) {grabber.stop();}} catch (Exception e) {log.error(e.getMessage(), e);}}private void reset() {recorder = null;grabber = null;isStart = false;}
}

3、编写前端请求接口

package com.de.controller;import com.de.entity.AjaxResult;
import com.de.rtsp.MediaVideoTransfer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;/*** * @projectName videoservice* * @title IndexController* * @package com.de.controller* * @description  首页* * @author IT_CREAT     * * @date  2020 2020/5/17/017 5:15  * * @version c1.0.0*/
@Slf4j
@Controller
public class IndexController {AtomicInteger sign = new AtomicInteger();ConcurrentHashMap<Integer, String> pathMap = new ConcurrentHashMap<>();ConcurrentHashMap<Integer, PipedOutputStream> outputStreamMap = new ConcurrentHashMap<>();ConcurrentHashMap<Integer, PipedInputStream> inputStreamMap = new ConcurrentHashMap<>();@GetMapping("/")public String indexView() {return "index";}@GetMapping("/test")public String testView() {return "test";}@PostMapping("/putVideo")@ResponseBodypublic AjaxResult putVideoPath(String path) {try {int id = sign.getAndIncrement();pathMap.put(id, path);PipedOutputStream pipedOutputStream = new PipedOutputStream();PipedInputStream pipedInputStream = new PipedInputStream();pipedOutputStream.connect(pipedInputStream);outputStreamMap.put(id, pipedOutputStream);inputStreamMap.put(id, pipedInputStream);return AjaxResult.success(id);} catch (Exception e) {log.error(e.getMessage(), e);return AjaxResult.error();}}@GetMapping("/getVideo")public void getVideo(HttpServletRequest request, HttpServletResponse response, int id) {log.info("进来了" + id);String path = pathMap.get(id);String fileName = UUID.randomUUID().toString();// 用于测试的时候,本地文件读取走这里if (path.endsWith(".mp4")) {String[] split = new File(path).getName().split("\\.");fileName = split[0];}response.addHeader("Content-Disposition", "attachment;filename=" + fileName + ".flv");try {ServletOutputStream outputStream = response.getOutputStream();write(id, outputStream);} catch (IOException e) {log.error(e.getMessage(), e);}}private void write(int id, OutputStream outputStream) {try {String path = pathMap.get(id);PipedOutputStream pipedOutputStream = outputStreamMap.get(id);new Thread(() -> {MediaVideoTransfer mediaVideoTransfer = new MediaVideoTransfer();mediaVideoTransfer.setOutputStream(pipedOutputStream);mediaVideoTransfer.setRtspTransportType("udp");mediaVideoTransfer.setRtspUrl(path);mediaVideoTransfer.live();}).start();print(inputStreamMap.get(id), outputStream);} catch (Exception e) {log.error(e.getMessage(), e);}}private void print(InputStream inputStream, OutputStream outputStream) throws IOException {byte[] buffer = new byte[1024];int length;while ((length = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, length);}}public static void main(String[] args) throws FileNotFoundException {IndexController indexController = new IndexController();AjaxResult ajaxResult = indexController.putVideoPath("F:\\视频\\体育素材\\篮球视频素材\\哇哈体育\\篮球\\有片头进球集锦亚运决赛分p(中国vs伊朗)\\2018亚运男篮决赛台语解说剪辑版2三部分.mp4");indexController.write((int) ajaxResult.get("data"), new FileOutputStream("F:\\视频\\体育素材\\篮球视频素材\\哇哈体育\\篮球\\有片头进球集锦亚运决赛分p(中国vs伊朗)\\2018亚运男篮决赛台语解说剪辑版2三部分(负担).flv"));}
}

@GetMapping("/test") 前端请求页面
@PostMapping("/putVideo") 添加视频地址接口,因为前端get请求不能直接添加本地地址,所以需要先用post方式提交数据
@GetMapping("/getVideo") 通过get请求请求视频,将视频流写入response的outPutStream流中

注:
1、PipedOutputStream 和PipedInputStream是用于多线程的输出输入流,当PipedOutputStream 和PipedInputStream建立联系后,
写入到PipedOutputStream 的数据实际是写入到了PipedInputStream,我们通过读取PipedInputStream,就能实时读取写入到PipedOutputStream 的数据
2、也可以直接将response的outPutStream设置到MediaVideoTransfer 中,这样就不用单独开一个写入线程

4、编写前端请求页面test.html(注意切换到es6)

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><th:block th:include="include :: header('视频展示rtsp')"/><link th:href="@{/css/video/video-js.min.css}" href="../static/css/video/video-js.min.css" rel="stylesheet"/><style>.search {display: block;margin-bottom: 30px;}.mainContainer {display: block;width: 1024px;margin-left: auto;margin-right: auto;}.centeredVideo {display: block;width: 100%;height: 576px;margin-left: auto;margin-right: auto;margin-bottom: auto;}.controls {display: block;width: 100%;text-align: center;margin-left: auto;margin-right: auto;margin-top: 30px;}</style>
</head><body class="gray-bg">
<div style="padding: 20px"><p style="font-size: 20px;color: #0a7491;font-weight: bold;font-family: 楷体;text-align: center">rtsp拉取视频显示</p><div style="text-align:center"><div class="search">文件地址(rtsp地址):<input id="video_path" type="text" style="width: 300px"/><button type="button" onclick="changePath()">确定</button></div><div class="mainContainer"><video id="videoElement" class="centeredVideo" controls autoplay width="1024" height="576">Your browser istoo old which doesn't support HTML5 video.</video></div><div class="controls"><button onclick="flv_start()">开始</button><button onclick="flv_pause()">暂停</button><button onclick="flv_destroy()">停止</button><input style="width:200px" type="text" name="seekpoint" placeholder="输入时间点,int值,秒单位"/><button onclick="flv_seekto()">跳转</button></div></div></div>
<th:block th:include="include :: footer"/>
<!--<script th:src="@{/js/video/video.min.js}" src="../static/js/video/video.min.js"></script>-->
<script th:src="@{/js/video/flv.js}" src="../static/js/video/flv.js"></script>
<script th:inline="javascript">let videoElement = document.getElementById('videoElement');function resetUrl(url) {if (flvjs.isSupported()) {let flvPlayer = flvjs.createPlayer({type: 'flv',"isLive": true,//<====加个这个url: url,//<==自行修改});flvPlayer.attachMediaElement(videoElement);flvPlayer.load(); //加载flvPlayer.play()flv_start();}}function flv_start() {videoElement.play();}function flv_pause() {videoElement.pause();}function flv_destroy() {videoElement.pause();videoElement.unload();videoElement.detachMediaElement();videoElement.destroy();videoElement = null;}function flv_seekto() {videoElement.currentTime = parseFloat(document.getElementsByName('seekpoint')[0].value);}function changePath() {let path = $("#video_path").val();if (path === null || path === "") {alert("请输入地址")return}$.ajax({type: "POST",url: ctx + "putVideo",data: {path: path},success: function (result) {if (result.code === 0) {resetUrl(ctx + "getVideo?id=" + result.data)}},error: function () {alert("请求出错")}})}
</script>
</body>
</html>```

(直播、监控)利用javacv解析rtsp流,转换为flv流,通过前端flv.js解析播放相关推荐

  1. RTSP再学习 -- 利用FFmpeg 将 rtsp 获取H264裸流并保存到文件中

    如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/78214267 既然已经可以通过 RTSP 获取h264 裸流了.那么通过 F ...

  2. 音视频开发(17)---RTSP再学习 -- 利用FFmpeg 将 rtsp 获取H264裸流并保存到文件中

    RTSP再学习 -- 利用FFmpeg 将 rtsp 获取H264裸流并保存到文件中 https://blog.csdn.net/qq_29350001/article/details/7821426 ...

  3. qmediaplayer获取流类型_Java 流API

    流相关的接口和类在java.util.stream包中. AutoCloseable接口来自java.lang包. 所有流接口从继承自AutoCloseable接口的BaseStream接口继承. A ...

  4. html5处理json数据,js解析json数组

    js读取解析JSON类型数据 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 Jav ...

  5. LiveNVR传统视频监控互联网无插件-按需直播最大利用有效上行

    介绍 按需直播肯定是为了减少带宽流量和服务器性能占用.下面介绍下LiveNVR中按需直播的实现机制. 解析 如下是LiveNVR中关于按需直播的配置: LiveNVR软件目录下有一个nginx的目录. ...

  6. 常用的RTMP、RTSP、HTTP协议流直播流地址

    一.RTMP.RTSP.HTTP协议 这三个协议都属于互联网 TCP/IP 五层体系结构中应用层的协议.理论上这三种都可以用来做视频直播或点播.但通常来说,直播一般用 RTMP.RTSP.而点播用 H ...

  7. RTMP、RTSP、HTTP协议流常用直播流地址

    引用 一.RTMP.RTSP.HTTP协议 这三个协议都属于互联网 TCP/IP 五层体系结构中应用层的协议.理论上这三种都可以用来做视频直播或点播.但通常来说,直播一般用 RTMP.RTSP.而点播 ...

  8. 监控、无人机摄像头RTSP协议对接腾讯云直播

    监控.无人机摄像头RTSP协议对接腾讯云直播 1. 需求与目标 传统监控高清摄像机ip camera(如: 海康,大华等)遵循监控行业标准,一般只支持rtsp传输协议,互联网直播通用标准为rtmp协议 ...

  9. 基于EasyNVR摄像机流媒体服务器实现RTSP或Onvif监控摄像头Web无插件化直播监控

    前言介绍 随着互联网的发展,尤其是移动互联网基于H5.微信的应用越来越多,企业也更多地想基于H5.微信公众号来快速开发和运营自己的产品,而传统的安防IPC所输出的各种RTSP.GB28181.SDK视 ...

最新文章

  1. acess dao示例
  2. CloudStack设计思想
  3. 网络名称空间 实例研究 veth处于不同网络的路由问题
  4. 方向键 上下左右的转译
  5. 洛谷-P1160 队列安排
  6. 修复漏洞的Istio 1.1.1 发布了
  7. (转)财新特稿丨风口浪尖上,李笑来谈ICO
  8. 机器人对话常用语模板_小a电话机器人免费咨询
  9. 基于NTT的循环码:RS码、BCH码、RM码
  10. 单目摄像机标定与双目摄像机标定有什么区别
  11. java-opencv 米粒数_Python opencv学习音符的米粒数,返回每个米粒的位置面积和总米粒数的平均面积,pythonopencv,笔记,之数,并,一个,及,个数...
  12. Oracle Audit
  13. 目前世界上15款优秀杀毒软件
  14. frp内网穿透https
  15. [BAPI]如何读取采购订单PO审批状态数据-[BAPI_PO_GETRELINFO]
  16. Unity3D中Quaternion.Euler方法解析
  17. 前端开发的流程与规范
  18. 蓦然回首,一切都是浮云
  19. Linux软件包管理-rpm、yum
  20. 2017天猫双11,1682亿背后的阿里绝密50+技术(长图下载)

热门文章

  1. 【泛微ecology】E-Mobile 7 win+linux 安装包(版本20210409)
  2. 华清远见嵌入式开发工程师2022
  3. 如何过滤掉xml字符串中的gt,lt,quot,amp,apos
  4. SQL Dblink SQL
  5. SEO之了解搜索引擎
  6. 二、常见的EDID问题
  7. Xcode14 正式版编译报错‘ does not contain bitcode.解决方案
  8. 在BIOS中设置U盘启动
  9. dom对象jquery对象
  10. 28.深度学习模型压缩方法-2