自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

老板怒吼:今晚整一個B站彈幕交互功能

開發(fā) 前端 開發(fā)工具
2021 年了,還有不支持彈幕的視頻網站嗎,現(xiàn)在各種彈幕玩法層出不窮,抽獎,PPT 都上彈幕玩法了,不整個彈幕都說不過去了。

 [[422775]]

圖片來自 包圖網

今天筆者就抽空做了一個實時視頻彈幕交互的小功能,不得不說這樣的形式為看視頻看直播,講義 PPT,抽獎等形式增加了許多樂趣。

技術選型

①Netty

官方對于 Netty 的描述:

  1. https://netty.io/ 

主要關鍵詞描述:Netty 是異步事件驅動網絡框架,可做各種協(xié)議服務端,并且支持了 FTP,SMTP,HTTP 等很多協(xié)議,并且性能,穩(wěn)定性,靈活性都很棒。

可以看到 Netty 整體架構上分了三個部分:

  • 以零拷貝,一致性接口,擴展事件模型的底層核心。
  • Socket,Datagram,Pipe,Http Tunnel 作為傳輸媒介。
  • 傳輸支持的各種協(xié)議,HTTP&WebSocket,SSL,大文件,zlib/gzip 壓縮,文本,二進制,Google Protobuf 等各種各種的傳輸形式。

②WebSocket

WebSocket 是一種在單個 TCP 連接上進行全雙工通信的協(xié)議。WebSocket 通信協(xié)議于 2011 年被 IETF 定為標準 RFC 6455,并由 RFC7936 補充規(guī)范。

WebSocket API 也被 W3C 定為標準。WebSocket 使得客戶端和服務器之間的數(shù)據(jù)交換變得更加簡單,允許服務端主動向客戶端推送數(shù)據(jù)。

在 WebSocket API 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進行雙向數(shù)據(jù)傳輸。

為什么做這樣的技術選型:

  • 由上述可知,實時直播交互作為互動式是一個雙向數(shù)據(jù)傳輸過程。所以使用 WebSocket。
  • Netty 本身支持了 WebSocket 協(xié)議的實現(xiàn),讓實現(xiàn)更加簡單方便。

實現(xiàn)思路

①服務架構

整體架構是所有客戶端都和我的服務端開啟一個雙向通道的架構。

②傳輸流程

如下圖:

實現(xiàn)效果

先看看效果吧,是不是 perfect,接下來就來看具體代碼是怎么實現(xiàn)的吧。

視頻直播彈幕示例

代碼實現(xiàn)

①項目結構

一個 maven 項目,將代碼放一個包下就行。

②Java 服務端

Java 服務端代碼,總共三個類,Server,Initailizer 和 Handler。

先做一個 netty nio 的服務端:一個 nio 的服務,開啟一個 tcp 端口。

  1. import io.netty.bootstrap.ServerBootstrap; 
  2. import io.netty.channel.ChannelFuture; 
  3. import io.netty.channel.EventLoopGroup; 
  4. import io.netty.channel.nio.NioEventLoopGroup; 
  5. import io.netty.channel.socket.nio.NioServerSocketChannel; 
  6.  
  7. /** 
  8.  * Copyright(c)lbhbinhao@163.com 
  9.  * @author liubinhao 
  10.  * @date 2021/1/14 
  11.  * ++++ ______                           ______             ______ 
  12.  * +++/     /|                         /     /|           /     /| 
  13.  * +/_____/  |                       /_____/  |         /_____/  | 
  14.  * |     |   |                      |     |   |        |     |   | 
  15.  * |     |   |                      |     |   |________|     |   | 
  16.  * |     |   |                      |     |  /         |     |   | 
  17.  * |     |   |                      |     |/___________|     |   | 
  18.  * |     |   |___________________   |     |____________|     |   | 
  19.  * |     |  /                  / |  |     |   |        |     |   | 
  20.  * |     |/ _________________/  /   |     |  /         |     |  / 
  21.  * |_________________________|/b    |_____|/           |_____|/ 
  22.  */ 
  23. public enum BulletChatServer { 
  24.     /** 
  25.      * Server instance 
  26.      */ 
  27.     SERVER; 
  28.  
  29.     private BulletChatServer(){ 
  30.         EventLoopGroup mainGroup = new NioEventLoopGroup(); 
  31.         EventLoopGroup subGroup  = new NioEventLoopGroup(); 
  32.         ServerBootstrap server = new ServerBootstrap(); 
  33.         server.group(mainGroup,subGroup) 
  34.                 .channel(NioServerSocketChannel.class) 
  35.                 .childHandler(new BulletChatInitializer()); 
  36.         ChannelFuture future = server.bind(9123); 
  37.     } 
  38.  
  39.     public static void main(String[] args) { 
  40.  
  41.     } 
  42.  

服務端的具體處理邏輯:

  1. import io.netty.channel.ChannelInitializer; 
  2. import io.netty.channel.ChannelPipeline; 
  3. import io.netty.channel.socket.SocketChannel; 
  4. import io.netty.handler.codec.http.HttpObjectAggregator; 
  5. import io.netty.handler.codec.http.HttpServerCodec; 
  6. import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; 
  7. import io.netty.handler.stream.ChunkedWriteHandler; 
  8. import io.netty.handler.timeout.IdleStateHandler; 
  9.  
  10. /** 
  11.  * Copyright(c)lbhbinhao@163.com 
  12.  * 
  13.  * @author liubinhao 
  14.  * @date 2021/1/14 
  15.  * ++++ ______                           ______             ______ 
  16.  * +++/     /|                         /     /|           /     /| 
  17.  * +/_____/  |                       /_____/  |         /_____/  | 
  18.  * |     |   |                      |     |   |        |     |   | 
  19.  * |     |   |                      |     |   |________|     |   | 
  20.  * |     |   |                      |     |  /         |     |   | 
  21.  * |     |   |                      |     |/___________|     |   | 
  22.  * |     |   |___________________   |     |____________|     |   | 
  23.  * |     |  /                  / |  |     |   |        |     |   | 
  24.  * |     |/ _________________/  /   |     |  /         |     |  / 
  25.  * |_________________________|/b    |_____|/           |_____|/ 
  26.  */ 
  27.  
  28. public class BulletChatInitializer extends ChannelInitializer<SocketChannel> { 
  29.     @Override 
  30.     protected void initChannel(SocketChannel ch) throws Exception { 
  31.         ChannelPipeline pipeline = ch.pipeline(); 
  32.         pipeline.addLast(new HttpServerCodec()); 
  33.         pipeline.addLast(new ChunkedWriteHandler()); 
  34.         pipeline.addLast(new HttpObjectAggregator(1024*64)); 
  35.         pipeline.addLast(new IdleStateHandler(8, 10, 12)); 
  36.         pipeline.addLast(new WebSocketServerProtocolHandler("/lbh")); 
  37.         pipeline.addLast(new BulletChatHandler()); 
  38.     } 

后臺處理邏輯,接受到消息,寫出到所有的客戶端:

  1. import io.netty.channel.Channel; 
  2. import io.netty.channel.ChannelHandler; 
  3. import io.netty.channel.ChannelHandlerContext; 
  4. import io.netty.channel.SimpleChannelInboundHandler; 
  5. import io.netty.channel.group.ChannelGroup; 
  6. import io.netty.channel.group.DefaultChannelGroup; 
  7. import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 
  8. import io.netty.util.concurrent.EventExecutorGroup; 
  9. import io.netty.util.concurrent.GlobalEventExecutor; 
  10.  
  11. /** 
  12.  * Copyright(c)lbhbinhao@163.com 
  13.  * 
  14.  * @author liubinhao 
  15.  * @date 2021/1/14 
  16.  * ++++ ______                           ______             ______ 
  17.  * +++/     /|                         /     /|           /     /| 
  18.  * +/_____/  |                       /_____/  |         /_____/  | 
  19.  * |     |   |                      |     |   |        |     |   | 
  20.  * |     |   |                      |     |   |________|     |   | 
  21.  * |     |   |                      |     |  /         |     |   | 
  22.  * |     |   |                      |     |/___________|     |   | 
  23.  * |     |   |___________________   |     |____________|     |   | 
  24.  * |     |  /                  / |  |     |   |        |     |   | 
  25.  * |     |/ _________________/  /   |     |  /         |     |  / 
  26.  * |_________________________|/b    |_____|/           |_____|/ 
  27.  */ 
  28.  
  29. public class BulletChatHandler  extends SimpleChannelInboundHandler<TextWebSocketFrame> { 
  30.     // 用于記錄和管理所有客戶端的channel 
  31.     public static ChannelGroup channels = 
  32.             new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); 
  33.     @Override 
  34.     protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { 
  35.         // 獲取客戶端傳輸過來的消息 
  36.         String content = msg.text(); 
  37.         System.err.println("收到消息:"+ content); 
  38.         channels.writeAndFlush(new TextWebSocketFrame(content)); 
  39.         System.err.println("寫出消息完成:"+content); 
  40.     } 
  41.  
  42.     @Override 
  43.     public void handlerAdded(ChannelHandlerContext ctx) throws Exception { 
  44.         channels.add(ctx.channel()); 
  45.     } 
  46.  
  47.     @Override 
  48.     public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { 
  49.  
  50.         String channelId = ctx.channel().id().asShortText(); 
  51.         System.out.println("客戶端被移除,channelId為:" + channelId); 
  52.         channels.remove(ctx.channel()); 
  53.     } 
  54.  
  55.     @Override 
  56.     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
  57.         cause.printStackTrace(); 
  58.         // 發(fā)生異常之后關閉連接(關閉channel),隨后從ChannelGroup中移除 
  59.         ctx.channel().close(); 
  60.         channels.remove(ctx.channel()); 
  61.     } 
  62.  

③網頁客戶端實現(xiàn)

代碼如下:

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="utf-8"
  5.     <meta http-equiv="X-UA-Compatible" content="IE=edge"
  6.     <title>Netty視頻彈幕實現(xiàn) Author:Binhao Liu</title> 
  7.     <link rel="stylesheet" href=""
  8.     <style type="text/css" media="screen"
  9.         * { 
  10.             margin: 0px; 
  11.             padding: 0px 
  12.         } 
  13.  
  14.         html, body { 
  15.             height: 100% 
  16.         } 
  17.  
  18.         body { 
  19.             overflow: hidden; 
  20.             background-color: #FFF; 
  21.             text-align: center; 
  22.         } 
  23.  
  24.         .flex-column { 
  25.             display: flex; 
  26.             flex-direction: column
  27.             justify-content: space-between;, align-items: center; 
  28.         } 
  29.  
  30.         .flex-row { 
  31.             display: flex; 
  32.             flex-direction: row; 
  33.             justify-content: center; 
  34.             align-items: center; 
  35.         } 
  36.  
  37.         .wrap { 
  38.             overflow: hidden; 
  39.             width: 70%; 
  40.             height: 600px; 
  41.             margin: 100px auto; 
  42.             padding: 20px; 
  43.             background-color: transparent; 
  44.             box-shadow: 0 0 9px #222; 
  45.             border-radius: 20px; 
  46.         } 
  47.  
  48.         .wrap .box { 
  49.             position: relative
  50.             width: 100%; 
  51.             height: 90%; 
  52.             background-color: #000000; 
  53.             border-radius: 10px 
  54.         } 
  55.  
  56.         .wrap .box span { 
  57.             position: absolute
  58.             top: 10px; 
  59.             left: 20px; 
  60.             display: block; 
  61.             padding: 10px; 
  62.             color: #336688 
  63.         } 
  64.  
  65.         .wrap .send { 
  66.             display: flex; 
  67.             width: 100%; 
  68.             height: 10%; 
  69.             background-color: #000000; 
  70.             border-radius: 8px 
  71.         } 
  72.  
  73.         .wrap .send input { 
  74.             width: 40%; 
  75.             height: 60%; 
  76.             border: 0; 
  77.             outline: 0; 
  78.             border-radius: 5px 0px 0px 5px; 
  79.             box-shadow: 0px 0px 5px #d9d9d9; 
  80.             text-indent: 1em 
  81.         } 
  82.  
  83.         .wrap .send .send-btn { 
  84.             width: 100px; 
  85.             height: 60%; 
  86.             background-color: #fe943b; 
  87.             color: #FFF; 
  88.             text-align: center; 
  89.             border-radius: 0px 5px 5px 0px; 
  90.             line-height: 30px; 
  91.             cursor: pointer; 
  92.         } 
  93.  
  94.         .wrap .send .send-btn:hover { 
  95.             background-color: #4cacdc 
  96.         } 
  97.     </style> 
  98. </head> 
  99. <script> 
  100.     var ws = new WebSocket("ws://localhost:9123/lbh"); 
  101.  
  102.     ws.onopen = function () { 
  103.         // Web Socket 已連接上,使用 send() 方法發(fā)送數(shù)據(jù) 
  104.         alert("數(shù)據(jù)發(fā)送中..."); 
  105.     }; 
  106.     ws.onmessage = function (e) { 
  107.         console.log("接受到消息:"+e.data); 
  108.         createEle(e.data); 
  109.     }; 
  110.     ws.onclose = function () { 
  111.         // 關閉 websocket 
  112.         alert("連接已關閉..."); 
  113.     }; 
  114.     function sendMsg(msg) { 
  115.         ws.send(msg) 
  116.     } 
  117.  
  118.  
  119. </script> 
  120. <body> 
  121. <div class="wrap flex-column"
  122.     <div class="box"
  123.         <video src="shape.mp4" width="100%" height="100%" controls autoplay></video> 
  124.     </div> 
  125.     <div class="send flex-row"
  126.  
  127.         <input type="text" class="con" placeholder="彈幕發(fā)送[]~(^v^)~*"/> 
  128.  
  129.         <div class="send-btn" onclick="javascript:sendMsg(document.querySelector('.con').value)">發(fā)送</div> 
  130.     </div> 
  131. </div> 
  132. <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js" type="text/javascript"></script> 
  133. <script> 
  134.     //1.獲取元素 
  135.     var oBox = document.querySelector('.box');   //獲取.box元素 
  136.     var cW = oBox.offsetWidth;   //獲取box的寬度 
  137.     var cH = oBox.offsetHeight;   //獲取box的高度 
  138.     function createEle(txt) { 
  139.         //動態(tài)生成span標簽 
  140.         var oMessage = document.createElement('span');   //創(chuàng)建標簽 
  141.         oMessage.innerHTML = txt;   //接收參數(shù)txt并且生成替換內容 
  142.         oMessage.style.left = cW + 'px';  //初始化生成位置x 
  143.         oBox.appendChild(oMessage);   //把標簽塞到oBox里面 
  144.         roll.call(oMessage, { 
  145.             //call改變函數(shù)內部this的指向 
  146.             timing: ['linear''ease-out'][~~(Math.random() * 2)], 
  147.             color: '#' + (~~(Math.random() * (1 << 24))).toString(16), 
  148.             top: random(0, cH), 
  149.             fontSize: random(16, 32) 
  150.         }); 
  151.     } 
  152.  
  153.     function roll(opt) { 
  154.         //彈幕滾動 
  155.         //如果對象中不存在timing 初始化 
  156.         opt.timing = opt.timing || 'linear'
  157.         opt.color = opt.color || '#fff'
  158.         opt.top = opt.top || 0; 
  159.         opt.fontSize = opt.fontSize || 16; 
  160.         this._left = parseInt(this.offsetLeft);   //獲取當前left的值 
  161.         this.style.color = opt.color;   //初始化顏色 
  162.         this.style.top = opt.top + 'px'
  163.         this.style.fontSize = opt.fontSize + 'px'
  164.         this.timer = setInterval(function () { 
  165.             if (this._left <= 100) { 
  166.                 clearInterval(this.timer);   //終止定時器 
  167.                 this.parentNode.removeChild(this); 
  168.                 return;   //終止函數(shù) 
  169.             } 
  170.             switch (opt.timing) { 
  171.                 case 'linear':   //如果勻速 
  172.                     this._left += -2; 
  173.                     break; 
  174.                 case 'ease-out':   // 
  175.                     this._left += (0 - this._left) * .01; 
  176.                     break; 
  177.             } 
  178.             this.style.left = this._left + 'px'
  179.         }.bind(this), 1000 / 60); 
  180.     } 
  181.  
  182.     function random(start, end) { 
  183.         //隨機數(shù)封裝 
  184.         return start + ~~(Math.random() * (end - start)); 
  185.     } 
  186.  
  187.     var aLi = document.querySelectorAll('li');   //10 
  188.  
  189.     function forEach(ele, cb) { 
  190.         for (var i = 0, len = aLi.length; i < len; i++) { 
  191.             cb && cb(ele[i], i); 
  192.         } 
  193.     } 
  194.  
  195.     forEach(aLi, function (ele, i) { 
  196.         ele.style.left = i * 100 + 'px'
  197.     }); 
  198.     //產生閉包 
  199.     var obj = { 
  200.         num: 1, 
  201.         addfunction () { 
  202.             this.num++;   //obj.num = 2; 
  203.             (function () { 
  204.                 console.log(this.num); 
  205.             }) 
  206.         } 
  207.     }; 
  208.     obj.add();//window 
  209.  
  210. </script> 
  211. </body> 
  212. </html> 

這樣一個實時的視頻彈幕功能就完成啦,是不是很簡單,各位小伙伴快來試試吧。

小結

這個還是很簡單,筆者寫這個的時候一會兒就寫完了。不過這也得益于筆者很久以前就寫過 Netty 的服務,對于 HTTP,TCP 之類協(xié)議也比較熟悉。

只有前端會有些難度,問下度娘,也很快能做完,在此分享出來與諸君分享,有問題可找筆者交流。

作者:興趣使然的程序猿

編輯:陶家龍

出處:http://adkx.net/w71wf

 

責任編輯:武曉燕 來源: adkx.net
相關推薦

2022-04-26 10:47:53

分配權限vuejs

2018-01-04 09:20:55

python爬蟲視頻彈幕

2024-08-06 10:16:52

Java AgentJava

2020-10-20 14:12:54

B站開源彈幕

2021-10-29 07:49:23

Python彈幕播放

2021-04-01 07:44:45

排名調整Java Java基礎

2020-03-26 09:36:06

AB Test平臺的流量

2025-04-28 08:59:25

2009-08-26 17:05:55

.NET委托

2012-12-07 10:04:58

管理項目管理日常管理

2022-12-26 00:00:05

Python爬蟲B站彈幕

2021-07-15 09:49:08

B站宕機黑客

2012-04-24 09:22:16

軟件測試

2013-08-13 10:07:57

移動互聯(lián)網移動開發(fā)轉行創(chuàng)業(yè)

2021-04-09 08:13:14

API網關互聯(lián)網

2022-06-07 07:38:43

威士忌氣味測試

2021-12-14 17:19:15

存儲數(shù)據(jù)

2015-06-03 16:58:01

創(chuàng)業(yè)創(chuàng)業(yè)干貨

2020-08-05 07:37:29

任務系統(tǒng)定時

2021-03-05 07:47:07

工作流引擎節(jié)點
點贊
收藏

51CTO技術棧公眾號