/** * Constants */ const NULL = 0; const SIZE_I32 = Uint32Array.BYTES_PER_ELEMENT; const DEFAULT_ARGS = ["./ffmpeg", "-nostdin", "-y"]; const DEFAULT_ARGS_FFPROBE = ["./ffprobe"]; Module["NULL"] = NULL; Module["SIZE_I32"] = SIZE_I32; Module["DEFAULT_ARGS"] = DEFAULT_ARGS; Module["DEFAULT_ARGS_FFPROBE"] = DEFAULT_ARGS_FFPROBE; /** * Variables */ Module["ret"] = -1; Module["timeout"] = -1; Module["logger"] = () => {}; Module["progress"] = () => {}; /** * Functions */ function stringToPtr(str) { const len = Module["lengthBytesUTF8"](str) + 1; const ptr = Module["_malloc"](len); Module["stringToUTF8"](str, ptr, len); return ptr; } function stringsToPtr(strs) { const len = strs.length; const ptr = Module["_malloc"](len * SIZE_I32); for (let i = 0; i < len; i++) { Module["setValue"](ptr + SIZE_I32 * i, stringToPtr(strs[i]), "i32"); } return ptr; } function print(message) { Module["logger"]({ type: "stdout", message }); } function printErr(message) { if (!message.startsWith("Aborted(native code called abort())")) Module["logger"]({ type: "stderr", message }); } function exec(..._args) { const args = [...Module["DEFAULT_ARGS"], ..._args]; try { Module["_ffmpeg"](args.length, stringsToPtr(args)); } catch (e) { if (!e.message.startsWith("Aborted")) { throw e; } } return Module["ret"]; } function ffprobe(..._args) { const args = [...Module["DEFAULT_ARGS_FFPROBE"], ..._args]; try { Module["_ffprobe"](args.length, stringsToPtr(args)); } catch (e) { if (!e.message.startsWith("Aborted")) { throw e; } } return Module["ret"]; } function setLogger(logger) { Module["logger"] = logger; } function setTimeout(timeout) { Module["timeout"] = timeout; } function setProgress(handler) { Module["progress"] = handler; } function receiveProgress(progress, time) { Module["progress"]({ progress, time }); } function reset() { Module["ret"] = -1; Module["timeout"] = -1; } /** * In multithread version of ffmpeg.wasm, the bootstrap process is like: * 1. Execute ffmpeg-core.js * 2. ffmpeg-core.js spawns workers by calling `new Worker("ffmpeg-core.worker.js")` * 3. ffmpeg-core.worker.js imports ffmpeg-core.js * 4. ffmpeg-core.js imports ffmpeg-core.wasm * * It is a straightforward process when all files are in the same location. * But when files are in different location (or Blob URL), #4 fails because * there is no way to pass custom ffmpeg-core.wasm URL to ffmpeg-core.worker.js * when it imports ffmpeg-core.js in #3. * * To fix this issue, a hack here is leveraging mainScriptUrlOrBlob variable by * adding wasmURL and workerURL in base64 format as query string. ex: * * http://example.com/ffmpeg-core.js#{btoa(JSON.stringify({"wasmURL": "...", "workerURL": "..."}))} * * Thus, we can successfully extract custom URLs using _locateFile funciton. */ function _locateFile(path, prefix) { const mainScriptUrlOrBlob = Module["mainScriptUrlOrBlob"]; if (mainScriptUrlOrBlob) { const { wasmURL, workerURL } = JSON.parse( atob(mainScriptUrlOrBlob.slice(mainScriptUrlOrBlob.lastIndexOf("#") + 1)) ); if (path.endsWith(".wasm")) return wasmURL; if (path.endsWith(".worker.js")) return workerURL; } return prefix + path; } Module["stringToPtr"] = stringToPtr; Module["stringsToPtr"] = stringsToPtr; Module["print"] = print; Module["printErr"] = printErr; Module["locateFile"] = _locateFile; Module["exec"] = exec; Module["ffprobe"] = ffprobe; Module["setLogger"] = setLogger; Module["setTimeout"] = setTimeout; Module["setProgress"] = setProgress; Module["reset"] = reset; Module["receiveProgress"] = receiveProgress;