Complete major refactor
This commit is contained in:
parent
25f37fa00b
commit
265cf4c580
@ -1,13 +1,11 @@
|
|||||||
const resolveURL = require('resolve-url');
|
const resolveURL = require('resolve-url');
|
||||||
const { dependencies } = require('../../package.json');
|
const { devDependencies } = require('../../package.json');
|
||||||
const defaultOptions = require('../constants/defaultOptions');
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default options for browser worker
|
* Default options for browser environment
|
||||||
*/
|
*/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...defaultOptions,
|
|
||||||
corePath: (typeof process !== 'undefined' && process.env.FFMPEG_ENV === 'development')
|
corePath: (typeof process !== 'undefined' && process.env.FFMPEG_ENV === 'development')
|
||||||
? resolveURL('/node_modules/@ffmpeg/core/ffmpeg-core.js')
|
? resolveURL('/node_modules/@ffmpeg/core/dist/ffmpeg-core.js')
|
||||||
: `https://unpkg.com/@ffmpeg/core@v${dependencies['@ffmpeg/core'].substring(1)}/ffmpeg-core.js`,
|
: `https://unpkg.com/@ffmpeg/core@v${devDependencies['@ffmpeg/core'].substring(1)}/ffmpeg-core.js`,
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
const resolveURL = require('resolve-url');
|
const resolveURL = require('resolve-url');
|
||||||
|
|
||||||
/**
|
|
||||||
* readFromBlobOrFile
|
|
||||||
*
|
|
||||||
* @name readFromBlobOrFile
|
|
||||||
* @function
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
const readFromBlobOrFile = (blob) => (
|
const readFromBlobOrFile = (blob) => (
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
@ -23,19 +16,21 @@ const readFromBlobOrFile = (blob) => (
|
|||||||
module.exports = async (_data) => {
|
module.exports = async (_data) => {
|
||||||
let data = _data;
|
let data = _data;
|
||||||
if (typeof _data === 'undefined') {
|
if (typeof _data === 'undefined') {
|
||||||
return 'undefined';
|
return new Uint8Array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof _data === 'string') {
|
if (typeof _data === 'string') {
|
||||||
// Base64 _data
|
/* From base64 format */
|
||||||
if (/data:_data\/([a-zA-Z]*);base64,([^"]*)/.test(_data)) {
|
if (/data:_data\/([a-zA-Z]*);base64,([^"]*)/.test(_data)) {
|
||||||
data = atob(_data.split(',')[1])
|
data = atob(_data.split(',')[1])
|
||||||
.split('')
|
.split('')
|
||||||
.map((c) => c.charCodeAt(0));
|
.map((c) => c.charCodeAt(0));
|
||||||
|
/* From remote server/URL */
|
||||||
} else {
|
} else {
|
||||||
const res = await fetch(resolveURL(_data));
|
const res = await fetch(resolveURL(_data));
|
||||||
data = await res.arrayBuffer();
|
data = await res.arrayBuffer();
|
||||||
}
|
}
|
||||||
|
/* From Blob or File */
|
||||||
} else if (_data instanceof File || _data instanceof Blob) {
|
} else if (_data instanceof File || _data instanceof Blob) {
|
||||||
data = await readFromBlobOrFile(_data);
|
data = await readFromBlobOrFile(_data);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
const resolveURL = require('resolve-url');
|
||||||
const { log } = require('../utils/log');
|
const { log } = require('../utils/log');
|
||||||
|
|
||||||
module.exports = async ({ corePath }) => {
|
module.exports = async ({ corePath: _corePath }) => {
|
||||||
if (typeof window.Module === 'undefined') {
|
if (typeof window.createFFmpegCore === 'undefined') {
|
||||||
log('info', 'fetch ffmpeg-core.worker.js script');
|
log('info', 'fetch ffmpeg-core.worker.js script');
|
||||||
|
const corePath = resolveURL(_corePath);
|
||||||
const workerBlob = await (await fetch(corePath.replace('ffmpeg-core.js', 'ffmpeg-core.worker.js'))).blob();
|
const workerBlob = await (await fetch(corePath.replace('ffmpeg-core.js', 'ffmpeg-core.worker.js'))).blob();
|
||||||
window.FFMPEG_CORE_WORKER_SCRIPT = URL.createObjectURL(workerBlob);
|
window.FFMPEG_CORE_WORKER_SCRIPT = URL.createObjectURL(workerBlob);
|
||||||
log('info', `worker object URL=${window.FFMPEG_CORE_WORKER_SCRIPT}`);
|
log('info', `worker object URL=${window.FFMPEG_CORE_WORKER_SCRIPT}`);
|
||||||
@ -12,10 +14,7 @@ module.exports = async ({ corePath }) => {
|
|||||||
const eventHandler = () => {
|
const eventHandler = () => {
|
||||||
script.removeEventListener('load', eventHandler);
|
script.removeEventListener('load', eventHandler);
|
||||||
log('info', 'initialize ffmpeg-core');
|
log('info', 'initialize ffmpeg-core');
|
||||||
window.Module.onRuntimeInitialized = () => {
|
resolve(window.createFFmpegCore);
|
||||||
log('info', 'ffmpeg-core initialized');
|
|
||||||
resolve(window.Module);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
script.src = corePath;
|
script.src = corePath;
|
||||||
script.type = 'text/javascript';
|
script.type = 'text/javascript';
|
||||||
@ -24,5 +23,5 @@ module.exports = async ({ corePath }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
log('info', 'ffmpeg-core is loaded already');
|
log('info', 'ffmpeg-core is loaded already');
|
||||||
return Promise.resolve(window.Module);
|
return Promise.resolve(window.createFFmpegCore);
|
||||||
};
|
};
|
@ -1,9 +1,9 @@
|
|||||||
const defaultOptions = require('./defaultOptions');
|
const defaultOptions = require('./defaultOptions');
|
||||||
const getModule = require('./getModule');
|
const getCreateFFmpegCore = require('./getCreateFFmpegCore');
|
||||||
const fetchFile = require('./fetchFile');
|
const fetchFile = require('./fetchFile');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
getModule,
|
getCreateFFmpegCore,
|
||||||
fetchFile,
|
fetchFile,
|
||||||
};
|
};
|
||||||
|
52
src/config.js
Normal file
52
src/config.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
module.exports = {
|
||||||
|
defaultArgs: [
|
||||||
|
/* args[0] is always the binary path */
|
||||||
|
'./ffmpeg',
|
||||||
|
/* Disable interaction mode */
|
||||||
|
'-nostdin',
|
||||||
|
/* Force to override output file */
|
||||||
|
'-y',
|
||||||
|
/* Not to output banner */
|
||||||
|
'-hide_banner',
|
||||||
|
],
|
||||||
|
baseOptions: {
|
||||||
|
/* Flag to turn on/off log messages in console */
|
||||||
|
log: false,
|
||||||
|
/*
|
||||||
|
* Custom logger to get ffmpeg.wasm output messages.
|
||||||
|
* a sample logger looks like this:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* logger = ({ type, message }) => {
|
||||||
|
* console.log(type, message);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* type can be one of following:
|
||||||
|
*
|
||||||
|
* info: internal workflow debug messages
|
||||||
|
* fferr: ffmpeg native stderr output
|
||||||
|
* ffout: ffmpeg native stdout output
|
||||||
|
*/
|
||||||
|
logger: () => {},
|
||||||
|
/*
|
||||||
|
* Progress handler to get current progress of ffmpeg command.
|
||||||
|
* a sample progress handler looks like this:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* progress = ({ ratio }) => {
|
||||||
|
* console.log(ratio);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ratio is a float number between 0 to 1.
|
||||||
|
*/
|
||||||
|
progress: () => {},
|
||||||
|
/*
|
||||||
|
* Path to find/download ffmpeg.wasm-core,
|
||||||
|
* this value should be overwriten by `defaultOptions` in
|
||||||
|
* each environment.
|
||||||
|
*/
|
||||||
|
corePath: '',
|
||||||
|
},
|
||||||
|
};
|
@ -1,5 +0,0 @@
|
|||||||
module.exports = [
|
|
||||||
'./ffmpeg', // args[0] is always binary path
|
|
||||||
'-nostdin', // Disable interaction mode
|
|
||||||
'-hide_banner', // Not to output banner
|
|
||||||
];
|
|
@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
log: false,
|
|
||||||
logger: () => {},
|
|
||||||
progress: () => {},
|
|
||||||
};
|
|
@ -1,21 +1,14 @@
|
|||||||
const defaultArgs = require('./constants/defaultArgs');
|
const { defaultArgs, baseOptions } = require('./config');
|
||||||
const { setLogging, log } = require('./utils/log');
|
const { setLogging, setCustomLogger, log } = require('./utils/log');
|
||||||
const resolvePaths = require('./utils/resolvePaths');
|
|
||||||
const parseProgress = require('./utils/parseProgress');
|
const parseProgress = require('./utils/parseProgress');
|
||||||
const stringList2pointer = require('./utils/stringList2pointer');
|
|
||||||
const parseArgs = require('./utils/parseArgs');
|
const parseArgs = require('./utils/parseArgs');
|
||||||
const {
|
const { defaultOptions, getCreateFFmpegCore } = require('./node');
|
||||||
defaultOptions,
|
|
||||||
getModule,
|
|
||||||
fetchFile,
|
|
||||||
} = require('./node');
|
|
||||||
|
|
||||||
const NO_LOAD = Error('FFmpeg.js is not ready, make sure you have completed load().');
|
const NO_LOAD = Error('ffmpeg.wasm is not ready, make sure you have completed load().');
|
||||||
const NO_MULTIPLE_RUN = Error('FFmpeg.js can only run one command at a time');
|
|
||||||
let Module = null;
|
|
||||||
let ffmpeg = null;
|
|
||||||
|
|
||||||
module.exports = (_options = {}) => {
|
module.exports = (_options = {}) => {
|
||||||
|
let Core = null;
|
||||||
|
let ffmpeg = null;
|
||||||
let runResolve = null;
|
let runResolve = null;
|
||||||
let running = false;
|
let running = false;
|
||||||
const {
|
const {
|
||||||
@ -23,109 +16,134 @@ module.exports = (_options = {}) => {
|
|||||||
logger,
|
logger,
|
||||||
progress,
|
progress,
|
||||||
...options
|
...options
|
||||||
} = resolvePaths({
|
} = {
|
||||||
|
...baseOptions,
|
||||||
...defaultOptions,
|
...defaultOptions,
|
||||||
..._options,
|
..._options,
|
||||||
});
|
};
|
||||||
const detectCompletion = ({ message, type }) => {
|
const detectCompletion = (message) => {
|
||||||
if (type === 'ffmpeg-stdout'
|
if (message === 'FFMPEG_END' && runResolve !== null) {
|
||||||
&& message === 'FFMPEG_END'
|
|
||||||
&& runResolve !== null) {
|
|
||||||
runResolve();
|
runResolve();
|
||||||
runResolve = null;
|
runResolve = null;
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const parseMessage = ({ type, message }) => {
|
||||||
|
log(type, message);
|
||||||
|
parseProgress(message, progress);
|
||||||
|
detectCompletion(message);
|
||||||
|
};
|
||||||
|
|
||||||
setLogging(logging);
|
/*
|
||||||
|
* Load ffmpeg.wasm-core script.
|
||||||
|
* In browser environment, the ffmpeg.wasm-core script is fetch from
|
||||||
|
* CDN and can be assign to a local path by assigning `corePath`.
|
||||||
|
* In node environment, we use dynamic require and the default `corePath`
|
||||||
|
* is `$ffmpeg/core`.
|
||||||
|
*
|
||||||
|
* Typically the load() func might take few seconds to minutes to complete,
|
||||||
|
* better to do it as early as possible.
|
||||||
|
*
|
||||||
|
*/
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
if (Module === null) {
|
|
||||||
log('info', 'load ffmpeg-core');
|
log('info', 'load ffmpeg-core');
|
||||||
Module = await getModule(options);
|
if (Core === null) {
|
||||||
Module.setLogger((_log) => {
|
log('info', 'loading ffmpeg-core');
|
||||||
detectCompletion(_log);
|
const createFFmpegCore = await getCreateFFmpegCore(options);
|
||||||
parseProgress(_log, progress);
|
Core = await createFFmpegCore({
|
||||||
logger(_log);
|
printErr: (message) => parseMessage({ type: 'fferr', message }),
|
||||||
log(_log.type, _log.message);
|
print: (message) => parseMessage({ type: 'ffout', message }),
|
||||||
});
|
});
|
||||||
if (ffmpeg === null) {
|
ffmpeg = Core.cwrap('proxy_main', 'number', ['number', 'number']);
|
||||||
ffmpeg = Module.cwrap('proxy_main', 'number', ['number', 'number']);
|
|
||||||
}
|
|
||||||
log('info', 'ffmpeg-core loaded');
|
log('info', 'ffmpeg-core loaded');
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const FS = (method, args) => {
|
|
||||||
if (Module === null) {
|
|
||||||
throw NO_LOAD;
|
|
||||||
} else {
|
} else {
|
||||||
log('info', `FS.${method} ${args[0]}`);
|
throw Error('ffmpeg.wasm was loaded, you should not load it again, use ffmpeg.isLoaded() to check next time.');
|
||||||
return Module.FS[method](...args);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const write = async (path, data) => (
|
|
||||||
FS('writeFile', [path, await fetchFile(data)])
|
|
||||||
);
|
|
||||||
|
|
||||||
const writeText = (path, text) => (
|
/*
|
||||||
FS('writeFile', [path, text])
|
* Determine whether the Core is loaded.
|
||||||
);
|
*/
|
||||||
|
const isLoaded = () => Core !== null;
|
||||||
|
|
||||||
const read = (path) => (
|
/*
|
||||||
FS('readFile', [path])
|
* Run ffmpeg command.
|
||||||
);
|
* This is the major function in ffmpeg.wasm, you can just imagine it
|
||||||
|
* as ffmpeg native cli and what you need to pass is the same.
|
||||||
const remove = (path) => (
|
*
|
||||||
FS('unlink', [path])
|
* For example, you can convert native command below:
|
||||||
);
|
*
|
||||||
|
* ```
|
||||||
const ls = (path) => (
|
* $ ffmpeg -i video.avi -c:v libx264 video.mp4
|
||||||
FS('readdir', [path])
|
* ```
|
||||||
);
|
*
|
||||||
|
* To
|
||||||
const run = (_args) => {
|
*
|
||||||
if (ffmpeg === null) {
|
* ```
|
||||||
|
* await ffmpeg.run('-i', 'video.avi', '-c:v', 'libx264', 'video.mp4');
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const run = (..._args) => {
|
||||||
|
log('info', `run ffmpeg command: ${_args.join(' ')}`);
|
||||||
|
if (Core === null) {
|
||||||
throw NO_LOAD;
|
throw NO_LOAD;
|
||||||
} else if (running) {
|
} else if (running) {
|
||||||
throw NO_MULTIPLE_RUN;
|
throw Error('ffmpeg.wasm can only run one command at a time');
|
||||||
} else {
|
} else {
|
||||||
running = true;
|
running = true;
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const args = [...defaultArgs, ...parseArgs(_args)].filter((s) => s.length !== 0);
|
const args = [...defaultArgs, ..._args].filter((s) => s.length !== 0);
|
||||||
log('info', `ffmpeg command: ${args.join(' ')}`);
|
|
||||||
runResolve = resolve;
|
runResolve = resolve;
|
||||||
ffmpeg(args.length, stringList2pointer(Module, args));
|
ffmpeg(...parseArgs(Core, args));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const transcode = (input, output, opts = '') => (
|
/*
|
||||||
run(`-i ${input} ${opts} ${output}`)
|
* Run FS operations.
|
||||||
);
|
* For input/output file of ffmpeg.wasm, it is required to save them to MEMFS
|
||||||
|
* first so that ffmpeg.wasm is able to consume them. Here we rely on the FS
|
||||||
const trim = (input, output, from, to, opts = '') => (
|
* methods provided by Emscripten.
|
||||||
run(`-i ${input} -ss ${from} -to ${to} ${opts} ${output}`)
|
*
|
||||||
);
|
* Common methods to use are:
|
||||||
|
* ffmpeg.FS('writeFile', 'video.avi', new Uint8Array(...)): writeFile writes
|
||||||
const concatDemuxer = (input, output, opts = '') => {
|
* data to MEMFS. You need to use Uint8Array for binary data.
|
||||||
const text = input.reduce((acc, path) => `${acc}\nfile ${path}`, '');
|
* ffmpeg.FS('readFile', 'video.mp4'): readFile from MEMFS.
|
||||||
writeText('concat_list.txt', text);
|
* ffmpeg.FS('unlink', 'video.map'): delete file from MEMFS.
|
||||||
return run(`-f concat -safe 0 -i concat_list.txt ${opts} ${output}`);
|
*
|
||||||
|
* For more info, check https://emscripten.org/docs/api_reference/Filesystem-API.html
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const FS = (method, ...args) => {
|
||||||
|
log('info', `run FS.${method} ${args.map((arg) => (typeof arg === 'string' ? arg : `<${arg.length} bytes binary file>`)).join(' ')}`);
|
||||||
|
if (Core === null) {
|
||||||
|
throw NO_LOAD;
|
||||||
|
} else {
|
||||||
|
let ret = null;
|
||||||
|
try {
|
||||||
|
ret = Core.FS[method](...args);
|
||||||
|
} catch (e) {
|
||||||
|
if (method === 'readdir') {
|
||||||
|
throw Error(`ffmpeg.FS('readdir', '${args[0]}') error. Check if the path exists, ex: ffmpeg.FS('readdir', '/')`);
|
||||||
|
} else if (method === 'readFile') {
|
||||||
|
throw Error(`ffmpeg.FS('readFile', '${args[0]}') error. Check if the path exists`);
|
||||||
|
} else {
|
||||||
|
throw Error('Oops, something went wrong in FS operation.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setLogging(logging);
|
||||||
|
setCustomLogger(logger);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
load,
|
load,
|
||||||
FS,
|
isLoaded,
|
||||||
write,
|
|
||||||
writeText,
|
|
||||||
read,
|
|
||||||
remove,
|
|
||||||
ls,
|
|
||||||
run,
|
run,
|
||||||
transcode,
|
FS,
|
||||||
trim,
|
|
||||||
concatDemuxer,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
30
src/index.js
30
src/index.js
@ -1,6 +1,36 @@
|
|||||||
require('regenerator-runtime/runtime');
|
require('regenerator-runtime/runtime');
|
||||||
const createFFmpeg = require('./createFFmpeg');
|
const createFFmpeg = require('./createFFmpeg');
|
||||||
|
const { fetchFile } = require('./node');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
/*
|
||||||
|
* Create ffmpeg instance.
|
||||||
|
* Each ffmpeg instance owns an isolated MEMFS and works
|
||||||
|
* independently.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* const ffmpeg = createFFmpeg({
|
||||||
|
* log: true,
|
||||||
|
* logger: () => {},
|
||||||
|
* progress: () => {},
|
||||||
|
* corePath: '',
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* For the usage of these four arguments, check config.js
|
||||||
|
*
|
||||||
|
*/
|
||||||
createFFmpeg,
|
createFFmpeg,
|
||||||
|
/*
|
||||||
|
* Helper function for fetching files from various resource.
|
||||||
|
* Sometimes the video/audio file you want to process may located
|
||||||
|
* in a remote URL and somewhere in your local file system.
|
||||||
|
*
|
||||||
|
* This helper function helps you to fetch to file and return an
|
||||||
|
* Uint8Array variable for ffmpeg.wasm to consume.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fetchFile,
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
const defaultOptions = require('../constants/defaultOptions');
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default options for node environment
|
* Default options for node environment
|
||||||
*/
|
*/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
...defaultOptions,
|
corePath: '@ffmpeg/core',
|
||||||
};
|
};
|
||||||
|
@ -10,14 +10,21 @@ module.exports = async (_data) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof _data === 'string') {
|
if (typeof _data === 'string') {
|
||||||
if (isURL(_data) || _data.startsWith('chrome-extension://') || _data.startsWith('file://')) {
|
/* From remote URL/server */
|
||||||
|
if (isURL(_data)
|
||||||
|
|| _data.startsWith('moz-extension://')
|
||||||
|
|| _data.startsWith('chrome-extension://')
|
||||||
|
|| _data.startsWith('file://')) {
|
||||||
const res = await fetch(_data);
|
const res = await fetch(_data);
|
||||||
data = await res.arrayBuffer();
|
data = await res.arrayBuffer();
|
||||||
|
/* From base64 format */
|
||||||
} else if (/data:_data\/([a-zA-Z]*);base64,([^"]*)/.test(_data)) {
|
} else if (/data:_data\/([a-zA-Z]*);base64,([^"]*)/.test(_data)) {
|
||||||
data = Buffer.from(_data.split(',')[1], 'base64');
|
data = Buffer.from(_data.split(',')[1], 'base64');
|
||||||
|
/* From local file path */
|
||||||
} else {
|
} else {
|
||||||
data = await util.promisify(fs.readFile)(_data);
|
data = await util.promisify(fs.readFile)(_data);
|
||||||
}
|
}
|
||||||
|
/* From Buffer */
|
||||||
} else if (Buffer.isBuffer(_data)) {
|
} else if (Buffer.isBuffer(_data)) {
|
||||||
data = _data;
|
data = _data;
|
||||||
}
|
}
|
||||||
|
7
src/node/getCreateFFmpegCore.js
Normal file
7
src/node/getCreateFFmpegCore.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const { log } = require('../utils/log');
|
||||||
|
|
||||||
|
module.exports = ({ corePath }) => new Promise((resolve) => {
|
||||||
|
log('info', `fetch ffmpeg.wasm-core script from ${corePath}`);
|
||||||
|
// eslint-disable-next-line import/no-dynamic-require
|
||||||
|
resolve(require(corePath));
|
||||||
|
});
|
@ -1,6 +0,0 @@
|
|||||||
module.exports = () => new Promise((resolve) => {
|
|
||||||
const Module = require('@ffmpeg/core');
|
|
||||||
Module.onRuntimeInitialized = () => {
|
|
||||||
resolve(Module);
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,9 +1,9 @@
|
|||||||
const defaultOptions = require('./defaultOptions');
|
const defaultOptions = require('./defaultOptions');
|
||||||
const getModule = require('./getModule');
|
const getCreateFFmpegCore = require('./getCreateFFmpegCore');
|
||||||
const fetchFile = require('./fetchFile');
|
const fetchFile = require('./fetchFile');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
getModule,
|
getCreateFFmpegCore,
|
||||||
fetchFile,
|
fetchFile,
|
||||||
};
|
};
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
const isElectron = require('is-electron');
|
|
||||||
|
|
||||||
module.exports = (key) => {
|
|
||||||
const env = {};
|
|
||||||
|
|
||||||
if (isElectron()) {
|
|
||||||
env.type = 'electron';
|
|
||||||
} else if (typeof window === 'object') {
|
|
||||||
env.type = 'browser';
|
|
||||||
} else if (typeof importScripts === 'function') {
|
|
||||||
env.type = 'webworker';
|
|
||||||
} else if (typeof process === 'object' && typeof require === 'function') {
|
|
||||||
env.type = 'node';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof key === 'undefined') {
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
return env[key];
|
|
||||||
};
|
|
@ -1,9 +1,24 @@
|
|||||||
let logging = false;
|
let logging = false;
|
||||||
|
let customLogger = () => {};
|
||||||
|
|
||||||
exports.logging = logging;
|
const setLogging = (_logging) => {
|
||||||
|
|
||||||
exports.setLogging = (_logging) => {
|
|
||||||
logging = _logging;
|
logging = _logging;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.log = (type, message) => (logging ? console.log(`[${type}] ${message}`) : null);
|
const setCustomLogger = (logger) => {
|
||||||
|
customLogger = logger;
|
||||||
|
};
|
||||||
|
|
||||||
|
const log = (type, message) => {
|
||||||
|
customLogger({ type, message });
|
||||||
|
if (logging) {
|
||||||
|
console.log(`[${type}] ${message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
logging,
|
||||||
|
setLogging,
|
||||||
|
setCustomLogger,
|
||||||
|
log,
|
||||||
|
};
|
||||||
|
@ -1,51 +1,9 @@
|
|||||||
module.exports = (cmd) => {
|
module.exports = (Core, args) => {
|
||||||
const args = [];
|
const argsPtr = Core._malloc(args.length * Uint32Array.BYTES_PER_ELEMENT);
|
||||||
let nextDelimiter = 0;
|
args.forEach((s, idx) => {
|
||||||
let prevDelimiter = 0;
|
const buf = Core._malloc(s.length + 1);
|
||||||
// eslint-disable-next-line no-cond-assign
|
Core.writeAsciiToMemory(s, buf);
|
||||||
while ((nextDelimiter = cmd.indexOf(' ', prevDelimiter)) >= 0) {
|
Core.setValue(argsPtr + (Uint32Array.BYTES_PER_ELEMENT * idx), buf, 'i32');
|
||||||
let arg = cmd.substring(prevDelimiter, nextDelimiter);
|
});
|
||||||
let quoteIdx = arg.indexOf('\'');
|
return [args.length, argsPtr];
|
||||||
let dblQuoteIdx = arg.indexOf('"');
|
|
||||||
|
|
||||||
if (quoteIdx === 0 || dblQuoteIdx === 0) {
|
|
||||||
/* The argument has a quote at the start i.e, 'id=0,streams=0 id=1,streams=1' */
|
|
||||||
const delimiter = arg[0];
|
|
||||||
const endDelimiter = cmd.indexOf(delimiter, prevDelimiter + 1);
|
|
||||||
|
|
||||||
if (endDelimiter < 0) {
|
|
||||||
throw new Error(`Bad command escape sequence ${delimiter} near ${nextDelimiter}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
arg = cmd.substring(prevDelimiter + 1, endDelimiter);
|
|
||||||
prevDelimiter = endDelimiter + 2;
|
|
||||||
args.push(arg);
|
|
||||||
} else if (quoteIdx > 0 || dblQuoteIdx > 0) {
|
|
||||||
/* The argument has a quote in it, it must be ended correctly i,e. title='test' */
|
|
||||||
if (quoteIdx === -1) quoteIdx = Infinity;
|
|
||||||
if (dblQuoteIdx === -1) dblQuoteIdx = Infinity;
|
|
||||||
const delimiter = (quoteIdx < dblQuoteIdx) ? '\'' : '"';
|
|
||||||
const quoteOffset = Math.min(quoteIdx, dblQuoteIdx);
|
|
||||||
const endDelimiter = cmd.indexOf(delimiter, prevDelimiter + quoteOffset + 1);
|
|
||||||
|
|
||||||
if (endDelimiter < 0) {
|
|
||||||
throw new Error(`Bad command escape sequence ${delimiter} near ${nextDelimiter}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
arg = cmd.substring(prevDelimiter, endDelimiter + 1);
|
|
||||||
prevDelimiter = endDelimiter + 2;
|
|
||||||
args.push(arg);
|
|
||||||
} else if (arg !== '') {
|
|
||||||
args.push(arg);
|
|
||||||
prevDelimiter = nextDelimiter + 1;
|
|
||||||
} else {
|
|
||||||
prevDelimiter = nextDelimiter + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevDelimiter !== cmd.length) {
|
|
||||||
args.push(cmd.substring(prevDelimiter));
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,7 @@ const ts2sec = (ts) => {
|
|||||||
return (parseFloat(h) * 60 * 60) + (parseFloat(m) * 60) + parseFloat(s);
|
return (parseFloat(h) * 60 * 60) + (parseFloat(m) * 60) + parseFloat(s);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = ({ message }, progress) => {
|
module.exports = (message, progress) => {
|
||||||
if (typeof message === 'string') {
|
if (typeof message === 'string') {
|
||||||
if (message.startsWith(' Duration')) {
|
if (message.startsWith(' Duration')) {
|
||||||
const ts = message.split(', ')[0].split(': ')[1];
|
const ts = message.split(', ')[0].split(': ')[1];
|
||||||
@ -19,6 +19,7 @@ module.exports = ({ message }, progress) => {
|
|||||||
progress({ ratio: t / duration });
|
progress({ ratio: t / duration });
|
||||||
} else if (message.startsWith('video:')) {
|
} else if (message.startsWith('video:')) {
|
||||||
progress({ ratio: 1 });
|
progress({ ratio: 1 });
|
||||||
|
duration = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
const isBrowser = require('./getEnvironment')('type') === 'browser';
|
|
||||||
const resolveURL = isBrowser ? require('resolve-url') : s => s; // eslint-disable-line
|
|
||||||
|
|
||||||
module.exports = (options) => {
|
|
||||||
const opts = { ...options };
|
|
||||||
['corePath'].forEach((key) => {
|
|
||||||
if (typeof options[key] !== 'undefined') {
|
|
||||||
opts[key] = resolveURL(opts[key]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return opts;
|
|
||||||
};
|
|
@ -1,8 +0,0 @@
|
|||||||
module.exports = (Module, s) => {
|
|
||||||
const ptr = Module._malloc((s.length + 1) * Uint8Array.BYTES_PER_ELEMENT);
|
|
||||||
for (let i = 0; i < s.length; i += 1) {
|
|
||||||
Module.setValue(ptr + i, s.charCodeAt(i), 'i8');
|
|
||||||
}
|
|
||||||
Module.setValue(ptr + s.length, 0, 'i8');
|
|
||||||
return ptr;
|
|
||||||
};
|
|
@ -1,12 +0,0 @@
|
|||||||
const string2pointer = require('./string2pointer');
|
|
||||||
|
|
||||||
module.exports = (Module, strList) => {
|
|
||||||
const listPtr = Module._malloc(strList.length * Uint32Array.BYTES_PER_ELEMENT);
|
|
||||||
|
|
||||||
strList.forEach((s, idx) => {
|
|
||||||
const strPtr = string2pointer(Module, s);
|
|
||||||
Module.setValue(listPtr + (4 * idx), strPtr, 'i32');
|
|
||||||
});
|
|
||||||
|
|
||||||
return listPtr;
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user