418 lines
17 KiB
Java
418 lines
17 KiB
Java
/*
|
|
* 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.CameraMetadata;
|
|
import android.hardware.camera2.CameraOfflineSession;
|
|
import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
|
|
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.os.ConditionVariable;
|
|
import android.util.Range;
|
|
import android.util.Log;
|
|
import android.view.Surface;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.concurrent.Executor;
|
|
|
|
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;
|
|
private final ConditionVariable mInitialized = new ConditionVariable();
|
|
private final String TAG = "CameraConstrainedHighSpeedCaptureSessionImpl";
|
|
|
|
/**
|
|
* 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,
|
|
CameraCaptureSession.StateCallback callback, Executor stateExecutor,
|
|
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
|
|
Executor deviceStateExecutor, boolean configureSuccess,
|
|
CameraCharacteristics characteristics) {
|
|
mCharacteristics = characteristics;
|
|
CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
|
|
mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
|
|
stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
|
|
mInitialized.open();
|
|
}
|
|
|
|
@Override
|
|
public List<CaptureRequest> createHighSpeedRequestList(CaptureRequest request)
|
|
throws CameraAccessException {
|
|
if (request == null) {
|
|
throw new IllegalArgumentException("Input capture request must not be null");
|
|
}
|
|
CameraCharacteristics.Key<StreamConfigurationMap> ck =
|
|
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
|
|
Integer sensorPixelMode = request.get(CaptureRequest.SENSOR_PIXEL_MODE);
|
|
if (sensorPixelMode != null && sensorPixelMode ==
|
|
CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
|
|
ck = CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
|
|
}
|
|
Collection<Surface> outputSurfaces = request.getTargets();
|
|
Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
|
|
|
|
StreamConfigurationMap config = mCharacteristics.get(ck);
|
|
SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange, config);
|
|
|
|
// Check the high speed video fps ranges for video size and find the min value from the list
|
|
// and assign it to previewFps which will be used to calculate the requestList size.
|
|
Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRangesFor(
|
|
SurfaceUtils.getSurfaceSize(outputSurfaces.iterator().next()));
|
|
Log.v(TAG, "High speed fps ranges: " + Arrays.toString(highSpeedFpsRanges));
|
|
int previewFps = Integer.MAX_VALUE;
|
|
for (Range<Integer> range : highSpeedFpsRanges) {
|
|
int rangeMin = range.getLower();
|
|
if (previewFps > rangeMin) {
|
|
previewFps = rangeMin;
|
|
}
|
|
}
|
|
// Since we only want to support 60fps apart from 30fps, if the min value is not 60,
|
|
// then continue to calculate the requestList size using value 30.
|
|
if (previewFps != 60 && previewFps != 30) {
|
|
Log.w(TAG, "previewFps is neither 60 nor 30.");
|
|
previewFps = 30;
|
|
}
|
|
Log.v(TAG, "previewFps: " + previewFps);
|
|
|
|
int requestListSize = fpsRange.getUpper() / previewFps;
|
|
// If it's a preview, keep requestList size fixed = 1.
|
|
if (fpsRange.getUpper() > fpsRange.getLower()) {
|
|
requestListSize = 1;
|
|
}
|
|
|
|
Log.v(TAG, "Request list size is: " + requestListSize);
|
|
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,
|
|
request.getLogicalCameraId(), /*physicalCameraIdSet*/ null);
|
|
// Carry over userTag, as native metadata doesn't have this field.
|
|
singleTargetRequestBuilder.setTag(request.getTag());
|
|
|
|
// 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,
|
|
request.getLogicalCameraId(), /*physicalCameraIdSet*/null);
|
|
doubleTargetRequestBuilder.setTag(request.getTag());
|
|
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 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 captureSingleRequest(CaptureRequest request, Executor executor,
|
|
CaptureCallback listener) 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 captureBurstRequests(List<CaptureRequest> requests, Executor executor,
|
|
CaptureCallback listener) 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.captureBurstRequests(requests, executor, listener);
|
|
}
|
|
|
|
@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 setSingleRepeatingRequest(CaptureRequest request, Executor executor,
|
|
CaptureCallback listener) 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 int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor,
|
|
CaptureCallback listener) 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.setRepeatingBurstRequests(requests, executor, listener);
|
|
}
|
|
|
|
@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 updateOutputConfiguration(OutputConfiguration config)
|
|
throws CameraAccessException {
|
|
throw new UnsupportedOperationException("Constrained high speed session doesn't support"
|
|
+ " this method");
|
|
}
|
|
|
|
@Override
|
|
public CameraOfflineSession switchToOffline(Collection<Surface> offlineOutputs,
|
|
Executor executor, CameraOfflineSessionCallback listener) throws CameraAccessException {
|
|
throw new UnsupportedOperationException("Constrained high speed session doesn't support"
|
|
+ " this method");
|
|
}
|
|
|
|
@Override
|
|
public boolean supportsOfflineProcessing(Surface surface) {
|
|
throw new UnsupportedOperationException("Constrained high speed session doesn't support" +
|
|
" offline mode");
|
|
}
|
|
|
|
@Override
|
|
public void closeWithoutDraining() {
|
|
throw new UnsupportedOperationException("Constrained high speed session doesn't support"
|
|
+ " this method");
|
|
}
|
|
|
|
@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 finalizeOutputConfigurations(List<OutputConfiguration> deferredOutputConfigs)
|
|
throws CameraAccessException {
|
|
mSessionImpl.finalizeOutputConfigurations(deferredOutputConfigs);
|
|
}
|
|
|
|
private class WrapperCallback extends StateCallback {
|
|
private final StateCallback mCallback;
|
|
|
|
public WrapperCallback(StateCallback callback) {
|
|
mCallback = callback;
|
|
}
|
|
|
|
@Override
|
|
public void onConfigured(CameraCaptureSession session) {
|
|
mInitialized.block();
|
|
mCallback.onConfigured(CameraConstrainedHighSpeedCaptureSessionImpl.this);
|
|
}
|
|
|
|
@Override
|
|
public void onConfigureFailed(CameraCaptureSession session) {
|
|
mInitialized.block();
|
|
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 onCaptureQueueEmpty(CameraCaptureSession session) {
|
|
mCallback.onCaptureQueueEmpty(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);
|
|
}
|
|
}
|
|
}
|