diff options
| author | 2020-01-27 18:24:07 +0000 | |
|---|---|---|
| committer | 2020-01-27 18:24:07 +0000 | |
| commit | 1636af3a1afbe63ff839a26419554d838f51d97b (patch) | |
| tree | 557b6e9b7a8d6484a4d466b648b21c3bcfb7a9a0 | |
| parent | 344d21a80f8805bf418a612d7589ef3794f1fed6 (diff) | |
| parent | a24f94b1fed3eca098f978e3925858e797d2c544 (diff) | |
Merge "camera2: Add apis for querying concurrent streaming support."
9 files changed, 565 insertions, 13 deletions
diff --git a/api/current.txt b/api/current.txt index b3d136df9c7b..b6d67ab412ca 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12017,6 +12017,7 @@ package android.content.pm { field public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = "android.hardware.camera.capability.manual_post_processing"; field public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = "android.hardware.camera.capability.manual_sensor"; field public static final String FEATURE_CAMERA_CAPABILITY_RAW = "android.hardware.camera.capability.raw"; + field public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent"; field public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external"; field public static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash"; field public static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front"; @@ -17174,6 +17175,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE; + field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES; @@ -17263,6 +17265,8 @@ package android.hardware.camera2 { public final class CameraManager { method @NonNull public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException; method @NonNull public String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException; + method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentStreamingCameraIds() throws android.hardware.camera2.CameraAccessException; + method @RequiresPermission(android.Manifest.permission.CAMERA) public boolean isConcurrentSessionConfigurationSupported(@NonNull java.util.Map<java.lang.String,android.hardware.camera2.params.SessionConfiguration>) throws android.hardware.camera2.CameraAccessException; method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull android.hardware.camera2.CameraDevice.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException; method public void registerAvailabilityCallback(@NonNull android.hardware.camera2.CameraManager.AvailabilityCallback, @Nullable android.os.Handler); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 38ffb516b138..b64c001ea6e2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1960,6 +1960,15 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device's main front and back cameras can stream + * concurrently as described in {@link + * android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds()} + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device is capable of communicating with * consumer IR devices. */ diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index dfc4f0f6586c..b3a1ee2f9b69 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2875,7 +2875,28 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri @NonNull public static final Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES = new Key<int[]>("android.scaler.availableRotateAndCropModes", int[].class); - + /** + * <p>An array of mandatory concurrent stream combinations. + * This is an app-readable conversion of the concurrent mandatory stream combination + * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p> + * <p>The array of + * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is + * generated according to the documented + * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each device + * which has its Id present in the set returned by + * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}. + * Clients can use the array as a quick reference to find an appropriate camera stream + * combination. + * The mandatory stream combination array will be {@code null} in case the device is not a part + * of at least one set of combinations returned by + * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @SyntheticKey + public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS = + new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryConcurrentStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class); /** * <p>The area of the image sensor which corresponds to active pixels after any geometric * distortion correction has been applied.</p> diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index cc066812ba1f..24d931154533 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -680,6 +680,25 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + *<p>Devices capable of streaming concurrently with other devices as described by + * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds} have the + * following guaranteed streams (when streaming concurrently with other devices)</p> + * + * <table> + * <tr><th colspan="5">Concurrent stream guaranteed configurations</th></tr> + * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>In-app video / image processing.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>In-app viewfinder analysis.</td> </tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>Standard Recording.</td> </tr> + * </table><br> + * </p> + * + * <p> For guaranteed concurrent stream configurations, MAXIMUM refers to the camera device's + * resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or + * 720p(1280X720) whichever is lower. </p> * <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} * includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices * supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV} diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 55025f0411f9..9ee56a928d81 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -31,6 +31,9 @@ import android.hardware.camera2.impl.CameraDeviceImpl; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.legacy.CameraDeviceUserShim; import android.hardware.camera2.legacy.LegacyMetadataMapper; +import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.utils.CameraIdAndSessionConfiguration; +import android.hardware.camera2.utils.ConcurrentCameraIdCombination; import android.os.Binder; import android.os.DeadObjectException; import android.os.Handler; @@ -40,6 +43,7 @@ import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.SystemProperties; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Size; import android.view.Display; @@ -48,6 +52,7 @@ import android.view.WindowManager; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -126,6 +131,66 @@ public final class CameraManager { } /** + * Return the list of combinations of currently connected camera devices identifiers, which + * support configuring camera device sessions concurrently. + * + * <p>The set of combinations may include camera devices that may be in use by other camera API + * clients.</p> + * + * <p>The set of combinations doesn't contain physical cameras that can only be used as + * part of a logical multi-camera device.</p> + * + * @return The set of combinations of currently connected camera devices, that may have + * sessions configured concurrently. The set of combinations will be empty if no such + * combinations are supported by the camera subsystem. + * + * @throws CameraAccessException if the camera device has been disconnected. + */ + @NonNull + public Set<Set<String>> getConcurrentStreamingCameraIds() throws CameraAccessException { + return CameraManagerGlobal.get().getConcurrentStreamingCameraIds(); + } + + /** + * Checks whether the provided set of camera devices and their corresponding + * {@link SessionConfiguration} can be configured concurrently. + * + * <p>This method performs a runtime check of the given {@link SessionConfiguration} and camera + * id combinations. The result confirms whether or not the passed session configurations can be + * successfully used to create camera capture sessions concurrently, on the given camera + * devices using {@link CameraDevice#createCaptureSession(SessionConfiguration)}. + * </p> + * + * <p>The method can be called at any point before, during and after active capture sessions. + * It will not impact normal camera behavior in any way and must complete significantly + * faster than creating a regular or constrained capture session.</p> + * + * <p>Although this method is faster than creating a new capture session, it is not intended + * to be used for exploring the entire space of supported concurrent stream combinations. The + * available mandatory concurrent stream combinations may be obtained by querying + * {@link #getCameraCharacteristics} for the key + * SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS. </p> + * + * <p>Note that session parameters will be ignored and calls to + * {@link SessionConfiguration#setSessionParameters} are not required.</p> + * + * @return {@code true} if the given combination of session configurations and corresponding + * camera ids are concurrently supported by the camera sub-system, + * {@code false} otherwise. + * + * @throws IllegalArgumentException if the set of camera devices provided is not a subset of + * those returned by getConcurrentStreamingCameraIds() + * @throws CameraAccessException if one of the camera devices queried is no longer connected. + */ + @RequiresPermission(android.Manifest.permission.CAMERA) + public boolean isConcurrentSessionConfigurationSupported( + @NonNull Map<String, SessionConfiguration> cameraIdAndSessionConfig) + throws CameraAccessException { + return CameraManagerGlobal.get().isConcurrentSessionConfigurationSupported( + cameraIdAndSessionConfig); + } + + /** * Register a callback to be notified about camera device availability. * * <p>Registering the same callback again will replace the handler with the @@ -336,8 +401,10 @@ public final class CameraManager { } catch (NumberFormatException e) { Log.e(TAG, "Failed to parse camera Id " + cameraId + " to integer"); } + boolean hasConcurrentStreams = + CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId); + info.setHasMandatoryConcurrentStreams(hasConcurrentStreams); info.setDisplaySize(displaySize); - characteristics = new CameraCharacteristics(info); } } catch (ServiceSpecificException e) { @@ -964,6 +1031,9 @@ public final class CameraManager { private final ArrayMap<String, ArrayList<String>> mUnavailablePhysicalDevices = new ArrayMap<String, ArrayList<String>>(); + private final Set<Set<String>> mConcurrentCameraIdCombinations = + new ArraySet<Set<String>>(); + // Registered availablility callbacks and their executors private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap = new ArrayMap<AvailabilityCallback, Executor>(); @@ -1068,7 +1138,22 @@ public final class CameraManager { } catch (RemoteException e) { // Camera service is now down, leave mCameraService as null } + + try { + ConcurrentCameraIdCombination[] cameraIdCombinations = + cameraService.getConcurrentStreamingCameraIds(); + for (ConcurrentCameraIdCombination comb : cameraIdCombinations) { + mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination()); + } + } catch (ServiceSpecificException e) { + // Unexpected failure + throw new IllegalStateException("Failed to get concurrent camera id combinations", + e); + } catch (RemoteException e) { + // Camera service died in all probability + } } + private String[] extractCameraIdListLocked() { String[] cameraIds = null; int idCount = 0; @@ -1089,6 +1174,31 @@ public final class CameraManager { } return cameraIds; } + + private Set<Set<String>> extractConcurrentCameraIdListLocked() { + Set<Set<String>> concurrentCameraIds = new ArraySet<Set<String>>(); + for (Set<String> cameraIds : mConcurrentCameraIdCombinations) { + Set<String> extractedCameraIds = new ArraySet<String>(); + for (String cameraId : cameraIds) { + // if the camera id status is NOT_PRESENT or ENUMERATING; skip the device. + // TODO: Would a device status NOT_PRESENT ever be in the map ? it gets removed + // in the callback anyway. + Integer status = mDeviceStatus.get(cameraId); + if (status == null) { + // camera id not present + continue; + } + if (status == ICameraServiceListener.STATUS_ENUMERATING + || status == ICameraServiceListener.STATUS_NOT_PRESENT) { + continue; + } + extractedCameraIds.add(cameraId); + } + concurrentCameraIds.add(extractedCameraIds); + } + return concurrentCameraIds; + } + private static void sortCameraIds(String[] cameraIds) { // The sort logic must match the logic in // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds @@ -1214,6 +1324,88 @@ public final class CameraManager { return cameraIds; } + public @NonNull Set<Set<String>> getConcurrentStreamingCameraIds() { + Set<Set<String>> concurrentStreamingCameraIds = null; + synchronized (mLock) { + // Try to make sure we have an up-to-date list of concurrent camera devices. + connectCameraServiceLocked(); + concurrentStreamingCameraIds = extractConcurrentCameraIdListLocked(); + } + // TODO: Some sort of sorting ? + return concurrentStreamingCameraIds; + } + + public boolean isConcurrentSessionConfigurationSupported( + @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations) + throws CameraAccessException { + + if (cameraIdsAndSessionConfigurations == null) { + throw new IllegalArgumentException("cameraIdsAndSessionConfigurations was null"); + } + + int size = cameraIdsAndSessionConfigurations.size(); + if (size == 0) { + throw new IllegalArgumentException("camera id and session combination is empty"); + } + + synchronized (mLock) { + // Go through all the elements and check if the camera ids are valid at least / + // belong to one of the combinations returned by getConcurrentStreamingCameraIds() + boolean subsetFound = false; + for (Set<String> combination : mConcurrentCameraIdCombinations) { + if (combination.containsAll(cameraIdsAndSessionConfigurations.keySet())) { + subsetFound = true; + } + } + if (!subsetFound) { + throw new IllegalArgumentException( + "The set of camera ids provided is not a subset of" + + "getConcurrentStreamingCameraIds"); + } + CameraIdAndSessionConfiguration [] cameraIdsAndConfigs = + new CameraIdAndSessionConfiguration[size]; + int i = 0; + for (Map.Entry<String, SessionConfiguration> pair : + cameraIdsAndSessionConfigurations.entrySet()) { + cameraIdsAndConfigs[i] = + new CameraIdAndSessionConfiguration(pair.getKey(), pair.getValue()); + i++; + } + try { + return mCameraService.isConcurrentSessionConfigurationSupported( + cameraIdsAndConfigs); + } catch (ServiceSpecificException e) { + throwAsPublicException(e); + } catch (RemoteException e) { + // Camera service died - act as if the camera was disconnected + throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, + "Camera service is currently unavailable", e); + } + } + + return false; + } + + /** + * Helper function to find out if a camera id is in the set of combinations returned by + * getConcurrentStreamingCameraIds() + * @param cameraId the unique identifier of the camera device to query + * @return Whether the camera device was found in the set of combinations returned by + * getConcurrentStreamingCameraIds + */ + public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) { + if (!mDeviceStatus.containsKey(cameraId)) { + Log.e(TAG, "cameraIdHasConcurrentStreamsLocked called on non existing camera id"); + return false; + } + for (Set<String> comb : mConcurrentCameraIdCombinations) { + if (comb.contains(cameraId)) { + return true; + } + } + return false; + } + public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException { synchronized(mLock) { @@ -1698,6 +1890,8 @@ public final class CameraManager { cameraId); } + mConcurrentCameraIdCombinations.clear(); + scheduleCameraServiceReconnectionLocked(); } } diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 3ae3d786af2a..aefe66fe5dec 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -621,6 +621,16 @@ public class CameraMetadataNative implements Parcelable { } }); sGetCommandMap.put( + CameraCharacteristics.SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS.getNativeKey(), + new GetCommand() { + @Override + @SuppressWarnings("unchecked") + public <T> T getValue(CameraMetadataNative metadata, Key<T> key) { + return (T) metadata.getMandatoryConcurrentStreamCombinations(); + } + }); + + sGetCommandMap.put( CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() { @Override @SuppressWarnings("unchecked") @@ -1247,7 +1257,8 @@ public class CameraMetadataNative implements Parcelable { return ret; } - private MandatoryStreamCombination[] getMandatoryStreamCombinations() { + private MandatoryStreamCombination[] getMandatoryStreamCombinationsHelper( + boolean getConcurrent) { int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); ArrayList<Integer> caps = new ArrayList<Integer>(); caps.ensureCapacity(capabilities.length); @@ -1257,7 +1268,13 @@ public class CameraMetadataNative implements Parcelable { int hwLevel = getBase(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); MandatoryStreamCombination.Builder build = new MandatoryStreamCombination.Builder( mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap()); - List<MandatoryStreamCombination> combs = build.getAvailableMandatoryStreamCombinations(); + + List<MandatoryStreamCombination> combs = null; + if (getConcurrent) { + combs = build.getAvailableMandatoryConcurrentStreamCombinations(); + } else { + combs = build.getAvailableMandatoryStreamCombinations(); + } if ((combs != null) && (!combs.isEmpty())) { MandatoryStreamCombination[] combArray = new MandatoryStreamCombination[combs.size()]; combArray = combs.toArray(combArray); @@ -1267,6 +1284,17 @@ public class CameraMetadataNative implements Parcelable { return null; } + private MandatoryStreamCombination[] getMandatoryConcurrentStreamCombinations() { + if (!mHasMandatoryConcurrentStreams) { + return null; + } + return getMandatoryStreamCombinationsHelper(true); + } + + private MandatoryStreamCombination[] getMandatoryStreamCombinations() { + return getMandatoryStreamCombinationsHelper(false); + } + private StreamConfigurationMap getStreamConfigurationMap() { StreamConfiguration[] configurations = getBase( CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS); @@ -1614,6 +1642,7 @@ public class CameraMetadataNative implements Parcelable { } private int mCameraId = -1; + private boolean mHasMandatoryConcurrentStreams = false; private Size mDisplaySize = new Size(0, 0); /** @@ -1628,6 +1657,18 @@ public class CameraMetadataNative implements Parcelable { } /** + * Set the current camera Id. + * + * @param hasMandatoryConcurrentStreams whether the metadata advertises mandatory concurrent + * streams. + * + * @hide + */ + public void setHasMandatoryConcurrentStreams(boolean hasMandatoryConcurrentStreams) { + mHasMandatoryConcurrentStreams = hasMandatoryConcurrentStreams; + } + + /** * Set the current display size. * * @param displaySize The current display size. @@ -1682,6 +1723,7 @@ public class CameraMetadataNative implements Parcelable { public void swap(CameraMetadataNative other) { nativeSwap(other); mCameraId = other.mCameraId; + mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams; mDisplaySize = other.mDisplaySize; } diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index 23f18a80caf8..41e1443d6866 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -16,30 +16,27 @@ package android.hardware.camera2.params; -import static com.android.internal.util.Preconditions.*; import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormat; -import android.annotation.IntRange; +import static com.android.internal.util.Preconditions.*; + import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.Context; import android.graphics.ImageFormat; import android.graphics.ImageFormat.Format; import android.hardware.camera2.CameraCharacteristics; -import android.hardware.camera2.CameraCharacteristics.Key; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.HashCodeHelpers; -import android.graphics.PixelFormat; import android.media.CamcorderProfile; -import android.util.Size; import android.util.Log; import android.util.Pair; +import android.util.Size; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -200,7 +197,6 @@ public final class MandatoryStreamCombination { mDescription = description; mIsReprocessable = isReprocessable; } - /** * Get the mandatory stream combination description. * @@ -271,7 +267,7 @@ public final class MandatoryStreamCombination { mStreamsInformation.hashCode()); } - private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM } + private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p } private static enum ReprocessType { NONE, PRIVATE, YUV } private static final class StreamTemplate { public int mFormat; @@ -653,6 +649,27 @@ public final class MandatoryStreamCombination { /*reprocessType*/ ReprocessType.YUV), }; + private static StreamCombinationTemplate sConcurrentStreamCombinations[] = { + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p) }, + "In-app video / image processing"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p) }, + "preview / preview to GPU"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)}, + "In-app video / image processing with preview"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)}, + "In-app video / image processing with preview"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p), + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p)}, + "Standard Recording"), + }; + /** * Helper builder class to generate a list of available mandatory stream combinations. * @hide @@ -687,6 +704,64 @@ public final class MandatoryStreamCombination { } /** + * Retrieve a list of all available mandatory concurrent stream combinations. + * This method should only be called for devices which are listed in combinations returned + * by CameraManager.getConcurrentStreamingCameraIds. + * + * @return a non-modifiable list of supported mandatory concurrent stream combinations. + */ + public @NonNull List<MandatoryStreamCombination> + getAvailableMandatoryConcurrentStreamCombinations() { + // Since concurrent streaming support is optional, we mandate these stream + // combinations regardless of camera device capabilities. + if (!isColorOutputSupported()) { + Log.v(TAG, "Device is not backward compatible!"); + throw new IllegalArgumentException("Camera device which is not BACKWARD_COMPATIBLE" + + " cannot have mandatory concurrent streams"); + } + Size size720p = new Size(1280, 720); + + ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations = + new ArrayList<MandatoryStreamCombination>(); + availableConcurrentStreamCombinations.ensureCapacity( + sConcurrentStreamCombinations.length); + for (StreamCombinationTemplate combTemplate : sConcurrentStreamCombinations) { + ArrayList<MandatoryStreamInformation> streamsInfo = + new ArrayList<MandatoryStreamInformation>(); + streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length); + for (StreamTemplate template : combTemplate.mStreamTemplates) { + MandatoryStreamInformation streamInfo; + List<Size> sizes = new ArrayList<Size>(); + Size sizeChosen = + getMinSize(size720p, + getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat))); + sizes.add(sizeChosen); + try { + streamInfo = new MandatoryStreamInformation(sizes, template.mFormat); + } catch (IllegalArgumentException e) { + String cause = "No available sizes found for format: " + template.mFormat + + " size threshold: " + template.mSizeThreshold + " combination: " + + combTemplate.mDescription; + throw new RuntimeException(cause, e); + } + streamsInfo.add(streamInfo); + } + + MandatoryStreamCombination streamCombination; + try { + streamCombination = new MandatoryStreamCombination(streamsInfo, + combTemplate.mDescription, /*isReprocess*/false); + } catch (IllegalArgumentException e) { + String cause = "No stream information for mandatory combination: " + + combTemplate.mDescription; + throw new RuntimeException(cause, e); + } + availableConcurrentStreamCombinations.add(streamCombination); + } + return Collections.unmodifiableList(availableConcurrentStreamCombinations); + } + + /** * Retrieve a list of all available mandatory stream combinations. * * @return a non-modifiable list of supported mandatory stream combinations or @@ -965,6 +1040,18 @@ public final class MandatoryStreamCombination { } /** + * Return the lower size + */ + public static @Nullable Size getMinSize(Size a, Size b) { + if (a == null || b == null) { + throw new IllegalArgumentException("sizes was empty"); + } + if (a.getWidth() * a.getHeight() < b.getHeight() * b.getWidth()) { + return a; + } + return b; + } + /** * Get the largest size by area. * * @param sizes an array of sizes, must have at least 1 element diff --git a/core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java b/core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java new file mode 100644 index 000000000000..cdc037cbd020 --- /dev/null +++ b/core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 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 android.hardware.camera2.utils; + +import android.annotation.NonNull; +import android.hardware.camera2.params.SessionConfiguration; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * CameraIdAndSessionConfiguration + * + * Includes the pair of a cameraId and its corresponding SessionConfiguration, to be used with + * ICameraService.isConcurrentSessionConfigurationSupported. + * @hide + */ +public class CameraIdAndSessionConfiguration implements Parcelable { + + private String mCameraId; + private SessionConfiguration mSessionConfiguration; + + public CameraIdAndSessionConfiguration(@NonNull String cameraId, + @NonNull SessionConfiguration sessionConfiguration) { + mCameraId = cameraId; + mSessionConfiguration = sessionConfiguration; + } + + public static final @NonNull + Parcelable.Creator<CameraIdAndSessionConfiguration> CREATOR = + new Parcelable.Creator<CameraIdAndSessionConfiguration>() { + @Override + public CameraIdAndSessionConfiguration createFromParcel(Parcel in) { + return new CameraIdAndSessionConfiguration(in); + } + + @Override + public CameraIdAndSessionConfiguration[] newArray(int size) { + return new CameraIdAndSessionConfiguration[size]; + } + }; + + private CameraIdAndSessionConfiguration(Parcel in) { + readFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mCameraId); + mSessionConfiguration.writeToParcel(dest, flags); + } + + /** + * helper for CREATOR + */ + public void readFromParcel(Parcel in) { + mCameraId = in.readString(); + mSessionConfiguration = SessionConfiguration.CREATOR.createFromParcel(in); + } + + public @NonNull String getCameraId() { + return mCameraId; + } + + public @NonNull SessionConfiguration getSessionConfiguration() { + return mSessionConfiguration; + } +} diff --git a/core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java b/core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java new file mode 100644 index 000000000000..8f4d6365f05e --- /dev/null +++ b/core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 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 android.hardware.camera2.utils; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.HashSet; +import java.util.Set; + +/** + * ConcurrentCameraIdCombination + * + * Includes a list of camera ids that may have sessions configured concurrently. + * @hide + */ +public class ConcurrentCameraIdCombination implements Parcelable { + + private Set<String> mConcurrentCameraIds = new HashSet<>(); + + public static final @NonNull + Parcelable.Creator<ConcurrentCameraIdCombination> CREATOR = + new Parcelable.Creator<ConcurrentCameraIdCombination>() { + @Override + public ConcurrentCameraIdCombination createFromParcel(Parcel in) { + return new ConcurrentCameraIdCombination(in); + } + + @Override + public ConcurrentCameraIdCombination[] newArray(int size) { + return new ConcurrentCameraIdCombination[size]; + } + }; + + private ConcurrentCameraIdCombination(Parcel in) { + readFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mConcurrentCameraIds.size()); + for (String cameraId : mConcurrentCameraIds) { + dest.writeString(cameraId); + } + } + + /** + * helper for CREATOR + */ + public void readFromParcel(Parcel in) { + mConcurrentCameraIds.clear(); + int cameraCombinationSize = in.readInt(); + if (cameraCombinationSize < 0) { + throw new RuntimeException("cameraCombinationSize " + cameraCombinationSize + + " should not be negative"); + } + for (int i = 0; i < cameraCombinationSize; i++) { + String cameraId = in.readString(); + if (cameraId == null) { + throw new RuntimeException("Failed to read camera id from Parcel"); + } + mConcurrentCameraIds.add(cameraId); + } + } + + /** + * Get this concurrent camera id combination. + */ + public Set<String> getConcurrentCameraIdCombination() { + return mConcurrentCameraIds; + } +} |