gRPCでmojaveに勝利してリモートからWebカメラを利用した
背景
macOSのMojaveからFaceTime HDカメラや、Webカメラ等のカメラデバイスへのアクセスが厳しくなり、 アプリ毎に設定が必要となってしまい、これまでのようにssh越しにリモートから利用することができなくなってしまった。
対策
あらかじめ、Termina.appでカメラにアクセスするコードを含むサーバプロセスを実行しておき、 SSHでリモートからアクセスした際はこのサーバプロセスにアクセスするクライアントを動かすことで、 Mojaveに怒られないようにする。
ちなみに、アクセス許可のないアプリ等からカメラデバイスにアクセスすると 以下の記事のようなことになる。
クライアントとサーバ間の通信方式にgRPCを採用
クライアントとサーバ間の通信方式にgRPCを使えば、今時な実装でイケてるのでは?という安易な選択で いざ実装。
gRPCでストリームを使う
これまで Raspberry Piの温度をgRPCを使ってElectronで作ったデスクトップマスコットに表示させた と macOSのCPU温度をgRPCを使ってElectronで作ったデスクトップマスコットに表示させた と複数のgRPCをつかったリポジトリを保有しているものの、 .protoファイルがHelloWorldを流用してやり過ごしていた。
今回一念発起して、独自の.protoファイルかつStreamをつかったgRPCにチャレンジした。
gRPCでstreamを使うポイント
HelloWorldとともに公開されているRoute_guideのソースを見れば、大体わかる。 特にNode.js実装だと自分が慣れているのもあり、
- サーバ側からstreamを返すパターン
- クライアント側からstreamを送るパターン
- クライアント、サーバともにstreamを送るパターン
のパターンがあり、これらのそれぞれのパターンに対応するメソッドがサンプルに 書かれている。
今回はクライアントからは通常のリクエスト、サーバ側からストリームというパターンで 作るのでサンプルの
function listFeatures(call) {
の関数を参考に作業を進めた。
クライアントからのリクエストを空にすることも可能
今回のアプリはWebカメラの画像を貰うだけなので、特にクライアントからサーバに渡すデータは 不要だったが、サンプルは何かしら入れているので、どうしたものかと一瞬悩んだが、
message Request {
}
という指定で.protoファイルを作ることが出来たので、スッキリした。
試行錯誤の末できた.protoファイル
こうして完成したのが、以下の.protoファイル
syntax = "proto3"; package camtype; message Request { } message Response { bytes ppm = 1; } service Camtype { rpc Camtype (Request) returns (stream Response) {} }
勘違いしていたStream
自分の中にはストリームというとバイトストリームのイメージが強く、 JSONが分割して送信されると誤解していたが、 .protoファイルで定義した構造体がNode.js実装の場合にはJSON形式でぼこぼこ返されるのだった。
message Response { bytes ppm = 1; }
というppmという定義は、PPMファイルを適当にぶった切ってバイト列をで送るものだと決めつけていたが、PPMファイルをまるまる一回の
call.write({ ppm: data })
で送ることが出来てしまった。
当初はgRPCでStream指定をしたら、gRPC側で適当に細切れにして、クライアントに送信することを 来していたが、裏でどうなっているかは知らないが、表面的なAPIとしては、 サーバで1個streamを送信したら、クライアント側で1個受け取れる模様。
ちなみにクライアント側のでstreamを受け取るのは以下。
call.on('data',(response)=>{ parsePpm(response.ppm) })
サーバ側からStreamを送る際の注意
サーバ側
一連のstreamをwriteしたらendメソッドを呼び出す必要がある模様。
call.end()
クライアント側は
call.on('end',()=> { console.log(`end`) })
として、サーバ側からのendを受け取ることができる。
成果物
学んだこと
これまでにgRPCには触ってはいたが、Helloworldの.protoファイルをそのまま流用しており、 今回初めて、自分のアプリ向けに.protoファイルを作成することを経験した。
Node.jsは動的に.protoファイルを扱えるので、試行錯誤しながら.protoファイルを作って 実装していくような今回のケースでは手軽で便利!
気になった事
サーバ側で、Webカメラの映像が安定することを期待してウェイトを入れているが、 これがどの程度長くしても怒られないのか、また、怒られる場合は、gRPCのタイムアウト になるのだろうか。
関連記事
- DeprecationWarning: grpc.load: Use the @grpc/proto-loader module with grpc.loadPackageDefinition insteadの警告を消すには
- Raspberry Piの温度をgRPCを使ってElectronで作ったデスクトップマスコットに表示させた
- macOSのCPU温度をgRPCを使ってElectronで作ったデスクトップマスコットに表示させた
7年前の記事
こどものマイクラで占領された自作Xeon PCをリモートから操作してIJuliaを入れてブラウザからJuliaを使うまで
こんばんは。今年も花粉症がひどくて沖縄や北海道にしばらく行っていたいです。次男も花粉症の疑いがあり、一緒に耳鼻科に行って花粉症対策の薬を処方してもらって二人で飲んでいます。@kjunichiです。
背景
Xoenで8コア積んでる自作PCを子供のマインクラフトに占領されている。
Jupyter経由でJuliaを動かせるようにして、貴重な計算機資源を奪回する
公式でOpenSSHがWindows10でも利用できるようになったので、これを利用する事で、 かなりのことがマインクラフトで占領されている自作PCに対して遠隔操作できるはず。
Juliaのインストール
これはこどもが起きてこないうちにさくっと実施。
以降はsshで繋いで作業
IJuliaのインストール
cd %homepath% AppData\Local\Julia-1.1.0\bin\julia.exe
using Pkg;Pkg.add("IJulia")
などで、普通にインストール
IJuliaを入れた際に入るjupyterを探す
リモートからjupyterにアクセスできるようにする。 その為には、まず、IJuliaをインストールした際に一緒にインストールされたjupyterを探しておく。
cd %homepath% fd jupyter.exe$ .julia
ここで、fdコマンドはrust実装の21世紀版find(UNIX版の意)コマンド。
.julia\conda\3\Scripts\jupyter.exe .julia\conda\3\pkgs\jupyter_core-4.4.0-py36_0\Scripts\jupyter.exe
のように見つかるはず、今後利用するのは、この例では、一番上のバージョンを含まない
.julia\conda\3\Scripts\jupyter.exe
を使っていく。
jupyter.exeが呼び出すサブコマンドが見つからないと怒られるので、以下のように パスを通しておく。
set path=%path%;%homepath%\.julia\conda\3\Scripts
デフォルトのjupyterの設定ファイルの雛形を生成
以下のコマンドで、デフォルトのjupyterの設定ファイル(%homepath%.jupyter\jupyter_notebook_config.py)の雛形を生成する。
jupyter notebook --generate-config
jupyterをリモートからアクセス出来るように設定
先ほど生成したjupyterの設定ファイルを編集して、リモートからjupyterにアクセス出来るようにする。
変更が必要な項目は以下の項目の模様。
c.NotebookApp.allow_remote_access = True c.NotebookApp.ip = '*' c.NotebookApp.local_hostnames = ['localhost','192.168.0.123']
192.168.0.123は子供に占領されてる自作PCのIPアドレスを指定。 (もしかするとlocalhostのみでもOKかも)
以上で、準備完了となる。
IJuliaをリモートから起動
cd %homepath% AppData\Local\Julia-1.1.0\bin\julia.exe
using IJulia notebook(detached=true)
ここでdetached=trueの指定が無いと、プロンプトが戻ってこないので、別のsshで繋ぐ必要がある。
Ijuliaのデフォルトの状態ではjupyterがパスワード運用ではないようなので、jupyterコマンドで
jupyter notebook list
Currently running servers: http://localhost:8888/?token=c0fc9b6d0e7684b58a1018eb2197603b0eb028a8c23883a9 :: C:\Users\kjunichi
として動かしているURLを取得して、これをローカルのブラウザに貼り付ける必要がある。 この際、localhostとホスト名がなっている場合、これを対象のPCのホスト名やIPアドレスに置きかる必要がある。
おわりに
Xeonといっても4万程度のCPUなので、2GHz以下のクロックで新しくしたいMac Book Pro 初代Retinaより 1コアあたりの処理は遅いことを思い出した。
関連記事
- 2016年のJulia言語の学習の振り返り
- Raspberry PiでもJuliaが動く件
- Juliaでゼータ関数を直接jupyterにプロットしたり、マインクラフト(MCPI)にプロットした
- IJuliaをHerokuの無料枠で動かした
こどもつながり
14年前の記事
9年前の記事
4年前の記事
だれもがターミナルにWebカメラ映像を表示を使うことができる時代なのに……
こんばんは。日曜日、自分のYシャツのアイロンをかけ終わり、翌朝の阻止馬出しのPTAの当番の為に早く寝るか。というタイミングで、長男が、金曜日に持ち帰って来ていた給食の白衣が残っている事が発覚、速攻で洗濯機を回し、アイロンをかけた@kjunichiです。
背景
だれもがターミナルにWebカメラ映像を表示を使うことができる時代なのに、Mojave以降その風景はガラリと変わってしまった。
Mojave以前
ssh越しにフツーに以下の自作アプリが使えた。
Mojave以降
カメラを利用するには許可を与えないといけない sshdに許可を与えれば解決しそうだが、これは.appに対してしか許可できなそう。
そこで、 カメラにアクセスするアプリはデスクトップ起動し、このアプリで ソケットを受け付け、接続してきたクライアントに対して撮影した画像を ソケット通信で返すという流れにすることで実現出来そう
図にすると以下のようなイメージ
やってみた
mojaveでカメラを使ってコマンドラインに表示する自作アプリがリモートから使えなくなったで、回避するべくクラサバ方式にnode.jsで実装してたのがようやく完成した。 pic.twitter.com/lBQ7NBPk8H
— kjunichi (@kjunichi) February 11, 2019
成果物
使い方
git clone https://github.com/kjunichi/camtyped cd camtyped npm i node main.js & node --experimental-modules client.mjs
関連記事
- RustでWebカメラの映像をコマンドプロンプトに出すコマンドを作った
- 久々にhomebridge-iSightを動かそうとしてmojaveにやられた
- gRPCでmojaveに勝利してリモートからWebカメラを利用した