diff options
author | Eino-Ville Talvala <etalvala@google.com> | 2015-06-30 10:34:48 -0700 |
---|---|---|
committer | Eino-Ville Talvala <etalvala@google.com> | 2015-07-10 00:56:18 +0000 |
commit | 639fffee624302ec5b175503d7bd8a441340a629 (patch) | |
tree | 90de0fdefbe2cbb6b31bbd6365004e540eab208a /core/java/android/hardware | |
parent | 171fe6ac0aa5b0d2dd64ac1cdda25cdcb5f183f3 (diff) | |
download | frameworks_base-639fffee624302ec5b175503d7bd8a441340a629.zip frameworks_base-639fffee624302ec5b175503d7bd8a441340a629.tar.gz frameworks_base-639fffee624302ec5b175503d7bd8a441340a629.tar.bz2 |
Camera2: Reorganize high-speed recording API slightly
- Split off distinct high speed capture session class from base capture session
- Move createHighSpeedRequestList to CameraConstrainedHighSpeedCaptureSession
Bug: 21664295
Change-Id: I67d705fdeee1eaa6e5e3e1416771d5d0df642843
Diffstat (limited to 'core/java/android/hardware')
12 files changed, 647 insertions, 329 deletions
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index 62ebfb3..46cafad 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -482,17 +482,6 @@ public abstract class CameraCaptureSession implements AutoCloseable { public abstract boolean isReprocessable(); /** - * Return if this capture session is constrained high speed session that is created by - * {@link CameraDevice#createConstrainedHighSpeedCaptureSession}. - * - * @return {@code true} if this session is constrained high speed capture session, - * {@code false} otherwise. - * - * @see CameraDevice#createConstrainedHighSpeedCaptureSession - */ - public abstract boolean isConstrainedHighSpeed(); - - /** * Get the input Surface associated with a reprocessable capture session. * * <p>Each reprocessable capture session has an input {@link Surface} where the reprocess diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 547cce4..30aa2d5 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -664,7 +664,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * the max possible number of frames the camera device will group together for this high * speed stream configuration. This max batch size will be used to generate a high speed * recording request list by - * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }. + * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList }. * The max batch size for each configuration will satisfy below conditions:</p> * <ul> * <li>Each max batch size will be a divisor of its corresponding fps_max / 30. For example, diff --git a/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java new file mode 100644 index 0000000..07d2443 --- /dev/null +++ b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java @@ -0,0 +1,131 @@ +/* + * Copyright 2015 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; + +import android.annotation.NonNull; +import android.hardware.camera2.params.StreamConfigurationMap; + +import java.util.List; + +/** + * A constrained high speed capture session for a {@link CameraDevice}, used for capturing high + * speed images from the {@link CameraDevice} for high speed video recording use case. + * <p> + * A CameraHighSpeedCaptureSession is created by providing a set of target output surfaces to + * {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, Once created, the session is + * active until a new session is created by the camera device, or the camera device is closed. + * </p> + * <p> + * An active high speed capture session is a specialized capture session that is only targeted at + * high speed video recording (>=120fps) use case if the camera device supports high speed video + * capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} contains + * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}). It only + * accepts request lists created via {@link #createHighSpeedRequestList}, and the request list can + * only be submitted to this session via {@link CameraCaptureSession#captureBurst captureBurst}, or + * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}. See + * {@link CameraDevice#createConstrainedHighSpeedCaptureSession} for more details of the + * limitations. + * </p> + * <p> + * Creating a session is an expensive operation and can take several hundred milliseconds, since it + * requires configuring the camera device's internal pipelines and allocating memory buffers for + * sending images to the desired targets. Therefore the setup is done asynchronously, and + * {@link CameraDevice#createConstrainedHighSpeedCaptureSession} will send the ready-to-use + * CameraCaptureSession to the provided listener's + * {@link CameraCaptureSession.StateCallback#onConfigured} callback. If configuration cannot be + * completed, then the {@link CameraCaptureSession.StateCallback#onConfigureFailed} is called, and + * the session will not become active. + * </p> + * <!-- + * <p> + * Any capture requests (repeating or non-repeating) submitted before the session is ready will be + * queued up and will begin capture once the session becomes ready. In case the session cannot be + * configured and {@link CameraCaptureSession.StateCallback#onConfigureFailed onConfigureFailed} is + * called, all queued capture requests are discarded. </p> + * --> + * <p> + * If a new session is created by the camera device, then the previous session is closed, and its + * associated {@link CameraCaptureSession.StateCallback#onClosed onClosed} callback will be + * invoked. All of the session methods will throw an IllegalStateException if called once the + * session is closed. + * </p> + * <p> + * A closed session clears any repeating requests (as if {@link #stopRepeating} had been called), + * but will still complete all of its in-progress capture requests as normal, before a newly created + * session takes over and reconfigures the camera device. + * </p> + */ +public abstract class CameraConstrainedHighSpeedCaptureSession extends CameraCaptureSession { + + /** + * <p>Create a unmodifiable list of requests that is suitable for constrained high speed capture + * session streaming.</p> + * + * <p>High speed video streaming creates significant performance pressure on the camera device, + * so to achieve efficient high speed streaming, the camera device may have to aggregate + * multiple frames together. This means requests must be sent in batched groups, with all + * requests sharing the same settings. This method takes the list of output target + * Surfaces (subject to the output Surface requirements specified by the constrained high speed + * session) and a {@link CaptureRequest request}, and generates a request list that has the same + * controls for each request. The input {@link CaptureRequest request} must contain the target + * output Surfaces and target high speed FPS range that is one of the + * {@link StreamConfigurationMap#getHighSpeedVideoFpsRangesFor} for the Surface size.</p> + * + * <p>If both preview and recording Surfaces are specified in the {@code request}, the + * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE target FPS range} in the input + * {@link CaptureRequest request} must be a fixed frame rate FPS range, where the + * {@link android.util.Range#getLower minimal FPS} == + * {@link android.util.Range#getUpper() maximum FPS}. The created request list will contain + * a interleaved request pattern such that the preview output FPS is at least 30fps, the + * recording output FPS is {@link android.util.Range#getUpper() maximum FPS} of the requested + * FPS range. The application can submit this request list directly to an active high speed + * capture session to achieve high speed video recording. When only preview or recording + * Surface is specified, this method will return a list of request that have the same controls + * and output targets for all requests.</p> + * + * <p>Submitting a request list created by this method to a normal capture session will result + * in an {@link IllegalArgumentException} if the high speed + * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} is not supported by + * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}.</p> + * + * @param request The high speed capture request that will be used to generate the high speed + * request list. + * @return A unmodifiable CaptureRequest list that is suitable for constrained high speed + * capture. + * + * @throws IllegalArgumentException if the set of output Surfaces in the request do not meet the + * high speed video capability requirements, or the camera + * device doesn't support high speed video capability, or the + * request doesn't meet the high speed video capability + * requirements, or the request doesn't contain the required + * controls for high speed capture. + * @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 CameraDevice#createConstrainedHighSpeedCaptureSession + * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE + * @see android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes + * @see android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRangesFor + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO + */ + @NonNull + public abstract List<CaptureRequest> createHighSpeedRequestList( + @NonNull CaptureRequest request) throws CameraAccessException; + +} diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 639c8b1..4a71aa0 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -613,8 +613,9 @@ public abstract class CameraDevice implements AutoCloseable { * When multiple Surfaces are configured, their size must be same.</li> * * <li>An active high speed capture session only accepts request lists created via - * {@link #createConstrainedHighSpeedRequestList}, and the request list can only be submitted - * to this session via {@link CameraCaptureSession#captureBurst captureBurst}, or + * {@link CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}, and the + * request list can only be submitted to this session via + * {@link CameraCaptureSession#captureBurst captureBurst}, or * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}.</li> * * <li>The FPS ranges being requested to this session must be selected from @@ -661,71 +662,13 @@ public abstract class CameraDevice implements AutoCloseable { * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO * @see CameraCaptureSession#captureBurst * @see CameraCaptureSession#setRepeatingBurst - * @see #createConstrainedHighSpeedRequestList + * @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList */ public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler) throws CameraAccessException; - - /** - * <p>Create a unmodifiable list of requests that is suitable for constrained high speed capture - * session streaming.</p> - * - * <p>High speed video streaming creates significant performance pressue on the camera device, - * so to achieve efficient high speed streaming, the camera device may have to aggregate - * multiple frames together. This means requests must be sent in batched groups, with all - * requests sharing the same settings. This method takes the list of output target - * Surfaces (subject to the output Surface requirements specified by the contrained high speed - * session) and a {@link CaptureRequest request}, and generates a request list that has the same - * controls for each request. The input {@link CaptureRequest request} must contain the target - * output Surfaces and target high speed FPS range that is one of the - * {@link StreamConfigurationMap#getHighSpeedVideoFpsRangesFor} for the Surface size.</p> - * - * <p>If both preview and recording Surfaces are specified in the {@code request}, the - * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE target FPS range} in the input - * {@link CaptureRequest request} must be a fixed framerate FPS range, where the - * {@link android.util.Range#getLower minimal FPS} == - * {@link android.util.Range#getUpper() maximum FPS}. The created request list will contain - * a interleaved request pattern such that the preview output FPS is at least 30fps, the - * recording output FPS is {@link android.util.Range#getUpper() maximum FPS} of the requested - * FPS range. The application can submit this request list directly to an active high speed - * capture session to achieve high speed video recording. When only preview or recording - * Surface is specified, this method will return a list of request that have the same controls - * and output targets for all requests.</p> - * - * <p>Submitting a request list created by this method to a normal capture session will result - * in an {@link IllegalArgumentException} if the high speed - * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} is not supported by - * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}.</p> - * - * @param request The high speed capture request that will be used to generate the high speed - * request list. - * @return A unmodifiable CaptureRequest list that is suitable for constrained high speed - * capture. - * - * @throws IllegalArgumentException if the set of output Surfaces in the request do not meet the - * high speed video capability requirements, or the camera - * device doesn't support high speed video capability, or the - * request doesn't meet the high speed video capability - * requirements, or the request doesn't contain the required - * controls for high speed capture. - * @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 #createConstrainedHighSpeedCaptureSession - * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE - * @see StreamConfigurationMap#getHighSpeedVideoSizes - * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor - * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES - * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO - */ - @NonNull - public abstract List<CaptureRequest> createConstrainedHighSpeedRequestList( - @NonNull CaptureRequest request)throws CameraAccessException; - /** * <p>Create a {@link CaptureRequest.Builder} for new capture requests, * initialized with template for a target use case. The settings are chosen diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 0aa6447..c8ae5d4 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -655,8 +655,8 @@ public abstract class CameraMetadata<TKey> { * <p>The device supports constrained high speed video recording (frame rate >=120fps) * use case. The camera device will support high speed capture session created by * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }, which - * only accepts high speed request list created by - * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }.</p> + * only accepts high speed request lists created by + * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList }.</p> * <p>A camera device can still support high speed video streaming by advertising the high speed * FPS ranges in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}. For this case, all normal * capture request per frame control and synchronization requirements will apply to @@ -717,9 +717,9 @@ public abstract class CameraMetadata<TKey> { * <li>The FPS ranges are selected from * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.</li> * </ul> - * <p>When above conditions are NOT satistied, the + * <p>When above conditions are NOT satistied, * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession } - * and {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList } will fail.</p> + * will fail.</p> * <p>Switching to a FPS range that has different maximum FPS may trigger some camera device * reconfigurations, which may introduce extra latency. It is recommended that * the application avoids unnecessary maximum target FPS changes as much as possible @@ -1813,9 +1813,8 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_SCENE_MODE_BARCODE = 16; /** - * <p>This is deprecated, please use - * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession } - * and {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList } + * <p>This is deprecated, please use {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession } + * and {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList } * for high speed video recording.</p> * <p>Optimized for high speed video recording (frame rate >=60fps) use case.</p> * <p>The supported high speed video sizes and fps ranges are specified in diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 6d8cc54..a136d0f 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -184,7 +184,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> private final CameraMetadataNative mSettings; private boolean mIsReprocess; // If this request is part of constrained high speed request list that was created by - // {@link CameraDevice#createConstrainedHighSpeedRequestList}. + // {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList} private boolean mIsPartOfCHSRequestList = false; // Each reprocess request must be tied to a reprocessable session ID. // Valid only for reprocess requests (mIsReprocess == true). @@ -340,14 +340,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Determine if this request is part of a constrained high speed request list that was - * created by {@link CameraDevice#createConstrainedHighSpeedRequestList}. A constrained high - * speed request list contains some constrained high speed capture requests with certain - * interleaved pattern that is suitable for high speed preview/video streaming. An active - * constrained high speed capture session only accepts constrained high speed request lists. - * This method can be used to do the sanity check when a constrained high speed capture session - * receives a request list via {@link CameraCaptureSession#setRepeatingBurst} or - * {@link CameraCaptureSession#captureBurst}. - * </p> + * created by + * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}. + * A constrained high speed request list contains some constrained high speed capture requests + * with certain interleaved pattern that is suitable for high speed preview/video streaming. An + * active constrained high speed capture session only accepts constrained high speed request + * lists. This method can be used to do the sanity check when a constrained high speed capture + * session receives a request list via {@link CameraCaptureSession#setRepeatingBurst} or + * {@link CameraCaptureSession#captureBurst}. </p> * * * @return {@code true} if this request is part of a constrained high speed request list, @@ -595,9 +595,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Mark this request as part of a constrained high speed request list created by - * {@link CameraDevice#createConstrainedHighSpeedRequestList}. A constrained high speed - * request list contains some constrained high speed capture requests with certain - * interleaved pattern that is suitable for high speed preview/video streaming.</p> + * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}. + * A constrained high speed request list contains some constrained high speed capture + * requests with certain interleaved pattern that is suitable for high speed preview/video + * streaming.</p> * * @hide */ diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java new file mode 100644 index 0000000..116f0f1 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 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.impl; + +/** + * Internal interface for CameraDeviceImpl to CameraCaptureSessionImpl(s) communication + */ +public interface CameraCaptureSessionCore { + + /** + * Replace this session with another session. + * + * <p>This is an optimization to avoid unconfiguring and then immediately having to + * reconfigure again.</p> + * + * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped. + * </p> + * + * <p>After this call completes, the session will not call any further methods on the camera + * device.</p> + * + * @see CameraCaptureSession#close + */ + void replaceSessionClose(); + + /** + * + * Create an internal state callback, to be invoked on the mDeviceHandler + * + * <p>It has a few behaviors: + * <ul> + * <li>Convert device state changes into session state changes. + * <li>Keep track of async tasks that the session began (idle, abort). + * </ul> + * </p> + * */ + CameraDeviceImpl.StateCallbackKK getDeviceStateCallback(); + + /** + * Whether currently in mid-abort. + * + * <p>This is used by the implementation to set the capture failure + * reason, in lieu of more accurate error codes from the camera service. + * Unsynchronized to avoid deadlocks between simultaneous session->device, + * device->session calls.</p> + * + */ + boolean isAborting(); + +} diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 3d261dd..3c19cd2 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -36,7 +36,8 @@ import java.util.List; import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler; import static com.android.internal.util.Preconditions.*; -public class CameraCaptureSessionImpl extends CameraCaptureSession { +public class CameraCaptureSessionImpl extends CameraCaptureSession + implements CameraCaptureSessionCore { private static final String TAG = "CameraCaptureSession"; private static final boolean DEBUG = false; @@ -60,7 +61,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl; /** Internal handler; used for all incoming events to preserve total order */ private final Handler mDeviceHandler; - private final boolean mIsConstrainedHighSpeedSession; /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */ private final TaskDrainer<Integer> mSequenceDrainer; @@ -89,14 +89,13 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler stateHandler, android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, - Handler deviceStateHandler, boolean configureSuccess, boolean isConstrainedHighSpeed) { + Handler deviceStateHandler, boolean configureSuccess) { if (outputs == null || outputs.isEmpty()) { throw new IllegalArgumentException("outputs must be a non-null, non-empty list"); } else if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } - mIsConstrainedHighSpeedSession = isConstrainedHighSpeed; mId = id; mIdString = String.format("Session %d: ", mId); @@ -136,30 +135,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } } - - private boolean isConstrainedHighSpeedRequestList(List<CaptureRequest> requestList) { - checkCollectionNotEmpty(requestList, "High speed request list"); - for (CaptureRequest request : requestList) { - if (!request.isPartOfCRequestList()) { - return false; - } - } - return true; - } - - /** - * If the session is constrained high speed session, it only accept constrained high speed - * request list. - */ - private void checkConstrainedHighSpeedRequestSanity(List<CaptureRequest> requestList) { - if (mIsConstrainedHighSpeedSession) { - if (!isConstrainedHighSpeedRequestList(requestList)) { - throw new IllegalArgumentException("It is only allowed to submit a constrained " - + "high speed request list to a constrained high speed session!!!"); - } - } - } - @Override public CameraDevice getDevice() { return mDeviceImpl; @@ -181,10 +156,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("capture request was created for another session"); } - if (mIsConstrainedHighSpeedSession) { - throw new UnsupportedOperationException("Constrained high speed session doesn't support" - + " this method"); - } checkNotClosed(); @@ -208,8 +179,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { throw new IllegalArgumentException("Requests must have at least one element"); } - checkConstrainedHighSpeedRequestSanity(requests); - for (CaptureRequest request : requests) { if (request.isReprocess()) { if (!isReprocessable()) { @@ -244,10 +213,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } else if (request.isReprocess()) { throw new IllegalArgumentException("repeating reprocess requests are not supported"); } - if (mIsConstrainedHighSpeedSession) { - throw new UnsupportedOperationException("Constrained high speed session doesn't support" - + " this method"); - } checkNotClosed(); @@ -271,8 +236,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { throw new IllegalArgumentException("requests must have at least one element"); } - checkConstrainedHighSpeedRequestSanity(requests); - for (CaptureRequest r : requests) { if (r.isReprocess()) { throw new IllegalArgumentException("repeating reprocess burst requests are not " + @@ -349,7 +312,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * * @see CameraCaptureSession#close */ - synchronized void replaceSessionClose() { + @Override + public synchronized void replaceSessionClose() { /* * In order for creating new sessions to be fast, the new session should be created * before the old session is closed. @@ -431,9 +395,9 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * Unsynchronized to avoid deadlocks between simultaneous session->device, * device->session calls.</p> * - * <p>Package-private.</p> */ - boolean isAborting() { + @Override + public boolean isAborting() { return mAborting; } @@ -521,7 +485,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * </ul> * </p> * */ - CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() { + @Override + public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() { final CameraCaptureSession session = this; return new CameraDeviceImpl.StateCallbackKK() { @@ -759,9 +724,4 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } } - @Override - public boolean isConstrainedHighSpeed() { - return mIsConstrainedHighSpeedSession; - } - } diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java new file mode 100644 index 0000000..d732535 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2015 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.impl; + +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.hardware.camera2.utils.SurfaceUtils; +import android.os.Handler; +import android.util.Range; +import android.view.Surface; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import static com.android.internal.util.Preconditions.*; + +/** + * Standard implementation of CameraConstrainedHighSpeedCaptureSession. + * + * <p> + * Mostly just forwards calls to an instance of CameraCaptureSessionImpl, + * but implements the few necessary behavior changes and additional methods required + * for the constrained high speed speed mode. + * </p> + */ + +public class CameraConstrainedHighSpeedCaptureSessionImpl + extends CameraConstrainedHighSpeedCaptureSession implements CameraCaptureSessionCore { + private final CameraCharacteristics mCharacteristics; + private final CameraCaptureSessionImpl mSessionImpl; + + /** + * Create a new CameraCaptureSession. + * + * <p>The camera device must already be in the {@code IDLE} state when this is invoked. + * There must be no pending actions + * (e.g. no pending captures, no repeating requests, no flush).</p> + */ + CameraConstrainedHighSpeedCaptureSessionImpl(int id, List<Surface> outputs, + CameraCaptureSession.StateCallback callback, Handler stateHandler, + android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, + Handler deviceStateHandler, boolean configureSuccess, + CameraCharacteristics characteristics) { + mCharacteristics = characteristics; + CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback); + mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, outputs, wrapperCallback, + stateHandler, deviceImpl, deviceStateHandler, configureSuccess); + } + + @Override + public List<CaptureRequest> createHighSpeedRequestList(CaptureRequest request) + throws CameraAccessException { + if (request == null) { + throw new IllegalArgumentException("Input capture request must not be null"); + } + Collection<Surface> outputSurfaces = request.getTargets(); + Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); + + StreamConfigurationMap config = + mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange, config); + + // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize + // the preview frame rate, should use maxBatch size for that high speed stream + // configuration. We choose the former for now. + int requestListSize = fpsRange.getUpper() / 30; + List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); + + // Prepare the Request builders: need carry over the request controls. + // First, create a request builder that will only include preview or recording target. + CameraMetadataNative requestMetadata = new CameraMetadataNative(request.getNativeCopy()); + // 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); + + // Overwrite the capture intent to make sure a good value is set. + Iterator<Surface> iterator = outputSurfaces.iterator(); + Surface firstSurface = iterator.next(); + Surface secondSurface = null; + if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(firstSurface)) { + singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW); + } else { + // Video only, or preview + video + singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); + } + singleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); + + // Second, Create a request builder that will include both preview and recording targets. + CaptureRequest.Builder doubleTargetRequestBuilder = null; + if (outputSurfaces.size() == 2) { + // Have to create a new copy, the original one was mutated after a new + // CaptureRequest.Builder creation. + requestMetadata = new CameraMetadataNative(request.getNativeCopy()); + doubleTargetRequestBuilder = new CaptureRequest.Builder( + requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); + doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); + doubleTargetRequestBuilder.addTarget(firstSurface); + secondSurface = iterator.next(); + doubleTargetRequestBuilder.addTarget(secondSurface); + doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); + // Make sure singleTargetRequestBuilder contains only recording surface for + // preview + recording case. + Surface recordingSurface = firstSurface; + if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) { + recordingSurface = secondSurface; + } + singleTargetRequestBuilder.addTarget(recordingSurface); + } else { + // Single output case: either recording or preview. + singleTargetRequestBuilder.addTarget(firstSurface); + } + + // Generate the final request list. + for (int i = 0; i < requestListSize; i++) { + if (i == 0 && doubleTargetRequestBuilder != null) { + // First request should be recording + preview request + requestList.add(doubleTargetRequestBuilder.build()); + } else { + requestList.add(singleTargetRequestBuilder.build()); + } + } + + return Collections.unmodifiableList(requestList); + } + + private boolean isConstrainedHighSpeedRequestList(List<CaptureRequest> requestList) { + checkCollectionNotEmpty(requestList, "High speed request list"); + for (CaptureRequest request : requestList) { + if (!request.isPartOfCRequestList()) { + return false; + } + } + return true; + } + + @Override + public CameraDevice getDevice() { + return mSessionImpl.getDevice(); + } + + @Override + public void prepare(Surface surface) throws CameraAccessException { + mSessionImpl.prepare(surface); + } + + @Override + public int capture(CaptureRequest request, CaptureCallback listener, Handler handler) + throws CameraAccessException { + throw new UnsupportedOperationException("Constrained high speed session doesn't support" + + " this method"); + } + + @Override + public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener, + Handler handler) throws CameraAccessException { + if (!isConstrainedHighSpeedRequestList(requests)) { + throw new IllegalArgumentException( + "Only request lists created by createHighSpeedRequestList() can be submitted to " + + "a constrained high speed capture session"); + } + return mSessionImpl.captureBurst(requests, listener, handler); + } + + @Override + public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener, + Handler handler) throws CameraAccessException { + throw new UnsupportedOperationException("Constrained high speed session doesn't support" + + " this method"); + } + + @Override + public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener, + Handler handler) throws CameraAccessException { + if (!isConstrainedHighSpeedRequestList(requests)) { + throw new IllegalArgumentException( + "Only request lists created by createHighSpeedRequestList() can be submitted to " + + "a constrained high speed capture session"); + } + return mSessionImpl.setRepeatingBurst(requests, listener, handler); + } + + @Override + public void stopRepeating() throws CameraAccessException { + mSessionImpl.stopRepeating(); + } + + @Override + public void abortCaptures() throws CameraAccessException { + mSessionImpl.abortCaptures(); + } + + @Override + public Surface getInputSurface() { + return null; + } + + @Override + public void close() { + mSessionImpl.close(); + } + + @Override + public boolean isReprocessable() { + return false; + } + + // Implementation of CameraCaptureSessionCore methods + + @Override + public void replaceSessionClose() { + mSessionImpl.replaceSessionClose(); + } + + @Override + public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() { + return mSessionImpl.getDeviceStateCallback(); + } + + @Override + public boolean isAborting() { + return mSessionImpl.isAborting(); + } + + private class WrapperCallback extends StateCallback { + private final StateCallback mCallback; + + public WrapperCallback(StateCallback callback) { + mCallback = callback; + } + + public void onConfigured(CameraCaptureSession session) { + mCallback.onConfigured(CameraConstrainedHighSpeedCaptureSessionImpl.this); + } + + public void onConfigureFailed(CameraCaptureSession session) { + mCallback.onConfigureFailed(CameraConstrainedHighSpeedCaptureSessionImpl.this); + } + + public void onReady(CameraCaptureSession session) { + mCallback.onReady(CameraConstrainedHighSpeedCaptureSessionImpl.this); + } + + public void onActive(CameraCaptureSession session) { + mCallback.onActive(CameraConstrainedHighSpeedCaptureSessionImpl.this); + } + + public void onClosed(CameraCaptureSession session) { + mCallback.onClosed(CameraConstrainedHighSpeedCaptureSessionImpl.this); + } + + public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { + mCallback.onSurfacePrepared(CameraConstrainedHighSpeedCaptureSessionImpl.this, + surface); + } + + + } +} diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 83128c3..c594228 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -113,7 +113,7 @@ public class CameraDeviceImpl extends CameraDevice { */ private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker(); - private CameraCaptureSessionImpl mCurrentSession; + private CameraCaptureSessionCore mCurrentSession; private int mNextSessionId = 0; // Runnables for all state transitions, except error, which needs the @@ -510,6 +510,26 @@ public class CameraDeviceImpl extends CameraDevice { /*isConstrainedHighSpeed*/false); } + @Override + public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs, + android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler) + throws CameraAccessException { + if (outputs == null || outputs.size() == 0 || outputs.size() > 2) { + throw new IllegalArgumentException( + "Output surface list must not be null and the size must be no more than 2"); + } + StreamConfigurationMap config = + getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputs, /*fpsRange*/null, config); + + List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); + for (Surface surface : outputs) { + outConfigurations.add(new OutputConfiguration(surface)); + } + createCaptureSessionInternal(null, outConfigurations, callback, handler, + /*isConstrainedHighSpeed*/true); + } + private void createCaptureSessionInternal(InputConfiguration inputConfig, List<OutputConfiguration> outputConfigurations, CameraCaptureSession.StateCallback callback, Handler handler, @@ -565,10 +585,16 @@ public class CameraDeviceImpl extends CameraDevice { outSurfaces.add(config.getSurface()); } // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise. - CameraCaptureSessionImpl newSession = - new CameraCaptureSessionImpl(mNextSessionId++, input, - outSurfaces, callback, handler, this, mDeviceHandler, - configureSuccess, isConstrainedHighSpeed); + CameraCaptureSessionCore newSession = null; + if (isConstrainedHighSpeed) { + newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++, + outSurfaces, callback, handler, this, mDeviceHandler, configureSuccess, + mCharacteristics); + } else { + newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, + outSurfaces, callback, handler, this, mDeviceHandler, + configureSuccess); + } // TODO: wait until current session closes, then create the new session mCurrentSession = newSession; @@ -1933,181 +1959,4 @@ public class CameraDeviceImpl extends CameraDevice { return mCharacteristics; } - /** - * A high speed output surface can only be preview or hardware encoder surface. - * - * @param surface The high speed output surface to be checked. - */ - private void checkHighSpeedSurfaceFormat(Surface surface) { - // TODO: remove this override since the default format should be - // ImageFormat.PRIVATE. b/9487482 - final int HAL_FORMAT_RGB_START = 1; // HAL_PIXEL_FORMAT_RGBA_8888 from graphics.h - final int HAL_FORMAT_RGB_END = 5; // HAL_PIXEL_FORMAT_BGRA_8888 from graphics.h - int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface); - if (surfaceFormat >= HAL_FORMAT_RGB_START && - surfaceFormat <= HAL_FORMAT_RGB_END) { - surfaceFormat = ImageFormat.PRIVATE; - } - - if (surfaceFormat != ImageFormat.PRIVATE) { - throw new IllegalArgumentException("Surface format(" + surfaceFormat + ") is not" - + " for preview or hardware video encoding!"); - } - } - - private void checkConstrainedHighSpeedSurfaces(Collection<Surface> surfaces, - Range<Integer> fpsRange) { - if (surfaces == null || surfaces.size() == 0 || surfaces.size() > 2) { - throw new IllegalArgumentException("Output target surface list must not be null and" - + " the size must be 1 or 2"); - } - - StreamConfigurationMap config = - getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); - List<Size> highSpeedSizes = null; - if (fpsRange == null) { - highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes()); - } else { - // Check the FPS range first if provided - Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRanges(); - if(!Arrays.asList(highSpeedFpsRanges).contains(fpsRange)) { - throw new IllegalArgumentException("Fps range " + fpsRange.toString() + " in the" - + " request is not a supported high speed fps range " + - Arrays.toString(highSpeedFpsRanges)); - } - highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizesFor(fpsRange)); - } - - for (Surface surface : surfaces) { - checkHighSpeedSurfaceFormat(surface); - - // Surface size must be supported high speed sizes. - Size surfaceSize = SurfaceUtils.getSurfaceSize(surface); - if (!highSpeedSizes.contains(surfaceSize)) { - throw new IllegalArgumentException("Surface size " + surfaceSize.toString() + " is" - + " not part of the high speed supported size list " + - Arrays.toString(highSpeedSizes.toArray())); - } - // Each output surface must be either preview surface or recording surface. - if (!SurfaceUtils.isSurfaceForPreview(surface) && - !SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) { - throw new IllegalArgumentException("This output surface is neither preview nor " - + "hardware video encoding surface"); - } - if (SurfaceUtils.isSurfaceForPreview(surface) && - SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) { - throw new IllegalArgumentException("This output surface can not be both preview" - + " and hardware video encoding surface"); - } - } - - // For 2 output surface case, they shouldn't be same type. - if (surfaces.size() == 2) { - // Up to here, each surface can only be either preview or recording. - Iterator<Surface> iterator = surfaces.iterator(); - boolean isFirstSurfacePreview = - SurfaceUtils.isSurfaceForPreview(iterator.next()); - boolean isSecondSurfacePreview = - SurfaceUtils.isSurfaceForPreview(iterator.next()); - if (isFirstSurfacePreview == isSecondSurfacePreview) { - throw new IllegalArgumentException("The 2 output surfaces must have different" - + " type"); - } - } - } - - @Override - public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs, - android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler) - throws CameraAccessException { - if (outputs == null || outputs.size() == 0 || outputs.size() > 2) { - throw new IllegalArgumentException( - "Output surface list must not be null and the size must be no more than 2"); - } - checkConstrainedHighSpeedSurfaces(outputs, /*fpsRange*/null); - - List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); - for (Surface surface : outputs) { - outConfigurations.add(new OutputConfiguration(surface)); - } - createCaptureSessionInternal(null, outConfigurations, callback, handler, - /*isConstrainedHighSpeed*/true); - } - - @Override - public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request) - throws CameraAccessException { - if (request == null) { - throw new IllegalArgumentException("Input capture request must not be null"); - } - Collection<Surface> outputSurfaces = request.getTargets(); - Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); - checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange); - - // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize - // the preview frame rate, should use maxBatch size for that high speed stream - // configuration. We choose the former for now. - int requestListSize = fpsRange.getUpper() / 30; - List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); - - // Prepare the Request builders: need carry over the request controls. - // First, create a request builder that will only include preview or recording target. - CameraMetadataNative requestMetadata = new CameraMetadataNative(request.getNativeCopy()); - // 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); - - // Overwrite the capture intent to make sure a good value is set. - Iterator<Surface> iterator = outputSurfaces.iterator(); - Surface firstSurface = iterator.next(); - Surface secondSurface = null; - if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(firstSurface)) { - singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, - CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW); - } else { - // Video only, or preview + video - singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, - CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); - } - singleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); - - // Second, Create a request builder that will include both preview and recording targets. - CaptureRequest.Builder doubleTargetRequestBuilder = null; - if (outputSurfaces.size() == 2) { - // Have to create a new copy, the original one was mutated after a new - // CaptureRequest.Builder creation. - requestMetadata = new CameraMetadataNative(request.getNativeCopy()); - doubleTargetRequestBuilder = new CaptureRequest.Builder( - requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); - doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, - CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); - doubleTargetRequestBuilder.addTarget(firstSurface); - secondSurface = iterator.next(); - doubleTargetRequestBuilder.addTarget(secondSurface); - doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); - // Make sure singleTargetRequestBuilder contains only recording surface for - // preview + recording case. - Surface recordingSurface = firstSurface; - if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) { - recordingSurface = secondSurface; - } - singleTargetRequestBuilder.addTarget(recordingSurface); - } else { - // Single output case: either recording or preview. - singleTargetRequestBuilder.addTarget(firstSurface); - } - - // Generate the final request list. - for (int i = 0; i < requestListSize; i++) { - if (i == 0 && doubleTargetRequestBuilder != null) { - // First request should be recording + preview request - requestList.add(doubleTargetRequestBuilder.build()); - } else { - requestList.add(singleTargetRequestBuilder.build()); - } - } - - return Collections.unmodifiableList(requestList); - } } diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index 639ad60..8e0eab2 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -495,7 +495,8 @@ public final class StreamConfigurationMap { * <p> * To enable high speed video recording, application must create a constrained create high speed * capture session via {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, and submit - * a CaptureRequest list created by {@link CameraDevice#createConstrainedHighSpeedRequestList} + * a CaptureRequest list created by + * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList} * to this session. The application must select the video size from this method and * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} from * {@link #getHighSpeedVideoFpsRangesFor} to configure the constrained high speed session and @@ -506,14 +507,15 @@ public final class StreamConfigurationMap { * the same size). Otherwise, the high speed session creation will fail. Once the size is * selected, application can get the supported FPS ranges by * {@link #getHighSpeedVideoFpsRangesFor}, and use these FPS ranges to setup the recording - * request lists via {@link CameraDevice#createConstrainedHighSpeedRequestList}. + * request lists via + * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}. * </p> * * @return an array of supported high speed video recording sizes * @see #getHighSpeedVideoFpsRangesFor(Size) * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO * @see CameraDevice#createConstrainedHighSpeedCaptureSession - * @see CameraDevice#createConstrainedHighSpeedRequestList + * @see android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList */ public Size[] getHighSpeedVideoSizes() { Set<Size> keySet = mHighSpeedVideoSizeMap.keySet(); @@ -571,7 +573,8 @@ public final class StreamConfigurationMap { * <p> * To enable high speed video recording, application must create a constrained create high speed * capture session via {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, and submit - * a CaptureRequest list created by {@link CameraDevice#createConstrainedHighSpeedRequestList} + * a CaptureRequest list created by + * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList} * to this session. The application must select the video size from this method and * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} from * {@link #getHighSpeedVideoFpsRangesFor} to configure the constrained high speed session and @@ -583,7 +586,7 @@ public final class StreamConfigurationMap { * recording streams must have the same size). Otherwise, the high speed session creation will * fail. Once the high speed capture session is created, the application can set the FPS range * in the recording request lists via - * {@link CameraDevice#createConstrainedHighSpeedRequestList}. + * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}. * </p> * <p> * The FPS ranges reported by this method will have below characteristics: @@ -601,7 +604,7 @@ public final class StreamConfigurationMap { * @see #getHighSpeedVideoSizesFor * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO * @see CameraDevice#createConstrainedHighSpeedCaptureSession - * @see CameraDevice#createConstrainedHighSpeedRequestList + * @see CameraDevice#createHighSpeedRequestList */ @SuppressWarnings("unchecked") public Range<Integer>[] getHighSpeedVideoFpsRanges() { diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java index 064b21a..4b958df 100644 --- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java +++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java @@ -19,9 +19,16 @@ package android.hardware.camera2.utils; import android.graphics.ImageFormat; import android.hardware.camera2.legacy.LegacyCameraDevice; import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.util.Range; import android.util.Size; import android.view.Surface; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + /** * Various Surface utilities. */ @@ -105,4 +112,93 @@ public class SurfaceUtils { return LegacyCameraDevice.isFlexibleConsumer(output); } + /** + * A high speed output surface can only be preview or hardware encoder surface. + * + * @param surface The high speed output surface to be checked. + */ + private static void checkHighSpeedSurfaceFormat(Surface surface) { + // TODO: remove this override since the default format should be + // ImageFormat.PRIVATE. b/9487482 + final int HAL_FORMAT_RGB_START = 1; // HAL_PIXEL_FORMAT_RGBA_8888 from graphics.h + final int HAL_FORMAT_RGB_END = 5; // HAL_PIXEL_FORMAT_BGRA_8888 from graphics.h + int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface); + if (surfaceFormat >= HAL_FORMAT_RGB_START && + surfaceFormat <= HAL_FORMAT_RGB_END) { + surfaceFormat = ImageFormat.PRIVATE; + } + + if (surfaceFormat != ImageFormat.PRIVATE) { + throw new IllegalArgumentException("Surface format(" + surfaceFormat + ") is not" + + " for preview or hardware video encoding!"); + } + } + + /** + * Verify that that the surfaces are valid for high-speed recording mode, + * and that the FPS range is supported + * + * @param surfaces the surfaces to verify as valid in terms of size and format + * @param fpsRange the target high-speed FPS range to validate + * @param config The stream configuration map for the device in question + */ + public static void checkConstrainedHighSpeedSurfaces(Collection<Surface> surfaces, + Range<Integer> fpsRange, StreamConfigurationMap config) { + if (surfaces == null || surfaces.size() == 0 || surfaces.size() > 2) { + throw new IllegalArgumentException("Output target surface list must not be null and" + + " the size must be 1 or 2"); + } + + List<Size> highSpeedSizes = null; + if (fpsRange == null) { + highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes()); + } else { + // Check the FPS range first if provided + Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRanges(); + if(!Arrays.asList(highSpeedFpsRanges).contains(fpsRange)) { + throw new IllegalArgumentException("Fps range " + fpsRange.toString() + " in the" + + " request is not a supported high speed fps range " + + Arrays.toString(highSpeedFpsRanges)); + } + highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizesFor(fpsRange)); + } + + for (Surface surface : surfaces) { + checkHighSpeedSurfaceFormat(surface); + + // Surface size must be supported high speed sizes. + Size surfaceSize = SurfaceUtils.getSurfaceSize(surface); + if (!highSpeedSizes.contains(surfaceSize)) { + throw new IllegalArgumentException("Surface size " + surfaceSize.toString() + " is" + + " not part of the high speed supported size list " + + Arrays.toString(highSpeedSizes.toArray())); + } + // Each output surface must be either preview surface or recording surface. + if (!SurfaceUtils.isSurfaceForPreview(surface) && + !SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) { + throw new IllegalArgumentException("This output surface is neither preview nor " + + "hardware video encoding surface"); + } + if (SurfaceUtils.isSurfaceForPreview(surface) && + SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) { + throw new IllegalArgumentException("This output surface can not be both preview" + + " and hardware video encoding surface"); + } + } + + // For 2 output surface case, they shouldn't be same type. + if (surfaces.size() == 2) { + // Up to here, each surface can only be either preview or recording. + Iterator<Surface> iterator = surfaces.iterator(); + boolean isFirstSurfacePreview = + SurfaceUtils.isSurfaceForPreview(iterator.next()); + boolean isSecondSurfacePreview = + SurfaceUtils.isSurfaceForPreview(iterator.next()); + if (isFirstSurfacePreview == isSecondSurfacePreview) { + throw new IllegalArgumentException("The 2 output surfaces must have different" + + " type"); + } + } + } + } |