[go: nahoru, domu]

blob: cce5604ea626d8c72c35c0d5c0ce95682f2798fb [file] [log] [blame]
/*
* Copyright 2020 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.view.Surface;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.ForwardingImageProxy.OnImageCloseListener;
import androidx.camera.core.impl.ImageReaderProxy;
import java.util.concurrent.Executor;
/**
* An {@link ImageReaderProxy} that wraps another ImageReaderProxy to safely wait until all
* produced {@link ImageProxy} are closed before closing the ImageReaderProxy.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class SafeCloseImageReaderProxy implements ImageReaderProxy {
// Lock to synchronize acquired ImageProxys and close.
private final Object mLock = new Object();
@GuardedBy("mLock")
private int mOutstandingImages = 0;
@GuardedBy("mLock")
private boolean mIsClosed = false;
// The wrapped instance of ImageReaderProxy
@GuardedBy("mLock")
private final ImageReaderProxy mImageReaderProxy;
@Nullable
private final Surface mSurface;
private OnImageCloseListener mForwardingImageCloseListener;
// Called after images are closed to check if the ImageReaderProxy should be closed
private final OnImageCloseListener mImageCloseListener = (image) -> {
OnImageCloseListener forwardingListener;
synchronized (mLock) {
mOutstandingImages--;
if (mIsClosed && mOutstandingImages == 0) {
close();
}
forwardingListener = mForwardingImageCloseListener;
}
if (forwardingListener != null) {
forwardingListener.onImageClose(image);
}
};
public SafeCloseImageReaderProxy(@NonNull ImageReaderProxy imageReaderProxy) {
mImageReaderProxy = imageReaderProxy;
mSurface = imageReaderProxy.getSurface();
}
@Nullable
@Override
public ImageProxy acquireLatestImage() {
synchronized (mLock) {
return wrapImageProxy(mImageReaderProxy.acquireLatestImage());
}
}
@Nullable
@Override
public ImageProxy acquireNextImage() {
synchronized (mLock) {
return wrapImageProxy(mImageReaderProxy.acquireNextImage());
}
}
/**
* @inheritDoc <p>This will directly close the wrapped {@link ImageReaderProxy} without
* waiting for
* outstanding {@link ImageProxy} to be closed. Typically, when using {@link
* SafeCloseImageReaderProxy} this should not be directly called. Instead call {@link
* #safeClose()} which safely waits for all ImageProxy to close before closing the wrapped
* ImageReaderProxy.
*/
@Override
public void close() {
synchronized (mLock) {
if (mSurface != null) {
mSurface.release();
}
mImageReaderProxy.close();
}
}
@GuardedBy("mLock")
@Nullable
private ImageProxy wrapImageProxy(@Nullable ImageProxy imageProxy) {
if (imageProxy != null) {
mOutstandingImages++;
SingleCloseImageProxy singleCloseImageProxy =
new SingleCloseImageProxy(imageProxy);
singleCloseImageProxy.addOnImageCloseListener(mImageCloseListener);
return singleCloseImageProxy;
} else {
return null;
}
}
/**
* Close the underlying {@link ImageReaderProxy} safely by deferring the close until the last
* {@link ImageProxy} has been closed.
*
* <p>Once this has been called, no more additional ImageProxy can be acquired from the
* {@link SafeCloseImageReaderProxy}.
*/
public void safeClose() {
synchronized (mLock) {
mIsClosed = true;
mImageReaderProxy.clearOnImageAvailableListener();
if (mOutstandingImages == 0) {
close();
}
}
}
/**
* Check if the {@link SafeCloseImageReaderProxy} is closed for testing.
*/
@VisibleForTesting
public boolean isClosed() {
synchronized (mLock) {
return mIsClosed;
}
}
/**
* Returns the number of empty slots in the queue.
*/
public int getCapacity() {
synchronized (mLock) {
return mImageReaderProxy.getMaxImages() - mOutstandingImages;
}
}
/**
* Sets a listener for close calls on this image.
*
* @param listener to set
*/
public void setOnImageCloseListener(@NonNull OnImageCloseListener listener) {
synchronized (mLock) {
mForwardingImageCloseListener = listener;
}
}
@Override
public int getHeight() {
synchronized (mLock) {
return mImageReaderProxy.getHeight();
}
}
@Override
public int getWidth() {
synchronized (mLock) {
return mImageReaderProxy.getWidth();
}
}
@Override
public int getImageFormat() {
synchronized (mLock) {
return mImageReaderProxy.getImageFormat();
}
}
@Override
public int getMaxImages() {
synchronized (mLock) {
return mImageReaderProxy.getMaxImages();
}
}
@Nullable
@Override
public Surface getSurface() {
synchronized (mLock) {
return mImageReaderProxy.getSurface();
}
}
@Override
public void setOnImageAvailableListener(@NonNull OnImageAvailableListener listener,
@NonNull Executor executor) {
synchronized (mLock) {
mImageReaderProxy.setOnImageAvailableListener(
imageReader -> listener.onImageAvailable(this), executor);
}
}
@Override
public void clearOnImageAvailableListener() {
synchronized (mLock) {
mImageReaderProxy.clearOnImageAvailableListener();
}
}
/**
* Returns the underlying {@link ImageReaderProxy} for testing.
*/
@VisibleForTesting
@NonNull
public ImageReaderProxy getImageReaderProxy() {
synchronized (mLock) {
return mImageReaderProxy;
}
}
}