点播弹幕实现
简要说明
之前突然觉得弹幕视频还挺有意思的,就想自己也实现点播弹幕和直播弹幕。由于是主学后端的,对于自己手动实现前端的弹幕功能感到困难,于是上网搜索有没有相关实现,首先找到了** CommentCoreLibrary**项目,不过貌似只能全局script导入,没有实现相应模块化,而我前端用的是vue3+ts,用起来感觉很难受,遂放弃。又经过一番搜寻,找到了 **DPlayer**项目,看了它的文档,正如它开头的一句🍭 Wow, such a lovely HTML5 danmaku video player
,首先实现了一个功能很完善的播放器,同时也支持定制化改造,完美超出我的预期。OK!找到这个项目后我已经完全编写代码就绪了,给项目起个名Zplay
,开写。
后来发现西瓜播放器也很好。
使用的技术
前端: vite+vue3+ts
记得上一次写前端是vue-cli+vue2,而上上次就是学习三件套+Jquery,还没怎么用,每次写前端代码都会学不少新东西,有点大跃进,感觉花里胡哨的是做不出来的,不过普普通通的还是没问题的,毕竟还有一堆现成的。
后端: SpringBoot+Mybatis-Plus
前后端接口
后端与数据库接口:
DROP DATABASE IF EXISTS `zplay`;
CREATE DATABASE `zplay` CHARACTER SET 'utf8mb4';
USE zplay;
CREATE TABLE danmaku(
`id` INT UNSIGNED AUTO_INCREMENT,
`barrage_type` TINYINT NOT NULL COMMENT "弹幕类型",
`time` DOUBLE NOT NULL COMMENT "弹幕时间",
`color` INT UNSIGNED NOT NULL DEFAULT 16777215 COMMENT "颜色16进制数",
`content` VARCHAR(50) NOT NULL,
`author` VARCHAR(20) NOT NULL,
`video_id` INT NOT NULL,
`gmt_create` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(),
PRIMARY KEY (`id`),
KEY `idx_video_id`(`video_id`)
)ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4;
后端实体类兼DTO
注解是Mybatis-Plus的注解:
@TableName("danmaku")
@Data
public class Barrage {
@TableId(value = "id",type = IdType.AUTO)
private Long id;
@TableField("barrage_type")
private Byte barrageType;
@TableField("time")
private Double time;
@TableField("color")
private Long color;
@TableField("content")
private String content;
@TableField("author")
private String author;
@TableField("video_id")
private Long videoId;
@TableField("gmt_create")
private Date createTime;
@TableField(exist = false) //直播时标识客户端的属性
private String sign;
}
后端响应简易封装
code,msg规范起来的话或许用枚举类实现。
@Data
public class Result<T> {
private Integer code;
private String msg;
private T data;
public Result() {
}
public Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <E> Result<E> ok(E data) {
return new Result<E>(200,"successful",data);
}
public static <E> Result<E> fail(E data) {
return new Result<E>(100,"fail",data);
}
}
前端相应接口:
有一些和Dplayer
ts声明指定的类型不同,比如id,color,不过有需要可以先转换为any就可以了,主要刚开始没注意,color是16进制数代表的颜色。
export interface Barrage {
id?: Number,
barrageType: Number,
time: Number,
color: Number,
content: string,
author: string,
videoId: Number,
createTime?: Date,
sign?: string
}
export interface DplayerBarrage {
type: Number,
time: Number,
color: Number,
text: string,
author: string
}
前端实现
查看了Dplayer文档,实现弹幕功能只需要创建Dplayer时重写apiBackend
即可,同时提供danmaku
属性即可拥有发弹幕的组件,另外url其实可以随便指定,只要在apibackend
中使用自己的url就可以了,另外Dplayer含支持MSE扩展,这里我使用了flv.js,于是代码如下:
function loadPlayer(videoInfo: VideoInfo) {
const dp = new DPlayer({
container: document.getElementById("video-player"),
video: {
url: videoInfo.url,
type: "customFlv",
customType: {
customFlv: function (video: HTMLMediaElement, player: any) {
const flvPlayer = flvjs.createPlayer({
type: 'flv',
url: video.src,
});
flvPlayer.attachMediaElement(video);
flvPlayer.load();
},
},
},
danmaku: {
id: String(videoInfo.id),
api: "http://127.0.0.1:3000/danmu",
},
apiBackend: {
read: (options)=> {
getBarrages(videoInfo.id).then((res)=>{
const danmus: Array<DplayerBarrage> = res.data.map(item=>{
return {
type: item.barrageType,
time: item.time,
color: item.color,
text: item.content,
author: item.author
}
})
options.success(danmus);
console.log(danmus);
}).catch((e)=>{
console.error(e);
options.error && options.error();
})
},
send: (options)=>{
const danmu = options.data;
const barrage: Barrage = {
barrageType: danmu.type,
content: danmu.text,
time: danmu.time,
color: danmu.color,
author: danmu.author,
videoId: danmu.id
}
sendBarrage(barrage).then((res)=>{
options.success && options.success(res.data)
}).catch((e)=>{
console.error(e);
options.error && options.error();
})
}
}
});
}
至于其中的options,是阅读了Dplayer源码得到的,发收弹幕实际上都是调用了源码中danmaku.js
中的方法,并传参options
调用api,阅读源码可查看细节参数。
后端实现
后端实现还是比较简单的,读取弹幕根据videoId获取相应弹幕返回即可,发送弹幕把弹幕存入数据库即可,没什么坑点。
跨域设置
自己调试的时候,因为前后端使用的是不同端口,所以也需要设置跨域。SpringBoot有两种常见的设置跨域的方法。一个是写配置类实现WebMvcConfigurer
接口,一个是写拦截器 ,这里采用第一种。
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET","POST")
.allowCredentials(true)
.allowedHeaders("*")
.allowedOriginPatterns("*")
.maxAge(3600);
}
}
杂项
1.设置"@"路径别名
vite.config.ts
中的配置:
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
}
tsconfig
中配置:
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
}