写一个基于js的视频弹幕功能时遇到的问题与解决方法(+直播)

想做一个模仿bilibili的加在视频上的弹幕,后来一不小心把直播的版本也写了……

Basic

bilibili用的是flash的播放器,可是我根本不会……搜到了一个叫flowplayer的播放器,有html5版本,就拿来用了。之后发现还能一键支持rtmp直播,真是棒呆了(ง’̀-‘́)ง

基本的思想就是用js生成一堆弹幕,扔在flowplayer生成的播放器里面,用jquery让他飘过去( ´◔ ‸◔`)

Demo在这里。生成了6k条左右的弹幕,加载可能需要一段时间。

Details

Backend

随便用什么东西搭个存取数据库的API,js每次根据视频id调用API获取弹幕,收到弹幕后再POST过去。

Frontend

  1. *弹幕的位置。*为了不让弹幕重叠,发送弹幕时需要确定弹幕的位置。使用relative的话会出现弹幕飞着飞着突然往上移了一格的情况(因为速度不同),所以需要用absolute在手动定位。我的做法是按照高度维护一个数组,每次将若已经完全移出右边界,不再影响新弹幕的弹幕去除,再按高度排序,寻找第一个空位。
  2. *弹幕的层数。*同时发送的弹幕过多会导致向下溢出边界,需要将弹幕分层,一层满了新建一层发。解决方法是在第1条里维护的数组中增加一个层数的关键字,以为第一关键字,每次找层数最高的空位,若已满则再建一层。
  3. *弹幕的发送时间。*之前尝试过使用setTimeInterval每隔一段时间查询一次是否有弹幕要发送,不过这样就会有最小时间间隔的问题,导致弹幕不能连续发送,而且在处理暂停、拖动等操作的时候还会有些微妙的逻辑问题。后来改成了对每一条弹幕setTimeout延迟发送,考虑到如果弹幕数量巨大放太多setTimeout太吃资源,所以开个缓冲区,先缓冲100条弹幕,第i条弹幕发送时立即将i+100号弹幕加入缓冲区。
  4. *暂停、继续、拖动。*我采取的策略是暂停时用jquery的pause插件将所有弹幕运动暂停并清除缓冲,继续时继续并重新开始缓冲,拖动时清空所有弹幕及弹幕缓冲并重新缓冲。需要注意的是清除缓冲时仍会导致有弹幕定时还没来得及被清除便触发了,这需要在setTimeout的动作中发射弹幕是判断是否处于暂停状态中。

Live

Backend

用了socket.io,首页有聊天版的教程,照着改改就可以了。

Player

Flowplayer HTML5版本原生支持rtmp视频的直播,示例如下。

code:

<div id="74f1850031742cad4362f0ebe7439dbb19e2d094" data-rtmp="rtmp://RTMP_IP:PORT/LIVE_ADDR" class="flowplayer" data-ratio="0.4167">
    <video>
        <source type="video/flash" src="ROOM_CODE">
        <!-- mp4 ver. -->
        <!-- <source type="video/flash" src="mp4:ROOM_CODE"> -->
    </video>
</div>

Software

可以直接用ffmpeg,网上教程有很多,不过配置有点麻烦。直播软件有Xsplit和OBS,前者在我机子上老是出错,推荐用OBS,完全免费。