序言:书接上文,我们继续
SSM架构 +Nginx+FFmpeg实现rtsp流转hls流,在前端html上实现视频播放
步骤一:把rtsp流转化为hls流,用Java代码进行转换
java
package com.tools;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class RTSPtoHLS {
public static void main(String[] args) {
System.out.println("111111111111111111");
String inputVideoPath="rtsp://admin:bfm100766@192.168.1.64:554/streaming/channels/101";
String outputVideoPath ="D:/nginx/html/orb/test.m3u8";
generateVideo( inputVideoPath, outputVideoPath);
}
//视频拼接的接口
public static boolean generateVideo(String inputVideoPath, String outputVideoPath) {
try {
// 构建FFmpeg命令,这里假设是将输入视频与模板视频合成,并输出成片
// 命令示例: ffmpeg -i input.mp4 -i template.mp4 -filter_complex "your_complex_filter_graph" output.mp4
// "your_complex_filter_graph" 是你用来应用模板的复杂滤波图
// ffmpeg -fflags +genpts -i rtsp://admin:bfm100766@192.168.1.64:554/streaming/channels/101 -c copy -f hls -hls_time 2.0 -hls_list_size 1 D:/nginx/html/hls/test.m3u8
String[] command = new String[]{
"ffmpeg",
"-fflags", "+genpts",
"-i", inputVideoPath,
"-c", "copy",
"-f", "hls",
"-hls_time","10",
"-hls_list_size","10",
outputVideoPath
};
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process process = processBuilder.start();
// 读取错误流并打印
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
while ((line = errorReader.readLine()) != null) {
System.out.println(line);
}
// 等待进程结束
process.waitFor();
System.out.println("Video generation completed!!!");
return true;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
System.out.println("Error generating video.");
}
return false;
}
}
步骤二:SSM 架构中Controller层 如何调用
java
package com.controller;
import java.util.Calendar;
import java.util.Timer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSONObject;
import com.tools.FileCleanupTask;
import com.tools.RTSPtoHLS;
@Controller
@RequestMapping("/rh")
public class RtspHlsController {
//1跳转到前端首页面
@RequestMapping("/rh_login.html")
public String getdrLogin(HttpServletRequest request,HttpSession session)
{
return "rh_login";
}
//使用传递 参数改变全局变量的参数
@ResponseBody
@RequestMapping(value="/setHls")
public String set_Entity(HttpServletRequest request)
{
JSONObject jsonObject=new JSONObject();
//第一步先清空nginx中hls文件夹中的内容
String directoryPath = "D:/nginx/html/hls/";
long thresholdSize = 2000 * 1024; // 文件阈值大小,例如10KB
// 创建定时任务
FileCleanupTask cleanupTask = new FileCleanupTask(directoryPath, thresholdSize);
// 创建定时器并设置初始延迟为0,之后每5分钟执行一次任务(5 * 60 * 1000毫秒)
Timer timer = new Timer();
timer.schedule(cleanupTask, 60*60*1000, 60 * 60 * 1000); // 初始延迟为1000毫秒,之后每5分钟执行一次(5分钟=5*60*1000毫秒)
//第二步nginx中录入中hls文件夹中内容
//rtsp视频流的访问路径
String inputVideoPath="rtsp://admin:bfm100766@192.168.1.64:554/streaming/channels/101";
//nginx的路径 把hls视频流输入到nginx服务器 hls文件夹中下
String outputVideoPath ="D:/nginx/html/hls/test.m3u8";
RTSPtoHLS.generateVideo( inputVideoPath, outputVideoPath);
jsonObject.put("saveUrl", outputVideoPath);
return jsonObject.toString();
}
//使用传递 参数改变全局变量的参数
@ResponseBody
@RequestMapping(value="/setHls1")
public String set_Entity1(HttpServletRequest request)
{
JSONObject jsonObject=new JSONObject();
//第一步先清空nginx中hls文件夹中的内容
String directoryPath = "D:/nginx/html/hls/";
long thresholdSize = 5000 * 1024; // 文件阈值大小,例如5000KB==5M
// 创建定时任务
FileCleanupTask cleanupTask = new FileCleanupTask(directoryPath, thresholdSize);
// 创建定时器并设置初始延迟为0,之后每5分钟执行一次任务(5 * 60 * 1000毫秒)
Timer timer = new Timer();
//timer.schedule(cleanupTask, 60*60*1000, 60 * 60 * 1000); // 初始延迟为1000毫秒,之后每5分钟执行一次(5分钟=5*60*1000毫秒)
//********设置每天的12点,执行某一函数************
// 计算下一个12点的时间
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 16); // 设置小时为12
calendar.set(Calendar.MINUTE, 40); // 设置分钟为0
calendar.set(Calendar.SECOND, 0); // 设置秒为0
calendar.set(Calendar.MILLISECOND, 0); // 设置毫秒为0
// 如果当前时间已超过今天的12点,则设置明天的12点
if (calendar.before(Calendar.getInstance())) {
calendar.add(Calendar.DATE, 1); // 明天的日期
}
// 安排任务在计算出的时间执行
timer.schedule(cleanupTask, calendar.getTime());
//第二步nginx中录入中hls文件夹中内容
//rtsp视频流的访问路径
String inputVideoPath="rtsp://admin:bfm100766@192.168.1.64:554/streaming/channels/101";
//nginx的路径 把hls视频流输入到nginx服务器 hls文件夹中下
String outputVideoPath ="D:/nginx/html/hls/test.m3u8";
RTSPtoHLS.generateVideo( inputVideoPath, outputVideoPath);
jsonObject.put("saveUrl", outputVideoPath);
return jsonObject.toString();
}
}
步骤三:html播放hls流视频
java
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0" />
<title>FLV-HLS 监控视频播放展示界面</title>
<link rel="stylesheet" href="${pageContext.request.contextPath }/statics/common/commonCss/bootstrap.min.css" />
<link rel="stylesheet" href="${pageContext.request.contextPath }/statics/common/demo.css" />
<script type="text/javascript" src="${pageContext.request.contextPath }/statics/common/commonJs/jquery.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/statics/common/commonJs/bootstrap.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/statics/common/player/dhhls.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/statics/common/player/dhflv.min.js"></script>
</head>
<style>
.mgt20 {
margin-top: 20px;
margin-bottom: 20px;
}
</style>
<%-- ${saveUrl} --%>
<body>
<div class="demo-box">
<div class="title">HLS播放器Demo</div>
<div class="content">
<!-- 左侧导航栏 -->
<!-- <div class="nav-box">
<ul class="nav nav-pills" role="tablist" id="mainTabs">
<li role="presentation" class="nav-link list-group-item active-item"><a href="#home" aria-controls="home" role="tab" data-toggle="tab">HLS 视频流播放</a></li>
<li role="presentation" class="nav-link list-group-item"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">FLV 视频流播放</a></li>
</ul>
</div> -->
<!-- tab主要内容 -->
<div class="tab-content content-box">
<!-- HLS 播放 -->
<div role="tabpanel" class="tab-pane active" id="home">
<!-- 步骤引导 -->
<!-- <div class="common-step play-step">
<div class="sec-title">HLS视频流播放步骤</div>
<div class="step-box">
<div class="step-item">1. 获取HLS实时流地址(如何获取流地址请查看<a href="https://open-icc.dahuatech.com/hlsplayer/#5">HLS详细说明</a>),获取HLS录像回放流地址(如何获取流地址请查看<a href="https://open-icc.dahuatech.com/#/home?url=%3Fnav%3Dwiki%2Fadmin%2Freplay.html&version=enterprisebase/5.0.15&blank=true">详细说明</a>)</div>
<div class="line-with-arrow"></div>
<div class="step-item">3. 播放配置输入流地址</div>
<div class="step-item">4. 开始播放</div>
</div>
</div> -->
<!-- 播放配置-->
<div class="common-step play-setting">
<div class="sec-title">播放配置</div>
<div id="oneInput" class="mgt20 mgl10">
<label style="display: none;">流地址:</label>
<input type="text" style="display: none;" class="stream-input" id="oneStartUrl" placeholder="请输入流地址" value="http://192.168.1.103:80/hls/test.m3u8">
<button class="common-btn" id="oneStartVideo">播放</button>
<button class="common-btn" style="display: none;" id="downloadVideo">录像下载</button><br><br>
</div>
</div>
<!-- 播放器 -->
<div>
<video class="video-box" id="myVideo" controls preload="auto" autoplay="autoplay"></video>
</div>
<!-- <div><strong>补充说明</strong>: hls H265视频编码播放仅Chrome104及以上版本支持</div> -->
</div>
</div>
</div>
</div>
</body>
<script src="${pageContext.request.contextPath }/statics/common/player/dhflv.min.js"></script>
<script src="${pageContext.request.contextPath }/statics/common/player/dhhls.min.js"></script>
<script type="text/javascript">
//异步请求处理,开始进行视频流输出
$.ajax({
async:true,
type:"post",
url:"${pageContext.request.contextPath }/rh/setHls1",
dataType:"json",
success:function(data){
console.log("nginx 输出视频流的绝对路径路径"+data.saveUrl);
},
error:function(){
//layer.msg('出现异常');
}
});
//菜单切换
$('#mainTabs a').click(function (e) {
$(this).tab('show');
$(this).siblings().removeClass('active');
$(this).parent().siblings().removeClass('active-item active');
$(this).parent().addClass('active-item');
})
/**
* 第一部分
* HLS实时播放
**/
//检测浏览器是否支持HLS 播放器
if (!Hls.isSupported()) {alert("浏览器不支持 HLS, 请升级!");}
//视频流帧数据、用于下载
let fmp4Data = {
audio: [],
video: [],
};
let url = "";
let hlsplayer = null;
$('#oneStartVideo').click(function(e) {
url = $('#oneStartUrl').val();
if(!url) {
alert("请输入流地址!");
return
}
//初始化录像配置
$('#downloadVideo')[0].innerText = "录像下载";
fmp4Data = {
audio: [],
video: [],
};
//开始播放
playHls('myVideo',url)
})
$(document).ready(function(e){
url = $('#oneStartUrl').val();
//url ="http://127.0.0.1:80/hls/test.m3u8";
if(!url) {
alert("请输入流地址!");
return
}
//初始化录像配置
$('#downloadVideo')[0].innerText = "录像下载";
fmp4Data = {
audio: [],
video: [],
};
//开始播放
playHls('myVideo',url)
})
//定时重新播放视频
function refreshPageAfter() {
//console.log("设定时间符合当前时间")
url = $('#oneStartUrl').val();
//url ="http://127.0.0.1:80/hls/test.m3u8";
if(!url) {
alert("请输入流地址!");
return
}
//初始化录像配置
$('#downloadVideo')[0].innerText = "录像下载";
fmp4Data = {
audio: [],
video: [],
};
//开始播放
playHls('myVideo',url)
}
//启动定时器
//self.setInterval("refreshPageAfter()", 61 * 60 *1000);
//莫一时间定时器
function scheduleRefresh() {
var now = new Date();
var targetTime = new Date();
// 设置目标时间为今天的12点15分
targetTime.setHours(17, 36, 0, 0);
// 如果当前时间已经超过12点15分,则将目标时间设置为明天的12点15分
if (now >= targetTime) {
targetTime.setDate(targetTime.getDate() + 1);
}
// 计算从现在到目标时间的毫秒数
var delay = targetTime - now;
console.log("设定时间符合当前时间"+delay)
// 设置延时,直到目标时间到达
setTimeout(refreshPageAfter, delay);
console.log("设定时间符合当前时间")
}
//初始化调用== 调用函数以设置定时刷新
scheduleRefresh();
//创建hls播放器
function playHls(id, url) {
//先触发销毁
if(hlsplayer != null) {
hlsplayer.destroy();
}
//创建播放
let video = document.getElementById(id);
if(Hls.isSupported()) {
hlsplayer = new Hls();
hlsplayer.loadSource(url);
hlsplayer.attachMedia(video);
hlsplayer.on(Hls.Events.MANIFEST_PARSED, function() {
hlsplayer.play();
});
} else if(video.canPlayType('application/vnd.apple.mpegurl')) {
console.log("apple原生");
// 如果支持原生播放
video.src = url;
video.play();
}
}
//hls录像下载
$('#downloadVideo').click(function(e) {
if(!hlsplayer) {
alert("请先触发播放");
return
}
let text = e.target.innerText;
if(text === "录像下载") {
$('#downloadVideo')[0].innerText = "录像中";
playHls('myVideo',url);
hlsplayer.on(Hls.Events.BUFFER_APPENDING, function (eventName, data) {
//录像数据缓存
fmp4Data[data.type].push(data.data);
console.log(fmp4Data)
});
} else {
$('#downloadVideo')[0].innerText = "录像下载";
//结束并下载录像
let type = "video";
if (fmp4Data[type].length) {
const blob = new Blob([arrayConcat(fmp4Data[type])], {
type: 'application/octet-stream',
});
const filename = type + '-' + new Date().toISOString() + '.mp4';
let aDom = document.createElement('a')
aDom.setAttribute("download",`hlsjs-` + filename);
aDom.setAttribute("href", self.URL.createObjectURL(blob));
aDom.style.display = 'none';
document.body.appendChild(aDom)
aDom.click()
document.body.removeChild(aDom);
}
//重置录像数据
fmp4Data = {
audio: [],
video: [],
};
}
})
//组合视频数据
function arrayConcat(inputArray) {
const totalLength = inputArray.reduce(function (prev, cur) {
return prev + cur.length;
}, 0);
const result = new Uint8Array(totalLength);
let offset = 0;
inputArray.forEach(function (element) {
result.set(element, offset);
offset += element.length;
});
return result;
}
/**
* 第二部分
* FLV实时播放
**/
//检测浏览器是否支持flv 播放器
if ( !dhflvjs.isSupported() )
{
alert("浏览器不支持 FLV, 请升级!");
}
let flvUrl = "";
let flvPlayer = null;
$('#oneStartVideoFlv').click(function(e) {
flvUrl = $('#oneStartUrlFlv').val();
if(!flvUrl) {
alert("请输入流地址!");
return
}
//开始播放
playflv('myVideoflV',flvUrl)
})
//创建flv播放器
function playflv(id, url) {
if (typeof flvPlayer !== "undefined") {
if (flvPlayer != null) {
flvPlayer.unload();
flvPlayer.detachMediaElement();
flvPlayer.destroy();
flvPlayer = null;
}
}
const video = document.getElementById(id);
flvPlayer = dhflvjs.createPlayer({
type: 'flv',
url : flvUrl,
});
flvPlayer.attachMediaElement(video);
flvPlayer.load();
flvPlayer.play();
}
//录像下载
$('#downloadVideoFlv').click(function(e) {
if(!flvPlayer) {
alert("请先触发播放");
return
}
let text = e.target.innerText;
if(text === "录像下载") {
$('#downloadVideoFlv')[0].innerText = "录像中";
//开始录像
downloadFLV(1);
} else {
$('#downloadVideoFlv')[0].innerText = "录像下载";
//结束录像
downloadFLV(0);
}
})
//下载录像
function downloadFLV(startFlag) {
if( startFlag ) {
flvPlayer && flvPlayer.startRecord()
} else {
flvPlayer && flvPlayer.endRecord()
}
}
</script>
<script type="text/javascript" src="${pageContext.request.contextPath }/statics/jquery-2.1.0.min.js"></script>
<%-- <script type="text/javascript" src="${pageContext.request.contextPath }/statics/js/sebeiqiehuan.js"></script> --%>
<script type="text/javascript" src="${pageContext.request.contextPath }/statics/video.min.js"></script>
</html>
步骤四:所需物料库