summaryrefslogtreecommitdiff
path: root/internal/media/ffmpeg
diff options
context:
space:
mode:
Diffstat (limited to 'internal/media/ffmpeg')
-rw-r--r--internal/media/ffmpeg/cache.go46
-rw-r--r--internal/media/ffmpeg/ffmpeg.go92
-rw-r--r--internal/media/ffmpeg/ffprobe.go92
-rw-r--r--internal/media/ffmpeg/pool.go75
4 files changed, 305 insertions, 0 deletions
diff --git a/internal/media/ffmpeg/cache.go b/internal/media/ffmpeg/cache.go
new file mode 100644
index 000000000..371d409dc
--- /dev/null
+++ b/internal/media/ffmpeg/cache.go
@@ -0,0 +1,46 @@
+// 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 (
+ "os"
+
+ "github.com/tetratelabs/wazero"
+)
+
+// shared WASM compilation cache.
+var cache wazero.CompilationCache
+
+func initCache() {
+ if cache != nil {
+ return
+ }
+
+ if dir := os.Getenv("WAZERO_COMPILATION_CACHE"); dir != "" {
+ var err error
+
+ // Use on-filesystem compilation cache given by env.
+ cache, err = wazero.NewCompilationCacheWithDir(dir)
+ if err != nil {
+ panic(err)
+ }
+ } else {
+ // Use in-memory compilation cache.
+ cache = wazero.NewCompilationCache()
+ }
+}
diff --git a/internal/media/ffmpeg/ffmpeg.go b/internal/media/ffmpeg/ffmpeg.go
new file mode 100644
index 000000000..357289fcc
--- /dev/null
+++ b/internal/media/ffmpeg/ffmpeg.go
@@ -0,0 +1,92 @@
+// 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"
+
+ ffmpeglib "codeberg.org/gruf/go-ffmpreg/embed/ffmpeg"
+ "codeberg.org/gruf/go-ffmpreg/util"
+ "codeberg.org/gruf/go-ffmpreg/wasm"
+
+ "github.com/tetratelabs/wazero"
+ "github.com/tetratelabs/wazero/api"
+ "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
+)
+
+// InitFfmpeg initializes the ffmpeg WebAssembly instance pool,
+// with given maximum limiting the number of concurrent instances.
+func InitFfmpeg(ctx context.Context, max int) error {
+ initCache() // ensure compilation cache initialized
+ return ffmpegPool.Init(ctx, max)
+}
+
+// Ffmpeg runs the given arguments with an instance of ffmpeg.
+func Ffmpeg(ctx context.Context, args wasm.Args) (uint32, error) {
+ return ffmpegPool.Run(ctx, args)
+}
+
+var ffmpegPool = wasmInstancePool{
+ inst: wasm.Instantiator{
+
+ // WASM module name.
+ Module: "ffmpeg",
+
+ // Per-instance WebAssembly runtime (with shared cache).
+ Runtime: func(ctx context.Context) wazero.Runtime {
+
+ // Prepare config with cache.
+ cfg := wazero.NewRuntimeConfig()
+ cfg = cfg.WithCoreFeatures(ffmpeglib.CoreFeatures)
+ cfg = cfg.WithCompilationCache(cache)
+
+ // Instantiate runtime with our config.
+ rt := wazero.NewRuntimeWithConfig(ctx, cfg)
+
+ // Prepare default "env" host module.
+ env := rt.NewHostModuleBuilder("env")
+ env = env.NewFunctionBuilder().
+ WithGoModuleFunction(
+ api.GoModuleFunc(util.Wasm_Tempnam),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32},
+ []api.ValueType{api.ValueTypeI32},
+ ).
+ Export("tempnam")
+
+ // Instantiate "env" module in our runtime.
+ _, err := env.Instantiate(context.Background())
+ if err != nil {
+ panic(err)
+ }
+
+ // Instantiate the wasi snapshot preview 1 in runtime.
+ _, err = wasi_snapshot_preview1.Instantiate(ctx, rt)
+ if err != nil {
+ panic(err)
+ }
+
+ return rt
+ },
+
+ // Per-run module configuration.
+ Config: wazero.NewModuleConfig,
+
+ // Embedded WASM.
+ Source: ffmpeglib.B,
+ },
+}
diff --git a/internal/media/ffmpeg/ffprobe.go b/internal/media/ffmpeg/ffprobe.go
new file mode 100644
index 000000000..0b9660e60
--- /dev/null
+++ b/internal/media/ffmpeg/ffprobe.go
@@ -0,0 +1,92 @@
+// 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"
+
+ ffprobelib "codeberg.org/gruf/go-ffmpreg/embed/ffprobe"
+ "codeberg.org/gruf/go-ffmpreg/util"
+ "codeberg.org/gruf/go-ffmpreg/wasm"
+
+ "github.com/tetratelabs/wazero"
+ "github.com/tetratelabs/wazero/api"
+ "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
+)
+
+// InitFfprobe initializes the ffprobe WebAssembly instance pool,
+// with given maximum limiting the number of concurrent instances.
+func InitFfprobe(ctx context.Context, max int) error {
+ initCache() // ensure compilation cache initialized
+ return ffprobePool.Init(ctx, max)
+}
+
+// Ffprobe runs the given arguments with an instance of ffprobe.
+func Ffprobe(ctx context.Context, args wasm.Args) (uint32, error) {
+ return ffprobePool.Run(ctx, args)
+}
+
+var ffprobePool = wasmInstancePool{
+ inst: wasm.Instantiator{
+
+ // WASM module name.
+ Module: "ffprobe",
+
+ // Per-instance WebAssembly runtime (with shared cache).
+ Runtime: func(ctx context.Context) wazero.Runtime {
+
+ // Prepare config with cache.
+ cfg := wazero.NewRuntimeConfig()
+ cfg = cfg.WithCoreFeatures(ffprobelib.CoreFeatures)
+ cfg = cfg.WithCompilationCache(cache)
+
+ // Instantiate runtime with our config.
+ rt := wazero.NewRuntimeWithConfig(ctx, cfg)
+
+ // Prepare default "env" host module.
+ env := rt.NewHostModuleBuilder("env")
+ env = env.NewFunctionBuilder().
+ WithGoModuleFunction(
+ api.GoModuleFunc(util.Wasm_Tempnam),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32},
+ []api.ValueType{api.ValueTypeI32},
+ ).
+ Export("tempnam")
+
+ // Instantiate "env" module in our runtime.
+ _, err := env.Instantiate(context.Background())
+ if err != nil {
+ panic(err)
+ }
+
+ // Instantiate the wasi snapshot preview 1 in runtime.
+ _, err = wasi_snapshot_preview1.Instantiate(ctx, rt)
+ if err != nil {
+ panic(err)
+ }
+
+ return rt
+ },
+
+ // Per-run module configuration.
+ Config: wazero.NewModuleConfig,
+
+ // Embedded WASM.
+ Source: ffprobelib.B,
+ },
+}
diff --git a/internal/media/ffmpeg/pool.go b/internal/media/ffmpeg/pool.go
new file mode 100644
index 000000000..9f6446be3
--- /dev/null
+++ b/internal/media/ffmpeg/pool.go
@@ -0,0 +1,75 @@
+// 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"
+
+ "codeberg.org/gruf/go-ffmpreg/wasm"
+)
+
+// wasmInstancePool wraps a wasm.Instantiator{} and a
+// channel of wasm.Instance{}s to provide a concurrency
+// safe pool of WebAssembly module instances capable of
+// compiling new instances on-the-fly, with a predetermined
+// maximum number of concurrent instances at any one time.
+type wasmInstancePool struct {
+ inst wasm.Instantiator
+ pool chan *wasm.Instance
+}
+
+func (p *wasmInstancePool) Init(ctx context.Context, sz int) error {
+ p.pool = make(chan *wasm.Instance, sz)
+ for i := 0; i < sz; i++ {
+ inst, err := p.inst.New(ctx)
+ if err != nil {
+ return err
+ }
+ p.pool <- inst
+ }
+ return nil
+}
+
+func (p *wasmInstancePool) Run(ctx context.Context, args wasm.Args) (uint32, error) {
+ var inst *wasm.Instance
+
+ select {
+ // Context canceled.
+ case <-ctx.Done():
+ return 0, ctx.Err()
+
+ // Acquire instance.
+ case inst = <-p.pool:
+
+ // Ensure instance is
+ // ready for running.
+ if inst.IsClosed() {
+ var err error
+ inst, err = p.inst.New(ctx)
+ if err != nil {
+ return 0, err
+ }
+ }
+ }
+
+ // Release instance to pool on end.
+ defer func() { p.pool <- inst }()
+
+ // Pass args to instance.
+ return inst.Run(ctx, args)
+}