Merge "Camera: Add support for client requests&results when using extension sessions"

This commit is contained in:
TreeHugger Robot
2022-02-05 04:15:45 +00:00
committed by Android (Google) Code Review
14 changed files with 474 additions and 59 deletions

View File

@@ -17443,6 +17443,8 @@ package android.hardware.camera2 {
}
public final class CameraExtensionCharacteristics {
method @NonNull public java.util.Set<android.hardware.camera2.CaptureRequest.Key> getAvailableCaptureRequestKeys(int);
method @NonNull public java.util.Set<android.hardware.camera2.CaptureResult.Key> getAvailableCaptureResultKeys(int);
method @Nullable public android.util.Range<java.lang.Long> getEstimatedCaptureLatencyRangeMillis(int, @NonNull android.util.Size, int);
method @NonNull public <T> java.util.List<android.util.Size> getExtensionSupportedSizes(int, @NonNull Class<T>);
method @NonNull public java.util.List<android.util.Size> getExtensionSupportedSizes(int, int);
@@ -17467,6 +17469,7 @@ package android.hardware.camera2 {
ctor public CameraExtensionSession.ExtensionCaptureCallback();
method public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
method public void onCaptureProcessStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
method public void onCaptureResultAvailable(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @NonNull android.hardware.camera2.TotalCaptureResult);
method public void onCaptureSequenceAborted(@NonNull android.hardware.camera2.CameraExtensionSession, int);
method public void onCaptureSequenceCompleted(@NonNull android.hardware.camera2.CameraExtensionSession, int);
method public void onCaptureStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, long);

View File

@@ -657,7 +657,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
*
* @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata
*/
private <TKey> List<TKey>
<TKey> List<TKey>
getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass, int[] filterTags,
boolean includeSynthetic) {

View File

@@ -30,6 +30,7 @@ import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.SizeList;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.ConditionVariable;
@@ -49,6 +50,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -785,8 +787,8 @@ public final class CameraExtensionCharacteristics {
if (latencyRange != null) {
return new Range(latencyRange.min, latencyRange.max);
}
}
} catch (RemoteException e) {
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+ " not respond!");
} finally {
@@ -795,4 +797,142 @@ public final class CameraExtensionCharacteristics {
return null;
}
/**
* Returns the set of keys supported by a {@link CaptureRequest} submitted in a
* {@link CameraExtensionSession} with a given extension type.
*
* <p>The set returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
* @param extension the extension type
*
* @return non-modifiable set of capture keys supported by camera extension session initialized
* with the given extension type.
* @throws IllegalArgumentException in case of unsupported extension.
*/
@NonNull
public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) {
long clientId = registerClient(mContext);
if (clientId < 0) {
throw new IllegalArgumentException("Unsupported extensions");
}
HashSet<CaptureRequest.Key> ret = new HashSet<>();
try {
if (!isExtensionSupported(mCameraId, extension, mChars)) {
throw new IllegalArgumentException("Unsupported extension");
}
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
extenders.second.init(mCameraId, mChars.getNativeMetadata());
CameraMetadataNative captureRequestMeta =
extenders.second.getAvailableCaptureRequestKeys();
if (captureRequestMeta != null) {
int[] requestKeys = captureRequestMeta.get(
CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS);
if (requestKeys == null) {
throw new AssertionError("android.request.availableRequestKeys must be non-null"
+ " in the characteristics");
}
CameraCharacteristics requestChars = new CameraCharacteristics(captureRequestMeta);
Object crKey = CaptureRequest.Key.class;
Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped,
requestKeys, /*includeSynthetic*/ false));
}
// Jpeg quality and orientation must always be supported
if (!ret.contains(CaptureRequest.JPEG_QUALITY)) {
ret.add(CaptureRequest.JPEG_QUALITY);
}
if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) {
ret.add(CaptureRequest.JPEG_ORIENTATION);
}
extenders.second.onDeInit();
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture request keys!");
} finally {
unregisterClient(clientId);
}
return Collections.unmodifiableSet(ret);
}
/**
* Returns the set of keys supported by a {@link CaptureResult} passed as an argument to
* {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}.
*
* <p>The set returned is not modifiable, so any attempts to modify it will throw
* a {@code UnsupportedOperationException}.</p>
*
* <p>In case the set is empty, then the extension is not able to support any capture results
* and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}
* callback will not be fired.</p>
*
* @param extension the extension type
*
* @return non-modifiable set of capture result keys supported by camera extension session
* initialized with the given extension type.
* @throws IllegalArgumentException in case of unsupported extension.
*/
@NonNull
public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) {
long clientId = registerClient(mContext);
if (clientId < 0) {
throw new IllegalArgumentException("Unsupported extensions");
}
HashSet<CaptureResult.Key> ret = new HashSet<>();
try {
if (!isExtensionSupported(mCameraId, extension, mChars)) {
throw new IllegalArgumentException("Unsupported extension");
}
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
extenders.second.init(mCameraId, mChars.getNativeMetadata());
CameraMetadataNative captureResultMeta =
extenders.second.getAvailableCaptureResultKeys();
if (captureResultMeta != null) {
int[] resultKeys = captureResultMeta.get(
CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS);
if (resultKeys == null) {
throw new AssertionError("android.request.availableResultKeys must be non-null "
+ "in the characteristics");
}
CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta);
Object crKey = CaptureResult.Key.class;
Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped,
resultKeys, /*includeSynthetic*/ false));
// Jpeg quality, orientation and sensor timestamp must always be supported
if (!ret.contains(CaptureResult.JPEG_QUALITY)) {
ret.add(CaptureResult.JPEG_QUALITY);
}
if (!ret.contains(CaptureResult.JPEG_ORIENTATION)) {
ret.add(CaptureResult.JPEG_ORIENTATION);
}
if (!ret.contains(CaptureResult.SENSOR_TIMESTAMP)) {
ret.add(CaptureResult.SENSOR_TIMESTAMP);
}
}
extenders.second.onDeInit();
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture result keys!");
} finally {
unregisterClient(clientId);
}
return Collections.unmodifiableSet(ret);
}
}

View File

@@ -172,6 +172,32 @@ public abstract class CameraExtensionSession implements AutoCloseable {
int sequenceId) {
// default empty implementation
}
/**
* This method is called when an image capture has fully completed and all the
* result metadata is available.
*
* <p>This callback will only be called in case
* {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys} returns a valid
* non-empty list.</p>
*
* <p>The default implementation of this method does nothing.</p>
*
* @param session The session received during
* {@link StateCallback#onConfigured(CameraExtensionSession)}
* @param request The request that was given to the CameraDevice
* @param result The total output metadata from the capture, which only includes the
* capture result keys advertised as supported in
* {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys}.
*
* @see #capture
* @see #setRepeatingRequest
* @see CameraExtensionCharacteristics#getAvailableCaptureResultKeys
*/
public void onCaptureResultAvailable(@NonNull CameraExtensionSession session,
@NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
// default empty implementation
}
}
/**

View File

@@ -17,6 +17,7 @@ package android.hardware.camera2.extension;
import android.view.Surface;
import android.hardware.camera2.extension.CaptureBundle;
import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.Size;
/** @hide */
@@ -25,5 +26,5 @@ interface ICaptureProcessorImpl
void onOutputSurface(in Surface surface, int imageFormat);
void onResolutionUpdate(in Size size);
void onImageFormatUpdate(int imageFormat);
void process(in List<CaptureBundle> capturelist);
void process(in List<CaptureBundle> capturelist, in IProcessResultImpl resultCallback);
}

View File

@@ -39,4 +39,6 @@ interface IImageCaptureExtenderImpl
int getMaxCaptureStage();
@nullable List<SizeList> getSupportedResolutions();
LatencyRange getEstimatedCaptureLatencyRange(in Size outputSize);
CameraMetadataNative getAvailableCaptureRequestKeys();
CameraMetadataNative getAvailableCaptureResultKeys();
}

View File

@@ -17,6 +17,7 @@ package android.hardware.camera2.extension;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.view.Surface;
import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.extension.Size;
@@ -26,5 +27,6 @@ interface IPreviewImageProcessorImpl
void onOutputSurface(in Surface surface, int imageFormat);
void onResolutionUpdate(in Size size);
void onImageFormatUpdate(int imageFormat);
void process(in ParcelImage image, in CameraMetadataNative result, int sequenceId);
void process(in ParcelImage image, in CameraMetadataNative result, int sequenceId,
in IProcessResultImpl resultCallback);
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2022, 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.extension;
import android.hardware.camera2.impl.CameraMetadataNative;
/** @hide */
interface IProcessResultImpl
{
void onCaptureCompleted(long shutterTimestamp, in CameraMetadataNative results);
}

View File

@@ -105,7 +105,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
@RequiresPermission(android.Manifest.permission.CAMERA)
public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
@NonNull CameraDevice cameraDevice, @NonNull Context ctx,
@NonNull ExtensionSessionConfiguration config)
@NonNull ExtensionSessionConfiguration config, int sessionId)
throws CameraAccessException, RemoteException {
long clientId = CameraExtensionCharacteristics.registerClient(ctx);
if (clientId < 0) {

View File

@@ -2495,10 +2495,10 @@ public class CameraDeviceImpl extends CameraDevice
if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) {
mCurrentAdvancedExtensionSession =
CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession(
this, mContext, extensionConfiguration);
this, mContext, extensionConfiguration, mNextSessionId++);
} else {
mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(
this, mContext, extensionConfiguration);
this, mContext, extensionConfiguration, mNextSessionId++);
}
} catch (RemoteException e) {
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);

View File

@@ -19,6 +19,7 @@ package android.hardware.camera2.impl;
import android.annotation.SuppressLint;
import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
@@ -114,12 +115,12 @@ public class CameraExtensionForwardProcessor {
}
}
public void process(ParcelImage image, TotalCaptureResult totalCaptureResult)
throws RemoteException {
public void process(ParcelImage image, TotalCaptureResult totalCaptureResult,
IProcessResultImpl resultCallback) throws RemoteException {
if ((mIntermediateSurface != null) && (mIntermediateSurface.isValid()) &&
!mOutputAbandoned) {
mProcessor.process(image, totalCaptureResult.getNativeMetadata(),
totalCaptureResult.getSequenceId());
totalCaptureResult.getSequenceId(), resultCallback);
}
}

View File

@@ -24,6 +24,7 @@ import android.graphics.ImageFormat;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.extension.CaptureBundle;
import android.hardware.camera2.extension.ICaptureProcessorImpl;
import android.hardware.camera2.extension.IProcessResultImpl;
import android.media.Image;
import android.media.Image.Plane;
import android.media.ImageReader;
@@ -183,11 +184,13 @@ public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
int cropLeft, int cropTop, int cropRight, int cropBottom,
int rot90);
public void process(List<CaptureBundle> captureBundle) throws RemoteException {
@Override
public void process(List<CaptureBundle> captureBundle, IProcessResultImpl captureCallback)
throws RemoteException {
JpegParameters jpegParams = getJpegParameters(captureBundle);
try {
mJpegParameters.add(jpegParams);
mProcessor.process(captureBundle);
mProcessor.process(captureBundle, captureCallback);
} catch (Exception e) {
mJpegParameters.remove(jpegParams);
throw e;

View File

@@ -36,6 +36,7 @@ import android.hardware.camera2.extension.ICaptureProcessorImpl;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.TotalCaptureResult;
@@ -67,6 +68,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
public final class CameraExtensionSessionImpl extends CameraExtensionSession {
@@ -83,6 +85,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private final StateCallback mCallbacks;
private final List<Size> mSupportedPreviewSizes;
private final InitializeSessionHandler mInitializeHandler;
private final int mSessionId;
private final Set<CaptureRequest.Key> mSupportedRequestKeys;
private final Set<CaptureResult.Key> mSupportedResultKeys;
private boolean mCaptureResultsSupported;
private CameraCaptureSession mCaptureSession = null;
private Surface mCameraRepeatingSurface, mClientRepeatingRequestSurface;
@@ -121,7 +127,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
public static CameraExtensionSessionImpl createCameraExtensionSession(
@NonNull CameraDevice cameraDevice,
@NonNull Context ctx,
@NonNull ExtensionSessionConfiguration config)
@NonNull ExtensionSessionConfiguration config,
int sessionId)
throws CameraAccessException, RemoteException {
long clientId = CameraExtensionCharacteristics.registerClient(ctx);
if (clientId < 0) {
@@ -202,7 +209,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
repeatingRequestSurface,
burstCaptureSurface,
config.getStateCallback(),
config.getExecutor());
config.getExecutor(),
sessionId,
extensionChars.getAvailableCaptureRequestKeys(config.getExtension()),
extensionChars.getAvailableCaptureResultKeys(config.getExtension()));
session.initialize();
@@ -217,7 +227,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
@Nullable Surface repeatingRequestSurface,
@Nullable Surface burstCaptureSurface,
@NonNull StateCallback callback,
@NonNull Executor executor) {
@NonNull Executor executor,
int sessionId,
@NonNull Set<CaptureRequest.Key> requestKeys,
@Nullable Set<CaptureResult.Key> resultKeys) {
mExtensionClientId = extensionClientId;
mImageExtender = imageExtender;
mPreviewExtender = previewExtender;
@@ -232,6 +245,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
mHandler = new Handler(mHandlerThread.getLooper());
mInitialized = false;
mInitializeHandler = new InitializeSessionHandler();
mSessionId = sessionId;
mSupportedRequestKeys = requestKeys;
mSupportedResultKeys = resultKeys;
mCaptureResultsSupported = !resultKeys.isEmpty();
}
private void initializeRepeatingRequestPipeline() throws RemoteException {
@@ -488,7 +505,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
return captureStageList;
}
private static List<CaptureRequest> createBurstRequest(CameraDevice cameraDevice,
private List<CaptureRequest> createBurstRequest(CameraDevice cameraDevice,
List<CaptureStageImpl> captureStageList, CaptureRequest clientRequest,
Surface target, int captureTemplate, Map<CaptureRequest, Integer> captureMap) {
CaptureRequest.Builder requestBuilder;
@@ -500,16 +517,13 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
return null;
}
// This will override the extension capture stage jpeg parameters with the user set
// jpeg quality and rotation. This will guarantee that client configured jpeg
// parameters always have highest priority.
Integer jpegRotation = clientRequest.get(CaptureRequest.JPEG_ORIENTATION);
if (jpegRotation != null) {
captureStage.parameters.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
}
Byte jpegQuality = clientRequest.get(CaptureRequest.JPEG_QUALITY);
if (jpegQuality != null) {
captureStage.parameters.set(CaptureRequest.JPEG_QUALITY, jpegQuality);
// This will guarantee that client configured
// parameters always have the highest priority.
for (CaptureRequest.Key requestKey : mSupportedRequestKeys){
Object value = clientRequest.get(requestKey);
if (value != null) {
captureStage.parameters.set(requestKey, value);
}
}
requestBuilder.addTarget(target);
@@ -522,10 +536,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
return ret;
}
private static CaptureRequest createRequest(CameraDevice cameraDevice,
List<CaptureStageImpl> captureStageList,
Surface target,
int captureTemplate) throws CameraAccessException {
private CaptureRequest createRequest(CameraDevice cameraDevice,
List<CaptureStageImpl> captureStageList, Surface target, int captureTemplate,
CaptureRequest clientRequest) throws CameraAccessException {
CaptureRequest.Builder requestBuilder;
requestBuilder = cameraDevice.createCaptureRequest(captureTemplate);
if (target != null) {
@@ -533,14 +546,35 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
CaptureRequest ret = requestBuilder.build();
CameraMetadataNative nativeMeta = ret.getNativeMetadata();
for (CaptureStageImpl captureStage : captureStageList) {
if (captureStage != null) {
CameraMetadataNative.update(ret.getNativeMetadata(), captureStage.parameters);
CameraMetadataNative.update(nativeMeta, captureStage.parameters);
}
}
if (clientRequest != null) {
// This will guarantee that client configured
// parameters always have the highest priority.
for (CaptureRequest.Key requestKey : mSupportedRequestKeys) {
Object value = clientRequest.get(requestKey);
if (value != null) {
nativeMeta.set(requestKey, value);
}
}
}
return ret;
}
private CaptureRequest createRequest(CameraDevice cameraDevice,
List<CaptureStageImpl> captureStageList,
Surface target,
int captureTemplate) throws CameraAccessException {
return createRequest(cameraDevice, captureStageList, target, captureTemplate,
/*clientRequest*/ null);
}
@Override
public int capture(@NonNull CaptureRequest request,
@NonNull Executor executor,
@@ -634,12 +668,17 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
private int setRepeatingRequest(CaptureStageImpl captureStage,
CameraCaptureSession.CaptureCallback requestHandler)
CameraCaptureSession.CaptureCallback requestHandler) throws CameraAccessException {
return setRepeatingRequest(captureStage, requestHandler, /*clientRequest*/ null);
}
private int setRepeatingRequest(CaptureStageImpl captureStage,
CameraCaptureSession.CaptureCallback requestHandler, CaptureRequest clientRequest)
throws CameraAccessException {
ArrayList<CaptureStageImpl> captureStageList = new ArrayList<>();
captureStageList.add(captureStage);
CaptureRequest repeatingRequest = createRequest(mCameraDevice,
captureStageList, mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW);
CaptureRequest repeatingRequest = createRequest(mCameraDevice, captureStageList,
mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW, clientRequest);
return mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler);
}
@@ -848,6 +887,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private ImageCallback mImageCallback = null;
private boolean mCaptureFailed = false;
private CaptureResultHandler mCaptureResultHandler = null;
public BurstRequestHandler(@NonNull CaptureRequest request, @NonNull Executor executor,
@NonNull ExtensionCaptureCallback callbacks,
@@ -968,20 +1008,18 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
if (timestamp != null) {
if (mCaptureResultsSupported && (mCaptureResultHandler == null)) {
mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
mCallbacks, result.getSessionId());
}
if (mImageProcessor != null) {
if (mCapturePendingMap.indexOfKey(timestamp) >= 0) {
Image img = mCapturePendingMap.get(timestamp).first;
mCaptureStageMap.put(stageId,
new Pair<>(img,
result));
mCaptureStageMap.put(stageId, new Pair<>(img, result));
checkAndFireBurstProcessing();
} else {
mCapturePendingMap.put(timestamp,
new Pair<>(null,
stageId));
mCaptureStageMap.put(stageId,
new Pair<>(null,
result));
mCapturePendingMap.put(timestamp, new Pair<>(null, stageId));
mCaptureStageMap.put(stageId, new Pair<>(null, result));
}
} else {
mCaptureRequestMap.clear();
@@ -991,6 +1029,18 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
() -> mCallbacks
.onCaptureProcessStarted(CameraExtensionSessionImpl.this,
mClientRequest));
if (mCaptureResultHandler != null) {
CameraMetadataNative captureResults = new CameraMetadataNative();
for (CaptureResult.Key key : mSupportedResultKeys) {
Object value = result.get(key);
if (value != null) {
captureResults.set(key, value);
}
}
mCaptureResultHandler.onCaptureCompleted(timestamp,
captureResults);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1018,7 +1068,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap,
jpegOrientation, jpegQuality);
try {
mImageProcessor.process(captureList);
mImageProcessor.process(captureList, mCaptureResultHandler);
} catch (RemoteException e) {
Log.e(TAG, "Failed to process multi-frame request! Extension service "
+ "does not respond!");
@@ -1233,6 +1283,43 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
}
private class CaptureResultHandler extends IProcessResultImpl.Stub {
private final Executor mExecutor;
private final ExtensionCaptureCallback mCallbacks;
private final CaptureRequest mClientRequest;
private final int mRequestId;
public CaptureResultHandler(@NonNull CaptureRequest clientRequest,
@NonNull Executor executor, @NonNull ExtensionCaptureCallback listener,
int requestId) {
mClientRequest = clientRequest;
mExecutor = executor;
mCallbacks = listener;
mRequestId = requestId;
}
@Override
public void onCaptureCompleted(long shutterTimestamp, CameraMetadataNative result) {
if (result == null) {
Log.e(TAG,"Invalid capture result!");
return;
}
result.set(CaptureResult.SENSOR_TIMESTAMP, shutterTimestamp);
TotalCaptureResult totalResult = new TotalCaptureResult(mCameraDevice.getId(), result,
mClientRequest, mRequestId, shutterTimestamp, new ArrayList<CaptureResult>(),
mSessionId, new PhysicalCaptureResultInfo[0]);
final long ident = Binder.clearCallingIdentity();
try {
mExecutor.execute(
() -> mCallbacks.onCaptureResultAvailable(CameraExtensionSessionImpl.this,
mClientRequest, totalResult));
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
// This handler can operate in two modes:
// 1) Using valid client callbacks, which means camera buffers will be propagated the
// registered output surfaces and clients will be notified accordingly.
@@ -1247,6 +1334,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
private OnImageAvailableListener mImageCallback = null;
private LongSparseArray<Pair<Image, TotalCaptureResult>> mPendingResultMap =
new LongSparseArray<>();
private CaptureResultHandler mCaptureResultHandler = null;
private boolean mRequestUpdatedNeeded = false;
@@ -1380,6 +1468,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
synchronized (mInterfaceLock) {
final Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
if (timestamp != null) {
if (mCaptureResultsSupported && mClientNotificationsEnabled &&
(mCaptureResultHandler == null)) {
mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
mCallbacks, result.getSessionId());
}
if (mPreviewProcessorType ==
IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) {
CaptureStageImpl captureStage = null;
@@ -1392,7 +1485,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
}
if (captureStage != null) {
try {
setRepeatingRequest(captureStage, this);
setRepeatingRequest(captureStage, this, request);
mRequestUpdatedNeeded = true;
} catch (IllegalStateException e) {
// This is possible in case the camera device closes and the
@@ -1411,7 +1504,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
ParcelImage parcelImage = initializeParcelImage(
mPendingResultMap.get(timestamp).first);
try {
mPreviewImageProcessor.process(parcelImage, result);
mPreviewImageProcessor.process(parcelImage, result,
mCaptureResultHandler);
} catch (RemoteException e) {
processStatus = false;
Log.e(TAG, "Extension service does not respond during " +
@@ -1449,6 +1543,19 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
.onCaptureProcessStarted(
CameraExtensionSessionImpl.this,
mClientRequest));
if ((mCaptureResultHandler != null) && (mPreviewProcessorType !=
IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR)) {
CameraMetadataNative captureResults =
new CameraMetadataNative();
for (CaptureResult.Key key : mSupportedResultKeys) {
Object value = result.get(key);
if (value != null) {
captureResults.set(key, value);
}
}
mCaptureResultHandler.onCaptureCompleted(timestamp,
captureResults);
}
} else {
mExecutor.execute(
() -> mCallbacks
@@ -1592,7 +1699,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
ParcelImage parcelImage = initializeParcelImage(img);
try {
mPreviewImageProcessor.process(parcelImage,
mPendingResultMap.get(timestamp).second);
mPendingResultMap.get(timestamp).second, mCaptureResultHandler);
} catch (RemoteException e) {
processStatus = false;
Log.e(TAG, "Extension service does not respond during " +

View File

@@ -43,6 +43,7 @@ import android.hardware.camera2.extension.IImageProcessorImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.IRequestCallback;
import android.hardware.camera2.extension.IRequestProcessorImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
@@ -90,6 +91,7 @@ import androidx.camera.extensions.impl.NightPreviewExtenderImpl;
import androidx.camera.extensions.impl.PreviewExtenderImpl;
import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType;
import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
import androidx.camera.extensions.impl.ProcessResultImpl;
import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl;
@@ -124,14 +126,17 @@ public class CameraExtensionsProxyService extends Service {
private static final String LATEST_VERSION = "1.2.0";
private static final String NON_INIT_VERSION_PREFIX = "1.0";
private static final String ADVANCED_VERSION_PREFIX = "1.2";
private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX,
"1.1", NON_INIT_VERSION_PREFIX};
private static final String RESULTS_VERSION_PREFIX = "1.3";
private static final String[] SUPPORTED_VERSION_PREFIXES = {RESULTS_VERSION_PREFIX,
ADVANCED_VERSION_PREFIX, "1.1", NON_INIT_VERSION_PREFIX};
private static final boolean EXTENSIONS_PRESENT = checkForExtensions();
private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ?
(new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null;
private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI();
private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT &&
(!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX));
private static final boolean RESULT_API_SUPPORTED = EXTENSIONS_PRESENT &&
(EXTENSIONS_VERSION.startsWith(RESULTS_VERSION_PREFIX));
private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
@@ -1242,7 +1247,7 @@ public class CameraExtensionsProxyService extends Service {
}
if (processor != null) {
return new PreviewImageProcessorImplStub(processor);
return new PreviewImageProcessorImplStub(processor, mCameraId);
}
return null;
@@ -1332,7 +1337,7 @@ public class CameraExtensionsProxyService extends Service {
public ICaptureProcessorImpl getCaptureProcessor() {
CaptureProcessorImpl captureProcessor = mImageExtender.getCaptureProcessor();
if (captureProcessor != null) {
return new CaptureProcessorImplStub(captureProcessor);
return new CaptureProcessorImplStub(captureProcessor, mCameraId);
}
return null;
@@ -1390,13 +1395,97 @@ public class CameraExtensionsProxyService extends Service {
return null;
}
@Override
public CameraMetadataNative getAvailableCaptureRequestKeys() {
if (RESULT_API_SUPPORTED) {
List<CaptureRequest.Key> supportedCaptureKeys =
mImageExtender.getAvailableCaptureRequestKeys();
if ((supportedCaptureKeys != null) && !supportedCaptureKeys.isEmpty()) {
CameraMetadataNative ret = new CameraMetadataNative();
long vendorId = mMetadataVendorIdMap.containsKey(mCameraId) ?
mMetadataVendorIdMap.get(mCameraId) : Long.MAX_VALUE;
ret.setVendorId(vendorId);
int requestKeyTags [] = new int[supportedCaptureKeys.size()];
int i = 0;
for (CaptureRequest.Key key : supportedCaptureKeys) {
requestKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
}
ret.set(CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS, requestKeyTags);
return ret;
}
}
return null;
}
@Override
public CameraMetadataNative getAvailableCaptureResultKeys() {
if (RESULT_API_SUPPORTED) {
List<CaptureResult.Key> supportedResultKeys =
mImageExtender.getAvailableCaptureResultKeys();
if ((supportedResultKeys != null) && !supportedResultKeys.isEmpty()) {
CameraMetadataNative ret = new CameraMetadataNative();
long vendorId = mMetadataVendorIdMap.containsKey(mCameraId) ?
mMetadataVendorIdMap.get(mCameraId) : Long.MAX_VALUE;
ret.setVendorId(vendorId);
int resultKeyTags [] = new int[supportedResultKeys.size()];
int i = 0;
for (CaptureResult.Key key : supportedResultKeys) {
resultKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
}
ret.set(CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS, resultKeyTags);
return ret;
}
}
return null;
}
}
private class ProcessResultCallback implements ProcessResultImpl {
private final IProcessResultImpl mProcessResult;
private final String mCameraId;
private ProcessResultCallback(IProcessResultImpl processResult, String cameraId) {
mProcessResult = processResult;
mCameraId = cameraId;
}
@Override
public void onCaptureCompleted(long shutterTimestamp,
List<Pair<CaptureResult.Key, Object>> result) {
if (result == null) {
Log.e(TAG, "Invalid capture result received!");
}
CameraMetadataNative captureResults = new CameraMetadataNative();
if (mMetadataVendorIdMap.containsKey(mCameraId)) {
captureResults.setVendorId(mMetadataVendorIdMap.get(mCameraId));
}
for (Pair<CaptureResult.Key, Object> pair : result) {
captureResults.set(pair.first, pair.second);
}
try {
mProcessResult.onCaptureCompleted(shutterTimestamp, captureResults);
} catch (RemoteException e) {
Log.e(TAG, "Remote client doesn't respond to capture results!");
}
}
}
private class CaptureProcessorImplStub extends ICaptureProcessorImpl.Stub {
private final CaptureProcessorImpl mCaptureProcessor;
private final String mCameraId;
public CaptureProcessorImplStub(CaptureProcessorImpl captureProcessor) {
public CaptureProcessorImplStub(CaptureProcessorImpl captureProcessor, String cameraId) {
mCaptureProcessor = captureProcessor;
mCameraId = cameraId;
}
@Override
@@ -1415,7 +1504,7 @@ public class CameraExtensionsProxyService extends Service {
}
@Override
public void process(List<CaptureBundle> captureList) {
public void process(List<CaptureBundle> captureList, IProcessResultImpl resultCallback) {
HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap = new HashMap<>();
for (CaptureBundle captureBundle : captureList) {
captureMap.put(captureBundle.stage, new Pair<> (
@@ -1424,7 +1513,14 @@ public class CameraExtensionsProxyService extends Service {
captureBundle.sequenceId)));
}
if (!captureMap.isEmpty()) {
mCaptureProcessor.process(captureMap);
if ((resultCallback != null) && (RESULT_API_SUPPORTED)) {
mCaptureProcessor.process(captureMap, new ProcessResultCallback(resultCallback,
mCameraId), null /*executor*/);
} else if (resultCallback == null) {
mCaptureProcessor.process(captureMap);
} else {
Log.e(TAG, "Process requests with capture results are not supported!");
}
} else {
Log.e(TAG, "Process request with absent capture stages!");
}
@@ -1433,9 +1529,11 @@ public class CameraExtensionsProxyService extends Service {
private class PreviewImageProcessorImplStub extends IPreviewImageProcessorImpl.Stub {
private final PreviewImageProcessorImpl mProcessor;
private final String mCameraId;
public PreviewImageProcessorImplStub(PreviewImageProcessorImpl processor) {
public PreviewImageProcessorImplStub(PreviewImageProcessorImpl processor, String cameraId) {
mProcessor = processor;
mCameraId = cameraId;
}
@Override
@@ -1455,9 +1553,17 @@ public class CameraExtensionsProxyService extends Service {
@Override
public void process(android.hardware.camera2.extension.ParcelImage image,
CameraMetadataNative result, int sequenceId) {
mProcessor.process(new ExtensionImage(image),
new TotalCaptureResult(result, sequenceId));
CameraMetadataNative result, int sequenceId, IProcessResultImpl resultCallback) {
if ((resultCallback != null) && RESULT_API_SUPPORTED) {
mProcessor.process(new ExtensionImage(image),
new TotalCaptureResult(result, sequenceId),
new ProcessResultCallback(resultCallback, mCameraId), null /*executor*/);
} else if (resultCallback == null) {
mProcessor.process(new ExtensionImage(image),
new TotalCaptureResult(result, sequenceId));
} else {
}
}
}