背景
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で作ったデスクトップマスコットに表示させた