script-astra/Android/Sdk/sources/android-35/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

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);
}
}
}