【问题标题】:MediaCodec and Camera: colorspaces don't matchMediaCodec 和相机:色彩空间不匹配
【发布时间】:2012-12-04 13:02:22
【问题描述】:

我一直在尝试使用新的低级MediaCodec 让 H264 编码与 Android 平板电脑上的相机捕获的输入一起工作。我在这方面遇到了一些困难,因为 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 种颜色格式:一种用于相机预览(标记为 &lt;1&gt; 的行),另一种用于 MediaCodec 对象(标记为 &lt;2&gt;

为了确定行&lt;1&gt; 的可接受值,我使用了parameters.getSupportedPreviewFormats()。由此,我知道相机上唯一支持的格式是ImageFormat.NV21ImageFormat.YV2

对于&lt;2&gt;,我检索了类型为video/avcMediaCodecInfo.CodecCapabilities-object,它是整数值19(对应于MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar 和2130708361(不对应于任何MediaCodecInfo.CodecCapabilities) 的值。

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

结合这些设置会产生不同的结果,我将在下面展示。这是 Android 上的屏幕截图(即“真实”颜色): 以下是 Gstreamer 显示的结果:

&lt;1&gt; = NV21, &lt;2&gt; = COLOR_FormatYUV420Planar

&lt;1&gt; = NV21, &lt;2&gt; = 2130708361

&lt;1&gt; = YV2, &lt;2&gt; = COLOR_FormatYUV420Planar

&lt;1&gt; = YV2, &lt;2&gt; = 2130708361

可以看出,这些都不令人满意。 YV2 色彩空间看起来最有希望,但看起来红色 (Cr) 和蓝色 (Cb) 是颠倒的。我猜 NV21 看起来是交错的(不过,我不是这个领域的专家)。

由于目的是与 Skype 通信,我认为我不应该更改解码器(即 Gstreamer 命令),对吧?这是否要在 Android 中解决?如果是这样:如何解决?或者这可以通过添加某些 RTP 有效负载信息来解决吗?还有什么建议吗?

【问题讨论】:

标签: android colors h.264 gstreamer rtp


【解决方案1】:

我通过自己在 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;
}

【讨论】:

  • FWIW,有些设备接受 COLOR_FormatYUV420Planar,有些接受 COLOR_FormatYUV420SemiPlanar。
  • 在 4.3 (API 18) 中,您可以使用 Surface 预览而不是 ByteBuffer。这避免了校正颜色平面的需要,并且是便携的。请参阅 bigflake.com/mediacodec 上的 CameraToMpegTest 示例
  • 是的,500、1000 毫秒,请删除这个答案
  • @user25,这个问题和答案 > 5 岁。我们都知道图像格式是错误的,但没有记录更改它的功能 - 甚至可能不存在。如果您仍然遇到类似的问题,您应该写一个新的答案,因为其他人肯定会遇到同样的问题。
【解决方案2】:

我认为交换值更有效。

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

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

【讨论】:

  • cameraParamters.setPreviewFormat(ImageFormat.YV12);(ImageFormat.YV12) 您的解决方案什么都不做(3 个平面切片按正确顺序
【解决方案3】:

Android 好像是在 YV12 中传输,但是 H264 标头中设置的格式是 YUV420。这些格式是相同的,只是 U 和 V 通道的顺序不同,这解释了红色和蓝色的交换。

最好的当然是修复 Android 端的设置。但是如果没有办法为相机和编码器设置兼容的设置,你将不得不在 GStreamer 端强制格式。

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

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

【讨论】:

  • 我也可以通过在 android 端添加 GStreamer 管道来解决这个问题吗?鉴于以上使用了高达 50% 的 CPU,您认为是否会有足够的资源来做到这一点,并最终并行进行一些 H264 解码?
  • android 默认是 NV21,不是 YV12
【解决方案4】:

在相机上设置 ImageFormat.NV21 并在编码器上设置 COLOR_FormatYUV420Planar 时,在我的情况下可以看到类似的蓝色阴影重叠。据我了解,上述交换功能不能在我的情况下使用,对可用于此的算法有什么建议吗? ps:当相机预览格式设置为YV12时,解码器完全黑屏

【讨论】:

  • 上述交换只有在相机端设置 ImageFormat.YV12 时才有效。 NV21 的转换会复杂得多,因为它包含一个交错平面(请参阅fourcc.org/yuv.php)。如果我理解正确的话,这个交错平面也交换了 U 和 V 值,所以无论如何交换功能可能会派上用场(稍作修改,因为宽度和高度只有一半)。
【解决方案5】:

我使用来自here 的代码通过媒体编码器将相机图像转换为视频,这导致了同样的问题。

所以参考这个wikipedia article 我修改了代码like this 并且输出似乎工作正常。代替uvuv字节顺序,改字节顺序为uuuuuvvvv ..

【讨论】:

    猜你喜欢
    • 2013-03-22
    • 2015-02-08
    • 1970-01-01
    • 2014-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-21
    • 2013-04-23
    相关资源
    最近更新 更多