我是在
https://blog.csdn.net/zfs_zs/article/details/106954769
这篇文章上的项目进行修改实现的,该文章附带文件代码,大家可以直接去下
ps:上文的项目,作者自行修改了jsmpeg.js 所以最好去百度一个原版的
用户点击摄像头,建立websocket,后台服务查询对应摄像头信息,进行推流到websocket,浏览器用jsmpeg拉流,展示给用户
看懂下面图,基本就知道流程和思路了=。=
我就不上全部的代码,上点核心代码
前端:
引用
<script type="text/javascript" src="../../public/res/vendor/jsmpeg/jsmpeg.min.js">script>
记得改src,各位大哥=。=
html
<canvas id="source" >canvas>
随便放哪,就是个渲染视频窗口用的
js
项目用的layui,所以=。=
//渲染视频,建立websocket
load_sxt_frame : function (eleId,token){
//获取页面html的canvas元素
var canvas = document.getElementById(eleId);
//这两行是我用来处理url
var url_sy =appPath.substring(appPath.indexOf("//"));
var url = 'ws:'+url_sy+'/rtsp';
//最主要的一段,这个对象在创建的时候会直接创建一个websocket
//当然后台也要做对应的服务设置
var player = new JSMpeg.Player(url, {
canvas: canvas,
});
//这个返回的对象很有用,里面有websocket的实例,可以用来调用websocket的方法,具体的自己打印看下吧,多动手=。=
return player;
}
以上就是前端的代码
java代码:
java 基本上是把
https://blog.csdn.net/zfs_zs/article/details/106954769
用来调用推流命令,工具类
ConvertVideoPakcet:
package com.sy.xmzdy.ws;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConvertVideoPakcet implements ApplicationRunner {
private static Map<String,Process> processMap = new ConcurrentHashMap<>();
public static Map<String, Process> getProcessMap() {
return processMap;
}
public Process process ;
public Integer pushVideoAsRTSP(String id, String fileName,String token){
// 输出ffmpeg推流日志
try {
String command = "";
command += "ffmpeg -rtsp_transport tcp"; // ffmpeg开头,-re代表按照帧率发送,在推流时必须有
command += " -i \"" + id + "\""; // 指定要推送的视频
command += " -q 0 -f mpegts -codec:v mpeg1video -s 700x400 " + fileName; // 指定推送服务器,-f:指定格式 1280 720
System.out.println("ffmpeg推流命令:" + command);
process = Runtime.getRuntime().exec(command);
//这里是为了区分不同的流,需求会打开多个摄像头,根据token
//后面可以对不需要的流关闭
processMap.put(token, process);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 1;
}
}
}
websocket配置类,主要用来配置websocket的连接地址
WebsocketConfiguration:
package com.sy.xmzdy.ws;
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import org.apache.catalina.session.StandardSessionFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
/**
* @author volume
**/
@Configuration
@EnableWebSocket
public class WebsocketConfiguration implements WebSocketConfigurer {
@Autowired
private WsIntercept wsIntercept;
@Autowired
private WsHandler wsHandler;
// @Bean
// public ServerEndpointExporter serverEndpointExporter() {
// return new ServerEndpointExporter();
// }
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
//监听websocket请求,创建连接
webSocketHandlerRegistry.addHandler(wsHandler,"/rtsp").addInterceptors(wsIntercept).setAllowedOrigins("*");
}
//这个没用到,看起来应该是设置websocket连接内存大小的=。=
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(10*1024);
container.setMaxBinaryMessageBufferSize(10*1024);
return container;
}
}
监听websocket状态发生改变时调用的方法
WsHandler:
package com.sy.xmzdy.ws;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.BinaryWebSocketHandler;
import javax.imageio.stream.FileImageOutputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class WsHandler extends BinaryWebSocketHandler {
/**
* 存放所有在线的客户端
*/
private static Map<String, WebSocketSession> clients = new ConcurrentHashMap<>();
public static Map<String, WebSocketSession> getClients() {
return clients;
}
//新连接时
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("-------------新的加入session.getId():"+session.getId()+"---------------------");
Map<String,Object> map = session.getAttributes();
String htSessionId = map.get("HTTP.SESSION.ID")==null?null:map.get("HTTP.SESSION.ID").toString();
if(htSessionId != null) {
clients.put(htSessionId, session);
}else {
clients.put(session.getId(), session);
}
}
//连接中断时
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status){
System.out.println("------------------退出的-----------------------------");
Map<String,Process> processMap = ConvertVideoPakcet.getProcessMap();
Map<String,Object> map = session.getAttributes();
String htSessionId = map.get("HTTP.SESSION.ID")==null?null:map.get("HTTP.SESSION.ID").toString();
if(htSessionId != null) {
clients.remove(htSessionId);
Process process = processMap.get(htSessionId);
try {
//process.waitFor();
process.destroy();
} catch (Exception e) {
//e.printStackTrace();
}
}else {
clients.remove(session.getId());
}
}
//连接出错时
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
Map<String,Object> map = session.getAttributes();
String htSessionId = map.get("HTTP.SESSION.ID")==null?null:map.get("HTTP.SESSION.ID").toString();
if(htSessionId != null) {
clients.remove(htSessionId);
}else {
clients.remove(session.getId());
}
}
//核心代码,用来推送信息给websocket
public void sendVideo(byte[] data) {
try{
BinaryMessage binaryMessage = new BinaryMessage(data);
for (Map.Entry<String, WebSocketSession> sessionEntry : clients.entrySet()) {
try {
WebSocketSession session = sessionEntry.getValue();
if (session.isOpen()) {
session.sendMessage(binaryMessage);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
//自己改的代码,因为需要区分推给那个websocket
public void sendOneVideo(byte[] data,WebSocketSession session) {
try{
BinaryMessage binaryMessage = new BinaryMessage(data);
try {
if (session.isOpen()) {
session.sendMessage(binaryMessage);
}
} catch (Exception e) {
e.printStackTrace();
}
}catch (Exception e){
e.printStackTrace();
}
}
//这个方法我没用到,应该是用来人脸识别返回截图的
public void byte2image(byte[] data,String path){
if(data.length<3||path.equals("")) return;
try{
FileImageOutputStream imageOutput = new FileImageOutputStream(new File(path));
imageOutput.write(data, 0, data.length);
imageOutput.close();
System.out.println("Make Picture success,Please find image in " + path);
} catch(Exception ex) {
System.out.println("Exception: " + ex);
ex.printStackTrace();
}
}
}
配置类,照着写,不要问,问就是我也不知道是在干嘛=。=
WsIntercept:
package com.sy.xmzdy.ws;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class WsIntercept extends HttpSessionHandshakeInterceptor {
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
HttpServletRequest request = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
HttpServletResponse response = ((ServletServerHttpResponse) serverHttpResponse).getServletResponse();
String header = request.getHeader("sec-websocket-protocol");
if (StringUtils.isNotEmpty(header)) {
response.addHeader("sec-websocket-protocol",header);
}
//这里调父类的方法,上面不是白写了么=。=
super.afterHandshake(serverHttpRequest,serverHttpResponse,webSocketHandler,e);
}
}
这两个controller 就是上面的地址1,和地址2
CameraController:
ps:地址1
package com.sy.xmzdy.controller;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.socket.WebSocketSession;
import com.fatowit.sys.utils.I_PropUtil;
import com.fatowit.xmzdy.bean.BaseResponse;
import com.fatowit.xmzdy.service.CameraService;
import com.fatowit.xmzdy.service.SxtConfigService;
import com.fatowit.xmzdy.utils.ProcessUtils;
import com.fatowit.xmzdy.ws.ConvertVideoPakcet;
import com.fatowit.xmzdy.ws.WsHandler;
/**
* 摄像头
* @author sy
*
*/
@Controller
@RequestMapping("/camera")
public class CameraController {
@Autowired
private WsHandler wsHandler;
@Autowired
private CameraService cameraService;
@Autowired
private SxtConfigService sxtConfigService;
/**
* 推送摄像头信息(地址1)
* @param request
* @param sxt_id
* @param token
* @return
*/
@SuppressWarnings("unchecked")
@RequestMapping(value="push_sxt_rtsp")
@ResponseBody
public BaseResponse push_sxt_rtsp(HttpServletRequest request,String sxt_id,String token,String rtsp_dz) {
Map<String, WebSocketSession> clients = wsHandler.getClients();
BaseResponse base = new BaseResponse();
Map<String,Object> userInfo = (Map<String, Object>) request.getSession().getAttribute("userInfo");
String user = (String) userInfo.get("userId");
ConvertVideoPakcet doffmpeg = new ConvertVideoPakcet();
HashMap<String,Object> camera = cameraService.queryById(sxt_id);
String rtsp = camera.get("SXT_RTSP")==null?null:camera.get("SXT_RTSP").toString();
if(rtsp == null) {
return null;
}
//查询推送的controller地址
HashMap<String,Object> sxtConfig = sxtConfigService.queryByJ("rtsp_dz");
String url_dz = sxtConfig.get("Z")==null?null:sxtConfig.get("Z").toString();
String sessinId = request.getSession().getId();
//主要是这段,开启数据流推送,调用系统命令开始推送数据到地址2
doffmpeg.pushVideoAsRTSP(rtsp, url_dz+"?id="+sessinId,request.getSession().getId());
base.setCode("0");
base.setData(null);
base.setMessage("查询数据");
return base;
}
}
SocketController:
ps:地址2
package com.sy.xmzdy.controller;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.server.PathParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.socket.WebSocketSession;
import com.fatowit.xmzdy.ws.ConvertVideoPakcet;
import com.fatowit.xmzdy.ws.WsHandler;
@Controller
@RequestMapping("/rtsp")
public class SocketController {
@Autowired
private WsHandler wsHandler;
@RequestMapping("/index")
public String index() {
return "index";
}
@GetMapping("/webSocket")
public ModelAndView socket() {
ModelAndView mav=new ModelAndView("/webSocket");
// mav.addObject("userId", userId);
return mav;
}
@RequestMapping("/receive")
@ResponseBody
public String receive(HttpServletRequest request, Object response,String id) {
//测试参数是否可以获取
System.out.println("method:" + request.getMethod());
Map<String,WebSocketSession> sessionMap = wsHandler.getClients();
WebSocketSession webSocketSession = sessionMap.get(id);
if(webSocketSession==null) {
return "1";
}
try {
ServletInputStream inputStream = request.getInputStream();
int len = -1;
while ((len =inputStream.available()) !=-1) {
byte[] data = new byte[len];
inputStream.read(data);
//推送数据给websocket
wsHandler.sendOneVideo(data, webSocketSession);
}
} catch (Exception e) {
//e.printStackTrace();
return "1";
}
System.out.println("over");
return "1";
}
/**
* 关闭websoket
* @param request
* @param response
* @return
*/
@RequestMapping("/closeWebsoket")
@ResponseBody
public String closeWebsoket(HttpServletRequest request) {
//测试参数是否可以获取
String session = request.getSession().getId();
System.out.println("http-session:"+session);
Map<String, WebSocketSession> map = wsHandler.getClients();
for (String s : map.keySet()) {
System.out.println("websoket:"+s);
}
System.out.println("over");
return "1";
}
}
windows:
ffmpeg -rtsp_transport tcp -i “rtsp” -q 0 -f mpegts -codec:v mpeg1video -s 800x600 地址2
linux(CentOS 7):
ffmpeg -i “rtsp” -q 0 -f mpegts -codec:v mpeg1video -s 800x600 地址2
上面rtsp记得换=。=
地址2:就controller 处理数据然后推送的那个地址
我是在
https://blog.csdn.net/zfs_zs/article/details/106954769
这篇文章上的项目进行修改实现的,该文章附带文件代码,大家可以直接去下
ps:上文的项目,作者自行修改了jsmpeg.js 所以最好去百度一个原版的