[go: nahoru, domu]

blob: b3e1d441f47fb0d492159131aa3b0eb0b0f64d39 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.core;
import android.media.Image;
import android.media.ImageReader;
import android.view.Surface;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.core.impl.ImageReaderProxy;
import androidx.camera.core.impl.utils.MainThreadAsyncHandler;
import java.util.concurrent.Executor;
/**
* An {@link ImageReaderProxy} which wraps around an {@link ImageReader}.
*
* <p>All methods map one-to-one between this {@link ImageReaderProxy} and the wrapped {@link
* ImageReader}.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
class AndroidImageReaderProxy implements ImageReaderProxy {
@GuardedBy("mLock")
private final ImageReader mImageReader;
private final Object mLock = new Object();
@GuardedBy("mLock")
private boolean mIsImageAvailableListenerCleared = true;
/**
* Creates a new instance which wraps the given image reader.
*
* @param imageReader to wrap
* @return new {@link AndroidImageReaderProxy} instance
*/
AndroidImageReaderProxy(ImageReader imageReader) {
mImageReader = imageReader;
}
@Override
@Nullable
public ImageProxy acquireLatestImage() {
synchronized (mLock) {
Image image;
try {
image = mImageReader.acquireLatestImage();
} catch (RuntimeException e) {
/* In API level 23 or below, it will throw "java.lang.RuntimeException:
ImageReaderContext is not initialized" when ImageReader is closed. To make the
behavior consistent as newer API levels, we make it return null Image instead.*/
if (isImageReaderContextNotInitializedException(e)) {
image = null;
} else {
throw e; // only catch RuntimeException:ImageReaderContext is not initialized
}
}
if (image == null) {
return null;
}
return new AndroidImageProxy(image);
}
}
@Override
@Nullable
public ImageProxy acquireNextImage() {
synchronized (mLock) {
Image image;
try {
image = mImageReader.acquireNextImage();
} catch (RuntimeException e) {
/* In API level 23 or below, it will throw "java.lang.RuntimeException:
ImageReaderContext is not initialized" when ImageReader is closed. To make the
behavior consistent as newer API levels, we make it return null Image instead.*/
if (isImageReaderContextNotInitializedException(e)) {
image = null;
} else {
throw e; // only catch RuntimeException:ImageReaderContext is not initialized
}
}
if (image == null) {
return null;
}
return new AndroidImageProxy(image);
}
}
private boolean isImageReaderContextNotInitializedException(RuntimeException e) {
return "ImageReaderContext is not initialized".equals(e.getMessage());
}
@Override
public void close() {
synchronized (mLock) {
mImageReader.close();
}
}
@Override
public int getHeight() {
synchronized (mLock) {
return mImageReader.getHeight();
}
}
@Override
public int getWidth() {
synchronized (mLock) {
return mImageReader.getWidth();
}
}
@Override
public int getImageFormat() {
synchronized (mLock) {
return mImageReader.getImageFormat();
}
}
@Override
public int getMaxImages() {
synchronized (mLock) {
return mImageReader.getMaxImages();
}
}
@Nullable
@Override
public Surface getSurface() {
synchronized (mLock) {
return mImageReader.getSurface();
}
}
@Override
public void setOnImageAvailableListener(
@NonNull OnImageAvailableListener listener,
@NonNull Executor executor) {
synchronized (mLock) {
mIsImageAvailableListenerCleared = false;
// ImageReader does not accept an executor. As a workaround, the callback is run on main
// handler then immediately posted to the executor.
ImageReader.OnImageAvailableListener transformedListener = (imageReader) -> {
synchronized (mLock) {
// There might be a timing issue that the listener is executed after
// clearOnImageAvailableListener() has been called. Uses a flag to skip the
// execution if the listener has actually been cleared.
if (!mIsImageAvailableListenerCleared) {
executor.execute(
() -> listener.onImageAvailable(AndroidImageReaderProxy.this));
}
}
};
mImageReader.setOnImageAvailableListener(transformedListener,
MainThreadAsyncHandler.getInstance());
}
}
@Override
public void clearOnImageAvailableListener() {
synchronized (mLock) {
mIsImageAvailableListenerCleared = true;
mImageReader.setOnImageAvailableListener(null, null);
}
}
}