From 55cbf63fb19ddfbff8c095ba4784ded450263080 Mon Sep 17 00:00:00 2001 From: Jerome Wu Date: Tue, 28 Apr 2020 19:36:33 +0800 Subject: [PATCH] Update documents --- README.md | 45 ++++++-- docs/api.md | 168 +++++++++++++--------------- examples/browser/concatDemuxer.html | 16 +-- examples/browser/image2video.html | 22 ++-- examples/browser/run.html | 20 ++-- examples/browser/transcode.html | 18 ++- examples/browser/trim.html | 20 ++-- examples/browser/webcam.html | 16 +-- examples/node/concatDemuxer.js | 19 ++-- examples/node/hstack.js | 19 ++-- examples/node/image2video.js | 27 ++--- examples/node/run.js | 19 ++-- examples/node/transcode.js | 18 ++- examples/node/trim.js | 19 ++-- 14 files changed, 202 insertions(+), 244 deletions(-) diff --git a/README.md b/README.md index b69308e..b8e736a 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,11 @@ Use FFmpeg directly in your browser without any backend services!! | ---- | ------- | ----------- | | Webcam | codepen | [Link](https://github.com/ffmpegjs/ffmpeg.js/blob/master/examples/browser/webcam.html) | +## Supported Formats + +- mp4 (x264) +- webm (vp8/vp9) (^0.8.0) +- mp3 (^0.8.0) --- @@ -43,17 +48,16 @@ ffmpeg.js provides simple to use APIs, to transcode a video you only need few li ```javascript const fs = require('fs'); -const { createWorker } = require('@ffmpeg/ffmpeg'); +const { createFFmpeg } = require('@ffmpeg/ffmpeg'); -const worker = createWorker(); +const ffmpeg = createFFmpeg({ log: true }); (async () => { - await worker.load(); - await worker.write('test.avi', './test.avi'); - await worker.transcode('test.avi', 'test.mp4'); - const { data } = await worker.read('test.mp4'); - fs.writeFileSync('./test.mp4', data); - await worker.terminate(); + await ffmpeg.load(); + await ffmpeg.write('test.avi', './test.avi'); + await ffmpeg.transcode('test.avi', 'test.mp4'); + fs.writeFileSync('./test.mp4', ffmpeg.read('test.mp4')); + process.exit(0); })(); ``` @@ -63,18 +67,34 @@ const worker = createWorker(); $ npm install @ffmpeg/ffmpeg ``` -> As we use `worker_threads` which was introduced in Node.js v10.5.0, please remember to add `--experimental-worker` if you are using Node.js v10, and you don't have to add anything if you are using Node.js v12 +> As we are using the latest experimental features, you need to add few flags to run in Node.js -Or, using a script tag in the browser: +``` +$ node --experimental-wasm-threads --experimental-wasm-bulk-memory transcode.js +``` + +Or, using a script tag in the browser (only works in Chrome): ```html - + ``` +## Multi-thread + +Starting from v0.8.0, multithreading is enabled and you can use this feature by passing `-threads ` (`NUM` < 8 ). For built-in functions like `transcode()`, you can pass it as 3rd argument. + +```javascript +// in transcode() +await ffmpeg.transcode('flame.avi', 'flame.mp4', '-threads 2'); + +// in run() +await ffmpeg.run('-i flame.avi -threads 2 flame.mp4'); +``` + ## Examples - With React: https://github.com/ffmpegjs/react-app @@ -93,3 +113,4 @@ Learn how to build ffmpeg.js from stories: - [Part.4 ffmpeg.js v0.2 — Web Worker and Libx264](https://medium.com/@jeromewus/build-ffmpeg-webassembly-version-ffmpeg-js-part-4-ffmpeg-js-v0-2-web-worker-and-libx264-d0596f1beb4e) - [Part.5 ffmpeg.js v0.3 — pre-js and live streaming](https://medium.com/@jeromewus/build-ffmpeg-webassembly-version-ffmpeg-js-part-5-ffmpeg-js-v0-3-pre-js-and-live-streaming-c1498939a74c) - [Part.6 a Deep Dive into File System](https://medium.com/@jeromewus/build-ffmpeg-webassembly-version-ffmpeg-js-part-6-a-deep-dive-into-file-system-56eba10067ca) +- [Part.7 multithreading (WIP)]() diff --git a/docs/api.md b/docs/api.md index f7751de..a782052 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,67 +1,61 @@ # API -- [createWorker()](#create-worker) - - [Worker.load](#worker-load) - - [Worker.write](#worker-write) - - [Worker.writeText](#worker-writeText) - - [Worker.read](#worker-read) - - [Worker.remove](#worker-remove) - - [Worker.transcode](#worker-transcode) - - [Worker.trim](#worker-trim) - - [Worker.concatDemuxer](#worker-concatDemuxer) - - [Worker.run](#worker-run) - - [Worker.terminate](#worker-terminate) +- [createFFmpeg()](#create-ffmpeg) + - [ffmpeg.load](#ffmpeg-load) + - [ffmpeg.write](#ffmpeg-write) + - [ffmpeg.writeText](#ffmpeg-writeText) + - [ffmpeg.read](#ffmpeg-read) + - [ffmpeg.remove](#ffmpeg-remove) + - [ffmpeg.transcode](#ffmpeg-transcode) + - [ffmpeg.trim](#ffmpeg-trim) + - [ffmpeg.concatDemuxer](#ffmpeg-concatDemuxer) + - [ffmpeg.run](#ffmpeg-run) --- - + -## createWorker(options): Worker +## createFFmpeg(options): ffmpeg -createWorker is a factory function that creates a ffmpeg worker, a worker is basically a Web Worker in browser and Child Process in Node. +createFFmpeg is a factory function that creates a ffmpeg instance. **Arguments:** - `options` an object of customized options - `corePath` path for ffmpeg-core.js script - - `workerPath` path for downloading worker script - - `workerBlobURL` a boolean to define whether to use Blob URL for worker script, default: true - - `logger` a function to log the progress, a quick example is `m => console.log(m)` + - `log` a boolean to turn on all logs, default is `false` + - `logger` a function to get log messages, a quick example is `({ message }) => console.log(message)` - `progress` a function to trace the progress, a quick example is `p => console.log(p)` **Examples:** ```javascript -const { createWorker } = FFmpeg; -const worker = createWorker({ +const { createFFmpeg } = FFmpeg; +const ffmpeg = createFFmpeg({ corePath: "./node_modules/@ffmpeg/core/ffmpeg-core.js", - logger: m => console.log(m) + log: true, }); ``` - + -### Worker.load(jobId): Promise +### ffmpeg.load(): Promise -Worker.load() loads ffmpeg-core.js script (download from remote if not presented), it makes Web Worker/Child Process ready for next action. - -**Arguments:** - -- `jobId` jobId is generated by ffmpeg.js to identify each job, but you can put your own when calling the function. +ffmpeg.load() loads ffmpeg-core.js script (download from remote if not presented), it makes WebAssembly code ready to use. **Examples:** ```javascript (async () => { - await worker.load(); + await ffmpeg.load(); })(); ``` - + -### Worker.write(path, data, jobId): Promise +### ffmpeg.write(path, data): Promise -Worker.write() writes data to specific path in Emscripten file system, it is an essential step before doing any other tasks. +ffmpeg.write() writes data to specific path in Emscripten file system, it is an essential step before doing any other tasks. > Currently we found an issue that you should not have parallel Worker.write() as it may cause unexpected behavior, please do it in sequential for-loop like [HERE](https://github.com/ffmpegjs/ffmpeg.js/blob/master/examples/browser/image2video.html#L36) @@ -69,139 +63,150 @@ Worker.write() writes data to specific path in Emscripten file system, it is an - `path` path to write data to file system - `data` data to write, can be Uint8Array, URL or base64 format -- `jobId` @see Worker.load() **Examples:** ```javascript (async () => { - await worker.write( + await ffmpeg.write( "flame.avi", "http://localhost:3000/tests/assets/flame.avi" ); })(); ``` - + -### Worker.writeText(path, text, jobId): Promise +### ffmpeg.writeText(path, text): undefined -Worker.write() writes text data to specific path in Emscripten file system. +ffmpeg.write() writes text data to specific path in Emscripten file system. **Arguments:** - `path` path to write data to file system - `text` string to write to file -- `jobId` @see Worker.load() **Examples:** ```javascript (async () => { - await worker.write("sub.srt", "..."); + ffmpeg.writeText("sub.srt", "..."); })(); ``` - + -### Worker.read(path, jobId): Promise +### ffmpeg.read(path): Uint8Array -Worker.read() reads data from file system, often used to get output data after specific task. +ffmpeg.read() reads data from file system, often used to get output data after specific task. **Arguments:** - `path` path to read data from file system -- `jobId` @see Worker.load() **Examples:** ```javascript (async () => { - const { data } = await worker.read("output.mp4"); + const data = ffmpeg.read("output.mp4"); })(); ``` - + -### Worker.remove(path, jobId): Promise +### ffmpeg.remove(path): Promise -Worker.remove() removes files in file system, it will be better to delete unused files if you need to run ffmpeg.js multiple times. +ffmpeg.remove() removes files in file system, it will be better to delete unused files if you need to run ffmpeg.js multiple times. **Arguments:** - `path` path for file to delete -- `jobId` @see Worker.load() **Examples:** ```javascript (async () => { - await worker.remove("output.mp4"); + ffmpeg.remove("output.mp4"); })(); ``` - + -### Worker.transcode(input, output, options, jobId): Promise +### ffmpeg.ls(path): Promise -Worker.transcode() transcode a video file to another format. +ffmpeg.ls() lists all files in specific path. **Arguments:** -- `input` input file path, the input file should be written through Worker.write() -- `output` output file path, can be read with Worker.read() later +- `path` path to list + +**Examples:** + +```javascript +(async () => { + const dirs = ffmpeg.ls("/"); +})(); +``` + + + +### ffmpeg.transcode(input, output, options): Promise + +ffmpeg.transcode() transcode a video file to another format. + +**Arguments:** + +- `input` input file path, the input file should be written through ffmpeg.write() +- `output` output file path, can be read with ffmpeg.read() later - `options` a string to add extra arguments to ffmpeg -- `jobId` @see Worker.load() **Examples:** ```javascript (async () => { - await worker.transcode("flame.avi", "output.mp4", "-s 1920x1080"); + await ffmpeg.transcode("flame.avi", "output.mp4", "-s 1920x1080"); })(); ``` - + -### Worker.trim(input, output, from, to, options, jobId): Promise +### ffmpeg.trim(input, output, from, to, options): Promise -Worker.trim() trims video to specific interval. +ffmpeg.trim() trims video to specific interval. **Arguments:** -- `inputPath` input file path, the input file should be written through Worker.write() -- `outputPath` output file path, can be read with Worker.read() later +- `inputPath` input file path, the input file should be written through ffmpeg.write() +- `outputPath` output file path, can be read with ffmpeg.read() later - `from` start time, can be in time stamp (00:00:12.000) or seconds (12) - `to` end time, rule same as above - `options` a string to add extra arguments to ffmpeg -- `jobId` @see Worker.load() **Examples:** ```javascript (async () => { - await worker.trim("flame.avi", "output.mp4", 1, 2); + await ffmpeg.trim("flame.avi", "output.mp4", 1, 2); })(); ``` - + -### Worker.concatDemuxer(input, output, options, jobId): Promise +### ffmpeg.concatDemuxer(input, output, options): Promise -Worker.concatDemuxer() concatenates multiple videos using concatDemuxer. This method won't encode the videos again. But it has its limitations. See [Concat demuxer Wiki](https://trac.ffmpeg.org/wiki/Concatenate) +ffmpeg.concatDemuxer() concatenates multiple videos using concatDemuxer. This method won't encode the videos again. But it has its limitations. See [Concat demuxer Wiki](https://trac.ffmpeg.org/wiki/Concatenate) **Arguments:** -- `input` input file paths as an Array, the input files should be written through Worker.write() -- `output` output file path, can be read with Worker.read() later +- `input` input file paths as an Array, the input files should be written through ffmpeg.write() +- `output` output file path, can be read with ffmpeg.read() later - `options` a string to add extra arguments to ffmpeg -- `jobId` check Worker.load() **Examples:** ```javascript (async () => { - await worker.concatDemuxer(["flame-1.avi", "flame-2.avi"], "output.mp4"); + await ffmpeg.concatDemuxer(["flame-1.avi", "flame-2.avi"], "output.mp4"); })(); ``` @@ -209,39 +214,24 @@ If the input video files are the same as the output video file, you can pass an ```javascript (async () => { - await worker.concatDemuxer(["flame-1.mp4", "flame-2.mp4"], "output.mp4", "-c copy"); + await ffmpeg.concatDemuxer(["flame-1.mp4", "flame-2.mp4"], "output.mp4", "-c copy"); })(); ``` - + -### Worker.run(args, jobId): Promise +### ffmpeg.run(args): Promise -Worker.run() is similar to FFmpeg cli tool, aims to provide maximum flexiblity for users. +ffmpeg.run() is similar to FFmpeg cli tool, aims to provide maximum flexiblity for users. **Arguments:** - `args` a string to represent arguments -- `jobId` check Worker.load() **Examples:** ```javascript (async () => { - await worker.run("-i flame.avi -s 1920x1080 output.mp4"); -})(); -``` - - - -### Worker.terminate(): Promise - -Worker.terminate() terminates web worker / worker\_threads, after terminate(), you cannot use this worker anymore. - -**Examples:** - -```javascript -(async () => { - await worker.terminate(); + await ffmpeg.run("-i flame.avi -s 1920x1080 output.mp4"); })(); ``` diff --git a/examples/browser/concatDemuxer.html b/examples/browser/concatDemuxer.html index f80803b..8afba6e 100644 --- a/examples/browser/concatDemuxer.html +++ b/examples/browser/concatDemuxer.html @@ -23,33 +23,29 @@

diff --git a/examples/browser/transcode.html b/examples/browser/transcode.html index 15b82e7..d946159 100644 --- a/examples/browser/transcode.html +++ b/examples/browser/transcode.html @@ -20,27 +20,23 @@

diff --git a/examples/browser/webcam.html b/examples/browser/webcam.html index 79acb22..2c8368b 100644 --- a/examples/browser/webcam.html +++ b/examples/browser/webcam.html @@ -23,11 +23,8 @@

diff --git a/examples/node/concatDemuxer.js b/examples/node/concatDemuxer.js index c248937..7e967a7 100755 --- a/examples/node/concatDemuxer.js +++ b/examples/node/concatDemuxer.js @@ -1,17 +1,12 @@ const fs = require('fs'); -const { createWorker } = require('../../src'); +const { createFFmpeg } = require('../../src'); -const worker = createWorker({ - logger: ({ message }) => console.log(message), -}); +const ffmpeg = createFFmpeg({ log: true }); (async () => { - await worker.load(); - console.log('Start to concat'); - await worker.write('flame.avi', '../../tests/assets/flame.avi'); - await worker.concatDemuxer(['flame.avi', 'flame.avi'], 'flame.mp4'); - const { data } = await worker.read('flame.mp4'); - console.log('Complete concat'); - fs.writeFileSync('flame.mp4', Buffer.from(data)); - await worker.terminate(); + await ffmpeg.load(); + await ffmpeg.write('flame.avi', '../../tests/assets/flame.avi'); + await ffmpeg.concatDemuxer(['flame.avi', 'flame.avi'], 'flame.mp4'); + fs.writeFileSync('flame.mp4', ffmpeg.read('flame.mp4')); + process.exit(0); })(); diff --git a/examples/node/hstack.js b/examples/node/hstack.js index 41aa39c..87d05c0 100755 --- a/examples/node/hstack.js +++ b/examples/node/hstack.js @@ -1,17 +1,12 @@ const fs = require('fs'); -const { createWorker } = require('../../src'); +const { createFFmpeg } = require('../../src'); -const worker = createWorker({ - logger: ({ message }) => console.log(message), -}); +const ffmpeg = createFFmpeg({ log: true }); (async () => { - await worker.load(); - console.log('Start hstack'); - await worker.write('flame.avi', '../../tests/assets/flame.avi'); - await worker.run('-i flame.avi -i flame.avi -filter_complex hstack flame.mp4'); - const { data } = await worker.read('flame.mp4'); - console.log('Complete hstack'); - fs.writeFileSync('flame.mp4', Buffer.from(data)); - await worker.terminate(); + await ffmpeg.load(); + await ffmpeg.write('flame.avi', '../../tests/assets/flame.avi'); + await ffmpeg.run('-i flame.avi -i flame.avi -filter_complex hstack flame.mp4'); + fs.writeFileSync('flame.mp4', ffmpeg.read('flame.mp4')); + process.exit(0); })(); diff --git a/examples/node/image2video.js b/examples/node/image2video.js index 894ef16..eb8303f 100755 --- a/examples/node/image2video.js +++ b/examples/node/image2video.js @@ -1,28 +1,21 @@ const fs = require('fs'); -const { createWorker } = require('../../src'); +const { createFFmpeg } = require('../../src'); -const worker = createWorker({ - progress: (p) => console.log(p), -}); +const ffmpeg = createFFmpeg({ log: true }); (async () => { - console.log('Loading ffmpeg-core.js'); - await worker.load(); - console.log('Loading data'); - await worker.write('audio.ogg', '../../tests/assets/triangle/audio.ogg'); + await ffmpeg.load(); + await ffmpeg.write('audio.ogg', '../../tests/assets/triangle/audio.ogg'); for (let i = 0; i < 60; i += 1) { const num = `00${i}`.slice(-3); - await worker.write(`tmp.${num}.png`, `../../tests/assets/triangle/tmp.${num}.png`); + await ffmpeg.write(`tmp.${num}.png`, `../../tests/assets/triangle/tmp.${num}.png`); } - console.log('Start transcoding'); - await worker.run('-framerate 30 -pattern_type glob -i *.png -i audio.ogg -c:a copy -shortest -c:v libx264 -pix_fmt yuv420p out.mp4'); - const { data } = await worker.read('out.mp4'); - console.log('Complete transcoding'); - await worker.remove('audio.ogg'); + await ffmpeg.run('-framerate 30 -pattern_type glob -i *.png -i audio.ogg -c:a copy -shortest -c:v libx264 -pix_fmt yuv420p out.mp4'); + await ffmpeg.remove('audio.ogg'); for (let i = 0; i < 60; i += 1) { const num = `00${i}`.slice(-3); - await worker.remove(`tmp.${num}.png`); + await ffmpeg.remove(`tmp.${num}.png`); } - fs.writeFileSync('out.mp4', Buffer.from(data)); - await worker.terminate(); + fs.writeFileSync('out.mp4', ffmpeg.read('out.mp4')); + process.exit(0); })(); diff --git a/examples/node/run.js b/examples/node/run.js index 7afb9e6..a96dfcf 100755 --- a/examples/node/run.js +++ b/examples/node/run.js @@ -1,17 +1,12 @@ const fs = require('fs'); -const { createWorker } = require('../../src'); +const { createFFmpeg } = require('../../src'); -const worker = createWorker({ - logger: ({ message }) => console.log(message), -}); +const ffmpeg = createFFmpeg({ log: true }); (async () => { - await worker.load(); - console.log('Start transcoding'); - await worker.write('flame.avi', '../../tests/assets/flame.avi'); - await worker.run('-i flame.avi flame.mp4'); - const { data } = await worker.read('flame.mp4'); - console.log('Complete transcoding'); - fs.writeFileSync('flame.mp4', Buffer.from(data)); - await worker.terminate(); + await ffmpeg.load(); + await ffmpeg.write('flame.avi', '../../tests/assets/flame.avi'); + await ffmpeg.run('-i flame.avi flame.mp4'); + fs.writeFileSync('flame.mp4', ffmpeg.read('flame.mp4')); + process.exit(0); })(); diff --git a/examples/node/transcode.js b/examples/node/transcode.js index b9658c8..a4b9558 100755 --- a/examples/node/transcode.js +++ b/examples/node/transcode.js @@ -1,17 +1,15 @@ const fs = require('fs'); -const { createWorker } = require('../../src'); +const { createFFmpeg } = require('../../src'); -const worker = createWorker({ - logger: ({ message }) => console.log(message), +const ffmpeg = createFFmpeg({ + log: true, }); (async () => { - await worker.load(); - console.log('Start transcoding'); - await worker.write('flame.avi', '../../tests/assets/flame.avi'); - await worker.transcode('flame.avi', 'flame.mp4'); - const { data } = await worker.read('flame.mp4'); - console.log('Complete transcoding'); + await ffmpeg.load(); + await ffmpeg.write('flame.avi', '../../tests/assets/flame.avi'); + await ffmpeg.transcode('flame.avi', 'flame.mp4', '-threads 2'); + const data = ffmpeg.read('flame.mp4'); fs.writeFileSync('flame.mp4', Buffer.from(data)); - await worker.terminate(); + process.exit(0); })(); diff --git a/examples/node/trim.js b/examples/node/trim.js index a3b3997..b0705f3 100755 --- a/examples/node/trim.js +++ b/examples/node/trim.js @@ -1,17 +1,12 @@ const fs = require('fs'); -const { createWorker } = require('../../src'); +const { createFFmpeg } = require('../../src'); -const worker = createWorker({ - logger: ({ message }) => console.log(message), -}); +const ffmpeg = createFFmpeg({ log: true }); (async () => { - await worker.load(); - console.log('Start trimming'); - await worker.write('flame.avi', '../../tests/assets/flame.avi'); - await worker.trim('flame.avi', 'flame_trim.avi', 0, 10); - const { data } = await worker.read('flame_trim.avi'); - console.log('Complete trimming'); - fs.writeFileSync('flame_trim.avi', Buffer.from(data)); - await worker.terminate(); + await ffmpeg.load(); + await ffmpeg.write('flame.avi', '../../tests/assets/flame.avi'); + await ffmpeg.trim('flame.avi', 'flame_trim.avi', 0, 10); + fs.writeFileSync('flame_trim.avi', ffmpeg.read('flame_trim.avi')); + process.exit(0); })();