summaryrefslogtreecommitdiff
path: root/internal/media/ffmpeg/wasm.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/media/ffmpeg/wasm.go')
-rw-r--r--internal/media/ffmpeg/wasm.go201
1 files changed, 201 insertions, 0 deletions
diff --git a/internal/media/ffmpeg/wasm.go b/internal/media/ffmpeg/wasm.go
new file mode 100644
index 000000000..ff5506f2a
--- /dev/null
+++ b/internal/media/ffmpeg/wasm.go
@@ -0,0 +1,201 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package ffmpeg
+
+import (
+ "context"
+ "io"
+ "os"
+
+ ffmpeglib "codeberg.org/gruf/go-ffmpreg/embed/ffmpeg"
+ ffprobelib "codeberg.org/gruf/go-ffmpreg/embed/ffprobe"
+ "github.com/tetratelabs/wazero"
+ "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
+ "github.com/tetratelabs/wazero/sys"
+)
+
+// Use all core features required by ffmpeg / ffprobe
+// (these should be the same but we OR just in case).
+const corefeatures = ffprobelib.CoreFeatures |
+ ffmpeglib.CoreFeatures
+
+var (
+ // shared WASM runtime instance.
+ runtime wazero.Runtime
+
+ // ffmpeg / ffprobe compiled WASM.
+ ffmpeg wazero.CompiledModule
+ ffprobe wazero.CompiledModule
+)
+
+// Args encapsulates the passing of common
+// configuration options to run an instance
+// of a compiled WebAssembly module that is
+// run in a typical CLI manner.
+type Args struct {
+
+ // Optional further module configuration function.
+ // (e.g. to mount filesystem dir, set env vars, etc).
+ Config func(wazero.ModuleConfig) wazero.ModuleConfig
+
+ // Standard FDs.
+ Stdin io.Reader
+ Stdout io.Writer
+ Stderr io.Writer
+
+ // CLI args.
+ Args []string
+}
+
+// run will run the given compiled
+// WebAssembly module using given args,
+// using the global wazero runtime.
+func run(
+ ctx context.Context,
+ cmod wazero.CompiledModule,
+ args Args,
+) (
+ uint32, // exit code
+ error,
+) {
+ // Prefix module name as argv0 to args.
+ cargs := make([]string, len(args.Args)+1)
+ copy(cargs[1:], args.Args)
+ cargs[0] = cmod.Name()
+
+ // Create base module config.
+ modcfg := wazero.NewModuleConfig()
+ modcfg = modcfg.WithArgs(cargs...)
+ modcfg = modcfg.WithStdin(args.Stdin)
+ modcfg = modcfg.WithStdout(args.Stdout)
+ modcfg = modcfg.WithStderr(args.Stderr)
+
+ if args.Config != nil {
+ // Pass through config fn.
+ modcfg = args.Config(modcfg)
+ }
+
+ // Instantiate the module from precompiled wasm module data.
+ mod, err := runtime.InstantiateModule(ctx, cmod, modcfg)
+
+ if mod != nil {
+ // Ensure closed.
+ _ = mod.Close(ctx)
+ }
+
+ // Try extract exit code.
+ switch err := err.(type) {
+ case *sys.ExitError:
+ return err.ExitCode(), nil
+ default:
+ return 0, err
+ }
+}
+
+// compileFfmpeg ensures the ffmpeg WebAssembly has been
+// pre-compiled into memory. If already compiled is a no-op.
+func compileFfmpeg(ctx context.Context) error {
+ if ffmpeg != nil {
+ return nil
+ }
+
+ // Ensure runtime already initialized.
+ if err := initRuntime(ctx); err != nil {
+ return err
+ }
+
+ // Compile the ffmpeg WebAssembly module into memory.
+ cmod, err := runtime.CompileModule(ctx, ffmpeglib.B)
+ if err != nil {
+ return err
+ }
+
+ // Set module.
+ ffmpeg = cmod
+ return nil
+}
+
+// compileFfprobe ensures the ffprobe WebAssembly has been
+// pre-compiled into memory. If already compiled is a no-op.
+func compileFfprobe(ctx context.Context) error {
+ if ffprobe != nil {
+ return nil
+ }
+
+ // Ensure runtime already initialized.
+ if err := initRuntime(ctx); err != nil {
+ return err
+ }
+
+ // Compile the ffprobe WebAssembly module into memory.
+ cmod, err := runtime.CompileModule(ctx, ffprobelib.B)
+ if err != nil {
+ return err
+ }
+
+ // Set module.
+ ffprobe = cmod
+ return nil
+}
+
+// initRuntime initializes the global wazero.Runtime,
+// if already initialized this function is a no-op.
+func initRuntime(ctx context.Context) error {
+ if runtime != nil {
+ return nil
+ }
+
+ var cache wazero.CompilationCache
+
+ if dir := os.Getenv("GTS_WAZERO_COMPILATION_CACHE"); dir != "" {
+ var err error
+
+ // Use on-filesystem compilation cache given by env.
+ cache, err = wazero.NewCompilationCacheWithDir(dir)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Prepare config with cache.
+ cfg := wazero.NewRuntimeConfig()
+ cfg = cfg.WithCoreFeatures(corefeatures)
+ cfg = cfg.WithCompilationCache(cache)
+
+ // Instantiate runtime with prepared config.
+ rt := wazero.NewRuntimeWithConfig(ctx, cfg)
+
+ // Prepare default "env" host module.
+ env := rt.NewHostModuleBuilder("env")
+
+ // Instantiate host "env" module.
+ _, err := env.Instantiate(ctx)
+ if err != nil {
+ return err
+ }
+
+ // Instantiate wasi snapshot preview features in runtime.
+ _, err = wasi_snapshot_preview1.Instantiate(ctx, rt)
+ if err != nil {
+ return err
+ }
+
+ // Set runtime.
+ runtime = rt
+ return nil
+}