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はまだまだ使える動画フォーマットだと思いますので、いろいろと試してみてください!
この記事が気に入ったらサポートをしてみませんか?