Loading core/java/android/hardware/camera2/CaptureRequest.java +45 −1 Original line number Diff line number Diff line Loading @@ -169,6 +169,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> private final HashSet<Surface> mSurfaceSet; 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}. private boolean mIsPartOfCHSRequestList = false; // Each reprocess request must be tied to a reprocessable session ID. // Valid only for reprocess requests (mIsReprocess == true). private int mReprocessableSessionId; Loading Loading @@ -197,6 +200,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mSettings = new CameraMetadataNative(source.mSettings); mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone(); mIsReprocess = source.mIsReprocess; mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList; mReprocessableSessionId = source.mReprocessableSessionId; mUserTag = source.mUserTag; } Loading Loading @@ -320,6 +324,35 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> return mIsReprocess; } /** * <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> * * * @return {@code true} if this request is part of a constrained high speed request list, * {@code false} otherwise. * * @hide */ public boolean isPartOfCRequestList() { return mIsPartOfCHSRequestList; } /** * Returns a copy of the underlying {@link CameraMetadataNative}. * @hide */ public CameraMetadataNative getNativeCopy() { return new CameraMetadataNative(mSettings); } /** * Get the reprocessable session ID this reprocess capture request is associated with. * Loading Loading @@ -546,6 +579,18 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mRequest.mUserTag = tag; } /** * <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> * * @hide */ public void setPartOfCHSRequestList(boolean partOfCHSList) { mRequest.mIsPartOfCHSRequestList = partOfCHSList; } /** * Build a request using the current target Surfaces and settings. * <p>Note that, although it is possible to create a {@code CaptureRequest} with no target Loading @@ -563,7 +608,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> return new CaptureRequest(mRequest); } /** * @hide */ Loading core/java/android/hardware/camera2/ICameraDeviceUser.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ interface ICameraDeviceUser * must be called before any requests can be submitted. * <p> */ int endConfigure(); int endConfigure(boolean isConstrainedHighSpeed); int deleteStream(int streamId); Loading core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +41 −3 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ 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; Loading Loading @@ -88,13 +89,14 @@ 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) { Handler deviceStateHandler, boolean configureSuccess, boolean isConstrainedHighSpeed) { 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); Loading Loading @@ -134,6 +136,30 @@ 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 constrianed high speed session!!!"); } } } @Override public CameraDevice getDevice() { return mDeviceImpl; Loading @@ -155,6 +181,10 @@ 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(); Loading @@ -178,6 +208,8 @@ 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()) { Loading Loading @@ -212,7 +244,10 @@ 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(); Loading @@ -236,6 +271,8 @@ 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 " + Loading Loading @@ -704,7 +741,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { // everything is idle. try { // begin transition to unconfigured mDeviceImpl.configureStreamsChecked(null, null); mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null, /*isConstrainedHighSpeed*/false); } catch (CameraAccessException e) { // OK: do not throw checked exceptions. Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e); Loading core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +170 −13 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.hardware.camera2.impl; import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; import android.graphics.ImageFormat; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; Loading @@ -35,17 +36,22 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.CameraBinderDecorator; import android.hardware.camera2.utils.CameraRuntimeException; import android.hardware.camera2.utils.LongParcelable; import android.hardware.camera2.utils.SurfaceUtils; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.util.Range; import android.util.Size; import android.util.SparseArray; import android.view.Surface; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; Loading Loading @@ -326,7 +332,8 @@ public class CameraDeviceImpl extends CameraDevice { for (Surface s : outputs) { outputConfigs.add(new OutputConfiguration(s)); } configureStreamsChecked(/*inputConfig*/null, outputConfigs); configureStreamsChecked(/*inputConfig*/null, outputConfigs, /*isConstrainedHighSpeed*/false); } Loading @@ -344,12 +351,14 @@ public class CameraDeviceImpl extends CameraDevice { * * @param inputConfig input configuration or {@code null} for no input * @param outputs a list of one or more surfaces, or {@code null} to unconfigure * @param isConstrainedHighSpeed If the streams configuration is for constrained high speed output. * @return whether or not the configuration was successful * * @throws CameraAccessException if there were any unexpected problems during configuration */ public boolean configureStreamsChecked(InputConfiguration inputConfig, List<OutputConfiguration> outputs) throws CameraAccessException { List<OutputConfiguration> outputs, boolean isConstrainedHighSpeed) throws CameraAccessException { // Treat a null input the same an empty list if (outputs == null) { outputs = new ArrayList<OutputConfiguration>(); Loading Loading @@ -422,7 +431,7 @@ public class CameraDeviceImpl extends CameraDevice { } try { mRemoteDevice.endConfigure(); mRemoteDevice.endConfigure(isConstrainedHighSpeed); } catch (IllegalArgumentException e) { // OK. camera service can reject stream config if it's not supported by HAL Loading Loading @@ -463,7 +472,8 @@ public class CameraDeviceImpl extends CameraDevice { for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } createCaptureSessionInternal(null, outConfigurations, callback, handler); createCaptureSessionInternal(null, outConfigurations, callback, handler, /*isConstrainedHighSpeed*/false); } @Override Loading @@ -475,7 +485,8 @@ public class CameraDeviceImpl extends CameraDevice { Log.d(TAG, "createCaptureSessionByOutputConfiguration"); } createCaptureSessionInternal(null, outputConfigurations, callback, handler); createCaptureSessionInternal(null, outputConfigurations, callback, handler, /*isConstrainedHighSpeed*/false); } @Override Loading @@ -494,13 +505,14 @@ public class CameraDeviceImpl extends CameraDevice { for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler); createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler, /*isConstrainedHighSpeed*/false); } private void createCaptureSessionInternal(InputConfiguration inputConfig, List<OutputConfiguration> outputConfigurations, CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException { CameraCaptureSession.StateCallback callback, Handler handler, boolean isConstrainedHighSpeed) throws CameraAccessException { synchronized(mInterfaceLock) { if (DEBUG) { Log.d(TAG, "createCaptureSessionInternal"); Loading @@ -508,6 +520,11 @@ public class CameraDeviceImpl extends CameraDevice { checkIfCameraClosedOrInError(); if (isConstrainedHighSpeed && inputConfig != null) { throw new IllegalArgumentException("Constrained high speed session doesn't support" + " input configuration yet."); } // Notify current session that it's going away, before starting camera operations // After this call completes, the session is not allowed to call into CameraDeviceImpl if (mCurrentSession != null) { Loading @@ -520,7 +537,8 @@ public class CameraDeviceImpl extends CameraDevice { Surface input = null; try { // configure streams and then block until IDLE configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations); configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, isConstrainedHighSpeed); if (inputConfig != null) { input = new Surface(); mRemoteDevice.getInputSurface(/*out*/input); Loading @@ -545,7 +563,7 @@ public class CameraDeviceImpl extends CameraDevice { CameraCaptureSessionImpl newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, outSurfaces, callback, handler, this, mDeviceHandler, configureSuccess); configureSuccess, isConstrainedHighSpeed); // TODO: wait until current session closes, then create the new session mCurrentSession = newSession; Loading Loading @@ -1906,17 +1924,156 @@ public class CameraDeviceImpl extends CameraDevice { return mCharacteristics; } 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) { // Surface size must be supported high speed sizes. Size surfaceSize = SurfaceUtils.getSurfaceSize(surface); int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface); if (surfaceFormat != ImageFormat.PRIVATE) { throw new IllegalArgumentException("Surface format is not for preview or" + " hardware video encoding" + surfaceFormat); } 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 { // TODO: to be implemented throw new UnsupportedOperationException("To be implemented!!!!"); 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 { throw new UnsupportedOperationException("To be implemented!!!!"); 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()); 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. Surface[] surfaces = (Surface[])outputSurfaces.toArray(); if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(surfaces[0])) { 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) { 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(surfaces[0]); doubleTargetRequestBuilder.addTarget(surfaces[1]); doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); // Make sure singleTargetRequestBuilder contains only recording surface for // preview + recording case. Surface recordingSurface = surfaces[0]; if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) { recordingSurface = surfaces[1]; } singleTargetRequestBuilder.addTarget(recordingSurface); } else { // Single output case: either recording or preview. singleTargetRequestBuilder.addTarget(surfaces[0]); } // 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); } } core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +1 −1 Original line number Diff line number Diff line Loading @@ -465,7 +465,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override public int endConfigure() { public int endConfigure(boolean isConstrainedHighSpeed) { if (DEBUG) { Log.d(TAG, "endConfigure called."); } Loading Loading
core/java/android/hardware/camera2/CaptureRequest.java +45 −1 Original line number Diff line number Diff line Loading @@ -169,6 +169,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> private final HashSet<Surface> mSurfaceSet; 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}. private boolean mIsPartOfCHSRequestList = false; // Each reprocess request must be tied to a reprocessable session ID. // Valid only for reprocess requests (mIsReprocess == true). private int mReprocessableSessionId; Loading Loading @@ -197,6 +200,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mSettings = new CameraMetadataNative(source.mSettings); mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone(); mIsReprocess = source.mIsReprocess; mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList; mReprocessableSessionId = source.mReprocessableSessionId; mUserTag = source.mUserTag; } Loading Loading @@ -320,6 +324,35 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> return mIsReprocess; } /** * <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> * * * @return {@code true} if this request is part of a constrained high speed request list, * {@code false} otherwise. * * @hide */ public boolean isPartOfCRequestList() { return mIsPartOfCHSRequestList; } /** * Returns a copy of the underlying {@link CameraMetadataNative}. * @hide */ public CameraMetadataNative getNativeCopy() { return new CameraMetadataNative(mSettings); } /** * Get the reprocessable session ID this reprocess capture request is associated with. * Loading Loading @@ -546,6 +579,18 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mRequest.mUserTag = tag; } /** * <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> * * @hide */ public void setPartOfCHSRequestList(boolean partOfCHSList) { mRequest.mIsPartOfCHSRequestList = partOfCHSList; } /** * Build a request using the current target Surfaces and settings. * <p>Note that, although it is possible to create a {@code CaptureRequest} with no target Loading @@ -563,7 +608,6 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> return new CaptureRequest(mRequest); } /** * @hide */ Loading
core/java/android/hardware/camera2/ICameraDeviceUser.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ interface ICameraDeviceUser * must be called before any requests can be submitted. * <p> */ int endConfigure(); int endConfigure(boolean isConstrainedHighSpeed); int deleteStream(int streamId); Loading
core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +41 −3 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ 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; Loading Loading @@ -88,13 +89,14 @@ 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) { Handler deviceStateHandler, boolean configureSuccess, boolean isConstrainedHighSpeed) { 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); Loading Loading @@ -134,6 +136,30 @@ 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 constrianed high speed session!!!"); } } } @Override public CameraDevice getDevice() { return mDeviceImpl; Loading @@ -155,6 +181,10 @@ 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(); Loading @@ -178,6 +208,8 @@ 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()) { Loading Loading @@ -212,7 +244,10 @@ 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(); Loading @@ -236,6 +271,8 @@ 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 " + Loading Loading @@ -704,7 +741,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { // everything is idle. try { // begin transition to unconfigured mDeviceImpl.configureStreamsChecked(null, null); mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null, /*isConstrainedHighSpeed*/false); } catch (CameraAccessException e) { // OK: do not throw checked exceptions. Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e); Loading
core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +170 −13 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.hardware.camera2.impl; import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; import android.graphics.ImageFormat; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; Loading @@ -35,17 +36,22 @@ import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.CameraBinderDecorator; import android.hardware.camera2.utils.CameraRuntimeException; import android.hardware.camera2.utils.LongParcelable; import android.hardware.camera2.utils.SurfaceUtils; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.util.Range; import android.util.Size; import android.util.SparseArray; import android.view.Surface; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; Loading Loading @@ -326,7 +332,8 @@ public class CameraDeviceImpl extends CameraDevice { for (Surface s : outputs) { outputConfigs.add(new OutputConfiguration(s)); } configureStreamsChecked(/*inputConfig*/null, outputConfigs); configureStreamsChecked(/*inputConfig*/null, outputConfigs, /*isConstrainedHighSpeed*/false); } Loading @@ -344,12 +351,14 @@ public class CameraDeviceImpl extends CameraDevice { * * @param inputConfig input configuration or {@code null} for no input * @param outputs a list of one or more surfaces, or {@code null} to unconfigure * @param isConstrainedHighSpeed If the streams configuration is for constrained high speed output. * @return whether or not the configuration was successful * * @throws CameraAccessException if there were any unexpected problems during configuration */ public boolean configureStreamsChecked(InputConfiguration inputConfig, List<OutputConfiguration> outputs) throws CameraAccessException { List<OutputConfiguration> outputs, boolean isConstrainedHighSpeed) throws CameraAccessException { // Treat a null input the same an empty list if (outputs == null) { outputs = new ArrayList<OutputConfiguration>(); Loading Loading @@ -422,7 +431,7 @@ public class CameraDeviceImpl extends CameraDevice { } try { mRemoteDevice.endConfigure(); mRemoteDevice.endConfigure(isConstrainedHighSpeed); } catch (IllegalArgumentException e) { // OK. camera service can reject stream config if it's not supported by HAL Loading Loading @@ -463,7 +472,8 @@ public class CameraDeviceImpl extends CameraDevice { for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } createCaptureSessionInternal(null, outConfigurations, callback, handler); createCaptureSessionInternal(null, outConfigurations, callback, handler, /*isConstrainedHighSpeed*/false); } @Override Loading @@ -475,7 +485,8 @@ public class CameraDeviceImpl extends CameraDevice { Log.d(TAG, "createCaptureSessionByOutputConfiguration"); } createCaptureSessionInternal(null, outputConfigurations, callback, handler); createCaptureSessionInternal(null, outputConfigurations, callback, handler, /*isConstrainedHighSpeed*/false); } @Override Loading @@ -494,13 +505,14 @@ public class CameraDeviceImpl extends CameraDevice { for (Surface surface : outputs) { outConfigurations.add(new OutputConfiguration(surface)); } createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler); createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler, /*isConstrainedHighSpeed*/false); } private void createCaptureSessionInternal(InputConfiguration inputConfig, List<OutputConfiguration> outputConfigurations, CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException { CameraCaptureSession.StateCallback callback, Handler handler, boolean isConstrainedHighSpeed) throws CameraAccessException { synchronized(mInterfaceLock) { if (DEBUG) { Log.d(TAG, "createCaptureSessionInternal"); Loading @@ -508,6 +520,11 @@ public class CameraDeviceImpl extends CameraDevice { checkIfCameraClosedOrInError(); if (isConstrainedHighSpeed && inputConfig != null) { throw new IllegalArgumentException("Constrained high speed session doesn't support" + " input configuration yet."); } // Notify current session that it's going away, before starting camera operations // After this call completes, the session is not allowed to call into CameraDeviceImpl if (mCurrentSession != null) { Loading @@ -520,7 +537,8 @@ public class CameraDeviceImpl extends CameraDevice { Surface input = null; try { // configure streams and then block until IDLE configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations); configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, isConstrainedHighSpeed); if (inputConfig != null) { input = new Surface(); mRemoteDevice.getInputSurface(/*out*/input); Loading @@ -545,7 +563,7 @@ public class CameraDeviceImpl extends CameraDevice { CameraCaptureSessionImpl newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, outSurfaces, callback, handler, this, mDeviceHandler, configureSuccess); configureSuccess, isConstrainedHighSpeed); // TODO: wait until current session closes, then create the new session mCurrentSession = newSession; Loading Loading @@ -1906,17 +1924,156 @@ public class CameraDeviceImpl extends CameraDevice { return mCharacteristics; } 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) { // Surface size must be supported high speed sizes. Size surfaceSize = SurfaceUtils.getSurfaceSize(surface); int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface); if (surfaceFormat != ImageFormat.PRIVATE) { throw new IllegalArgumentException("Surface format is not for preview or" + " hardware video encoding" + surfaceFormat); } 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 { // TODO: to be implemented throw new UnsupportedOperationException("To be implemented!!!!"); 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 { throw new UnsupportedOperationException("To be implemented!!!!"); 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()); 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. Surface[] surfaces = (Surface[])outputSurfaces.toArray(); if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(surfaces[0])) { 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) { 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(surfaces[0]); doubleTargetRequestBuilder.addTarget(surfaces[1]); doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); // Make sure singleTargetRequestBuilder contains only recording surface for // preview + recording case. Surface recordingSurface = surfaces[0]; if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) { recordingSurface = surfaces[1]; } singleTargetRequestBuilder.addTarget(recordingSurface); } else { // Single output case: either recording or preview. singleTargetRequestBuilder.addTarget(surfaces[0]); } // 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); } }
core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +1 −1 Original line number Diff line number Diff line Loading @@ -465,7 +465,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override public int endConfigure() { public int endConfigure(boolean isConstrainedHighSpeed) { if (DEBUG) { Log.d(TAG, "endConfigure called."); } Loading