SurfaceTexture

SurfaceTexture は、サーフェスと OpenGL ES(GLES)テクスチャの組み合わせです。SurfaceTexture のインスタンスは、GLES テクスチャに出力するサーフェスを提供するために使用します。

SurfaceTexture には、アプリがコンシューマーである BufferQueue のインスタンスが含まれています。onFrameAvailable() コールバックは、プロデューサーが新しいバッファをキューに登録すると、アプリに通知します。次に、アプリは updateTexImage() を呼び出して以前保持していたバッファを解放することでキューから新しいバッファを取得し、EGL を呼び出して GLES がバッファを外部テクスチャとして使用できるようにします。

外部 GLES テクスチャ

外部 GLES テクスチャ(GL_TEXTURE_EXTERNAL_OES)は、従来の GLES テクスチャ(GL_TEXTURE_2D)とは次の点で異なります。

  • 外部テクスチャは、BufferQueue から受け取ったデータからテクスチャのあるポリゴンを直接レンダリングします。
  • 外部テクスチャのレンダラの構成は、従来の GLES テクスチャのレンダラの構成とは異なります。
  • 外部テクスチャが、従来の GLES テクスチャのアクティビティをすべて実行できるとは限りません。

外部テクスチャの主なメリットは、BufferQueue データから直接レンダリングできることです。SurfaceTexture のインスタンスは、外部テクスチャ用の BufferQueue のインスタンスを作成するときにコンシューマーの使用フラグを GRALLOC_USAGE_HW_TEXTURE に設定して、バッファ内のデータを GLES が認識できるようにします。

SurfaceTexture のインスタンスは EGL コンテキストとやり取りするため、アプリでのメソッドの呼び出しは、テクスチャを持つ EGL コンテキストが呼び出し元のスレッドで最新の間のみ可能になります。詳細については、SurfaceTexture クラスのドキュメントをご覧ください。

タイムスタンプと変換

SurfaceTexture のインスタンスには、タイムスタンプを取得する getTimeStamp() メソッドと、変換行列を取得する getTransformMatrix() メソッドがあります。updateTexImage() の呼び出しにより、タイムスタンプと変換行列の両方が設定されます。BufferQueue が渡す各バッファには、変換パラメータとタイムスタンプが含まれます。

変換パラメータは効率化を図るうえで有用です。場合によっては、コンシューマーに対するソースデータの向きが正しくないことがあります。コンシューマーに送信する前にデータを回転させる代わりに、その向きのデータを修正する変換で送信します。変換行列は、データが使用されるときに他の変換と統合され、オーバーヘッドを最小限に抑えます。

タイムスタンプは、時間に依存するバッファのソースにとって有用です。たとえば、setPreviewTexture() がプロデューサー インターフェースをカメラの出力に接続すると、カメラのフレームを使用して動画を作成できます。各フレームには、アプリがフレームを受信したときからではなく、フレームがキャプチャされたときからのプレゼンテーション タイムスタンプが必要です。カメラコードがバッファで提供されたタイムスタンプを設定することで、一連のタイムスタンプの整合性が向上します。

ケーススタディ: Grafika の連続キャプチャ

Grafika の連続キャプチャでは、デバイスのカメラからフレームを記録し、画面にこれらのフレームを表示します。フレームを記録するには、MediaCodec クラスの createInputSurface() メソッドでサーフェスを作成して、カメラに渡します。フレームを表示するには、SurfaceView のインスタンスを作成し、サーフェスを setPreviewDisplay() に渡します。ただし、フレームを記録して同時に表示するには、より複雑なプロセスが必要です。

連続キャプチャのアクティビティでは、動画の撮影中にカメラからの動画を表示します。この場合、エンコードされた動画はメモリ内の循環バッファに書き込まれ、ディスクにいつでも保存できます。

このフローには次の 3 つのバッファキューが含まれます。

  • App - アプリは SurfaceTexture のインスタンスを使用してカメラからフレームを受信し、外部の GLES テクスチャに変換します。
  • SurfaceFlinger - アプリは SurfaceView のインスタンスを宣言してフレームを表示します。
  • MediaServer - 入力サーフェスで MediaCodec エンコーダを構成して動画を作成します。

下の図では、矢印はカメラからのデータの伝播を示しています。BufferQueue のインスタンスは色付きです(プロデューサーは青緑色、コンシューマーは緑色です)。

Grafika 連続キャプチャ アクティビティ

図 1. Grafika の連続キャプチャ アクティビティ

エンコードされた H.264 動画は、アプリのプロセスで RAM の循環バッファに移動します。ユーザーがキャプチャ ボタンを押すと、MediaMuxer クラスは、エンコードされた動画をディスク上の MP4 ファイルに書き込みます。

BufferQueue のすべてのインスタンスはアプリ内の単一の EGL コンテキストで処理され、GLES オペレーションは UI スレッドで実行されます。エンコードされたデータの処理(循環バッファの管理とディスクへの書き込み)は別のスレッドで行われます。

SurfaceView クラスを使用する場合、surfaceCreated() コールバックは、ディスプレイと動画エンコーダ用の EGLContextEGLSurface のインスタンスを作成します。新しいフレームが届くと、SurfaceTexture は次の 4 つのアクティビティを実行します。
  1. フレームを取得する。
  2. フレームを GLES テクスチャとして使用可能にする。
  3. フレームを GLES コマンドでレンダリングする。
  4. EGLSurface のインスタンスごとに変換とタイムスタンプを転送する。

その後、エンコーダのスレッドはエンコードされた出力を MediaCodec から取得し、メモリ内に保管します。

安全なテクスチャ動画の再生

Android は、保護された動画コンテンツの GPU 後処理をサポートします。これにより、アプリが GPU を使用して、複雑な非線形動画エフェクト(例: ワープ)、一般的なグラフィック シーンでの使用を目的とした、保護された動画コンテンツのテクスチャへのマッピング(例: GLES の使用)、バーチャル リアリティ(VR)を実現できるようになります。

安全なテクスチャ動画の再生

図 2. 安全なテクスチャ動画の再生

サポートを有効にするには、次の 2 つの拡張機能を使用します。

  • EGL 拡張機能 -(EGL_EXT_protected_content)どちらも保護されたコンテンツ上で動作する、保護された GL コンテキストとサーフェスの作成を可能にします。
  • GLES 拡張機能 -(GL_EXT_protected_textures)タグ付けテクスチャを保護対象として有効にし、フレームバッファのテクスチャ アタッチメントとして使用できるようにします。

Android は、ウィンドウのサーフェスが SurfaceFlinger に対しキューを実行しない場合や、保護されたコンテキスト内で使用する動画サーフェスを用意している場合でも、保護されたコンテンツを送信できるように SurfaceTexture と ACodec(libstagefright.so)を有効にします。これを実行するには、保護されたコンシューマーのビット(GRALLOC_USAGE_PROTECTED)を、保護されたコンテキスト(ACodec で検証済み)で作成されたサーフェスで設定します。

安全なテクスチャ動画の再生は、OpenGL ES 環境での強力な DRM 実装の基盤となります。Widevine Level 1 などの強力な DRM 実装がないと、多くのコンテンツ プロバイダが OpenGL ES 環境で高価値コンテンツのレンダリングを許可しないため、DRM で保護されたコンテンツを VR で再生するなどの VR の重要なユースケースがブロックされます。

AOSP には、安全なテクスチャ動画再生のフレームワーク コードが含まれています。ドライバのサポートは OEM に委ねられます。デバイスの実装者は、EGL_EXT_protected_contentGL_EXT_protected_textures extensions を実装する必要があります。(libstagefright を置き換えるために)独自のコーデック ライブラリを使用している場合は、コンシューマの使用ビットに GRALLOC_USAGE_PROTECTED が含まれる限り、GRALLOC_USAGE_PROTECTED のマークが付いたバッファを(ANativeWindow がウィンドウの作成者に対しキューを直接実行していなくても)ANativeWindow に送信できるようにする /frameworks/av/media/libstagefright/SurfaceUtils.cpp の変更を確認してください。拡張機能の実装に関する詳細なドキュメントについては、Khronos レジストリ(EGL_EXT_protected_contentGL_EXT_protected_textures)をご覧ください。