Merge "camera: Add support for multiple camera capture requests"

This commit is contained in:
Shuzhen Wang
2018-01-23 00:36:16 +00:00
committed by Android (Google) Code Review
7 changed files with 312 additions and 29 deletions

View File

@@ -173,6 +173,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
private List<CameraCharacteristics.Key<?>> mKeys;
private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
private List<CaptureRequest.Key<?>> mAvailableSessionKeys;
private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys;
private List<CaptureResult.Key<?>> mAvailableResultKeys;
/**
@@ -315,6 +316,45 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
return mAvailableSessionKeys;
}
/**
* <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that can
* be overriden for physical devices backing a logical multi-camera.</p>
*
* <p>This is a subset of android.request.availableRequestKeys which contains a list
* of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
* The respective value of such request key can be obtained by calling
* {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
* individual physical device requests must be built via
* {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
* Such extended capture requests can be passed only to
* {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
* not to {@link CameraCaptureSession#setRepeatingRequest } or
* {@link CameraCaptureSession#setRepeatingBurst }.</p>
*
* <p>The list returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
* <p>Each key is only listed once in the list. The order of the keys is undefined.</p>
*
* @return List of keys that can be overriden in individual physical device requests.
* In case the camera device doesn't support such keys the list can be null.
*/
@SuppressWarnings({"unchecked"})
public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() {
if (mAvailableSessionKeys == null) {
Object crKey = CaptureRequest.Key.class;
Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
int[] filterTags = get(REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS);
if (filterTags == null) {
return null;
}
mAvailablePhysicalRequestKeys =
getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags);
}
return mAvailablePhysicalRequestKeys;
}
/**
* Returns the list of keys supported by this {@link CameraDevice} for querying
* with a {@link CaptureRequest}.
@@ -1745,6 +1785,30 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
public static final Key<int[]> REQUEST_AVAILABLE_SESSION_KEYS =
new Key<int[]>("android.request.availableSessionKeys", int[].class);
/**
* <p>A subset of the available request keys that can be overriden for
* physical devices backing a logical multi-camera.</p>
* <p>This is a subset of android.request.availableRequestKeys which contains a list
* of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
* The respective value of such request key can be obtained by calling
* {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
* individual physical device requests must be built via
* {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
* Such extended capture requests can be passed only to
* {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
* not to {@link CameraCaptureSession#setRepeatingRequest } or
* {@link CameraCaptureSession#setRepeatingBurst }.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Limited capability</b> -
* Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @hide
*/
public static final Key<int[]> REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS =
new Key<int[]>("android.request.availablePhysicalCameraRequestKeys", int[].class);
/**
* <p>The list of image formats that are supported by this
* camera device for output streams.</p>

View File

@@ -31,6 +31,7 @@ import android.os.Handler;
import android.view.Surface;
import java.util.List;
import java.util.Set;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -909,6 +910,47 @@ public abstract class CameraDevice implements AutoCloseable {
public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType)
throws CameraAccessException;
/**
* <p>Create a {@link CaptureRequest.Builder} for new capture requests,
* initialized with template for a target use case. This methods allows
* clients to pass physical camera ids which can be used to customize the
* request for a specific physical camera. The settings are chosen
* to be the best options for the specific logical camera device. If
* additional physical camera ids are passed, then they will also use the
* same settings template. Requests containing individual physical camera
* settings can be passed only to {@link CameraCaptureSession#capture} or
* {@link CameraCaptureSession#captureBurst} and not to
* {@link CameraCaptureSession#setRepeatingRequest} or
* {@link CameraCaptureSession#setRepeatingBurst}</p>
*
* @param templateType An enumeration selecting the use case for this request. Not all template
* types are supported on every device. See the documentation for each template type for
* details.
* @param physicalCameraIdSet A set of physical camera ids that can be used to customize
* the request for a specific physical camera.
* @return a builder for a capture request, initialized with default
* settings for that template, and no output streams
*
* @throws IllegalArgumentException if the templateType is not supported by
* this device, or one of the physical id arguments matches with logical camera id.
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
*
* @see #TEMPLATE_PREVIEW
* @see #TEMPLATE_RECORD
* @see #TEMPLATE_STILL_CAPTURE
* @see #TEMPLATE_VIDEO_SNAPSHOT
* @see #TEMPLATE_MANUAL
* @see CaptureRequest.Builder#setKey
* @see CaptureRequest.Builder#getKey
*/
@NonNull
public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType,
Set<String> physicalCameraIdSet) throws CameraAccessException {
throw new UnsupportedOperationException("Subclasses must override this method");
}
/**
* <p>Create a {@link CaptureRequest.Builder} for a new reprocess {@link CaptureRequest} from a
* {@link TotalCaptureResult}.

View File

@@ -33,9 +33,11 @@ import android.view.Surface;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* <p>An immutable package of settings and outputs needed to capture a single
@@ -219,7 +221,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
private static final ArraySet<Surface> mEmptySurfaceSet = new ArraySet<Surface>();
private final CameraMetadataNative mSettings;
private String mLogicalCameraId;
private CameraMetadataNative mLogicalCameraSettings;
private final HashMap<String, CameraMetadataNative> mPhysicalCameraSettings =
new HashMap<String, CameraMetadataNative>();
private boolean mIsReprocess;
// If this request is part of constrained high speed request list that was created by
// {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
@@ -236,8 +242,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* Used by Binder to unparcel this object only.
*/
private CaptureRequest() {
mSettings = new CameraMetadataNative();
setNativeInstance(mSettings);
mIsReprocess = false;
mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
@@ -249,8 +253,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*/
@SuppressWarnings("unchecked")
private CaptureRequest(CaptureRequest source) {
mSettings = new CameraMetadataNative(source.mSettings);
setNativeInstance(mSettings);
mLogicalCameraId = new String(source.mLogicalCameraId);
for (Map.Entry<String, CameraMetadataNative> entry :
source.mPhysicalCameraSettings.entrySet()) {
mPhysicalCameraSettings.put(new String(entry.getKey()),
new CameraMetadataNative(entry.getValue()));
}
mLogicalCameraSettings = mPhysicalCameraSettings.get(mLogicalCameraId);
setNativeInstance(mLogicalCameraSettings);
mSurfaceSet.addAll(source.mSurfaceSet);
mIsReprocess = source.mIsReprocess;
mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList;
@@ -272,16 +282,35 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* reprocess capture request to the same session where
* the {@link TotalCaptureResult}, used to create the reprocess
* capture, came from.
* @param logicalCameraId Camera Id of the actively open camera that instantiates the
* Builder.
*
* @param physicalCameraIdSet A set of physical camera ids that can be used to customize
* the request for a specific physical camera.
*
* @throws IllegalArgumentException If creating a reprocess capture request with an invalid
* reprocessableSessionId.
* reprocessableSessionId, or multiple physical cameras.
*
* @see CameraDevice#createReprocessCaptureRequest
*/
private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
int reprocessableSessionId) {
mSettings = CameraMetadataNative.move(settings);
setNativeInstance(mSettings);
int reprocessableSessionId, String logicalCameraId, Set<String> physicalCameraIdSet) {
if ((physicalCameraIdSet != null) && isReprocess) {
throw new IllegalArgumentException("Create a reprocess capture request with " +
"with more than one physical camera is not supported!");
}
mLogicalCameraId = logicalCameraId;
mLogicalCameraSettings = CameraMetadataNative.move(settings);
mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings);
if (physicalCameraIdSet != null) {
for (String physicalId : physicalCameraIdSet) {
mPhysicalCameraSettings.put(physicalId, new CameraMetadataNative(
mLogicalCameraSettings));
}
}
setNativeInstance(mLogicalCameraSettings);
mIsReprocess = isReprocess;
if (isReprocess) {
if (reprocessableSessionId == CameraCaptureSession.SESSION_ID_NONE) {
@@ -309,7 +338,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*/
@Nullable
public <T> T get(Key<T> key) {
return mSettings.get(key);
return mLogicalCameraSettings.get(key);
}
/**
@@ -319,7 +348,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
@SuppressWarnings("unchecked")
@Override
protected <T> T getProtected(Key<?> key) {
return (T) mSettings.get(key);
return (T) mLogicalCameraSettings.get(key);
}
/**
@@ -403,7 +432,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @hide
*/
public CameraMetadataNative getNativeCopy() {
return new CameraMetadataNative(mSettings);
return new CameraMetadataNative(mLogicalCameraSettings);
}
/**
@@ -444,14 +473,16 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
return other != null
&& Objects.equals(mUserTag, other.mUserTag)
&& mSurfaceSet.equals(other.mSurfaceSet)
&& mSettings.equals(other.mSettings)
&& mPhysicalCameraSettings.equals(other.mPhysicalCameraSettings)
&& mLogicalCameraId.equals(other.mLogicalCameraId)
&& mLogicalCameraSettings.equals(other.mLogicalCameraSettings)
&& mIsReprocess == other.mIsReprocess
&& mReprocessableSessionId == other.mReprocessableSessionId;
}
@Override
public int hashCode() {
return HashCodeHelpers.hashCodeGeneric(mSettings, mSurfaceSet, mUserTag);
return HashCodeHelpers.hashCodeGeneric(mPhysicalCameraSettings, mSurfaceSet, mUserTag);
}
public static final Parcelable.Creator<CaptureRequest> CREATOR =
@@ -479,8 +510,25 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @hide
*/
private void readFromParcel(Parcel in) {
mSettings.readFromParcel(in);
setNativeInstance(mSettings);
int physicalCameraCount = in.readInt();
if (physicalCameraCount <= 0) {
throw new RuntimeException("Physical camera count" + physicalCameraCount +
" should always be positive");
}
//Always start with the logical camera id
mLogicalCameraId = in.readString();
mLogicalCameraSettings = new CameraMetadataNative();
mLogicalCameraSettings.readFromParcel(in);
setNativeInstance(mLogicalCameraSettings);
mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings);
for (int i = 1; i < physicalCameraCount; i++) {
String physicalId = in.readString();
CameraMetadataNative physicalCameraSettings = new CameraMetadataNative();
physicalCameraSettings.readFromParcel(in);
mPhysicalCameraSettings.put(physicalId, physicalCameraSettings);
}
mIsReprocess = (in.readInt() == 0) ? false : true;
mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
@@ -509,7 +557,19 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
@Override
public void writeToParcel(Parcel dest, int flags) {
mSettings.writeToParcel(dest, flags);
int physicalCameraCount = mPhysicalCameraSettings.size();
dest.writeInt(physicalCameraCount);
//Logical camera id and settings always come first.
dest.writeString(mLogicalCameraId);
mLogicalCameraSettings.writeToParcel(dest, flags);
for (Map.Entry<String, CameraMetadataNative> entry : mPhysicalCameraSettings.entrySet()) {
if (entry.getKey().equals(mLogicalCameraId)) {
continue;
}
dest.writeString(entry.getKey());
entry.getValue().writeToParcel(dest, flags);
}
dest.writeInt(mIsReprocess ? 1 : 0);
synchronized (mSurfacesLock) {
@@ -541,6 +601,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
return Collections.unmodifiableCollection(mSurfaceSet);
}
/**
* Retrieves the logical camera id.
* @hide
*/
public String getLogicalCameraId() {
return mLogicalCameraId;
}
/**
* @hide
*/
@@ -633,14 +701,20 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* submits a reprocess capture request to the same session
* where the {@link TotalCaptureResult}, used to create the
* reprocess capture, came from.
* @param logicalCameraId Camera Id of the actively open camera that instantiates the
* Builder.
* @param physicalCameraIdSet A set of physical camera ids that can be used to customize
* the request for a specific physical camera.
*
* @throws IllegalArgumentException If creating a reprocess capture request with an invalid
* reprocessableSessionId.
* @hide
*/
public Builder(CameraMetadataNative template, boolean reprocess,
int reprocessableSessionId) {
mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId);
int reprocessableSessionId, String logicalCameraId,
Set<String> physicalCameraIdSet) {
mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId,
logicalCameraId, physicalCameraIdSet);
}
/**
@@ -682,7 +756,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* type to the key.
*/
public <T> void set(@NonNull Key<T> key, T value) {
mRequest.mSettings.set(key, value);
mRequest.mLogicalCameraSettings.set(key, value);
}
/**
@@ -696,7 +770,71 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*/
@Nullable
public <T> T get(Key<T> key) {
return mRequest.mSettings.get(key);
return mRequest.mLogicalCameraSettings.get(key);
}
/**
* Set a capture request field to a value. The field definitions can be
* found in {@link CaptureRequest}.
*
* <p>Setting a field to {@code null} will remove that field from the capture request.
* Unless the field is optional, removing it will likely produce an error from the camera
* device when the request is submitted.</p>
*
*<p>This method can be called for logical camera devices, which are devices that have
* REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
* {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
* physical devices that are backing the logical camera. The camera Id included in the
* 'physicalCameraId' argument selects an individual physical device that will receive
* the customized capture request field.</p>
*
* @throws IllegalArgumentException if the physical camera id is not valid
*
* @param key The metadata field to write.
* @param value The value to set the field to, which must be of a matching
* @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained
* via calls to {@link CameraCharacteristics#getPhysicalCameraIds}.
* @return The builder object.
* type to the key.
*/
public <T> Builder setPhysicalCameraKey(@NonNull Key<T> key, T value,
@NonNull String physicalCameraId) {
if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) {
throw new IllegalArgumentException("Physical camera id: " + physicalCameraId +
" is not valid!");
}
mRequest.mPhysicalCameraSettings.get(physicalCameraId).set(key, value);
return this;
}
/**
* Get a capture request field value for a specific physical camera Id. The field
* definitions can be found in {@link CaptureRequest}.
*
*<p>This method can be called for logical camera devices, which are devices that have
* REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
* {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
* physical devices that are backing the logical camera. The camera Id included in the
* 'physicalCameraId' argument selects an individual physical device and returns
* its specific capture request field.</p>
*
* @throws IllegalArgumentException if the key or physical camera id were not valid
*
* @param key The metadata field to read.
* @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained
* via calls to {@link CameraCharacteristics#getPhysicalCameraIds}.
* @return The value of that key, or {@code null} if the field is not set.
*/
@Nullable
public <T> T getPhysicalCameraKey(Key<T> key,@NonNull String physicalCameraId) {
if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) {
throw new IllegalArgumentException("Physical camera id: " + physicalCameraId +
" is not valid!");
}
return mRequest.mPhysicalCameraSettings.get(physicalCameraId).get(key);
}
/**
@@ -748,7 +886,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* @hide
*/
public boolean isEmpty() {
return mRequest.mSettings.isEmpty();
return mRequest.mLogicalCameraSettings.isEmpty();
}
}

View File

@@ -94,8 +94,8 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl
// Note that after this step, the requestMetadata is mutated (swapped) and can not be used
// for next request builder creation.
CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder(
requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
request.getLogicalCameraId(), /*physicalCameraIdSet*/ null);
// Carry over userTag, as native metadata doesn't have this field.
singleTargetRequestBuilder.setTag(request.getTag());
@@ -120,7 +120,8 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl
// CaptureRequest.Builder creation.
requestMetadata = new CameraMetadataNative(request.getNativeCopy());
doubleTargetRequestBuilder = new CaptureRequest.Builder(
requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
request.getLogicalCameraId(), /*physicalCameraIdSet*/null);
doubleTargetRequestBuilder.setTag(request.getTag());
doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);

View File

@@ -55,6 +55,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -710,6 +711,38 @@ public class CameraDeviceImpl extends CameraDevice
request.set(CaptureRequest.CONTROL_ENABLE_ZSL, newValue);
}
@Override
public CaptureRequest.Builder createCaptureRequest(int templateType,
Set<String> physicalCameraIdSet)
throws CameraAccessException {
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
for (String physicalId : physicalCameraIdSet) {
if (physicalId == getId()) {
throw new IllegalStateException("Physical id matches the logical id!");
}
}
CameraMetadataNative templatedRequest = null;
templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
// If app target SDK is older than O, or it's not a still capture template, enableZsl
// must be false in the default request.
if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
templateType != TEMPLATE_STILL_CAPTURE) {
overrideEnableZsl(templatedRequest, false);
}
CaptureRequest.Builder builder = new CaptureRequest.Builder(
templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
getId(), physicalCameraIdSet);
return builder;
}
}
@Override
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
@@ -728,7 +761,8 @@ public class CameraDeviceImpl extends CameraDevice
}
CaptureRequest.Builder builder = new CaptureRequest.Builder(
templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
getId(), /*physicalCameraIdSet*/ null);
return builder;
}
@@ -744,7 +778,7 @@ public class CameraDeviceImpl extends CameraDevice
CameraMetadataNative(inputResult.getNativeCopy());
return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
inputResult.getSessionId());
inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null);
}
}