am a2cd9c56: Merge "Camera2: Reorganize high-speed recording API slightly" into mnc-dev
* commit 'a2cd9c56f8648b5a4faee14d9dd0c4151730f78c': Camera2: Reorganize high-speed recording API slightly
This commit is contained in:
@@ -13178,7 +13178,6 @@ package android.hardware.camera2 {
|
||||
method public abstract void close();
|
||||
method public abstract android.hardware.camera2.CameraDevice getDevice();
|
||||
method public abstract android.view.Surface getInputSurface();
|
||||
method public abstract boolean isConstrainedHighSpeed();
|
||||
method public abstract boolean isReprocessable();
|
||||
method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
@@ -13296,12 +13295,16 @@ package android.hardware.camera2 {
|
||||
method public final int hashCode();
|
||||
}
|
||||
|
||||
public abstract class CameraConstrainedHighSpeedCaptureSession extends android.hardware.camera2.CameraCaptureSession {
|
||||
ctor public CameraConstrainedHighSpeedCaptureSession();
|
||||
method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
|
||||
}
|
||||
|
||||
public abstract class CameraDevice implements java.lang.AutoCloseable {
|
||||
method public abstract void close();
|
||||
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createConstrainedHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract java.lang.String getId();
|
||||
|
||||
@@ -13518,7 +13518,6 @@ package android.hardware.camera2 {
|
||||
method public abstract void close();
|
||||
method public abstract android.hardware.camera2.CameraDevice getDevice();
|
||||
method public abstract android.view.Surface getInputSurface();
|
||||
method public abstract boolean isConstrainedHighSpeed();
|
||||
method public abstract boolean isReprocessable();
|
||||
method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
@@ -13636,12 +13635,16 @@ package android.hardware.camera2 {
|
||||
method public final int hashCode();
|
||||
}
|
||||
|
||||
public abstract class CameraConstrainedHighSpeedCaptureSession extends android.hardware.camera2.CameraCaptureSession {
|
||||
ctor public CameraConstrainedHighSpeedCaptureSession();
|
||||
method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
|
||||
}
|
||||
|
||||
public abstract class CameraDevice implements java.lang.AutoCloseable {
|
||||
method public abstract void close();
|
||||
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createConstrainedHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
method public abstract java.lang.String getId();
|
||||
|
||||
@@ -481,17 +481,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.
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user