The list of image formats that are supported by this
* camera device for output streams.
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 639795ab80803..40ee8348ac4cb 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -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;
+ /**
+ * 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}
+ *
+ * @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 physicalCameraIdSet) throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
/**
* Create a {@link CaptureRequest.Builder} for a new reprocess {@link CaptureRequest} from a
* {@link TotalCaptureResult}.
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 437d6b0e629f8..481b7649610a7 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -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;
/**
*
An immutable package of settings and outputs needed to capture a single
@@ -219,7 +221,11 @@ public final class CaptureRequest extends CameraMetadata>
private static final ArraySet mEmptySurfaceSet = new ArraySet();
- private final CameraMetadataNative mSettings;
+ private String mLogicalCameraId;
+ private CameraMetadataNative mLogicalCameraSettings;
+ private final HashMap mPhysicalCameraSettings =
+ new HashMap();
+
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>
* 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>
*/
@SuppressWarnings("unchecked")
private CaptureRequest(CaptureRequest source) {
- mSettings = new CameraMetadataNative(source.mSettings);
- setNativeInstance(mSettings);
+ mLogicalCameraId = new String(source.mLogicalCameraId);
+ for (Map.Entry 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>
* 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 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>
*/
@Nullable
public T get(Key key) {
- return mSettings.get(key);
+ return mLogicalCameraSettings.get(key);
}
/**
@@ -319,7 +348,7 @@ public final class CaptureRequest extends CameraMetadata>
@SuppressWarnings("unchecked")
@Override
protected T getProtected(Key> key) {
- return (T) mSettings.get(key);
+ return (T) mLogicalCameraSettings.get(key);
}
/**
@@ -403,7 +432,7 @@ public final class CaptureRequest extends CameraMetadata>
* @hide
*/
public CameraMetadataNative getNativeCopy() {
- return new CameraMetadataNative(mSettings);
+ return new CameraMetadataNative(mLogicalCameraSettings);
}
/**
@@ -444,14 +473,16 @@ public final class CaptureRequest extends CameraMetadata>
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 CREATOR =
@@ -479,8 +510,25 @@ public final class CaptureRequest extends CameraMetadata>
* @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>
@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 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>
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>
* 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 physicalCameraIdSet) {
+ mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId,
+ logicalCameraId, physicalCameraIdSet);
}
/**
@@ -682,7 +756,7 @@ public final class CaptureRequest extends CameraMetadata>
* type to the key.
*/
public void set(@NonNull Key key, T value) {
- mRequest.mSettings.set(key, value);
+ mRequest.mLogicalCameraSettings.set(key, value);
}
/**
@@ -696,7 +770,71 @@ public final class CaptureRequest extends CameraMetadata>
*/
@Nullable
public T get(Key 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}.
+ *
+ * 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.
+ *
+ *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.
+ *
+ * @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 Builder setPhysicalCameraKey(@NonNull Key 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}.
+ *
+ *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.
+ *
+ * @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 getPhysicalCameraKey(Key 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>
* @hide
*/
public boolean isEmpty() {
- return mRequest.mSettings.isEmpty();
+ return mRequest.mLogicalCameraSettings.isEmpty();
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 8c4dbfa58d051..06c2c25ab6bbb 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -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);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 8d1c96f6fb7fc..cab9d7046fcdc 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -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 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);
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index fadb76d80fe5c..4c96d890a751a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -197,7 +197,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
assertFalse(metadata.isEmpty());
CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false,
- CameraCaptureSession.SESSION_ID_NONE);
+ CameraCaptureSession.SESSION_ID_NONE, mCameraId, /*physicalCameraIdSet*/null);
assertFalse(request.isEmpty());
assertFalse(metadata.isEmpty());
if (needStream) {