見出し画像

Androidのカメラ映像をMotionJPEGで配信する方法 (Java)

はじめに

こんにちは。Daddy's Officeの市川です。

長いこと監視カメラソフトの開発を続けていますが、未だによく利用するのがMotionJPEG形式での動画配信です。
その理由は、実装が楽で処理負荷も低く、監視カメラ用途では十分な性能が出るためです。

特に、監視カメラでは、各フレーム映像に対して何かしらの処理を行う場合が多いため、任意のフレームを自由に切り出すことができるMotionJPEG形式は、現在でも各社の監視カメラのネットワーク配信フォーマットとして採用されています。

今回は、このMotionJPEGを使用して、Androidのカメラ映像をネットワーク上に動画として配信するアプリケーションの書き方(Java)を説明します。

ソースコード

サンプルアプリケーションの全ソースコードはこちらのGitHubに置いてあります。

サンプルを実行すると、背面カメラの映像を10080ポートから、MotionJPEG形式で配信します。

ChromeかFirefoxで配信しているAndroid端末に接続すると、カメラ映像が確認できます。

MotionJPEGとは

細かい解説はWiKiペディア Motion JPEGを参照してください。

簡単に言うと、JPEG画像の連続フレームで構成されたものがMotionJPEGです。

ただ、そのまとめ方(コンテナ)に関しては、定型フォーマットが存在していません。
例えばビデオカメラなどではAVIコンテナにJPEGとLPCMを入れて、MotionJPEGと表記している物もありますし、MOV形式のMotionJPEGもあります。

ネットワークカメラなどのHTTP上のストリーミング形式のものは、Content-Type: multipart/x-mixed-replaceを使用して複数のJPEGを連続して送るフォーマットをMotionJPEGと表記しています。

つまり、JPEG画像の連続フレームで構成されたものは、すべてMotionJPEGと名乗っている状態です。

Panasonicなど、主要メーカ製のネットワークカメラは、ほとんどがHTTP上のmultipartストリーミング形式になっており、私が開発しているLiveCapture3でもこのmultipart方式で配信しています。

Multipart方式のMotionJPEGフォーマット

HTTP上でMotionJPEGを配信するのに使用される、multipart/x-mixed-replaceというのは、Netscape社が提唱した、サーバからのプッシュ配信の為のもので、クライアント側は、新たなpartが送られてきたら、表示中のものを破棄して新しいPartを表示する、という仕様になっています。

MotionJPEGを配信する場合、配信サーバはこのmultipart/mixed-replaceを使用して、クライアントがコネクションを切断するまでJPEG画像を流し続けます。

各JPEG画像の区切りの判断は、通常のmultipart同様、Content-Typeに記載したboundary文字列で判断します。

サーバからのレスポンスを図にすると、こんな感じです。

ただ、このmultipart/mixed-replaceは、あまり広まりませんでした。
その為、すべてのブラウザでこのContentTypeが処理できるとは限りません。

私の手元のPC(Windows10)では、ChromeとFireFoxでは再生できましたが、IEとEdgeでは再生できませんでした。
※AndroidのChromeでも再生できました。

ということで、この形式のMotionJPEGを表示させるためには、サーバが一方的に送りつけてくるコマ送りJPEGを遅延なく受信⇒デコード⇒表示を繰り返し処理可能な能力をもったクライアントアプリケーションを作る必要があります。

ただ、今回は配信側の説明のみを行いますので、動作確認は上記のMotionJPEG再生可能なブラウザを使ってください。

サンプルソースコード(Java)説明

Androidサンプルの全ソースコード(Java)はこちらのGitHubに置いてあります。

以下、各クラスの説明です。

  • CameraPreview カメラの画像を取得するSurfaceを保持したクラス

  • MJpegServer MotionJPEGを配信する簡易HTTPサーバクラス

  • MainActivity CameraPreviewが保持するプレビュー用Surfaceを張り付けるActivity

  • PermissionConfirmActivity カメラ権限確認用Activity


カメラ映像の取得

古い端末でも動くように、カメラ映像の取得にはCamera APIを使用しています。
カメラ映像の取得は他にもサンプルがたくさんありますので、詳しくはそちらを参照してください。

ポイントは、カメラ映像フレームの取得処理です。
android.hardware.cameraのsetPreviewCallbackWithBufferを使用して、カメラのフレーム映像を取得していますが、取得できるフレーム画像をJPEGに変換する必要があります。
取得できるカメラ映像の色空間はYUV系になるので、それをandroid.graphics.YuvImageを使用してJPEGに変換しています。


MotionJPEG配信サーバ起動

MotionJPEGの配信はHTTPで行いますので、簡易的なHTTPサーバを作っています。

MJpegServerのstartをコールすると、クライアント接続用の待ち受けスレッドが起動します。
そのスレッド処理の頭で、サーバソケットを作成し、指定されたポート番号にバインドして、クライアントからの接続を待ちます。

JavaのSocketでは、クライアントの待ち受けを行うacceptを強制停止することができません。
そのため、acceptを行うサーバソケットにsetSoTimeoutでタイムアウト値を設定して、定期的にSocketTimeoutExceptionを発生させることで、中断処理を可能にします。


クライアント接続

接続してきた各クライアントとの処理は、別のスレッドを起動して行います。

今回のサンプルでは、HTTP GETリクエストであればすべてOKとし、それ以外はエラーとしています。


MotionJPEG送信

クライアントソケットから、すべてのリクエスト情報を読みだした後に、レスポンスとして、MotionJPEGを返却します。

接続が切れるまで、繰り返しMotioJPEGを送信し続けます。

ブラウザでの動作確認

Windows10上のChromeで、サンプルプログラムを起動したAndroidに接続すると、以下のようにカメラのライブ映像が表示されます。


最後に

ソースコードを見てもらえればわかりますが、MotionJPEGであれば、非常に単純なプログラムで動画配信ができます。

若干のカクツキなどが生じますが、表示するクライアント側のプレイヤーの開発もJPEGを表示するだけでOKなので、用途によってはとても有用なフォーマットだと思います。

また、最近はクラウドを介したサービスが多くなる中、サーバ側の運用費を抑えた動画配信サービスの開発も可能です。

MPEGライセンスなどの問題もなく、編集がやりやすいなど、MotionJPEGはまだまだ使える動画フォーマットだと思いますので、いろいろと試してみてください!


この記事が気に入ったらサポートをしてみませんか?