Complete basic rewrite
This commit is contained in:
parent
2f18a2d806
commit
a136b8b1bb
@ -37,11 +37,11 @@ RUN bash -x /src/build.sh
|
|||||||
|
|
||||||
# Build ffmpeg.wasm
|
# Build ffmpeg.wasm
|
||||||
FROM ffmpeg-builder AS ffmpeg-wasm-builder
|
FROM ffmpeg-builder AS ffmpeg-wasm-builder
|
||||||
COPY src/bind /src/wasm/bind
|
COPY src/bind /src/src/bind
|
||||||
COPY src/fftools /src/wasm/fftools
|
COPY src/fftools /src/src/fftools
|
||||||
COPY build/ffmpeg-wasm.sh build.sh
|
COPY build/ffmpeg-wasm.sh build.sh
|
||||||
RUN mkdir -p /src/dist/umd && bash -x /src/build.sh -o dist/umd/ffmpeg.js
|
RUN mkdir -p /src/dist/umd && bash -x /src/build.sh -o dist/umd/ffmpeg-core.js
|
||||||
RUN mkdir -p /src/dist/esm && bash -x /src/build.sh -sEXPORT_ES6 -o dist/esm/ffmpeg.js
|
RUN mkdir -p /src/dist/esm && bash -x /src/build.sh -sEXPORT_ES6 -o dist/esm/ffmpeg-core.js
|
||||||
|
|
||||||
# Export ffmpeg-core.wasm to dist/, use `docker buildx build -o . .` to get assets
|
# Export ffmpeg-core.wasm to dist/, use `docker buildx build -o . .` to get assets
|
||||||
FROM scratch AS exportor
|
FROM scratch AS exportor
|
||||||
|
11
Makefile
11
Makefile
@ -9,13 +9,13 @@ PROD_CFLAGS := -O3 -msimd128
|
|||||||
PROD_MT_CFLAGS := $(PROD_CFLAGS) $(MT_FLAGS)
|
PROD_MT_CFLAGS := $(PROD_CFLAGS) $(MT_FLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf ./packages/ffmpeg$(PKG_SUFFIX)/dist
|
rm -rf ./packages/core$(PKG_SUFFIX)/dist
|
||||||
rm -rf ./packages/ffmpeg$(PKG_SUFFIX)/types
|
rm -rf ./packages/core$(PKG_SUFFIX)/types
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build:
|
build:
|
||||||
make clean PKG_SUFFIX="$(PKG_SUFFIX)"
|
make clean PKG_SUFFIX="$(PKG_SUFFIX)"
|
||||||
cp -r src/types/ffmpeg packages/ffmpeg$(PKG_SUFFIX)/types
|
cp -r src/types/ffmpeg-core packages/core$(PKG_SUFFIX)/types
|
||||||
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
|
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
|
||||||
EXTRA_LDFLAGS="$(EXTRA_LDFLAGS)" \
|
EXTRA_LDFLAGS="$(EXTRA_LDFLAGS)" \
|
||||||
FFMPEG_ST="$(FFMPEG_ST)" \
|
FFMPEG_ST="$(FFMPEG_ST)" \
|
||||||
@ -25,7 +25,7 @@ build:
|
|||||||
--build-arg EXTRA_LDFLAGS \
|
--build-arg EXTRA_LDFLAGS \
|
||||||
--build-arg FFMPEG_MT \
|
--build-arg FFMPEG_MT \
|
||||||
--build-arg FFMPEG_ST \
|
--build-arg FFMPEG_ST \
|
||||||
-o ./packages/ffmpeg$(PKG_SUFFIX) \
|
-o ./packages/core$(PKG_SUFFIX) \
|
||||||
$(EXTRA_ARGS) \
|
$(EXTRA_ARGS) \
|
||||||
.
|
.
|
||||||
|
|
||||||
@ -49,3 +49,6 @@ prd:
|
|||||||
|
|
||||||
prd-mt:
|
prd-mt:
|
||||||
make build-mt EXTRA_CFLAGS="$(PROD_MT_CFLAGS)"
|
make build-mt EXTRA_CFLAGS="$(PROD_MT_CFLAGS)"
|
||||||
|
|
||||||
|
test:
|
||||||
|
npm run test
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "browser example",
|
"description": "browser example",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npx http-server ../../"
|
"start": "npx http-server -c-1 ../../"
|
||||||
},
|
},
|
||||||
"author": "Jerome Wu <jeromewus@gmail.com>",
|
"author": "Jerome Wu <jeromewus@gmail.com>",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
@ -11,20 +11,32 @@
|
|||||||
<p id="message"></p>
|
<p id="message"></p>
|
||||||
<script>
|
<script>
|
||||||
const { fetchFile } = FFmpegUtil;
|
const { fetchFile } = FFmpegUtil;
|
||||||
|
const { FFmpeg } = FFmpegWASM;
|
||||||
let ffmpeg = null;
|
let ffmpeg = null;
|
||||||
|
|
||||||
const transcode = async ({ target: { files } }) => {
|
const transcode = async ({ target: { files } }) => {
|
||||||
const message = document.getElementById('message');
|
const message = document.getElementById('message');
|
||||||
if (ffmpeg === null) {
|
if (ffmpeg === null) {
|
||||||
ffmpeg = await createFFmpeg();
|
ffmpeg = new FFmpeg();
|
||||||
ffmpeg.setProgress((progress) => { console.log(progress * 100); });
|
ffmpeg.on(FFmpeg.DOWNLOAD, ({ url, total, received, done }) => {
|
||||||
|
console.log(`downloading ${url}, progress: ${received / total * 100} %, done: ${done}`);
|
||||||
|
});
|
||||||
|
ffmpeg.on(FFmpeg.PROGRESS, (p) => {
|
||||||
|
message.innerHTML = `${p * 100} %`;
|
||||||
|
});
|
||||||
|
ffmpeg.on(FFmpeg.LOG, ({ message }) => {
|
||||||
|
console.log(message);
|
||||||
|
});
|
||||||
|
await ffmpeg.load({
|
||||||
|
coreURL: "/packages/core/dist/umd/ffmpeg-core.js",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const { name } = files[0];
|
const { name } = files[0];
|
||||||
ffmpeg.FS.writeFile(name, await fetchFile(files[0]));
|
await ffmpeg.writeFile(name, await fetchFile(files[0]));
|
||||||
message.innerHTML = 'Start transcoding';
|
message.innerHTML = 'Start transcoding';
|
||||||
await ffmpeg.exec('-i', name, 'output.mp4');
|
await ffmpeg.exec(['-i', name, 'output.mp4']);
|
||||||
message.innerHTML = 'Complete transcoding';
|
message.innerHTML = 'Complete transcoding';
|
||||||
const data = ffmpeg.FS.readFile('output.mp4');
|
const data = await ffmpeg.readFile('output.mp4');
|
||||||
|
|
||||||
const video = document.getElementById('output-video');
|
const video = document.getElementById('output-video');
|
||||||
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
|
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
# ex:
|
# ex:
|
||||||
# bash ffmpeg-wasm.sh -o ffmpeg.js
|
# bash ffmpeg-wasm.sh -o ffmpeg.js
|
||||||
|
|
||||||
EXPORT_NAME="createFFmpeg"
|
EXPORT_NAME="createFFmpegCore"
|
||||||
|
|
||||||
CONF_FLAGS=(
|
CONF_FLAGS=(
|
||||||
-I.
|
-I.
|
||||||
-I./wasm/fftools
|
-I./src/fftools
|
||||||
-I$INSTALL_DIR/include
|
-I$INSTALL_DIR/include
|
||||||
-L$INSTALL_DIR/lib
|
-L$INSTALL_DIR/lib
|
||||||
-Llibavcodec
|
-Llibavcodec
|
||||||
@ -35,17 +35,17 @@ CONF_FLAGS=(
|
|||||||
${FFMPEG_MT:+ -sPTHREAD_POOL_SIZE=32} # use 32 threads
|
${FFMPEG_MT:+ -sPTHREAD_POOL_SIZE=32} # use 32 threads
|
||||||
${FFMPEG_ST:+ -sINITIAL_MEMORY=32MB -sALLOW_MEMORY_GROWTH} # Use just enough memory as memory usage can grow
|
${FFMPEG_ST:+ -sINITIAL_MEMORY=32MB -sALLOW_MEMORY_GROWTH} # Use just enough memory as memory usage can grow
|
||||||
-sEXPORT_NAME="$EXPORT_NAME" # required in browser env, so that user can access this module from window.createFFmpeg
|
-sEXPORT_NAME="$EXPORT_NAME" # required in browser env, so that user can access this module from window.createFFmpeg
|
||||||
-sEXPORTED_FUNCTIONS=$(node wasm/bind/ffmpeg/export.js) # exported functions
|
-sEXPORTED_FUNCTIONS=$(node src/bind/ffmpeg/export.js) # exported functions
|
||||||
-sEXPORTED_RUNTIME_METHODS=$(node wasm/bind/ffmpeg/export-runtime.js) # exported built-in functions
|
-sEXPORTED_RUNTIME_METHODS=$(node src/bind/ffmpeg/export-runtime.js) # exported built-in functions
|
||||||
--pre-js wasm/bind/ffmpeg/bind.js # extra bindings, contains most of the ffmpeg.wasm javascript code
|
--pre-js src/bind/ffmpeg/bind.js # extra bindings, contains most of the ffmpeg.wasm javascript code
|
||||||
# ffmpeg source code
|
# ffmpeg source code
|
||||||
wasm/fftools/cmdutils.c
|
src/fftools/cmdutils.c
|
||||||
wasm/fftools/ffmpeg.c
|
src/fftools/ffmpeg.c
|
||||||
wasm/fftools/ffmpeg_filter.c
|
src/fftools/ffmpeg_filter.c
|
||||||
wasm/fftools/ffmpeg_hw.c
|
src/fftools/ffmpeg_hw.c
|
||||||
wasm/fftools/ffmpeg_mux.c
|
src/fftools/ffmpeg_mux.c
|
||||||
wasm/fftools/ffmpeg_opt.c
|
src/fftools/ffmpeg_opt.c
|
||||||
wasm/fftools/opt_common.c
|
src/fftools/opt_common.c
|
||||||
)
|
)
|
||||||
|
|
||||||
emcc "${CONF_FLAGS[@]}" $@
|
emcc "${CONF_FLAGS[@]}" $@
|
||||||
|
11372
package-lock.json
generated
11372
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
2
packages/core-mt/.gitignore
vendored
Normal file
2
packages/core-mt/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
dist/
|
||||||
|
types/
|
50
packages/core-mt/package.json
Normal file
50
packages/core-mt/package.json
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "@ffmpeg/core-mt",
|
||||||
|
"version": "0.11.5",
|
||||||
|
"description": "FFmpeg WebAssembly version",
|
||||||
|
"main": "./dist/umd/ffmpeg-core.js",
|
||||||
|
"types": "./types/ffmpeg-core.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./types/ffmpeg-core.d.ts",
|
||||||
|
"import": "./dist/esm/ffmpeg-core.js",
|
||||||
|
"require": "./dist/umd/ffmpeg-core.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint types"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"types/ffmpeg-core.d.ts"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/ffmpegwasm/ffmpeg.wasm.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"ffmpeg",
|
||||||
|
"WebAssembly",
|
||||||
|
"video",
|
||||||
|
"audio",
|
||||||
|
"transcode"
|
||||||
|
],
|
||||||
|
"author": "Jerome Wu <jeromewus@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/ffmpegwasm/ffmpeg.wasm/issues"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.6.0"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/ffmpegwasm/ffmpeg.wasm#readme",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.37.0",
|
||||||
|
"@typescript-eslint/parser": "^5.37.0",
|
||||||
|
"eslint": "^8.23.1",
|
||||||
|
"typescript": "^4.8.3"
|
||||||
|
}
|
||||||
|
}
|
2
packages/core/.gitignore
vendored
Normal file
2
packages/core/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
dist/
|
||||||
|
types/
|
50
packages/core/package.json
Normal file
50
packages/core/package.json
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "@ffmpeg/core",
|
||||||
|
"version": "0.11.5",
|
||||||
|
"description": "FFmpeg WebAssembly version",
|
||||||
|
"main": "./dist/umd/ffmpeg-core.js",
|
||||||
|
"types": "./types/ffmpeg-core.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./types/ffmpeg-core.d.ts",
|
||||||
|
"import": "./dist/esm/ffmpeg-core.js",
|
||||||
|
"require": "./dist/umd/ffmpeg-core.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint types"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"types/ffmpeg-core.d.ts"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/ffmpegwasm/ffmpeg.wasm.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"ffmpeg",
|
||||||
|
"WebAssembly",
|
||||||
|
"video",
|
||||||
|
"audio",
|
||||||
|
"transcode"
|
||||||
|
],
|
||||||
|
"author": "Jerome Wu <jeromewus@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/ffmpegwasm/ffmpeg.wasm/issues"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.6.0"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/ffmpegwasm/ffmpeg.wasm#readme",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.37.0",
|
||||||
|
"@typescript-eslint/parser": "^5.37.0",
|
||||||
|
"eslint": "^8.23.1",
|
||||||
|
"typescript": "^4.8.3"
|
||||||
|
}
|
||||||
|
}
|
1
packages/ffmpeg/.eslintignore
Normal file
1
packages/ffmpeg/.eslintignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
.eslintrc.cjs
|
13
packages/ffmpeg/.eslintrc.cjs
Normal file
13
packages/ffmpeg/.eslintrc.cjs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||||
|
],
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ["./tsconfig.json", "./src/worker/tsconfig.json"],
|
||||||
|
},
|
||||||
|
plugins: ["@typescript-eslint"],
|
||||||
|
};
|
2
packages/ffmpeg/.gitignore
vendored
2
packages/ffmpeg/.gitignore
vendored
@ -1,2 +1,2 @@
|
|||||||
dist/
|
dist/
|
||||||
types/
|
docs/
|
||||||
|
@ -3,16 +3,23 @@
|
|||||||
"version": "0.11.5",
|
"version": "0.11.5",
|
||||||
"description": "FFmpeg WebAssembly version",
|
"description": "FFmpeg WebAssembly version",
|
||||||
"main": "./dist/umd/ffmpeg.js",
|
"main": "./dist/umd/ffmpeg.js",
|
||||||
"types": "./types/ffmpeg.d.ts",
|
"types": "./dist/umd/ffmpeg.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./types/ffmpeg.d.ts",
|
"types": "./dist/umd/ffmpeg.d.ts",
|
||||||
"import": "./dist/esm/ffmpeg.js",
|
"import": "./dist/esm/index.js",
|
||||||
"require": "./dist/umd/ffmpeg.js"
|
"require": "./dist/umd/ffmpeg.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint types"
|
"dev": "webpack --watch -c webpack.dev.config.js",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"clean": "rimraf dist",
|
||||||
|
"build:umd": "webpack",
|
||||||
|
"build:d": "tsc -p tsconfig.d.json",
|
||||||
|
"build:esm": "tsc -p tsconfig.esm.json",
|
||||||
|
"build": "npm-run-all clean build:*",
|
||||||
|
"docs": "typedoc --entryPointStrategy expand ./src"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
@ -45,6 +52,16 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^5.37.0",
|
"@typescript-eslint/eslint-plugin": "^5.37.0",
|
||||||
"@typescript-eslint/parser": "^5.37.0",
|
"@typescript-eslint/parser": "^5.37.0",
|
||||||
"eslint": "^8.23.1",
|
"eslint": "^8.23.1",
|
||||||
"typescript": "^4.8.3"
|
"npm-run-all": "^4.1.5",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"ts-loader": "^9.4.1",
|
||||||
|
"typedoc": "^0.23.15",
|
||||||
|
"typescript": "^4.8.3",
|
||||||
|
"webpack-cli": "^4.10.0",
|
||||||
|
"worker-loader": "^3.0.8"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ffmpeg/types": "^0.11.5",
|
||||||
|
"events": "^3.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
215
packages/ffmpeg/src/classes.ts
Normal file
215
packages/ffmpeg/src/classes.ts
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
import EventEmitter from "events";
|
||||||
|
import { FFMessageType } from "./const";
|
||||||
|
import {
|
||||||
|
CallbackData,
|
||||||
|
Callbacks,
|
||||||
|
DownloadProgressEvent,
|
||||||
|
FFMessageEventCallback,
|
||||||
|
FFMessageLoadConfig,
|
||||||
|
IsDone,
|
||||||
|
IsFirst,
|
||||||
|
LogEvent,
|
||||||
|
Message,
|
||||||
|
Progress,
|
||||||
|
} from "./types";
|
||||||
|
import { getMessageID } from "./utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides APIs to interact with ffmpeg web worker.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const ffmpeg = new FFmpeg();
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class FFmpeg extends EventEmitter {
|
||||||
|
/** @event */ static readonly DOWNLOAD = "download" as const;
|
||||||
|
/** @event */ static readonly LOG = "log" as const;
|
||||||
|
/** @event */ static readonly PROGRESS = "progress" as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to download progress events from `ffmpeg.load()`.
|
||||||
|
*
|
||||||
|
* @category Event
|
||||||
|
*/
|
||||||
|
on(
|
||||||
|
event: typeof FFmpeg.DOWNLOAD,
|
||||||
|
listener: (data: DownloadProgressEvent) => void
|
||||||
|
): this;
|
||||||
|
/**
|
||||||
|
* Listen to log events from `ffmpeg.exec()`.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* log includes output to stdout and stderr.
|
||||||
|
*
|
||||||
|
* @category Event
|
||||||
|
*/
|
||||||
|
on(event: typeof FFmpeg.LOG, listener: (log: LogEvent) => void): this;
|
||||||
|
/**
|
||||||
|
* Listen to progress events from `ffmpeg.exec()`.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* The progress events are accurate only when the length of
|
||||||
|
* input and output video/audio file are the same.
|
||||||
|
*
|
||||||
|
* @category Event
|
||||||
|
*/
|
||||||
|
on(
|
||||||
|
event: typeof FFmpeg.PROGRESS,
|
||||||
|
listener: (progress: Progress) => void
|
||||||
|
): this;
|
||||||
|
on(event: string, listener: any): this {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#worker: Worker;
|
||||||
|
#resolves: Callbacks = {};
|
||||||
|
#rejects: Callbacks = {};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.#worker = new Worker(new URL("./worker.ts", import.meta.url));
|
||||||
|
this.#registerHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** register worker message event handlers.
|
||||||
|
*/
|
||||||
|
#registerHandlers = () => {
|
||||||
|
this.#worker.onmessage = ({
|
||||||
|
data: { id, type, data },
|
||||||
|
}: FFMessageEventCallback) => {
|
||||||
|
switch (type) {
|
||||||
|
case FFMessageType.LOAD:
|
||||||
|
case FFMessageType.EXEC:
|
||||||
|
case FFMessageType.WRITE_FILE:
|
||||||
|
case FFMessageType.READ_FILE:
|
||||||
|
this.#resolves[id](data);
|
||||||
|
break;
|
||||||
|
case FFMessageType.DOWNLOAD:
|
||||||
|
this.emit(FFmpeg.DOWNLOAD, data as DownloadProgressEvent);
|
||||||
|
break;
|
||||||
|
case FFMessageType.LOG:
|
||||||
|
this.emit(FFmpeg.LOG, data as LogEvent);
|
||||||
|
break;
|
||||||
|
case FFMessageType.PROGRESS:
|
||||||
|
this.emit(FFmpeg.PROGRESS, data as Progress);
|
||||||
|
break;
|
||||||
|
case FFMessageType.ERROR:
|
||||||
|
this.#rejects[id](data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delete this.#resolves[id];
|
||||||
|
delete this.#rejects[id];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic function to send messages to web worker.
|
||||||
|
*/
|
||||||
|
#send = (
|
||||||
|
{ type, data }: Message,
|
||||||
|
trans: Transferable[] = []
|
||||||
|
): Promise<CallbackData> =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const id = getMessageID();
|
||||||
|
this.#worker.postMessage({ id, type, data }, trans);
|
||||||
|
this.#resolves[id] = resolve;
|
||||||
|
this.#rejects[id] = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads ffmpeg-core inside web worker. It is required to call this method first
|
||||||
|
* as it initializes WebAssembly and other essential variables.
|
||||||
|
*
|
||||||
|
* @category FFmpeg
|
||||||
|
* @returns `true` if ffmpeg core is loaded for the first time.
|
||||||
|
*/
|
||||||
|
public load = (config: FFMessageLoadConfig): Promise<IsFirst> =>
|
||||||
|
this.#send({
|
||||||
|
type: FFMessageType.LOAD,
|
||||||
|
data: config,
|
||||||
|
}) as Promise<IsFirst>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute ffmpeg command.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* To avoid common I/O issues, ["-nostdin", "-y"] are prepended to the args
|
||||||
|
* by default.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const ffmpeg = new FFmpeg();
|
||||||
|
* await ffmpeg.load();
|
||||||
|
* await ffmpeg.writeFile("video.avi", ...);
|
||||||
|
* // ffmpeg -i video.avi video.mp4
|
||||||
|
* await ffmpeg.exec(["-i", "video.avi", "video.mp4"]);
|
||||||
|
* const data = ffmpeg.readFile("video.mp4");
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @returns `0` if no error, `!= 0` if timeout (1) or error.
|
||||||
|
* @category FFmpeg
|
||||||
|
*/
|
||||||
|
public exec = (
|
||||||
|
/** ffmpeg command line args */
|
||||||
|
args: string[],
|
||||||
|
/**
|
||||||
|
* milliseconds to wait before stopping the command execution.
|
||||||
|
*
|
||||||
|
* @defaultValue -1
|
||||||
|
*/
|
||||||
|
timeout = -1
|
||||||
|
): Promise<number> =>
|
||||||
|
this.#send({
|
||||||
|
type: FFMessageType.EXEC,
|
||||||
|
data: { args, timeout },
|
||||||
|
}) as Promise<number>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to ffmpeg.wasm in memory file system.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const ffmpeg = new FFmpeg();
|
||||||
|
* await ffmpeg.load();
|
||||||
|
* await ffmpeg.writeFile("video.avi", await fetchFile("../video.avi"));
|
||||||
|
* await ffmpeg.writeFile("text.txt", "hello world");
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @category File System
|
||||||
|
*/
|
||||||
|
public writeFile = (
|
||||||
|
path: string,
|
||||||
|
bin: Uint8Array | string
|
||||||
|
): Promise<IsDone> => {
|
||||||
|
const trans: Transferable[] = [];
|
||||||
|
if (bin instanceof Uint8Array) {
|
||||||
|
trans.push(bin.buffer);
|
||||||
|
}
|
||||||
|
return this.#send(
|
||||||
|
{
|
||||||
|
type: FFMessageType.WRITE_FILE,
|
||||||
|
data: { path, bin },
|
||||||
|
},
|
||||||
|
trans
|
||||||
|
) as Promise<IsDone>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from ffmpeg.wasm in memory file system.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const ffmpeg = new FFmpeg();
|
||||||
|
* await ffmpeg.load();
|
||||||
|
* const data = await ffmpeg.readFile("video.mp4");
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @category File System
|
||||||
|
*/
|
||||||
|
public readFile = (path: string): Promise<Uint8Array> =>
|
||||||
|
this.#send({
|
||||||
|
type: FFMessageType.READ_FILE,
|
||||||
|
data: { path },
|
||||||
|
}) as Promise<Uint8Array>;
|
||||||
|
}
|
18
packages/ffmpeg/src/const.ts
Normal file
18
packages/ffmpeg/src/const.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export const HeaderContentLength = "Content-Length";
|
||||||
|
export const MIME_TYPE_JAVASCRIPT = "text/javascript";
|
||||||
|
export const MIME_TYPE_WASM = "application/wasm";
|
||||||
|
|
||||||
|
export const CORE_VERSION = "0.12.0";
|
||||||
|
export const CORE_URL = `https://unpkg.com/@ffmpeg/core@${CORE_VERSION}/dist/umd/ffmpeg-core.js`;
|
||||||
|
|
||||||
|
export enum FFMessageType {
|
||||||
|
LOAD = "load",
|
||||||
|
WRITE_FILE = "WRITE_FILE",
|
||||||
|
EXEC = "EXEC",
|
||||||
|
READ_FILE = "READ_FILE",
|
||||||
|
ERROR = "ERROR",
|
||||||
|
|
||||||
|
DOWNLOAD = "DOWNLOAD",
|
||||||
|
PROGRESS = "PROGRESS",
|
||||||
|
LOG = "LOG",
|
||||||
|
}
|
13
packages/ffmpeg/src/errors.ts
Normal file
13
packages/ffmpeg/src/errors.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export const ERROR_RESPONSE_BODY_READER = new Error(
|
||||||
|
"failed to get response body reader"
|
||||||
|
);
|
||||||
|
export const ERROR_ZERO_CONTENT_LENGTH = new Error(
|
||||||
|
"failed to get Content-Length"
|
||||||
|
);
|
||||||
|
export const ERROR_UNKNOWN_MESSAGE_TYPE = new Error("unknown message type");
|
||||||
|
export const ERROR_NOT_LOADED = new Error(
|
||||||
|
"ffmpeg is not loaded, call `await ffmpeg.load()` first"
|
||||||
|
);
|
||||||
|
export const ERROR_INCOMPLETED_DOWNLOAD = new Error(
|
||||||
|
"failed to complete download"
|
||||||
|
);
|
1
packages/ffmpeg/src/index.ts
Normal file
1
packages/ffmpeg/src/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./classes";
|
128
packages/ffmpeg/src/types.ts
Normal file
128
packages/ffmpeg/src/types.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
export type FFFSPath = string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ffmpeg-core loading configuration.
|
||||||
|
*/
|
||||||
|
export interface FFMessageLoadConfig {
|
||||||
|
/**
|
||||||
|
* `ffmpeg-core.js` URL.
|
||||||
|
*
|
||||||
|
* @defaultValue `https://unpkg.com/@ffmpeg/core@${CORE_VERSION}/dist/umd/ffmpeg-core.js`;
|
||||||
|
*/
|
||||||
|
coreURL?: string;
|
||||||
|
/**
|
||||||
|
* `ffmpeg-core.wasm` URL.
|
||||||
|
*
|
||||||
|
* @defaultValue `https://unpkg.com/@ffmpeg/core@${CORE_VERSION}/dist/umd/ffmpeg-core.wasm`;
|
||||||
|
*/
|
||||||
|
wasmURL?: string;
|
||||||
|
/**
|
||||||
|
* `ffmpeg-core.worker.js` URL, only being loaded when `thread` is `true`.
|
||||||
|
*
|
||||||
|
* @defaultValue `https://unpkg.com/@ffmpeg/core-mt@${CORE_VERSION}/dist/umd/ffmpeg-core.worker.js`;
|
||||||
|
*/
|
||||||
|
workerURL?: string;
|
||||||
|
/**
|
||||||
|
* When `blob` is true, the content of `coreURL`, `wasmURL` and `workerURL`
|
||||||
|
* will be fetched and convert to blob URL. This avoids problems like CORS
|
||||||
|
* and provides download progress than can be listened like below:
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const ffmpeg = new FFmpeg();
|
||||||
|
* ffmpeg.on(FFmpeg.DOWNLOAD, (ev) => {
|
||||||
|
* console.log(ev);
|
||||||
|
* })
|
||||||
|
* await ffmpeg.load();
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @defaultValue `true`
|
||||||
|
*/
|
||||||
|
blob?: boolean;
|
||||||
|
/**
|
||||||
|
* When `thread` is true, ffmpeg imports `ffmpeg-core.worker.js` and thus
|
||||||
|
* makes multi-threaded core work.
|
||||||
|
*
|
||||||
|
* @defaultValue `false`
|
||||||
|
*/
|
||||||
|
thread?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FFMessageWriteFileData {
|
||||||
|
path: FFFSPath;
|
||||||
|
bin: Uint8Array | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FFMessageExecData {
|
||||||
|
args: string[];
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FFMessageReadFileData {
|
||||||
|
path: FFFSPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FFMessageData =
|
||||||
|
| FFMessageLoadConfig
|
||||||
|
| FFMessageWriteFileData
|
||||||
|
| FFMessageExecData
|
||||||
|
| FFMessageReadFileData;
|
||||||
|
|
||||||
|
export interface Message {
|
||||||
|
type: string;
|
||||||
|
data?: FFMessageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FFMessage extends Message {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FFMessageEvent extends MessageEvent {
|
||||||
|
data: FFMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DownloadProgressEvent {
|
||||||
|
url: string | URL;
|
||||||
|
total: number;
|
||||||
|
received: number;
|
||||||
|
delta: number;
|
||||||
|
done: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LogEvent {
|
||||||
|
type: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExitCode = number;
|
||||||
|
export type ErrorMessage = string;
|
||||||
|
export type FileData = Uint8Array;
|
||||||
|
export type Progress = number;
|
||||||
|
export type IsFirst = boolean;
|
||||||
|
export type IsDone = boolean;
|
||||||
|
|
||||||
|
export type CallbackData =
|
||||||
|
| FileData
|
||||||
|
| ExitCode
|
||||||
|
| ErrorMessage
|
||||||
|
| DownloadProgressEvent
|
||||||
|
| LogEvent
|
||||||
|
| Progress
|
||||||
|
| IsFirst
|
||||||
|
| IsDone
|
||||||
|
| Error
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
export interface Callbacks {
|
||||||
|
[id: number | string]: (data: CallbackData) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FFMessageEventCallback {
|
||||||
|
data: {
|
||||||
|
id: number;
|
||||||
|
type: string;
|
||||||
|
data: CallbackData;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProgressCallback = (event: DownloadProgressEvent) => void;
|
62
packages/ffmpeg/src/utils.ts
Normal file
62
packages/ffmpeg/src/utils.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
ERROR_RESPONSE_BODY_READER,
|
||||||
|
ERROR_ZERO_CONTENT_LENGTH,
|
||||||
|
ERROR_INCOMPLETED_DOWNLOAD,
|
||||||
|
} from "./errors";
|
||||||
|
import { HeaderContentLength } from "./const";
|
||||||
|
import { ProgressCallback } from "./types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an unique message ID.
|
||||||
|
*/
|
||||||
|
export const getMessageID = (() => {
|
||||||
|
let messageID = 0;
|
||||||
|
return () => messageID++;
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download content of a URL with progress.
|
||||||
|
*/
|
||||||
|
export const downloadWithProgress = async (
|
||||||
|
url: string | URL,
|
||||||
|
cb: ProgressCallback
|
||||||
|
): Promise<Uint8Array> => {
|
||||||
|
const resp = await fetch(url);
|
||||||
|
const reader = resp.body?.getReader();
|
||||||
|
if (!reader) throw ERROR_RESPONSE_BODY_READER;
|
||||||
|
|
||||||
|
const total = parseInt(resp.headers.get(HeaderContentLength) || "0");
|
||||||
|
if (total === 0) throw ERROR_ZERO_CONTENT_LENGTH;
|
||||||
|
|
||||||
|
const data = new Uint8Array(total);
|
||||||
|
let received = 0;
|
||||||
|
for (;;) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
const delta = value ? value.length : 0;
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
if (total !== received) throw ERROR_INCOMPLETED_DOWNLOAD;
|
||||||
|
cb({ url, total, received, delta, done });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.set(value, received);
|
||||||
|
received += delta;
|
||||||
|
cb({ url, total, received, delta, done });
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an URL to an Blob URL to avoid issues like CORS.
|
||||||
|
*/
|
||||||
|
export const toBlobURL = async (
|
||||||
|
url: string,
|
||||||
|
/** mime type like `text/javascript` and `application/wasm` */
|
||||||
|
mimeType: string,
|
||||||
|
cb: ProgressCallback
|
||||||
|
): Promise<string> =>
|
||||||
|
URL.createObjectURL(
|
||||||
|
new Blob([(await downloadWithProgress(url, cb)).buffer], { type: mimeType })
|
||||||
|
);
|
132
packages/ffmpeg/src/worker.ts
Normal file
132
packages/ffmpeg/src/worker.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/// <reference no-default-lib="true" />
|
||||||
|
/// <reference lib="esnext" />
|
||||||
|
/// <reference lib="webworker" />
|
||||||
|
|
||||||
|
import type { FFmpegCoreModule, FFmpegCoreModuleFactory } from "@ffmpeg/types";
|
||||||
|
import type {
|
||||||
|
FFMessageEvent,
|
||||||
|
FFMessageLoadConfig,
|
||||||
|
FFMessageWriteFileData,
|
||||||
|
FFMessageExecData,
|
||||||
|
FFMessageReadFileData,
|
||||||
|
CallbackData,
|
||||||
|
IsFirst,
|
||||||
|
IsDone,
|
||||||
|
ExitCode,
|
||||||
|
} from "./types";
|
||||||
|
import { toBlobURL } from "./utils";
|
||||||
|
import {
|
||||||
|
CORE_URL,
|
||||||
|
FFMessageType,
|
||||||
|
MIME_TYPE_JAVASCRIPT,
|
||||||
|
MIME_TYPE_WASM,
|
||||||
|
} from "./const";
|
||||||
|
import { ERROR_UNKNOWN_MESSAGE_TYPE, ERROR_NOT_LOADED } from "./errors";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface WorkerGlobalScope {
|
||||||
|
createFFmpegCore: FFmpegCoreModuleFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ffmpeg: FFmpegCoreModule;
|
||||||
|
|
||||||
|
const load = async ({
|
||||||
|
coreURL: _coreURL = CORE_URL,
|
||||||
|
wasmURL: _wasmURL,
|
||||||
|
workerURL: _workerURL,
|
||||||
|
blob = true,
|
||||||
|
thread = false,
|
||||||
|
}: FFMessageLoadConfig): Promise<IsFirst> => {
|
||||||
|
const first = !ffmpeg;
|
||||||
|
let coreURL = _coreURL;
|
||||||
|
let wasmURL = _wasmURL ? _wasmURL : _coreURL.replace(/.js$/g, ".wasm");
|
||||||
|
let workerURL = _workerURL
|
||||||
|
? _workerURL
|
||||||
|
: _coreURL.replace(/.js$/g, ".worker.js");
|
||||||
|
|
||||||
|
if (blob) {
|
||||||
|
coreURL = await toBlobURL(coreURL, MIME_TYPE_JAVASCRIPT, (data) =>
|
||||||
|
self.postMessage({ type: FFMessageType.DOWNLOAD, data })
|
||||||
|
);
|
||||||
|
wasmURL = await toBlobURL(wasmURL, MIME_TYPE_WASM, (data) =>
|
||||||
|
self.postMessage({ type: FFMessageType.DOWNLOAD, data })
|
||||||
|
);
|
||||||
|
if (thread) {
|
||||||
|
try {
|
||||||
|
workerURL = await toBlobURL(workerURL, MIME_TYPE_JAVASCRIPT, (data) =>
|
||||||
|
self.postMessage({ type: FFMessageType.DOWNLOAD, data })
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
importScripts(coreURL);
|
||||||
|
ffmpeg = await (self as WorkerGlobalScope).createFFmpegCore({
|
||||||
|
// Fixed `Overload resolution failed.` when using multi-threaded ffmpeg-core.
|
||||||
|
mainScriptUrlOrBlob: coreURL,
|
||||||
|
locateFile: (path: string, prefix: string): string => {
|
||||||
|
if (path.endsWith(".wasm")) return wasmURL;
|
||||||
|
if (path.endsWith(".worker.js")) return workerURL;
|
||||||
|
return prefix + path;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
ffmpeg.setLogger((data) =>
|
||||||
|
self.postMessage({ type: FFMessageType.LOG, data })
|
||||||
|
);
|
||||||
|
ffmpeg.setProgress((data: number) =>
|
||||||
|
self.postMessage({ type: FFMessageType.PROGRESS, data })
|
||||||
|
);
|
||||||
|
return first;
|
||||||
|
};
|
||||||
|
|
||||||
|
const writeFile = ({ path, bin }: FFMessageWriteFileData): IsDone => {
|
||||||
|
ffmpeg.FS.writeFile(path, bin);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const exec = ({ args, timeout = -1 }: FFMessageExecData): ExitCode => {
|
||||||
|
ffmpeg.setTimeout(timeout);
|
||||||
|
ffmpeg.exec(...args);
|
||||||
|
const ret = ffmpeg.ret;
|
||||||
|
ffmpeg.reset();
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
const readFile = ({ path }: FFMessageReadFileData): Uint8Array =>
|
||||||
|
ffmpeg.FS.readFile(path);
|
||||||
|
|
||||||
|
self.onmessage = async ({
|
||||||
|
data: { id, type, data: _data },
|
||||||
|
}: FFMessageEvent): Promise<void> => {
|
||||||
|
const trans = [];
|
||||||
|
let data: CallbackData;
|
||||||
|
try {
|
||||||
|
if (type !== FFMessageType.LOAD && !ffmpeg) throw ERROR_NOT_LOADED;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case FFMessageType.LOAD:
|
||||||
|
data = await load(_data as FFMessageLoadConfig);
|
||||||
|
break;
|
||||||
|
case FFMessageType.WRITE_FILE:
|
||||||
|
data = writeFile(_data as FFMessageWriteFileData);
|
||||||
|
break;
|
||||||
|
case FFMessageType.EXEC:
|
||||||
|
data = exec(_data as FFMessageExecData);
|
||||||
|
break;
|
||||||
|
case FFMessageType.READ_FILE:
|
||||||
|
data = readFile(_data as FFMessageReadFileData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw ERROR_UNKNOWN_MESSAGE_TYPE;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
self.postMessage({ id, type: FFMessageType.ERROR, data: e as Error });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data instanceof Uint8Array) {
|
||||||
|
trans.push(data.buffer);
|
||||||
|
}
|
||||||
|
self.postMessage({ id, type, data }, trans);
|
||||||
|
};
|
10
packages/ffmpeg/tsconfig.d.json
Normal file
10
packages/ffmpeg/tsconfig.d.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"outFile": "dist/umd/ffmpeg.d.ts"
|
||||||
|
}
|
||||||
|
}
|
9
packages/ffmpeg/tsconfig.esm.json
Normal file
9
packages/ffmpeg/tsconfig.esm.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src",
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": "./dist/esm",
|
||||||
|
"target": "esnext"
|
||||||
|
}
|
||||||
|
}
|
7
packages/ffmpeg/tsconfig.json
Normal file
7
packages/ffmpeg/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "esnext",
|
||||||
|
"rootDir": "src"
|
||||||
|
}
|
||||||
|
}
|
27
packages/ffmpeg/webpack.config.js
Normal file
27
packages/ffmpeg/webpack.config.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: "production",
|
||||||
|
devtool: "source-map",
|
||||||
|
entry: "./src/index.ts",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
loader: "ts-loader",
|
||||||
|
options: {
|
||||||
|
transpileOnly: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: [".ts"],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, "dist/umd"),
|
||||||
|
filename: "ffmpeg.js",
|
||||||
|
library: "FFmpegWASM",
|
||||||
|
libraryTarget: "umd",
|
||||||
|
},
|
||||||
|
};
|
27
packages/ffmpeg/webpack.dev.config.js
Normal file
27
packages/ffmpeg/webpack.dev.config.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: "development",
|
||||||
|
devtool: "source-map",
|
||||||
|
entry: "./src/index.ts",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
loader: "ts-loader",
|
||||||
|
options: {
|
||||||
|
transpileOnly: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: [".ts"],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, "dist/umd"),
|
||||||
|
filename: "ffmpeg.js",
|
||||||
|
library: "FFmpegWASM",
|
||||||
|
libraryTarget: "umd",
|
||||||
|
},
|
||||||
|
};
|
38
packages/types/package.json
Normal file
38
packages/types/package.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "@ffmpeg/types",
|
||||||
|
"version": "0.11.5",
|
||||||
|
"description": "ffmpeg.wasm types",
|
||||||
|
"types": "types",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "dtslint types"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"types"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/ffmpegwasm/ffmpeg.wasm.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"ffmpeg",
|
||||||
|
"WebAssembly",
|
||||||
|
"video",
|
||||||
|
"audio",
|
||||||
|
"transcode"
|
||||||
|
],
|
||||||
|
"author": "Jerome Wu <jeromewus@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/ffmpegwasm/ffmpeg.wasm/issues"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.6.0"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/ffmpegwasm/ffmpeg.wasm#readme",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@definitelytyped/dtslint": "^0.0.133"
|
||||||
|
}
|
||||||
|
}
|
39
packages/types/types/index.d.ts
vendored
Normal file
39
packages/types/types/index.d.ts
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
export type Pointer = number;
|
||||||
|
export type StringPointer = Pointer;
|
||||||
|
export type StringArrayPointer = Pointer;
|
||||||
|
|
||||||
|
export interface FS {
|
||||||
|
mkdir: (fileName: string) => void;
|
||||||
|
readFile: (fileName: string) => Uint8Array;
|
||||||
|
readdir: (pathName: string) => string[];
|
||||||
|
unlink: (fileName: string) => void;
|
||||||
|
writeFile: (fileName: string, binaryData: Uint8Array | string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Log {
|
||||||
|
type: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FFmpegCoreModule {
|
||||||
|
DEFAULT_ARGS: string[];
|
||||||
|
FS: FS;
|
||||||
|
NULL: Pointer;
|
||||||
|
SIZE_I32: number;
|
||||||
|
|
||||||
|
ret: number;
|
||||||
|
timeout: number;
|
||||||
|
mainScriptUrlOrBlob: string;
|
||||||
|
|
||||||
|
exec: (...args: string[]) => number;
|
||||||
|
reset: () => void;
|
||||||
|
setLogger: (logger: (log: Log) => void) => void;
|
||||||
|
setTimeout: (timeout: number) => void;
|
||||||
|
setProgress: (handler: (progress: number) => void) => void;
|
||||||
|
|
||||||
|
locateFile: (path: string, prefix: string) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FFmpegCoreModuleFactory = (
|
||||||
|
moduleOverrides?: Partial<FFmpegCoreModule>
|
||||||
|
) => Promise<FFmpegCoreModule>;
|
18
packages/types/types/tsconfig.json
Normal file
18
packages/types/types/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["es6"],
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"types": [],
|
||||||
|
"noEmit": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
|
||||||
|
// If the library is an external module (uses `export`), this allows your test file to import "mylib" instead of "./index".
|
||||||
|
// If the library is global (cannot be imported via `import` or `require`), leave this out.
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": { "@ffmpeg/types": ["."] }
|
||||||
|
}
|
||||||
|
}
|
@ -1707,7 +1707,8 @@ static void print_report(int is_last_report, int64_t timer_start, int64_t cur_ti
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (print_stats || is_last_report) {
|
if (print_stats || is_last_report) {
|
||||||
const char end = is_last_report ? '\n' : '\r';
|
// Always print a new line of message.
|
||||||
|
const char end = '\n'; //is_last_report ? '\n' : '\r';
|
||||||
if (print_stats==1 && AV_LOG_INFO > av_log_get_level()) {
|
if (print_stats==1 && AV_LOG_INFO > av_log_get_level()) {
|
||||||
fprintf(stderr, "%s %c", buf.str, end);
|
fprintf(stderr, "%s %c", buf.str, end);
|
||||||
} else
|
} else
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
"globals": {
|
"globals": {
|
||||||
"expect": true,
|
"expect": true,
|
||||||
"createFFmpeg": true,
|
"createFFmpegCore": true,
|
||||||
"VIDEO_1S_MP4": true,
|
"VIDEO_1S_MP4": true,
|
||||||
"FFMPEG_TYPE": true
|
"FFMPEG_TYPE": true
|
||||||
},
|
},
|
||||||
|
@ -9,13 +9,13 @@
|
|||||||
<div id="mocha"></div>
|
<div id="mocha"></div>
|
||||||
<script src="../node_modules/mocha/mocha.js"></script>
|
<script src="../node_modules/mocha/mocha.js"></script>
|
||||||
<script src="../node_modules/chai/chai.js"></script>
|
<script src="../node_modules/chai/chai.js"></script>
|
||||||
<script src="../packages/ffmpeg-mt/dist/umd/ffmpeg.js"></script>
|
<script src="../packages/core-mt/dist/umd/ffmpeg-core.js"></script>
|
||||||
<script src="./constants.js"></script>
|
<script src="./constants.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.FFMPEG_TYPE = "mt";
|
window.FFMPEG_TYPE = "mt";
|
||||||
</script>
|
</script>
|
||||||
<script>mocha.setup('bdd');</script>
|
<script>mocha.setup('bdd');</script>
|
||||||
<script src="./ffmpeg.test.js"></script>
|
<script src="./ffmpeg-core.test.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.expect = chai.expect;
|
window.expect = chai.expect;
|
||||||
mocha.run();
|
mocha.run();
|
@ -9,13 +9,13 @@
|
|||||||
<div id="mocha"></div>
|
<div id="mocha"></div>
|
||||||
<script src="../node_modules/mocha/mocha.js"></script>
|
<script src="../node_modules/mocha/mocha.js"></script>
|
||||||
<script src="../node_modules/chai/chai.js"></script>
|
<script src="../node_modules/chai/chai.js"></script>
|
||||||
<script src="../packages/ffmpeg/dist/umd/ffmpeg.js"></script>
|
<script src="../packages/core/dist/umd/ffmpeg-core.js"></script>
|
||||||
<script src="./constants.js"></script>
|
<script src="./constants.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.FFMPEG_TYPE = "st";
|
window.FFMPEG_TYPE = "st";
|
||||||
</script>
|
</script>
|
||||||
<script>mocha.setup('bdd');</script>
|
<script>mocha.setup('bdd');</script>
|
||||||
<script src="./ffmpeg.test.js"></script>
|
<script src="./ffmpeg-core.test.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.expect = chai.expect;
|
window.expect = chai.expect;
|
||||||
mocha.run();
|
mocha.run();
|
@ -1,6 +1,6 @@
|
|||||||
let ffmpeg;
|
let ffmpeg;
|
||||||
|
|
||||||
const genName = (name) => `[ffmpeg][${FFMPEG_TYPE}] ${name}`;
|
const genName = (name) => `[ffmpeg-core][${FFMPEG_TYPE}] ${name}`;
|
||||||
|
|
||||||
const b64ToUint8Array = (b64) => {
|
const b64ToUint8Array = (b64) => {
|
||||||
const bin = atob(b64);
|
const bin = atob(b64);
|
||||||
@ -19,7 +19,7 @@ const reset = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
ffmpeg = await createFFmpeg();
|
ffmpeg = await createFFmpegCore();
|
||||||
ffmpeg.FS.writeFile("video.mp4", b64ToUint8Array(VIDEO_1S_MP4));
|
ffmpeg.FS.writeFile("video.mp4", b64ToUint8Array(VIDEO_1S_MP4));
|
||||||
});
|
});
|
||||||
|
|
@ -2,7 +2,7 @@ const chai = require("chai");
|
|||||||
const constants = require("./constants");
|
const constants = require("./constants");
|
||||||
|
|
||||||
global.expect = chai.expect;
|
global.expect = chai.expect;
|
||||||
global.createFFmpeg = require("../packages/ffmpeg-mt");
|
global.createFFmpegCore = require("../packages/core-mt");
|
||||||
global.atob = require("./util").atob;
|
global.atob = require("./util").atob;
|
||||||
global.FFMPEG_TYPE = "mt";
|
global.FFMPEG_TYPE = "mt";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ const chai = require("chai");
|
|||||||
const constants = require("./constants");
|
const constants = require("./constants");
|
||||||
|
|
||||||
global.expect = chai.expect;
|
global.expect = chai.expect;
|
||||||
global.createFFmpeg = require("../packages/ffmpeg");
|
global.createFFmpegCore = require("../packages/core");
|
||||||
global.atob = require("./util").atob;
|
global.atob = require("./util").atob;
|
||||||
global.FFMPEG_TYPE = "st";
|
global.FFMPEG_TYPE = "st";
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
/* Projects */
|
/* Projects */
|
||||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
// "composite": true, [> Enable constraints that allow a TypeScript project to be used with project references. <]
|
||||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
@ -46,7 +46,7 @@
|
|||||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||||
|
|
||||||
/* Emit */
|
/* Emit */
|
||||||
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
// "declaration": true, [> Generate .d.ts files from TypeScript and JavaScript files in your project. <]
|
||||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
|
Loading…
Reference in New Issue
Block a user