2017-09-25 54 views
1

私はAndroid搭載端末でカメラアプリケーションを実装しています。現在、Camera2 APIとImageReaderを使用して画像データをYUV_420_888形式で取得していますが、これらのデータをMediaCodecに正確に書き込む方法はわかりません。ここでYUV_420_888とMediaCodecでImageReaderを正しく使用して、ビデオをh264形式にエンコードする方法はありますか?

私の質問は以下のとおりです。

  1. YUV_420_888は何?それは右、そのようYUV420PYUV420PPYUV420SPYUV420PSPとして、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、...?

  1. ​​とは何ですか?

​​の形式もYUV420ファミリに属する​​任意の形式である可能性があるため、あいまいですか? MediaCodecオブジェクトのKEY_COLOR_FORMATオプションを​​に設定した場合、MediaCodecオブジェクトに入力するデータのフォーマット(YUV420P、YUV420SP ...?)は?

  1. 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に直接書き込む必要があります。しかし、私はまだそれを行う方法を知らない。

答えて

0

あなたが正しいです、YUV_420_888は、異なるYUV 420フォーマットをラップできるフォーマットです。この仕様では、UプレーンとVプレーンの配置は規定されていませんが、いくつかの制約があります。例えばUプレーンにピクセルストライド2がある場合は、Vにも同じことが適用されます(下にあるバイトバッファはNV21になります)。

COLOR_FormatYUV420Flexibleは、それぞれYUV_420_888の同義語ですが、MediaCodecとImageFormatという異なるクラスに属しています。

スペックは説明する:LOLLIPOP_MR1ので、0のバッファ:2:

をすべてのビデオコーデックは、柔軟なYUV 4をサポートしています。

COLOR_FormatSurfaceはMediaCodecのための最高のパフォーマンスを提供することができます不透明な形式ですが、これは価格で来る:あなたが直接読み取るか、その内容を操作することはできません。 MediaCodecに送られるデータを操作する必要がある場合、ImageReaderを使用することはオプションです。それがByteBufferよりも効率的かどうかは、あなたがやることややる方法によって異なります。 API 24+の場合、C++でcamera2とMediaCodecの両方で作業できます。

MediaCodecの詳細なリソースはhttp://www.bigflake.com/mediacodecです。これは、符号化された264のfull exampleを参照している。

+0

ありがとうございました。私はCOLOR_FormatSurfaceでビデオをエンコードしようとしましたが、パフォーマンスは良くありません(私のEDIT1セクションを参照)。 COLOR_FormatYUV420Flexible(または他のYUV形式)を使用してビデオをエンコードする例はありますか? – user3032481

+0

正しく理解すれば、** imageIUV **はカメラフレームの編集コピーになるように、** onImageAvailable()**にコードを追加する予定です。それ以外の場合は、プレーンを** image **から別のByteBufferに単純にコピーする理由はありません。 –

+0

いずれにせよ、レンダスクリプトであっても、表示手順は最適ではありません。 OpenGLを直接使用する方が、YUV420入力をオンザフライでRGBに変換するシェーダを使う方がはるかに高速かもしれません。 –

関連する問題