web-dev-qa-db-ja.com

MediaCodecとカメラ:色空間が一致しません

Android新しい低レベルを使用するタブレット MediaCodec のカメラでキャプチャされた入力でH264エンコーディングが機能するようにしています。いくつかの問題を経験しましたこれで、MediaCodecAPIは十分に文書化されていないので、ついに動作するようになりました。

次のようにカメラを設定しています。

_        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPreviewFormat(ImageFormat.YV12); // <1>
        parameters.setPreviewFpsRange(4000,60000);
        parameters.setPreviewSize(640, 480);            
        mCamera.setParameters(parameters);
_

エンコード部分では、MediaCodecオブジェクトを次のようにインスタンス化しています。

_    mediaCodec = MediaCodec.createEncoderByType("video/avc");
    MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 640, 480);
    mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 500000);
    mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
    mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
            MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar); // <2>
    mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
    mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    mediaCodec.start();
_

最終的な目標はRTPストリームを作成すること(そしてSkypeに対応すること)ですが、これまでのところ、生のH264を直接デスクトップにストリーミングしているだけです。そこで、次のGStreamerパイプラインを使用して結果を表示します。

_gst-launch udpsrc port=5555 ! video/x-h264,width=640,height=480,framerate=15/1 ! ffdec_h264 ! autovideosink
_

色を除いて、すべてうまくいきます。コンピューターで2つのカラーフォーマットを設定する必要があります。1つはカメラプレビュー(_<1>_でタグ付けされた行)ともう1つはMediaCodecオブジェクト(_<2>_でタグ付けされた行)です。

行の許容値を決定するために_<1>_ parameters.getSupportedPreviewFormats()を使用しました。このことから、カメラでサポートされている形式は ImageFormat.NV21ImageFormat.YV2 のみであることがわかります。

_<2>_の場合、タイプvideo/avcMediaCodecInfo.CodecCapabilities -objectを取得し、整数値19(-に対応) MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar および2130708361(これは MediaCodecInfo.CodecCapabilities の値に対応していません)。

上記以外の値を指定すると、クラッシュが発生します。

これらの設定を組み合わせると、さまざまな結果が得られます。これを以下に示します。 Android(つまり、「実際の」色)のスクリーンショットは次のとおりです。 Input on Android-tablet Gstreamerが示す結果は次のとおりです。

_<1>_ = NV21、_<2>_ = COLOR_FormatYUV420Planar Gstreamer-output for NV21-COLOR_FormatYUV420Planar

_<1>_ = NV21、_<2>_ = 2130708361 Gstreamer-output for NV21-2130708361

_<1>_ = YV2、_<2>_ = COLOR_FormatYUV420Planar Gstreamer-output for YV2-COLOR_FormatYUV420Planar

_<1>_ = YV2、_<2>_ = 2130708361 Gstreamer-output for YV2-2130708361

見て分かるように、これらはどれも満足のいくものではありません。 YV2色空間は最も有望に見えますが、赤(Cr)と青(Cb)が反転しているように見えます。 NV21はインターレースのように見えます(ただし、私はこの分野の専門家ではありません)。

目的はSkypeと通信することなので、デコーダー(つまり、Gstreamerコマンド)を変更しないでください。これはAndroidで解決されますか?そうであれば:どのようにですか?または、これは特定のRTPペイロード情報を追加することで解決できますか?他の提案?

32
gleerman

私は、Androidレベルで単純な関数を使用して、自分でバイトプレーンをスワップすることによってそれを解決しました:

public byte[] swapYV12toI420(byte[] yv12bytes, int width, int height) {
    byte[] i420bytes = new byte[yv12bytes.length];
    for (int i = 0; i < width*height; i++)
        i420bytes[i] = yv12bytes[i];
    for (int i = width*height; i < width*height + (width/2*height/2); i++)
        i420bytes[i] = yv12bytes[i + (width/2*height/2)];
    for (int i = width*height + (width/2*height/2); i < width*height + 2*(width/2*height/2); i++)
        i420bytes[i] = yv12bytes[i - (width/2*height/2)];
    return i420bytes;
}
7
gleerman

値を入れ替えた方が効率的だと思います。

        int wh4 = input.length/6; //wh4 = width*height/4
        byte tmp;
        for (int i=wh4*4; i<wh4*5; i++)
            {
            tmp = input[i];
            input[i] = input[i+wh4];
            input[i+wh4] = tmp;
            }

多分さらに良い、代わりに置き換えることができます

            inputBuffer.put(input);

3つの平面スライスが正しい順序で

            inputBuffer.put(input, 0, wh4*4);
            inputBuffer.put(input, wh4*5, wh4);
            inputBuffer.put(input, wh4*4, wh4);

ほんの少しのオーバーヘッドしかないと思います

6
John La Rooy

AndroidはYV12で送信していますが、H264ヘッダーで設定されたフォーマットはYUV420です。これらのフォーマットは、UチャネルとVチャネルが異なる順序であることを除いて同じです。青い。

もちろん、Android=側の設定を修正することをお勧めします。ただし、カメラとエンコーダーに互換性のある設定を設定する方法がない場合は、GStreamer側でフォーマットを強制する必要があります。

これは、_ffdec_h264_の後にcapssetter要素を追加することで実行できます。

... ! ffdec_h264 ! capssetter caps="video/x-raw-yuv, format=(fourcc)YV12" ! colorspace ! ...

3
jpa

カメラにImageFormat.NV21を設定し、エンコーダーにCOLOR_FormatYUV420Planarを使用すると、同様の青い影が重なって見える場合があります。上記のスワップ関数は私の場合には使用できないことを理解しているので、これに使用できるアルゴリズムについて何か提案はありますか? ps:カメラのプレビュー形式がYV12に設定されている場合、デコーダーで完全に黒い画面

0
Lakshmi