💬

Docker越しにマイクを使いたい話

2024/11/28に公開

例えば(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のサイトをホスティング する形で起動しています。

https://docs.streamlit.io/develop/concepts/architecture/architecture

これはどういうことかと言う例を出します。

Streamlit側でPyaudioを使ってマイクを取得すると、それはStreamlit のrunを実行したOSのマイク を掴みます。
(つまり、10台のPCがStreamlitのサイトにアクセスしても、それぞれのPCに刺さっているマイクを使うのではなくて、Streamlitの サーバを建てているPCのマイクを10台のPCが奪い合う ことになっちゃいます)
それは困るよね…と思ったあなたは別のソリューションを考えましょう。

例えば下記では(この辺の面倒くさい部分を隠蔽して)Streamlit用のWebRTCライブラリを使っていますが、内部的にはhtml側の方にWebRTC接続を仕込んでいます。

https://zenn.dev/yag_ays/articles/368ebbb637c102

追記: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