Created
February 4, 2020 08:33
-
-
Save alexcohn/f452420fe23d333be99498f503e849e7 to your computer and use it in GitHub Desktop.
optimized conversion from YUV_420_888 to NV21, see https://stackoverflow.com/a/52740776/192373
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private static byte[] YUV_420_888toNV21(Image image) { | |
// optimized conversion from YUV_420_888 to NV21 | |
// see https://stackoverflow.com/a/52740776/192373 | |
int width = image.getWidth(); | |
int height = image.getHeight(); | |
int ySize = width*height; | |
int uvSize = width*height/4; | |
byte[] nv21 = new byte[ySize + uvSize*2]; | |
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); // Y | |
ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); // U | |
ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); // V | |
int rowStride = image.getPlanes()[0].getRowStride(); | |
assert(image.getPlanes()[0].getPixelStride() == 1); | |
int pos = 0; | |
if (rowStride == width) { // likely | |
yBuffer.get(nv21, 0, ySize); | |
pos += ySize; | |
} | |
else { | |
long yBufferPos = width - rowStride; // not an actual position | |
for (; pos<ySize; pos+=width) { | |
yBufferPos += rowStride - width; | |
yBuffer.position(yBufferPos); | |
yBuffer.get(nv21, pos, width); | |
} | |
} | |
rowStride = image.getPlanes()[2].getRowStride(); | |
int pixelStride = image.getPlanes()[2].getPixelStride(); | |
assert(rowStride == image.getPlanes()[1].getRowStride()); | |
assert(pixelStride == image.getPlanes()[1].getPixelStride()); | |
if (pixelStride == 2 && rowStride == width && uBuffer.get(0) == vBuffer.get(1)) { | |
// maybe V an U planes overlap as per NV21, which means vBuffer[1] is alias of uBuffer[0] | |
byte savePixel = vBuffer.get(1); | |
try { | |
vBuffer.put(1, (byte)~savePixel); | |
if (uBuffer.get(0) == (byte)~savePixel) { | |
vBuffer.put(1, savePixel); | |
vBuffer.get(nv21, ySize, uvSize); | |
return nv21; // shortcut | |
} | |
catch (ReadOnlyBufferException ex) { | |
// unfortunately, we cannot check if vBuffer and uBuffer overlap | |
} | |
// unfortunately, the check failed. We must save U and V pixel by pixel | |
vBuffer.put(1, savePixel); | |
} | |
// other optimizations could check if (pixelStride == 1) or (pixelStride == 2), | |
// but performance gain would be less significant | |
for (int row=0; row<height/2; row++) { | |
for (int col=0; col<width/2; col++) { | |
int vuPos = col*pixelStride + row*rowStride; | |
nv21[pos++] = vBuffer.get(vuPos); | |
nv21[pos++] = uBuffer.get(vuPos); | |
} | |
} | |
return nv21; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment