任务描述
给定一个视频的媒体文件和弹幕文件,你需要提取其中弹幕爆发的片段,并剪辑成一个新视频。
思路分析
这个问题太开放了。本文 YY 了一种 naive 的方法来近似完成这一功能。
采用 [2] 中的计算方法,取两个不同的窗口宽度 \(\Delta_1 <\Delta_2\)。
设定一个阈值 \(T\)。对于每个整数时刻,如果 \(\rho(t,\Delta_1)>T\cdot \rho(t,\Delta_2)\),则我们认为 \(t\) 是一个弹幕爆发点。截取其时间邻域,利用 [3] 中的方法剪辑输出即可。
代码实现
import math
import ffmpeg
import json
import matplotlib.pyplot as plt
import biliDanmu
import biliMedia
import myMediaEdit
def readDanmuList(bid):
with open("outputdanmu/{bid}.danmu.json".format(bid=bid), "r", encoding="utf-8") as f:
str_danmu = f.read()
return json.loads(str_danmu)
def getMediaDuration(bid):
duration = ffmpeg.probe(
"output/{bid}.mp4".format(bid=bid))["format"]["duration"]
return duration
def calcDanmuDensity(danmu_list, duration, Delta=10):
lim = int(math.ceil(float(duration)))
ans = [0]*lim
for i in range(lim):
for danmu in danmu_list:
if abs(float(danmu["time"])-i) < Delta/2:
ans[i] += 1/min(i+1, lim-i, Delta)
return ans
if __name__ == '__main__':
bid = "BV1ds411k74N"
biliDanmu.saveDanmuByBid(bid)
biliMedia.getMP4ByBid(bid)
danmu_list = readDanmuList(bid)
duration = getMediaDuration(bid)
density1 = calcDanmuDensity(danmu_list, duration, Delta=15)
density2 = calcDanmuDensity(danmu_list, duration, Delta=60)
ratio = [(density1[i]/(density2[i]+1e-6)) for i in range(len(density1))]
print(ratio)
plt.plot(ratio)
plt.show()
T=1.2
hot_times = [i for i in range(len(ratio)) if ratio[i]>T]
clip_desc_list = []
for i in hot_times:
clip_desc_list+=[{"filename":"output/{bid}.mp4".format(bid=bid),"start":i,"duration":1}];
print(clip_desc_list)
myMediaEdit.edit(clip_desc_list,"editByDensity.mp4")
测试结果
某视频的 ratio
References
[1] Python + ffmpeg 视频剪辑 - Mollnn - 博客园 (cnblogs.com)
[2] 视频弹幕时间密度分析与作图 - Mollnn - 博客园 (cnblogs.com)
[3] Bilibili 弹幕爬取初探 - Mollnn - 博客园 (cnblogs.com)