プログラムで動画作成:Android SDK2015/07/15

前回の記事の通り、アプリ「おかしなカメラ」に、動画を保存する機能を追加しました。

Androidで動画を作成する方法はネットで調べればわかりますが、実際にアプリで機能させるにはそれ以外にも作らなければいけない所があります。
もちろんそれらも調べることはできるのですが、常套手段やどんな手法があるかもわからないと検索するのもままなりません。

そこで今回は検索用のキーワードを挙げる意味でも、簡単に動画作成の流れを追ってみます。


データをエンコードして動画に
動画ファイルの作成には、MediaCodecと MediaMuxerというクラスを使用します。
MediaCodecでデータをエンコード、MediaMuxerでファイルにまとめます。
画像と音声は別データなので、それぞれ MediaCodecが必要です。

MediaCodecは getInputBuffers()で入力用のバッファを取得して、エンコードのためのバイナリデータを送ることができるので、音声の方は AudioRecordで取得したデータを送っています。

ただし、画像の方はこの方法は使いません。
バイナリでやり取りしたのでは重いし、そもそも機種毎に対応している画像フォーマットが違うらしいです。

画像データを送るには、Surfaceを使用する方法が用意されています。
MediaCodecの createInputSurfaceで Surfaceを取得、そこへエンコードする画像を描画します。

Surfaceへの描画というと lockCanvasで Canvasを取得して…という方法がありますが、MediaCodec.javaの createInputSurfaceの所には lockCanvasは使うな、OpenGL ES(の類)を使え、とあります。実際、動画の作成を行うようなアプリが描画に Canvasを使用しているとは思えませんが…。


◇OpenGLによる Surfaceへの描画
こんなことは Androidで OpenGLを使うための基本的な話なんでしょうが、最初から GLSurfaceViewなんて物を使っていると案外知らなかったりします。

それぞれのクラスがどんな役割を持っているか、正確にはわかっていないのですが必要な処理を並べてみます。

・EGL10を取得
・EGLDisplayを取得
・EGLConfigを取得
・EGLContextを作成
・EGLSurfaceを作成
 このとき eglCreateWindowSurfaceの第3引数に、createInputSurfaceで取得した Surfaceを指定します。

後は毎フレーム glMakeCurrentから描画、eglSwapBuffers…とかあるんですが、このあたりはサンプルも含めてちゃんと解説している所を探した方が良いでしょう。

これで OpenGLを使った描画で動画を作成できるようになりました。しかし、まだ問題があります。


◇異なる EGLContext間の描画
実際のアプリでは画面キャプチャ等、画面に表示するテクスチャやフレームバッファで動画を作成する必要が多いのではないかと思います。
ところが、画面描画用の Surface(GLSurfaceView)とエンコード用の Surfaceでは EGLContextが異なっているので、このままではテクスチャを共用できません(テクスチャ等は EGLContextごとに管理されるため)。

一旦テクスチャデータをメモリに落として、エンコード用の Surfaceに切り替えたときにテクスチャを生成すれば描画できなくもありませんが、毎フレームそんな重い処理をするのは避けたいところです。

そこで、shared_contextという手法(実は手法の名前なのか、よくわかっていません)を使って、テクスチャ等を共有します。
具体的には、エンコード用の EGLContextを作成するときの eglCreateContextで、第3引数に画面描画に使用している EGLContextを指定します。
これで、画面に表示するテクスチャやフレームバッファを動画にすることができます。

ちなみに、GLSurfaceViewで使用している EGLContextを取得するには setEGLContextFactoryを使用します。
EGLContextを作成するクラスを作って、createContextの戻り値が必要とする EGLContextとなります。


以上、動画作成部分を実装するまでに調べた所や、つまづいた所を簡単に挙げてみました。
詳しい解説やサンプル等は、それぞれの処理で調べてみてください。

なお、上に挙げた方法がベストとは限りません。また、大体のニュアンスで書いていて細かい所で間違っている表現もあるかもしれないので気を付けてください。