CheckedSurfaceTexture.java
/*
* 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.graphics.SurfaceTexture;
import android.os.Looper;
import android.util.Size;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.Executor;
/**
* A {@link DeferrableSurface} which verifies the {@link SurfaceTexture} that backs the {@link
* Surface} is unreleased before returning the Surface.
*/
final class CheckedSurfaceTexture extends DeferrableSurface implements SurfaceTextureHolder {
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@NonNull
final FixedSizeSurfaceTexture mSurfaceTexture;
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@NonNull
final Surface mSurface;
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
final Resource mResource;
CheckedSurfaceTexture(Size resolution) {
mResource = new Resource();
mSurfaceTexture = new FixedSizeSurfaceTexture(0, resolution, mResource);
mSurfaceTexture.detachFromGLContext();
mSurface = new Surface(mSurfaceTexture);
mResource.setSurfaceTexture(mSurfaceTexture);
mResource.setSurface(mSurface);
}
/**
* Returns the {@link Surface} that is backed by a {@link SurfaceTexture}.
*
* <p>If the {@link SurfaceTexture} has already been released then the surface will be reset
* using a new {@link SurfaceTexture}.
*/
@Override
@NonNull
public ListenableFuture<Surface> provideSurface() {
return CallbackToFutureAdapter.getFuture(
new CallbackToFutureAdapter.Resolver<Surface>() {
@Override
public Object attachCompleter(
@NonNull final CallbackToFutureAdapter.Completer<Surface> completer) {
Runnable checkAndSetRunnable =
new Runnable() {
@Override
public void run() {
if (mResource.isReleasing()) {
completer.setException(new SurfaceClosedException(
"Surface already released",
CheckedSurfaceTexture.this));
} else {
completer.set(mSurface);
}
}
};
runOnMainThread(checkAndSetRunnable);
return "CheckSurfaceTexture";
}
});
}
@Override
@NonNull
public SurfaceTexture getSurfaceTexture() {
return mSurfaceTexture;
}
@UiThread
@Override
public void release() {
releaseResourceWhenDetached(mResource);
}
void releaseResourceWhenDetached(final Resource resource) {
resource.setReleasing(true);
setOnSurfaceDetachedListener(CameraXExecutors.mainThreadExecutor(),
new OnSurfaceDetachedListener() {
@Override
public void onSurfaceDetached() {
resource.release();
}
});
}
void runOnMainThread(Runnable runnable) {
Executor executor =
(Looper.myLooper() == Looper.getMainLooper())
? CameraXExecutors.directExecutor()
: CameraXExecutors.mainThreadExecutor();
executor.execute(runnable);
}
/**
* Contains a pair of SurfaceTexture and Surface and also implements
* FixedSizeSurfaceTexture.Owner interface to control the release timing of
* FixedSizeSurfaceTexture.
*/
class Resource implements FixedSizeSurfaceTexture.Owner {
FixedSizeSurfaceTexture mSurfaceTexture;
Surface mSurface;
boolean mIsReleasing = false;
boolean mIsReadyToRelease = false;
@UiThread
public void setSurfaceTexture(FixedSizeSurfaceTexture surfaceTexture) {
mSurfaceTexture = surfaceTexture;
}
@UiThread
public void setSurface(Surface surface) {
mSurface = surface;
}
public synchronized boolean isReleasing() {
return mIsReleasing;
}
public synchronized void setReleasing(boolean releasing) {
mIsReleasing = releasing;
}
@Override
public synchronized boolean requestRelease() {
if (mIsReadyToRelease) {
return true;
}
releaseResourceWhenDetached(this);
return false;
}
@UiThread
public synchronized void release() {
mIsReadyToRelease = true;
if (mSurfaceTexture != null) {
mSurfaceTexture.release();
mSurfaceTexture = null;
}
if (mSurface != null) {
mSurface.release();
mSurface = null;
}
}
}
}