アーキテクチャ
このドキュメントでは、sub-screen-player の構成、各部分の責務、新しいコードをどこに置くべきかを説明します。
ssp CLI ・ スクリプト / アプリ ・ (今後)Web 画面、プラグイン │ HTTP / WebSocket (127.0.0.1:7920) │━━ ssp-server ━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ api/ ルーティング、認証、WebSocket ストリーム manager ディスプレイの検出、抜き差し、各ディスプレイの表示内容 (Content) sources/ フレームを描く組み込み画面(時計、静止画) │ │ Frame(横長の RGB、パネルと同じサイズ) │━━ ssp-core ━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Presenter 最新フレーム優先のキュー、エンコード/デバイス用スレッド、 送信ペースの制御、同一フレームの省略、キープアライブ Encoder パネルの向きへの回転、エンコード(JPEG) Display すべてのドライバが実装するトレイト Transport 通信路のトレイト(hid::HidTransport が実装) │ │ EncodedImage │━━ ssp-driver-<model> ━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ protocol デバイス向けレポートを組み立てる純粋関数 Display 実装: 表示、保存、明るさ、電源、キープアライブ Driver 対応する USB ID と、デバイスの開き方 │ │ レポート │ USB (HID)これらはすべて ssp という1つのバイナリに入っています。ssp serve がデーモンを動かし、それ以外のコマンドは
すべて、そのデーモンに対する小さな HTTP クライアントです。
クレートと責務
Section titled “クレートと責務”| クレート | パス | 担当すること | してはいけないこと |
|---|---|---|---|
ssp-core |
crates/core |
Display/Driver/Transport トレイト、HID アクセス、フレームとエンコード、Presenter |
特定の機種のプロトコルを知ること |
ssp-driver-<model> |
crates/drivers/<model> |
1つの機種ファミリー: そのプロトコル、機能、癖 | ほかのドライバ、サーバ、非同期コードへの依存 |
ssp-server |
crates/server |
デーモン: デバイス管理、組み込みソース、設定、HTTP/WebSocket API | 機種固有のバイト列を持つこと |
sub-screen-player(バイナリ名 ssp) |
crates/cli |
コマンドライン、自動起動、ログ設定 | デバイスと直接通信すること(デーモン未起動時の一覧表示を除く) |
依存は下向きだけです: cli → server → drivers/* → core
Frame(core): 呼び出し側が描くもの。常に横長で、パネルとちょうど同じサイズの 8 ビット RGB です。 サイズの違う画像はFrame::fit(contain/cover/stretch)で合わせます。PanelSpec(core): 呼び出し側から見たパネルのサイズ、送信時に必要な回転、画像形式。たとえば D92 は 1920x462、時計回りに 90° 回転、JPEG です。EncodedImage(core): 回転とエンコードを済ませ、ドライバに渡せる状態のフレーム。Display(core のトレイト、ドライバが実装):show(ライブ表示、保存しない)、save(デバイスに保存)、set_brightness、wake、sleep、clear、keep_alive。機種にない操作は既定の実装のままにしておくと、Error::Unsupportedを返します。Capabilities(core): ディスプレイが対応している操作、フレームレートの上限、キープアライブの間隔、 受け付ける画像の最大サイズ。ほかの部分は機種を判別する代わりにこれを参照します。Driver(core のトレイト): ドライバが扱う USB インターフェース(UsbMatch)とopen。 デーモンが使うドライバの一覧はcrates/server/src/drivers.rsにあります。どのドライバを使うかはDriverSelection(--driverオプションや設定ファイルの[drivers])で決まり、Registry::selectで反映します。experimental(試験中)のドライバは、名前を指定したときだけ使われます。Content(server): ディスプレイに表示させる内容。なし、画像、時計、ストリームのいずれかです。 ディスプレイ ID ごとに保持されるので、挿し直したディスプレイは元の表示を続けます。Source(server のトレイト): フレームを描き、次に絵が変わるタイミングを知っているもの(時計、静止画)。
フレームが表示されるまで
Section titled “フレームが表示されるまで”- フレームが作られます。
Sourceがディスプレイごとのプレイヤースレッドで描画するか、 WebSocket/HTTP のクライアントが送ってきたものをサーバがデコードします。 Presenter::submitはフレームを1枠のスロットに入れ、すぐに戻ります。スロットに残っていた前のフレームは 捨てられます(droppedとして数えます)。- エンコードスレッドがフレームを取り出し、回転してエンコードします。結果がデバイスの受け付けるサイズを 超えた場合は、JPEG の品質を段階的に下げます。
- デバイススレッドは、フレーム間隔(
1 / max_fps)が経過した時点で最新のエンコード済みフレームを送ります。 画面に出ているものと同じフレームは送りません(duplicatesとして数えます)。次のフレームのエンコードは 現在のフレームの送信と並行して進むため、フレームレートは両者の合計ではなく、遅い方で決まります。 - ドライバの
Display::showが画像をレポートに変換し(protocol.rs)、Transportを通して書き込みます。
制御コマンド(明るさ、電源、消去、保存)も同じデバイススレッドを順番に通り、呼び出し側は結果を待ちます。
キープアライブも同じスレッドから keep_alive_interval ごとに送られます。
| スレッド | 数 | 役割 |
|---|---|---|
| tokio ランタイム | 数本 | HTTP と WebSocket の処理。ブロックする処理は spawn_blocking に回します。 |
ssp-scan |
1 | 2 秒ごとに USB を走査し、新しいディスプレイを開き、抜かれたものを検知します |
ssp-encode-<id> |
ディスプレイごとに1 | フレームの回転とエンコード |
ssp-device-<id> |
ディスプレイごとに1 | Display を所有し、フレーム・コマンド・キープアライブを送ります |
ssp-player-<id> |
ディスプレイごとに0〜1 | 現在の組み込み Source を動かします(例: 時計を刻む) |
Display を使うのは自分のデバイススレッドだけなので、ドライバ側でロックは不要です。
エラーと再接続
Section titled “エラーと再接続”Error::TransportとError::Disconnectedは致命的です。Presenter は停止し、待ち行列のコマンドを失敗させます。 マネージャは次の走査でそのデバイスを外しますがContentは保持し、再び現れたら開き直します。- それ以外のエラー(
InvalidArgument、Image、Unsupported)は呼び出し側に返され、ディスプレイは動き続けます。 - デバイスを開けなかった場合(ほかのプログラムが使用中など)、マネージャは 10 秒ごとに再試行し、ログは最初の失敗だけ出します。
セキュリティモデル
Section titled “セキュリティモデル”- API は既定で
127.0.0.1で待ち受けます。tokenを設定しない限り、ループバック以外のlistenアドレスは 設定の検証で拒否されます。 - トークンなしの場合、
HostまたはOriginヘッダーがループバックでないリクエストは拒否します。これにより、 ブラウザで開いた Web ページがディスプレイを操作すること(CSRF、DNS リバインディング)を防ぎます。 - トークンありの場合、すべてのリクエストに
Authorization: Bearer <token>または?token=<token>が必要です。
拡張ポイント
Section titled “拡張ポイント”現在あるものと、今後の機能がどこに入るか:
| 拡張 | 状況 | 入る場所 |
|---|---|---|
| 新しいディスプレイ機種 | 利用可能 | crates/drivers/ 以下の新しいクレート(手順) |
| 外部プログラムからの描画 | 利用可能 | WebSocket ストリームまたは HTTP の画像エンドポイント(API) |
| 組み込み画面 | 利用可能 | crates/server/src/sources/ の Source と、Content のバリアント |
| HID 以外の通信方式(USB バルク、シリアル) | 予定 | crates/core に新しい Transport 実装 |
| Web(HTML/CSS)画面 | 予定 | ページを描画する Source、またはフレームを送る外部クライアント |
| WASM プラグイン | 予定 | サンドボックス化したプラグインを動かす Source |
| 設定ファイルでのレイアウト合成 | 予定 | ほかのソースを組み合わせる Source(config.rs で設定) |
新しい種類のコンテンツが差し込まれる継ぎ目は、Source トレイトとストリーム API の2つです。どちらも小さく、
安定した形に保ってください。