blob: 17773246691d241c0b961c234f873b514fb00d23 [file] [log] [blame]
/*
* Copyright 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.util.SparseArray;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A {@link ImageProxyBundle} with a predefined set of captured ids. The {@link ListenableFuture}
* for the capture id becomes valid when the corresponding {@link ImageProxy} has been set.
*/
final class SettableImageProxyBundle implements ImageProxyBundle {
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
final Object mLock = new Object();
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@GuardedBy("mLock")
final SparseArray<CallbackToFutureAdapter.Completer<ImageProxy>> mCompleters =
new SparseArray<>();
/** Map of id to {@link ImageProxy} Future. */
@GuardedBy("mLock")
private final SparseArray<ListenableFuture<ImageProxy>> mFutureResults = new SparseArray<>();
@GuardedBy("mLock")
private final List<ImageProxy> mOwnedImageProxies = new ArrayList<>();
private final List<Integer> mCaptureIdList;
// Whether or not the bundle has been closed or not
@GuardedBy("mLock")
private boolean mClosed = false;
/**
* Create a {@link ImageProxyBundle} for captures with the given ids.
*
* @param captureIds The set of captureIds contained by the ImageProxyBundle
*/
SettableImageProxyBundle(List<Integer> captureIds) {
mCaptureIdList = captureIds;
setup();
}
@Override
public ListenableFuture<ImageProxy> getImageProxy(int captureId) {
synchronized (mLock) {
if (mClosed) {
throw new IllegalStateException("ImageProxyBundle already closed.");
}
// Returns the future that has been set if it exists
ListenableFuture<ImageProxy> result = mFutureResults.get(captureId);
if (result == null) {
throw new IllegalArgumentException(
"ImageProxyBundle does not contain this id: " + captureId);
}
return result;
}
}
@Override
public List<Integer> getCaptureIds() {
return Collections.unmodifiableList(mCaptureIdList);
}
/**
* Add an {@link ImageProxy} to synchronize.
*/
void addImageProxy(ImageProxy imageProxy) {
synchronized (mLock) {
if (mClosed) {
return;
}
Integer captureId = (Integer) imageProxy.getImageInfo().getTag();
if (captureId == null) {
throw new IllegalArgumentException("CaptureId is null.");
}
// If the CaptureId is associated with this SettableImageProxyBundle, set the
// corresponding Future. Otherwise, throws exception.
CallbackToFutureAdapter.Completer<ImageProxy> completer = mCompleters.get(captureId);
if (completer != null) {
completer.set(imageProxy);
mOwnedImageProxies.add(imageProxy);
} else {
throw new IllegalArgumentException(
"ImageProxyBundle does not contain this id: " + captureId);
}
}
}
/**
* Flush all {@link ImageProxy} that have been added.
*/
void close() {
synchronized (mLock) {
if (mClosed) {
return;
}
for (ImageProxy imageProxy : mOwnedImageProxies) {
imageProxy.close();
}
mOwnedImageProxies.clear();
mFutureResults.clear();
mCompleters.clear();
mClosed = true;
}
}
/**
* Clear all {@link ImageProxy} that have been added and recreate the entries from the bundle.
*/
void reset() {
synchronized (mLock) {
if (mClosed) {
return;
}
for (ImageProxy imageProxy : mOwnedImageProxies) {
imageProxy.close();
}
mOwnedImageProxies.clear();
mFutureResults.clear();
mCompleters.clear();
setup();
}
}
private void setup() {
synchronized (mLock) {
for (final int captureId : mCaptureIdList) {
ListenableFuture<ImageProxy> futureResult = CallbackToFutureAdapter.getFuture(
new CallbackToFutureAdapter.Resolver<ImageProxy>() {
@Override
public Object attachCompleter(
@NonNull CallbackToFutureAdapter.Completer<ImageProxy>
completer) {
synchronized (mLock) { // Not technically needed since
// attachCompleter is called inline, but mLock is re-entrant
// so there's no harm.
mCompleters.put(captureId, completer);
}
return "getImageProxy(id: " + captureId + ")";
}
});
mFutureResults.put(captureId, futureResult);
}
}
}
}