120 lines of code to achieve pure web video editing

Micro medicine front-end team 2021-09-15 04:18:44
lines code achieve pure web

Weng Jiarui : Front end engineer of front end Technology Department of micro medicine , One loves to play dota2 Salted fish .


I occasionally saw an article a few days ago webassembly Related articles of , I'm still interested in this technology , On the basis of understanding some relevant knowledge , See if you can practice a little .

What is? webasembly?

WebAssembly(wasm) Is a portable 、 Small volume 、 Load fast and compatible Web New format . Can be C,C++ Modules written in other languages are created by the compiler wasm File format , This module is sent to the browser in binary mode , then js Can pass wasm Call the method function .

WebAssembly The advantages of

There should be a lot of related introductions on the Internet ,WebAssembly Advantages: good performance , The running speed is much higher than Js, For those requiring high computation 、 Application scenarios with high performance requirements, such as images / Video decoding 、 The image processing 、3D/WebVR/AR etc. , The advantages are obvious , We can use the existing C、C++ The library written in other languages is directly compiled into WebAssembly Run to the browser , And can be used as a library JavaScript quote . That means we can transfer a lot of back-end work to the front-end , Reduce the pressure on the server . .........

WebAssembly The simplest practice call

Let's write the simplest c file

int add(int a,int b) {
return a + b;
 Copy code 

Then install the for Emscripten compiler Emscripten Installation guide

emcc test.c -Os -s WASM=1 -s SIDE_MODULE=1 -o test.wasm
 Copy code 

And then we were in html It can be introduced and used in

fetch('./test.wasm').then(response =>
).then(bytes =>
).then(results => {
const add = results.instance.exports.add
 Copy code 

At this time, we can see the corresponding print log on the console , Successfully call our compiled code

Official start

Now that we know how to quickly call some mature C,C++ Class library of , Then we're a step closer to the expected goal of online video editing .

Final demo demonstration

Due to the recording operation of the computer cpu Not very good , So it may take a long time , But the overall effect can still be seen

demo Warehouse address



Before this, you have to know a little about what is FFmpeg? The following explanation is based on the Wikipedia Directory

FFmpeg Is an open source free software , Can run audio and video recording in multiple formats 、 transformation 、 Stream function [1], Contains libavcodec—— This is a decoder library for audio and video in multiple projects , as well as libavformat—— An audio and video format conversion library .

Simply put, this is by C Video processing software written in , Its usage is also quite simple

I mainly transferred the commands needed this time , If you may use other commands , It can be viewed according to his official documents , You can also learn about Ruan Yifeng's article

ffmpeg -ss [start] -i [input] -to [end] -c copy [output]
 Copy code 

start For the start time end For the end time input For the video source file to be operated output Is the location name of the output file

This line of code is the command we need to edit the video

Get relevant FFmpeg Of wasm

Because through Emscripten compile ffmpeg become wasm There are many environmental problems , So this time we directly use the online compiled CDN resources

This mature library is directly used here github.com/ffmpegwasm/…

For the convenience of local debugging , I put down all the relevant resources altogether 4 Resource files

 Copy code 

When we use it, we only need to import the first file , Other files will be called through fetch Way to pull resources

Minimal functional implementation

Pre function implementation : We need to implement a node service , Because use ffmpeg This module will appear if the response header is not set on the server , Will report a mistake SharedArrayBuffer is not defined, This is because of the security vulnerability of the system , The browser disables this by default api, To enable it, you need to be in header Set... On the head

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
 Copy code 

Let's start a simple node service

const Koa = require('koa');
const path = require('path')
const fs = require('fs')
const router = require('koa-router')();
const static = require('koa-static')
const staticPath = './static'
const app = new Koa();
path.join(__dirname, staticPath)
// log request URL:
app.use(async (ctx, next) => {
console.log(`Process ${ctx.request.method} ${ctx.request.url}...`);
ctx.set('Cross-Origin-Opener-Policy', 'same-origin')
ctx.set('Cross-Origin-Embedder-Policy', 'require-corp')
await next();
router.get('/', async (ctx, next) => {
ctx.response.body = '<h1>Index</h1>';
router.get('/:filename', async (ctx, next) => {
const filePath = path.join(__dirname, ctx.request.url);
const htmlContent = fs.readFileSync(filePath);
ctx.type = "html";
ctx.body = htmlContent;
console.log('app started at port 3000...');
 Copy code 

Let's make a minimization demo To realize this editing function , The second before editing the video Create a new one demo.html file , Introduce relevant resources

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<script src="./assets/ffmpeg.min.js"></script>
<div class="container">
<div class="operate">
Select the original video file :
<input type="file" id="select_origin_file">
<button id="start_clip"> Start editing the video </button>
<div class="video-container">
<div class="label"> Original video </div>
<video class="my-video" id="origin-video" controls></video>
<div class="video-container">
<div class="label"> Processed video </div>
<video class="my-video" id="handle-video" controls></video>
 Copy code 
let originFile
$(document).ready(function () {
$('#select_origin_file').on('change', (e) => {
const file = e.target.files[0]
originFile = file
const url = window.webkitURL.createObjectURL(file)
$('#origin-video').attr('src', url)
$('#start_clip').on('click', async function () {
const { fetchFile, createFFmpeg } = FFmpeg;
ffmpeg = createFFmpeg({
log: true,
corePath: './assets/ffmpeg-core.js',
const file = originFile
const { name } = file;
if (!ffmpeg.isLoaded()) {
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(file));
await ffmpeg.run('-i', name, '-ss', '00:00:00', '-to', '00:00:01', 'output.mp4');
const data = ffmpeg.FS('readFile', 'output.mp4');
const tempURL = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
$('#handle-video').attr('src', tempURL)
 Copy code 

The meaning of the code is also quite simple , By introducing FFmpeg To create an instance , And then through ffmpeg.load() Method to load the corresponding wasm and worker resources Not optimized wasm The resources are quite large , The local file has 23MB, If this needs to be put into production, it must pass emcc Adjust the packaging parameters to remove useless modules . Then pass fetchFile Method will select input file Load into memory , You can go through ffmpeg.run Operation and The same as the local command line ffmpeg The command line parameters are basically the same

At this time, our core functions have been realized .

Do a little optimization

For editing, it's best to choose a time period , For convenience, I'll just element The to cdn Way to introduce the use of adopt slider To intercept the video interval , I only post js Related code , Specific code can go to github Take a closer look inside the warehouse

class ClipVideo {
constructor() {
this.ffmpeg = null
this.originFile = null
this.handleFile = null
this.vueInstance = null
this.currentSliderValue = [0, 0]
init() {
initVueSlider(maxSliderValue = 100) {
console.log(`maxSliderValue ${maxSliderValue}`)
if (!this.vueInstance) {
const _this = this
const Main = {
data() {
return {
value: [0, 0],
maxSliderValue: maxSliderValue
watch: {
value() {
_this.currentSliderValue = this.value
methods: {
formatTooltip(val) {
return _this.transformSecondToVideoFormat(val);
const Ctor = Vue.extend(Main)
this.vueInstance = new Ctor().$mount('#app')
} else {
this.vueInstance.maxSliderValue = maxSliderValue
this.vueInstance.value = [0, 0]
transformSecondToVideoFormat(value = 0) {
const totalSecond = Number(value)
let hours = Math.floor(totalSecond / (60 * 60))
let minutes = Math.floor(totalSecond / 60) % 60
let second = totalSecond % 60
let hoursText = ''
let minutesText = ''
let secondText = ''
if (hours < 10) {
hoursText = `0${hours}`
} else {
hoursText = `${hours}`
if (minutes < 10) {
minutesText = `0${minutes}`
} else {
minutesText = `${minutes}`
if (second < 10) {
secondText = `0${second}`
} else {
secondText = `${second}`
return `${hoursText}:${minutesText}:${secondText}`
initFfmpeg() {
const { createFFmpeg } = FFmpeg;
this.ffmpeg = createFFmpeg({
log: true,
corePath: './assets/ffmpeg-core.js',
bindSelectOriginFile() {
$('#select_origin_file').on('change', (e) => {
const file = e.target.files[0]
this.originFile = file
const url = window.webkitURL.createObjectURL(file)
$('#origin-video').attr('src', url)
bindOriginVideoLoad() {
$('#origin-video').on('loadedmetadata', (e) => {
const duration = Math.floor(e.target.duration)
bindClipBtn() {
$('#start_clip').on('click', () => {
console.log('start clip')
async clipFile(file) {
const { ffmpeg, currentSliderValue } = this
const { fetchFile } = FFmpeg;
const { name } = file;
const startTime = this.transformSecondToVideoFormat(currentSliderValue[0])
const endTime = this.transformSecondToVideoFormat(currentSliderValue[1])
console.log('clipRange', startTime, endTime)
if (!ffmpeg.isLoaded()) {
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(file));
await ffmpeg.run('-i', name, '-ss', startTime, '-to', endTime, 'output.mp4');
const data = ffmpeg.FS('readFile', 'output.mp4');
const tempURL = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
$('#handle-video').attr('src', tempURL)
$(document).ready(function () {
const instance = new ClipVideo()
 Copy code 

In this way, the effect at the beginning of the article is realized


webassbembly It's still a relatively new technology , I only applied a small part of the functions , There are still many places worth exploring , Welcome to communicate more

Reference material

Go to the online diagnosis and treatment platform of micro medical Internet Hospital , Quick consultation ,3 I'll find the top three doctor for you in minutes .

 copy _ copy _ unnamed _ Customize px_2021-09-08-0.gif

本文为[Micro medicine front-end team]所创,转载请带上原文链接,感谢

  1. Monthly salary 10K yard farmer, job hopping to 40K architect, summary of technical learning Roadmap
  2. Manually compile and deploy LNMP environment (centos7.5 + nginx-1.18.0 + mysql-5.7.30 + php-7.4.14)
  3. The drama "light" of the Republic of China premiered, with a dismal audience rating of less than 0.4, creating the worst audience rating of Hunan Satellite TV!
  4. It is said that Ericsson will cancel Nanjing R & D center
  5. Question d'entrevue commune de Spring Cloud, où est l'avenir de 2021 Java?
  6. Sharedpreferences Replacement - mmkv Integration and Principles, 27 years old Preschool Development
  7. Différence entre TCP et UDP, développement frontal mobile
  8. The whole process of mybatis dynamic proxy implementation of spring series? The correct answer rate is less than 1%
  9. Ajax Foundation
  10. Développement Android comprendre ces entrevues naturelles sans peur, Android Knowledge System
  11. L'expérience de l'entrevue d'embauche de l'école Java peut vous aider à obtenir une entrevue.
  12. Leetcode pinceau question 989 - simple - addition d'entiers sous forme de tableau
  13. Children of the Qiao family: after reading Qiao Zuwang's last selflessness, we can recognize his simple father's love
  14. Partager une petite expérience d'entrevue et obtenir un produit
  15. Solution de capture de paquets https sur la plate - forme Android et analyse des problèmes, Android développe un apprentissage complet
  16. Young people are the first choice for transportation. These hatchbacks suck countless powder. Is it your dish?
  17. "Net red granny" over 70 has countless circle powder by wearing, and is more fashionable than young people
  18. Méthodes de fonctionnement des tableaux couramment utilisées en javascript
  19. Summary of Vue knowledge points
  20. 10 septembre: deuxième visite d'une journée à Yantai, Shandong - vue nocturne de Chaoyang Street, Zhifu District
  21. Front and back end data interaction (V) -- what is Axios?
  22. Windows configures nginx to boot automatically
  23. Des questions d'entrevue communes à Tomcat pour discuter de votre compréhension de la technologie de verrouillage distribué,
  24. JS handscrap, Classic interview question, web front end Development Process
  25. Android 400 questions d'entrevue pour vous aider à entrer dans l'usine, un tour pour vous apprendre à comprendre netty
  26. Développement et projet d'application Web statique côté PC
  27. Recommandé pour le tutoriel Spring Framework, 2021 dernière question d'entrevue d'embauche de la société aiqiyi Java,
  28. La dernière revue scientifique de l'académicien Luo Liqun: architecture de la boucle neuronale pour stimuler la nouvelle Ia
  29. [partage d'expérience de travail], 2021 les dernières questions d'entrevue Java de Baidu, Headlines, etc.
  30. Lisez l'analyse de 497 questions pour l'entrevue d'ingénieur principal Android et vérifiez les lacunes.
  31. Grâce à cette collection de questions d'entrevue d'automne, le salaire de saut d'emploi et l'entrevue de développement audio et vidéo ont doublé.
  32. Prenez d'un coup l'offre de Tencent meituan et jetez un coup d'oeil à cette copie de l'entrevue de printemps!
  33. L'expérience et l'expérience d'un Maverick Java en matière d'entrevue sur les MTD, l'expérience de l'entrevue d'embauche du printemps Java en 2021,
  34. Vue中自定义列表复选框和全选框-案例
  35. Vue bidirectional binding (V-model bidirectional binding,. Sync bidirectional binding,. Sync transfer object)
  36. CSS text overflow ellipsis summary, as you wish
  37. C'est la mode la plus étrange que j'ai jamais vue.
  38. Cases à cocher et toutes les cases à cocher de la liste personnalisée en vue - CAS
  39. Vue bidirectional binding (V-model bidirectional binding,. Sync bidirectional binding,. Sync transfer object)
  40. Vue3.0 using Gaode map to obtain longitude and latitude information
  41. Front end interview daily 3 + 1 - day 877
  42. Vue bidirectional binding (V-model bidirectional binding,. Sync bidirectional binding,. Sync transfer object)
  43. React realizes the function of copying pictures with one click
  44. White space, word break and word wrap are the three most basic and confusing attributes in CSS - thoroughly understand
  45. Trois ans d'expérience d'entrevue avec une femme de programmation diplômée, une réflexion sur la cohérence de l'expiration des données de redis Master slave Node,
  46. Résumé de l'entrevue Android de Dachang, carte technique Android
  47. Un plan de carrière Java correct, découvrez les questions que vous devez poser lors de l'entrevue d'embauche du printemps Java de cette année.
  48. Le résumé de l'entrevue Android de Dachang est en retard
  49. Un article vous a appris à gérer les entrevues sur le Web, à partager 350 vraies questions d'entrevue Java,
  50. Jquery Tools Methodology collation, Sharing a little interview Experience
  51. Jquery plug - in urianchor, app front end Development
  52. $in jquery, Visualized Web Development Tool
  53. Le développement Java doit être fait. Les entrevues https demandent souvent une analyse complète.
  54. vue v-if未生效问题
  55. vue动态改变组件外部元素样式
  56. Jdk's Past Life: The Classic Features of Thin Number - java5 - - - 15 -, webfront Development
  57. Résumé des questions d'entrevue pour les ingénieurs en développement Java, analyse des questions d'entrevue à haute fréquence Dubbo,
  58. Who doesn't want to make a scratch music by himself? Scratch music is realized by native JS
  59. Learn XPath to help climb the data of major e-commerce platforms in the Mid Autumn Festival
  60. vue動態改變組件外部元素樣式