1322 lines
56 KiB
Java
1322 lines
56 KiB
Java
/*
|
|
* Copyright (C) 2021 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.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.RequiresPermission;
|
|
import android.content.Context;
|
|
import android.graphics.ColorSpace;
|
|
import android.graphics.ImageFormat;
|
|
import android.graphics.SurfaceTexture;
|
|
import android.hardware.SyncFence;
|
|
import android.hardware.camera2.CameraAccessException;
|
|
import android.hardware.camera2.CameraCaptureSession;
|
|
import android.hardware.camera2.CameraCharacteristics;
|
|
import android.hardware.camera2.CameraDevice;
|
|
import android.hardware.camera2.CameraExtensionCharacteristics;
|
|
import android.hardware.camera2.CameraExtensionSession;
|
|
import android.hardware.camera2.CaptureFailure;
|
|
import android.hardware.camera2.CaptureRequest;
|
|
import android.hardware.camera2.CaptureResult;
|
|
import android.hardware.camera2.TotalCaptureResult;
|
|
import android.hardware.camera2.extension.CameraOutputConfig;
|
|
import android.hardware.camera2.extension.CameraSessionConfig;
|
|
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
|
|
import android.hardware.camera2.extension.ICaptureCallback;
|
|
import android.hardware.camera2.extension.IImageProcessorImpl;
|
|
import android.hardware.camera2.extension.IInitializeSessionCallback;
|
|
import android.hardware.camera2.extension.IRequestCallback;
|
|
import android.hardware.camera2.extension.IRequestProcessorImpl;
|
|
import android.hardware.camera2.extension.ISessionProcessorImpl;
|
|
import android.hardware.camera2.extension.LatencyPair;
|
|
import android.hardware.camera2.extension.OutputConfigId;
|
|
import android.hardware.camera2.extension.OutputSurface;
|
|
import android.hardware.camera2.extension.ParcelCaptureResult;
|
|
import android.hardware.camera2.extension.ParcelImage;
|
|
import android.hardware.camera2.extension.ParcelTotalCaptureResult;
|
|
import android.hardware.camera2.extension.Request;
|
|
import android.hardware.camera2.params.ColorSpaceProfiles;
|
|
import android.hardware.camera2.params.DynamicRangeProfiles;
|
|
import android.hardware.camera2.params.ExtensionSessionConfiguration;
|
|
import android.hardware.camera2.params.OutputConfiguration;
|
|
import android.hardware.camera2.params.SessionConfiguration;
|
|
import android.hardware.camera2.utils.ExtensionSessionStatsAggregator;
|
|
import android.hardware.camera2.utils.SurfaceUtils;
|
|
import android.media.Image;
|
|
import android.media.ImageReader;
|
|
import android.os.Binder;
|
|
import android.os.Handler;
|
|
import android.os.HandlerThread;
|
|
import android.os.IBinder;
|
|
import android.os.RemoteException;
|
|
import android.util.IntArray;
|
|
import android.util.Log;
|
|
import android.util.Size;
|
|
import android.view.Surface;
|
|
|
|
import com.android.internal.camera.flags.Flags;
|
|
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.Executor;
|
|
|
|
public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSession {
|
|
private static final String TAG = "CameraAdvancedExtensionSessionImpl";
|
|
|
|
private final Executor mExecutor;
|
|
private CameraDevice mCameraDevice;
|
|
private final Map<String, CameraMetadataNative> mCharacteristicsMap;
|
|
private final Handler mHandler;
|
|
private final HandlerThread mHandlerThread;
|
|
private final CameraExtensionSession.StateCallback mCallbacks;
|
|
private IAdvancedExtenderImpl mAdvancedExtender;
|
|
// maps registered camera surfaces to extension output configs
|
|
private final HashMap<Surface, CameraOutputConfig> mCameraConfigMap = new HashMap<>();
|
|
// maps camera extension output ids to camera registered image readers
|
|
private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
|
|
private RequestProcessor mRequestProcessor = new RequestProcessor();
|
|
private final int mSessionId;
|
|
private IBinder mToken = null;
|
|
|
|
private Surface mClientRepeatingRequestSurface;
|
|
private Surface mClientCaptureSurface;
|
|
private Surface mClientPostviewSurface;
|
|
private OutputConfiguration mClientRepeatingRequestOutputConfig;
|
|
private OutputConfiguration mClientCaptureOutputConfig;
|
|
private OutputConfiguration mClientPostviewOutputConfig;
|
|
private CameraCaptureSession mCaptureSession = null;
|
|
private ISessionProcessorImpl mSessionProcessor = null;
|
|
private final InitializeSessionHandler mInitializeHandler;
|
|
private final ExtensionSessionStatsAggregator mStatsAggregator;
|
|
|
|
private boolean mInitialized;
|
|
private boolean mSessionClosed;
|
|
private int mExtensionType;
|
|
|
|
|
|
private final Context mContext;
|
|
|
|
// Lock to synchronize cross-thread access to device public interface
|
|
final Object mInterfaceLock;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
@RequiresPermission(android.Manifest.permission.CAMERA)
|
|
public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
|
|
@NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice,
|
|
@NonNull Map<String, CameraCharacteristics> characteristicsMap,
|
|
@NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId,
|
|
@NonNull IBinder token)
|
|
throws CameraAccessException, RemoteException {
|
|
String cameraId = cameraDevice.getId();
|
|
CameraExtensionCharacteristics extensionChars = new CameraExtensionCharacteristics(ctx,
|
|
cameraId, characteristicsMap);
|
|
|
|
Map<String, CameraMetadataNative> characteristicsMapNative =
|
|
CameraExtensionUtils.getCharacteristicsMapNative(characteristicsMap);
|
|
if (!CameraExtensionCharacteristics.isExtensionSupported(cameraDevice.getId(),
|
|
config.getExtension(), characteristicsMapNative)) {
|
|
throw new UnsupportedOperationException("Unsupported extension type: " +
|
|
config.getExtension());
|
|
}
|
|
|
|
if (config.getOutputConfigurations().isEmpty() ||
|
|
config.getOutputConfigurations().size() > 2) {
|
|
throw new IllegalArgumentException("Unexpected amount of output surfaces, received: " +
|
|
config.getOutputConfigurations().size() + " expected <= 2");
|
|
}
|
|
|
|
for (OutputConfiguration c : config.getOutputConfigurations()) {
|
|
if (c.getDynamicRangeProfile() != DynamicRangeProfiles.STANDARD) {
|
|
if (Flags.extension10Bit() && Flags.cameraExtensionsCharacteristicsGet()) {
|
|
DynamicRangeProfiles dynamicProfiles = extensionChars.get(
|
|
config.getExtension(),
|
|
CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES);
|
|
if (dynamicProfiles == null || !dynamicProfiles.getSupportedProfiles()
|
|
.contains(c.getDynamicRangeProfile())) {
|
|
throw new IllegalArgumentException("Unsupported dynamic range profile: "
|
|
+ c.getDynamicRangeProfile());
|
|
}
|
|
} else {
|
|
throw new IllegalArgumentException("Unsupported dynamic range profile: "
|
|
+ c.getDynamicRangeProfile());
|
|
}
|
|
}
|
|
if (c.getStreamUseCase() !=
|
|
CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
|
|
throw new IllegalArgumentException("Unsupported stream use case: " +
|
|
c.getStreamUseCase());
|
|
}
|
|
}
|
|
|
|
int suitableSurfaceCount = 0;
|
|
List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
|
|
config.getExtension(), SurfaceTexture.class);
|
|
Surface repeatingRequestSurface = CameraExtensionUtils.getRepeatingRequestSurface(
|
|
config.getOutputConfigurations(), supportedPreviewSizes);
|
|
OutputConfiguration repeatingRequestOutputConfig = null;
|
|
if (repeatingRequestSurface != null) {
|
|
for (OutputConfiguration outputConfig : config.getOutputConfigurations()) {
|
|
if (outputConfig.getSurface() == repeatingRequestSurface) {
|
|
repeatingRequestOutputConfig = outputConfig;
|
|
}
|
|
}
|
|
suitableSurfaceCount++;
|
|
}
|
|
|
|
HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>();
|
|
|
|
IntArray supportedCaptureOutputFormats =
|
|
new IntArray(CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.length);
|
|
supportedCaptureOutputFormats.addAll(
|
|
CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS);
|
|
if (Flags.extension10Bit()) {
|
|
supportedCaptureOutputFormats.add(ImageFormat.YCBCR_P010);
|
|
}
|
|
for (int format : supportedCaptureOutputFormats.toArray()) {
|
|
List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes(
|
|
config.getExtension(), format);
|
|
if (supportedSizes != null) {
|
|
supportedCaptureSizes.put(format, supportedSizes);
|
|
}
|
|
}
|
|
|
|
int captureFormat = ImageFormat.UNKNOWN;
|
|
Surface burstCaptureSurface = CameraExtensionUtils.getBurstCaptureSurface(
|
|
config.getOutputConfigurations(), supportedCaptureSizes);
|
|
OutputConfiguration burstCaptureOutputConfig = null;
|
|
if (burstCaptureSurface != null) {
|
|
for (OutputConfiguration outputConfig : config.getOutputConfigurations()) {
|
|
if (outputConfig.getSurface() == burstCaptureSurface) {
|
|
burstCaptureOutputConfig = outputConfig;
|
|
}
|
|
}
|
|
suitableSurfaceCount++;
|
|
|
|
if (Flags.analytics24q3()) {
|
|
CameraExtensionUtils.SurfaceInfo burstCaptureSurfaceInfo =
|
|
CameraExtensionUtils.querySurface(burstCaptureSurface);
|
|
captureFormat = burstCaptureSurfaceInfo.mFormat;
|
|
}
|
|
}
|
|
|
|
if (suitableSurfaceCount != config.getOutputConfigurations().size()) {
|
|
throw new IllegalArgumentException("One or more unsupported output surfaces found!");
|
|
}
|
|
|
|
Surface postviewSurface = null;
|
|
OutputConfiguration postviewOutputConfig = config.getPostviewOutputConfiguration();
|
|
if (burstCaptureSurface != null && config.getPostviewOutputConfiguration() != null) {
|
|
CameraExtensionUtils.SurfaceInfo burstCaptureSurfaceInfo =
|
|
CameraExtensionUtils.querySurface(burstCaptureSurface);
|
|
Size burstCaptureSurfaceSize =
|
|
new Size(burstCaptureSurfaceInfo.mWidth, burstCaptureSurfaceInfo.mHeight);
|
|
HashMap<Integer, List<Size>> supportedPostviewSizes = new HashMap<>();
|
|
for (int format : supportedCaptureOutputFormats.toArray()) {
|
|
List<Size> supportedSizesPostview = extensionChars.getPostviewSupportedSizes(
|
|
config.getExtension(), burstCaptureSurfaceSize, format);
|
|
if (supportedSizesPostview != null) {
|
|
supportedPostviewSizes.put(format, supportedSizesPostview);
|
|
}
|
|
}
|
|
|
|
postviewSurface = CameraExtensionUtils.getPostviewSurface(
|
|
config.getPostviewOutputConfiguration(), supportedPostviewSizes,
|
|
burstCaptureSurfaceInfo.mFormat);
|
|
if (postviewSurface == null) {
|
|
throw new IllegalArgumentException("Unsupported output surface for postview!");
|
|
}
|
|
}
|
|
|
|
IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension(
|
|
config.getExtension());
|
|
extender.init(cameraId, characteristicsMapNative);
|
|
|
|
CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(ctx,
|
|
extender, cameraDevice, characteristicsMapNative, repeatingRequestOutputConfig,
|
|
burstCaptureOutputConfig, postviewOutputConfig, config.getStateCallback(),
|
|
config.getExecutor(), sessionId, token, config.getExtension());
|
|
|
|
if (Flags.analytics24q3()) {
|
|
ret.mStatsAggregator.setCaptureFormat(captureFormat);
|
|
}
|
|
ret.mStatsAggregator.setClientName(ctx.getOpPackageName());
|
|
ret.mStatsAggregator.setExtensionType(config.getExtension());
|
|
|
|
ret.initialize();
|
|
|
|
return ret;
|
|
}
|
|
|
|
private CameraAdvancedExtensionSessionImpl(Context ctx,
|
|
@NonNull IAdvancedExtenderImpl extender,
|
|
@NonNull CameraDeviceImpl cameraDevice,
|
|
Map<String, CameraMetadataNative> characteristicsMap,
|
|
@Nullable OutputConfiguration repeatingRequestOutputConfig,
|
|
@Nullable OutputConfiguration burstCaptureOutputConfig,
|
|
@Nullable OutputConfiguration postviewOutputConfig,
|
|
@NonNull StateCallback callback, @NonNull Executor executor,
|
|
int sessionId,
|
|
@NonNull IBinder token,
|
|
int extension) {
|
|
mContext = ctx;
|
|
mAdvancedExtender = extender;
|
|
mCameraDevice = cameraDevice;
|
|
mCharacteristicsMap = characteristicsMap;
|
|
mCallbacks = callback;
|
|
mExecutor = executor;
|
|
mClientRepeatingRequestOutputConfig = repeatingRequestOutputConfig;
|
|
mClientCaptureOutputConfig = burstCaptureOutputConfig;
|
|
mClientPostviewOutputConfig = postviewOutputConfig;
|
|
if (repeatingRequestOutputConfig != null) {
|
|
mClientRepeatingRequestSurface = repeatingRequestOutputConfig.getSurface();
|
|
}
|
|
if (burstCaptureOutputConfig != null) {
|
|
mClientCaptureSurface = burstCaptureOutputConfig.getSurface();
|
|
}
|
|
if (postviewOutputConfig != null) {
|
|
mClientPostviewSurface = postviewOutputConfig.getSurface();
|
|
}
|
|
mHandlerThread = new HandlerThread(TAG);
|
|
mHandlerThread.start();
|
|
mHandler = new Handler(mHandlerThread.getLooper());
|
|
mInitialized = false;
|
|
mSessionClosed = false;
|
|
mInitializeHandler = new InitializeSessionHandler();
|
|
mSessionId = sessionId;
|
|
mToken = token;
|
|
mInterfaceLock = cameraDevice.mInterfaceLock;
|
|
mExtensionType = extension;
|
|
|
|
mStatsAggregator = new ExtensionSessionStatsAggregator(mCameraDevice.getId(),
|
|
/*isAdvanced=*/true);
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public synchronized void initialize() throws CameraAccessException, RemoteException {
|
|
if (mInitialized) {
|
|
Log.d(TAG, "Session already initialized");
|
|
return;
|
|
}
|
|
|
|
OutputSurface previewSurface = initializeParcelable(mClientRepeatingRequestOutputConfig);
|
|
OutputSurface captureSurface = initializeParcelable(mClientCaptureOutputConfig);
|
|
OutputSurface postviewSurface = initializeParcelable(mClientPostviewOutputConfig);
|
|
|
|
mSessionProcessor = mAdvancedExtender.getSessionProcessor();
|
|
CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mToken,
|
|
mCameraDevice.getId(),
|
|
mCharacteristicsMap, previewSurface, captureSurface, postviewSurface);
|
|
List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
|
|
ArrayList<OutputConfiguration> outputList = new ArrayList<>();
|
|
for (CameraOutputConfig output : outputConfigs) {
|
|
Surface outputSurface = initializeSurface(output);
|
|
if (outputSurface == null) {
|
|
continue;
|
|
}
|
|
OutputConfiguration cameraOutput = new OutputConfiguration(output.surfaceGroupId,
|
|
outputSurface);
|
|
|
|
if (output.isMultiResolutionOutput) {
|
|
cameraOutput.setMultiResolutionOutput();
|
|
}
|
|
if ((output.sharedSurfaceConfigs != null) && !output.sharedSurfaceConfigs.isEmpty()) {
|
|
cameraOutput.enableSurfaceSharing();
|
|
for (CameraOutputConfig sharedOutputConfig : output.sharedSurfaceConfigs) {
|
|
Surface sharedSurface = initializeSurface(sharedOutputConfig);
|
|
if (sharedSurface == null) {
|
|
continue;
|
|
}
|
|
cameraOutput.addSurface(sharedSurface);
|
|
mCameraConfigMap.put(sharedSurface, sharedOutputConfig);
|
|
}
|
|
}
|
|
|
|
// The extension processing logic needs to be able to match images to capture results via
|
|
// image and result timestamps.
|
|
cameraOutput.setTimestampBase(OutputConfiguration.TIMESTAMP_BASE_SENSOR);
|
|
cameraOutput.setReadoutTimestampEnabled(false);
|
|
cameraOutput.setPhysicalCameraId(output.physicalCameraId);
|
|
if (Flags.extension10Bit()) {
|
|
boolean validDynamicRangeProfile = false;
|
|
for (long profile = DynamicRangeProfiles.STANDARD;
|
|
profile < DynamicRangeProfiles.PUBLIC_MAX; profile <<= 1) {
|
|
if (output.dynamicRangeProfile == profile) {
|
|
validDynamicRangeProfile = true;
|
|
break;
|
|
}
|
|
}
|
|
if (validDynamicRangeProfile) {
|
|
cameraOutput.setDynamicRangeProfile(output.dynamicRangeProfile);
|
|
} else {
|
|
Log.e(TAG, "Extension configured dynamic range profile "
|
|
+ output.dynamicRangeProfile
|
|
+ " is not valid, using default DynamicRangeProfile.STANDARD");
|
|
}
|
|
}
|
|
outputList.add(cameraOutput);
|
|
mCameraConfigMap.put(cameraOutput.getSurface(), output);
|
|
}
|
|
|
|
int sessionType = SessionConfiguration.SESSION_REGULAR;
|
|
if (sessionConfig.sessionType != -1 &&
|
|
(sessionConfig.sessionType != SessionConfiguration.SESSION_HIGH_SPEED)) {
|
|
sessionType = sessionConfig.sessionType;
|
|
Log.v(TAG, "Using session type: " + sessionType);
|
|
}
|
|
|
|
SessionConfiguration sessionConfiguration = new SessionConfiguration(sessionType,
|
|
outputList, new CameraExtensionUtils.HandlerExecutor(mHandler),
|
|
new SessionStateHandler());
|
|
if (Flags.extension10Bit()) {
|
|
if (sessionConfig.colorSpace >= 0
|
|
&& sessionConfig.colorSpace < ColorSpace.Named.values().length) {
|
|
sessionConfiguration.setColorSpace(
|
|
ColorSpace.Named.values()[sessionConfig.colorSpace]);
|
|
} else {
|
|
Log.e(TAG, "Extension configured color space " + sessionConfig.colorSpace
|
|
+ " is not valid, using default unspecified color space");
|
|
}
|
|
}
|
|
if ((sessionConfig.sessionParameter != null) &&
|
|
(!sessionConfig.sessionParameter.isEmpty())) {
|
|
CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(
|
|
sessionConfig.sessionTemplateId);
|
|
CaptureRequest sessionRequest = requestBuilder.build();
|
|
CameraMetadataNative.update(sessionRequest.getNativeMetadata(),
|
|
sessionConfig.sessionParameter);
|
|
sessionConfiguration.setSessionParameters(sessionRequest);
|
|
}
|
|
|
|
mCameraDevice.createCaptureSession(sessionConfiguration);
|
|
}
|
|
|
|
private static ParcelCaptureResult initializeParcelable(CaptureResult result) {
|
|
ParcelCaptureResult ret = new ParcelCaptureResult();
|
|
ret.cameraId = result.getCameraId();
|
|
ret.results = result.getNativeMetadata();
|
|
ret.parent = result.getRequest();
|
|
ret.sequenceId = result.getSequenceId();
|
|
ret.frameNumber = result.getFrameNumber();
|
|
|
|
return ret;
|
|
}
|
|
|
|
private static ParcelTotalCaptureResult initializeParcelable(TotalCaptureResult totalResult) {
|
|
ParcelTotalCaptureResult ret = new ParcelTotalCaptureResult();
|
|
ret.logicalCameraId = totalResult.getCameraId();
|
|
ret.results = totalResult.getNativeMetadata();
|
|
ret.parent = totalResult.getRequest();
|
|
ret.sequenceId = totalResult.getSequenceId();
|
|
ret.frameNumber = totalResult.getFrameNumber();
|
|
ret.sessionId = totalResult.getSessionId();
|
|
ret.partials = new ArrayList<>(totalResult.getPartialResults().size());
|
|
for (CaptureResult partial : totalResult.getPartialResults()) {
|
|
ret.partials.add(initializeParcelable(partial));
|
|
}
|
|
Map<String, TotalCaptureResult> physicalResults =
|
|
totalResult.getPhysicalCameraTotalResults();
|
|
ret.physicalResult = new ArrayList<>(physicalResults.size());
|
|
for (TotalCaptureResult physicalResult : physicalResults.values()) {
|
|
ret.physicalResult.add(new PhysicalCaptureResultInfo(physicalResult.getCameraId(),
|
|
physicalResult.getNativeMetadata()));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private static OutputSurface initializeParcelable(OutputConfiguration o) {
|
|
OutputSurface ret = new OutputSurface();
|
|
|
|
if (o != null && o.getSurface() != null) {
|
|
Surface s = o.getSurface();
|
|
ret.surface = s;
|
|
ret.size = new android.hardware.camera2.extension.Size();
|
|
Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
|
|
ret.size.width = surfaceSize.getWidth();
|
|
ret.size.height = surfaceSize.getHeight();
|
|
ret.imageFormat = SurfaceUtils.getSurfaceFormat(s);
|
|
|
|
if (Flags.extension10Bit()) {
|
|
ret.dynamicRangeProfile = o.getDynamicRangeProfile();
|
|
ColorSpace colorSpace = o.getColorSpace();
|
|
if (colorSpace != null) {
|
|
ret.colorSpace = colorSpace.getId();
|
|
} else {
|
|
ret.colorSpace = ColorSpaceProfiles.UNSPECIFIED;
|
|
}
|
|
} else {
|
|
ret.dynamicRangeProfile = DynamicRangeProfiles.STANDARD;
|
|
ret.colorSpace = ColorSpaceProfiles.UNSPECIFIED;
|
|
}
|
|
} else {
|
|
ret.surface = null;
|
|
ret.size = new android.hardware.camera2.extension.Size();
|
|
ret.size.width = -1;
|
|
ret.size.height = -1;
|
|
ret.imageFormat = ImageFormat.UNKNOWN;
|
|
ret.dynamicRangeProfile = DynamicRangeProfiles.STANDARD;
|
|
ret.colorSpace = ColorSpaceProfiles.UNSPECIFIED;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
@Override
|
|
public @NonNull CameraDevice getDevice() {
|
|
synchronized (mInterfaceLock) {
|
|
return mCameraDevice;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public StillCaptureLatency getRealtimeStillCaptureLatency() throws CameraAccessException {
|
|
synchronized (mInterfaceLock) {
|
|
if (!mInitialized) {
|
|
throw new IllegalStateException("Uninitialized component");
|
|
}
|
|
|
|
try {
|
|
LatencyPair latency = mSessionProcessor.getRealtimeCaptureLatency();
|
|
if (latency != null) {
|
|
return new StillCaptureLatency(latency.first, latency.second);
|
|
}
|
|
|
|
return null;
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to query realtime latency! Extension service does not "
|
|
+ "respond");
|
|
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int setRepeatingRequest(@NonNull CaptureRequest request, @NonNull Executor executor,
|
|
@NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
|
|
int seqId = -1;
|
|
synchronized (mInterfaceLock) {
|
|
if (!mInitialized) {
|
|
throw new IllegalStateException("Uninitialized component");
|
|
}
|
|
|
|
if (mClientRepeatingRequestSurface == null) {
|
|
throw new IllegalArgumentException("No registered preview surface");
|
|
}
|
|
|
|
if (!request.containsTarget(mClientRepeatingRequestSurface) ||
|
|
(request.getTargets().size() != 1)) {
|
|
throw new IllegalArgumentException("Invalid repeating request output target!");
|
|
}
|
|
|
|
try {
|
|
mSessionProcessor.setParameters(request);
|
|
|
|
seqId = mSessionProcessor.startRepeating(new RequestCallbackHandler(request,
|
|
executor, listener, mCameraDevice.getId()));
|
|
} catch (RemoteException e) {
|
|
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
|
|
"Failed to enable repeating request, extension service failed to respond!");
|
|
}
|
|
}
|
|
|
|
return seqId;
|
|
}
|
|
|
|
@Override
|
|
public int capture(@NonNull CaptureRequest request,
|
|
@NonNull Executor executor,
|
|
@NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
|
|
int seqId = -1;
|
|
synchronized (mInterfaceLock) {
|
|
if (!mInitialized) {
|
|
throw new IllegalStateException("Uninitialized component");
|
|
}
|
|
|
|
validateCaptureRequestTargets(request);
|
|
|
|
if ((mClientCaptureSurface != null) && request.containsTarget(mClientCaptureSurface)) {
|
|
try {
|
|
boolean isPostviewRequested = request.containsTarget(mClientPostviewSurface);
|
|
mSessionProcessor.setParameters(request);
|
|
|
|
seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
|
|
executor, listener, mCameraDevice.getId()), isPostviewRequested);
|
|
} catch (RemoteException e) {
|
|
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " +
|
|
" to submit capture request, extension service failed to respond!");
|
|
}
|
|
} else if ((mClientRepeatingRequestSurface != null) &&
|
|
request.containsTarget(mClientRepeatingRequestSurface)) {
|
|
try {
|
|
seqId = mSessionProcessor.startTrigger(request, new RequestCallbackHandler(
|
|
request, executor, listener, mCameraDevice.getId()));
|
|
} catch (RemoteException e) {
|
|
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " +
|
|
" to submit trigger request, extension service failed to respond!");
|
|
}
|
|
} else {
|
|
throw new IllegalArgumentException("Invalid single capture output target!");
|
|
}
|
|
}
|
|
|
|
return seqId;
|
|
}
|
|
|
|
private void validateCaptureRequestTargets(@NonNull CaptureRequest request) {
|
|
if (request.getTargets().size() == 1) {
|
|
boolean containsCaptureTarget =
|
|
mClientCaptureSurface != null && request.containsTarget(mClientCaptureSurface);
|
|
boolean containsRepeatingTarget =
|
|
mClientRepeatingRequestSurface != null &&
|
|
request.containsTarget(mClientRepeatingRequestSurface);
|
|
|
|
if (!containsCaptureTarget && !containsRepeatingTarget) {
|
|
throw new IllegalArgumentException("Target output combination requested is " +
|
|
"not supported!");
|
|
}
|
|
}
|
|
|
|
if ((request.getTargets().size() == 2) &&
|
|
(!request.getTargets().containsAll(Arrays.asList(mClientCaptureSurface,
|
|
mClientPostviewSurface)))) {
|
|
throw new IllegalArgumentException("Target output combination requested is " +
|
|
"not supported!");
|
|
}
|
|
|
|
if (request.getTargets().size() > 2) {
|
|
throw new IllegalArgumentException("Target output combination requested is " +
|
|
"not supported!");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void stopRepeating() throws CameraAccessException {
|
|
synchronized (mInterfaceLock) {
|
|
if (!mInitialized) {
|
|
throw new IllegalStateException("Uninitialized component");
|
|
}
|
|
|
|
mCaptureSession.stopRepeating();
|
|
|
|
try {
|
|
mSessionProcessor.stopRepeating();
|
|
} catch (RemoteException e) {
|
|
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
|
|
"Failed to notify about the end of repeating request, extension service"
|
|
+ " failed to respond!");
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void close() throws CameraAccessException {
|
|
synchronized (mInterfaceLock) {
|
|
if (mInitialized) {
|
|
try {
|
|
try {
|
|
mCaptureSession.stopRepeating();
|
|
} catch (IllegalStateException e) {
|
|
// OK: already be closed, nothing else to do
|
|
}
|
|
mSessionProcessor.stopRepeating();
|
|
mSessionProcessor.onCaptureSessionEnd();
|
|
mSessionClosed = true;
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to stop the repeating request or end the session,"
|
|
+ " , extension service does not respond!") ;
|
|
}
|
|
// Commit stats before closing the capture session
|
|
mStatsAggregator.commit(/*isFinal*/true);
|
|
mCaptureSession.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called by {@link CameraDeviceImpl} right before the capture session is closed, and before it
|
|
* calls {@link #release}
|
|
*/
|
|
public void commitStats() {
|
|
synchronized (mInterfaceLock) {
|
|
if (mInitialized) {
|
|
// Only commit stats if a capture session was initialized
|
|
mStatsAggregator.commit(/*isFinal*/true);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void release(boolean skipCloseNotification) {
|
|
boolean notifyClose = false;
|
|
|
|
synchronized (mInterfaceLock) {
|
|
mHandlerThread.quitSafely();
|
|
|
|
if (mSessionProcessor != null) {
|
|
try {
|
|
if (!mSessionClosed) {
|
|
mSessionProcessor.onCaptureSessionEnd();
|
|
}
|
|
mSessionProcessor.deInitSession(mToken);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to de-initialize session processor, extension service"
|
|
+ " does not respond!") ;
|
|
}
|
|
mSessionProcessor = null;
|
|
}
|
|
|
|
|
|
if (mToken != null) {
|
|
if (mInitialized || (mCaptureSession != null)) {
|
|
notifyClose = true;
|
|
CameraExtensionCharacteristics.releaseSession(mExtensionType);
|
|
}
|
|
CameraExtensionCharacteristics.unregisterClient(mContext, mToken, mExtensionType);
|
|
}
|
|
mInitialized = false;
|
|
mToken = null;
|
|
|
|
for (ImageReader reader : mReaderMap.values()) {
|
|
reader.close();
|
|
}
|
|
mReaderMap.clear();
|
|
|
|
mClientRepeatingRequestSurface = null;
|
|
mClientCaptureSurface = null;
|
|
mCaptureSession = null;
|
|
mRequestProcessor = null;
|
|
mCameraDevice = null;
|
|
mAdvancedExtender = null;
|
|
}
|
|
|
|
if (notifyClose && !skipCloseNotification) {
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mExecutor.execute(() -> mCallbacks.onClosed(
|
|
CameraAdvancedExtensionSessionImpl.this));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void notifyConfigurationFailure() {
|
|
synchronized (mInterfaceLock) {
|
|
if (mInitialized) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
release(true /*skipCloseNotification*/);
|
|
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mExecutor.execute(
|
|
() -> mCallbacks.onConfigureFailed(
|
|
CameraAdvancedExtensionSessionImpl.this));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
|
|
private class SessionStateHandler extends
|
|
android.hardware.camera2.CameraCaptureSession.StateCallback {
|
|
@Override
|
|
public void onClosed(@NonNull CameraCaptureSession session) {
|
|
release(false /*skipCloseNotification*/);
|
|
}
|
|
|
|
@Override
|
|
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
|
|
notifyConfigurationFailure();
|
|
}
|
|
|
|
@Override
|
|
public void onConfigured(@NonNull CameraCaptureSession session) {
|
|
synchronized (mInterfaceLock) {
|
|
mCaptureSession = session;
|
|
// Commit basic stats as soon as the capture session is created
|
|
mStatsAggregator.commit(/*isFinal*/false);
|
|
}
|
|
|
|
try {
|
|
CameraExtensionCharacteristics.initializeSession(
|
|
mInitializeHandler, mExtensionType);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to initialize session! Extension service does"
|
|
+ " not respond!");
|
|
notifyConfigurationFailure();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class InitializeSessionHandler extends IInitializeSessionCallback.Stub {
|
|
@Override
|
|
public void onSuccess() {
|
|
mHandler.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
boolean status = true;
|
|
synchronized (mInterfaceLock) {
|
|
try {
|
|
if (mSessionProcessor != null) {
|
|
mInitialized = true;
|
|
mSessionProcessor.onCaptureSessionStart(mRequestProcessor,
|
|
mStatsAggregator.getStatsKey());
|
|
} else {
|
|
Log.v(TAG, "Failed to start capture session, session " +
|
|
" released before extension start!");
|
|
status = false;
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to start capture session,"
|
|
+ " extension service does not respond!");
|
|
status = false;
|
|
mInitialized = false;
|
|
}
|
|
}
|
|
|
|
if (status) {
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mExecutor.execute(() -> mCallbacks.onConfigured(
|
|
CameraAdvancedExtensionSessionImpl.this));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
} else {
|
|
onFailure();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onFailure() {
|
|
mHandler.post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mCaptureSession.close();
|
|
|
|
Log.e(TAG, "Failed to initialize proxy service session!"
|
|
+ " This can happen when trying to configure multiple "
|
|
+ "concurrent extension sessions!");
|
|
notifyConfigurationFailure();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
private final class RequestCallbackHandler extends ICaptureCallback.Stub {
|
|
private final CaptureRequest mClientRequest;
|
|
private final Executor mClientExecutor;
|
|
private final ExtensionCaptureCallback mClientCallbacks;
|
|
private final String mCameraId;
|
|
|
|
private RequestCallbackHandler(@NonNull CaptureRequest clientRequest,
|
|
@NonNull Executor clientExecutor,
|
|
@NonNull ExtensionCaptureCallback clientCallbacks,
|
|
@NonNull String cameraId) {
|
|
mClientRequest = clientRequest;
|
|
mClientExecutor = clientExecutor;
|
|
mClientCallbacks = clientCallbacks;
|
|
mCameraId = cameraId;
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureStarted(int captureSequenceId, long timestamp) {
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mClientExecutor.execute(
|
|
() -> mClientCallbacks.onCaptureStarted(
|
|
CameraAdvancedExtensionSessionImpl.this, mClientRequest,
|
|
timestamp));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureProcessStarted(int captureSequenceId) {
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mClientExecutor.execute(
|
|
() -> mClientCallbacks.onCaptureProcessStarted(
|
|
CameraAdvancedExtensionSessionImpl.this, mClientRequest));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureFailed(int captureSequenceId) {
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mClientExecutor.execute(
|
|
() -> mClientCallbacks.onCaptureFailed(
|
|
CameraAdvancedExtensionSessionImpl.this, mClientRequest));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureProcessFailed(int captureSequenceId, int captureFailureReason) {
|
|
if (Flags.concertMode()) {
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mClientExecutor.execute(
|
|
() -> mClientCallbacks.onCaptureFailed(
|
|
CameraAdvancedExtensionSessionImpl.this, mClientRequest,
|
|
captureFailureReason
|
|
));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureSequenceCompleted(int captureSequenceId) {
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mClientExecutor.execute(
|
|
() -> mClientCallbacks.onCaptureSequenceCompleted(
|
|
CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureSequenceAborted(int captureSequenceId) {
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mClientExecutor.execute(
|
|
() -> mClientCallbacks.onCaptureSequenceAborted(
|
|
CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureCompleted(long timestamp, int requestId, CameraMetadataNative result) {
|
|
if (result == null) {
|
|
Log.e(TAG,"Invalid capture result!");
|
|
return;
|
|
}
|
|
|
|
result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
|
|
TotalCaptureResult totalResult = new TotalCaptureResult(mCameraId, result,
|
|
mClientRequest, requestId, timestamp, new ArrayList<>(), mSessionId,
|
|
new PhysicalCaptureResultInfo[0]);
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mExecutor.execute(
|
|
() -> mClientCallbacks.onCaptureResultAvailable(
|
|
CameraAdvancedExtensionSessionImpl.this, mClientRequest,
|
|
totalResult));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureProcessProgressed(int progress) {
|
|
final long ident = Binder.clearCallingIdentity();
|
|
try {
|
|
mExecutor.execute(
|
|
() -> mClientCallbacks.onCaptureProcessProgressed(
|
|
CameraAdvancedExtensionSessionImpl.this, mClientRequest,
|
|
progress));
|
|
} finally {
|
|
Binder.restoreCallingIdentity(ident);
|
|
}
|
|
}
|
|
}
|
|
|
|
private final class CaptureCallbackHandler extends CameraCaptureSession.CaptureCallback {
|
|
private final IRequestCallback mCallback;
|
|
|
|
public CaptureCallbackHandler(IRequestCallback callback) {
|
|
mCallback = callback;
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureBufferLost(CameraCaptureSession session, CaptureRequest request,
|
|
Surface target, long frameNumber) {
|
|
try {
|
|
if (request.getTag() instanceof Integer) {
|
|
Integer requestId = (Integer) request.getTag();
|
|
mCallback.onCaptureBufferLost(requestId, frameNumber,
|
|
mCameraConfigMap.get(target).outputId.id);
|
|
} else {
|
|
Log.e(TAG, "Invalid capture request tag!");
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to notify lost capture buffer, extension service doesn't"
|
|
+ " respond!");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
|
|
TotalCaptureResult result) {
|
|
try {
|
|
if (request.getTag() instanceof Integer) {
|
|
Integer requestId = (Integer) request.getTag();
|
|
mCallback.onCaptureCompleted(requestId, initializeParcelable(result));
|
|
} else {
|
|
Log.e(TAG, "Invalid capture request tag!");
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to notify capture result, extension service doesn't"
|
|
+ " respond!");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
|
|
CaptureFailure failure) {
|
|
try {
|
|
if (request.getTag() instanceof Integer) {
|
|
Integer requestId = (Integer) request.getTag();
|
|
android.hardware.camera2.extension.CaptureFailure captureFailure =
|
|
new android.hardware.camera2.extension.CaptureFailure();
|
|
captureFailure.request = request;
|
|
captureFailure.reason = failure.getReason();
|
|
captureFailure.errorPhysicalCameraId = failure.getPhysicalCameraId();
|
|
captureFailure.frameNumber = failure.getFrameNumber();
|
|
captureFailure.sequenceId = failure.getSequenceId();
|
|
captureFailure.dropped = !failure.wasImageCaptured();
|
|
mCallback.onCaptureFailed(requestId, captureFailure);
|
|
} else {
|
|
Log.e(TAG, "Invalid capture request tag!");
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to notify capture failure, extension service doesn't"
|
|
+ " respond!");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
|
|
CaptureResult partialResult) {
|
|
try {
|
|
if (request.getTag() instanceof Integer) {
|
|
Integer requestId = (Integer) request.getTag();
|
|
mCallback.onCaptureProgressed(requestId, initializeParcelable(partialResult));
|
|
} else {
|
|
Log.e(TAG, "Invalid capture request tag!");
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to notify capture partial result, extension service doesn't"
|
|
+ " respond!");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureSequenceAborted(CameraCaptureSession session, int sequenceId) {
|
|
try {
|
|
mCallback.onCaptureSequenceAborted(sequenceId);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to notify aborted sequence, extension service doesn't"
|
|
+ " respond!");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
|
|
long frameNumber) {
|
|
try {
|
|
mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to notify sequence complete, extension service doesn't"
|
|
+ " respond!");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
|
|
long timestamp, long frameNumber) {
|
|
try {
|
|
if (request.getTag() instanceof Integer) {
|
|
Integer requestId = (Integer) request.getTag();
|
|
mCallback.onCaptureStarted(requestId, frameNumber, timestamp);
|
|
} else {
|
|
Log.e(TAG, "Invalid capture request tag!");
|
|
}
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to notify capture started, extension service doesn't"
|
|
+ " respond!");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static final class ImageReaderHandler implements ImageReader.OnImageAvailableListener {
|
|
private final OutputConfigId mOutputConfigId;
|
|
private final IImageProcessorImpl mIImageProcessor;
|
|
private final String mPhysicalCameraId;
|
|
|
|
private ImageReaderHandler(int outputConfigId,
|
|
IImageProcessorImpl iImageProcessor, String physicalCameraId) {
|
|
mOutputConfigId = new OutputConfigId();
|
|
mOutputConfigId.id = outputConfigId;
|
|
mIImageProcessor = iImageProcessor;
|
|
mPhysicalCameraId = physicalCameraId;
|
|
}
|
|
|
|
@Override
|
|
public void onImageAvailable(ImageReader reader) {
|
|
if (mIImageProcessor == null) {
|
|
return;
|
|
}
|
|
|
|
Image img;
|
|
try {
|
|
img = reader.acquireNextImage();
|
|
} catch (IllegalStateException e) {
|
|
Log.e(TAG, "Failed to acquire image, too many images pending!");
|
|
return;
|
|
}
|
|
if (img == null) {
|
|
Log.e(TAG, "Invalid image!");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
reader.detachImage(img);
|
|
} catch(Exception e) {
|
|
Log.e(TAG, "Failed to detach image");
|
|
img.close();
|
|
return;
|
|
}
|
|
|
|
ParcelImage parcelImage = new ParcelImage();
|
|
parcelImage.buffer = img.getHardwareBuffer();
|
|
try {
|
|
SyncFence fd = img.getFence();
|
|
if (fd.isValid()) {
|
|
parcelImage.fence = fd.getFdDup();
|
|
}
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "Failed to parcel buffer fence!");
|
|
}
|
|
parcelImage.width = img.getWidth();
|
|
parcelImage.height = img.getHeight();
|
|
parcelImage.format = img.getFormat();
|
|
parcelImage.timestamp = img.getTimestamp();
|
|
parcelImage.transform = img.getTransform();
|
|
parcelImage.scalingMode = img.getScalingMode();
|
|
parcelImage.planeCount = img.getPlaneCount();
|
|
parcelImage.crop = img.getCropRect();
|
|
|
|
try {
|
|
mIImageProcessor.onNextImageAvailable(mOutputConfigId, parcelImage,
|
|
mPhysicalCameraId);
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "Failed to propagate image buffer on output surface id: " +
|
|
mOutputConfigId + " extension service does not respond!");
|
|
} finally {
|
|
parcelImage.buffer.close();
|
|
img.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
private final class RequestProcessor extends IRequestProcessorImpl.Stub {
|
|
@Override
|
|
public void setImageProcessor(OutputConfigId outputConfigId,
|
|
IImageProcessorImpl imageProcessor) {
|
|
synchronized (mInterfaceLock) {
|
|
if (mReaderMap.containsKey(outputConfigId.id)) {
|
|
ImageReader reader = mReaderMap.get(outputConfigId.id);
|
|
String physicalCameraId = null;
|
|
if (mCameraConfigMap.containsKey(reader.getSurface())) {
|
|
physicalCameraId =
|
|
mCameraConfigMap.get(reader.getSurface()).physicalCameraId;
|
|
reader.setOnImageAvailableListener(new ImageReaderHandler(outputConfigId.id,
|
|
imageProcessor, physicalCameraId), mHandler);
|
|
} else {
|
|
Log.e(TAG, "Camera output configuration for ImageReader with " +
|
|
" config Id " + outputConfigId.id + " not found!");
|
|
}
|
|
} else {
|
|
Log.e(TAG, "ImageReader with output config id: " + outputConfigId.id +
|
|
" not found!");
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int submit(Request request, IRequestCallback callback) {
|
|
ArrayList<Request> captureList = new ArrayList<>();
|
|
captureList.add(request);
|
|
return submitBurst(captureList, callback);
|
|
}
|
|
|
|
@Override
|
|
public int submitBurst(List<Request> requests, IRequestCallback callback) {
|
|
int seqId = -1;
|
|
try {
|
|
synchronized (mInterfaceLock) {
|
|
if (!mInitialized) {
|
|
return seqId;
|
|
}
|
|
|
|
CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
|
|
ArrayList<CaptureRequest> captureRequests = new ArrayList<>();
|
|
for (Request request : requests) {
|
|
captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
|
|
mCameraConfigMap));
|
|
}
|
|
seqId = mCaptureSession.captureBurstRequests(captureRequests,
|
|
new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
|
|
}
|
|
} catch (CameraAccessException e) {
|
|
Log.e(TAG, "Failed to submit capture requests!");
|
|
} catch (IllegalStateException e) {
|
|
Log.e(TAG, "Capture session closed!");
|
|
}
|
|
|
|
return seqId;
|
|
}
|
|
|
|
@Override
|
|
public int setRepeating(Request request, IRequestCallback callback) {
|
|
int seqId = -1;
|
|
try {
|
|
synchronized (mInterfaceLock) {
|
|
if (!mInitialized) {
|
|
return seqId;
|
|
}
|
|
|
|
CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
|
|
request, mCameraConfigMap);
|
|
CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
|
|
seqId = mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
|
|
new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
|
|
}
|
|
} catch (CameraAccessException e) {
|
|
Log.e(TAG, "Failed to enable repeating request!");
|
|
} catch (IllegalStateException e) {
|
|
Log.e(TAG, "Capture session closed!");
|
|
}
|
|
|
|
return seqId;
|
|
}
|
|
|
|
@Override
|
|
public void abortCaptures() {
|
|
try {
|
|
synchronized (mInterfaceLock) {
|
|
if (!mInitialized) {
|
|
return;
|
|
}
|
|
|
|
mCaptureSession.abortCaptures();
|
|
}
|
|
} catch (CameraAccessException e) {
|
|
Log.e(TAG, "Failed during capture abort!");
|
|
} catch (IllegalStateException e) {
|
|
Log.e(TAG, "Capture session closed!");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void stopRepeating() {
|
|
try {
|
|
synchronized (mInterfaceLock) {
|
|
if (!mInitialized) {
|
|
return;
|
|
}
|
|
|
|
mCaptureSession.stopRepeating();
|
|
}
|
|
} catch (CameraAccessException e) {
|
|
Log.e(TAG, "Failed during repeating capture stop!");
|
|
} catch (IllegalStateException e) {
|
|
Log.e(TAG, "Capture session closed!");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static CaptureRequest initializeCaptureRequest(CameraDevice cameraDevice,
|
|
Request request, HashMap<Surface, CameraOutputConfig> surfaceIdMap)
|
|
throws CameraAccessException {
|
|
CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(request.templateId);
|
|
for (OutputConfigId configId : request.targetOutputConfigIds) {
|
|
boolean found = false;
|
|
for (Map.Entry<Surface, CameraOutputConfig> entry : surfaceIdMap.entrySet()) {
|
|
if (entry.getValue().outputId.id == configId.id) {
|
|
builder.addTarget(entry.getKey());
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
Log.e(TAG, "Surface with output id: " + configId.id +
|
|
" not found among registered camera outputs!");
|
|
}
|
|
}
|
|
|
|
builder.setTag(request.requestId);
|
|
CaptureRequest ret = builder.build();
|
|
CameraMetadataNative.update(ret.getNativeMetadata(), request.parameters);
|
|
return ret;
|
|
}
|
|
|
|
private Surface initializeSurface(CameraOutputConfig output) {
|
|
switch(output.type) {
|
|
case CameraOutputConfig.TYPE_SURFACE:
|
|
if (output.surface == null) {
|
|
Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
|
|
", skipping!");
|
|
return null;
|
|
}
|
|
return output.surface;
|
|
case CameraOutputConfig.TYPE_IMAGEREADER:
|
|
if ((output.imageFormat == ImageFormat.UNKNOWN) || (output.size.width <= 0) ||
|
|
(output.size.height <= 0)) {
|
|
Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
|
|
", skipping!");
|
|
return null;
|
|
}
|
|
ImageReader reader = ImageReader.newInstance(output.size.width,
|
|
output.size.height, output.imageFormat, output.capacity,
|
|
output.usage);
|
|
mReaderMap.put(output.outputId.id, reader);
|
|
return reader.getSurface();
|
|
case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER:
|
|
// Support for multi-resolution outputs to be added in future releases
|
|
default:
|
|
throw new IllegalArgumentException("Unsupported output config type: " +
|
|
output.type);
|
|
}
|
|
}
|
|
}
|