Java 实现视频弹幕功能
2021年了,还有不支持弹幕的视频网站吗,现在各种弹幕玩法层出不穷,抽奖,ppt都上弹幕玩法了,不整个弹幕都说不过去了,今天笔者就抽空做了一个实时视频弹幕交互功能的实现,不得不说这样的形式为看视频看直播,讲义PPT,抽奖等形式增加了许多乐趣。
1 技术选型
1.1 netty
官方对于netty的描述:
https://netty.io/
主要关键词描述:netty是异步事件驱动网络框架,可做各种协议服务端,并且支持了FTP,SMTP,HTTP等很多协议,并且性能,稳定性,灵活性都很棒。
可以看到netty整体架构上分了三个部分:
以零拷贝,一致性接口,扩展事件模型的底层核心。
Socket,Datagram,Pipe,Http Tunnel作为传输媒介。
传输支持的各种协议,HTTP&WebSocket,SSL,大文件,zlib/gzip压缩,文本,二进制,Google Protobuf等各种各种的传输形式。
1.2 WebSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
1.3 为什么做这样的技术选型。
由上述可知,实时直播交互作为互动式是一个双向数据传输过程。所以使用webSocket。
netty本身支持了webSocket协议的实现,让实现更加简单方便。
2 实现思路
2.1 服务架构
整体架构是所有客户端都和我的服务端开启一个双向通道的架构。
2.2 传输流程
3 实现效果
3.1 视频展示
先看看效果吧,是不是perfect,接下来就来看具体代码是怎么实现的吧。
4 代码实现
4.1 项目结构
一个maven项目,将代码放一个包下就行。
4.2 Java服务端
Java服务端代码,总共三个类,Server,Initailizer和 Handler。
4.2.1 先做一个netty nio的服务端:
一个nio的服务,开启一个tcp端口。
/*** Copyright(c)lbhbinhao@163.com* @author liubinhao* @date 2021/1/14* ++++ ______ ______ ______* +++/ /| / /| / /|* +/_____/ | /_____/ | /_____/ |* | | | | | | | | |* | | | | | |________| | |* | | | | | / | | |* | | | | |/___________| | |* | | |___________________ | |____________| | |* | | / / | | | | | | |* | |/ _________________/ / | | / | | /* |_________________________|/b |_____|/ |_____|/*/
public enum BulletChatServer {/*** Server instance*/SERVER;private BulletChatServer(){EventLoopGroup mainGroup = new NioEventLoopGroup();EventLoopGroup subGroup = new NioEventLoopGroup();ServerBootstrap server = new ServerBootstrap();server.group(mainGroup,subGroup).channel(NioServerSocketChannel.class).childHandler(new BulletChatInitializer());ChannelFuture future = server.bind(9123);}public static void main(String[] args) {}}
4.2.2 服务端的具体处理逻辑
/*** Copyright(c)lbhbinhao@163.com** @author liubinhao* @date 2021/1/14* ++++ ______ ______ ______* +++/ /| / /| / /|* +/_____/ | /_____/ | /_____/ |* | | | | | | | | |* | | | | | |________| | |* | | | | | / | | |* | | | | |/___________| | |* | | |___________________ | |____________| | |* | | / / | | | | | | |* | |/ _________________/ / | | / | | /* |_________________________|/b |_____|/ |_____|/*/public class BulletChatInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new HttpServerCodec());pipeline.addLast(new ChunkedWriteHandler());pipeline.addLast(new HttpObjectAggregator(1024*64));pipeline.addLast(new IdleStateHandler(8, 10, 12));pipeline.addLast(new WebSocketServerProtocolHandler("/lbh"));pipeline.addLast(new BulletChatHandler());}
}
后台处理逻辑,接受到消息,写出到所有的客户端:
/*** Copyright(c)lbhbinhao@163.com** @author liubinhao* @date 2021/1/14* ++++ ______ ______ ______* +++/ /| / /| / /|* +/_____/ | /_____/ | /_____/ |* | | | | | | | | |* | | | | | |________| | |* | | | | | / | | |* | | | | |/___________| | |* | | |___________________ | |____________| | |* | | / / | | | | | | |* | |/ _________________/ / | | / | | /* |_________________________|/b |_____|/ |_____|/*/public class BulletChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {// 用于记录和管理所有客户端的channelpublic static ChannelGroup channels =new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);@Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {// 获取客户端传输过来的消息String content = msg.text();System.err.println("收到消息:"+ content);channels.writeAndFlush(new TextWebSocketFrame(content));System.err.println("写出消息完成:"+content);}@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {channels.add(ctx.channel());}@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {String channelId = ctx.channel().id().asShortText();System.out.println("客户端被移除,channelId为:" + channelId);channels.remove(ctx.channel());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();// 发生异常之后关闭连接(关闭channel),随后从ChannelGroup中移除ctx.channel().close();channels.remove(ctx.channel());}}
4.3 网页客户端实现
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>Netty视频弹幕实现 Author:Binhao Liu</title><link rel="stylesheet" href=""><style type="text/css" media="screen">* {margin: 0px;padding: 0px}html, body {height: 100%}body {overflow: hidden;background-color: #FFF;text-align: center;}.flex-column {display: flex;flex-direction: column;justify-content: space-between;, align-items: center;}.flex-row {display: flex;flex-direction: row;justify-content: center;align-items: center;}.wrap {overflow: hidden;width: 70%;height: 600px;margin: 100px auto;padding: 20px;background-color: transparent;box-shadow: 0 0 9px #222;border-radius: 20px;}.wrap .box {position: relative;width: 100%;height: 90%;background-color: #000000;border-radius: 10px}.wrap .box span {position: absolute;top: 10px;left: 20px;display: block;padding: 10px;color: #336688}.wrap .send {display: flex;width: 100%;height: 10%;background-color: #000000;border-radius: 8px}.wrap .send input {width: 40%;height: 60%;border: 0;outline: 0;border-radius: 5px 0px 0px 5px;box-shadow: 0px 0px 5px #d9d9d9;text-indent: 1em}.wrap .send .send-btn {width: 100px;height: 60%;background-color: #fe943b;color: #FFF;text-align: center;border-radius: 0px 5px 5px 0px;line-height: 30px;cursor: pointer;}.wrap .send .send-btn:hover {background-color: #4cacdc}</style>
</head>
<script>var ws = new WebSocket("ws://localhost:9123/lbh");ws.onopen = function () {// Web Socket 已连接上,使用 send() 方法发送数据alert("数据发送中...");};ws.onmessage = function (e) {console.log("接受到消息:"+e.data);createEle(e.data);};ws.onclose = function () {// 关闭 websocketalert("连接已关闭...");};function sendMsg(msg) {ws.send(msg)}</script>
<body>
<div class="wrap flex-column"><div class="box"><video src="shape.mp4" width="100%" height="100%" controls autoplay></video></div><div class="send flex-row"><input type="text" class="con" placeholder="弹幕发送[]~(^v^)~*"/><div class="send-btn" onclick="javascript:sendMsg(document.querySelector('.con').value)">发送</div></div>
</div>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js" type="text/javascript"></script>
<script>//1.获取元素var oBox = document.querySelector('.box'); //获取.box元素var cW = oBox.offsetWidth; //获取box的宽度var cH = oBox.offsetHeight; //获取box的高度function createEle(txt) {//动态生成span标签var oMessage = document.createElement('span'); //创建标签oMessage.innerHTML = txt; //接收参数txt并且生成替换内容oMessage.style.left = cW + 'px'; //初始化生成位置xoBox.appendChild(oMessage); //把标签塞到oBox里面roll.call(oMessage, {//call改变函数内部this的指向timing: ['linear', 'ease-out'][~~(Math.random() * 2)],color: '#' + (~~(Math.random() * (1 << 24))).toString(16),top: random(0, cH),fontSize: random(16, 32)});}function roll(opt) {//弹幕滚动//如果对象中不存在timing 初始化opt.timing = opt.timing || 'linear';opt.color = opt.color || '#fff';opt.top = opt.top || 0;opt.fontSize = opt.fontSize || 16;this._left = parseInt(this.offsetLeft); //获取当前left的值this.style.color = opt.color; //初始化颜色this.style.top = opt.top + 'px';this.style.fontSize = opt.fontSize + 'px';this.timer = setInterval(function () {if (this._left <= 100) {clearInterval(this.timer); //终止定时器this.parentNode.removeChild(this);return; //终止函数}switch (opt.timing) {case 'linear': //如果匀速this._left += -2;break;case 'ease-out': //this._left += (0 - this._left) * .01;break;}this.style.left = this._left + 'px';}.bind(this), 1000 / 60);}function random(start, end) {//随机数封装return start + ~~(Math.random() * (end - start));}var aLi = document.querySelectorAll('li'); //10function forEach(ele, cb) {for (var i = 0, len = aLi.length; i < len; i++) {cb && cb(ele[i], i);}}forEach(aLi, function (ele, i) {ele.style.left = i * 100 + 'px';});//产生闭包var obj = {num: 1,add: function () {this.num++; //obj.num = 2;(function () {console.log(this.num);})}};obj.add();//window</script>
</body>
</html>
这样一个实时的视频弹幕功能就完成啦,是不是很简单,各位小伙伴快来试试吧。
5 小结
上班撸代码,下班继续撸代码写博客,这个还是很简单,笔者写这个的时候一会儿就写完了,不过这也得益于笔者很久以前就写过netty的服务,对于Http,Tcp之类协议也比较熟悉,只有前端会有些难度,问下度娘,也很快能做完,在此分享出来与诸君分享,有问题可找笔者交流。
往期推荐
Redis分布式锁需要考虑的这些事!
Redis 面试题!精华!收藏一波 !
解决kafka 消息堆积问题的排查及调优
MySQL 的隔离级别和事务需要知道的
3行代码能写出8个接口!同事这样做的
Java 服务性能优化,提升QPS
API 接口文档平台,多人协作太顺手了
回复【干货】获取精选干货视频教程
回复【加群】加入疑难问题攻坚交流群
回复【mat】获取内存溢出问题分析详细文档教程
回复【赚钱】获取用java写一个能赚钱的微信机器人
回复【副业】获取程序员副业攻略一份
戳这儿
Java 实现视频弹幕功能相关推荐
- JavaScript css3模拟简单的视频弹幕功能
最近相对比较空闲,想写一些东西写着玩.就尝试写了一个demo模拟了最简单的视频弹幕功能~~. 思路: 设置一个<div>和所播放的video的大小一致,把这个div标签蒙在video上面用 ...
- 直播电商平台开发,video组件实现视频弹幕功能
直播电商平台开发,video组件实现视频弹幕功能 发送弹幕 WXML文件代码如下: <!--pages/video/video.wxml--> <video class=" ...
- 视频弹幕技术 php,HTML5实现视频弹幕功能
1.首先展示一下弹幕视频弹幕原图,事实说话 2.代码展示 1>html代码展示 关闭弹幕 开启静音 2>css代码展示 *{ padding: 0; margin: 0; } input{ ...
- java实现视频弹幕效果,SpringBoot实现视频弹幕功能 DanmuPlayer插件的使用
前几天朋友让帮忙做一个视频弹幕网站,在找弹幕实现上发现了一个不错的开源的插件. 本文介绍一下基本使用. 一.下载插件和官方示例 直接从官网下载,可以运行官方的 demo 例子先玩一下. 我这里进行了一 ...
- java获取视频弹幕
发生缘由: 通过哔哩哔哩动画下载视频到本地文件里面,看视频没有弹幕,所以我想着看一下大神们都在说什么. 但是下载的弹幕都在一个文件里面,并且里面的弹幕都在一行里面.可读性十分的差,所以我想着加一些换行 ...
- python3抓取b站弹幕_python3写爬取B站视频弹幕功能
需要准备的环境: 一个B站账号,需要先登录,否则不能查看历史弹幕记录 联网的电脑和顺手的浏览器,我用的Chrome Python3环境以及request模块,安装使用命令,换源比较快: pip3 in ...
- html5 canvas实现高并发视频弹幕功能
由于项目需要,分享自己开发的高并发弹幕功能 以下为源代码,仅80行而已,可以根据canvas 的宽高自动适配: /*!*@作者: 赵玉*@邮箱: sailiy@126.com*@公司: 彩虹世纪文化传 ...
- JAVA计算机毕业设计弹幕视频网站计算机(附源码、数据库)
JAVA计算机毕业设计弹幕视频网站计算机(附源码.数据库) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Eclispe ...
- java毕业生设计弹幕视频网站计算机源码+系统+mysql+调试部署+lw
java毕业生设计弹幕视频网站计算机源码+系统+mysql+调试部署+lw java毕业生设计弹幕视频网站计算机源码+系统+mysql+调试部署+lw 本源码技术栈: 项目架构:B/S架构 开发语言: ...
最新文章
- 如何将一个字典转换为玲阶矩阵_基础渲染系列(一)图形学的基石——矩阵
- openssl 使用命令
- Android开发面试题Service之startService和bindService之间的区别
- 如何交到一个女朋友?
- n1运行linux,斐讯N1折腾记:运行 Linux 及优化
- predis操作大全
- 注解的定义与反射调用
- HTML中包含地图和筛选条件,如何实现DedeCMS多条件筛选并以筛选词为标题
- 模仿电影中黑客电脑界面,CMD装逼代码
- VvvebJs可视化前端设计开发工具
- 什么是IPv6,IPv6有什么优势
- Mac下使用Eclipse读java源码
- django教程day07
- Java中grabImage_JavaCV实现将视频以帧方式抽取
- 精准医学: 应用脑脊液游离DNA全基因组甲基化测序筛选小儿髓母细 胞瘤早期诊断与预后监测的可靠生物标志物|液体活检专题
- cv2.connectedComponentsWithStats 计算不规则连通区域
- 小白必看!渗透测试的8个步骤
- 【CES遇见人工智能】欧莱雅发微型可穿戴设备:可帮助保护你的皮肤
- pta答案厦门大学C语言,C语言I博客作业02 - osc_dmzfpa0c的个人空间 - OSCHINA - 中文开源技术交流社区...
- spl android,SPL Spectrum Analyzer
热门文章
- 【热点解读】冬奥会上的中国元素
- python文件读写、字典、习题、模块包库、pycharm、面向对象
- 重磅:CMMI DEV V2.0发布!
- random.seed(seed)、np.random.seed(seed)、torch.manual_seed(seed)作用
- Ranklib部分源码分析
- [Alpha] Scrum Meeting 2 - TEAM LESS ERROR
- 如何一下清空微信好友_微信通讯录中的好友,怎样全部删除?
- 网络游戏服务器架构流程
- Unknown database ‘xxx‘
- 计算机健康小知识,日常生活养生小知识