前回Rustで作ったpipeをwebsocketにつなぐコマンドを使って、音声のストリーミングをしてみます。
準備
前回作ったものはgithubにソースを置いてあります。
Rustはデバッグビルドとリリースビルドでは実行速度にかなり差があるということなので、必ずリリースビルドしたものを使用してください。
cargo build --release
それ以外のコマンドはUbuntu 18.04 では以下で揃います。
sudo apt install alsa-utils opus-tools
事前の確認
以下のようにすると私の環境ではUSBのWebcamの内蔵マイクの音声をイヤホンから出すことができます。(量子化16bit、サンプリングレート 48kHz, モノラル)
arecord -D plughw:2 -f S16_LE -r 48000 -c 1 -t raw \
| aplay -f S16_LE -r 48000 -c 1
aplay, arecordの引数は環境によって調整してください。
遅延を減らすために-B
オプションでバッファリングする時間指定します。マイクロ秒単位です。あまり減らしすぎるとbuffer underrun のエラーが発生しやすくなるので、いい感じに調整します。
arecord -D plughw:2 -f S16_LE -r 48000 -c 1 -B 10000 -t raw \
| aplay -f S16_LE -r 48000 -c 1 -B 20000
これでほぼリアルタイムになりました。
これをネットワークにまたがって行えばストリーミングになります。
非圧縮の音声データのストリーミング
受信側
こちらのマシンのIPアドレスは192.168.10.210です。ポートは8001を使用します。
./ws2stdout 0.0.0.0:8001 \
| aplay -f S16_LE -r 48000 -c 1 -B 20000
送信側
arecord -D plughw:2 -f S16_LE -r 48000 -c 1 -B 10000 -t raw \
| ./stdin2ws ws://192.168.10.210:8001
この方法だとわりと遅延が少ない状態でストリーミングすることができます。しかし非圧縮の音声データなので、100kB/s くらいネットワークの帯域を食います。
opusで圧縮した音声データのストリーミング
opusでのエンコード、デコードをはさんでみます。
受信側
./ws2stdout 0.0.0.0:8001 \
| opusdec - - \
| aplay -f S16_LE -r 48000 -c 1 -B 20000
送信側
arecord -D plughw:2 -f S16_LE -r 48000 -c 1 -B 10000 -t wav \
| opusenc --max-delay 10 --bitrate 32 - - \
| ./stdin2ws ws://192.168.10.210:8001
この方法だとネットワークの帯域はぐっと下がって4KB/s くらいです。
しかし遅延は1秒くらいになります。
opusdec コマンドに遅延に関する設定がないので、ここでバッファリングしているのでしょうか。
参考
昔私が書いた記事。
PCMのデータを自分でいじってデジタルオーディオを理解する