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

问题描述 投票:1回答:1

我正在使用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++;

}

我曾考虑过可能从相机获得一帧,检测其行距和像素步幅,然后使用它来确定编码器可能期望什么。但是即使那样,我仍然仍然不知道如何汇编字节,而且,我不确定编码器无论如何都希望使用相同的格式。

任何帮助或指出正确方向的人都将受到赞赏。

android-camera2 mediacodec yuv android-mediacodec
1个回答
0
投票

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

最好的学习方法是运行Grafika应用程序,尝试其不同的活动,结合一些概念,等等。>>

© www.soinside.com 2019 - 2024. All rights reserved.