gpt4 book ai didi

camera - 如何正确使用ImageReader与YUV_420_888和MediaCodec将视频编码为h264格式?

转载 作者:行者123 更新时间:2023-12-01 22:19:18 30 4
gpt4 key购买 nike

我正在 Android 设备上实现相机应用程序。目前,我使用Camera2 API和ImageReader来获取YUV_420_888格式的图像数据,但我不知道如何准确地将这些数据写入MediaCodec。

这是我的问题:

  1. 什么是 YUV_420_888

格式 YUV_420_888 不明确,因为它可以是属于 YUV420 系列的任何格式,例如 YUV420PYUV420PP YUV420SPYUV420PSP,对吗?

通过访问图像的三个平面(#0、#1、#2),我可以获得该图像的 Y(#0)、U(#1)、V(#2) 值。但这些值的排列在不同的设备上可能不一样。例如,如果YUV_420_888真正表示YUV420P,则Plane#1和Plane#2的大小都是Plane#0大小的四分之一。如果 YUV_420_888 真正表示 YUV420SP,则 Plane#1 和 Plane#2 的大小都是 Plane#0 大小的一半(Plane#1 和 Plane#2 中的每个包含U、V值)。

如果我想将图像的三个平面的这些数据写入MediaCodec,我需要转换成什么样的格式? YUV420、NV21、NV12,...?

  • 什么是COLOR_FormatYUV420Flexible
  • 格式COLOR_FormatYUV420Flexible也是不明确的,因为它可以是属于YUV420系列的任何格式,对吧?如果我将 MediaCodec 对象的 KEY_COLOR_FORMAT 选项设置为 COLOR_FormatYUV420Flexible,我应该向 MediaCodec 对象输入什么格式的数据(YUV420P、YUV420SP...?)?

  • 使用COLOR_FormatSurface怎么样?
  • 我知道MediaCodec有自己的表面,如果我将MediaCodec对象的KEY_COLOR_FORMAT选项设置为COLOR_FormatSurface,就可以使用它。使用Camera2 API,我不需要自己向MediaCodec对象写入任何数据。我可以耗尽输出缓冲区。

    但是,我需要更改相机的图像。例如,绘制其他图片,在上面写一些文字,或者插入另一个视频作为 POP(图片的图片)。

    我可以使用ImageReader从Camera读取图像,然后重新绘制后,将新数据写入MediaCodec的表面,然后将其排出吗?如何做到这一点?

    编辑1

    我使用COLOR_FormatSurface和RenderScript实现了该功能。这是我的代码:

    onImageAvailable 方法:

    public void onImageAvailable(ImageReader imageReader) {
    try {
    try (Image image = imageReader.acquireLatestImage()) {
    if (image == null) {
    return;
    }
    Image.Plane[] planes = image.getPlanes();
    if (planes.length >= 3) {
    ByteBuffer bufferY = planes[0].getBuffer();
    ByteBuffer bufferU = planes[1].getBuffer();
    ByteBuffer bufferV = planes[2].getBuffer();
    int lengthY = bufferY.remaining();
    int lengthU = bufferU.remaining();
    int lengthV = bufferV.remaining();
    byte[] dataYUV = new byte[lengthY + lengthU + lengthV];
    bufferY.get(dataYUV, 0, lengthY);
    bufferU.get(dataYUV, lengthY, lengthU);
    bufferV.get(dataYUV, lengthY + lengthU, lengthV);
    imageYUV = dataYUV;
    }
    }
    } catch (final Exception ex) {

    }
    }

    将 YUV_420_888 转换为 RGB:

    public static Bitmap YUV_420_888_toRGBIntrinsics(Context context, int width, int height, byte[] yuv) {
    RenderScript rs = RenderScript.create(context);
    ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));

    Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuv.length);
    Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);

    Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
    Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);


    Bitmap bmpOut = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

    in.copyFromUnchecked(yuv);

    yuvToRgbIntrinsic.setInput(in);
    yuvToRgbIntrinsic.forEach(out);
    out.copyTo(bmpOut);
    return bmpOut;
    }

    媒体编解码器:

    mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
    ...
    mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    ...
    surface = mediaCodec.createInputSurface(); // This surface is not used in Camera APIv2. Camera APIv2 uses ImageReader's surface.

    在另一个线程中:

    while (!stop) {
    final byte[] image = imageYUV;

    // Do some yuv computation

    Bitmap bitmap = YUV_420_888_toRGBIntrinsics(getApplicationContext(), width, height, image);
    Canvas canvas = surface.lockHardwareCanvas();
    canvas.drawBitmap(bitmap, matrix, paint);
    surface.unlockCanvasAndPost(canvas);
    }

    这种方式可行,但是性能不好。它无法输出 30fps 视频文件(仅~12fps)。也许我不应该使用 COLOR_FormatSurface 和表面的 Canvas 进行编码。计算出的 YUV 数据应直接写入 mediaCodec,无需任何表面进行任何转换。但我还是不知道该怎么做。

    最佳答案

    你说得对,YUV_420_888是一种可以包装不同YUV 420格式的格式。规范仔细解释了,U、V平面的排列方式没有规定,但是有一定的限制;例如如果 U 平面的像素步长为 2,则同样适用于 V(然后底层字节缓冲区可以是 NV21)。

    COLOR_FormatYUV420FlexibleYUV_420_888 的同义词,但它们分别属于不同的类:MediaCodec 和 ImageFormat。

    规范解释:

    All video codecs support flexible YUV 4:2:0 buffers since LOLLIPOP_MR1.

    COLOR_FormatSurface 是一种不透明格式,可以为 MediaCodec 提供最佳性能,但这是有代价的:您无法直接读取或操作其内容。如果您需要操作发送至 MediaCodec 的数据,则可以选择使用 ImageReader;它是否会比 ByteBuffer 更高效,取决于你做什么以及如何做。请注意,对于 API 24+您可以在 C++ 中使用camera2 和MediaCodec。

    有关 MediaCodec 的宝贵详细信息资源是 http://www.bigflake.com/mediacodec 。它引用了 full example 264编码。

    关于camera - 如何正确使用ImageReader与YUV_420_888和MediaCodec将视频编码为h264格式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46403934/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com