平时在用B站时有看到B站的上传功能中,视频都是在上传完成之前,就已经可以在浏览器端访问到视频的截图了。这个功能其实在我最近的项目中因为有自己去折腾过,所以想在这里再整理一下,虽然实际项目中我选择用了node.js,但纯前端也是完全可以实现的。
利用编译为WebAssembly的FFmpeg
网上其实有很多人提供了编译为wasm的ffmpeg,
这里我还是直接选用了videoconverter.js开源库里作者编译好的ffmpeg.js
文件本身容量有20多mb大小,网络请求多少负担还是有些大,需要更轻量的可以考虑自行编译去掉不需要的功能
WebWorker调用
实际的利用方法,使用WebWorker或者ServiceWorker在别的线程中去调用ffmpeg来执行处理,
WebWorker文件的创建例子:
importScripts('./ffmpeg.js');
var now = Date.now;
function print(text) {
postMessage({
'type' : 'stdout',
'data' : text
});
}
onmessage = event => {
var message = event.data;
if (message.type === "command") {
var Module = {
print: print,
printErr: print,
files: message.files || [],
arguments: message.arguments || [],
TOTAL_MEMORY: message.TOTAL_MEMORY || false
// Can play around with this option - must be a power of 2
// TOTAL_MEMORY: 268435456
};
postMessage({
'type' : 'start',
'data' : Module.arguments.join(" ")
});
postMessage({
'type' : 'stdout',
'data' : 'Received command: ' +
Module.arguments.join(" ") +
((Module.TOTAL_MEMORY) ? ". Processing with " + Module.TOTAL_MEMORY + " bits." : "")
});
var time = now();
var result = ffmpeg_run(Module);
var totalTime = now() - time;
postMessage({
'type' : 'stdout',
'data' : 'Finished processing (took ' + totalTime + 'ms)'
});
postMessage({
'type' : 'done',
'data' : result,
'time' : totalTime
});
}
};
postMessage({
'type' : 'ready'
});
然后我们在主线程中加载这个worker文件,向监听的Message发出请求与接收响应。
这里WebWorker只能加载能通过网络请求访问到的文件。
worker = new Worker("worker.js");
worker.onmessage = event => {
const message = event.data;
}
worker.postMessage({
type: "command",
arguments: ['-i', 'input.mp4', '-ss', '00:00:00', 'output.mp4'],
files: [
{
"name": "input.mp4",
"data": Uint8Array
}
]
});
关于ffmpeg截取视频图片速度的问题
实际使用中有遇到截取视频少量图片速度依旧很慢的问题,
而且截取的时间点越往后越慢,貌似每次都会从头开始解析视频,
这里我发现只要把命令中的 -ss 指定时间点的参数提到最前面就可以很快的进行截取
-ss 00:00:50 -i input.mp4 -t 1 -r 1 -y -s 256x256 -f image2 -an out%d.jpeg
具体原理不太清楚,但看来ffmpeg对参数的顺序是有要求的。