Ваш код вернет правильный NV21 только в том случае, если заполнение вообще отсутствует, а равнины U и V перекрываются и фактически представляют чересстрочные значения VU.Это происходит довольно часто для предварительного просмотра, но в этом случае вы выделяете дополнительные w * h / 4 байта для вашего массива (что, вероятно, не является проблемой).Возможно, для захваченного изображения вам нужна более надежная реализация, например,
private static byte[] YUV_420_888toNV21(Image image) {
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);
vBuffer.put(1, (byte)0);
if (uBuffer.get(0) == 0) {
vBuffer.put(1, (byte)255);
if (uBuffer.get(0) == 255) {
vBuffer.put(1, savePixel);
vBuffer.get(nv21, ySize, uvSize);
return nv21; // shortcut
}
}
// 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;
}
Если вы все равно намереваетесь передать полученный массив в C ++, вы можете воспользоваться фактом , что
возвращенный буфер всегда будет иметь возвращаемое значение isDirect true, поэтому базовые данные могут быть отображены как указатель в JNI без каких-либо копий с GetDirectBufferAddress.
Это означает, что одно и то же преобразование можетбыть сделано в C ++ с минимальными издержками.В C ++ вы можете даже обнаружить, что фактическое расположение пикселей уже составляет NV21!
PS На самом деле, это может быть сделано в Java с незначительными накладными расходами, см. Строку if (pixelStride == 2 && …
выше.