Bilibili弹幕实现本地字幕播放

5,267次阅读

共计 1311 个字符,预计需要花费 4 分钟才能阅读完成。

因为学校网速原因,导致很多视频都很难看高清的在线视频,就只能下载到电脑看本地视频了,但是这样就没法看弹幕了,所以就萌生了将B站弹幕转换成本地字幕形式播放的想法。

B站的弹幕格式如图:
Bilibili弹幕实现本地字幕播放

分别代表着:弹幕出现时间、弹幕类型、字体大小、字体颜色、创建时间、不清楚、创建者ID、弹幕ID。

接下来就是ass字幕的格式了

Dialogue: 1,0:02:02.84,0:02:08.84,Dedualt,,0000,0000,0000,,{\move(1024, 51, -1428, 51)}你以为雷总不上b站了

我们除了需要将原来的时间、颜色、字体大小做改变之外,最难的应该是确实字幕的位置和相应的动作了。

这里讲一下B站的字幕类型:
1. 1代表普通滚动弹幕
2. 4代表底部中间弹幕
3. 5代表顶部中间弹幕

还有其他一些高级弹幕,难度太大就不做处理了。
为了减少弹幕突然集中出现导致的弹幕重叠情况,将弹幕分组储存,分行输出,这样同一时间发出的弹幕可以在多行展示。

这里处理弹幕的算法是:先根据播放器屏幕大小和弹幕字体大小将屏幕分为固定行数,即line = width / fontsize。得到固定行数之后,定义三组数组分别储存三种类型的弹幕,当每组数组内有固定行数个元素之后,也就是当前屏幕所有行数都存在弹幕时,即输出此行数组,并且将当前数组清除准备储存下一组数据,核心代码如下:

    def ComposeComment(self, filename):
        '''
        组合弹幕,将不同类型的弹幕放到一个list
        并且当list存满ROWS时输出给ConvertComment
        '''
        rows = [[] for i in xrange(3)]
        # 弹幕类型
        category_dic = {'1':0, '4':1, '5':2}
        for index, comment in enumerate(self.ReadComment(filename), 1):
            category = category_dic[comment[1]]
            rows[category].append(comment)
            if len(rows[category]) == ROWS:
                yield rows[category]
                rows[category] = []
        for row in rows:
            yield row

接下来就是计算弹幕的位置了,按照ComposeComment函数输出的ROWS,计算出当前弹幕的X轴和Y轴的位置:

styles.append('\\move(%(width)d, %(row)d, %(neglen)d, %(row)d)' % {'width': WIDTH, 'row': index*SIZE, 'neglen': -math.ceil(len(text)*SIZE)})

动画的格式如下:
\move(<x1>, <y1>, <x2>, <y2>[, <t1>, <t2>])---移动效果
x1,y1---移动开始的位置
x2,y2---移动结束的位置
t1,t2---移动开始和结束时间,省略后以字幕开始和结束时间为准

核心问题解决后,剩下的就是时间戳、颜色的转换了,很简单就不贴代码了。
最后贴一张效果图:

Bilibili弹幕实现本地字幕播放

参考资料:
* Danmaku2ASS
* bilibili弹幕转ass
* ASS格式

正文完
 
root
版权声明:本站原创文章,由 root 2016-12-11发表,共计1311字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。