この記事は 夏のブログリレー 32日目の記事です。
FFmpeg の提供する機能全体からなる集合を とします。
より、 を実装することで、題意を満たすことができました。
いかがでしたか?
こんばんは、25B のみどりむしと申します。
情報工学系の 200 番台には「プログラミング創造演習」という講義があり、パソカタをするだけことで単位が貰えます (しかも結構いい点数が来たり来なかったりします)。なんとも夢のような話です。
そこで私は「pure-Go によるマルチメディアトランスコーディングのためのオープンソース FFmpeg 代替アーキテクチャの開発」テーマで開発に取り組むことにしました。
実際にはこんな大層なものが 2 四半期で終わるわけがないので、音声コーデックの、そしてそのさらに一部に限って実装することにしていました (FFmpeg 代替とか言ってますがタイトルでカッコつけただけです。)。
動機
私は traP で traQ というチャットツールを開発しているのですが (紛らわしいですね)、その中で .m4a の波形のプレビューをしたいという要望がありました (波形画像を生成するために Linear PCM に変換する必要があるため,現在は .wav や .mp3 のみ対応しています)。
しかし、Go にはそこそこ動きそうな .m4a のデコーダライブラリがなさそうだったため、FFmpeg を導入することも考えられたのですが、せっかくサーバーが pure-Go なのにもったいないといったような議論があり一時保留となりました。
それを機に pure-Go で音声コーデックを実装することに関して興味を持ったという流れになります (先にオチを話すと、.m4a に記録できるコーデックのうち ACC の仕様書が学生には高額すぎたため、結局 .m4a は実装してません。)。
また、個人的に Go はあまり好きではなかったのですが、そういえば大してなにも作っていないのに毛嫌いするのもよくないかと思い、もう少し真面目に Go を触ってみることにしました (結果的には今は「普通」くらいに思っています。Python よりちょっと好きくらい)。
作ったもの
どーん
最初はポリレポにしようと思って一丁前に Organization まで作ったのですが、複数リポジトリの同期リリースが面倒すぎてやめました (ちょっと時期尚早だったね。)。
AI 君と特大リファクタリングを行っている最中に飽きて放り投げたので結構コードがひどいことになっていますが、た、多分動くと思います...... (やる気が戻ってきたらまたメンテナンスします。)
創造演習当時のバージョンを
go install github.com/godexture/godec@latestでインストールもできます (リリースをサボっているので今の master とは若干サブコマンドとかが違います)。
あとは
godec convert in.mp3 out.wav
godec play out.wavとかできます (再生には https://github.com/ebitengine/oto を使ってます。God Library.)。
楽しくなって delay とか reverb とか resample とか FFT convolver とかいろんなフィルタつくりました。
godec play out.flac --filter trim --filter fade:fade-in=10s とか
godec convert \
in.wav out.wav \
--filter "split=mixer[in=1,out=2]" \
--filter reverb \
--filter delay \
--filter "join=mixer[in=2,out=1]" \
--wire "split.in0=@in" \
--wire "reverb.in=split.out0" \
--wire "delay.in=split.out1" \
--wire "join.in0=reverb.out" \
--wire "join.in1=delay.out" \
--wire "@out.in=join.out0"とかできます。たのしい!
Go の API もあります。Go としてどうなのかはわかりません。
例によって今の master では動きません (:person_facepalming:)
package main
import (
"context"
"fmt"
"os"
"runtime"
"runtime/debug"
"runtime/pprof"
"github.com/godexture/filter-audio"
"github.com/godexture/sdk/dsp"
"github.com/godexture/sdk/profiling"
"github.com/godexture/sdk/timer"
godec "github.com/godexture/core"
"github.com/godexture/core/domain/media"
"github.com/godexture/core/routing"
flacFormat "github.com/godexture/format-flac"
_ "github.com/godexture/codec-flac"
_ "github.com/godexture/codec-mp3"
_ "github.com/godexture/codec-pcm"
_ "github.com/godexture/format-flac"
_ "github.com/godexture/format-mp3"
_ "github.com/godexture/format-wav"
)
const targetCodec = media.CodecFLAC
func main() {
inputPath := os.Args[1]
outputPath := os.Args[2]
inputFile, err := os.Open(inputPath)
if err != nil {
fmt.Printf("Failed to open input: %v\n", err)
return
}
defer inputFile.Close()
outputFile, err := os.Create(outputPath)
if err != nil {
fmt.Printf("Failed to create output: %v\n", err)
return
}
defer outputFile.Close()
negotiator := godec.NewNegotiator()
spec := routing.ConversionSpec{
Input: inputFile,
Output: outputFile,
TargetCodec: targetCodec,
Filters: []routing.FilterSpec{
{
Config: filter.MustNewResampleConfig(
filter.WithSampleRate(48000),
),
},
},
MuxConfig: flacFormat.MustNewMuxerConfig(),
}
geometry, err := negotiator.NegotiateConversion(context.Background(), spec)
if err != nil {
fmt.Printf("Failed to negotiate conversion: %v\n", err)
return
}
builder := godec.NewBuilder()
conversion, err := builder.Build(geometry)
if err != nil {
fmt.Printf("Failed to build pipeline: %v\n", err)
return
}
defer conversion.Close()
if err := conversion.Run(context.Background()); err != nil {
fmt.Printf("Pipeline execution failed: %v\n", err)
return
}
}実は SIMD を有効にすると一部 SIMD で動きます。それなりにパフォーマンスチューニングを頑張りました。
あとは調子に乗って Web で動くエディタも作りました (半分くらい Claude と Codex が)。
いろいろできて結構楽しいです。
Go なので WASM で動かすこともできます。遅いけど。


サポート
Formats (Demultiplexing / Multiplexing)
- WAV, RIFF (Demux, Mux)
- U8, S8, S16, S32, S64, F32, F64
- Big Endian, Little Endian
- RF64
- FLAC (Demux, Mux)
- MP3 (Demux)
Metadata (Unmarshalling / Marshalling)
- ID3 (Unmarshall, Marshall)
- ID3v1
- ID3v2.1, ID3v2.2, ID3v2.3, ID3v2.4
- Vorbis Comments (Unmarshall, Marshall)
Codecs (Decoding / Encoding):
- PCM (Decode, Encode)
- Linear PCM
- G.711: μ-Law, A-Law
- ADPCM
- MS ADPCM
- IMA ADPCM
- FLAC (Decode, Encode)
- Full Support!!
- MP3 (Decode)
- MPEG-1 Audio Layer-3
Filters (Processing):
- Resample
- Remix
- Gain
- Normalize
- Fade
- Gate
- Trim
- Retime
- Convert Format
- Remove DC Offset
- Compress
- Equalize
- Reverb
- Delay
- Convolve (FFT)
感想
Go ちょっと楽しいかもって思いました。
でもバイナリ操作したりするなら流石に Rust とか C++ のほうがいいね。
GC 便利だけどやっぱりパフォーマンスチューニングとかしようとすると結局 GC のお気持ちを考えてご機嫌取りしないといけないのがつらいです、私は GC 無い方が好きかな、嘘です、参照カウントくらいは欲しいかも。
でも Go にもゼロアロケーションを謳ってるライブラリとかあるらしくてすごい。正気?
あと仕様書が有料ってちょっと嫌な気持になりますね。いやしょうがないけど、こっちも学生なので許してほしい。というか工業大学って名前なら ISO/IEC とか JIS の規格書くらい全部タダで読めてほしくないですか? あ今は工業大学じゃないんでしたねそういえば。(ISO/IEC 14882:2024 の寄贈待ってます♡)
学生に使ってほしい人は無料で仕様書を公開するといいと思います (?)
FLAC とかすべてが無料で読めてとても楽しかったです。
皆様もよき FFmpeg ライフを。
あと創造演習はいいですよ、ぜひ履修してください。
二時間前くらいに書き始めてなんとか間に合いました。
殴り書きですみません。
明日のブログリレーの投稿者は 25/jk と @ramdos です、お楽しみに。
