Major refactor to adapt new ffmpeg-core.js
This commit is contained in:
parent
cd5fe43905
commit
b36360f16f
6
package-lock.json
generated
6
package-lock.json
generated
@ -2034,9 +2034,9 @@
|
||||
}
|
||||
},
|
||||
"@ffmpeg/core": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg/core/-/core-0.6.0.tgz",
|
||||
"integrity": "sha512-5VNyabaCPZJEFmn92C5SIh2vFChL2l9OVGut1hmst8IPuChZahF6yniFjEdF2Ri8ZZFKXTAQrPh3WfpVaEPK9A=="
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@ffmpeg/core/-/core-0.7.0.tgz",
|
||||
"integrity": "sha512-ulDKwckLF4NrNIn98sYe8KCzKM9GfIfc9kR2Mel4pA8URQ9GEr2tGDMST5xW63Usnr6ybZCZKgwiDkAHK7VWCQ=="
|
||||
},
|
||||
"@hapi/address": {
|
||||
"version": "2.1.2",
|
||||
|
10
package.json
10
package.json
@ -14,13 +14,13 @@
|
||||
"wait": "rimraf dist && wait-on http://localhost:3000/dist/ffmpeg.dev.js",
|
||||
"test": "npm-run-all -p -r start test:all",
|
||||
"test:all": "npm-run-all wait test:browser:ffmpeg test:node:all",
|
||||
"test:node": "NODE_OPTIONS=--experimental-worker nyc mocha --exit --bail --require ./scripts/test-helper.js",
|
||||
"test:node": "node --experimental-wasm-threads --experimental-wasm-bulk-memory node_modules/.bin/_mocha --exit --bail --require ./scripts/test-helper.js",
|
||||
"test:node:all": "npm run test:node -- ./tests/*.test.js",
|
||||
"test:browser": "mocha-headless-chrome -a incognito -a no-sandbox -a disable-setuid-sandbox -a disable-logging -t 300000",
|
||||
"test:browser": "mocha-headless-chrome -a allow-file-access-from-files -a incognito -a no-sandbox -a disable-setuid-sandbox -a disable-logging -t 300000",
|
||||
"test:browser:ffmpeg": "npm run test:browser -- -f ./tests/ffmpeg.test.html"
|
||||
},
|
||||
"browser": {
|
||||
"./src/worker/node/index.js": "./src/worker/browser/index.js"
|
||||
"./src/node/index.js": "./src/browser/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -37,11 +37,11 @@
|
||||
"url": "https://github.com/ffmpegjs/ffmpeg.js/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
"node": ">=12.16.1"
|
||||
},
|
||||
"homepage": "https://github.com/ffmpegjs/ffmpeg.js#readme",
|
||||
"dependencies": {
|
||||
"@ffmpeg/core": "^0.6.0",
|
||||
"@ffmpeg/core": "^0.7.0",
|
||||
"idb": "^4.0.5",
|
||||
"is-electron": "^2.2.0",
|
||||
"is-url": "^1.2.4",
|
||||
|
@ -32,8 +32,4 @@ module.exports = [
|
||||
library: 'FFmpeg',
|
||||
libraryTarget: 'umd',
|
||||
}),
|
||||
genConfig({
|
||||
entry: path.resolve(__dirname, '..', 'src', 'worker-script', 'browser', 'index.js'),
|
||||
filename: 'worker.dev.js',
|
||||
}),
|
||||
];
|
||||
|
@ -23,8 +23,4 @@ module.exports = [
|
||||
library: 'FFmpeg',
|
||||
libraryTarget: 'umd',
|
||||
}),
|
||||
genConfig({
|
||||
entry: path.resolve(__dirname, '..', 'src', 'worker-script', 'browser', 'index.js'),
|
||||
filename: 'worker.min.js',
|
||||
}),
|
||||
];
|
||||
|
13
src/browser/defaultOptions.js
Normal file
13
src/browser/defaultOptions.js
Normal file
@ -0,0 +1,13 @@
|
||||
const resolveURL = require('resolve-url');
|
||||
const { dependencies } = require('../../package.json');
|
||||
const defaultOptions = require('../constants/defaultOptions');
|
||||
|
||||
/*
|
||||
* Default options for browser worker
|
||||
*/
|
||||
module.exports = {
|
||||
...defaultOptions,
|
||||
corePath: (typeof process !== 'undefined' && process.env.FFMPEG_ENV === 'development')
|
||||
? resolveURL('/node_modules/@ffmpeg/core/ffmpeg-core.js')
|
||||
: `https://unpkg.com/@ffmpeg/core@v${dependencies['@ffmpeg/core'].substring(1)}/ffmpeg-core.js`,
|
||||
};
|
23
src/browser/getModule.js
Normal file
23
src/browser/getModule.js
Normal file
@ -0,0 +1,23 @@
|
||||
const { log } = require('../utils/log');
|
||||
|
||||
module.exports = ({ corePath }) => new Promise((resolve) => {
|
||||
if (typeof window.Module === 'undefined') {
|
||||
log('info', `download ffmpeg-core script (~25 MB) from ${corePath}`);
|
||||
const script = document.createElement('script');
|
||||
const eventHandler = () => {
|
||||
script.removeEventListener('load', eventHandler);
|
||||
log('info', 'initialize ffmpeg-core');
|
||||
window.Module.onRuntimeInitialized = () => {
|
||||
log('info', 'ffmpeg-core initialized');
|
||||
resolve(window.Module);
|
||||
};
|
||||
};
|
||||
script.src = corePath;
|
||||
script.type = 'text/javascript';
|
||||
script.addEventListener('load', eventHandler);
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
} else {
|
||||
log('info', 'ffmpeg-core is loaded already');
|
||||
resolve(window.Module);
|
||||
}
|
||||
});
|
@ -1,11 +1,9 @@
|
||||
const defaultOptions = require('./defaultOptions');
|
||||
const spawnWorker = require('./spawnWorker');
|
||||
const onMessage = require('./onMessage');
|
||||
const getModule = require('./getModule');
|
||||
const fetchFile = require('./fetchFile');
|
||||
|
||||
module.exports = {
|
||||
defaultOptions,
|
||||
spawnWorker,
|
||||
onMessage,
|
||||
getModule,
|
||||
fetchFile,
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
module.exports = [
|
||||
'./ffmpeg', // args[0] is always binary path
|
||||
'-nostdin', // Disable interaction mode
|
||||
'-hide_banner', // Not to output banner
|
||||
];
|
@ -1,4 +1,5 @@
|
||||
module.exports = {
|
||||
log: false,
|
||||
logger: () => {},
|
||||
progress: () => {},
|
||||
};
|
||||
|
130
src/createFFmpeg.js
Normal file
130
src/createFFmpeg.js
Normal file
@ -0,0 +1,130 @@
|
||||
const defaultArgs = require('./constants/defaultArgs');
|
||||
const { setLogging, log } = require('./utils/log');
|
||||
const resolvePaths = require('./utils/resolvePaths');
|
||||
const parseProgress = require('./utils/parseProgress');
|
||||
const stringList2pointer = require('./utils/stringList2pointer');
|
||||
const {
|
||||
defaultOptions,
|
||||
getModule,
|
||||
fetchFile,
|
||||
} = require('./node');
|
||||
|
||||
const NO_LOAD = Error('FFmpeg.js 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 = {}) => {
|
||||
let runResolve = null;
|
||||
let running = false;
|
||||
const {
|
||||
log: logging,
|
||||
logger,
|
||||
progress,
|
||||
...options
|
||||
} = resolvePaths({
|
||||
...defaultOptions,
|
||||
..._options,
|
||||
});
|
||||
const detectCompletion = ({ message, type }) => {
|
||||
if (type === 'ffmpeg-stdout'
|
||||
&& message === 'FFMPEG_END'
|
||||
&& runResolve !== null) {
|
||||
runResolve();
|
||||
runResolve = null;
|
||||
running = false;
|
||||
}
|
||||
};
|
||||
|
||||
setLogging(logging);
|
||||
|
||||
const load = async () => {
|
||||
if (Module === null) {
|
||||
log('info', 'load ffmpeg-core');
|
||||
Module = await getModule(options);
|
||||
Module.setLogger((_log) => {
|
||||
detectCompletion(_log);
|
||||
parseProgress(_log, progress);
|
||||
logger(_log);
|
||||
log(_log.type, _log.message);
|
||||
});
|
||||
if (ffmpeg === null) {
|
||||
ffmpeg = Module.cwrap('proxy_main', 'number', ['number', 'number']);
|
||||
}
|
||||
log('info', 'ffmpeg-core loaded');
|
||||
}
|
||||
};
|
||||
|
||||
const FS = (method, args) => {
|
||||
if (Module === null) {
|
||||
throw NO_LOAD;
|
||||
} else {
|
||||
log('info', `FS.${method} ${args[0]}`);
|
||||
return Module.FS[method](...args);
|
||||
}
|
||||
};
|
||||
|
||||
const write = async (path, data) => (
|
||||
FS('writeFile', [path, await fetchFile(data)])
|
||||
);
|
||||
|
||||
const writeText = (path, text) => (
|
||||
FS('writeFile', [path, text])
|
||||
);
|
||||
|
||||
const read = (path) => (
|
||||
FS('readFile', [path])
|
||||
);
|
||||
|
||||
const remove = (path) => (
|
||||
FS('unlink', [path])
|
||||
);
|
||||
|
||||
const ls = (path) => (
|
||||
FS('readir', [path])
|
||||
);
|
||||
|
||||
const run = (_args) => {
|
||||
if (ffmpeg === null) {
|
||||
throw NO_LOAD;
|
||||
} else if (running) {
|
||||
throw NO_MULTIPLE_RUN;
|
||||
} else {
|
||||
running = true;
|
||||
return new Promise((resolve) => {
|
||||
const args = [...defaultArgs, ..._args.trim().split(' ')].filter((s) => s.length !== 0);
|
||||
log('info', `ffmpeg command: ${args.join(' ')}`);
|
||||
runResolve = resolve;
|
||||
ffmpeg(args.length, stringList2pointer(Module, args));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const transcode = (input, output, opts = '') => (
|
||||
run(`-i ${input} ${opts} ${output}`)
|
||||
);
|
||||
|
||||
const trim = (input, output, from, to, opts = '') => (
|
||||
run(`-i ${input} -ss ${from} -to ${to} ${opts} ${output}`)
|
||||
);
|
||||
|
||||
const concatDemuxer = (input, output, opts = '') => {
|
||||
const text = input.reduce((acc, path) => `${acc}\nfile ${path}`, '');
|
||||
writeText('concat_list.txt', text);
|
||||
return run(`-f concat -safe 0 -i concat_list.txt ${opts} ${output}`);
|
||||
};
|
||||
|
||||
return {
|
||||
load,
|
||||
FS,
|
||||
write,
|
||||
writeText,
|
||||
read,
|
||||
remove,
|
||||
ls,
|
||||
run,
|
||||
transcode,
|
||||
trim,
|
||||
concatDemuxer,
|
||||
};
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
const getId = require('./utils/getId');
|
||||
|
||||
let jobCounter = 0;
|
||||
|
||||
module.exports = ({
|
||||
id: _id,
|
||||
action,
|
||||
payload = {},
|
||||
}) => {
|
||||
let id = _id;
|
||||
if (typeof id === 'undefined') {
|
||||
id = getId('Job', jobCounter);
|
||||
jobCounter += 1;
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
action,
|
||||
payload,
|
||||
};
|
||||
};
|
@ -1,217 +0,0 @@
|
||||
const createJob = require('./createJob');
|
||||
const { log } = require('./utils/log');
|
||||
const getId = require('./utils/getId');
|
||||
const parseProgress = require('./utils/parseProgress');
|
||||
const resolvePaths = require('./utils/resolvePaths');
|
||||
const getTransferables = require('./utils/getTransferables');
|
||||
const {
|
||||
defaultOptions,
|
||||
spawnWorker,
|
||||
onMessage,
|
||||
fetchFile,
|
||||
} = require('./worker/node');
|
||||
|
||||
let workerCounter = 0;
|
||||
|
||||
module.exports = (_options = {}) => {
|
||||
const id = getId('Worker', workerCounter);
|
||||
const {
|
||||
logger,
|
||||
progress,
|
||||
...options
|
||||
} = resolvePaths({
|
||||
...defaultOptions,
|
||||
..._options,
|
||||
});
|
||||
const resolves = {};
|
||||
const rejects = {};
|
||||
let worker = spawnWorker(options);
|
||||
|
||||
workerCounter += 1;
|
||||
|
||||
const setResolve = (action, res) => {
|
||||
resolves[action] = res;
|
||||
};
|
||||
|
||||
const setReject = (action, rej) => {
|
||||
rejects[action] = rej;
|
||||
};
|
||||
|
||||
const startJob = ({ id: jobId, action, payload }) => (
|
||||
new Promise((resolve, reject) => {
|
||||
const packet = {
|
||||
workerId: id,
|
||||
jobId,
|
||||
action,
|
||||
payload,
|
||||
};
|
||||
log(`[${id}]: Start ${jobId}, action=${action}`);
|
||||
setResolve(action, resolve);
|
||||
setReject(action, reject);
|
||||
/*
|
||||
* By using Transferable in postMessage, we are able
|
||||
* to send large files to worker
|
||||
* @ref: https://github.com/ffmpegjs/ffmpeg.js/issues/8#issuecomment-572230128
|
||||
*/
|
||||
worker.postMessage(packet, getTransferables(packet));
|
||||
})
|
||||
);
|
||||
|
||||
const load = (jobId) => (
|
||||
startJob(createJob({
|
||||
id: jobId, action: 'load', payload: { options },
|
||||
}))
|
||||
);
|
||||
|
||||
const write = async (path, data, jobId) => (
|
||||
startJob(createJob({
|
||||
id: jobId,
|
||||
action: 'FS',
|
||||
payload: {
|
||||
method: 'writeFile',
|
||||
args: [path, await fetchFile(data)],
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
const writeText = (path, text, jobId) => (
|
||||
startJob(createJob({
|
||||
id: jobId,
|
||||
action: 'FS',
|
||||
payload: {
|
||||
method: 'writeFile',
|
||||
args: [path, text],
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
const read = (path, jobId) => (
|
||||
startJob(createJob({
|
||||
id: jobId,
|
||||
action: 'FS',
|
||||
payload: {
|
||||
method: 'readFile',
|
||||
args: [path],
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
const remove = (path, jobId) => (
|
||||
startJob(createJob({
|
||||
id: jobId,
|
||||
action: 'FS',
|
||||
payload: {
|
||||
method: 'unlink',
|
||||
args: [path],
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
const run = (args, jobId) => (
|
||||
startJob(createJob({
|
||||
id: jobId,
|
||||
action: 'run',
|
||||
payload: {
|
||||
args,
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
const ls = (path, jobId) => (
|
||||
startJob(createJob({
|
||||
id: jobId,
|
||||
action: 'FS',
|
||||
payload: {
|
||||
method: 'readdir',
|
||||
args: [path],
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
const transcode = (input, output, opts = '', jobId) => (
|
||||
run(
|
||||
`-i ${input} ${opts} ${output}`,
|
||||
jobId,
|
||||
)
|
||||
);
|
||||
|
||||
const trim = (input, output, from, to, opts = '', jobId) => (
|
||||
run(
|
||||
`-i ${input} -ss ${from} -to ${to} ${opts} ${output}`,
|
||||
jobId,
|
||||
)
|
||||
);
|
||||
|
||||
const concatDemuxer = async (input, output, opts = '', jobId) => {
|
||||
const text = input.reduce((acc, path) => `${acc}\nfile ${path}`, '');
|
||||
await writeText('concat_list.txt', text);
|
||||
return run(`-f concat -safe 0 -i concat_list.txt ${opts} ${output}`, jobId);
|
||||
};
|
||||
|
||||
const terminate = async () => {
|
||||
if (worker !== null) {
|
||||
/*
|
||||
await startJob(createJob({
|
||||
id: jobId,
|
||||
action: 'terminate',
|
||||
}));
|
||||
*/
|
||||
worker.terminate();
|
||||
worker = null;
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
onMessage(worker, ({
|
||||
workerId, jobId, action, status, payload,
|
||||
}) => {
|
||||
if (status === 'resolve') {
|
||||
const { message, data } = payload;
|
||||
log(`[${workerId}]: Complete ${jobId}`);
|
||||
resolves[action]({
|
||||
workerId,
|
||||
jobId,
|
||||
message,
|
||||
data,
|
||||
});
|
||||
} else if (status === 'reject') {
|
||||
rejects[action](payload);
|
||||
throw Error(
|
||||
`${payload}
|
||||
|
||||
To get more informaion for debugging, please add logger in createWorker():
|
||||
|
||||
const worker = createWorker({
|
||||
logger: ({ message }) => console.log(message),
|
||||
});
|
||||
|
||||
Even more details:
|
||||
|
||||
const { setLogging } = require('@ffmpeg/ffmpeg');
|
||||
setLogging(true);
|
||||
`,
|
||||
);
|
||||
} else if (status === 'progress') {
|
||||
parseProgress(payload, progress);
|
||||
logger(payload);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
id,
|
||||
worker,
|
||||
setResolve,
|
||||
setReject,
|
||||
load,
|
||||
write,
|
||||
writeText,
|
||||
read,
|
||||
remove,
|
||||
ls,
|
||||
run,
|
||||
transcode,
|
||||
trim,
|
||||
concatDemuxer,
|
||||
terminate,
|
||||
};
|
||||
};
|
7
src/index.js
Executable file → Normal file
7
src/index.js
Executable file → Normal file
@ -1,9 +1,6 @@
|
||||
require('regenerator-runtime/runtime');
|
||||
const { logging, setLogging } = require('./utils/log');
|
||||
const createWorker = require('./createWorker');
|
||||
const createFFmpeg = require('./createFFmpeg');
|
||||
|
||||
module.exports = {
|
||||
logging,
|
||||
setLogging,
|
||||
createWorker,
|
||||
createFFmpeg,
|
||||
};
|
||||
|
8
src/node/defaultOptions.js
Normal file
8
src/node/defaultOptions.js
Normal file
@ -0,0 +1,8 @@
|
||||
const defaultOptions = require('../constants/defaultOptions');
|
||||
|
||||
/*
|
||||
* Default options for node environment
|
||||
*/
|
||||
module.exports = {
|
||||
...defaultOptions,
|
||||
};
|
6
src/node/getModule.js
Normal file
6
src/node/getModule.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = () => new Promise((resolve) => {
|
||||
const Module = require('@ffmpeg/core');
|
||||
Module.onRuntimeInitialized = () => {
|
||||
resolve(Module);
|
||||
};
|
||||
});
|
9
src/node/index.js
Normal file
9
src/node/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
const defaultOptions = require('./defaultOptions');
|
||||
const getModule = require('./getModule');
|
||||
const fetchFile = require('./fetchFile');
|
||||
|
||||
module.exports = {
|
||||
defaultOptions,
|
||||
getModule,
|
||||
fetchFile,
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
module.exports = (prefix, cnt) => (
|
||||
`${prefix}-${cnt}-${Math.random().toString(16).slice(3, 8)}`
|
||||
);
|
@ -1,16 +0,0 @@
|
||||
module.exports = (packet) => {
|
||||
const transferables = [];
|
||||
const check = (b) => {
|
||||
if (b instanceof Uint8Array) {
|
||||
transferables.push(b.buffer);
|
||||
} else if (b instanceof ArrayBuffer) {
|
||||
transferables.push(b);
|
||||
}
|
||||
};
|
||||
const { payload: { args, data } } = packet;
|
||||
check(data);
|
||||
if (Array.isArray(args)) {
|
||||
args.forEach((arg) => check(arg));
|
||||
}
|
||||
return transferables;
|
||||
};
|
@ -6,4 +6,4 @@ exports.setLogging = (_logging) => {
|
||||
logging = _logging;
|
||||
};
|
||||
|
||||
exports.log = (...args) => (logging ? console.log.apply(this, args) : null);
|
||||
exports.log = (type, message) => (logging ? console.log(`[${type}] ${message}`) : null);
|
||||
|
@ -3,7 +3,7 @@ const resolveURL = isBrowser ? require('resolve-url') : s => s; // eslint-disabl
|
||||
|
||||
module.exports = (options) => {
|
||||
const opts = { ...options };
|
||||
['corePath', 'workerPath'].forEach((key) => {
|
||||
['corePath'].forEach((key) => {
|
||||
if (typeof options[key] !== 'undefined') {
|
||||
opts[key] = resolveURL(opts[key]);
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
const str2ptr = require('./str2ptr');
|
||||
const string2pointer = require('./string2pointer');
|
||||
|
||||
module.exports = (Module, strList) => {
|
||||
const listPtr = Module._malloc(strList.length * Uint32Array.BYTES_PER_ELEMENT);
|
||||
|
||||
strList.forEach((s, idx) => {
|
||||
const strPtr = str2ptr(Module, s);
|
||||
const strPtr = string2pointer(Module, s);
|
||||
Module.setValue(listPtr + (4 * idx), strPtr, 'i32');
|
||||
});
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"worker": true
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
module.exports = (corePath) => {
|
||||
if (typeof global.Module === 'undefined') {
|
||||
global.importScripts(corePath);
|
||||
}
|
||||
return global.Module;
|
||||
};
|
@ -1,10 +0,0 @@
|
||||
const worker = require('..');
|
||||
const getCore = require('./getCore');
|
||||
|
||||
global.addEventListener('message', ({ data }) => {
|
||||
worker.dispatchHandlers(data, postMessage);
|
||||
});
|
||||
|
||||
worker.setAdapter({
|
||||
getCore,
|
||||
});
|
@ -1,96 +0,0 @@
|
||||
require('regenerator-runtime/runtime');
|
||||
const defaultArgs = require('./constants/defaultArgs');
|
||||
const strList2ptr = require('./utils/strList2ptr');
|
||||
const getTransferables = require('../utils/getTransferables');
|
||||
|
||||
const NO_LOAD_ERROR = 'FFmpegCore is not ready, make sure you have completed Worker.load().';
|
||||
|
||||
let action = 'unknown';
|
||||
let Module = null;
|
||||
let adapter = null;
|
||||
let ffmpeg = null;
|
||||
|
||||
const load = ({ workerId, payload: { options: { corePath } } }, res) => {
|
||||
if (Module === null) {
|
||||
const Core = adapter.getCore(corePath);
|
||||
Core()
|
||||
.then(async (_Module) => {
|
||||
Module = _Module;
|
||||
Module.setLogger((message, type) => {
|
||||
res.progress({
|
||||
workerId, action, type, message,
|
||||
});
|
||||
});
|
||||
ffmpeg = Module.cwrap('ffmpeg', 'number', ['number', 'number']);
|
||||
res.resolve({ message: 'Loaded ffmpeg-core' });
|
||||
});
|
||||
} else {
|
||||
res.resolve({ message: 'Loaded ffmpeg-core' });
|
||||
}
|
||||
};
|
||||
|
||||
const FS = ({
|
||||
payload: {
|
||||
method,
|
||||
args,
|
||||
},
|
||||
}, res) => {
|
||||
if (Module === null) {
|
||||
throw NO_LOAD_ERROR;
|
||||
} else {
|
||||
res.resolve({
|
||||
message: `Complete ${method}`,
|
||||
data: Module.FS[method](...args),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const run = ({
|
||||
payload: {
|
||||
args: _args,
|
||||
},
|
||||
}, res) => {
|
||||
if (Module === null) {
|
||||
throw NO_LOAD_ERROR;
|
||||
} else {
|
||||
const args = [...defaultArgs, ..._args.trim().split(' ')].filter((s) => s.length !== 0);
|
||||
ffmpeg(args.length, strList2ptr(Module, args));
|
||||
res.resolve({
|
||||
message: `Complete ${args.join(' ')}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.dispatchHandlers = (packet, send) => {
|
||||
const { workerId, jobId, action: act } = packet;
|
||||
const res = (status, payload) => {
|
||||
const pkt = {
|
||||
workerId,
|
||||
jobId,
|
||||
action: act,
|
||||
status,
|
||||
payload,
|
||||
};
|
||||
send(pkt, getTransferables(pkt));
|
||||
};
|
||||
res.resolve = res.bind(this, 'resolve');
|
||||
res.reject = res.bind(this, 'reject');
|
||||
res.progress = res.bind(this, 'progress');
|
||||
|
||||
action = act;
|
||||
try {
|
||||
({
|
||||
load,
|
||||
FS,
|
||||
run,
|
||||
})[act](packet, res);
|
||||
} catch (err) {
|
||||
/** Prepare exception to travel through postMessage */
|
||||
res.reject(err.toString());
|
||||
}
|
||||
action = 'unknown';
|
||||
};
|
||||
|
||||
exports.setAdapter = (_adapter) => {
|
||||
adapter = _adapter;
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
let FFmpegCore = null;
|
||||
|
||||
module.exports = () => {
|
||||
if (FFmpegCore === null) {
|
||||
FFmpegCore = require('@ffmpeg/core');
|
||||
}
|
||||
return FFmpegCore;
|
||||
};
|
@ -1,16 +0,0 @@
|
||||
const { parentPort } = require('worker_threads');
|
||||
const worker = require('..');
|
||||
const getCore = require('./getCore');
|
||||
|
||||
parentPort.on('message', (packet) => {
|
||||
worker.dispatchHandlers(
|
||||
packet,
|
||||
(...args) => {
|
||||
parentPort.postMessage(...args);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
worker.setAdapter({
|
||||
getCore,
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
const resolveURL = require('resolve-url');
|
||||
const { version, dependencies } = require('../../../package.json');
|
||||
const defaultOptions = require('../../constants/defaultOptions');
|
||||
|
||||
/*
|
||||
* Default options for browser worker
|
||||
*/
|
||||
module.exports = {
|
||||
...defaultOptions,
|
||||
workerPath: (typeof process !== 'undefined' && process.env.FFMPEG_ENV === 'development')
|
||||
? resolveURL(`/dist/worker.dev.js?nocache=${Math.random().toString(36).slice(3)}`)
|
||||
: `https://unpkg.com/@ffmpeg/ffmpeg@v${version}/dist/worker.min.js`,
|
||||
corePath: `https://unpkg.com/@ffmpeg/core@v${dependencies['@ffmpeg/core'].substring(1)}/ffmpeg-core.js`,
|
||||
workerBlobURL: true,
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Tesseract Worker adapter for browser
|
||||
*
|
||||
* @fileoverview Tesseract Worker adapter for browser
|
||||
* @author Kevin Kwok <antimatter15@gmail.com>
|
||||
* @author Guillermo Webster <gui@mit.edu>
|
||||
* @author Jerome Wu <jeromewus@gmail.com>
|
||||
*/
|
||||
const defaultOptions = require('./defaultOptions');
|
||||
const spawnWorker = require('./spawnWorker');
|
||||
const onMessage = require('./onMessage');
|
||||
const fetchFile = require('./fetchFile');
|
||||
|
||||
module.exports = {
|
||||
defaultOptions,
|
||||
spawnWorker,
|
||||
onMessage,
|
||||
fetchFile,
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
module.exports = (worker, handler) => {
|
||||
worker.onmessage = ({ data }) => { // eslint-disable-line
|
||||
handler(data);
|
||||
};
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
/**
|
||||
* spawnWorker
|
||||
*
|
||||
* @name spawnWorker
|
||||
* @function create a new Worker in browser
|
||||
* @access public
|
||||
*/
|
||||
module.exports = ({ workerPath, workerBlobURL }) => {
|
||||
let worker;
|
||||
if (Blob && URL && workerBlobURL) {
|
||||
/* Use Blob to load cross domain worker script */
|
||||
const blob = new Blob([`importScripts("${workerPath}");`], {
|
||||
type: 'text/javascript',
|
||||
});
|
||||
worker = new Worker(URL.createObjectURL(blob));
|
||||
} else {
|
||||
worker = new Worker(workerPath);
|
||||
}
|
||||
return worker;
|
||||
};
|
@ -1,10 +0,0 @@
|
||||
const path = require('path');
|
||||
const defaultOptions = require('../../constants/defaultOptions');
|
||||
|
||||
/*
|
||||
* Default options for node worker
|
||||
*/
|
||||
module.exports = {
|
||||
...defaultOptions,
|
||||
workerPath: path.join(__dirname, '..', '..', 'worker-script', 'node', 'index.js'),
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
module.exports = (worker, handler) => {
|
||||
worker.on('message', handler);
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
const { Worker } = require('worker_threads');
|
||||
|
||||
/**
|
||||
* spawnWorker
|
||||
*
|
||||
* @name spawnWorker
|
||||
* @function fork a new worker thread in node
|
||||
* @access public
|
||||
*/
|
||||
module.exports = ({ workerPath }) => (
|
||||
new Worker(workerPath)
|
||||
);
|
BIN
tests/assets/StarWars3.wav
Normal file
BIN
tests/assets/StarWars3.wav
Normal file
Binary file not shown.
@ -1,11 +1,9 @@
|
||||
const TIMEOUT = 30000;
|
||||
const TIMEOUT = 60000;
|
||||
const BASE_URL = 'http://localhost:3000/tests/assets';
|
||||
const IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
||||
const OPTIONS = {
|
||||
corePath: '../node_modules/@ffmpeg/core/ffmpeg-core.js',
|
||||
...(IS_BROWSER ? { workerPath: '../dist/worker.dev.js' } : {}),
|
||||
};
|
||||
const FLAME_MP4_LENGTH = 100374;
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = {
|
||||
@ -13,6 +11,5 @@ if (typeof module !== 'undefined') {
|
||||
BASE_URL,
|
||||
IS_BROWSER,
|
||||
OPTIONS,
|
||||
FLAME_MP4_LENGTH,
|
||||
};
|
||||
}
|
||||
|
@ -1,20 +1,27 @@
|
||||
const { createWorker } = FFmpeg;
|
||||
const worker = createWorker(OPTIONS);
|
||||
const { createFFmpeg } = FFmpeg;
|
||||
const ffmpeg = createFFmpeg(OPTIONS);
|
||||
|
||||
before(async function cb() {
|
||||
this.timeout(0);
|
||||
await worker.load();
|
||||
await ffmpeg.load();
|
||||
});
|
||||
|
||||
describe('transcode()', () => {
|
||||
describe('should transcode different format', () => {
|
||||
['flame.avi'].forEach((name) => (
|
||||
it(`transcode ${name}`, async () => {
|
||||
await worker.write(name, `${BASE_URL}/${name}`);
|
||||
await worker.transcode(name, 'output.mp4');
|
||||
const { data } = await worker.read('output.mp4');
|
||||
expect(data.length).to.be(FLAME_MP4_LENGTH);
|
||||
}).timeout(TIMEOUT)
|
||||
));
|
||||
[1, 2, 4].forEach((n) => {
|
||||
[
|
||||
{ from: 'flame.avi', to: 'flame.mp4' },
|
||||
{ from: 'flame.avi', to: 'flame.webm' },
|
||||
{ from: 'StarWars3.wav', to: 'StarWars3.mp3' },
|
||||
].forEach(({ from, to }) => (
|
||||
it(`transcode ${from} to ${to} (${n} threads)`, async () => {
|
||||
await ffmpeg.write(from, `${BASE_URL}/${from}`);
|
||||
await ffmpeg.transcode(from, to, `-threads ${n}`);
|
||||
const data = ffmpeg.read(to);
|
||||
ffmpeg.remove(to);
|
||||
expect(data.length).not.to.be(0);
|
||||
}).timeout(TIMEOUT)
|
||||
));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user