MediaCodecを理解しようと思い、リファレンスの Class OverView を読むついでに訳しました。まだよく理解できず、変な訳になってるところもあります。
Class Overview
MediaCodec クラスは低レベルなメディアコーデック ( つまりエンコーダやデコーダ ) にアクセスするために使用できます。Androidの低レベルなマルチメディアサポートインフラの一部です。 ( 通常、MediaExtractor や MediaSync、MediaMuxer、MediaCrypto、MediaDrm、Image、Surface、AudioTrack などと共に使用されます。 )
ざっくり言うと、コーデックは入力データを処理して、出力データを生成します。データを非同期で処理し、入力と出力のバッファをセットで使用します。その工程を単純化すると、クライアント ( あなた ) が空の入力バッファをリクエスト(またはレシーブ)し、そのバッファをデータで埋めた後に、処理を行うためにコーデックに送ります。コーデックは送られてきたデータを変換し、空の出力バッファを埋めていきます。最終的に、クライアントは埋められた出力バッファをリクエスト(またはレシーブ)し、そのコンテンツを消費し、コーデックへ戻して解放します。
Data Types
コーデックは3種類のデータを操作します。圧縮データ、RAW オーディオデータ、RAW ビデオデータです。3種類のデータ全てが ByteBuffers を使用して処理することが出来ます。ただし、 RAW ビデオデータを扱う場合は、コーデックのパフォーマンスを上げるために、Surface を使うべきです。Surface はネイティブビデオバッファを ByteBuffers へのマッピングやコピーなしに使用します。したがって、より効率的です。通常、Surface の使用中は RAW ビデオデータにアクセス出来ませんが、 ImageReader クラスを使用し、デコードされた(RAW) ビデオフレームにアクセス出来ます。これは、いくつかのネイティブバッファが direct ByteBuffers へマッピングできるように、 ByteBuffers を使用するよりも効果的となり得るかもしれません。ByteBuffer モードを使用していると、Image クラス と getInput や OutputImage(int) メソッドを使用して、Raw ビデオフレームにアクセス出来ます。
Compressed Buffers
(デコーダ用)入力バッファと(エンコーダ用)出力バッファはフォーマット形式に応じた 圧縮データを含んでいます。ビデオ形式の場合、これは単一の圧縮されたビデオフレームです。オーディオデータの場合、これは通常、単一のアクセスユニット ( フォーマット形式により記録された、一般的にはオーディオの数ミリ秒 を含むエンコードされたオーディオセグメント) ですが、ひとつのバッファが多重にエンコードされたオーディオのアクセスユニットを含むかもしれないという点で、この要件はわずかに緩いです。どちらにせよ、バッファは任意のバイト間においてではなく、むしろフレームやアクセスユニットの間で開始/終了します。
Raw Audio Buffers
RAW オーディオバッファは、チャンネル順で各チャンネル毎に1サンプルであるPCM オーディオデータの全フレームを含んでいます。それぞれのサンプルは ネイティブバイト順で符号付の16ビット整数値です。
short[] getSamplesForChannel(MediaCodec codec, int bufferId, int channelIx) {
ByteBuffer outputBuffer = codec.getOutputBuffer(bufferId);
MediaFormat format = codec.getOutputFormat(bufferId);
ShortBuffer samples = outputBuffer.order(ByteOrder.nativeOrder()).asShortBuffer();
int numChannels = formet.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
if (channelIx < 0 || channelIx >= numChannels) {
return null;
}
short[] res = new short[samples.remaining() / numChannels];
for (int i = 0; i < res.length; ++i) {
res[i] = samples.get(i * numChannels + channelIx);
}
return res;
}
Raw Video Buffers
ByteBuffer モードにおいて、ビデオバッファはそれらのカラーフォーマットによりレイアウトされます。サポートされているカラーフォーマットは getCodeInfo().getCapabilitiesForType(...).ColoFormats
から1配列として取得可能です。ビデオコーデックは3種類のカラーフォーマットをサポートしています。
-
ネイティブ RAW ビデオフォーマット :
COLOR_FormatSurface
によって作られ、入力または出力 Surface と共に使用できます。 -
フレキシブル YUV バッファ ( 例えば
COLOR_FormatYUV420Flexble
) : これらは入力/出力 Surface と共に、getInput / OutputImage(int)
を使って、 ByteBuffer モードと同じように使用できます。 -
その他、特定のフォーマット : これらは通常 ByteBufferモードでのみサポートされる。幾つかのカラーフォマットはベンダー固有のものです。その他のものは
MediaCodecInfo.CodecCapabilities
に定められています。フレキシブルフォーマットに相当するカラーフォーマットの場合、まだgetInput / OutputImage(int)
が使用できます。
全てのビデオコーデックは LOLLIPOP_MR1
からフレキシブル YUV 4:2:0 バッファをサポートします。
States
コーデックには概念として3つのステート(状態)が存在します。Stopped と Excuting と Released です。Stopped は実際には集合ステートで、内部にさらに3つのステートが存在します。 Uninitialized と Configured と Errorです。その一方で Excuting もまた処理の工程として、3つのサブステートを持っています。Flushed と Running と End- of-Stream です。
ファクトリメソッドの1つを使ってコードを作成するとき、コーデックは Uninitializes ステートにいます。まず最初に、 configure(...)
により Configured ステートに移動し、コーデックを構成にする必要があります。そこで start()
を呼び出すと、 Excuting ステートへ移動します。このステート内で、上で解説したように、データをバッファの列を通して処理します。
Executing ステートは3つのサブステートを持っています。 Flushed、Running、End-of-Stream です。start()
を実行するとすぐにコーデックは Flushed サブステートに入ります。Flushed サブステートでは全バッファを保持します。最初の入力バッファが取り出されるとすぐに、コーデックはRunning サブステートへ移動します。 ここで最も多くの時間を費やします。入力バッファが end-of-stream maker
と共に格納されると、コーデックは End-of-Stream サブステートへ移ります。このステートに入るとコーデックはそれ以上の入力バッファを受け入れません。しかし、end-of-stream
が出力へ到達するまでは、まだ出力バッファを生成します。Executing ステート内で flush()
を使用すれば、いつでも Flushed サブステートへ戻ることができます。
コーデックを Uninitialized ステートへ戻すには stop()
を使用します。その結果、再び使用するには、再度 Configured へ移動する必要があります。
コーデックの使用が完了した場合、 release()
によりコーデックを解放しなければなりません。
稀に、コーデックがエラーに遭遇し、 Error ステートへ移動するかもしれません。これはキューイングオペレーションから、または時折 Exception を経由して、無効な戻り値を使って通信されます。コーデックを再び利用可能にするには reset()
を呼び出します。reset()
はどのステートからでも呼び出すことができ、コーデックを Uninitialized ステートへ戻します。その他では、 release()
を呼び出すと、Released ステートへ移動します。
Creation
特定の MediaFormat
の MediaCodec を作成するにはMediaCodecList
を使用します。 ファイルをデコード、またはストリームしているとき、MediaExtractor.getTrackFomat
からお望みのフォーマトを取得できます。MediaFormat.setFeatureEnabled
を使用して追加したい任意の機能を挿入し、特定のメディアフォーマットを扱えるコーデックの名前を取得するためにMediaCodecList.findDecoderForFormat
を呼びます。最後に、 createByCodecname(String)
を使用してコーデックを作成します。
LOLIPOP
では、MediaCodecList.findDecoder / EncoderForFormat
へのフォーマットに フレームレートを含んではいけません。format.setString(MediaFormat.KEY_FRAME_RATE, null)
を使用して、フォーマット内に存在するフレームレート設定を消去してください。
createDecoder / EncoderByType(String)
を使用して、特定のMIMEタイプに合ったコーデックをせく制することも可能です。しかしながら、これは機能を挿入することができず、作成されたコーデックは、特定の望ましいメディアフォーマットを扱えません。
Creating secure decoders
KITKAT_WATCH
以前のバージョンでは、セキュアコーデックは MediaCodecList
には記載されていない場合が有りますが、システム上ではまだ使用可能な場合も有ります。
セキュアコーデックは、名前だけ、またはレギュラーコーデックの名前に ".secure"
を追加することで、インスタンスを生成できます。(全てのセキュアコーデックの名前は ".secure"
で終わらなければならない。) コーデックがシステム上に存在しなければ、createByCodecname(String)
は IOException
を投げます。
LOLIPOP
以降は、セキュアデコーダーを作成するためには、メディアフォーマット内の FEATURE_SecurePlayback
機能を使用すべきです。
Initialization
コーデックを作成した後、非同期でデータを処理したいなら、setCallback
を使用してコールバックをセットできます。その時、 特定のメディアフォーマットを使用してコーデックを構成します。ビデオ制作元 ( 例えば、 ビデオデコーダのようなRAWビデオデータを生成するコーデック ) 用に 出力 Surface
を指定できるのがこのときです。セキュアコーデック( MediaCrypto
参照 ) 用に復元パラメータをセットできるもこのときです。最後に、いくつかのコーデックが多重モードで操作可能なので、デコーダかエンコーダのどちらとして機能してほしいか指定しなければなりません。
LOLIPOP
からは、Configured ステート内で結果としての入力と出力の形式を照会できます。これを使用して、コーデックを開始する前に、(例えばカラーフォーマットの ) 結果としての構成を確認できます。
もし、生の入力ビデオバッファをビデオコンシューマ ( ビデオエンコーダのような、RAW ビデオ入力を処理するコーデック ) とネイティブに処理したいなら、構成後に createInputSurface()
を使用して入力データの行き先用の Surface を作成します。あるいは、setInputSurface(Surface)
を呼んで、あらかじめ作成していた persistent input surface
を使用するようにセットアップします。
Codec-specific Data
幾つかのフォーマット、特に AAC オーディオや MPEG4、H.264 や H.265 ビデオフォーマットは、セットアップデータまたはコーデック固有のデータを含むバッファの数により付加されるために、実際のデータを必要とします。そのような圧縮フォーマットを処理するとき、このデータは start()
の後に、どのフレームデータよりも先にコーデックへ提出されなければなりません。そのようなデータは、queueInputBuffer
への呼び出しの中で、 BUFFER_FLAG_CODEC_CONFIG
フラグを使用してマークされなければなりません。
コーデック固有のデータは、"csd-0"や"csd-1"などのキーと共にByteBufferエントリの中に構成するために渡されるフォーマットに含ませることも可能です。これらのキーは常に、MediaExtractor
から入手した MediaFormat
トラックの中に含まれます。そのフォーマットの中のコーデック固有のデータは strt()
上で自動的にコーデックに サブミットされます。手動でサブミットしてはいけません。もしフォーマットがコーデック固有のデータを含んでいなかったら、フォーマットの要望に応じて、正しい順序で指定されたバッファの番号を使用して、それをサブミットすることもできます。あるいは、全コーデック固有データを連結し、単一のコーデック設定バッファとしてサブミットすることができます。
Android は以下のコーデック固有のデータバッファを使用します。これらはまた、適当な MediaMuxer
トラック構成 トラックフォーマットにセットされる必要が有ります。それぞれのパラメータセットと(*)でマークされたコーデック固有データセクションはスタートコード "\x00\x00\x00\x01"
で開始されなければなりません。
フォーマット | CSD buffer #0 | CSD buffer #1 | CSD buffer |
---|---|---|---|
AAC | ESDS* からのデコーダ固有の情報 | 不使用 | 不使用 |
VORBIS | 識別ヘッダ | セットアップヘッダ | 不使用 |
OPUS | 識別ヘッダ | ナノ秒での事前スキップ (符号なし64ビットのネイティブ順整数) これは識別ヘッダ内の事前スキップ値を上書きする | ナノ秒でのシークの事前ロール (符号なし64ビットのネイティブ順整数) |
MPEG-4 | ESDS* からのデコーダ固有の情報 | 不使用 | 不使用 |
H.264 AVC | SPS (シーケンスパラメータセット*) | PPS (ピクチャパラメータセット*) | 不使用 |
H.265 HEVC | VPS (ビデオパラメータセット*) + |
SPS (シーケンスパラメータセット) +
PPS (ピクチャパラメータセット)|不使用|不使用|
出力バッファや出力フォーマットの変更が返される前に、コーデックが
start()
で Flushed ステートになると、コーデック固有のデータが flush の間に失われるかもしれないので、注意する必要があります。適切なコーデックオペレーションを保証するために、そのような フラッシュ の後に、BUFFER_FLAG_CODEC_CONFIG
でマークされたバッファを使用して、 データを再度サブミットしなければなりません。
エンコーダ ( または 圧縮データを生成するコーデック ) は、codec-config flag
でマークされた 出力バッファの中の有効な出力バッファの前にコーデック固有のデータを作成し、返します。 コーデック固有データを含むバッファは、意味のあるタイムスタンプを持ちません。
Data Processing
それぞれのコーデックは入出力バッファのセットを保持し、APIコールの中でバッファIDによって呼び出されます。start()
の呼び出しが成功した後は、クライアントは入力も出力もどちらのバッファも"所有していません"。同期モードの場合、コーデックから入力または出力バッファを入手する ( オーナー権限を取得する ) ために、dequeueInput / OutputBuffer(...)
を呼びます。非同期モードの場合は、MediaCodec.Callback.onInput / OutputBufferAvailable(...)
コールバック経由で、自動的に利用可能なバッファを受け取ります。
入力バッファを入手するとすぐに、データで埋めて queueInputBuffer
( もし復元を使用する場合は queueSecureInputBuffer
) を使用してコーデックへサブミットします。 多重の入力バッファを同じタイムスタンプでサブミットしてはいけません(それがそのようにマークされたコーデック固有のデータでない限り)。
処理中のコーデックは、非同期モードの onPutputBufferAvailable
コールバック経由で読み込み専用の出力バッファを返します。出力バッファの処理が完了したら、バッファをコーデックへ返すために releaseOutputBuffer
メドッドの1つを呼びます。
バッファをすぐにコーデックへ再サブミット/リリースする必要がない場合、その間に入出力(またはその一方)のバッファを固持するのは、コーデックを失速させます。この振る舞いはデバイスに依存します。具体的には、コーデックが出力バッファを生成を、全ての未納バッファがリリース/再サブミットされ終わるまで、保留する可能性があります。 従って、可能な限り小さな利用可能バッファを固持するように努めましょう。
APIのバージョン次第で、3つの処理方法があります。
処理モード | APIバージョンが20以下 ( Jelly Bean / KitKat ) | APIバージョンが21以上 ( Lollipop 以降 ) |
---|---|---|
バッファ配列を使用した同期型API | サポートしている | 非推奨 |
バッファを使用した同期型API | 使用不可 | サポートしている |
バッファを使用した非同期型API | 使用不可 | サポートしている |
Asynchronous Processing using Buffers
LOLLIPOP
から、configure
を呼び出す前に、コールバックを設定し、 非同期でデータを処理することが望ましいです。Running サブステートへ遷移して入力バッファの受け取りを開始するためには、flush()
の後に start()
を呼び出さないといけないので、非同期モードではステート遷移をわずかに変更します。start
の初期コールでコーデックは直接 Runnning サブステートへ移動し、コールバック経由で利用可能な入力バッファの通過を開始します。
非同期モードにおける MediaCodec は一般的に以下のように使用されます。
MediaCodec codec = MediaCodec.createByCodecName(name);
MediaFormat mOutputFormat; // メンバ変数
codec.setCallback(new MediaCodec.Callback() {
@Override
void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
// 有効なデータで inputBuffer を埋める
…
codec.queueInputBuffer(inputBufferId, …);
}
@Override
void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // オプション A
// bufferFormat は mOutputFormat と同じ
// outputBuffer は処理される(または表示される)準備ができている
…
codec.releaseOutputBuffer(outputBufferId, …);
}
@Override
void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
// サブシーケンスデータは新しいフォーマットへ適合される
// getOutputFormat(outputBufferId) を使用するなら無視できる
mOutputFormat = format; // オプション B
}
@Override
void onError(…) {
…
}
});
codec.configure(format, …);
mOutputFormat = codec.getOutputFormat(); // オプション B
codec.start();
// 処理が完了するのを待つ
codec.stop();
codec.release();
Synchronous Processing using Buffers
LOLLIPOP
以降は、同期モードでコーデックを使用する場合でも、getInput
/ OutputBuffer(int)
や getInput
/ OutputImage(int)
を使用して入出力バッファを取り出すべきです。これはフレームワークによるある程度の最適化を許します。例えば、動的コンテンツを処理するときです。もし getInput
/ OutputBuffers()
を呼ぶと、この最適化は使用不可にされます。
バッファとバッファ配列を使用するメソッドを同じタイミングにミックスしてはいけません。特に、
start()
の直後やINFO_OUTPUT_FORMAT_CHANGED
の値と一緒に出力バッファIDがキューから取り出された直後は、getInput
/OutputBuffers
を直接呼び出すだけにしましょう。
同期モードにおける MediaCodec は一般的に以下のように使われます。
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
MediaFormat outputFormat = codec.getOutputFormat(); // オプション B
codec.start();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
if (inputBufferId >= 0) {
ByteBuffer inputBuffer = codec.getInputBuffer(…);
// 有効なデータで inputBuffer を埋める
…
codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // オプション A
// bufferFormat は outputFormat と同一
// outputBuffer は処理される(または表示される)準備ができている
…
codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// サブシーケンスデータは新しいフォーマットへ適合される
// getOutputFormat(outputBufferId) を使用する場合は無視する
outputFormat = codec.getOutputFormat(); // オプション B
}
}
codec.stop();
codec.release();
Synchronous Processing using Buffer Arrays (deprecated)
KITKAT_WATCH
以前のバージョンでは、 入出力バッファのセットは ByteBuffer[]
配列によって表されます。 start()
コールが成功した後、getInput
/ OutputBuffers()
を使用して バッファ配列を取り出します。以下に示すように、これらの配列(ネガティブではないとき)へのインデックスとして、バッファIDを使用してください。配列のサイズとシステムにより使用される入出力バッファの数との固有の関係はないのにもかかわらず、配列のサイズは上限を提供します。
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
codec.start();
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(…);
if (inputBufferId >= 0) {
// inputBuffers[inputBufferId] を有効なデータで埋める
…
codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
// outputBuffer は処理される(または表示される)準備ができている
…
codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffers = codec.getOutputBuffers();
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// サブシーケンスデータは新しいフォーマットに適合される
MediaFormat format = codec.getOutputFormat();
}
}
codec.stop();
codec.release();
End-of-stream Handling
入力データが終わりに到達したら、queueInput Buffer
へのコール内で BUFFER_FLAG_END_OF_STREAM
フラグを指定することにより、コーデックへ合図を送らなければなりません。これは、最後の有効な入力バッファ上か、あるいは、end-of-stream フラグセットと一緒に追加の空入力バッファをサブミットすることにより行うことができます。もしからのバッファを使用する場合は、タイムスタンプは無視されるでしょう。
dequeueOutputBuffer
の中、またはonOutputBufferAvailable
経由で返されるMediaCodec.BufferInfo
セットにある同じ end-of-stream フラグを指定することで、最後に出力ストリームの終わりを合図するまで、コーデックは出力バッファを返し続けます。フラグは最後の有効な出力バッファ上か、またはその後の空のバッファ上にセットできます。そのような空のバッファのタイムスタンプは無視されます。
コーデックが flushed か stopped 、または restared されない限り、入力ストリームの終わりを合図した後に、追加の入力バッファをサブミットしてはいけません。
Using an Output Surface
出力用 Surface
を使用したときのデータの処理は ByteBuffer モードとほとんど一致します。しかしながら、出力バッファはアクセス不可となり、 null
値として扱われます。例えば getOutputBuffer
/ Image(int)
がnullを返し、getOutputBuffers()
が null
だけを含んだ配列を返します。
出力 Surface を使用するとき、surface上の各出力バッファをレンダリングするかどうかを選択することができます。それには3つの選択肢があります。
-
バッファをレンダリングしない:
releaseOutputBuffer(bufferId, false)
をコール -
デフォルトのタイムスタンプを使用してバッファをレンダリング:
releaseOutputBuffer(bufferId, true)
をコール -
固有のタイムスタンプを使用してバッファをレンダリング:
releaseOutputBuffer(bufferId, timestamp)
をコール
Marshmallow
からは、デフォルトのタイムスタンプは(ナノ秒にコンバートされた)バッファの presentation timestamp
となります。その前までは定義されていませんでした。
Marshmallow
以降も、setOutputSurface
を使用して、動的に出力Surfaceを変更できます。
Using an Input Surface
入力 Surfaceを使用しているとき、アクセス可能な入力バッファはなく、バッファは自動的に入力Surfaceからコーデックへ通過します。dequeueInputBuffer
をコールすると、 IllefalStateException
を投げ、 getInputBuffers()
は偽の ByteBuffer[]
配列(絶対に書き込み禁止)を返します。
end-of-streamを記すためにsignalEndOfInputStream()
コールします。コール後すぐに、入力Surfaceはコーデックへのデータのサブミットを停止します。
Seeking & Adaptive Playback Support
ビデオ デコーダ(および圧縮されたビデオデータを消費する一般的なコーデック)は、シークやフォーマット変更に関して、それらが adaptive playback をサポートし、そのために構成されているかどうかで、異なる振る舞いをします。 CodecCapabilities.isFeatureSupported(String)
経由で、デコーダが adaptive playback をサポートしているかどうかを確認可能です。Surface上にデコードするコーデックを設定する場合は、ビデオデコーダのための adaptive playback サポートはアクティベートだけされます。
Stream Boundary and Key Frames
start()
やflush()
の後の入力データが、適切なストリームの境界で始まるということは重要です。最初のフレームはキーフレームです。キーフレームはその上に完全にデコードすることができ(ほとんどのコーデックために、これは Iフレームを意味します)、キーフレームがキーフレームの前のフレームを参照した後に表示されるフレームはありません。
以下のテーブルは様々なビデオフォーマットに適したキーフレームをまとめたものです。
フォーマット | 適したキーフレーム |
---|---|
VP9/VP8 | その後のフレームはこのフレームの前のフレームを参照しない適切なイントラフレーム。 (このようなキーフレームには特定の名前がありません) |
H.265 HEVC | IDR または CRA |
H.264 AVC | IDR |
MPEG-4 H.263 MPEG-2 | その後のフレームはこのフレームの前のフレームを参照しない、適切なIフレーム。 (このようなキーフレームには特定の名前がありません) |
adaptive playbackをサポートしないデコーダのために (Surface上にデコードしていない時を含める)
事前にサブミットされたデータに隣接していない(例えばシーク後の)データのデコードを開始するために、デコーダを flush しなければなりません。全ての出力バッファが flush 箇所ですぐに無効となるため、flush
をコールする前に end-of-stream を待つ信号が欲しいでしょう。flush後に入力データが
適切なストリームの境界/キーフレームで始まるということは重要です。
flush 後にサブミットされたデータのフォーマットは変更できません。
flush()
はフォーマットの中断をサポートしません。そのため、stop()
-configure(...)
-start()
のフルサイクルが必要です。
start()
後すぐにコーデックも flush する場合 - 通常は、最初の出力バッファや出力フォーマットの変更が受け取られる前 - 、コーデックにコーデック固有データの再サブミットが必要です。より詳細な情報はcodec-specific-data section
をみてください。
adaptive playbackをサポートし、構成されているデコーダのために
事前にサブミットされたデータに隣接していない(例えばシーク後の)データのデコードを開始するために、デコーダを flush する必要はありません。しかしながら、中断後の入力データは適したストリームの境界/キーフレームから開始しなければなりません。
幾つかのビデオフォーマット - 正確には H.264やH.265、VP8やVP9 - では、画像サイズやミッドストリームの構成を変更することも可能です。これを行うためには、新しいコーデック固有の構成データとキーフレームの全てを一緒に、単一バッファ(幾つかの開始コードを含む)の中に内包し、レギュラー入力バッファとしてサブミットしなければなりません。
画像サイズが変更が発生し、新しいサイズで幾つかのフレームが返される前に、dequeueOutputBuffer
や onOutputFormatChanged
コールバック から INFO_OUTPUT_FORMAT_CHANGED
戻り値を受け取ります。
コーデック固有データの場合、画像サイズの変更後すぐに
flush()
を呼び出す時は注意してください。画像サイズ変更の設定を受け取っていないと、新しいサイズのリクエストを繰り返す必要が発生します。
Error handling
createByCodecName
や createDecoder
/ EncoderByType
ファクトリメソッドは 失敗時に IOExceptionを投げるので、キャッチするか、上に渡す宣言をしなければなりません。 MediaCodec メソッドは、許可されていないコーデックステートからコールされると IllegalStateException
を投げます。これは通常、間違ったアプリケーションAPIの使用が原因です。セキュアバッファを含むメソッドは、getErrorCode()
から入手できる、さらなるエラー情報を含む MediaCodec.CryptoException
を投げることがあります。
MediaCodec.CodecException
内の、内部コーデックエラーの結果は、
アプリケーションが正常にAPIを利用している場合でも、メディアコンテンツの破損や、ハードウェア障害、リソースの枯渇などが原因となりえます。CodecExeption
を受け取った時に推奨するアクションは isRecoverable()
とisTransient()
をコールすることにより確定できます。
recoverable errors :
isRecoverable()
が true を返したら、stop()
、configure(...)
、start() をコールしてリカバーします。transient error :
isTransient()
か true を返したら、リソースが一時的に利用できなくなっており、後でメソッドを再試行します。fatal errors:
isRecoverable()
とisTransient()
が false を返したら、CodecException
は致命的で、コーデックをreset
またはrelease
しなければなりません
isRecoverable()
と isTransient()
の両方が同時に true を返すことはありません。