1.问题描述
在android camera2基础上取到img的Y,U,V三个通道数据后,生成uyvy格式并转为nv12后发生崩溃现象,打开logcat日志,发现有如下异常信息:
2022-12-15 17:14:54.881 9593-9628/com.arcsoft.se1000dmstestbed E/AndroidRuntime: FATAL EXCEPTION: BasicCamera2Thread
Process: com.arcsoft.se1000dmstestbed, PID: 9593
java.lang.IllegalStateException: maxImages (1) has already been acquired, call #close before acquiring more.
at android.media.ImageReader.acquireNextImage(ImageReader.java:527)
at com.arcsoft.se1000dmstestbed.BasicCamera2$4.onImageAvailable(BasicCamera2.java:343)
at android.media.ImageReader$ListenerHandler.handleMessage(ImageReader.java:812)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.os.HandlerThread.run(HandlerThread.java:67)
2.camera2业务流程
mBackgroundHandler = new Handler(mBackgroundThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
if (message.what != 1) {
return false;
}
if (null == mByteNv12) {
mByteNv12 = new byte[mPreviewReader.getWidth() * mPreviewReader.getHeight() * 3 / 2];
}
if ((true == mDebugUYVY) && (null == mByteUYVY)) {
mByteUYVY = new byte[mPreviewReader.getWidth() * mPreviewReader.getHeight() * 2];
}
long curTimeMillis = System.currentTimeMillis();
final Image img = (Image)message.obj;
Log.d(TAG, "Capture Y size " + img.getPlanes()[0].getBuffer().remaining());
Log.d(TAG, "Capture U size " + img.getPlanes()[1].getBuffer().remaining());
Log.d(TAG, "Capture V size " + img.getPlanes()[2].getBuffer().remaining());
Log.d(TAG, "Preview wdith:" + mPreviewReader.getWidth() + " height:" + mPreviewReader.getHeight());
boolean ret = getBytesForImage(img);
img.close();
...
}
如上述代码,在获取ImageReader中img数据后,执行了getBytesForImage业务,然后关闭img:
//private boolean getBytesForImage(final Image img) {
private boolean getBytesForImage(Image img) {
int imageType = YuvUtil.getImageType(img);
if (YuvUtil.NoneType == imageType) {
img.close();
Log.e(TAG, "can not get correct yuv format from image!");
return false;
}
byte byteY[] = new byte[img.getPlanes()[0].getBuffer().remaining()];
byte byteU[] = new byte[img.getPlanes()[1].getBuffer().remaining()];
byte byteV[] = new byte[img.getPlanes()[2].getBuffer().remaining()];
img.getPlanes()[0].getBuffer().get(byteY, 0, img.getPlanes()[0].getBuffer().remaining());
img.getPlanes()[1].getBuffer().get(byteU, 0, img.getPlanes()[1].getBuffer().remaining());
img.getPlanes()[2].getBuffer().get(byteV, 0, img.getPlanes()[2].getBuffer().remaining());
img.close(); // quickly close the image to prevent abnormality
switch (imageType) {
case YuvUtil.SE1000:
if (true == mDebugUYVY) {
YuvUtil.makeUYVY(byteY, byteU, byteV, mByteUYVY);
}
else {
YuvUtil.changeUYVYToNV12(byteY, byteU, byteV, mPreviewReader.getWidth(), mByteNv12);
}
break;
case YuvUtil.YUV420P:
YuvUtil.Yuv420ToNV12(byteY, byteU, byteV, mPreviewReader.getWidth(), mPreviewReader.getHeight(), mByteNv12);
break;
default:
break;
}
return true;
}
在imagerReader初始化时后只有imagebuffer容量是1,如下所示:
mPreviewReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), mPreviewFormat,1);
这样的话会导致1个问题,当imageReader回调函数将Image放入缓冲区,而后台线程并没有执行Image.close,这就会导致如上述错误,因此修改imageReader的缓冲区为3,如下所示:
mPreviewReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), mPreviewFormat,3); // stored 3 images
3.从img生成uyvy代码
由于摄像头是uyvy格式,因此camera2拿到的数据Y、U、V也满足YUV422格式,相应代码如下:
public static void makeUYVY(byte[] yuv420y, byte[] yuv420u, byte[] yuv420v, byte[] uyvy) {
int yLen = yuv420y.length;
int uLen = yuv420u.length;
int vLen = yuv420v.length;
int yPos = 0;
int uPos = 0;
int vPos = 0;
int offset = 0;
// copy uyvy
while ((yPos < yLen) && (yPos + 1 < yLen) && (uPos < uLen) && (vPos < vLen)) {
uyvy[offset++] = yuv420u[uPos++];
uyvy[offset++] = yuv420y[yPos++];
uyvy[offset++] = yuv420v[vPos++];
uyvy[offset++] = yuv420y[yPos++];
}
}
4.由uyvy转nv12代码
public static void changeUYVYToNV12(byte[] yuv420y, byte[] yuv420u, byte[] yuv420v, int width, byte[] nv12) {
// copy y channel
System.arraycopy(yuv420y,0, nv12, 0, yuv420y.length);
// copy uv channel
int offset = yuv420y.length;
int uv_size = yuv420u.length;
int uv_row_size = width / 2; // One row of u accounts for half of the width
int pos = 0;
while (pos < uv_size) {
nv12[offset++] = yuv420u[pos];
nv12[offset++] = yuv420v[pos];
if (0 == ((pos + 1) % uv_row_size)) {
pos += (uv_row_size + 1);
}
else {
pos++;
}
}
}
5.问题解决
修改业务代码,如下所示,尽快关闭img