/*
* 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.ImageFormat;
import android.media.ImageReader;
import android.os.Handler;
import android.util.Log;
import android.util.Size;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Different implementations of {@link ImageReaderProxy}.
*
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public final class ImageReaderProxys {
private static final String TAG = ImageReaderProxys.class.getSimpleName();
private static final int SHARED_IMAGE_FORMAT = ImageFormat.YUV_420_888;
private static final int SHARED_MAX_IMAGES = 8;
static final List<QueuedImageReaderProxy> sSharedImageReaderProxys = new ArrayList<>();
private static Set<DeviceProperties> sSharedReaderWhitelist;
private static ImageReader sSharedImageReader;
private ImageReaderProxys() {
}
/**
* Creates an {@link ImageReaderProxy} which chooses a device-compatible implementation.
*
* @param cameraId of the target camera
* @param width of the reader
* @param height of the reader
* @param format of the reader
* @param maxImages of the reader
* @param handler for on-image-available callbacks
* @return new {@link ImageReaderProxy} instance
*/
static ImageReaderProxy createCompatibleReader(
String cameraId, int width, int height, int format, int maxImages, Handler handler) {
if (inSharedReaderWhitelist(DeviceProperties.create())) {
return createSharedReader(cameraId, width, height, format, maxImages, handler);
} else {
return createIsolatedReader(width, height, format, maxImages, handler);
}
}
/**
* Creates an {@link ImageReaderProxy} which uses its own isolated {@link ImageReader}.
*
* @param width of the reader
* @param height of the reader
* @param format of the reader
* @param maxImages of the reader
* @param handler for on-image-available callbacks
* @return new {@link ImageReaderProxy} instance
*/
public static ImageReaderProxy createIsolatedReader(
int width, int height, int format, int maxImages, Handler handler) {
ImageReader imageReader = ImageReader.newInstance(width, height, format, maxImages);
return new AndroidImageReaderProxy(imageReader);
}
/**
* Creates an {@link ImageReaderProxy} which shares an underlying {@link ImageReader}.
*
* @param cameraId of the target camera
* @param width of the reader
* @param height of the reader
* @param format of the reader
* @param maxImages of the reader
* @param handler for on-image-available callbacks
* @return new {@link ImageReaderProxy} instance
*/
public static ImageReaderProxy createSharedReader(
String cameraId, int width, int height, int format, int maxImages, Handler handler) {
if (sSharedImageReader == null) {
Size resolution =
CameraX.getSurfaceManager().getMaxOutputSize(cameraId, SHARED_IMAGE_FORMAT);
Log.d(TAG, "Resolution of base ImageReader: " + resolution);
sSharedImageReader =
ImageReader.newInstance(
resolution.getWidth(),
resolution.getHeight(),
SHARED_IMAGE_FORMAT,
SHARED_MAX_IMAGES);
}
Log.d(TAG, "Resolution of forked ImageReader: " + new Size(width, height));
QueuedImageReaderProxy imageReaderProxy =
new QueuedImageReaderProxy(
width, height, format, maxImages, sSharedImageReader.getSurface());
sSharedImageReaderProxys.add(imageReaderProxy);
sSharedImageReader.setOnImageAvailableListener(
new ForwardingImageReaderListener(sSharedImageReaderProxys), handler);
imageReaderProxy.addOnReaderCloseListener(
new QueuedImageReaderProxy.OnReaderCloseListener() {
@Override
public void onReaderClose(ImageReaderProxy reader) {
sSharedImageReaderProxys.remove(reader);
if (sSharedImageReaderProxys.isEmpty()) {
clearSharedReaders();
}
}
});
return imageReaderProxy;
}
/**
* Returns true if the device is in the shared reader whitelist.
*
* <p>Devices in the whitelist are known to work with shared readers. Devices outside the
* whitelist may also work with shared readers, but they have not been tested yet.
*
* @param device to check
* @return true if device is in whitelist
*/
static boolean inSharedReaderWhitelist(DeviceProperties device) {
if (sSharedReaderWhitelist == null) {
sSharedReaderWhitelist = new HashSet<>();
for (int sdkVersion = 21; sdkVersion <= 27; ++sdkVersion) {
// TODO(b/128944206)
// The image reader sharing was for 4-use-case scenario (image capture, image
// analysis, video capture, preview). Since 4-use-case scenario is currently
// deprioritized and video capture is deprioritized. Just make this empty list.
}
}
return sSharedReaderWhitelist.contains(device);
}
static void clearSharedReaders() {
sSharedImageReaderProxys.clear();
sSharedImageReader.setOnImageAvailableListener(null, null);
sSharedImageReader.close();
sSharedImageReader = null;
}
}