私はAndroid搭載端末でカメラアプリケーションを実装しています。現在、Camera2 APIとImageReaderを使用して画像データをYUV_420_888
形式で取得していますが、これらのデータをMediaCodecに正確に書き込む方法はわかりません。ここでYUV_420_888とMediaCodecでImageReaderを正しく使用して、ビデオをh264形式にエンコードする方法はありますか?
私の質問は以下のとおりです。
YUV_420_888
は何?それは右、そのようYUV420P
、YUV420PP
、YUV420SP
とYUV420PSP
として、YUV420
ファミリーに属する任意のフォーマットである可能性があるため
形式YUV_420_888
はあいまいですか?
この画像のY(#0)、U(#1)、V(#2)の値を取得できます。しかし、これらの値の配置は、異なるデバイスでは同じではない可能性があります。たとえば、YUV_420_888
が真にYUV420P
を意味する場合、プレーン#1とプレーン#2のサイズは、プレーン#0のサイズの1/4です。 YUV_420_888
が本当にYUV420SP
を意味する場合、プレーン#1とプレーン#2のサイズは、プレーン#0のサイズの半分です(プレーン#1とプレーン#2のそれぞれはU、V値を含みます)。
イメージの3つのプレーンからこれらのデータをMediaCodecに書きたい場合は、どのようなフォーマットに変換する必要がありますか? YUV420、NV21、NV12、...?
- とは何ですか?
の形式もYUV420
ファミリに属する任意の形式である可能性があるため、あいまいですか? MediaCodecオブジェクトのKEY_COLOR_FORMAT
オプションをに設定した場合、MediaCodecオブジェクトに入力するデータのフォーマット(YUV420P、YUV420SP ...?)は?
COLOR_FormatSurface
はどうですか?
私はMediaCodecは私がCOLOR_FormatSurface
にMediaCodecオブジェクトのKEY_COLOR_FORMAT
オプションを設定する場合に使用することができ、独自の表面を、持って知っています。 Camera2 APIでは、自分でデータをMediaCodecオブジェクトに書き込む必要はありません。出力バッファを排水できます。
ただし、カメラから画像を変更する必要があります。たとえば、他の画像を描画したり、テキストを書き込んだり、別の画像をPOP(Picture of Picture)として挿入したりします。
ImageReaderを使用してカメラから画像を読み取って再描画した後、新しいデータをMediaCodecのサーフェスに書き込んだ後に排出することはできますか?どうやってするか?
EDIT1
私はCOLOR_FormatSurface
とのrenderScriptを使用して機能を実装しました。ここに私のコードは次のとおりです。
onImageAvailable
方法:RGBへ
public void onImageAvailable(ImageReader imageReader) {
try {
try (Image image = imageReader.acquireLatestImage()) {
if (image == null) {
return;
}
Image.Plane[] planes = image.getPlanes();
if (planes.length >= 3) {
ByteBuffer bufferY = planes[0].getBuffer();
ByteBuffer bufferU = planes[1].getBuffer();
ByteBuffer bufferV = planes[2].getBuffer();
int lengthY = bufferY.remaining();
int lengthU = bufferU.remaining();
int lengthV = bufferV.remaining();
byte[] dataYUV = new byte[lengthY + lengthU + lengthV];
bufferY.get(dataYUV, 0, lengthY);
bufferU.get(dataYUV, lengthY, lengthU);
bufferV.get(dataYUV, lengthY + lengthU, lengthV);
imageYUV = dataYUV;
}
}
} catch (final Exception ex) {
}
}
コンバートYUV_420_888:
public static Bitmap YUV_420_888_toRGBIntrinsics(Context context, int width, int height, byte[] yuv) {
RenderScript rs = RenderScript.create(context);
ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuv.length);
Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
Bitmap bmpOut = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
in.copyFromUnchecked(yuv);
yuvToRgbIntrinsic.setInput(in);
yuvToRgbIntrinsic.forEach(out);
out.copyTo(bmpOut);
return bmpOut;
}
MediaCodec:
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
...
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
...
surface = mediaCodec.createInputSurface(); // This surface is not used in Camera APIv2. Camera APIv2 uses ImageReader's surface.
そしてathotherスレッド:
while (!stop) {
final byte[] image = imageYUV;
// Do some yuv computation
Bitmap bitmap = YUV_420_888_toRGBIntrinsics(getApplicationContext(), width, height, image);
Canvas canvas = surface.lockHardwareCanvas();
canvas.drawBitmap(bitmap, matrix, paint);
surface.unlockCanvasAndPost(canvas);
}
この方法では機能しますが、パフォーマンスは良くありません。 30fpsのビデオファイルを出力することはできません(〜12fpsのみ)。おそらく、COLOR_FormatSurface
とサーフェスのキャンバスをエンコーディングに使用しないでください。計算されたYUVデータは、いかなる変換も行わずにmediaCodecに直接書き込む必要があります。しかし、私はまだそれを行う方法を知らない。
ありがとうございました。私はCOLOR_FormatSurfaceでビデオをエンコードしようとしましたが、パフォーマンスは良くありません(私のEDIT1セクションを参照)。 COLOR_FormatYUV420Flexible(または他のYUV形式)を使用してビデオをエンコードする例はありますか? – user3032481
正しく理解すれば、** imageIUV **はカメラフレームの編集コピーになるように、** onImageAvailable()**にコードを追加する予定です。それ以外の場合は、プレーンを** image **から別のByteBufferに単純にコピーする理由はありません。 –
いずれにせよ、レンダスクリプトであっても、表示手順は最適ではありません。 OpenGLを直接使用する方が、YUV420入力をオンザフライでRGBに変換するシェーダを使う方がはるかに高速かもしれません。 –