猿问

MediaCodec和Camera:色彩空间不匹配

我一直在尝试使用新的低级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);


最终目标是创建一个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>,我检索的MediaCodecInfo.CodecCapabilities -object类型视频/ AVC,作为整数值19(与对应MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar和2130708361(其不与任何值对应MediaCodecInfo.CodecCapabilities)。


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


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


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


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


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


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


可以看出,这些都不令人满意。YV2色彩空间看起来是最有前途的,但看起来像红色(Cr)和蓝色(Cb)是倒置的。我猜NV21看起来是隔行扫描的(但是,我不是该领域的专家)。


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


ITMISS
浏览 1073回答 3
3回答

繁花不似锦

我通过使用一个简单的函数在Android级别上交换字节平面解决了该问题:public byte[] swapYV12toI420(byte[] yv12bytes, int width, int height) {&nbsp; &nbsp; byte[] i420bytes = new byte[yv12bytes.length];&nbsp; &nbsp; for (int i = 0; i < width*height; i++)&nbsp; &nbsp; &nbsp; &nbsp; i420bytes[i] = yv12bytes[i];&nbsp; &nbsp; for (int i = width*height; i < width*height + (width/2*height/2); i++)&nbsp; &nbsp; &nbsp; &nbsp; i420bytes[i] = yv12bytes[i + (width/2*height/2)];&nbsp; &nbsp; for (int i = width*height + (width/2*height/2); i < width*height + 2*(width/2*height/2); i++)&nbsp; &nbsp; &nbsp; &nbsp; i420bytes[i] = yv12bytes[i - (width/2*height/2)];&nbsp; &nbsp; return i420bytes;}

桃花长相依

我认为将价值交换到位会更有效。&nbsp; &nbsp; &nbsp; &nbsp; int wh4 = input.length/6; //wh4 = width*height/4&nbsp; &nbsp; &nbsp; &nbsp; byte tmp;&nbsp; &nbsp; &nbsp; &nbsp; for (int i=wh4*4; i<wh4*5; i++)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tmp = input[i];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; input[i] = input[i+wh4];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; input[i+wh4] = tmp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }甚至更好,您可以代替&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inputBuffer.put(input);以正确的顺序排列3个平面切片&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inputBuffer.put(input, 0, wh4*4);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inputBuffer.put(input, wh4*5, wh4);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 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 ! ...
随时随地看视频慕课网APP

相关分类

Android
我要回答