重要:この記事で紹介する方法は、WindowsがiPadを外部ディスプレイとして認識する方法ではありません。HDMIキャプチャでWindows画面を取得し、Raspberry Pi Zero WHからSafariへ画像として配信する方式です。
※旧題:iPadをWindowsの疑似サブディスプレイにする方法 – Raspberry Pi Zero WHで実機検証
以前、spacedeskを使いWindowsの画面をiPadに映す方法を紹介しました。
Windows PCに専用ソフトをインストールせず、Raspberry Pi Zero WHとUSB HDMIキャプチャを使って、iPadを疑似的なサブディスプレイとして利用する方法を紹介します。
今回はそこから一歩進めて、Windows側に専用ソフトをインストールしないという条件で、別の方法を実際に作ってみました。完成までにかかった時間は3日間です。
JPEGへの再エンコードによってRaspberry Pi Zero WHのCPU使用率が90%以上になる問題、SSHを終了すると処理が止まる問題、SafariでMJPEGストリームをうまく表示できない問題、長時間動かすとキャプチャが停止する問題など、実際に動かしたことで初めて分かった問題がいくつもありました。
その結果として、FFmpeg、v4l-utils、Python標準ライブラリ、この構成まで追い込むことができました。記事の内容としては「こうしたらできました」ではなく「どうしてこの構成」について見てもらえればと思います。

結論:iPadを疑似的なサブディスプレイとして利用できた
Windowsへ専用ソフトをインストールせず、Raspberry Pi Zero WHを経由して、iPadのSafariへWindowsの画面を表示できました。
構成は次のようになります。
Windows PC → HDMIキャプチャ → Raspberry Pi Zero WH → HTTP → iPadのSafari
Raspberry Pi Zero WHがWindowsからディスプレイとして認識されるわけではありません。Windowsの画面をHDMIキャプチャで取得し、Raspberry Pi Zero WH上のFFmpegで画像として保存します。
その画像をPython標準ライブラリのhttp.serverで配信し、iPadのSafariから定期的に取得して表示します。つまり、一般的なHDMIディスプレイのように映像信号をそのまま表示しているわけではありません。
Windowsの画面を連続した静止画として取得し、それをブラウザで更新しするという構造です。
Windows複製モード

Windowsの「複製」機能を利用すると、メインディスプレイと同じ画面をiPadへ表示できます。
遅延はありますが、PDFやWordなどの資料を表示したり、軽いプレゼンテーションで補助画面として利用したりする用途には使えました。
Windows拡張モード

今回の仕組みでもWindowsの「表示画面を拡張する」機能を利用できます。
私の環境では、Windowsのデスクトップを横断してPowerShellのウィンドウやマウスポインターを移動できました。ただし、iPad側に表示されるのはRaspberry Pi Zero WHが取得した画像です。
そのため、通常のディスプレイ接続と比較すると画面更新には遅延があります。常時表示するターミナルや監視画面など、動きの少ない情報を置く用途に向いています。。
iPhoneでも拡張モードで表示できる

今回の仕組みはiPad専用の通信方式を使用しているわけではありません。
Raspberry PiからHTTPで画像を配信し、ブラウザで表示しています。そのため、同じURLへアクセスすればiPhoneでも表示できます。
ただし、今回の検証はSafariを中心に行っています。FirefoxやDuckDuckGoなど、別のブラウザでは同じ結果にならない可能性があります。
この方法で何ができるのか
この構成で重要なのは、「iPadを本物のディスプレイにする」ことではありません。
専用アプリをインストールできない、またはインストールしたくない端末へ、ブラウザだけでWindowsの画面を表示することに意味があります。
例えば、次のような用途です。
- PowerShellやターミナルを常時表示する
- システム監視画面を表示する
- DiscordやTeamsを表示しておく
- PDFやWordなどの資料を表示する
- 作業中の参考資料を別画面へ置く
- 余っているiPadを情報表示端末として再利用する
反対に、次の用途には向いていません。
- PCゲーム
- 動画視聴
- 高速なマウス操作
- 映像編集
- リアルタイム映像の監視
これは、このシステムが動画ストリーミングではなく、画像を一定間隔で更新する方式だからです。
この記事で分かること
記事では、実際にRaspberry Pi Zero WHへ環境を構築し、Windowsの画面をiPadへ表示するまでに行った検証をまとめています。
- Windows側へ専用ソフトを入れない理由
- Raspberry Pi Zero WHを選んだ理由
- HDMIキャプチャの確認方法
- FFmpegで画面を取得する方法
- JPEGの再エンコードを避けてCPU負荷を下げる方法
- Python標準ライブラリだけでHTTPサーバーを動かす方法
- SafariでMJPEG方式を採用しなかった理由
- Fetch APIで画像を更新する方法
- systemdで常駐サービス化する方法
- 長時間運用で発生した問題
- 最初の構成が失敗した理由
- 最終的に構成を3つの主要ソフトウェアまで減らした理由
これらについて説明をしていきたいと思います。
コマンドをそのままコピーして動かすだけではなく、なぜそのコマンドが必要なのかも説明します。
検証環境
今回の記事は、私が実際にRaspberry Pi Zero WHへOS Lite 32bitをインストールし、USB HDMIキャプチャを接続して検証した環境をもとにしています。
| 項目 | 使用環境 |
|---|---|
| ホストPC | Windows 11 Pro |
| クライアント | iPad 第10世代(Safari) |
| 動作確認 | iPhone 15 ProのSafari |
| Raspberry Pi | Raspberry Pi Zero WH |
| OS | Raspberry Pi OS Lite 32bit Trixie |
| キャプチャカード | UVC対応 USB HDMIキャプチャ |
| USB接続 | microUSB OTG変換アダプター USB-A |
| 使用ソフト | FFmpeg / v4l-utils / Python3標準ライブラリ |
| Webサーバー | Python http.server |
| ネットワーク | Wi-Fi 2.4GHz |
| 電源 | コンセントから直接取ると安定します。 |
この記事で中心となる検証結果は、上記の環境で実際に動作を確認したものです。
Raspberry Piのモデル、OS、キャプチャカード、ブラウザなどが異なる場合は、対応する解像度やフレームレートなどを調整する必要があります。
なぜRaspberry Pi Zero WHを使ったのか
もっと高性能なRaspberry Piを使えば、今回の仕組みを動かすこと自体は簡単になるかもしれません。それでもRaspberry Pi Zero WHを選んだのは、限られた性能でどこまで構成を単純化できるか試したかったからです。
Raspberry Pi Zero WHは非常に小型で、今回のような画像処理を行うには余裕のあるコンピューターではありません。実際、最初の構成ではCPU使用率が92%以上に張り付き、SSHの操作まで重くなりました。
そこで私は、高性能なソフトウェアを追加する方向ではなく、逆に「処理そのものを減らす」方向へ設計を変更しました。
この考え方が、最終的な構成を決める大きなポイントになりました。
設計思想:できるだけ処理しない
今回の設計では、Raspberry Pi OSをインストールした直後の環境から、必要最小限のパッケージだけを追加して再現できる構成を目指しました。
使用する主要なコンポーネントは次の3つです。
FFmpeg
USB HDMIキャプチャからV4L2経由で映像を取得し、画像ファイルとして出力します。
今回の構成では、キャプチャカードがMJPEGを出力できることを利用し、不要なデコードと再エンコードを避けました。
v4l-utils
USB HDMIキャプチャが対応している解像度、フレームレート、映像形式などを確認するために使用します。
特に、キャプチャカードによって対応するフォーマットが異なるため、最初に確認しておくとトラブルシューティングが容易になります。
Python標準ライブラリ
Pythonのhttp.serverを利用して、保存された画像をHTTPで配信します。
またApacheやnginxなどのWebサーバーを新たに導入せず、Pythonに含まれる標準ライブラリだけで構成しました。
最小の構成で再現性を高めつつ、安定してブラウザのみで運用できることを目標にしました。
なぜソフトウェアを増やさなかったのか
Linuxには便利なソフトウェアが多数あります。
しかし、今回のようにRaspberry Pi Zero WHの限られたリソースで動かす場合、依存関係を増やすことにはデメリットがあります。
- パッケージのバージョンに依存する
- アップデートによって挙動が変わる
- 設定項目が増える
- トラブル発生時の切り分けが難しくなる
- 不要になった依存パッケージが残る
このような問題がつねにつきまといます。
そこで今回は、必要な機能を追加するよりも、標準的な仕組みだけで目的を達成することを優先しました。
これは「最も高性能な構成」を目指したものではありません。できるだけ少ない部品で、再構築しやすい構成を目指したものです。
完成版の構築手順
ここからは、実際に検証したPaperDisplay Version 1.0の構築手順を説明します。
記事執筆時点(2026年8月5日)の検証環境は、次の通りです。
- Raspberry Pi Zero WH
- Raspberry Pi OS Lite 32bit Trixie
- FFmpeg
- v4l-utils
- Python標準ライブラリ
OSやパッケージのバージョンによって表示内容が異なる場合があるため気を付けてください。
STEP 0:Raspberry Pi OSを最新状態へ更新する
新しくRaspberry Pi OS Lite 32bit Trixieをインストールしたら、まずパッケージ情報を更新してからシステムを更新します。
sudo apt update
sudo apt -y full-upgrade
sudo reboot
再現性を最優先にすること
Linuxでは便利なソフトウェアが数多く公開されています。しかし利用するライブラリが増えれば増えるほどに困ったこともでてきます。
- バージョンに依存する
- 更新による設計の見直し
- インストールの手順
- 場合によっては削除したのにautoremoveをしなければ残り続ける断片
これらはあまり好ましくないと考えています。そのため、Raspberry Pi OS標準で揃えることを目標にしました。これならば、全ての書き直しということにはならないと思いました。
数年後も使えるように比較的再現しやすい方法を選びました。
完成版の構築手順
ここからはVersion 1.0の構築を実際に行った順番で記録します。
記事執筆時点(2026年8月3日)の検証環境は次の通りです。
- Raspberry Pi Zero WH
- Raspberry Pi OS Lite 32bit Trixie
- FFmpeg
- v4l-utils
- Python標準ライブラリ
これだけです。この環境以外では動作が異なる可能性があります。ご容赦お願いします。
完成版の実際の手順
何回かWindowsの.sshフォルダの中身を消して、microSDカードを消去し、OSを入れ直して手順通りにりましたが、問題なく動きました。
STEP 0:システムを最新状態へ更新する
Raspberry Pi OS Lite 32bit Trixieを想定しています。ノートの隅のパラパラ漫画みたいだったので、仮にPaperDisplayと各フォルダ等に名前を付けています。
sudo apt update
sudo apt -y full-upgrade
sudo reboot
再起動後、SSHなどで再接続してください。今回の検証では、OS更新後の状態を基準として作業を進めました。
STEP 1:必要なパッケージをインストールする
FFmpegとv4l-utilsをインストールします。
sudo apt install -y ffmpeg
sudo apt install -y v4l-utils
FFmpegはキャプチャ映像の取得と画像ファイルの生成に使用するため絶対に必要です。v4l-utilsは、Linuxから認識されたUSB HDMIキャプチャの情報を確認するために使用します。必須かと言われれば、合った方が便利です。
STEP 2:作業ディレクトリを作成する
次にPaperDisplay用のディレクトリを作成します。
mkdir -p ~/PaperDisplay/www
cd ~/PaperDisplay
今回の重要な点はディレクトリwwwです。Pythonのhttp.serverは、起動したディレクトリ以下のファイルをHTTPで公開してしまいます。そこでWebから公開するファイルをwwwに収めます。
最終的にはHTTPで公開するファイルはwww内のcapture.jpgとindex.htmlになります。
STEP 3:HDMIキャプチャカードを確認する
USB HDMIキャプチャをRaspberry Piへ接続し、認識されているか確認します。
v4l2-ctl --list-devices
次に、/dev/video0として認識された場合は対応フォーマットを確認します。
v4l2-ctl -d /dev/video0 --list-formats-ext
ここで重要なのは、キャプチャカードがどの解像度、フレームレートが映像形式に対応しているかどうかを確認することです。
今回の構成ではキャプチャカードがMJPEGに対応していたためMJPEG入力を利用しました。
STEP 4:FFmpegでキャプチャする
キャプチャ用スクリプトを作成します。
sudo nano ~/PaperDisplay/capture.sh
以下を入力します。
#!/bin/bash
exec /usr/bin/ffmpeg \
-f v4l2 \
-input_format mjpeg \
-video_size 1024x768 \
-framerate 5 \
-i /dev/video0 \
-c:v copy \
-update 1 \
-y \
/home/pi/PaperDisplay/www/capture.jpg \
-loglevel error
ここはいくつか重要なものがあります。まず-c:v copyです。これがなければRaspberry Pi Zero WHでは動かなかったでしょう。キャプチャカードがすでにMJPEGを出力しているため、Raspberry Pi側で映像をデコードして再びエンコードする必要がないため、必須の記述です。
-framerate 5は1秒間に5フレームを取得するという意味です。色々試してみたのですが、サブディスプレイが滑らかに動かなくてもストレスを感じない数値として落としどころにしました。
あと-yです。これを付けないと作業が本当に大変になります。
最後にスクリプトへ実行権限を付けます。
chmod +x ~/PaperDisplay/capture.sh
STEP 5:FFmpegをsystemdで常駐させる
SSHから直接FFmpegを起動すると、SSHセッションを終了したときにプロセスも終了してしまいます。
そこでsystemdへ処理を任せます。
sudo nano /etc/systemd/system/paperdisplay-capture.service
次の内容を書き込みます。
[Unit]
Description=PaperDisplay Capture
After=network-online.target
[Service]
Type=simple
User=pi
Nice=10
WorkingDirectory=/home/pi/PaperDisplay
ExecStart=/home/pi/PaperDisplay/capture.sh
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
Restart=alwaysを設定することで、FFmpegが停止した場合もsystemdが再起動します。
また、Nice=10として、Raspberry Pi Zero WH上で他の処理へCPUを譲りやすくしています。
STEP 6:Python HTTPサーバーを常駐させる
次に、画像を配信するHTTPサーバーをsystemdで起動します。
sudo nano /etc/systemd/system/paperdisplay-http.service
[Unit]
Description=PaperDisplay HTTP Server
After=network-online.target
[Service]
Type=simple
User=pi
Nice=10
WorkingDirectory=/home/pi/PaperDisplay/www
ExecStart=/usr/bin/python3 -m http.server 8000
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
ここではPython標準ライブラリに含まれるhttp.serverを使用しています。これで専用のWebサーバーをインストールする必要はありません。
STEP 7:Safari用の表示ページを作成する
PaperDisplay表示用のHTMLファイルを作成します。
sudo nano ~/PaperDisplay/www/index.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>html,body{margin:0;background:#000;height:100%;overflow:hidden}
img{width:100%;height:100%;object-fit:contain;display:block}</style>
</head>
<body>
<img id="s"><script>
const img = document.getElementById('s');
let cur = null;
async function tick(){
try{
const res = await fetch('capture.jpg?t=' + Date.now(), {cache:'no-store'});
const blob = await res.blob();
const url = URL.createObjectURL(blob);
img.src = url;
if(cur) URL.revokeObjectURL(cur);
cur = url;
}catch(e){}
setTimeout(tick, 200);
}
tick();
</script>
</body>
</html>
動画ストリームを再生する書き方ではありません。200ミリ秒ごとにcapture.jpgを取得して、その画像を高速で移しているだけです。ノートのすみのパラパラ漫画みたいな感じです。
Date.now()をURLへ追加しているのは、ブラウザキャッシュによって古い画像が表示されることを避けるためです。
また、URL.revokeObjectURL()を利用して、不要になったBlob URLを解放しています。
STEP 8:systemdサービスを有効化する
STEP 7まで進んだらsystemdへ読み込ませます。
sudo systemctl daemon-reload
まずは何かを変更したらsystemctl daemon-reloadを行った方が良いと思います。
続いて2つのサービスを有効化します。
sudo systemctl enable --now paperdisplay-capture.service
sudo systemctl enable --now paperdisplay-http.serv
少し間があきますが、入力できるようになったら、ステータスの確認を行います。
systemctl status paperdisplay-capture.service
systemctl status paperdisplay-http.service
active(running)ならば成功です。FFmpegのCPU使用率が知りたい場合は次のコマンドを入力してください。
top -b -n 3 -d 1 | grep ffmpeg
STEP 9:Raspberry Piを再起動して確認する
電源が落ちても再起動できるかの確認を行います。
sudo reboot
SSHなどで再接続後、サービスのステータスを確認します。
systemctl status paperdisplay-capture.service
systemctl status paperdisplay-http.service
active (running)なら問題ありません。成功です。
FFmpegmのログも確認しておいた方が良いと思います。
journalctl -u paperdisplay-capture.service -n 30
ここまで正常に動作していれば、Raspberry Pi Zero WH側の基本構成は完成です。
STEP 10:iPadのSafariからアクセスする
ここからが本番となります。iPadのSafariを開き、Raspberry Pi Zero WHのIPアドレスへアクセスします。ホスト名からアクセスする方法とIPアドレス(仮に192.168.1.10とします)からアクセスする方法があります。
- http://ホスト名.local:8000
- http://192.168.1.10:8000(環境に合わせて変えてください)
ちなみにIPアドレスを確認する方法は次の通りです。
hostname -I
ページが表示されたら、Windows側の操作となります。
Win + P→複製、Win + P→拡張といった具合です。ただしすでにマルチモニターを使っている方は、システムから3番目のディスプレイとして操作してください。
これで完了です。Windowsの画面がRaspberry Pi Zero WH経由でiPadへ表示されます。
最初の校正は失敗しました。
ここからは、この構成に行き着く前になかなかうまく行かなかった失敗例について説明します。
私が最初に考えたのは、もっと単純な構成でした。
FFmpeg→capture.jpg→Python HTTP Server→Safari
実際、この構成でも画面を表示すること自体はできました。しかし、数時間動かしてみると、単純「画面が映る」だけでは実用にならないことが分かりました。
今回の検証では、大きく4つの問題が発生しました。
- CPU負荷(92%以上に張り付き)
- SSH依存(SSHが途切れると止まる)
- Safariとの互換性
- 長時間運用で止まる
問題1:CPU使用率が92%以上になった
最初はFFmpegでJPEGへ再エンコードしていました。画面は正常に表示されるため、一見すると問題ありません。
ところがRaspberry Pi Zero WHのCPU使用率が92%以上に張り付き、SSHからの操作まで重くなりました。意図した範囲でCPU使用率が高いならば、それ自体は自体が悪いわけではありません。
問題は、画像変換だけでCPUリソースを使い切っていたことでした。これは意図していないことであったため、早急に対策が必要でした。
問題2:SSHを終了すると停止した
最初はSSHからFFmpegを直接起動していました。
python3 -m http.server
そのためSSHセッションが途切れてしまうと、処理も終了してしまいます。テスト運用ならば問題ありませんが、常時表示することを目的としているため、毎回SSHでログインしてプロセスを起動するのは使いにくいです。
systemdに移行することが妥当でした。
問題3:SafariではMJPEGストリームが期待通りに動かなかった
当初は、MJPEGストリームをそのままブラウザへ配信する方法を採用しました。実際Chromeでは表示できました。ところが同じ構成をSafariで試すと、予期せぬ不具合連続でした。
主な症状はダウンロード扱いになったり、画像が正常に更新されなかったりしました。
そこで、動画ストリームを無理に扱うことはやめました。そのかわり画像の表示はできるため、Safariが画像を定期的に取得する方式へ変更しました。
問題4:長時間動かすと停止した
もう一つの問題は長時間運用が課題でした。
while true→FFmpeg起動→JPEG生成→FFmpeg終了→再びFFmpeg起動
短時間なら動作しましたが、不安定で15分程度で停止することもあれば、2時間程度で停止することもありました。
最初はとても熱くなっていたキャプチャカードの発熱と故障も考えました。
しかし検証を続けた結果、結局はFFmpegを一度起動してプロセスを常駐させ、画像を書き換え続ける構成へ変更しました。この方法で長時間運用時の突然停止が改善できました。
4つの問題から構成を見直す
検証を繰り返したことで、問題を4つに分けて考えることができることが分かりました。
| 問題 | 対策 |
| CPU負荷が想定以上に高い | MJPEGを再エンコードせずコピー |
| SSHを終了すると停止 | systemdで常駐化 |
| SafariでMJPEGが扱いにくい | 静止画の定期高速取得へ変更 |
| 長時間運用で停止 | FFmpegを常駐プロセス化 |
ここまで整理すると、最初は複雑に見えた問題も個別に対処できることがわかりました。
PaperDisplay Version 1.0で改善したポイント
| 項目 | 初期構成 | PaperDisplay Ver. 1.0 |
| FFmpeg | JPEGへ再エンコード | -c:v copy |
| フレームレート | 10fps固定 | 5fps |
| FFmpeg起動 | 手動 | systemd |
| HTTPサーバー | 手動 | systemd |
| ブラウザ更新 | 画像URL変更 | Fetch API + Blob |
| メモリ管理 | SSH依存 | revokeObjectURL() |
| プロセス管理 | SSH依存 | systemdによる自動復旧 |
| CPU負荷 | 非常に高い(92%) | 大幅に改善(34%) |
失敗したときの構成とかけた時間
3日間、頭を悩ませた流れです。解決してしまえばこんなものですが、本当にわかりませんでした。
| 日付 | 試したこと | 結果 | 原因 | 対策 |
| 1日目 | JPEGエンコード | CPU 92%以上 | CPU負荷 | -c:v copy |
| 同日 | MJPEG配信 | Safariで問題発生 | ブラウザ互換性 | Fetchを使う |
| 2日目 | SSH | 切断で停止 | プロセス管理 | systemd |
| 3日目 | 長時間稼働 | キャプチャ停止 | デバイス再初期化 | FFmpeg常駐 |
改善1:「処理する」から「処理しない」へ
最初の設計ではかなり無理をしていました。
MJPEG → デコード → JPEGへ再エンコード
という処理以外ないと思っていました。しかし、キャプチャカード自体がMJPEGを出力していることが分かったので-c:v copyを利用して、再エンコードを避けることに成功しました。
私の検証環境ではCPU使用率が大幅に改善し、条件によっては34%未満になることもありました。
Raspberry Pi Zero WHのような低性能なコンピューターでは、処理能力を上げるより、不要な処理を削る方が効果的な場合がありました。
改善2:Raspberry Pi Zero WHに運用を任せる
初期の設定には致命的な点がありました。テスト運用ということもあり、SSHが止まるとiPadとの接続が切断してしまうということでした。
SSHログイン→HTTPサーバー起動→別SSHセッション→FFmpeg起動
この作業を毎回行っていました。これでは、SSHが切れる、Windowsがスリープする、ネットワークが一時的に切れる、こういった状況で停止してしまいます。
そこでsystemdに任せることにしました。Raspberry Pi Zero WHにプロセスを管理するため、Windows側からSSHを切断してもサービスが続くようにしました。
改善3:Safariに合わせて方式を変更した
当初はMJPEGストリームをそのままSafariへ配信する方法を考えていました。おそらくMJPEGストリームは正しいと思います。しかし、Safariの仕様のせいかダウンロードになったり画面が映らなかったりと不安定でした。
capture.jpg→Fetch API→Blob→img.src
このような素朴な仕組みにしました。200ミリ秒ごとに画像を取得するため、5fps程度の画面の更新になります。
色々調整した結果、ストレスを感じるか感じないかのギリギリを攻めてみました。
改善4:FFmpegを常駐させた
最初はFFmpegを何度も起動していました。しかし、キャプチャデバイスを繰り返し開閉することが長時間運用上の問題につながりました。
そこでFFmpegを一度だけ起動し、プロセスを継続させる構成へ変更しました。
さらにsystemdにある記述によってプロセスが停止した場合には自動的に再起動するようにしました。
Restart=always
RestartSec=2
これによって、手動操作への依存を減らしています。
結局、何をしているのか
ここまで読むと複雑なことをしているように思えます。しかし、実際に行っていることは非常に単純です。
Windowsの画面を一定間隔で画像として取得し、その画像をブラウザへ表示しています。
流れとしてはこれだけです。
Windows画面→HDMI→USB HDMIキャプチャ→Raspberry Pi Zero WH→FFmpeg→capture.jpg→Python HTTP Server→Safari→iPad
新しい映像圧縮技術を開発したわけではありません。
FFmpeg、HTTP、HTML、JavaScript、Pythonという、既存の技術を組み合わせています。今回面白かったのは、何か新しい技術を追加したことではありません。
既存の技術から不要な部分を削っていった結果、Raspberry Pi Zero WHでも動作する構成になったことです。
この構成のメリットとデメリット
実際に使って分かったことを箇条書きにします。
この構成のメリット
- Windowsへ専用ソフトをインストールしなくてよい
- iPad側もSafariだけで利用できる
- Raspberry Pi Zero WHでも動作する
- Raspberry Pi側のソフトウェア構成が小さい
- systemdによる自動起動ができる
- iPhoneでも表示できる
- 余っているiPadを再利用できる
この構成デメリット
- 通常のディスプレイより遅延が大きい
- 動画には向かない
- ゲームには向かない
- マウス操作には向かない
- HDMIキャプチャが必要
- Raspberry Pi Zero WH側の設定が必要
- ブラウザによって動作結果が変わる可能性がある
つまり、普通のディスプレイの代用品として考えるのではなく、情報表示用のセカンド画面として考えるのが適しています。
よくある質問(FAQ)
- QRaspberry Pi Zero 2 WやPi 3、Pi 4、Pi 5でも使えますか?
- A
今回実際に検証したのはRaspberry Pi Zero WHです。他のモデルについて「この記事と同じ構成で必ず動作する」とは断言できません。
より高性能なRaspberry Piであれば、CPU性能には余裕ができるため、同様の構成を応用できる可能性はあります。
その場合も、使用するUSB HDMIキャプチャがLinuxのV4L2で正常に認識されるか確認してください。
- QAndroidタブレットでも使えますか?
- A
今回の検証はiPadのSafariを中心に行っていまが、Raspberry Pi Zero WHからHTTPで画像を配信し、ブラウザで画像を表示する仕組みそのものはiPad専用ではありません。
そのため、HTTPで配信された画像を正常に表示できるブラウザであれば、同じ考え方を応用できるはずです。
- Q動画視聴やゲームにも使えますか?
- A
無理です。遅延がひどいため、どうにもできません。今回の構成は動画ストリーミングではなく、画像を一定間隔で取得して表示する方式です。画面更新に遅延があるため、動画やゲームのようにリアルタイム性が必要な用途には適していません。
PDF、文章、ターミナル、監視画面など、静的な情報を表示する用途が適しています。
- QなぜMJPEGストリームを使わなかったのですか?
- A
Safariでの動作を優先したためです。今回の検証では、MJPEGストリームをブラウザへ直接配信する方法が正しいとおもいます。しかしSafariで期待した動作にならない問題がありました。
そこで、通常のJPEG画像をFetch APIで定期的に取得する方式へ変更しました。性能だけを考えれば、別のストリーミング方式を検討する余地があります。
今回は、Safariで動作し、専用ソフトを使わず、構成を単純にすることを優先しました。
- QなぜPython標準ライブラリだけを使ったのですか?
- A
再現しやすい環境を作りたかったからです。Apacheやnginxなど、Webサーバーには優れた選択肢があります。
しかし今回は最小構成にこだわりました。そこでcapture.jpgとindex.htmlをHTTPで配信する機能だけでサブディスプレイ化しました。Pythonに標準で含まれるhttp.serverを利用したのもそのためです。
必要以上にソフトウェアを追加しないことで、設定項目や依存関係を減らせます。
この実験で分かったこと
今回の実験で一番大きかった発見は、Raspberry Pi Zero WHの性能を「上げる」必要はなかったことです。高性能なものを追加するのではなく、不要な処理を削るという方向が低スペックマシンでは必要だと分かりました。
これはRaspberry Piに限らず、低スペックなコンピューターを利用するときにも応用できる考え方だと思います。
まとめ
Windowsへ専用ソフトをインストールせず、Raspberry Pi Zero WHとUSB HDMIキャプチャを組み合わせることで、iPadを疑似的なサブディスプレイとして利用することができました。
一般的なディスプレイとは仕組みが違い、Windowsの画面をHDMIキャプチャで取得し、Raspberry Pi Zero WHで画像として保存し、HTTP経由でSafariへ配信するという婉曲的な方法だと思います。
ノートの端のパラパラ漫画のような仕組みのため、動画やゲームには向きません。一方で、ターミナル、PDF、Word、システム監視など、動きの少ない情報を常時表示する用途では十分に利用できました。
失敗でしたが、JPEGへの再エンコードによるCPU負荷、SSH依存、Safariとの互換性、長時間運用という4つの問題を解決していくことも学びになったと思います。
そして結果として、FFmpeg + v4l-utils + Python標準ライブラリ、という小さな構成に落ち着きました。Raspberry Pi Zero WHの性能を引き上げることではなく、必要のない処理をさせないことが重要でした。
「古いRaspberry Piだから何もできない」と考えるのではなく、用途を限定し、処理を減らしていくことで、まだ使える場面があります。
今回のPaperDisplayは、その一例になりました。
リファレンス
- FFmpeg Documentation
https://ffmpeg.org/documentation.html - FFmpeg Devices Documentation / Video4Linux2
https://ffmpeg.org/ffmpeg-devices.html#video4linux2_002c-v4l2 - Raspberry Pi Documentation
https://www.raspberrypi.com/documentation/ - Python Software Foundation /
http.server
https://docs.python.org/3/library/http.server.html - MDN Web Docs / Fetch API
https://developer.mozilla.org/docs/Web/API/Fetch_API - MDN Web Docs / URL.createObjectURL()
https://developer.mozilla.org/docs/Web/API/URL/createObjectURL - MDN Web Docs / URL.revokeObjectURL()
https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL - WebKit Project
https://webkit.org/
更新履歴
- 2026年9月22日:サイト見直しに伴いタイトルを簡素化
- 2026年8月24日:軽量化に成功したため一部訂正
- 2026年8月5日:初公開