diff --git a/extension/bg/js/ffmpeg.js b/extension/bg/js/ffmpeg.js index d30b13a..6ae16fe 100644 --- a/extension/bg/js/ffmpeg.js +++ b/extension/bg/js/ffmpeg.js @@ -1,128 +1,128 @@ -class FFmpeg { - constructor() { - this.ffmpegCore = null; - this.ffmpegMain = null; - this.videoFile = null; - this.running = false; - this.runResolve = null; - this.fileCount = 0; - this.defaultArgs = [ - /* args[0] is always the binary path */ - './ffmpeg', - /* Disable interaction mode */ - '-nostdin', - /* Force to override output file */ - '-y', - ]; - } - - detectCompletion(message) { - if (message === 'FFMPEG_END' && this.runResolve !== null) { - this.runResolve(); - this.runResolve = null; - this.running = false; - } - } - - log(message) { - console.log(message); - this.detectCompletion(message); - } - - async createCore() { - try { - return await createFFmpegCore({ - mainScriptUrlOrBlob: '/bg/js/ffmpeg/ffmpeg-core.js', - printErr: message => this.log(message), - print: message => this.log(message), - locateFile: (path, prefix) => { - if (path.endsWith('ffmpeg-core.wasm')) { - return '/bg/js/ffmpeg/ffmpeg-core.wasm'; - } - if (path.endsWith('ffmpeg-core.worker.js')) { - return '/bg/js/ffmpeg/ffmpeg-core.worker.js'; - } - return prefix + path; - } - }); - } catch (e) { - if (e.message === 'bad memory') { - throw new UserFacingError(`ffmpeg didn't start. #enable-webassembly-threads may not be enabled in chrome://flags. Open chrome console for more info.`) - } - throw new Error(`ffmpeg didn't start: ` + e.message); - } - } - - async load() { - if (this.ffmpegCore) - return 'ffmpeg already loaded'; - - this.ffmpegCore = await this.createCore(); - this.ffmpegMain = this.ffmpegCore.cwrap('proxy_main', 'number', ['number', 'number']); - return 'Loaded ffmpeg'; - } - - updateFile(newFile) { - this.videoFile = newFile; - const FS = this.ffmpegCore.FS; - const rootDirs = FS.readdir('/'); - if (rootDirs.indexOf('input') === -1) { - FS.mkdir('/input'); - } - else { - this.ffmpegCore.FS_unmount('/input'); - } - if (rootDirs.indexOf('output') === -1) { - FS.mkdir('/output'); - } - - const WORKERFS = this.ffmpegCore.FS_filesystems.WORKERFS; - const tmpfile = new File( [ newFile ], 'tmpfile', { type: newFile.type } ); - this.ffmpegCore.FS_mount(WORKERFS, { files: [ tmpfile ]}, '/input'); - } - - parseArgs(args) { - const argsPtr = this.ffmpegCore._malloc(args.length * Uint32Array.BYTES_PER_ELEMENT); - args.forEach((s, idx) => { - const buf = this.ffmpegCore._malloc(s.length + 1); - this.ffmpegCore.writeAsciiToMemory(s, buf); - this.ffmpegCore.setValue(argsPtr + (Uint32Array.BYTES_PER_ELEMENT * idx), buf, 'i32'); - }); - return [args.length, argsPtr]; - } - - FS(method, ...args) { - if (!this.ffmpegCore) - throw new Error("Failed to run command. ffmpeg isn't loaded yet"); - var ret = null; - try { - ret = this.ffmpegCore.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; - } - - async run(..._args) { - if (!this.ffmpegCore) - throw new Error("Failed to run command. ffmpeg isn't loaded yet"); - else if (this.running) { - throw new Error("ffmpeg.wasm can only run one command at a time"); - } - else { - this.running = true; - return new Promise((resolve) => { - const args = [...this.defaultArgs, ..._args].filter((s) => s.length !== 0); - console.log(`Run: ${args.join(' ')}`) - this.runResolve = resolve; - this.ffmpegMain(...this.parseArgs(args)); - }); - } - } +class FFmpeg { + constructor() { + this.ffmpegCore = null; + this.ffmpegMain = null; + this.videoFile = null; + this.running = false; + this.runResolve = null; + this.fileCount = 0; + this.defaultArgs = [ + /* args[0] is always the binary path */ + './ffmpeg', + /* Disable interaction mode */ + '-nostdin', + /* Force to override output file */ + '-y', + ]; + } + + detectCompletion(message) { + if (message === 'FFMPEG_END' && this.runResolve !== null) { + this.runResolve(); + this.runResolve = null; + this.running = false; + } + } + + log(message) { + console.log(message); + this.detectCompletion(message); + } + + async createCore() { + try { + return await createFFmpegCore({ + mainScriptUrlOrBlob: '/bg/js/ffmpeg/ffmpeg-core.js', + printErr: message => this.log(message), + print: message => this.log(message), + locateFile: (path, prefix) => { + if (path.endsWith('ffmpeg-core.wasm')) { + return '/bg/js/ffmpeg/ffmpeg-core.wasm'; + } + if (path.endsWith('ffmpeg-core.worker.js')) { + return '/bg/js/ffmpeg/ffmpeg-core.worker.js'; + } + return prefix + path; + } + }); + } catch (e) { + if (e.message === 'bad memory') { + throw new UserFacingError(`ffmpeg didn't start. #enable-webassembly-threads may not be enabled in chrome://flags. Open chrome console for more info.`) + } + throw new Error(`ffmpeg didn't start: ` + e.message); + } + } + + async load() { + if (this.ffmpegCore) + return 'ffmpeg already loaded'; + + this.ffmpegCore = await this.createCore(); + this.ffmpegMain = this.ffmpegCore.cwrap('proxy_main', 'number', ['number', 'number']); + return 'Loaded ffmpeg'; + } + + updateFile(newFile) { + this.videoFile = newFile; + const FS = this.ffmpegCore.FS; + const rootDirs = FS.readdir('/'); + if (rootDirs.indexOf('input') === -1) { + FS.mkdir('/input'); + } + else { + this.ffmpegCore.FS_unmount('/input'); + } + if (rootDirs.indexOf('output') === -1) { + FS.mkdir('/output'); + } + + const WORKERFS = this.ffmpegCore.FS_filesystems.WORKERFS; + const tmpfile = new File( [ newFile ], 'tmpfile', { type: newFile.type } ); + this.ffmpegCore.FS_mount(WORKERFS, { files: [ tmpfile ]}, '/input'); + } + + parseArgs(args) { + const argsPtr = this.ffmpegCore._malloc(args.length * Uint32Array.BYTES_PER_ELEMENT); + args.forEach((s, idx) => { + const buf = this.ffmpegCore._malloc(s.length + 1); + this.ffmpegCore.writeAsciiToMemory(s, buf); + this.ffmpegCore.setValue(argsPtr + (Uint32Array.BYTES_PER_ELEMENT * idx), buf, 'i32'); + }); + return [args.length, argsPtr]; + } + + FS(method, ...args) { + if (!this.ffmpegCore) + throw new Error("Failed to run command. ffmpeg isn't loaded yet"); + var ret = null; + try { + ret = this.ffmpegCore.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; + } + + async run(..._args) { + if (!this.ffmpegCore) + throw new Error("Failed to run command. ffmpeg isn't loaded yet"); + else if (this.running) { + throw new Error("ffmpeg.wasm can only run one command at a time"); + } + else { + this.running = true; + return new Promise((resolve) => { + const args = [...this.defaultArgs, ..._args].filter((s) => s.length !== 0); + console.log(`Run: ${args.join(' ')}`) + this.runResolve = resolve; + this.ffmpegMain(...this.parseArgs(args)); + }); + } + } } \ No newline at end of file diff --git a/extension/bg/js/ffmpeg_worker.js b/extension/bg/js/ffmpeg_worker.js index c080636..6398393 100644 --- a/extension/bg/js/ffmpeg_worker.js +++ b/extension/bg/js/ffmpeg_worker.js @@ -1,39 +1,39 @@ -self.importScripts( - 'ffmpeg/ffmpeg-core.js', - 'ffmpeg.js', - 'time_formatter.js', - 'errors.js', - 'blob_utils.js', - 'ffmpeg_commands.js', - 'ffmpeg_client.js' -); - -var ffmpeg = new FFmpeg(); - -handleMessage = async (message) => { - if (crossOriginIsolated) - console.log('Can use SharedArrayBuffer from ffmpeg_worker') - const ffmpegClient = new FFmpegClient(ffmpeg, message.settings); - switch (message.type) { - case 'load': - return await ffmpeg.load(); - case 'updateFile': - ffmpeg.updateFile(message.file); - return 'Updated file'; - case 'getAudioData': - return await ffmpegClient.getAudioData(message.start, message.end, message.audioTrack); - case 'getImage': - return await ffmpegClient.getImage(message.start, message.end, message.time); - case 'cleanAudio': - return await ffmpegClient.cleanAudio(message.audioData); - } -} - -onmessage = function (e) { - const message = e.data; - handleMessage(message).then(result => { - self.postMessage({ id: message.id, payload: result}); - }).catch(error => { - self.postMessage({ id: message.id, err: { message: error.message, stack: error.stack, name: error.name } }); - }) -} +self.importScripts( + 'ffmpeg/ffmpeg-core.js', + 'ffmpeg.js', + 'time_formatter.js', + 'errors.js', + 'blob_utils.js', + 'ffmpeg_commands.js', + 'ffmpeg_client.js' +); + +var ffmpeg = new FFmpeg(); + +handleMessage = async (message) => { + if (crossOriginIsolated) + console.log('Can use SharedArrayBuffer from ffmpeg_worker') + const ffmpegClient = new FFmpegClient(ffmpeg, message.settings); + switch (message.type) { + case 'load': + return await ffmpeg.load(); + case 'updateFile': + ffmpeg.updateFile(message.file); + return 'Updated file'; + case 'getAudioData': + return await ffmpegClient.getAudioData(message.start, message.end, message.audioTrack); + case 'getImage': + return await ffmpegClient.getImage(message.start, message.end, message.time); + case 'cleanAudio': + return await ffmpegClient.cleanAudio(message.audioData); + } +} + +onmessage = function (e) { + const message = e.data; + handleMessage(message).then(result => { + self.postMessage({ id: message.id, payload: result}); + }).catch(error => { + self.postMessage({ id: message.id, err: { message: error.message, stack: error.stack, name: error.name } }); + }) +} diff --git a/extension/bg/js/promise_worker.js b/extension/bg/js/promise_worker.js index 026792f..1cde5e0 100644 --- a/extension/bg/js/promise_worker.js +++ b/extension/bg/js/promise_worker.js @@ -1,54 +1,54 @@ -let ab_resolves = {} -let ab_rejects = {} -let ab_globalMessageId = 0 - -class PromiseWorker { - constructor(filename) { - this.worker = new Worker(filename); - this.worker.onmessage = this.handleMsg; - } - - sendMessage(message) { - return this.sendMsg(message, this.worker); - } - - // Activate calculation in the worker, returning a promise - sendMsg(payload, worker){ - const msgId = ab_globalMessageId++ - const msg = { - id: msgId, - ...payload - } - return new Promise((resolve, reject) => { - // save callbacks for later - ab_resolves[msgId] = resolve - ab_rejects[msgId] = reject - worker.postMessage(msg) - }) - } - - // Handle incoming calculation result - handleMsg(msg) { - const {id, err, payload} = msg.data - if (payload) { - const resolve = ab_resolves[id] - if (resolve) { - resolve(payload) - } - } else { - // error condition - const reject = ab_rejects[id] - if (reject) { - if (err) { - reject(err) - } else { - reject('Got nothing') - } - } - } - - // purge used callbacks - delete ab_resolves[id] - delete ab_rejects[id] - } +let ab_resolves = {} +let ab_rejects = {} +let ab_globalMessageId = 0 + +class PromiseWorker { + constructor(filename) { + this.worker = new Worker(filename); + this.worker.onmessage = this.handleMsg; + } + + sendMessage(message) { + return this.sendMsg(message, this.worker); + } + + // Activate calculation in the worker, returning a promise + sendMsg(payload, worker){ + const msgId = ab_globalMessageId++ + const msg = { + id: msgId, + ...payload + } + return new Promise((resolve, reject) => { + // save callbacks for later + ab_resolves[msgId] = resolve + ab_rejects[msgId] = reject + worker.postMessage(msg) + }) + } + + // Handle incoming calculation result + handleMsg(msg) { + const {id, err, payload} = msg.data + if (payload) { + const resolve = ab_resolves[id] + if (resolve) { + resolve(payload) + } + } else { + // error condition + const reject = ab_rejects[id] + if (reject) { + if (err) { + reject(err) + } else { + reject('Got nothing') + } + } + } + + // purge used callbacks + delete ab_resolves[id] + delete ab_rejects[id] + } } \ No newline at end of file