当前位置: 首页 > 知识库问答 >
问题:

如何正确使用带有YUV_420_888和MediaCodec的ImageReader将视频编码为h264格式?

解鸿运
2023-03-14

以下是我的问题:

  1. 什么是yuv_420_888

格式yuv_420_888是不明确的,因为它可以是属于yuv420家族的任何格式,例如yuv420pyuv420ppyuv420spyuv420psp,对吗?

如果我想把这些数据从Image的三个平面写到MediaCodec中,我需要转换成什么样的格式?YUV420、NV21、NV12、……?

格式color_formatyuv420flext也不明确,因为它可以是属于yuv420家族的任何格式,对吗?如果我将MediaCodec对象的key_color_format选项设置为color_formatyuv420flext,那么是什么格式(YUV420P,yuv420sp...)是否应该向MediaCodec对象输入数据?

我知道MediaCodec有自己的曲面,如果我将MediaCodec对象的key_color_format选项设置为color_formatsurface就可以使用该曲面。使用Camera2 API,我不需要自己向MediaCodec对象写入任何数据。我可以耗尽输出缓冲区。

我是否可以使用ImageReader从相机中读取图像,并在重新绘制之后,将新数据写入MediaCodec的表面,然后将其排出?怎么做?

编辑1

我使用color_formatsurface和renderscript实现了该函数。下面是我的代码:

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

    }
}
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和曲面的画布进行编码。计算出的YUV数据应该直接写入mediaCodec而不需要任何曲面进行任何转换。但我还是不知道怎么做。

共有1个答案

向修谨
2023-03-14

您是对的,YUV_420_888是一种可以包装不同YUV420格式的格式。规范仔细解释,U面和V面的布置没有规定,但有一定的限制;例如,如果U平面具有像素步幅2,则同样适用于V(然后底层字节缓冲器可以是NV21)。

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

规范解释说:

 类似资料:
  • 我正在开发一个通过RTP接收H264编码数据的应用程序,但我无法让Android的MediaCodec输出任何内容。我正在按照https://stackoverflow.com/a/7668578/10788248对RTP数据包进行解包 在编码帧被重新组装后,我将它们输入到出列的输入缓冲区中。 当我对输入缓冲区进行排队时,我不会得到任何错误,但是解码器的回调从来不会调用onOutputBuffer

  • 我之前也尝试使用此方法创建Mediacodec 但视频质量与YouTube上分享的视频一样。

  • 我在Android设备上的相机捕获过程中使用音频记录来记录音频流。由于我想处理帧数据并处理音频/视频样本,所以我不使用MediaRecorder。 我在另一个线程中运行AudioRecord并调用read()来收集原始音频数据。一旦我得到数据流,我就将它们馈送到配置为AAC音频编码器的MediaCodec中。 以下是我关于录音机/编码器的一些代码: 我发现这是第一次录音。read()的返回时间较长

  • 我正在尝试使用MediaCodec和MediaMuxer对来自相机的视频和来自麦克风的音频进行编码。我在录制时使用OpenGL在图像上覆盖文本。 我以这些课程为例: http://bigflake.com/mediacodec/CameraToMpegTest.java.txt https://github.com/OnlyInAmerica/HWEncoderExperiments/blob/m

  • 我正在使用MediaCodec将PCM数据转换为AAC,并使用MediaMuxer将此aac数据存储到m4a文件中。没有视频。 该文件会生成,甚至会播放。但是没有声音。如果我将aac文件导入Audacity,它不会显示任何数据。音频的长度甚至是预期的时间。我知道数据正在被编码,尽管我不确定这些数据是否被正确编码。 对pcm数据进行编码: 我已经浏览了大量示例,我所做的一切似乎都是正确的。如果我在o

  • 我可以看到视频播放在我的TextureView,但它是相当腐败。我已经验证了我正在以正确的顺序接收完整的数据包。我已经能够正确解析RTP头。我相信我的问题与SPS和PPS以及MediaCodec有关。 正如您所看到的,我没有从我的视频流中为MediaFormat提供SPS和PPS,而是使用了一个internet示例中的硬编码集。我试图找到解释如何从数据包中提取SPS和PPS的源,但没有找到任何东西