MediaCodec将视频帧RGB编码为YUV_420_888 | Android Camera2 API

时间:2019-09-06 12:04:54

标签: android-camera2 mediacodec yuv android-mediacodec

我正在使用MediaCodec将图像编码为视频。我可以在网上找到大量文档,以将YUV_420_888格式的图像转换为RGB,以便使用ImageReader从Camera2 API接收和处理帧。但是,我发现很难找到有关如何将RGB图像转换为YUV_420_888以进行编码的任何文档-问题是这种格式具有灵活性,可以表示编码器未专门提供的多种格式。

我已经能够通过将位图转换为YUV420SP图像并将字节发送到编码器来对帧进行编码。但是,在某些设备上,由于编码器期望使用不同的格式,因此输出视频会变色或失真。如果没有办法为特定设备或编码器编写骇人的解决方法,那我将感到震惊。

这是我的视频编解码器的设置方式:

Bitmap frameBitmap; //this is initiated later in my app
final int VIDEO_BIT_RATE = 4000000; //min supported by Android for 1280x720
final int VIDEO_FRAME_INTERVAL = 1;
final int VIDEO_WIDTH = 1280;
final int VIDEO_HEIGHT = 720;
final int VIDEO_FRAME_RATE = 30;
//...
videoCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_MPEG4);
MediaFormat videoFormat = MediaFormat.createVideoFormat(videoMimeType, mVideoSize.getWidth(), mVideoSize.getHeight());
videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, selectColorFormat(videoCodecInfo, videoMimeType));
videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormatSelected);
videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, VIDEO_BIT_RATE);
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, VIDEO_FRAME_RATE);
videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, VIDEO_FRAME_INTERVAL);
videoFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);
//...
private static int selectColorFormat(MediaCodecInfo codecInfo,
                                     String mimeType)
{
    MediaCodecInfo.CodecCapabilities capabilities = codecInfo
            .getCapabilitiesForType(mimeType);
    int selectedColorFormat = 0;
    for (int i = 0; i < capabilities.colorFormats.length; i++)
    {
        int colorFormat = capabilities.colorFormats[i];

        if (isRecognizedFormat(colorFormat))
        {
            selectedColorFormat = colorFormat;
        }
    }
    return selectedColorFormat;
}
private static boolean isRecognizedFormat(int colorFormat)
{
    switch (colorFormat)
    {
        //I use YUV420Flexible - other values are deprecated.
        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible:
            return true;
        default:
            return false;
    }
}

这是我目前编写编码器的两种方法-我知道它们都不完整,仅用于上下文:

int inputBufIndex = videoCodec.dequeueInputBuffer(timeout);
if (inputBufIndex >= 0) {

    //APPROACH A - using getInputImage
    currentVideoInputImage = videoCodec.getInputImage(inputBufIndex);
    assert currentVideoInputImage != null;
    ByteBuffer yBuffer = currentVideoInputImage.getPlanes()[0].getBuffer();
    ByteBuffer uBuffer = currentVideoInputImage.getPlanes()[1].getBuffer();
    ByteBuffer vBuffer = currentVideoInputImage.getPlanes()[2].getBuffer();
    yBuffer.put(yBytes);
    uBuffer.put(uBytes);
    vBuffer.put(vBytes);

    //or APPROACH B - writing the bytes directly
    currentVideoInputBuffer = videoCodec.getInputBuffer(inputBufIndex);
    assert currentVideoInputBuffer != null;
    int remaining = currentVideoInputBuffer.remaining(); 
    currentVideoInputBuffer.put(data); //data is in YUV420SP format currently
    videoCodec.queueInputBuffer(inputBufIndex, 0, remaining, presentationTimestamp, 0);
    totalFramesQueued++;

}

我考虑过可能从相机获得一帧,检测其行距和像素跨度,然后使用它来确定编码器可能期望的内容。但是即使那样,我仍然不知道如何组装字节,而且我不确定编码器是否仍希望使用相同的格式。

任何帮助或指出正确方向的事情都会受到赞赏。

1 个答案:

答案 0 :(得分:0)

最后,我完全放弃了这种方法,并使用OpenGL实现了一个解决方案。很有魅力,但是很难学习并且花了很长时间才能实现。我对任何希望学习OpenGL的人的建议是从开源Grafika项目开始。链接: https://github.com/google/grafika

最好的学习方法是运行Grafika应用程序,进行各种活动,结合一些概念等。