Android将ImageProxy中的YUV420数据转为NV21数据

630 阅读1分钟

目录

前言

由于有些三方的SDK接收的是NV21的数据因此需要对Camerax回调的YUV420数据转为NV21数据才能满足需要

实现代码

之前在网上找了一个实现此需求的逻辑(如下),不过后来发现某些手机会出错

```
fun yuv420ToNv21(image: ImageProxy): ByteArray{
    val planes = image.planes
    val yBuffer: ByteBuffer = planes[0].buffer
    val uBuffer: ByteBuffer = planes[1].buffer
    val vBuffer: ByteBuffer = planes[2].buffer
    val ySize: Int = yBuffer.remaining()
    val uSize: Int = uBuffer.remaining()
    val vSize: Int = vBuffer.remaining()
    val size = image.width * image.height
    val nv21 = ByteArray(size * 3 / 2)
    yBuffer.get(nv21, 0, ySize)
    vBuffer.get(nv21, ySize, vSize)
    val u = ByteArray(uSize)
    uBuffer.get(u)
    //每隔开一位替换V,达到VU交替
    var pos = ySize + 1
    for (i in 0 until uSize) {
        if (i % 2 == 0) {
            nv21[pos] = u[i]
            pos += 2
        }
    }
    return nv21
}
```

后来通过检验使用如下逻辑可以完美解决

```
fun YUV420toNV21(image: ImageProxy): ByteArray{
    val crop: Rect = image.getCropRect()
    val format: Int = image.getFormat()
    val width = crop.width()
    val height = crop.height()
    val planes: Array<ImageProxy.PlaneProxy> = image.getPlanes()
    val data = ByteArray(width * height * ImageFormat.getBitsPerPixel(format) / 8)
    val rowData = ByteArray(planes[0].getRowStride())
    var channelOffset = 0
    var outputStride = 1
    for (i in planes.indices) {
        when (i) {
            0 -> {
                channelOffset = 0
                outputStride = 1
            }
            1 -> {
                channelOffset = width * height + 1
                outputStride = 2
            }
            2 -> {
                channelOffset = width * height
                outputStride = 2
            }
        }
        val buffer: ByteBuffer = planes[i].getBuffer()
        val rowStride: Int = planes[i].getRowStride()
        val pixelStride: Int = planes[i].getPixelStride()
        val shift = if (i == 0) 0 else 1
        val w = width shr shift
        val h = height shr shift
        buffer.position(rowStride * (crop.top shr shift) + pixelStride * (crop.left shr shift))
        for (row in 0 until h) {
            var length: Int
            if (pixelStride == 1 && outputStride == 1) {
                length = w
                buffer[data, channelOffset, length]
                channelOffset += length
            } else {
                length = (w - 1) * pixelStride + 1
                buffer[rowData, 0, length]
                for (col in 0 until w) {
                    data[channelOffset] = rowData[col * pixelStride]
                    channelOffset += outputStride
                }
            }
            if (row < h - 1) {
                buffer.position(buffer.position() + rowStride - length)
            }
        }
    }
    return data
}
```