Android 12 で導入された互換性のあるメディアのコード変換は、アプリとの互換性を維持しながら、デバイスでストレージ効率に優れた最新のメディア形式(HEVC など)を使用して動画をキャプチャできるようにする機能です。この機能を使用すると、デバイス メーカーは AVC の代わりに HEVC をデフォルトで使用して、動画の品質を改善しながら、ストレージと帯域幅の要件を低減することができます。互換性のあるメディアのコード変換機能が有効になっている Android デバイスで、HEVC や HDR などの形式で動画(最大 1 分)を撮影し、その形式に対応していないアプリで開くと、対応する形式に自動的に変換されます。そのため、デバイスでキャプチャされた動画が新しい形式でも、アプリは問題なく機能します。
互換性のあるメディアのコード変換機能はデフォルトでオフになっています。メディアのコード変換をリクエストするには、アプリでメディア機能を宣言する必要があります。メディア機能の宣言について詳しくは、Android デベロッパー サイトの互換性のあるメディアのコード変換をご覧ください。
仕組み
互換性のあるメディアのコード変換機能は、主に 2 つの部分で構成されています。
- メディア フレームワークのコード変換サービス: これらのサービスでは、ファイルをある形式から別の形式に変換する際にハードウェアを使用して、低レイテンシかつ高品質な変換を実現します。これには、コード変換 API、コード変換サービス、カスタム フィルタ用の OEM プラグイン、ハードウェアが含まれます。詳細については、アーキテクチャの概要をご覧ください。
- メディア プロバイダの互換性のあるメディアのコード変換機能: このメディア プロバイダ内のコンポーネントは、メディア ファイルにアクセスするアプリをインターセプトし、アプリで宣言された機能に基づいて、元のファイルまたはコード変換されたファイルを提供します。アプリがメディア ファイルの形式をサポートしている場合は、特別な処理は必要ありません。アプリがメディア ファイルの形式をサポートしていない場合、アプリがファイルにアクセスすると、フレームワークはファイルを古い形式(AVC など)に変換します。
図 1 に、メディアのコード変換プロセスの概要を示します。
図 1. 互換性のあるメディアのコード変換の概要。
サポートされている形式
互換性のあるメディアのコード変換機能では、次の形式の変換がサポートされています。
- HEVC(8 ビット)から AVC への変換: コーデックの変換は、1 つの MediaCodec デコーダと 1 つの Mediacode エンコーダを接続して行います。
- HDR10+(10 ビット)から AVC(SDR)へ: HDR から SDR への変換は、MediaCodec インスタンスと、デコーダ インスタンスへのベンダー プラグイン フックを使用して実行されます。詳しくは、HDR から SDR へのエンコードをご覧ください。
サポートされているコンテンツ ソース
互換性のあるメディアのコード変換機能は、ネイティブ OEM カメラアプリによって生成されたデバイス上のメディアをサポートします。このメディアは、プライマリ外部ボリュームの DCIM/Camera/
フォルダに保存されます。この機能は、セカンダリ ストレージ上のメディアをサポートしていません。
メールや SD カードからデバイスに渡されるコンテンツはサポートされていません。
アプリはさまざまなファイルパスに基づいてファイルにアクセスします。以下は、コード変換が有効になる、またはバイパスされるファイルパスです。
コード変換が有効になるパス:
- MediaStore API を介したアプリのアクセス
- Java やネイティブ コードを含むダイレクト ファイルパス API を介したアプリのアクセス
- ストレージ アクセス フレームワーク(SAF)を介したアプリのアクセス
- OS 共有シート インテントを介したアプリのアクセス(MediaStore URI のみ)。
- スマートフォンから PC への MTP/PTP ファイル転送
コード変換がバイパスされるパス:
- SD カードの取り出しによるデバイスからのファイル転送
- ニアバイシェアや Bluetooth 転送などのオプションを使用した、デバイス間でのファイル転送
コード変換用のカスタマイズされたファイルパスを追加する
必要に応じて、デバイス メーカーは DCIM/
ディレクトリ内にあるメディアのコード変換用のファイルパスを追加できます。DCIM/
ディレクトリを含まないパスは承認されません。
携帯通信会社の要件や地域の規制を遵守するためにこのようなファイルパスの追加が必要になることがあります。
ファイルパスを追加するには、コード変換パスのランタイム リソース オーバーレイ(RRO)、config_supported_transcoding_relative_paths
を使用します。以下に、ファイルパスの追加方法の例を示します。
<string-array name="config_supported_transcoding_relative_paths" translatable="false">
<item>DCIM/JCF/</item>
</string-array>
設定されたファイルパスを確認するには、次のコマンドを使用します。
adb shell dumpsys activity provider com.google.android.providers.media.module/com.android.providers.media.MediaProvider | head -n 20
アーキテクチャの概要
このセクションでは、メディアのコード変換機能のアーキテクチャについて説明します。
図 2. メディアのコード変換のアーキテクチャ。
メディアのコード変換のアーキテクチャは、次のコンポーネントで構成されています。
- MediaTranscodingManager system API: クライアントが MediaTranscoding サービスと通信できるようにするインターフェース。MediaProvider モジュールはこの API を使用します。
- MediaTranscodingService: クライアント接続を管理し、コード変換リクエストをスケジュールし、
TranscodingSessions
のブックキーピングを管理するネイティブ サービス。 - MediaTranscoder: コード変換を実行するネイティブ ライブラリ。このライブラリは、モジュールと互換性を持たせるためにメディア フレームワーク NDK 上にビルドされています。
互換性のあるメディアのコード変換機能は、サービスとメディアのトランスコーダの両方で、コード変換指標をログに記録します。クライアント サイドとサービスサイドのコードは MediaProvider モジュールにあるため、バグの修正と更新を迅速に行うことができます。
ファイルへのアクセス
互換性のあるメディアのコード変換は、Filesystem in Userspace(FUSE)ファイルシステム上にビルドされ、対象範囲別ストレージで使用されます。FUSE により、MediaProvider モジュールはユーザー空間でのファイル オペレーションを調べ、アクセスを許可、拒否、または匿名化するためのポリシーに基づいてファイルへのアクセスを制御することができます。
アプリがファイルにアクセスしようとすると、FUSE デーモンはそのアプリからのファイルの読み取りアクセスをインターセプトします。アプリが新しい形式(HEVC など)をサポートしている場合は、元のファイルが返されます。アプリがその形式をサポートしていない場合、ファイルは古い形式(AVC など)にコード変換されます。変換されたバージョンがある場合は、ファイルはキャッシュから返されます。
コード変換されたファイルをリクエストする
互換性のあるメディアのコード変換機能はデフォルトで無効になっています。つまり、デバイスが HEVC に対応している場合、Android はアプリのマニフェスト ファイル、または強制コード変換リストで指定されない限り、ファイルをコード変換しません。
アプリは、コード変換されたアセットを以下の方法でリクエストできます。
- マニフェスト ファイルで、サポートされていない形式を宣言する。詳しくは、リソース内で機能を宣言するとコード内で機能を宣言するをご覧ください。
- MediaProvider モジュールに含まれている強制コード変換リストにアプリを追加する。これにより、マニフェスト ファイルが更新されていないアプリに対してコード変換が可能になります。サポートされていない形式を追加してアプリのマニフェスト ファイルを更新した場合、そのアプリを強制コード変換リストから削除する必要があります。デバイス メーカーは、パッチの送信またはバグの報告により、アプリを強制コード変換リストに追加または削除するように指定できます。リストは Android チームによって定期的に確認され、リストからアプリが削除されます。
- 実行時にアプリの互換性フレームワークでサポートされている形式を無効にする(ユーザーが [設定] でアプリごとに無効にすることもできます)。
openTypedAssetFileDescriptor
API でサポートされていない形式を明示的に指定し、MediaStore
でファイルを開く。
USB 転送(デバイスから PC)の場合、コード変換はデフォルトで無効になっていますが、図 3 に示すように、[USB の設定] 設定画面で [動画を AVC に変換] トグルを使用して、コード変換を有効にすることもできます。
図 3. [USB の設定] 画面でメディアのコード変換を有効にする。
コード変換されたファイルのリクエストに関する制限
コード変換のリクエストによりシステム リソースが長時間ロックされないようにするために、コード変換セッションをリクエストするアプリには、以下の制限が適用されます。
- リクエストは連続して 10 回まで
- リクエストの合計実行時間は 3 分まで
アプリがこれらの制限をすべて超えた場合、フレームワークにより元のファイル記述子が返されます。
デバイスの要件
互換性のあるメディアのコード変換機能をサポートするには、デバイスが次の要件を満たしている必要があります。
- デバイスのネイティブ カメラアプリで、デフォルトで HEVC エンコードが有効になっている
- (HDR から SDR へのコード変換をサポートするデバイス)デバイスが HDR 動画のキャプチャをサポートしている
デバイスでメディアのコード変換を確実に実行するには、動画ハードウェアとストレージの読み取りと書き込みのパフォーマンスを最適化する必要があります。メディア コーデックの優先度が 1
に設定されている場合、コーデックは最高のスループットで動作する必要があります。コード変換のパフォーマンスは 200 fps 以上にすることをおすすめします。ハードウェアのパフォーマンスをテストするには、frameworks/av/media/libmediatranscoding/transcoder/benchmark
でメディア トランスコーダ ベンチマークを実行します。
検証
互換性のあるメディアのコード変換機能を検証するには、次の CTS テストを実行します。
android.media.mediatranscoding.cts
android.mediaprovidertranscode.cts
メディアのコード変換をグローバルで有効にする
メディアのコード変換フレームワークまたはコード変換時のアプリの動作をテストするには、互換性のあるメディアのコード変換機能をグローバルで有効または無効にします。[設定] > [システム] > [開発者向けオプション] > [メディアのコード変換設定] で、[デフォルトのコード変換をオーバーライド] を有効、[コード変換を有効にする] を有効または無効に設定します。この設定を有効にした場合、開発中のアプリ以外のアプリのバックグラウンドで、メディアのコード変換が発生する可能性があります。
コード変換のステータスを確認する
テストでは、次の ADB シェルコマンドを使用して、コード変換のステータス(現在および過去のコード変換セッションを含む)を確認できます。
adb shell dumpsys media.transcoding
動画の長さ制限を延長する
テスト目的のため、次のコマンドを使用することで、コード変換の動画の長さの制限時間を 1 分間より長くできます。このコマンドを実行した後は、再起動が必要になることがあります。
adb shell device_config put storage_native_boot transcode_max_duration_ms <LARGE_NUMBER_IN_MS>
AOSP のソースとリファレンス
互換性のあるメディアのコード変換に関連する AOSP ソースコードは次のとおりです。
Transcoding System API(MediaProvider でのみ使用)
ApplicationMediaCapabilities API
frameworks/base/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
MediaTranscoding サービス
frameworks/av/services/mediatranscoding/
frameworks/av/media/libmediatranscoding/
ネイティブ MediaTranscoder
frameworks/av/media/libmediatranscoding/transcoder
MediaTranscoder 用の HDR サンプル プラグイン
MediaProvider ファイルのインターセプトおよびコード変換用コード
MediaTranscoder ベンチマーク
frameworks/av/media/libmediatranscoding/transcoder/benchmark
CTS テスト
cts/tests/tests/mediatranscoding/
HDR から SDR へのエンコード
HDR から SDR へのエンコードをサポートするために、デバイス メーカーは /platform/frameworks/av/media/codec2/hidl/plugin/
にある AOSP サンプル コーデック 2.0 フィルタ プラグインを使用できます。このセクションでは、フィルタ プラグインの仕組み、プラグインの実装方法、プラグインのテスト方法について説明します。
HDR から SDR へのエンコードをサポートするプラグインがデバイスにない場合、HDR 動画にアクセスするアプリは、マニフェストで宣言されているアプリのメディア機能に関係なく、元のファイル記述子を取得します。
仕組み
このセクションでは、Codec 2.0 フィルタ プラグインの一般的な動作について説明します。
背景
Android には、Codec 2.0 インターフェースと android::hardware::media::c2
の android.hardware.media.c2
HAL インターフェースの間に適合レイヤが実装されています。AOSP には、フィルタ プラグイン用に、デコーダをフィルタ プラグインと一緒にラップするラッパー メカニズムが含まれています。MediaCodec
は、ラップされたこれらのコンポーネントをフィルタリング機能を備えたデコーダとして認識します。
概要
FilterWrapper
クラスはベンダー コーデックを受け取り、ラップされたコーデックを media.c2
適合レイヤに返します。FilterWrapper
クラスは、FilterWrapper::Plugin
API を介して libc2filterplugin.so
を読み込み、プラグインから利用可能なフィルタを記録します。作成時に、FilterWrapper
は使用可能なすべてのフィルタをインスタンス化します。バッファを変更するフィルタのみが起動時に開始されます。
図 1. フィルタ プラグイン アーキテクチャ。
フィルタ プラグイン インターフェース
FilterPlugin.h
インターフェースは、フィルタを公開する次の API を定義します。
std::shared_ptr<C2ComponentStore>getComponentStore()
フィルタが格納されている
C2ComponentStore
オブジェクトを返します。これは、ベンダーの Codec 2.0 実装で公開されるものとは異なります。通常、このストアにはFilterWrapper
クラスが使用するフィルタのみが含まれます。bool describe(C2String name, Descriptor *desc)
C2ComponentStore
の内容に加えて、フィルタについて記述します。次の記述を定義します。controlParam
: フィルタの動作を制御するパラメータ。たとえば、HDR から SDR へのトーンマッパーの場合、コントロール パラメータはターゲット転送関数です。affectedParams
: フィルタリング オペレーションの影響を受けるパラメータ。たとえば、HDR から SDR へのトーンマッパーの場合、影響を受けるパラメータは色相です。
bool isFilteringEnabled(const std::shared_ptr<C2ComponentInterface> &intf)
フィルタ コンポーネントがバッファを変更した場合に、
true
を返します。たとえば、ターゲット伝達関数が SDR で、入力伝達関数が HDR(HLG または PQ)の場合、トーン マッピング フィルタはtrue
を返します。
FilterWrapper の詳細
このセクションでは、FilterWrapper
クラスの詳細について説明します。
作成
ラップされたコンポーネントは、基盤となるデコーダと作成時に定義されたすべてのフィルタをインスタンス化します。
クエリと設定
ラップされたコンポーネントは、フィルタの記述に従って、受信パラメータをクエリまたは設定リクエストから分離します。たとえば、フィルタ コントロール パラメータの設定は対応するフィルタにルーティングされ、フィルタの影響を受けるパラメータはクエリに存在します(影響を受けないパラメータを持つデコーダからは読み取りません)。
図 2. クエリと設定。
開始
開始時に、ラップされたコンポーネントは、デコーダとバッファを変更するすべてのフィルタを起動します。有効なフィルタがない場合、ラップされたコンポーネントはデコーダとパススルー バッファを開始し、デコーダ自体にコマンドを送信します。
バッファ処理
図 3. バッファ処理。
ラップされたデコーダのキューに追加されたバッファは、基盤となるデコーダに移動します。ラップされたコンポーネントは、onWorkDone_nb()
コールバックを通じてデコーダから出力バッファを取得し、フィルタのキューに入れます。最後のフィルタからの最終出力バッファがクライアントにレポートされます。
このバッファ処理が機能するためには、ラップされたコンポーネントが C2PortBlockPoolsTuning
を最後のフィルタに設定して、想定するブロックプールからのバッファをフレームワークが出力するように設定する必要があります。
停止、リセット、リリース
停止時に、ラップされたコンポーネントはデコーダと開始済みの有効なフィルタをすべて停止します。リセット時とリリース時に、有効になっているかどうかにかかわらず、すべてのコンポーネントがリセットまたはリリースされます。
サンプル フィルタ プラグインを実装する
プラグインを有効にする手順は次のとおりです。
FilterPlugin
インターフェースをライブラリに実装して、/vendor/lib[64]/libc2filterplugin.so.
にドロップします。- 必要に応じて、
mediacodec.te
に権限を追加します。 - 適合レイヤを Android 12 に更新し、
media.c2
サービスを再ビルドします。
プラグインをテストする
サンプル プラグインをテストする方法は次のとおりです。
- デバイスを再ビルドしてフラッシュします。
次のコマンドを使用してサンプル プラグインをビルドします。
m sample-codec2-filter-plugin
デバイスを再マウントし、コーデック サービスが認識できるようにベンダー プラグインの名前を変更します。
adb root adb remount adb reboot adb wait-for-device adb root adb remount adb push /out/target/<...>/lib64/sample-codec2-filter-plugin.so \ /vendor/lib64/libc2filterplugin.so adb push /out/target/<...>/lib/sample-codec2-filter-plugin.so \ /vendor/lib/libc2filterplugin.so adb reboot