【scrcpy】Androidスマホの画面を
Mac PCに映すには?
(開発者向けオプション)

  • Bluetoothだけでスマホの画面をパソコンに映す方法は、ほぼありません。映像を送り続けるには帯域が足りないからです。
  • scrcpy は画面を映しているのではなく、起動のたびに端末へ小さなプログラムを送り込み、端末自身に録らせて送り返させています。
  • 開発者向けオプションとUSBデバッグは、その通り道の入口です。端末側は、この機能を開発専用だと説明します。
  • 手元のMacでは adb がUSBの初期化から戻ってこず、画面が出る以前のところで止まりました。

関連記事

1. Bluetoothでは映せない

1. Bluetoothでは映せない

AndroidのスマホをMacBookに映せないか、と考えたのが始まりでした。
Bluetoothでキーボードもイヤホンもつながるのだから、画面も飛ばせるのではないか。
ところが調べてみると、Bluetoothだけで画面をリアルタイムに映す方法は、ほとんど見つかりません。

理由は帯域です。
Bluetoothの古くからある転送方式は、速い設定でも実効で毎秒2メガビットほど1。
一方、scrcpy が既定で流す映像は毎秒8メガビットです2。

音声を1本通すのと、動画を通し続けるのとでは、必要な量が桁で違います。
イヤホンで音楽が途切れずに聴けることと、画面が滑らかに映ることは、同じ土俵の話ではありませんでした。

もうひとつ効いているのが、スマホには映像を外へ出す口が用意されていないことが多い点です。
USB-Cから映像信号をそのまま出せる仕組みはありますが、対応するかどうかは機種によります3。
パソコンのHDMI端子のように挿せば映るものだと思っていると、ケーブルをつないでもうんともすんとも言いません。

Pixelの場合、その仕組みが使えるようになったのはPixel 8からでした4。
しかも端子の側が対応していただけで、実際に外部の画面へ出せるようになったのは、Android 14の途中で解禁されてからです5。
今回つないだのはPixel 5なので、そもそも対象外。

2. 映しているのではなく、録らせて送らせている

では、scrcpy(screen copy:画面の複写)は何をしているのか。
名前のとおり画面を複写しているのですが、複写する主体がパソコンではないところが、思っていたのと違いました。

2.1. scrcpy は起動のたびに端末へプログラムを送り込む

起動したときのログを見て、意外に思いました。

scrcpy-server: 1 file pushed, 0 skipped. 3.1 MB/s (733706 bytes in 0.228s)
[server] INFO: Device: [Google] google Pixel 5 (Android 14)

最初にやっているのは、733,706バイトのファイルを端末へ転送することです6。
これが端末の中で動いて、自分の画面を録画し、圧縮して送り返してきます7。

Mac側のログは「Renderer: metal」「Texture: 1080×2340」で終わります。
受け取った映像を、端末の解像度そのままの大きさで描いているだけ。
汗をかいているのは端末のほうで、パソコンは表示係です。

だから、端末に常駐アプリを入れる必要がありません8。
送り込まれたファイルは一時置き場に置かれ、ウィンドウを閉じれば動作を終えます。
アプリの一覧にも出てきません。

作者が挙げている目安は、毎秒30枚から120枚、遅れは35ミリ秒から70ミリ秒です9。
この数字で収まるのは、端末の中にある映像処理の回路をそのまま使って圧縮しているからです。
パソコン側でいちから映像を組み立て直していたら、ここまで詰められません。

2.2. 開発者向けオプションを出す手間の意味

2.2. 開発者向けオプションを出す手間の意味

この転送を通すために、端末側で開けておく必要があるのがUSBデバッグです。
そして、その設定項目は初めから見えるところにありません。
設定のデバイス情報を開き、ビルド番号を7回続けてタップすると、ようやく開発者向けオプションが現れます10。

なぜ、こんな儀式めいた手順になっているのか。
USBデバッグを許可するときに端末が出す説明文が、そのまま答えになっています。
「USBデバッグは開発専用に設計されています。パソコンとデバイスの間でデータをコピーする場合や、アプリを通知なしでデバイスにインストールする場合、ログデータを読み取る場合に使用できます」。

通知なしでアプリを入れられて、ログを読める11。
画面のミラーリングは、その強い権限の副産物として成り立っています。
7回タップは、うっかり辿り着かないようにするための距離でした。

3. つないでも映らないときに何が起きているか

ケーブルを挿しても、すぐには映りません。
端末とパソコンが互いを認めるやりとりが先にあり、そこを通らないと一歩も進みません。

3.1. RSAキーの確認は、パソコン側の身分証

USBデバッグをオンにしてつなぐと、端末に確認のダイアログが出ます。
「このパソコンのRSAキーのフィンガープリント」という見出しで、英数字が2文字ずつ並んだ文字列が示されます12。

これは、パソコン側が初回に作った鍵の対のうち、公開鍵を要約したものです13。
手元のMacでも、初めてadbが動いたときにホームフォルダの中へ鍵のファイルが2つ作られていました14。
端末は、この鍵を見て、さっきと同じパソコンだと判断します。

ダイアログの「このパソコンからのUSBデバッグを常に許可する」にチェックを入れると、端末側がその鍵を覚えます。
覚えるのは端末で、覚えられるのはパソコン。
だから、別のパソコンにつなぎ替えれば、同じダイアログがもう一度出ます。

3.2. adb が USB の初期化から戻ってこなかった

ここまで書くと順調に進んだように見えますが、実際には手前でしばらく止まりました。
端末を触る以前に、Mac側の adb(Android Debug Bridge:端末とパソコンをつなぐ橋渡し)が起動しなかったのです。

adb devices と打つと「ADB server didn’t ACK」と返ってきます。
プロセスの一覧を見ると、サーバー自身は生きています。
それなのに、待ち受けているはずのポートには誰もいない。

手がかりは2つありました。
鍵のファイルが置かれるはずのフォルダが空のままだったことと、止まっている位置を調べたら、USB機器を開こうとしたところから戻ってきていなかったことです。

adbは、USBの初期化を済ませてから鍵を作り、その後でポートの待ち受けを始めます。
一列に並んだ処理なので、最初でつかえると後ろが全部動きません。
このMacに挿さっているハブか内蔵機器のどれかが、開こうとしたときに応答しなかったようです15。

回避は環境変数ひとつでした。
USBの扱いをlibusbという別の実装に切り替えると、鍵が作られ、ポートも開き、端末が見えました16。

ADB_LIBUSB=1 adb devicesCode language: Bash (bash)

腑に落ちないのは、公式の資料にはバージョン34以降のadbならWindows以外でlibusbが既定だと書かれていることです17。
手元は37.0.1で、既定のまま起動すると止まりました。
止まっていた場所がmacOS独自の実装の中だったので、少なくともこの環境では既定が切り替わっていません。

つないだのに映らないとき、疑う先は3つあります。
ケーブルが充電専用でデータが通っていないか、端末側の許可のダイアログが出たまま放置されているか、そもそもパソコン側のadbが立ち上がっていないか。
最後のものだけは、端末をいくら触っても直りません。

スマホの画面をパソコンに出す、と言葉にすると、映像を引っ張ってくる作業に聞こえます。
実際にやっているのは、開発者のために開けられた通用口から入って、端末に自分の姿を録ってもらうことでした。
仕組みを知ってから見ると、7回タップも鍵の確認のダイアログも、面倒な手続きではなく、そこが裏口である印に見えてきます。

  1. Bluetooth 2.0で導入されたEDRは変調方式の改良で生の3Mbpsを出しますが、誤り訂正やヘッダを差し引いた実効は約2.1Mbpsとされます – Bluetooth 2 with Enhanced Data Rate, EDR
  2. 既定の映像ビットレートは8Mbpsで、–video-bit-rate(短縮形は -b)で変更できます – scrcpy/doc/video.md
  3. USB-Cの端子にDisplayPortの信号を流す、DisplayPort Alt Modeという仕組みです – The Pixel 8 series supports display output over USB-C despite what Google says
  4. Android 16のデスクトップモードもDisplayPort Alt Modeを備えたPixel 8以降が条件で、それ以前のPixelは外部ディスプレイへ映像を出せません – How to Use Android 16 Desktop Mode with a Pixel Phone and USB-C Hub or Adapter
  5. Android 14 QPR3 Beta 2で、Pixel 8の外部ディスプレイ出力が有効になりました – The Pixel 8 is finally getting support for display output over USB-C
  6. scrcpyのクライアントはadb経由で scrcpy-server.jar を /data/local/tmp へ転送し、app_process で起動します – Android Server | Genymobile/scrcpy | DeepWiki
  7. 端末側はMediaCodecで画面を取り込み、既定ではH.264で符号化した生のストリームを、adbのトンネルを通して送ります – scrcpy for developers
  8. scrcpyはroot権限も、端末側へのアプリのインストールも必要としません – GitHub – Genymobile/scrcpy: Display and control your Android device
  9. 公式のREADMEは、性能を30〜120fps、遅延を35〜70msと記しています – scrcpy/README.md at master
  10. ビルド番号を7回タップすると開発者向けオプションが解放される、とAndroidの公式ドキュメントが説明しています – Configure on-device developer options | Android Developers
  11. adbはアプリのインストールやデバッグ、端末上のUnixシェルの実行までを行うコマンドラインツールです – Android Debug Bridge (adb) | Android Developers
  12. Android 4.2.2以降、接続したパソコンのRSAフィンガープリントを利用者が確認して承認する画面が出ます – Android Debug Bridge – Wikipedia
  13. 表示されるフィンガープリントは、公開鍵のMD5を取ったものです – Check the computer’s RSA key fingerprint
  14. 鍵は .android フォルダに adbkey と adbkey.pub として置かれ、無ければadbが自動で作ります – Android Debug Bridge – Wikipedia
  15. macOSでadbが端末を安定して検出できないという不具合は、以前から報告が続いています – adb fails to reliably detect devices in MacOS Sierra
  16. ADB_LIBUSB を 1 にしてサーバーを起動するとlibusbバックエンドが使われます。実際に切り替わったかは adb host-features の出力に libusb が出るかで確かめられます – Android Debug Bridge (adb) | Android Developers
  17. 公式のリリースノートは、adb v34以降Windows以外ではlibusbが既定になったと記しています – SDK Platform Tools release notes | Android Developers