-
-
-
-
+
+
);
}
diff --git a/examples/sound-effects/video-to-sfx/app/state/orchestrator.ts b/examples/sound-effects/video-to-sfx/app/state/orchestrator.ts
index b46314a..0f4d04e 100644
--- a/examples/sound-effects/video-to-sfx/app/state/orchestrator.ts
+++ b/examples/sound-effects/video-to-sfx/app/state/orchestrator.ts
@@ -55,4 +55,9 @@ export class Orchestrator {
stop() {
this.playing = false;
}
+
+ getAudioUrl(index: number) {
+ const player = this.sfxPlayers[index];
+ return player.data;
+ }
}
diff --git a/examples/sound-effects/video-to-sfx/app/state/player.ts b/examples/sound-effects/video-to-sfx/app/state/player.ts
index 9393e07..b5bba92 100644
--- a/examples/sound-effects/video-to-sfx/app/state/player.ts
+++ b/examples/sound-effects/video-to-sfx/app/state/player.ts
@@ -82,11 +82,13 @@ export class AudioPlayer {
audio: HTMLAudioElement;
progress: number = 0;
playing: boolean;
+ data: string;
constructor(data: string) {
this.audioLoaded = false;
this.waveformLoaded = false;
this.playing = false;
+ this.data = data;
this._player = new Tone.Player(
data,
action(() => {
diff --git a/examples/sound-effects/video-to-sfx/lib/mergeAndDownload.ts b/examples/sound-effects/video-to-sfx/lib/mergeAndDownload.ts
new file mode 100644
index 0000000..2168315
--- /dev/null
+++ b/examples/sound-effects/video-to-sfx/lib/mergeAndDownload.ts
@@ -0,0 +1,69 @@
+export async function mergeAndDownload(
+ videoFile: File | null,
+ audioData: string
+) {
+ const { FFmpeg } = await import("@ffmpeg/ffmpeg");
+ const { fetchFile, toBlobURL } = await import("@ffmpeg/util");
+ const ffmpeg = new FFmpeg();
+
+ const load = async () => {
+ const baseURL = "https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd";
+ ffmpeg.on("log", ({ message }) => {
+ console.log(message);
+ });
+ await ffmpeg.load({
+ coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"),
+ wasmURL: await toBlobURL(
+ `${baseURL}/ffmpeg-core.wasm`,
+ "application/wasm"
+ ),
+ });
+ };
+ const process = async () => {
+ console.log("transcoding");
+ if (!videoFile) {
+ throw new Error("No video file");
+ }
+
+ if (videoFile) {
+ await ffmpeg.writeFile(
+ "input.mp4",
+ await fetchFile(URL.createObjectURL(videoFile))
+ );
+ }
+
+ await ffmpeg.writeFile("audio.mpeg", await fetchFile(audioData));
+
+ await ffmpeg.exec(["-v", "error", "-i", "input.mp4", "-f", "null", "-"]);
+
+ await ffmpeg.exec(["-v", "error", "-i", "audio.mpeg", "-f", "null", "-"]);
+
+ await ffmpeg.exec([
+ "-v",
+ "verbose",
+ "-i",
+ "input.mp4",
+ "-i",
+ "audio.mpeg",
+ "-c:v",
+ "copy", // Copy the video codec
+ "-c:a",
+ "aac", // Transcode audio to AAC
+ "-strict",
+ "experimental",
+ "output.mp4",
+ ]);
+ console.log("transcoding completed");
+ const data = await ffmpeg.readFile("output.mp4");
+ const final_url = URL.createObjectURL(
+ new Blob([data.buffer], { type: "video/mp4" })
+ );
+ const downloadLinkFinalVideo = document.createElement("a");
+ downloadLinkFinalVideo.href = final_url;
+ downloadLinkFinalVideo.download = "final_output.mp4";
+ downloadLinkFinalVideo.click();
+ };
+
+ await load();
+ await process();
+}
diff --git a/examples/sound-effects/video-to-sfx/package-lock.json b/examples/sound-effects/video-to-sfx/package-lock.json
index d4a90d9..05bdeda 100644
--- a/examples/sound-effects/video-to-sfx/package-lock.json
+++ b/examples/sound-effects/video-to-sfx/package-lock.json
@@ -8,6 +8,8 @@
"name": "video-to-sfx",
"version": "0.1.0",
"dependencies": {
+ "@ffmpeg/ffmpeg": "^0.12.10",
+ "@ffmpeg/util": "^0.12.1",
"@hookform/resolvers": "^3.6.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
@@ -45,6 +47,7 @@
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.1.5",
"framer-motion": "^11.2.10",
+ "geist": "^1.3.0",
"input-otp": "^1.2.4",
"lodash": "^4.17.21",
"lucide-react": "^0.395.0",
@@ -101,6 +104,33 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@ffmpeg/ffmpeg": {
+ "version": "0.12.10",
+ "resolved": "https://registry.npmjs.org/@ffmpeg/ffmpeg/-/ffmpeg-0.12.10.tgz",
+ "integrity": "sha512-lVtk8PW8e+NUzGZhPTWj2P1J4/NyuCrbDD3O9IGpSeLYtUZKBqZO8CNj1WYGghep/MXoM8e1qVY1GztTkf8YYQ==",
+ "dependencies": {
+ "@ffmpeg/types": "^0.12.2"
+ },
+ "engines": {
+ "node": ">=18.x"
+ }
+ },
+ "node_modules/@ffmpeg/types": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/@ffmpeg/types/-/types-0.12.2.tgz",
+ "integrity": "sha512-NJtxwPoLb60/z1Klv0ueshguWQ/7mNm106qdHkB4HL49LXszjhjCCiL+ldHJGQ9ai2Igx0s4F24ghigy//ERdA==",
+ "engines": {
+ "node": ">=16.x"
+ }
+ },
+ "node_modules/@ffmpeg/util": {
+ "version": "0.12.1",
+ "resolved": "https://registry.npmjs.org/@ffmpeg/util/-/util-0.12.1.tgz",
+ "integrity": "sha512-10jjfAKWaDyb8+nAkijcsi9wgz/y26LOc1NKJradNMyCIl6usQcBbhkjX5qhALrSBcOy6TOeksunTYa+a03qNQ==",
+ "engines": {
+ "node": ">=18.x"
+ }
+ },
"node_modules/@floating-ui/core": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz",
@@ -2401,6 +2431,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/geist": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/geist/-/geist-1.3.0.tgz",
+ "integrity": "sha512-IoGBfcqVEYB4bEwsfHd35jF4+X9LHRPYZymHL4YOltHSs9LJa24DYs1Z7rEMQ/lsEvaAIc61Y9aUxgcJaQ8lrg==",
+ "peerDependencies": {
+ "next": ">=13.2.0 <15.0.0-0"
+ }
+ },
"node_modules/get-nonce": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
diff --git a/examples/sound-effects/video-to-sfx/package.json b/examples/sound-effects/video-to-sfx/package.json
index 6739320..f20d11b 100644
--- a/examples/sound-effects/video-to-sfx/package.json
+++ b/examples/sound-effects/video-to-sfx/package.json
@@ -9,6 +9,8 @@
"lint": "next lint"
},
"dependencies": {
+ "@ffmpeg/ffmpeg": "^0.12.10",
+ "@ffmpeg/util": "^0.12.1",
"@hookform/resolvers": "^3.6.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
@@ -46,6 +48,7 @@
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.1.5",
"framer-motion": "^11.2.10",
+ "geist": "^1.3.0",
"input-otp": "^1.2.4",
"lodash": "^4.17.21",
"lucide-react": "^0.395.0",