java+ffmpeg+websocket+jsmpeg 调用摄像头RTSP在浏览器显示

说明

我是在

https://blog.csdn.net/zfs_zs/article/details/106954769

这篇文章上的项目进行修改实现的,该文章附带文件代码,大家可以直接去下
ps:上文的项目,作者自行修改了jsmpeg.js 所以最好去百度一个原版的

功能介绍

用户点击摄像头,建立websocket,后台服务查询对应摄像头信息,进行推流到websocket,浏览器用jsmpeg拉流,展示给用户

看懂下面图,基本就知道流程和思路了=。=

java+ffmpeg+websocket+jsmpeg 调用摄像头RTSP在浏览器显示_第1张图片

代码

我就不上全部的代码,上点核心代码
前端:
引用

<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

文章里面的代码改了下
根据包分开
java+ffmpeg+websocket+jsmpeg 调用摄像头RTSP在浏览器显示_第2张图片

用来调用推流命令,工具类

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";
    } 
    
    
}


ffmpeg 推送指令:

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 所以最好去百度一个原版的

你可能感兴趣的:(个人学习记录,websocket,java,web)