diff --git a/README.md b/README.md index a9bf8c6..411802d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Welcome to FFmpeg.js, an innovative library that offers a WebAssembly-powered interface for utilizing FFmpeg in the browser. πŸŒπŸ’‘ -### [πŸ‘₯Join our Discord](https://discord.gg/EmUMFvMdhT) +### [πŸ‘₯Join our Discord](https://discord.gg/n3mpzfejAb) ---- @@ -25,7 +25,7 @@ Addressing the issues above, FFmpeg.js: However, it's important to note that as of now, FFmpeg.js runs only in Chrome, Firefox, and Edge browsers. It doesn't support Safari or Node. πŸš§πŸ” -## βš™οΈ Setup +## πŸš€ Setup Setting up FFmpeg.js is a breeze! @@ -117,14 +117,23 @@ Take a look at these tests for more examples: - Webassembly is limited to 2GB - Difficult to handle in unit tests, it's probably best if you mock the FFmpeg class and leave the testing to us (which is also good practice). -- The LGPLv2.1 build of FFmpeg without external libaries doesn't support any mainstream video delivery codecs such as h264/hevc/vp9. But it's very useful for audio processing, run the following commands for more information +- There is no hardware accileration available, making video encoding/decoding rather slow. + +## βš™οΈ Configurations + +Currently there are two different FFmpeg configurations available with more on the way. + +- `lgpl-base` (default): It is a compilation of FFmpeg without any external libraries, which is useful for audio & video muxing/demuxing and audio encoding/decoding. It is v2.1LGPL compliant and can therefore be used for commercial projects. +- `gpl-extended`: This is the [@ffmpeg/core](https://github.com/ffmpegwasm/ffmpeg.wasm-core) configuration, that has been built with `--enable-gpl` and `--enable-nonfree` and can therefore only be used for commercial projects if the entire codebase is publicly accessible. It supports popular delivery codecs such as `h264/h265/vp9` etc. + +For more information about the supported codecs and muxers run the following commands: ```typescript console.log(await ffmpeg.codecs()); console.log(await ffmpeg.formats()); ``` -**BUT WAIT THERE IS MORE!** FFmpeg js is compatible with the binaries of `@ffmpeg/core`, which supports all major codecs like those mentioned before. Here is how you can configure it: +This is how you can switch the configuration: ```typescript import { @@ -134,10 +143,15 @@ import { // FFmpegConfigurationGPLExtended will add the type extensions const ffmpeg = new FFmpeg({ - lib: 'gpl-extended', + config: 'gpl-extended', }); ``` Thats it! -> More FFmpeg LGPLv2.1 builds with external libraries such as VP9 and LAME will be added soon! We believe that FFmpeg.js will significantly streamline your interaction with FFmpeg in the browser, providing a more effective and efficient coding experience. Happy coding! πŸš€πŸŒŸ + +### DISCLAIMER + +*The information contained in this text is provided for informational purposes only. It is not intended as a comprehensive guide to the GPL and LGPL license usages nor does it offer legal advice. Laws and regulations surrounding software licenses can be complex and may change over time. The author and provider of this information cannot be held responsible for any errors, omissions, or any outcomes related to your use of this information.* + +*While every effort has been made to ensure the information presented here is accurate as of the date of publication, no guarantee is given as to its currency or applicability to your specific situation. You should not rely upon this information as a substitute for consultation with a legal professional or other competent advisors. Always consult with a qualified professional familiar with your particular circumstances before making decisions that could have legal implications.* diff --git a/examples/package-lock.json b/examples/package-lock.json index e9b4458..5b8ab85 100644 --- a/examples/package-lock.json +++ b/examples/package-lock.json @@ -8,27 +8,17 @@ "name": "examples", "version": "0.0.0", "dependencies": { - "@diffusion-studio/ffmpeg-js": "file:.." + "@diffusion-studio/ffmpeg-js": "^0.0.2" }, "devDependencies": { "typescript": "^5.0.2", "vite": "^4.3.9" } }, - "..": { - "name": "@diffusion-studio/ffmpeg-js", - "version": "0.0.1", - "devDependencies": { - "@playwright/test": "^1.35.1", - "prettier": "2.8.8", - "typescript": "^5.0.2", - "vite": "^4.3.9", - "vite-plugin-dts": "^3.0.0-beta.3" - } - }, "node_modules/@diffusion-studio/ffmpeg-js": { - "resolved": "..", - "link": true + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@diffusion-studio/ffmpeg-js/-/ffmpeg-js-0.0.2.tgz", + "integrity": "sha512-ZadHuH7dU9mClMvrl/TwxhGy4k7dzdGWHREgu2hHrU+OfO9NrDkSbO6id29uNbjSo7PKUqIBp9NyM2ZUIIq9zg==" }, "node_modules/@esbuild/android-arm": { "version": "0.17.19", @@ -486,9 +476,9 @@ } }, "node_modules/rollup": { - "version": "3.25.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.3.tgz", - "integrity": "sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.0.tgz", + "integrity": "sha512-YzJH0eunH2hr3knvF3i6IkLO/jTjAEwU4HoMUbQl4//Tnl3ou0e7P5SjxdDr8HQJdeUJShlbEHXrrnEHy1l7Yg==", "dev": true, "bin": { "rollup": "dist/bin/rollup" diff --git a/examples/package.json b/examples/package.json index 90a595a..924f674 100644 --- a/examples/package.json +++ b/examples/package.json @@ -13,6 +13,6 @@ "vite": "^4.3.9" }, "dependencies": { - "@diffusion-studio/ffmpeg-js": "file:.." + "@diffusion-studio/ffmpeg-js": "^0.0.2" } } diff --git a/examples/src/main.ts b/examples/src/main.ts index b5d3716..d23acbd 100644 --- a/examples/src/main.ts +++ b/examples/src/main.ts @@ -3,8 +3,7 @@ import typescriptLogo from './typescript.svg'; import viteLogo from '/vite.svg'; import { FFmpeg } from '@diffusion-studio/ffmpeg-js'; -const logger = () => null; -const ffmpeg = new FFmpeg({ logger, lib: "lgpl-base" }); +const ffmpeg = new FFmpeg({ log: false }); document.querySelector('#app')!.innerHTML = `
diff --git a/package.json b/package.json index 35cb59b..7ee029d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@diffusion-studio/ffmpeg-js", "private": false, - "version": "0.0.1", + "version": "0.0.2", "description": "FFmpeg.js - Use FFmpeg in the browser powered by WebAssembly", "type": "module", "files": [ @@ -54,7 +54,7 @@ "homepage": "https://github.com/diffusion-studio/ffmpeg-js#readme", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "rm -r -f ./dist && tsc && vite build", "preview": "vite preview", "test": "npx playwright test --project=chromium", "prettier": "npx prettier --write ./src" diff --git a/playwright.config.ts b/playwright.config.ts index e3c61ca..81e0250 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -14,7 +14,6 @@ export default defineConfig({ command: 'npm run dev', url: 'http://localhost:5173/', }, - testDir: './src/tests', /* Run tests in files in parallel */ fullyParallel: false, @@ -47,9 +46,9 @@ export default defineConfig({ use: { ...devices['Desktop Firefox'] }, }, - { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, - }, + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, ], }); diff --git a/src/lib-config.ts b/src/ffmpeg-config.ts similarity index 100% rename from src/lib-config.ts rename to src/ffmpeg-config.ts diff --git a/src/ffmpeg.ts b/src/ffmpeg.ts index 39e9fe8..ca905c7 100644 --- a/src/ffmpeg.ts +++ b/src/ffmpeg.ts @@ -1,7 +1,8 @@ import { IFFmpegConfiguration } from './interfaces'; import { FFmpegBase } from './ffmpeg-base'; import * as types from './types'; -import libs from './lib-config'; +import configs from './ffmpeg-config'; +import { noop } from './utils'; export class FFmpeg< Config extends IFFmpegConfiguration< @@ -14,11 +15,19 @@ export class FFmpeg< private _output?: types.OutputOptions; private _middleware: string[] = []; - public constructor(settings: types.FFmpegSettings = { lib: 'lgpl-base' }) { - super({ - logger: settings.logger ?? console.log, - source: settings.source ?? libs[settings.lib], - }); + public constructor(settings: types.FFmpegSettings = {}) { + let logger = console.log; + let source = configs[settings?.config ?? "lgpl-base"]; + + if (settings?.log == false) { + logger = noop; + } + + if (settings?.source) { + source = settings.source; + } + + super({ logger, source }); } /** diff --git a/src/tests/base.spec.ts b/src/tests/base.spec.ts new file mode 100644 index 0000000..8bf45b6 --- /dev/null +++ b/src/tests/base.spec.ts @@ -0,0 +1,96 @@ +import { test, expect, Page } from '@playwright/test'; + +// Annotate entire file as serial. +test.describe.configure({ mode: 'serial' }); + +let page: Page; + +test.describe('FFmpegBase functionalities', async () => { + /** + * Get index page and wait until ffmpeg is ready + */ + test.beforeAll(async ({ browser }) => { + page = await browser.newPage(); + await page.goto('http://localhost:5173/'); + + const ready = await page.evaluate(async () => { + if (!ffmpeg.isReady) { + await new Promise((resolve) => { + ffmpeg.whenReady(resolve); + }); + } + + return ffmpeg.isReady; + }); + expect(ready).toBe(true); + }); + + test('test intercepting logs', async () => { + const messages = await page.evaluate(async () => { + const _messages: string[] = []; + ffmpeg.onMessage(((msg) => { + _messages.push(msg); + })); + await ffmpeg.exec(["-help"]); + return _messages; + }); + expect(messages.length).toBeGreaterThan(0); + expect(messages.at(0)).toBeTruthy(); + expect(messages.at(0)?.length).toBeGreaterThan(0); + }); + + test('test removing onMessage callback', async () => { + const messages = await page.evaluate(async () => { + const _messages0: string[] = []; + const _messages1: string[] = []; + + const cb0 = (msg: string) => _messages0.push(msg) + const cb1 = (msg: string) => _messages1.push(msg) + + ffmpeg.onMessage(cb0); + ffmpeg.onMessage(cb1); + + await ffmpeg.exec(["-help"]); + + ffmpeg.removeOnMessage(cb1); + + return [_messages0, _messages1]; + }); + + expect(messages[0].length).toBeGreaterThan(0); + expect(messages[1].length).toBeGreaterThan(0); + // callback function 1 should have recieved + // half as many messages than callback funtion 0 + expect(messages[1].length).toBeGreaterThan(messages[0].length / 2); + }); + + + test('test clearing memory works', async () => { + const result = await page.evaluate(async () => { + const inputName = 'input.ogg'; + const outputName = 'output.wav'; + await ffmpeg.writeFile(inputName, 'http://localhost:5173/samples/audio.ogg'); + await ffmpeg.exec(['-i', inputName, outputName]); + const render = ffmpeg.readFile(outputName).length; + + ffmpeg.clearMemory(); + // try to read memory again should fail + let input = null; + let output = null; + + // input and output should stay null + try { + input = ffmpeg.readFile(inputName); + } catch (e) { } + try { + output = ffmpeg.readFile(outputName); + } catch (e) { } + + return { render, input, output } + }); + + expect(result.render).toBeGreaterThan(0); + expect(result.output).toBe(null); + expect(result.input).toBe(null); + }); +}); diff --git a/src/tests/export.spec.ts b/src/tests/export.spec.ts index e10bc8f..abe6ddf 100644 --- a/src/tests/export.spec.ts +++ b/src/tests/export.spec.ts @@ -123,6 +123,18 @@ test.describe('FFmpeg basic exporting', async () => { expect(length).toBeGreaterThan(0); }); + test('test converting mp4 into gif', async () => { + const length = await page.evaluate(async () => { + const result = await ffmpeg + .input({ source: 'http://localhost:5173/samples/video.mp4' }) + .ouput({ format: 'gif' }) + .export(); + + return result?.length; + }); + expect(length).toBeGreaterThan(0); + }); + test('test adding wav audio track to mp4', async () => { const length = await page.evaluate(async () => { const result = await ffmpeg diff --git a/src/types/common.ts b/src/types/common.ts index 102ec87..6aa85bc 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -1,4 +1,4 @@ -import libs from '../lib-config'; +import configs from '../ffmpeg-config'; /** * Defines a callback function that @@ -63,12 +63,11 @@ export type FFmpegSettings = { * **COMPLY WITH**, checkout https://ffmpeg.org/legal.html * for more. */ - lib: keyof typeof libs; + config?: keyof typeof configs; /** - * Overwrite the logging function. - * The default is console log + * Choose whether ffmpeg should log each output */ - logger?(msg?: any, ...params: any[]): void; + log?: boolean; }; /**