/* * 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.OutputConfiguration; 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. * *

* 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. *

*/ public class CameraConstrainedHighSpeedCaptureSessionImpl extends CameraConstrainedHighSpeedCaptureSession implements CameraCaptureSessionCore { private final CameraCharacteristics mCharacteristics; private final CameraCaptureSessionImpl mSessionImpl; /** * Create a new CameraCaptureSession. * *

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).

*/ CameraConstrainedHighSpeedCaptureSessionImpl(int id, List 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 createHighSpeedRequestList(CaptureRequest request) throws CameraAccessException { if (request == null) { throw new IllegalArgumentException("Input capture request must not be null"); } Collection outputSurfaces = request.getTargets(); Range 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 requestList = new ArrayList(); // 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 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 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 void prepare(int maxCount, Surface surface) throws CameraAccessException { mSessionImpl.prepare(maxCount, surface); } @Override public void tearDown(Surface surface) throws CameraAccessException { mSessionImpl.tearDown(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 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 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(); } @Override public void finishDeferredConfiguration(List deferredOutputConfigs) throws CameraAccessException { mSessionImpl.finishDeferredConfiguration(deferredOutputConfigs); } private class WrapperCallback extends StateCallback { private final StateCallback mCallback; public WrapperCallback(StateCallback callback) { mCallback = callback; } @Override public void onConfigured(CameraCaptureSession session) { mCallback.onConfigured(CameraConstrainedHighSpeedCaptureSessionImpl.this); } @Override public void onConfigureFailed(CameraCaptureSession session) { mCallback.onConfigureFailed(CameraConstrainedHighSpeedCaptureSessionImpl.this); } @Override public void onReady(CameraCaptureSession session) { mCallback.onReady(CameraConstrainedHighSpeedCaptureSessionImpl.this); } @Override public void onActive(CameraCaptureSession session) { mCallback.onActive(CameraConstrainedHighSpeedCaptureSessionImpl.this); } @Override public void onClosed(CameraCaptureSession session) { mCallback.onClosed(CameraConstrainedHighSpeedCaptureSessionImpl.this); } @Override public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { mCallback.onSurfacePrepared(CameraConstrainedHighSpeedCaptureSessionImpl.this, surface); } } }