美文网首页IT必备技能js css html
websocket实现实时直播(滑鸡版)

websocket实现实时直播(滑鸡版)

作者: 凌康ACG | 来源:发表于2020-02-17 11:04 被阅读0次

    websocket实现实时直播

    作为一名web开发者,我使用websocket实现实时直播(滑鸡版)。
    为什么是滑鸡版呢?因为他上不了生产,只能了解一下直播的思路,不过也挺有意思的!

    思路

    开发思路,我们使用websocket实现数据传输,后台就用spring boot集成了websocket,当然用netty自定义更好,我这里直接拿spring全家桶快速开发。
    主播视频数据实时推送到服务端,然后由websocket推送给用户观看。最后把弹幕给实现了,这里我用Chrome浏览器+笔记本,谷歌+360极速浏览器。有些浏览器调用摄像头函数可能不一致,导致无法启动。
    项目源码:https://github.com/xcocean/wszb

    一、创建项目

    创建一个spring boot项目wszb快速开发,maven如下:

        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
    

    二、实现websocket服务端---画面传输

    配置

    @Configuration
    public class WebSocketConfig {
      @Bean
      public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
      }
    }
    

    controller

    用来做页面控制

    @Controller
    public class WsController {
      /**
       * 用户可以看到画面
       */
      @GetMapping("")
      public String index() {
        return "index";
      }
    
      /**
       * 主播直播的画面
       */
      @GetMapping("video")
      public String video() {
        return "video";
      }
    }
    

    视频websocket

    package com.lingkang.wszb.websocket;
    
    import java.io.IOException;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    import org.springframework.stereotype.Component;
    
    @ServerEndpoint(value = "/v")
    @Component
    public class VideoWebsocket {
    
      //concurrent包的线程安全,用来存放每个客户端对应的WebSocket
      private static ConcurrentHashMap<String, VideoWebsocket> webSocket = new ConcurrentHashMap<String, VideoWebsocket>();
      //与某个客户端的连接会话,需要通过它来给客户端发送数据
      private Session session;
      // 表示主播
      private static final String video = "v";
      // 给用户一个id
      private static int id = 0;
    
      private static String thisUser = "";
    
    
      /**
       * 连接建立成功调用的方法
       */
      @OnOpen
      public void onOpen(Session session) {
        this.session = session;
        try {
          String type = session.getQueryString();
          if (type.equals("video")) {
            // 表示主播
            thisUser = video;
            webSocket.put(thisUser, this);
            System.out.println("有人加入,是主播");
          } else {
            // 表示用户
            thisUser = String.valueOf(id);
            webSocket.put(thisUser, this);
            System.out.println("有人加入,是用户");
            id = id + 1;
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    
      /**
       * 连接关闭调用的方法
       */
      @OnClose
      public void onClose(Session session) throws Exception {
        System.out.println("关闭连接:" + thisUser);
        //需要清除当前和移除内存里的,不然还能接收信息
        session.close();
        webSocket.remove(thisUser);
      }
    
      /**
       * 收到客户端消息后调用的方法
       */
      //@OnMessage(maxMessageSize = 12)表示超出12个字节会自动关闭这个连接
      @OnMessage(maxMessageSize = 56666)
      public void onMessage(String message, Session session) throws IOException {
        System.out.println("来自客户端的消息:" + message);
        //群发消息
        Iterator iter = webSocket.entrySet().iterator();
        while (iter.hasNext()) {
          Map.Entry entry = (Map.Entry) iter.next();
          if (!entry.getKey().equals(video)) {//主播除外
            webSocket.get(entry.getKey()).session.getBasicRemote().sendText(message);
          }
        }
      }
    
      /**
       * 发生错误时调用
       */
      @OnError
      public void onError(Session session, Throwable error) throws Exception {
        System.out.println("发生错误:" + thisUser);
        session.close();
        webSocket.remove(thisUser);
      }
    }
    

    index.html

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>用户观看页面</title>
    </head>
    <body>
    <h2 style="text-align: center;">这是用户观看页面</h2>
    
    <img id="receiver" style="width: 500px;height: 450px;">
    <script type="text/javascript" charset="utf-8">
      //创建一个socket实例 ?user用来代表用户
      var socket = new WebSocket("ws://localhost:8080/v?user");
      //打开socket
      socket.onopen = function () {
        console.log("open success")
      }
      var image = document.getElementById('receiver');
      //接收到消息的回调方法
      socket.onmessage = function (data) {
        image.src = data.data;
      }
      //连接关闭的回调方法
      socket.onclose = function () {
        console.log("close");
      }
    </script>
    </body>
    </html>
    

    video.html

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>video</title>
    </head>
    <body>
    <h2 style="text-align: center;">这是主播页面</h2>
    <input type="button" title="开启摄像头" value="开启摄像头" onclick="getMedia()"/>
    <video id="video" width="500px" height="500px" autoplay="autoplay"></video>
    <canvas id="canvas" width="500px" height="500px"></canvas>
    <button onclick="start()">开始直播</button>
    <button onclick="stop()">停止直播</button>
    <script>
      //获得video摄像头区域
      var video = document.getElementById("video");
      var canvas = document.getElementById("canvas");
      var ctx = canvas.getContext('2d');
    
      function getMedia() {
        var constraints = {
          video: {width: 500, height: 500},
          audio: false
        };
        /*
        这里介绍新的方法:H5新媒体接口 navigator.mediaDevices.getUserMedia()
        这个方法会提示用户是否允许媒体输入,(媒体输入主要包括相机,视频采集设备,屏幕共享服务,麦克风,A/D转换器等)
        返回的是一个Promise对象。
        如果用户同意使用权限,则会将 MediaStream对象作为resolve()的参数传给then()
        如果用户拒绝使用权限,或者请求的媒体资源不可用,则会将 PermissionDeniedError作为reject()的参数传给catch()
        */
        var promise = navigator.mediaDevices.getUserMedia(constraints);
        promise.then(function (MediaStream) {
          video.srcObject = MediaStream;
          video.play();
        }).catch(function (PermissionDeniedError) {
          console.log(PermissionDeniedError);
        })
      }
    
      var socket = new WebSocket("ws://localhost:8080/v?video");
      //打开socket
    
      socket.onopen = function () {
        console.log("open success")
      }
    
      //接收到消息的回调方法
      socket.onmessage = function (event) {
        console.log(event)
      }
    
      //连接关闭的回调方法
      socket.onclose = function () {
        console.log("close");
      }
    
      var interval
    
      function start() {
        interval = window.setInterval(function () {
          ctx.drawImage(video, 0, 0, 500, 500);
          socket.send(canvas.toDataURL("image/jpeg", 0.5));
        }, 60);
      }
    
      function stop() {
        clearInterval(interval)
      }
    </script>
    </body>
    </html>
    

    演示:专业操作,请勿模仿

    啥丢操作.gif

    实现即时弹幕

    弹幕我就懒得实现了,再new 一个websocket做弹幕链接,然后用js把接收的弹幕在显示地方移动过来就行了。

    相关文章

      网友评论

        本文标题:websocket实现实时直播(滑鸡版)

        本文链接:https://www.haomeiwen.com/subject/jfavfhtx.html