2020-02-11

PHPとRustを組み合わせて音声ファイルをエンコードする話

PHPerKaigi 2020 で、 PHP と Rust を組み合わせて音声ファイルをエンコードする話 というタイトルで発表を行いました。

サンプルコードは以下の通りです。

https://github.com/yaminoma/php-mp3-encoder

今回、スライドで話せなかった内容について補足していきたいと思っています。

LAME とは?

libmp3lame(LAME)とは、LAME MP3 Encoder のことで、昔からある MP3 のエンコーダーです。中身は C で書かれています。

MP3 で重要になってるのが特許ですが、大部分はドイツにある研究機関・Fraunhofer が保持していました。

しかし、2017 年 4 月 23 日に特許の保護期間が終了したことに伴い、現在では誰でも MP3 の技術を使うことが出来ます。

最近では。MP3 のエンコーダーやデコーダーを一から書く取り組みも流行りつつあります。

今回は、時間の都合上、既存のライブラリである LAME を使っています。

Bindgen

LAME は C で書かれたライブラリです。これを Rust で呼び出すには FFI を使う必要があります。

Rust で FFI をするにはいくつか方法がありますが、手取り速いのは Bindgen を使うことです。

Bindgen を使うことで、C のヘッダーファイルから Rust のファイルを生成してくれるので、実装がとても楽です。

今回は、私が数年前に作った libmp3lame-sys と呼ばれる crate を使っています。中身は libmp3lame を bindgen で FFI しただけです。

https://github.com/yaminoma/libmp3lame-sys

この libmp3lame-sys を使って、MP3 をエンコードする実装例に関しては。私が書いたコードをご覧ください。今回は詳しく解説しません。

https://github.com/yaminoma/php-mp3-encoder/blob/master/src/lib.rs#L83-L117

PHP で やりとりする方法

今回の本題ですが、PHP から Rust の音声ファイルをエンコードする際にどうやってファイルの受け渡しをすればよいでしょうか。

今回は、fread で WAV ファイルを読み取り、そのバイナリ文字列を Unsigned Int のポインタにコピーして、Rust 側に渡しています。

本来なら Rust でエンコードした MP3 のデータをポインタとして PHP 側に返すことも検討しましたが、簡略化のため、今回は Rust 側でファイル生成まで行い、そのパスを char で返すような実装を行いました。

PHP の FFI ですが、ドキュメントに書かれている内容が非常に薄く、ソースコードを読まないとわからない部分が多かったため、かなり苦戦しました。

実用性について

現在、私はメディア関連のお仕事をしていて、業務で配信システムを作っているのですが、その中で得た知見を生かして、今回このような発表をさせていただきました。

PHP FFI は、まだ完全に成熟された機能であるとは言い切れませんが、FFI を使うことにより、既存のライブラリを PHP に組み込むことができるので、夢が広がりますね。