diff --git a/docs/api.md b/docs/api.md index bfa0a6d..1be2595 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,5 +1,4 @@ -API -=== +# API - [createWorker()](#create-worker) - [Worker.load](#worker-load) @@ -14,6 +13,7 @@ API --- + ## createWorker(options): Worker createWorker is a factory function that creates a ffmpeg worker, a worker is basically a Web Worker in browser and Child Process in Node. @@ -27,17 +27,18 @@ createWorker is a factory function that creates a ffmpeg worker, a worker is bas - `logger` a function to log the progress, a quick example is `m => console.log(m)` - `progress` a function to trace the progress, a quick example is `p => console.log(p)` - **Examples:** ```javascript const { createWorker } = FFmpeg; const worker = createWorker({ - corePath: './node_modules/@ffmpeg/core/ffmpeg-core.js', - logger: m => console.log(m), + corePath: "./node_modules/@ffmpeg/core/ffmpeg-core.js", + logger: m => console.log(m) }); ``` + + ### Worker.load(jobId): Promise Worker.load() loads ffmpeg-core.js script (download from remote if not presented), it makes Web Worker/Child Process ready for next action. @@ -55,6 +56,7 @@ Worker.load() loads ffmpeg-core.js script (download from remote if not presented ``` + ### Worker.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. @@ -68,11 +70,15 @@ Worker.write() writes data to specific path in Emscripten file system, it is an ```javascript (async () => { - await worker.write('flame.avi', 'http://localhost:3000/tests/assets/flame.avi'); + await worker.write( + "flame.avi", + "http://localhost:3000/tests/assets/flame.avi" + ); })(); ``` + ### Worker.writeText(path, text): Promise Worker.write() writes text data to specific path in Emscripten file system. @@ -86,11 +92,12 @@ Worker.write() writes text data to specific path in Emscripten file system. ```javascript (async () => { - await worker.write('sub.srt', '...'); + await worker.write("sub.srt", "..."); })(); ``` + ### Worker.read(path, del): Promise Worker.read() reads data from file system, often used to get output data after specific task. @@ -104,11 +111,12 @@ Worker.read() reads data from file system, often used to get output data after s ```javascript (async () => { - const { data } = await worker.read('output.mp4'); + const { data } = await worker.read("output.mp4"); })(); ``` + ### Worker.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. @@ -121,11 +129,12 @@ Worker.remove() removes files in file system, it will be better to delete unused ```javascript (async () => { - await worker.remove('output.mp4'); + await worker.remove("output.mp4"); })(); ``` + ### Worker.transcode(inputPath, outputPath, options, del, jobId): Promise Worker.transcode() transcode a video file to another format. @@ -142,11 +151,12 @@ Worker.transcode() transcode a video file to another format. ```javascript (async () => { - await worker.transcode('flame.avi', 'output.mp4', '-s 1920x1080'); + await worker.transcode("flame.avi", "output.mp4", "-s 1920x1080"); })(); ``` + ### Worker.trim(inputPath, outputPath, from, to, options, del, jobId): Promise Worker.trim() trims video to specific interval. @@ -165,11 +175,34 @@ Worker.trim() trims video to specific interval. ```javascript (async () => { - await worker.trim('flame.avi', 'output.mp4', 1, 2); + await worker.trim("flame.avi", "output.mp4", 1, 2); +})(); +``` + + + +### Worker.concatDemuxer(inputPaths, outputPath, options, del, jobId): 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) + +**Arguments:** + +- `inputPaths` input file paths as an Array, the input files should be written through Worker.write() +- `outputPath` output file path, can be read with Worker.read() later +- `options` a string to add extra arguments to ffmpeg +- `del` a boolean to determine whether to delete input file after the task is done, default: true +- `jobId` check Worker.load() + +**Examples:** + +```javascript +(async () => { + await worker.trim(["flame-1.avi", "flame-2.avi"], "output.mp4"); })(); ``` + ### Worker.run(args, options, jobId): Promise Worker.run() is similar to FFmpeg cli tool, aims to provide maximum flexiblity for users. @@ -184,6 +217,9 @@ Worker.run() is similar to FFmpeg cli tool, aims to provide maximum flexiblity f ```javascript (async () => { - await worker.run('-i /data/flame.avi -s 1920x1080 output.mp4', { inputPath: 'flame.avi', outputPath: 'output.mp4' }); + await worker.run("-i /data/flame.avi -s 1920x1080 output.mp4", { + inputPath: "flame.avi", + outputPath: "output.mp4" + }); })(); ``` diff --git a/examples/browser/concatDemuxer.html b/examples/browser/concatDemuxer.html new file mode 100644 index 0000000..1295ceb --- /dev/null +++ b/examples/browser/concatDemuxer.html @@ -0,0 +1,57 @@ + + + + + + + +

Select multiple video files to Concatenate

+
+ +

+ + + diff --git a/src/createWorker.js b/src/createWorker.js index d04a724..be3dd28 100644 --- a/src/createWorker.js +++ b/src/createWorker.js @@ -122,6 +122,14 @@ module.exports = (_options = {}) => { ) ); + const concatDemuxer = async (inputPaths, outputPath, opts = '', del = true, jobId) => { + const text = inputPaths.reduce((acc, input) => `${acc}\nfile ${input}`, ''); + await writeText('concat_list.txt', text); + return run(`${opts} -f concat -safe 0 -i /data/concat_list.txt -c copy ${outputPath}`, + { del, outputPath, inputPaths: [...inputPaths, 'concat_list.txt'] }, + jobId); + }; + const ls = (path, jobId) => ( startJob(createJob({ id: jobId, action: 'ls', payload: { path }, @@ -175,6 +183,7 @@ module.exports = (_options = {}) => { run, transcode, trim, + concatDemuxer, ls, terminate, }; diff --git a/src/worker-script/index.js b/src/worker-script/index.js index bc46e16..ae90ab8 100644 --- a/src/worker-script/index.js +++ b/src/worker-script/index.js @@ -66,7 +66,9 @@ const ls = ({ const run = async ({ payload: { args: _args, - options: { inputPath, outputPath, del }, + options: { + inputPath, inputPaths, outputPath, del, + }, }, }, res) => { const args = [...defaultArgs, ..._args.trim().split(' ')]; @@ -75,6 +77,9 @@ const run = async ({ Module.FS.unlink(outputPath); if (del && typeof inputPath === 'string') { await adapter.fs.deleteFile(inputPath); + } else if (del && Array.isArray(inputPaths)) { + inputPaths.reduce((promise, input) => promise.then(() => adapter.fs.deleteFile(input)), + Promise.resolve()); } res.resolve({ message: `Complete ${args.join(' ')}` }); };