Merge pull request #13 from santosh898/master
Implemented ConcatDemuxer.
This commit is contained in:
		
						commit
						9cc345c5a4
					
				
							
								
								
									
										60
									
								
								docs/api.md
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								docs/api.md
									
									
									
									
									
								
							| @ -1,5 +1,4 @@ | |||||||
| API | # API | ||||||
| === |  | ||||||
| 
 | 
 | ||||||
| - [createWorker()](#create-worker) | - [createWorker()](#create-worker) | ||||||
|   - [Worker.load](#worker-load) |   - [Worker.load](#worker-load) | ||||||
| @ -14,6 +13,7 @@ API | |||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| <a name="create-worker"></a> | <a name="create-worker"></a> | ||||||
|  | 
 | ||||||
| ## createWorker(options): Worker | ## 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. | 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)` |   - `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)` |   - `progress` a function to trace the progress, a quick example is `p => console.log(p)` | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| **Examples:** | **Examples:** | ||||||
| 
 | 
 | ||||||
| ```javascript | ```javascript | ||||||
| const { createWorker } = FFmpeg; | const { createWorker } = FFmpeg; | ||||||
| const worker = createWorker({ | const worker = createWorker({ | ||||||
|   corePath: './node_modules/@ffmpeg/core/ffmpeg-core.js', |   corePath: "./node_modules/@ffmpeg/core/ffmpeg-core.js", | ||||||
|   logger: m => console.log(m), |   logger: m => console.log(m) | ||||||
| }); | }); | ||||||
| ``` | ``` | ||||||
|  | 
 | ||||||
| <a name="worker-load"></a> | <a name="worker-load"></a> | ||||||
|  | 
 | ||||||
| ### Worker.load(jobId): Promise | ### 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. | 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 | |||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| <a name="worker-write"></a> | <a name="worker-write"></a> | ||||||
|  | 
 | ||||||
| ### Worker.write(path, data): Promise | ### 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. | 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 | ```javascript | ||||||
| (async () => { | (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" | ||||||
|  |   ); | ||||||
| })(); | })(); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| <a name="worker-writeText"></a> | <a name="worker-writeText"></a> | ||||||
|  | 
 | ||||||
| ### Worker.writeText(path, text): Promise | ### Worker.writeText(path, text): Promise | ||||||
| 
 | 
 | ||||||
| Worker.write() writes text data to specific path in Emscripten file system. | 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 | ```javascript | ||||||
| (async () => { | (async () => { | ||||||
|   await worker.write('sub.srt', '...'); |   await worker.write("sub.srt", "..."); | ||||||
| })(); | })(); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| <a name="worker-read"></a> | <a name="worker-read"></a> | ||||||
|  | 
 | ||||||
| ### Worker.read(path, del): Promise | ### Worker.read(path, del): Promise | ||||||
| 
 | 
 | ||||||
| Worker.read() reads data from file system, often used to get output data after specific task. | 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 | ```javascript | ||||||
| (async () => { | (async () => { | ||||||
|   const { data } = await worker.read('output.mp4'); |   const { data } = await worker.read("output.mp4"); | ||||||
| })(); | })(); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| <a name="worker-remove"></a> | <a name="worker-remove"></a> | ||||||
|  | 
 | ||||||
| ### Worker.remove(path): Promise | ### 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. | 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 | ```javascript | ||||||
| (async () => { | (async () => { | ||||||
|   await worker.remove('output.mp4'); |   await worker.remove("output.mp4"); | ||||||
| })(); | })(); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| <a name="worker-transcode"></a> | <a name="worker-transcode"></a> | ||||||
|  | 
 | ||||||
| ### Worker.transcode(inputPath, outputPath, options, del, jobId): Promise | ### Worker.transcode(inputPath, outputPath, options, del, jobId): Promise | ||||||
| 
 | 
 | ||||||
| Worker.transcode() transcode a video file to another format. | Worker.transcode() transcode a video file to another format. | ||||||
| @ -142,11 +151,12 @@ Worker.transcode() transcode a video file to another format. | |||||||
| 
 | 
 | ||||||
| ```javascript | ```javascript | ||||||
| (async () => { | (async () => { | ||||||
|   await worker.transcode('flame.avi', 'output.mp4', '-s 1920x1080'); |   await worker.transcode("flame.avi", "output.mp4", "-s 1920x1080"); | ||||||
| })(); | })(); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| <a name="worker-trim"></a> | <a name="worker-trim"></a> | ||||||
|  | 
 | ||||||
| ### Worker.trim(inputPath, outputPath, from, to, options, del, jobId): Promise | ### Worker.trim(inputPath, outputPath, from, to, options, del, jobId): Promise | ||||||
| 
 | 
 | ||||||
| Worker.trim() trims video to specific interval. | Worker.trim() trims video to specific interval. | ||||||
| @ -165,11 +175,34 @@ Worker.trim() trims video to specific interval. | |||||||
| 
 | 
 | ||||||
| ```javascript | ```javascript | ||||||
| (async () => { | (async () => { | ||||||
|   await worker.trim('flame.avi', 'output.mp4', 1, 2); |   await worker.trim("flame.avi", "output.mp4", 1, 2); | ||||||
|  | })(); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | <a name="worker-concatDemuxer"></a> | ||||||
|  | 
 | ||||||
|  | ### 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"); | ||||||
| })(); | })(); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| <a name="worker-run"></a> | <a name="worker-run"></a> | ||||||
|  | 
 | ||||||
| ### Worker.run(args, options, jobId): Promise | ### Worker.run(args, options, jobId): Promise | ||||||
| 
 | 
 | ||||||
| Worker.run() is similar to FFmpeg cli tool, aims to provide maximum flexiblity for users. | 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 | ```javascript | ||||||
| (async () => { | (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" | ||||||
|  |   }); | ||||||
| })(); | })(); | ||||||
| ``` | ``` | ||||||
|  | |||||||
							
								
								
									
										57
									
								
								examples/browser/concatDemuxer.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								examples/browser/concatDemuxer.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | |||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <script src="/dist/ffmpeg.dev.js"></script> | ||||||
|  |     <style> | ||||||
|  |       html, | ||||||
|  |       body { | ||||||
|  |         margin: 0; | ||||||
|  |         width: 100%; | ||||||
|  |         height: 100%; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       body { | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |         align-items: center; | ||||||
|  |       } | ||||||
|  |     </style> | ||||||
|  |   </head> | ||||||
|  | 
 | ||||||
|  |   <body> | ||||||
|  |     <h3>Select multiple video files to Concatenate</h3> | ||||||
|  |     <video id="output-video" controls></video><br /> | ||||||
|  |     <input type="file" id="uploader" multiple /> | ||||||
|  |     <p id="message"></p> | ||||||
|  |     <script> | ||||||
|  |       const { createWorker } = FFmpeg; | ||||||
|  |       const worker = createWorker({ | ||||||
|  |         corePath: "../../node_modules/@ffmpeg/core/ffmpeg-core.js", | ||||||
|  |         logger: ({ message }) => console.log(message) | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       const transcode = async ({ target: { files } }) => { | ||||||
|  |         const message = document.getElementById("message"); | ||||||
|  |         message.innerHTML = "Loading ffmpeg-core.js"; | ||||||
|  |         await worker.load(); | ||||||
|  |         message.innerHTML = "Start Concating"; | ||||||
|  |         const inputPaths = []; | ||||||
|  |         for (const file of files) { | ||||||
|  |           const { name } = file; | ||||||
|  |           await worker.write(name, file); | ||||||
|  |           inputPaths.push(name); | ||||||
|  |         } | ||||||
|  |         await worker.concatDemuxer(inputPaths, "output.mp4"); | ||||||
|  |         message.innerHTML = "Complete Concating"; | ||||||
|  |         const { data } = await worker.read("output.mp4"); | ||||||
|  |         const video = document.getElementById("output-video"); | ||||||
|  |         video.src = URL.createObjectURL( | ||||||
|  |           new Blob([data.buffer], { | ||||||
|  |             type: "video/mp4" | ||||||
|  |           }) | ||||||
|  |         ); | ||||||
|  |       }; | ||||||
|  |       const elm = document.getElementById("uploader"); | ||||||
|  |       elm.addEventListener("change", transcode); | ||||||
|  |     </script> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
| @ -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) => ( |   const ls = (path, jobId) => ( | ||||||
|     startJob(createJob({ |     startJob(createJob({ | ||||||
|       id: jobId, action: 'ls', payload: { path }, |       id: jobId, action: 'ls', payload: { path }, | ||||||
| @ -175,6 +183,7 @@ module.exports = (_options = {}) => { | |||||||
|     run, |     run, | ||||||
|     transcode, |     transcode, | ||||||
|     trim, |     trim, | ||||||
|  |     concatDemuxer, | ||||||
|     ls, |     ls, | ||||||
|     terminate, |     terminate, | ||||||
|   }; |   }; | ||||||
|  | |||||||
| @ -66,7 +66,9 @@ const ls = ({ | |||||||
| const run = async ({ | const run = async ({ | ||||||
|   payload: { |   payload: { | ||||||
|     args: _args, |     args: _args, | ||||||
|     options: { inputPath, outputPath, del }, |     options: { | ||||||
|  |       inputPath, inputPaths, outputPath, del, | ||||||
|  |     }, | ||||||
|   }, |   }, | ||||||
| }, res) => { | }, res) => { | ||||||
|   const args = [...defaultArgs, ..._args.trim().split(' ')]; |   const args = [...defaultArgs, ..._args.trim().split(' ')]; | ||||||
| @ -75,6 +77,9 @@ const run = async ({ | |||||||
|   Module.FS.unlink(outputPath); |   Module.FS.unlink(outputPath); | ||||||
|   if (del && typeof inputPath === 'string') { |   if (del && typeof inputPath === 'string') { | ||||||
|     await adapter.fs.deleteFile(inputPath); |     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(' ')}` }); |   res.resolve({ message: `Complete ${args.join(' ')}` }); | ||||||
| }; | }; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 jeromewu
						jeromewu