Linux
rust
streaming
Opus
ALSA

Websocketで簡単に音声のストリーミングをしてみる

前回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のデータを自分でいじってデジタルオーディオを理解する