YUV_420_888是YUV_420的一個大類,android camera2的ImageReader中設置了YUV_420_888後具體返回數據U和V是單獨處於不同平面還是相同平面交叉排列要看具體的設備了,一般來說pixelStride=1表示獨佔一個平面(一般Y平面就只有Y數據),pixelStride=2則表示U和V是交叉排列。
最近工作中發現ImageReader設置了特定尺寸後,實際返回的數據並不是之前設定的尺寸,所得非所設,玩我把。
比如我設置的分辨率是640*480
mImageReader2 = ImageReader.newInstance(640, 480,
ImageFormat.YUV_420_888, /*maxImages*/2);
mImageReader2.setOnImageAvailableListener(
mOnImageAvailableListener2, mBackgroundHandler);
但是返回的數據尺寸可能是1024*480,此時rowStride=1024,需要對數據進行裁剪纔可以顯示真實數據
public static byte[] getBytesFromImageAsType(Image image, int type) {
try {
//獲取源數據,如果是YUV格式的數據planes.length = 3
//plane[i]裏面的實際數據可能存在byte[].length <= capacity (緩衝區總大小)
final Image.Plane[] planes = image.getPlanes();
LogUtil.d("getBytesFromImageAsType,type="+type+",,top="+image.getCropRect().top+",left="+image.getCropRect().left+",bottom="+image.getCropRect().bottom+",right="+image.getCropRect().right);
//數據有效寬度,一般的,圖片width <= rowStride,這也是導致byte[].length <= capacity的原因
// 所以我們只取width部分
int width = image.getWidth();
int height = image.getHeight();
//此處用來裝填最終的YUV數據,需要1.5倍的圖片大小,因爲Y U V 比例爲 4:1:1
byte[] yuvBytes = new byte[width * height * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8];
//目標數組的裝填到的位置
int dstIndex = 0;
//臨時存儲uv數據的
byte uBytes[] = new byte[width * height / 4];
byte vBytes[] = new byte[width * height / 4];
int uIndex = 0;
int vIndex = 0;
int pixelsStride, rowStride;
for (int i = 0; i < planes.length; i++) {
pixelsStride = planes[i].getPixelStride();
rowStride = planes[i].getRowStride();
ByteBuffer buffer = planes[i].getBuffer();
//如果pixelsStride==2,一般的Y的buffer長度=640*480,UV的長度=640*480/2-1
//源數據的索引,y的數據是byte中連續的,u的數據是v向左移以爲生成的,兩者都是偶數位爲有效數據
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
LogUtil.e("i="+i+",bytes.length="+bytes.length+",rowStride="+rowStride);
int srcIndex = 0;
if (i == 0) {
//直接取出來所有Y的有效區域,也可以存儲成一個臨時的bytes,到下一步再copy
for (int j = 0; j < height; j++) {
System.arraycopy(bytes, srcIndex, yuvBytes, dstIndex, width);
srcIndex += rowStride;
dstIndex += width;
}
LogUtil.e("i == 0,srcIndex="+srcIndex+",dstIndex="+dstIndex+",row="+rowStride);
//ScreenCaptureUtil.dumpFile(new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"y.yuv").getAbsolutePath(),bytes);
} else if (i == 1) {
//根據pixelsStride取相應的數據
for (int j = 0; j < height / 2; j++) {
for (int k = 0; k < width / 2; k++) {
uBytes[uIndex++] = bytes[srcIndex];
srcIndex += pixelsStride;
}
if (pixelsStride == 2) {
srcIndex += rowStride - width;
} else if (pixelsStride == 1) {
srcIndex += rowStride - width / 2;
}
}
LogUtil.d("srcIndex="+srcIndex+",uIndex="+uIndex);
} else if (i == 2) {
//根據pixelsStride取相應的數據
for (int j = 0; j < height / 2; j++) {
for (int k = 0; k < width / 2; k++) {
vBytes[vIndex++] = bytes[srcIndex];
srcIndex += pixelsStride;
}
if (pixelsStride == 2) {
srcIndex += rowStride - width;
} else if (pixelsStride == 1) {
srcIndex += rowStride - width / 2;
}
}
LogUtil.d("srcIndex="+srcIndex+",bytes.length="+bytes.length+",vIndex="+vIndex);
}
}
image.close();
//根據要求的結果類型進行填充
switch (type) {
case YUV420P:
// System.arraycopy(uBytes, 0, yuvBytes, dstIndex, uBytes.length);
System.arraycopy(vBytes, 0, yuvBytes, dstIndex , vBytes.length);
break;
case YUV420SP:
for (int i = 0; i < vBytes.length; i++) {
yuvBytes[dstIndex++] = uBytes[i];
yuvBytes[dstIndex++] = vBytes[i];
}
break;
case NV21:
for (int i = 0; i < vBytes.length; i++) {
yuvBytes[dstIndex++] = vBytes[i];
yuvBytes[dstIndex++] = uBytes[i];
}
break;
}
return yuvBytes;
} catch (final Exception e) {
if (image != null) {
image.close();
}
LogUtil.d( e.toString());
}
return null;
}