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;
}