MediaCodec和Camera:色彩空间不匹配

我一直在尝试使用H264编码来处理Android平板电脑上摄像头捕获的输入,并使用新的低级别MediaCodec。 我经历了一些困难,因为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> ),另一种用于MediaCodec对象(标有<2>

要确定行<1>的可接受值,我使用了parameters.getSupportedPreviewFormats() 。 由此我知道,相机上唯一支持的格式是ImageFormat.NV21和ImageFormat.YV2。

对于<2> ,我检索了类型为video / avc的MediaCodecInfo.CodecCapabilities对象,该值为整数值19(与MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar和2130708361(它不与MediaCodecInfo.CodecCapabilities的任何值对应)对应)。

除上述以外的任何其他值都会导致崩溃。

结合这些设置给出了不同的结果,我将在下面展示。 以下是Android上的截图(即“真实”颜色): 在Android平板电脑上输入 以下是Gstreamer显示的结果:

<1> = NV21, <2> = COLOR_FormatYUV420Planar GSTreamer-NV21-COLOR_FormatYUV420Planar输出

<1> = NV21, <2> = 2130708361 Gstreamer-NV21-2130708361的输出

<1> = YV2, <2> = COLOR_FormatYUV420Planar 用于YV2-COLOR_FormatYUV420Planar的Gstreamer输出

<1> = YV2, <2> = 2130708361 Gstreamer-YV2-2130708361的输出

可以看出,这些都不令人满意。 YV2色彩空间看起来最有前途,但它看起来像红色(Cr)和蓝色(Cb)被倒置。 NV21看起来交错我猜(但是,我不是这方面的专家)。

既然目的是与Skype通信,我想我不应该改变解码器(即Gstreamer-command),对吧? 这是在Android中解决的,如果是这样的话:怎么样? 或者可以通过添加某些RTP有效负载信息来解决这个问题? 任何其他建议?


我通过使用一个简单的函数自己在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;
}

我认为交换价值更有效率。

        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);

我认为这应该只有很小的开销


看起来Android是在YV12上传输,但在H264头文件中设置的格式是YUV420。 这些格式是相同的,除了U和V通道的顺序不同,这就解释了红色和蓝色的交换。

当然,最好的解决方案是在Android端进行修复。 但是如果没有办法设置相机和编码器的兼容设置,则必须强制GStreamer侧的格式。

这可以通过添加完成capssetter后元素ffdec_h264

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

链接地址: http://www.djcxy.com/p/92529.html

上一篇: MediaCodec and Camera: colorspaces don't match

下一篇: XCode 5 Storyboard Internal Inconsistencies