平时在用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对参数的顺序是有要求的


参考链接

添加新评论

已有 2 条评论

  1. 不愿透露姓名的程同学

    不愿透露姓名的程同学

    能评论吗

  2. rantrism

    rantrism

    您好~我是腾讯云+社区的运营,关注了您在分享的技术文章,觉得内容很棒,我们诚挚邀请您加入腾讯云自媒体分享计划。完整福利和申请地址请见:https://cloud.tencent.com/developer/support-plan
    作者申请此计划后将作者的文章进行搬迁同步到社区的专栏下,你只需要简单填写一下表单申请即可,我们会给作者提供包括流量、云服务器、域名等,另外还有些周边礼物。 我们诚挚的邀请您并期待您的加入