赞
踩
我是在
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 所以最好去百度一个原版的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。