Docker越しにマイクを使いたい話
例えば(PyAudioなどで)音声入力を受け付けるPythonサーバを書いたとして、Docker上に書いたサーバを構築するとマイクが無い!
と怒られることがあります。
(この記事ではDockerを立ち上げている手元のWindowsやMacをHostOS、と呼ぶことにします)
そもそもDockerはHostOSから何を受け取っているか
- HostOSのネットワークの特定ポートの通信はDocker側に横流ししてね!と設定する
- 同じようにCUDAを使うからグラフィックボードのアクセスはDocker側にも許可してね!と設定する
などはDockerを使った環境で広く使われているテクニックです。
一方でDocker上のプログラムでHostOSから見えているWebカメラを使おうと思うと、結構大変です。(例:Windowsなら https://zenn.dev/pinto0309/articles/e1432253d29e30 などを参照ください)
同じようにDocker上のプログラムでHostOSから見えているマイクを使おうと思うと、
--privileged -v /dev/bus/usb:/dev/bus/usb
こんな感じでUSBマイクを掴むように案内があります。
ですがこれはHostOSがLinux系の時にしか使えません
じゃあどうすれば良いの…
HostOSのブラウザ経由でマイクを掴む
適当なWebRTCのアクセスからマイクを掴んで入力音声をWebsocketで飛ばすWebサイトを作り、HostOSのブラウザでサイトにアクセスします。
するとマイクを探す主体はDocker上のPythonサーバではなく、ブラウザを起動しているHostOSになります。よって接続されているマイクが見つけられます。
余談、Streamlitは何をしているのか
PythonでWebのフロントエンドが構築できるStreamlitなどの仕組みがあります。
これはPythonのコードをtranscompileしてjavascriptやhtmlを生成している、訳ではなくて内部でPythonのサーバを組み立て て、そのサーバと通信するhtml+javascriptのサイトをホスティング する形で起動しています。
これはどういうことかと言う例を出します。
Streamlit側でPyaudioを使ってマイクを取得すると、それはStreamlit のrunを実行したOSのマイク を掴みます。
(つまり、10台のPCがStreamlitのサイトにアクセスしても、それぞれのPCに刺さっているマイクを使うのではなくて、Streamlitの サーバを建てているPCのマイクを10台のPCが奪い合う ことになっちゃいます)
それは困るよね…と思ったあなたは別のソリューションを考えましょう。
例えば下記では(この辺の面倒くさい部分を隠蔽して)Streamlit用のWebRTCライブラリを使っていますが、内部的にはhtml側の方にWebRTC接続を仕込んでいます。
追記:Streamlit-webrtcなどをDockerで使うときにWebRTCの使用ポートを指定しておく
こういう記述をdocker-compose.ymlに追記しておく必要があります。
sysctls:
net.ipv4.ip_local_port_range: "40000 40010"
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8501:8501"
sysctls:
net.ipv4.ip_local_port_range: "40000 40010"
volumes:
- .:/app
Discussion