Merge "Camera: Initial support for Camera2 extension"
This commit is contained in:
committed by
Android (Google) Code Review
commit
af68bebbaa
@@ -17631,6 +17631,7 @@ package android.hardware.camera2 {
|
||||
method public void createCaptureSession(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
|
||||
method @Deprecated public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
method @Deprecated public abstract void createConstrainedHighSpeedCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
method public void createExtensionSession(@NonNull android.hardware.camera2.params.ExtensionSessionConfiguration) throws android.hardware.camera2.CameraAccessException;
|
||||
method @NonNull public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(@NonNull android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
|
||||
method @Deprecated public abstract void createReprocessableCaptureSession(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
method @Deprecated public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
|
||||
@@ -17662,8 +17663,44 @@ package android.hardware.camera2 {
|
||||
field public static final int ERROR_MAX_CAMERAS_IN_USE = 2; // 0x2
|
||||
}
|
||||
|
||||
public final class CameraExtensionCharacteristics {
|
||||
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);
|
||||
method @NonNull public java.util.List<java.lang.Integer> getSupportedExtensions();
|
||||
field public static final int EXTENSION_AUTOMATIC = 0; // 0x0
|
||||
field public static final int EXTENSION_BEAUTY = 1; // 0x1
|
||||
field public static final int EXTENSION_BOKEH = 2; // 0x2
|
||||
field public static final int EXTENSION_HDR = 3; // 0x3
|
||||
field public static final int EXTENSION_NIGHT = 4; // 0x4
|
||||
}
|
||||
|
||||
public abstract class CameraExtensionSession implements java.lang.AutoCloseable {
|
||||
method public int capture(@NonNull android.hardware.camera2.CaptureRequest, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.ExtensionCaptureCallback) throws android.hardware.camera2.CameraAccessException;
|
||||
method public void close() throws android.hardware.camera2.CameraAccessException;
|
||||
method @NonNull public android.hardware.camera2.CameraDevice getDevice();
|
||||
method public int setRepeatingRequest(@NonNull android.hardware.camera2.CaptureRequest, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.ExtensionCaptureCallback) throws android.hardware.camera2.CameraAccessException;
|
||||
method public void stopRepeating() throws android.hardware.camera2.CameraAccessException;
|
||||
}
|
||||
|
||||
public abstract static class CameraExtensionSession.ExtensionCaptureCallback {
|
||||
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 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);
|
||||
}
|
||||
|
||||
public abstract static class CameraExtensionSession.StateCallback {
|
||||
ctor public CameraExtensionSession.StateCallback();
|
||||
method public void onClosed(@NonNull android.hardware.camera2.CameraExtensionSession);
|
||||
method public abstract void onConfigureFailed(@NonNull android.hardware.camera2.CameraExtensionSession);
|
||||
method public abstract void onConfigured(@NonNull android.hardware.camera2.CameraExtensionSession);
|
||||
}
|
||||
|
||||
public final class CameraManager {
|
||||
method @NonNull public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException;
|
||||
method @NonNull public android.hardware.camera2.CameraExtensionCharacteristics getCameraExtensionCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException;
|
||||
method @NonNull public String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
|
||||
method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentCameraIds() throws android.hardware.camera2.CameraAccessException;
|
||||
method @RequiresPermission(android.Manifest.permission.CAMERA) public boolean isConcurrentSessionConfigurationSupported(@NonNull java.util.Map<java.lang.String,android.hardware.camera2.params.SessionConfiguration>) throws android.hardware.camera2.CameraAccessException;
|
||||
@@ -18173,6 +18210,14 @@ package android.hardware.camera2.params {
|
||||
method public android.util.Rational getElement(int, int);
|
||||
}
|
||||
|
||||
public final class ExtensionSessionConfiguration {
|
||||
ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback);
|
||||
method @NonNull public java.util.concurrent.Executor getExecutor();
|
||||
method public int getExtension();
|
||||
method @NonNull public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
|
||||
method @NonNull public android.hardware.camera2.CameraExtensionSession.StateCallback getStateCallback();
|
||||
}
|
||||
|
||||
public final class Face {
|
||||
method public android.graphics.Rect getBounds();
|
||||
method public int getId();
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.hardware.camera2.params.ExtensionSessionConfiguration;
|
||||
import android.hardware.camera2.params.InputConfiguration;
|
||||
import android.hardware.camera2.params.OutputConfiguration;
|
||||
import android.hardware.camera2.params.SessionConfiguration;
|
||||
@@ -350,6 +351,66 @@ public abstract class CameraDevice implements AutoCloseable {
|
||||
@Nullable Handler handler)
|
||||
throws CameraAccessException;
|
||||
|
||||
/**
|
||||
* Initialize a specific device-specific extension augmented camera capture
|
||||
* session.
|
||||
*
|
||||
* <p>Extension sessions can be used to enable device-specific operation modes like
|
||||
* {@link CameraExtensionCharacteristics#EXTENSION_NIGHT} or
|
||||
* {@link CameraExtensionCharacteristics#EXTENSION_HDR}. These modes are less flexible than the
|
||||
* full camera API, but enable access to more sophisticated processing algorithms that can
|
||||
* capture multi-frame bursts to generate single output images. To query for available
|
||||
* extensions on this device call
|
||||
* {@link CameraExtensionCharacteristics#getSupportedExtensions()}.</p>
|
||||
*
|
||||
* <p>This method will also trigger the setup of the internal
|
||||
* processing pipeline for extension augmented preview and multi-frame
|
||||
* still capture.</p>
|
||||
*
|
||||
* <p>If a prior CameraCaptureSession already exists when this method is called, the previous
|
||||
* session will no longer be able to accept new capture requests and will be closed. Any
|
||||
* in-progress capture requests made on the prior session will be completed before it's closed.
|
||||
* </p>
|
||||
*
|
||||
* <p>The CameraExtensionSession will be active until the client
|
||||
* either calls CameraExtensionSession.close() or creates a new camera
|
||||
* capture session. In both cases all internal resources will be
|
||||
* released, continuous repeating requests stopped and any pending
|
||||
* multi-frame capture requests flushed.</p>
|
||||
*
|
||||
* <p>Note that the CameraExtensionSession currently supports at most two
|
||||
* multi frame capture surface formats: ImageFormat.YUV_420_888 and
|
||||
* ImageFormat.JPEG. Clients must query the multi-frame capture format support using
|
||||
* {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, int)}.
|
||||
* For repeating requests CameraExtensionSession supports only
|
||||
* {@link android.graphics.SurfaceTexture} as output. Clients can query the supported resolution
|
||||
* for the repeating request output using
|
||||
* {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, Class)
|
||||
* getExtensionSupportedSizes(..., Class)}.</p>
|
||||
*
|
||||
* <p>At the very minimum the initialization expects either one valid output
|
||||
* surface for repeating or one valid output for high-quality single requests registered in the
|
||||
* outputs argument of the extension configuration argument. At the maximum the initialization
|
||||
* will accept two valid output surfaces, one for repeating and the other for single requests.
|
||||
* Additional unsupported surfaces passed to ExtensionSessionConfiguration will cause an
|
||||
* {@link IllegalArgumentException} to be thrown.</p>
|
||||
*
|
||||
* @param extensionConfiguration extension configuration
|
||||
* @throws IllegalArgumentException If both the preview and still
|
||||
* capture surfaces are not set or invalid, or if any of the
|
||||
* registered surfaces do not meet the device-specific
|
||||
* extension requirements such as dimensions and/or
|
||||
* (output format)/(surface type), or if the extension is not
|
||||
* supported.
|
||||
* @see CameraExtensionCharacteristics#getSupportedExtensions
|
||||
* @see CameraExtensionCharacteristics#getExtensionSupportedSizes
|
||||
*/
|
||||
public void createExtensionSession(
|
||||
@NonNull ExtensionSessionConfiguration extensionConfiguration)
|
||||
throws CameraAccessException {
|
||||
throw new UnsupportedOperationException("No default implementation");
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard camera operation mode.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
import android.hardware.camera2.extension.IPreviewExtenderImpl;
|
||||
import android.hardware.camera2.extension.SizeList;
|
||||
import android.hardware.camera2.params.ExtensionSessionConfiguration;
|
||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.Size;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>Allows clients to query availability and supported resolutions of camera extensions.</p>
|
||||
*
|
||||
* <p>Camera extensions give camera clients access to device-specific algorithms and sequences that
|
||||
* can improve the overall image quality of snapshots in various cases such as low light, selfies,
|
||||
* portraits, and scenes that can benefit from enhanced dynamic range. Often such sophisticated
|
||||
* processing sequences will rely on multiple camera frames as input and will produce a single
|
||||
* output.</p>
|
||||
*
|
||||
* <p>Camera extensions are not guaranteed to be present on all devices so camera clients must
|
||||
* query for their availability via {@link CameraExtensionCharacteristics#getSupportedExtensions()}.
|
||||
* </p>
|
||||
*
|
||||
* <p>In order to use any available camera extension, camera clients must create a corresponding
|
||||
* {@link CameraExtensionSession} via
|
||||
* {@link CameraDevice#createExtensionSession(ExtensionSessionConfiguration)}</p>
|
||||
*
|
||||
* <p>Camera clients must be aware that device-specific camera extensions may support only a
|
||||
* subset of the available camera resolutions and must first query
|
||||
* {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, int)} for supported
|
||||
* single high-quality request output sizes and
|
||||
* {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, Class)} for supported
|
||||
* repeating request output sizes.</p>
|
||||
*
|
||||
* @see CameraManager#getCameraExtensionCharacteristics(String)
|
||||
*/
|
||||
public final class CameraExtensionCharacteristics {
|
||||
private static final String TAG = "CameraExtensionCharacteristics";
|
||||
|
||||
/**
|
||||
* Device-specific extension implementation for automatic selection of particular extension
|
||||
* such as HDR or NIGHT depending on the current lighting and environment conditions.
|
||||
*/
|
||||
public static final int EXTENSION_AUTOMATIC = 0;
|
||||
|
||||
/**
|
||||
* Device-specific extension implementation which tends to smooth the skin and apply other
|
||||
* cosmetic effects to people's faces.
|
||||
*/
|
||||
public static final int EXTENSION_BEAUTY = 1;
|
||||
|
||||
/**
|
||||
* Device-specific extension implementation which can blur certain regions of the final image
|
||||
* thereby "enhancing" focus for all remaining non-blurred parts.
|
||||
*/
|
||||
public static final int EXTENSION_BOKEH = 2;
|
||||
|
||||
/**
|
||||
* Device-specific extension implementation for enhancing the dynamic range of the
|
||||
* final image.
|
||||
*/
|
||||
public static final int EXTENSION_HDR = 3;
|
||||
|
||||
/**
|
||||
* Device-specific extension implementation that aims to suppress noise and improve the
|
||||
* overall image quality under low light conditions.
|
||||
*/
|
||||
public static final int EXTENSION_NIGHT = 4;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(flag = true, value = {EXTENSION_AUTOMATIC,
|
||||
EXTENSION_BEAUTY,
|
||||
EXTENSION_BOKEH,
|
||||
EXTENSION_HDR,
|
||||
EXTENSION_NIGHT})
|
||||
public @interface Extension {
|
||||
}
|
||||
|
||||
/**
|
||||
* Default camera output in case additional processing from CameraX extensions is not needed
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int NON_PROCESSING_INPUT_FORMAT = ImageFormat.PRIVATE;
|
||||
|
||||
/**
|
||||
* CameraX extensions require YUV_420_888 as default input for processing at the moment
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int PROCESSING_INPUT_FORMAT = ImageFormat.YUV_420_888;
|
||||
|
||||
private static final @Extension
|
||||
int[] EXTENSION_LIST = new int[]{
|
||||
EXTENSION_AUTOMATIC,
|
||||
EXTENSION_BEAUTY,
|
||||
EXTENSION_BOKEH,
|
||||
EXTENSION_HDR,
|
||||
EXTENSION_NIGHT};
|
||||
|
||||
private final Context mContext;
|
||||
private final String mCameraId;
|
||||
private final CameraCharacteristics mChars;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public CameraExtensionCharacteristics(Context context, String cameraId,
|
||||
CameraCharacteristics chars) {
|
||||
mContext = context;
|
||||
mCameraId = cameraId;
|
||||
mChars = chars;
|
||||
}
|
||||
|
||||
private static List<Size> generateSupportedSizes(List<SizeList> sizesList,
|
||||
Integer format,
|
||||
StreamConfigurationMap streamMap) {
|
||||
// Per API contract it is assumed that the extension is able to support all
|
||||
// camera advertised sizes for a given format in case it doesn't return
|
||||
// a valid non-empty size list.
|
||||
ArrayList<Size> ret = new ArrayList<>();
|
||||
if ((sizesList != null) && (!sizesList.isEmpty())) {
|
||||
for (SizeList entry : sizesList) {
|
||||
if ((entry.format == format) && !entry.sizes.isEmpty()) {
|
||||
for (android.hardware.camera2.extension.Size sz : entry.sizes) {
|
||||
ret.add(new Size(sz.width, sz.height));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
Size[] supportedSizes = streamMap.getOutputSizes(format);
|
||||
if (supportedSizes != null) {
|
||||
ret.addAll(Arrays.asList(supportedSizes));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* A per-process global camera extension manager instance, to track and
|
||||
* initialize/release extensions depending on client activity.
|
||||
*/
|
||||
private static final class CameraExtensionManagerGlobal {
|
||||
private static final String TAG = "CameraExtensionManagerGlobal";
|
||||
private static final String PROXY_PACKAGE_NAME = "com.android.camera";
|
||||
private static final String PROXY_SERVICE_NAME =
|
||||
"com.android.camera.CameraExtensionsProxyService";
|
||||
|
||||
// Singleton instance
|
||||
private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
|
||||
new CameraExtensionManagerGlobal();
|
||||
private final Object mLock = new Object();
|
||||
private final int PROXY_SERVICE_DELAY_MS = 1000;
|
||||
private InitializerFuture mInitFuture = null;
|
||||
private ServiceConnection mConnection = null;
|
||||
private ICameraExtensionsProxyService mProxy = null;
|
||||
|
||||
// Singleton, don't allow construction
|
||||
private CameraExtensionManagerGlobal() {}
|
||||
|
||||
public static CameraExtensionManagerGlobal get() {
|
||||
return GLOBAL_CAMERA_MANAGER;
|
||||
}
|
||||
|
||||
private void connectToProxyLocked(Context ctx) {
|
||||
if (mConnection == null) {
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName(PROXY_PACKAGE_NAME, PROXY_SERVICE_NAME);
|
||||
mInitFuture = new InitializerFuture();
|
||||
mConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName component) {
|
||||
mInitFuture.setStatus(false);
|
||||
mConnection = null;
|
||||
mProxy = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName component, IBinder binder) {
|
||||
mProxy = ICameraExtensionsProxyService.Stub.asInterface(binder);
|
||||
mInitFuture.setStatus(true);
|
||||
}
|
||||
};
|
||||
ctx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE |
|
||||
Context.BIND_IMPORTANT | Context.BIND_ABOVE_CLIENT |
|
||||
Context.BIND_NOT_VISIBLE);
|
||||
|
||||
try {
|
||||
mInitFuture.get(PROXY_SERVICE_DELAY_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
Log.e(TAG, "Timed out while initializing proxy service!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class InitializerFuture implements Future<Boolean> {
|
||||
private volatile Boolean mStatus;
|
||||
ConditionVariable mCondVar = new ConditionVariable(/*opened*/false);
|
||||
|
||||
public void setStatus(boolean status) {
|
||||
mStatus = status;
|
||||
mCondVar.open();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return false; // don't allow canceling this task
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return false; // can never cancel this task
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return mStatus != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get() {
|
||||
mCondVar.block();
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(long timeout, TimeUnit unit) throws TimeoutException {
|
||||
long timeoutMs = unit.convert(timeout, TimeUnit.MILLISECONDS);
|
||||
if (!mCondVar.block(timeoutMs)) {
|
||||
throw new TimeoutException(
|
||||
"Failed to receive status after " + timeout + " " + unit);
|
||||
}
|
||||
|
||||
if (mStatus == null) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
return mStatus;
|
||||
}
|
||||
}
|
||||
|
||||
public long registerClient(Context ctx) {
|
||||
synchronized (mLock) {
|
||||
connectToProxyLocked(ctx);
|
||||
if (mProxy != null) {
|
||||
try {
|
||||
return mProxy.registerClient();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to initialize extension! Extension service does "
|
||||
+ " not respond!");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterClient(long clientId) {
|
||||
synchronized (mLock) {
|
||||
if (mProxy != null) {
|
||||
try {
|
||||
mProxy.unregisterClient(clientId);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to de-initialize extension! Extension service does"
|
||||
+ " not respond!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IPreviewExtenderImpl initializePreviewExtension(int extensionType)
|
||||
throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
if (mProxy != null) {
|
||||
return mProxy.initializePreviewExtension(extensionType);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IImageCaptureExtenderImpl initializeImageExtension(int extensionType)
|
||||
throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
if (mProxy != null) {
|
||||
return mProxy.initializeImageExtension(extensionType);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static long registerClient(Context ctx) {
|
||||
return CameraExtensionManagerGlobal.get().registerClient(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static void unregisterClient(long clientId) {
|
||||
CameraExtensionManagerGlobal.get().unregisterClient(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static boolean isExtensionSupported(String cameraId, int extensionType,
|
||||
CameraCharacteristics chars) {
|
||||
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
|
||||
try {
|
||||
extenders = initializeExtension(extensionType);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return extenders.first.isExtensionAvailable(cameraId, chars.getNativeMetadata()) &&
|
||||
extenders.second.isExtensionAvailable(cameraId, chars.getNativeMetadata());
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to query extension availability! Extension service does not"
|
||||
+ " respond!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> initializeExtension(
|
||||
@Extension int extensionType) {
|
||||
IPreviewExtenderImpl previewExtender;
|
||||
IImageCaptureExtenderImpl imageExtender;
|
||||
try {
|
||||
previewExtender =
|
||||
CameraExtensionManagerGlobal.get().initializePreviewExtension(extensionType);
|
||||
imageExtender =
|
||||
CameraExtensionManagerGlobal.get().initializeImageExtension(extensionType);
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException("Failed to initialize extension: " + extensionType);
|
||||
}
|
||||
if ((imageExtender == null) || (previewExtender == null)) {
|
||||
throw new IllegalArgumentException("Unknown extension: " + extensionType);
|
||||
}
|
||||
|
||||
return new Pair<>(previewExtender, imageExtender);
|
||||
}
|
||||
|
||||
private static <T> boolean isOutputSupportedFor(Class<T> klass) {
|
||||
Objects.requireNonNull(klass, "klass must not be null");
|
||||
|
||||
if (klass == android.graphics.SurfaceTexture.class) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of supported device-specific extensions for a given camera device.
|
||||
*
|
||||
* @return non-modifiable list of available extensions
|
||||
*/
|
||||
public @NonNull List<Integer> getSupportedExtensions() {
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
long clientId = registerClient(mContext);
|
||||
if (clientId < 0) {
|
||||
return Collections.unmodifiableList(ret);
|
||||
}
|
||||
|
||||
try {
|
||||
for (int extensionType : EXTENSION_LIST) {
|
||||
if (isExtensionSupported(mCameraId, extensionType, mChars)) {
|
||||
ret.add(extensionType);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unregisterClient(clientId);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of sizes compatible with {@code klass} to use as an output for the
|
||||
* repeating request
|
||||
* {@link CameraExtensionSession#setRepeatingRequest}.
|
||||
*
|
||||
* <p>Note that device-specific extensions are allowed to support only a subset
|
||||
* of the camera output surfaces and resolutions.
|
||||
* The {@link android.graphics.SurfaceTexture} class is guaranteed at least one size for
|
||||
* backward compatible cameras whereas other output classes are not guaranteed to be supported.
|
||||
* </p>
|
||||
*
|
||||
* @param extension the extension type
|
||||
* @param klass a non-{@code null} {@link Class} object reference
|
||||
* @return non-modifiable list of available sizes or an empty list if the Surface output is not
|
||||
* supported
|
||||
* @throws NullPointerException if {@code klass} was {@code null}
|
||||
* @throws IllegalArgumentException in case of unsupported extension.
|
||||
*/
|
||||
@NonNull
|
||||
public <T> List<Size> getExtensionSupportedSizes(@Extension int extension,
|
||||
@NonNull Class<T> klass) {
|
||||
if (!isOutputSupportedFor(klass)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// TODO: Revisit this code once the Extension preview processor output format
|
||||
// ambiguity is resolved in b/169799538.
|
||||
|
||||
long clientId = registerClient(mContext);
|
||||
if (clientId < 0) {
|
||||
throw new IllegalArgumentException("Unsupported extensions");
|
||||
}
|
||||
|
||||
try {
|
||||
if (!isExtensionSupported(mCameraId, extension, mChars)) {
|
||||
throw new IllegalArgumentException("Unsupported extension");
|
||||
}
|
||||
|
||||
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
|
||||
initializeExtension(extension);
|
||||
StreamConfigurationMap streamMap = mChars.get(
|
||||
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
||||
extenders.first.init(mCameraId, mChars.getNativeMetadata());
|
||||
return generateSupportedSizes(extenders.first.getSupportedResolutions(),
|
||||
ImageFormat.PRIVATE, streamMap);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
|
||||
+ " not respond!");
|
||||
return new ArrayList<>();
|
||||
} finally {
|
||||
unregisterClient(clientId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given extension is available and return the
|
||||
* supported output surface resolutions that can be used for high-quality capture
|
||||
* requests via {@link CameraExtensionSession#capture}.
|
||||
*
|
||||
* <p>Note that device-specific extensions are allowed to support only a subset
|
||||
* of the camera resolutions advertised by
|
||||
* {@link StreamConfigurationMap#getOutputSizes}.</p>
|
||||
*
|
||||
* <p>Device-specific extensions currently support at most two
|
||||
* multi-frame capture surface formats, ImageFormat.YUV_420_888 or
|
||||
* ImageFormat.JPEG.</p>
|
||||
*
|
||||
* @param extension the extension type
|
||||
* @param format device-specific extension output format
|
||||
* @return non-modifiable list of available sizes or an empty list if the format is not
|
||||
* supported.
|
||||
* @throws IllegalArgumentException in case of format different from ImageFormat.JPEG /
|
||||
* ImageFormat.YUV_420_888; or unsupported extension.
|
||||
*/
|
||||
public @NonNull
|
||||
List<Size> getExtensionSupportedSizes(@Extension int extension, int format) {
|
||||
try {
|
||||
long clientId = registerClient(mContext);
|
||||
if (clientId < 0) {
|
||||
throw new IllegalArgumentException("Unsupported extensions");
|
||||
}
|
||||
|
||||
try {
|
||||
if (!isExtensionSupported(mCameraId, extension, mChars)) {
|
||||
throw new IllegalArgumentException("Unsupported extension");
|
||||
}
|
||||
|
||||
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
|
||||
initializeExtension(extension);
|
||||
StreamConfigurationMap streamMap = mChars.get(
|
||||
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
||||
if (format == ImageFormat.YUV_420_888) {
|
||||
extenders.second.init(mCameraId, mChars.getNativeMetadata());
|
||||
if (extenders.second.getCaptureProcessor() == null) {
|
||||
// Extensions that don't implement any capture processor are limited to
|
||||
// JPEG only!
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return generateSupportedSizes(extenders.second.getSupportedResolutions(),
|
||||
format, streamMap);
|
||||
} else if (format == ImageFormat.JPEG) {
|
||||
extenders.second.init(mCameraId, mChars.getNativeMetadata());
|
||||
if (extenders.second.getCaptureProcessor() == null) {
|
||||
return generateSupportedSizes(null, format, streamMap);
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported format: " + format);
|
||||
} finally {
|
||||
unregisterClient(clientId);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
|
||||
+ " not respond!");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
383
core/java/android/hardware/camera2/CameraExtensionSession.java
Normal file
383
core/java/android/hardware/camera2/CameraExtensionSession.java
Normal file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* A camera capture session that enables access to device-specific camera extensions, which
|
||||
* often use multi-frame bursts and sophisticated post-process algorithms for image capture.
|
||||
*
|
||||
* <p>The capture session will be returned after a successful call to
|
||||
* {@link CameraDevice#createExtensionSession} as part of the argument
|
||||
* in the registered state callback {@link StateCallback#onConfigured}
|
||||
* method. </p>
|
||||
*
|
||||
* <p>Note that CameraExtensionSession is currently limited to a maximum of two output
|
||||
* surfaces for continuous repeating and multi-frame processing respectively. Some
|
||||
* features such as capture settings will not be supported as the device-specific
|
||||
* Extension is allowed to override all capture parameters.</p>
|
||||
*
|
||||
* <p>Information about support for specific device-specific extensions can be queried
|
||||
* from {@link CameraExtensionCharacteristics}. </p>
|
||||
*/
|
||||
public abstract class CameraExtensionSession implements AutoCloseable {
|
||||
/** @hide */
|
||||
public CameraExtensionSession () {}
|
||||
|
||||
/**
|
||||
* A callback object for tracking the progress of a
|
||||
* {@link CaptureRequest} submitted to the camera device.
|
||||
*
|
||||
* <p>This callback is invoked when a request triggers a capture to start,
|
||||
* and when the device-specific Extension post processing begins. In case of an
|
||||
* error capturing an image, the error method is triggered instead of
|
||||
* the completion method.</p>
|
||||
*
|
||||
* @see #capture
|
||||
* @see #setRepeatingRequest
|
||||
*/
|
||||
public static abstract class ExtensionCaptureCallback {
|
||||
|
||||
/**
|
||||
* This method is called when the camera device has started
|
||||
* capturing the initial input image of the device-specific extension
|
||||
* post-process request.
|
||||
*
|
||||
* <p>This callback is invoked right as the capture of a frame begins,
|
||||
* so it is the most appropriate time for playing a shutter sound,
|
||||
* or triggering UI indicators of capture.</p>
|
||||
*
|
||||
* <p>The request that is being used for this capture is provided,
|
||||
* along with the actual timestamp for the start of exposure.</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 for the capture that just begun
|
||||
* @param timestamp the timestamp at start of capture for repeating
|
||||
* request or the timestamp at start of capture of the
|
||||
* first frame in a multi-frame capture.
|
||||
*/
|
||||
public void onCaptureStarted(@NonNull CameraExtensionSession session,
|
||||
@NonNull CaptureRequest request, long timestamp) {
|
||||
// default empty implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when an image (or images in case of multi-frame
|
||||
* capture) is captured and device-specific extension
|
||||
* processing is triggered.
|
||||
*
|
||||
* <p>Each request will generate at most {@code 1}
|
||||
* {@link #onCaptureProcessStarted}.</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 CameraExtensionSession
|
||||
*
|
||||
* @see #capture
|
||||
* @see #setRepeatingRequest
|
||||
*/
|
||||
public void onCaptureProcessStarted(@NonNull CameraExtensionSession session,
|
||||
@NonNull CaptureRequest request) {
|
||||
// default empty implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called instead of
|
||||
* {@link #onCaptureProcessStarted} when the camera device failed
|
||||
* to produce the required input for the device-specific extension. The
|
||||
* cause could be a failed camera capture request, a failed
|
||||
* capture result or dropped camera frame.
|
||||
*
|
||||
* <p>Other requests are unaffected, and some or all image buffers
|
||||
* from the capture may have been pushed to their respective output
|
||||
* streams.</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
|
||||
*
|
||||
* @see #capture
|
||||
* @see #setRepeatingRequest
|
||||
*/
|
||||
public void onCaptureFailed(@NonNull CameraExtensionSession session,
|
||||
@NonNull CaptureRequest request) {
|
||||
// default empty implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called independently of the others in
|
||||
* ExtensionCaptureCallback, when a capture sequence finishes.
|
||||
*
|
||||
* <p>In total, there will be at least one
|
||||
* {@link #onCaptureProcessStarted}/{@link #onCaptureFailed}
|
||||
* invocation before this callback is triggered. If the capture
|
||||
* sequence is aborted before any requests have begun processing,
|
||||
* {@link #onCaptureSequenceAborted} is invoked instead.</p>
|
||||
*
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*
|
||||
* @param session the session received during
|
||||
* {@link StateCallback#onConfigured(CameraExtensionSession)}
|
||||
* @param sequenceId A sequence ID returned by the {@link #capture}
|
||||
* family of functions.
|
||||
* @see #onCaptureSequenceAborted
|
||||
*/
|
||||
public void onCaptureSequenceCompleted(@NonNull CameraExtensionSession session,
|
||||
int sequenceId) {
|
||||
// default empty implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a capture sequence aborts.
|
||||
*
|
||||
* <p>Due to the asynchronous nature of the camera device, not all
|
||||
* submitted captures are immediately processed. It is possible to
|
||||
* clear out the pending requests by a variety of operations such
|
||||
* as {@link CameraExtensionSession#stopRepeating}. When such an event
|
||||
* happens, {@link #onCaptureProcessStarted} will not be called.</p>
|
||||
*
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*
|
||||
* @param session the session received during
|
||||
* {@link StateCallback#onConfigured(CameraExtensionSession)}
|
||||
* @param sequenceId A sequence ID returned by the {@link #capture}
|
||||
* family of functions.
|
||||
* @see #onCaptureProcessStarted
|
||||
*/
|
||||
public void onCaptureSequenceAborted(@NonNull CameraExtensionSession session,
|
||||
int sequenceId) {
|
||||
// default empty implementation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback object for receiving updates about the state of a camera extension session.
|
||||
*
|
||||
*/
|
||||
public static abstract class StateCallback {
|
||||
|
||||
/**
|
||||
* This method is called when the camera device has finished configuring itself, and the
|
||||
* session can start processing capture requests.
|
||||
*
|
||||
* <p>If the camera device configuration fails, then {@link #onConfigureFailed} will
|
||||
* be invoked instead of this callback.</p>
|
||||
*
|
||||
* @param session A valid extension session
|
||||
*/
|
||||
public abstract void onConfigured(@NonNull CameraExtensionSession session);
|
||||
|
||||
/**
|
||||
* This method is called if the session cannot be configured as requested.
|
||||
*
|
||||
* <p>This can happen if the set of requested outputs contains unsupported sizes,
|
||||
* too many outputs are requested at once or the camera device encounters an
|
||||
* unrecoverable error during configuration.</p>
|
||||
*
|
||||
* <p>The session is considered to be closed, and all methods called on it after this
|
||||
* callback is invoked will throw an IllegalStateException.</p>
|
||||
*
|
||||
* @param session the session instance that failed to configure
|
||||
*/
|
||||
public abstract void onConfigureFailed(@NonNull CameraExtensionSession session);
|
||||
|
||||
/**
|
||||
* This method is called when the session is closed.
|
||||
*
|
||||
* <p>A session is closed when a new session is created by the parent camera device,
|
||||
* or when the parent camera device is closed (either by the user closing the device,
|
||||
* or due to a camera device disconnection or fatal error).</p>
|
||||
*
|
||||
* <p>Once a session is closed, all methods on it will throw an IllegalStateException, and
|
||||
* any repeating requests are stopped (as if {@link #stopRepeating()} was called).
|
||||
* However, any in-progress capture requests submitted to the session will be completed
|
||||
* as normal.</p>
|
||||
*
|
||||
* @param session the session received during
|
||||
* {@link StateCallback#onConfigured(CameraExtensionSession)}
|
||||
*/
|
||||
public void onClosed(@NonNull CameraExtensionSession session) {
|
||||
// default empty implementation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the camera device that this session is created for.
|
||||
*/
|
||||
@NonNull
|
||||
public android.hardware.camera2.CameraDevice getDevice() {
|
||||
throw new UnsupportedOperationException("Subclasses must override this method");
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a request for device-specific processing using input
|
||||
* from the camera device, to produce a single high-quality output result.
|
||||
*
|
||||
* <p>Note that single capture requests currently do not support
|
||||
* client parameters. Settings included in the request will
|
||||
* be entirely overridden by the device-specific extension. </p>
|
||||
*
|
||||
* <p>The {@link CaptureRequest.Builder#addTarget} supports only one
|
||||
* ImageFormat.YUV_420_888 or ImageFormat.JPEG target surface. {@link CaptureRequest}
|
||||
* arguments that include further targets will cause
|
||||
* IllegalArgumentException to be thrown. </p>
|
||||
*
|
||||
* <p>Each request will produce one new frame for one target Surface, set
|
||||
* with the CaptureRequest builder's
|
||||
* {@link CaptureRequest.Builder#addTarget} method.</p>
|
||||
*
|
||||
* <p>Multiple requests can be in progress at once. Requests are
|
||||
* processed in first-in, first-out order.</p>
|
||||
*
|
||||
* <p>Requests submitted through this method have higher priority than
|
||||
* those submitted through {@link #setRepeatingRequest}, and will be
|
||||
* processed as soon as the current repeat processing completes.</p>
|
||||
*
|
||||
* @param request the settings for this capture
|
||||
* @param executor the executor which will be used for invoking the
|
||||
* listener.
|
||||
* @param listener The callback object to notify once this request has
|
||||
* been processed.
|
||||
* @return int A unique capture sequence ID used by
|
||||
* {@link ExtensionCaptureCallback#onCaptureSequenceCompleted}.
|
||||
* @throws CameraAccessException if the camera device is no longer
|
||||
* connected or has encountered a fatal error
|
||||
* @throws IllegalStateException if this session is no longer active,
|
||||
* either because the session was explicitly closed, a new
|
||||
* session has been created or the camera device has been
|
||||
* closed.
|
||||
* @throws IllegalArgumentException if the request targets no Surfaces
|
||||
* or Surfaces that are not configured as outputs for this
|
||||
* session; or the request targets a set of Surfaces that
|
||||
* cannot be submitted simultaneously.
|
||||
*/
|
||||
public int capture(@NonNull CaptureRequest request,
|
||||
@NonNull Executor executor,
|
||||
@NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
|
||||
throw new UnsupportedOperationException("Subclasses must override this method");
|
||||
}
|
||||
|
||||
/**
|
||||
* Request endlessly repeating device-specific extension processing of
|
||||
* camera images.
|
||||
*
|
||||
* <p>With this method, the camera device will continually capture images
|
||||
* and process them using the device-specific extension at the maximum
|
||||
* rate possible.</p>
|
||||
*
|
||||
* <p>Note that repeating capture requests currently do not support
|
||||
* client parameters. Settings included in the request will
|
||||
* be completely overridden by the device-specific extension.</p>
|
||||
*
|
||||
* <p>The {@link CaptureRequest.Builder#addTarget} supports only one
|
||||
* target surface. {@link CaptureRequest} arguments that include further
|
||||
* targets will cause IllegalArgumentException to be thrown.</p>
|
||||
*
|
||||
* <p>Repeating requests are a simple way for an application to maintain a
|
||||
* preview or other continuous stream of frames.</p>
|
||||
*
|
||||
* <p>Repeat requests have lower priority than those submitted
|
||||
* through {@link #capture}, so if {@link #capture} is called when a
|
||||
* repeating request is active, the capture request will be processed
|
||||
* before any further repeating requests are processed.</p>
|
||||
*
|
||||
* <p>To stop the repeating capture, call {@link #stopRepeating}.</p>
|
||||
*
|
||||
* <p>Calling this method will replace any earlier repeating request.</p>
|
||||
*
|
||||
* @param request the request to repeat indefinitely
|
||||
* @param executor the executor which will be used for invoking the
|
||||
* listener.
|
||||
* @param listener The callback object to notify every time the
|
||||
* request finishes processing.
|
||||
* @return int A unique capture sequence ID used by
|
||||
* {@link ExtensionCaptureCallback#onCaptureSequenceCompleted}.
|
||||
* @throws CameraAccessException if the camera device is no longer
|
||||
* connected or has encountered a fatal error
|
||||
* @throws IllegalStateException if this session is no longer active,
|
||||
* either because the session was explicitly closed, a new
|
||||
* session has been created or the camera device has been
|
||||
* closed.
|
||||
* @throws IllegalArgumentException If the request references no
|
||||
* Surfaces or references Surfaces that are not currently
|
||||
* configured as outputs.
|
||||
* @see #capture
|
||||
*/
|
||||
public int setRepeatingRequest(@NonNull CaptureRequest request,
|
||||
@NonNull Executor executor,
|
||||
@NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
|
||||
throw new UnsupportedOperationException("Subclasses must override this method");
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel any ongoing repeating capture set by
|
||||
* {@link #setRepeatingRequest setRepeatingRequest}. Has no effect on
|
||||
* requests submitted through {@link #capture capture}.
|
||||
*
|
||||
* <p>Any currently in-flight captures will still complete.</p>
|
||||
*
|
||||
* @throws CameraAccessException if the camera device is no longer
|
||||
* connected or has encountered a fatal error
|
||||
* @throws IllegalStateException if this session is no longer active,
|
||||
* either because the session was explicitly closed, a new
|
||||
* session has been created or the camera device has been closed.
|
||||
* @see #setRepeatingRequest
|
||||
*/
|
||||
public void stopRepeating() throws CameraAccessException {
|
||||
throw new UnsupportedOperationException("Subclasses must override this method");
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this capture session asynchronously.
|
||||
*
|
||||
* <p>Closing a session frees up the target output Surfaces of the session
|
||||
* for reuse with either a new session, or to other APIs that can draw
|
||||
* to Surfaces.</p>
|
||||
*
|
||||
* <p>Note that creating a new capture session with
|
||||
* {@link android.hardware.camera2.CameraDevice#createCaptureSession} or
|
||||
* {@link android.hardware.camera2.CameraDevice#createExtensionSession}
|
||||
* will close any existing capture session automatically, and call the
|
||||
* older session listener's {@link StateCallback#onClosed} callback.
|
||||
* Using
|
||||
* {@link android.hardware.camera2.CameraDevice#createCaptureSession} or
|
||||
* {@link android.hardware.camera2.CameraDevice#createExtensionSession}
|
||||
* directly without closing is the recommended approach for quickly
|
||||
* switching to a new session, since unchanged target outputs can be
|
||||
* reused more efficiently.</p>
|
||||
*
|
||||
* <p>Once a session is closed, all methods on it will throw an
|
||||
* IllegalStateException, and any repeating requests are
|
||||
* stopped (as if {@link #stopRepeating()} was called).</p>
|
||||
*
|
||||
* <p>Closing a session is idempotent; closing more than once has no
|
||||
* effect.</p>
|
||||
*/
|
||||
public void close() throws CameraAccessException {
|
||||
throw new UnsupportedOperationException("Subclasses must override this method");
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import android.hardware.ICameraService;
|
||||
import android.hardware.ICameraServiceListener;
|
||||
import android.hardware.camera2.impl.CameraDeviceImpl;
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
import android.hardware.camera2.params.ExtensionSessionConfiguration;
|
||||
import android.hardware.camera2.params.SessionConfiguration;
|
||||
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
|
||||
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
|
||||
@@ -434,6 +435,28 @@ public final class CameraManager {
|
||||
return characteristics;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Query the camera extension capabilities of a camera device.</p>
|
||||
*
|
||||
* @param cameraId The id of the camera device to query. This must be a standalone
|
||||
* camera ID which can be directly opened by {@link #openCamera}.
|
||||
* @return The properties of the given camera
|
||||
*
|
||||
* @throws IllegalArgumentException if the cameraId does not match any
|
||||
* known camera device.
|
||||
* @throws CameraAccessException if the camera device has been disconnected.
|
||||
*
|
||||
* @see CameraExtensionCharacteristics
|
||||
* @see CameraDevice#createExtensionSession(ExtensionSessionConfiguration)
|
||||
* @see CameraExtensionSession
|
||||
*/
|
||||
@NonNull
|
||||
public CameraExtensionCharacteristics getCameraExtensionCharacteristics(
|
||||
@NonNull String cameraId) throws CameraAccessException {
|
||||
CameraCharacteristics chars = getCameraCharacteristics(cameraId);
|
||||
return new CameraExtensionCharacteristics(mContext, cameraId, chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for opening a connection to a camera with the given ID.
|
||||
*
|
||||
@@ -473,7 +496,8 @@ public final class CameraManager {
|
||||
callback,
|
||||
executor,
|
||||
characteristics,
|
||||
mContext.getApplicationInfo().targetSdkVersion);
|
||||
mContext.getApplicationInfo().targetSdkVersion,
|
||||
mContext);
|
||||
|
||||
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
|
||||
|
||||
|
||||
@@ -111,6 +111,15 @@ public abstract class CameraMetadata<TKey> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the CameraMetadataNative instance.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public CameraMetadataNative getNativeMetadata() {
|
||||
return mNativeInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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;
|
||||
import android.hardware.camera2.extension.ParcelImage;
|
||||
|
||||
/** @hide */
|
||||
parcelable CaptureBundle
|
||||
{
|
||||
int stage;
|
||||
int sequenceId;
|
||||
CameraMetadataNative captureResult;
|
||||
ParcelImage captureImage;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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 */
|
||||
parcelable CaptureStageImpl
|
||||
{
|
||||
int id;
|
||||
CameraMetadataNative parameters;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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.extension.IPreviewExtenderImpl;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
|
||||
/** @hide */
|
||||
interface ICameraExtensionsProxyService
|
||||
{
|
||||
long registerClient();
|
||||
void unregisterClient(long clientId);
|
||||
@nullable IPreviewExtenderImpl initializePreviewExtension(int extensionType);
|
||||
@nullable IImageCaptureExtenderImpl initializeImageExtension(int extensionType);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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.view.Surface;
|
||||
import android.hardware.camera2.extension.CaptureBundle;
|
||||
import android.hardware.camera2.extension.Size;
|
||||
|
||||
/** @hide */
|
||||
interface ICaptureProcessorImpl
|
||||
{
|
||||
void onOutputSurface(in Surface surface, int imageFormat);
|
||||
void onResolutionUpdate(in Size size);
|
||||
void onImageFormatUpdate(int imageFormat);
|
||||
void process(in List<CaptureBundle> capturelist);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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;
|
||||
|
||||
import android.hardware.camera2.extension.CaptureStageImpl;
|
||||
import android.hardware.camera2.extension.ICaptureProcessorImpl;
|
||||
import android.hardware.camera2.extension.SizeList;
|
||||
|
||||
/** @hide */
|
||||
interface IImageCaptureExtenderImpl
|
||||
{
|
||||
void onInit(in String cameraId, in CameraMetadataNative cameraCharacteristics);
|
||||
void onDeInit();
|
||||
@nullable CaptureStageImpl onPresetSession();
|
||||
@nullable CaptureStageImpl onEnableSession();
|
||||
@nullable CaptureStageImpl onDisableSession();
|
||||
|
||||
boolean isExtensionAvailable(in String cameraId, in CameraMetadataNative chars);
|
||||
void init(in String cameraId, in CameraMetadataNative chars);
|
||||
@nullable ICaptureProcessorImpl getCaptureProcessor();
|
||||
@nullable List<CaptureStageImpl> getCaptureStages();
|
||||
int getMaxCaptureStage();
|
||||
@nullable List<SizeList> getSupportedResolutions();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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;
|
||||
|
||||
import android.hardware.camera2.extension.CaptureStageImpl;
|
||||
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
|
||||
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
|
||||
import android.hardware.camera2.extension.SizeList;
|
||||
|
||||
/** @hide */
|
||||
interface IPreviewExtenderImpl
|
||||
{
|
||||
void onInit(in String cameraId, in CameraMetadataNative cameraCharacteristics);
|
||||
void onDeInit();
|
||||
@nullable CaptureStageImpl onPresetSession();
|
||||
@nullable CaptureStageImpl onEnableSession();
|
||||
@nullable CaptureStageImpl onDisableSession();
|
||||
|
||||
void init(in String cameraId, in CameraMetadataNative chars);
|
||||
boolean isExtensionAvailable(in String cameraId, in CameraMetadataNative chars);
|
||||
@nullable CaptureStageImpl getCaptureStage();
|
||||
|
||||
const int PROCESSOR_TYPE_REQUEST_UPDATE_ONLY = 0;
|
||||
const int PROCESSOR_TYPE_IMAGE_PROCESSOR = 1;
|
||||
const int PROCESSOR_TYPE_NONE = 2;
|
||||
int getProcessorType();
|
||||
@nullable IPreviewImageProcessorImpl getPreviewImageProcessor();
|
||||
@nullable IRequestUpdateProcessorImpl getRequestUpdateProcessor();
|
||||
|
||||
@nullable List<SizeList> getSupportedResolutions();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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;
|
||||
import android.view.Surface;
|
||||
import android.hardware.camera2.extension.ParcelImage;
|
||||
import android.hardware.camera2.extension.Size;
|
||||
|
||||
/** @hide */
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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;
|
||||
import android.view.Surface;
|
||||
import android.hardware.camera2.extension.CaptureStageImpl;
|
||||
import android.hardware.camera2.extension.Size;
|
||||
|
||||
/** @hide */
|
||||
interface IRequestUpdateProcessorImpl
|
||||
{
|
||||
void onOutputSurface(in Surface surface, int imageFormat);
|
||||
void onResolutionUpdate(in Size size);
|
||||
void onImageFormatUpdate(int imageFormat);
|
||||
@nullable CaptureStageImpl process(in CameraMetadataNative result, int sequenceId);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
/** @hide */
|
||||
parcelable ParcelImage
|
||||
{
|
||||
int format;
|
||||
int width;
|
||||
int height;
|
||||
int transform;
|
||||
int scalingMode;
|
||||
long timestamp;
|
||||
int planeCount;
|
||||
Rect crop;
|
||||
HardwareBuffer buffer;
|
||||
ParcelFileDescriptor fence;
|
||||
}
|
||||
23
core/java/android/hardware/camera2/extension/Size.aidl
Normal file
23
core/java/android/hardware/camera2/extension/Size.aidl
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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;
|
||||
|
||||
/** @hide */
|
||||
parcelable Size
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
}
|
||||
25
core/java/android/hardware/camera2/extension/SizeList.aidl
Normal file
25
core/java/android/hardware/camera2/extension/SizeList.aidl
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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.extension.Size;
|
||||
|
||||
/** @hide */
|
||||
parcelable SizeList
|
||||
{
|
||||
int format;
|
||||
List<Size> sizes;
|
||||
}
|
||||
@@ -19,10 +19,12 @@ package android.hardware.camera2.impl;
|
||||
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.hardware.ICameraService;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCaptureSession;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraExtensionSession;
|
||||
import android.hardware.camera2.CameraOfflineSession;
|
||||
import android.hardware.camera2.CameraDevice;
|
||||
import android.hardware.camera2.CaptureFailure;
|
||||
@@ -32,6 +34,7 @@ import android.hardware.camera2.ICameraDeviceCallbacks;
|
||||
import android.hardware.camera2.ICameraDeviceUser;
|
||||
import android.hardware.camera2.ICameraOfflineSession;
|
||||
import android.hardware.camera2.TotalCaptureResult;
|
||||
import android.hardware.camera2.params.ExtensionSessionConfiguration;
|
||||
import android.hardware.camera2.params.InputConfiguration;
|
||||
import android.hardware.camera2.params.OutputConfiguration;
|
||||
import android.hardware.camera2.params.SessionConfiguration;
|
||||
@@ -110,6 +113,7 @@ public class CameraDeviceImpl extends CameraDevice
|
||||
private final String mCameraId;
|
||||
private final CameraCharacteristics mCharacteristics;
|
||||
private final int mTotalPartialCount;
|
||||
private final Context mContext;
|
||||
|
||||
private static final long NANO_PER_SECOND = 1000000000; //ns
|
||||
|
||||
@@ -127,6 +131,7 @@ public class CameraDeviceImpl extends CameraDevice
|
||||
private FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
|
||||
|
||||
private CameraCaptureSessionCore mCurrentSession;
|
||||
private CameraExtensionSessionImpl mCurrentExtensionSession;
|
||||
private int mNextSessionId = 0;
|
||||
|
||||
private final int mAppTargetSdkVersion;
|
||||
@@ -250,7 +255,8 @@ public class CameraDeviceImpl extends CameraDevice
|
||||
};
|
||||
|
||||
public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor,
|
||||
CameraCharacteristics characteristics, int appTargetSdkVersion) {
|
||||
CameraCharacteristics characteristics, int appTargetSdkVersion,
|
||||
Context ctx) {
|
||||
if (cameraId == null || callback == null || executor == null || characteristics == null) {
|
||||
throw new IllegalArgumentException("Null argument given");
|
||||
}
|
||||
@@ -259,6 +265,7 @@ public class CameraDeviceImpl extends CameraDevice
|
||||
mDeviceExecutor = executor;
|
||||
mCharacteristics = characteristics;
|
||||
mAppTargetSdkVersion = appTargetSdkVersion;
|
||||
mContext = ctx;
|
||||
|
||||
final int MAX_TAG_LEN = 23;
|
||||
String tag = String.format("CameraDevice-JV-%s", mCameraId);
|
||||
@@ -1322,6 +1329,10 @@ public class CameraDeviceImpl extends CameraDevice
|
||||
mRemoteDevice.unlinkToDeath(this, /*flags*/0);
|
||||
}
|
||||
|
||||
if (mCurrentExtensionSession != null) {
|
||||
mCurrentExtensionSession.release();
|
||||
mCurrentExtensionSession = null;
|
||||
}
|
||||
// Only want to fire the onClosed callback once;
|
||||
// either a normal close where the remote device is valid
|
||||
// or a close after a startup error (no remote device but in error state)
|
||||
@@ -2297,4 +2308,16 @@ public class CameraDeviceImpl extends CameraDevice
|
||||
return mRemoteDevice.getGlobalAudioRestriction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
|
||||
throws CameraAccessException {
|
||||
try {
|
||||
mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(this,
|
||||
mContext,
|
||||
extensionConfiguration);
|
||||
} catch (RemoteException e) {
|
||||
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.SuppressLint;
|
||||
import android.hardware.camera2.CameraExtensionCharacteristics;
|
||||
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
|
||||
import android.hardware.camera2.extension.ParcelImage;
|
||||
import android.hardware.camera2.TotalCaptureResult;
|
||||
import android.media.Image;
|
||||
import android.media.ImageReader;
|
||||
import android.media.ImageWriter;
|
||||
import android.annotation.NonNull;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.Surface;
|
||||
|
||||
// Wrap a given 'PreviewImageProcessorImpl' so that the processed output can
|
||||
// be redirected to a given surface or looped back in the internal intermediate surface.
|
||||
public class CameraExtensionForwardProcessor {
|
||||
public final static String TAG = "CameraExtensionForward";
|
||||
private final static int FORWARD_QUEUE_SIZE = 3;
|
||||
|
||||
private final IPreviewImageProcessorImpl mProcessor;
|
||||
private final long mOutputSurfaceUsage;
|
||||
private final int mOutputSurfaceFormat;
|
||||
|
||||
private ImageReader mIntermediateReader = null;
|
||||
private Surface mIntermediateSurface = null;
|
||||
private Size mResolution = null;
|
||||
private Surface mOutputSurface = null;
|
||||
private ImageWriter mOutputWriter = null;
|
||||
private boolean mOutputAbandoned = false;
|
||||
|
||||
public CameraExtensionForwardProcessor(@NonNull IPreviewImageProcessorImpl processor,
|
||||
int format, long surfaceUsage) {
|
||||
mProcessor = processor;
|
||||
mOutputSurfaceUsage = surfaceUsage;
|
||||
mOutputSurfaceFormat = format;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (mOutputWriter != null) {
|
||||
mOutputWriter.close();
|
||||
mOutputWriter = null;
|
||||
}
|
||||
|
||||
if (mIntermediateReader != null) {
|
||||
mIntermediateReader.close();
|
||||
mIntermediateReader = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void onOutputSurface(Surface surface, int format) {
|
||||
mOutputSurface = surface;
|
||||
try {
|
||||
initializePipeline();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to initialize forward processor, extension service does not"
|
||||
+ " respond!");
|
||||
}
|
||||
}
|
||||
|
||||
public void onResolutionUpdate(Size size) {
|
||||
mResolution = size;
|
||||
}
|
||||
|
||||
public void onImageFormatUpdate(int format) {
|
||||
if (format != CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT) {
|
||||
Log.e(TAG, "Unsupported input format: " + format);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
private void initializePipeline() throws RemoteException {
|
||||
if (mOutputWriter != null) {
|
||||
mOutputWriter.close();
|
||||
mOutputWriter = null;
|
||||
}
|
||||
|
||||
if (mIntermediateReader == null) {
|
||||
mIntermediateReader = ImageReader.newInstance(mResolution.getWidth(),
|
||||
mResolution.getHeight(), CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
|
||||
FORWARD_QUEUE_SIZE, mOutputSurfaceUsage);
|
||||
mIntermediateSurface = mIntermediateReader.getSurface();
|
||||
mIntermediateReader.setOnImageAvailableListener(new ForwardCallback(), null);
|
||||
|
||||
mProcessor.onOutputSurface(mIntermediateSurface, mOutputSurfaceFormat);
|
||||
// PreviewImageProcessorImpl always expect the extension processing format as input
|
||||
mProcessor.onImageFormatUpdate(CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT);
|
||||
android.hardware.camera2.extension.Size sz =
|
||||
new android.hardware.camera2.extension.Size();
|
||||
sz.width = mResolution.getWidth();
|
||||
sz.height = mResolution.getHeight();
|
||||
mProcessor.onResolutionUpdate(sz);
|
||||
}
|
||||
}
|
||||
|
||||
public void process(ParcelImage image, TotalCaptureResult totalCaptureResult)
|
||||
throws RemoteException {
|
||||
if ((mIntermediateSurface != null) && (mIntermediateSurface.isValid()) &&
|
||||
!mOutputAbandoned) {
|
||||
mProcessor.process(image, totalCaptureResult.getNativeMetadata(),
|
||||
totalCaptureResult.getSequenceId());
|
||||
}
|
||||
}
|
||||
|
||||
private class ForwardCallback implements ImageReader.OnImageAvailableListener {
|
||||
@Override public void onImageAvailable(ImageReader reader) {
|
||||
Image processedImage = null;
|
||||
try {
|
||||
processedImage = mIntermediateReader.acquireNextImage();
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Failed to acquire processed image!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mOutputSurface != null && mOutputSurface.isValid() && !mOutputAbandoned) {
|
||||
if (mOutputWriter == null) {
|
||||
mOutputWriter = ImageWriter.newInstance(mOutputSurface, FORWARD_QUEUE_SIZE,
|
||||
processedImage.getFormat());
|
||||
}
|
||||
try {
|
||||
mOutputWriter.queueInputImage(processedImage);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Failed to queue processed buffer!");
|
||||
processedImage.close();
|
||||
mOutputAbandoned = true;
|
||||
}
|
||||
} else {
|
||||
processedImage.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -382,6 +382,19 @@ public class CameraMetadataNative implements Parcelable {
|
||||
return newObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all metadata values in the destination argument by using the corresponding
|
||||
* values from the source. Metadata tags present in the destination and absent
|
||||
* from the source will remain unmodified.
|
||||
*
|
||||
* @param dst Destination metadata
|
||||
* @param src Source metadata
|
||||
* @hide
|
||||
*/
|
||||
public static void update(CameraMetadataNative dst, CameraMetadataNative src) {
|
||||
nativeUpdate(dst.mMetadataPtr, src.mMetadataPtr);
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<CameraMetadataNative> CREATOR =
|
||||
new Parcelable.Creator<CameraMetadataNative>() {
|
||||
@Override
|
||||
@@ -1719,6 +1732,9 @@ public class CameraMetadataNative implements Parcelable {
|
||||
private static native long nativeAllocateCopy(long ptr)
|
||||
throws NullPointerException;
|
||||
|
||||
|
||||
@FastNative
|
||||
private static native void nativeUpdate(long dst, long src);
|
||||
private static synchronized native void nativeWriteToParcel(Parcel dest, long ptr);
|
||||
private static synchronized native void nativeReadFromParcel(Parcel source, long ptr);
|
||||
private static synchronized native void nativeSwap(long ptr, long otherPtr)
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.params;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import android.hardware.camera2.CameraExtensionCharacteristics.Extension;
|
||||
import android.hardware.camera2.CameraExtensionSession;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* A class that aggregates all supported arguments for
|
||||
* {@link CameraExtensionSession} initialization.
|
||||
*/
|
||||
public final class ExtensionSessionConfiguration {
|
||||
private static final String TAG = "ExtensionSessionConfiguration";
|
||||
|
||||
private int mExtensionType;
|
||||
private List<OutputConfiguration> mOutputs;
|
||||
private Executor mExecutor = null;
|
||||
private CameraExtensionSession.StateCallback mCallback = null;
|
||||
|
||||
/**
|
||||
* Create a new ExtensionSessionConfiguration
|
||||
*
|
||||
* @param extension to be used for processing
|
||||
* @param outputs a list of output configurations for the capture session
|
||||
* @param executor the executor which will be used for invoking the callbacks
|
||||
* @param listener callbacks to be invoked when the state of the
|
||||
* CameraExtensionSession changes
|
||||
*/
|
||||
public ExtensionSessionConfiguration(@Extension int extension,
|
||||
@NonNull List<OutputConfiguration> outputs, @NonNull Executor executor,
|
||||
@NonNull CameraExtensionSession.StateCallback listener) {
|
||||
mExtensionType = extension;
|
||||
mOutputs = outputs;
|
||||
mExecutor = executor;
|
||||
mCallback = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the extension type.
|
||||
*
|
||||
* @return the extension type.
|
||||
*/
|
||||
public @Extension
|
||||
int getExtension() {
|
||||
return mExtensionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the {@link OutputConfiguration} list for the capture
|
||||
* session.
|
||||
*
|
||||
* @return A list of output configurations for the capture session.
|
||||
*/
|
||||
public @NonNull
|
||||
List<OutputConfiguration> getOutputConfigurations() {
|
||||
return mOutputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the CameraCaptureSession.StateCallback
|
||||
* listener.
|
||||
*
|
||||
* @return A state callback interface implementation.
|
||||
*/
|
||||
public @NonNull
|
||||
CameraExtensionSession.StateCallback getStateCallback() {
|
||||
return mCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Executor for the CameraExtensionSession instance.
|
||||
*
|
||||
* @return The Executor on which the callback will be invoked.
|
||||
*/
|
||||
public @NonNull
|
||||
Executor getExecutor() {
|
||||
return mExecutor;
|
||||
}
|
||||
}
|
||||
@@ -1896,7 +1896,8 @@ public final class StreamConfigurationMap {
|
||||
|
||||
// from system/core/include/system/graphics.h
|
||||
private static final int HAL_PIXEL_FORMAT_RAW16 = 0x20;
|
||||
private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
|
||||
/** @hide */
|
||||
public static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
|
||||
private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
|
||||
private static final int HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23;
|
||||
private static final int HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24;
|
||||
@@ -1910,7 +1911,8 @@ public final class StreamConfigurationMap {
|
||||
private static final int HAL_DATASPACE_RANGE_SHIFT = 27;
|
||||
|
||||
private static final int HAL_DATASPACE_UNKNOWN = 0x0;
|
||||
private static final int HAL_DATASPACE_V0_JFIF =
|
||||
/** @hide */
|
||||
public static final int HAL_DATASPACE_V0_JFIF =
|
||||
(2 << HAL_DATASPACE_STANDARD_SHIFT) |
|
||||
(3 << HAL_DATASPACE_TRANSFER_SHIFT) |
|
||||
(1 << HAL_DATASPACE_RANGE_SHIFT);
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <binder/Parcel.h>
|
||||
|
||||
#include <private/gui/ComposerService.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
#include <ui/PixelFormat.h>
|
||||
|
||||
#include <hardware/gralloc1.h>
|
||||
@@ -224,6 +223,16 @@ AHardwareBuffer* android_hardware_HardwareBuffer_getNativeHardwareBuffer(
|
||||
}
|
||||
}
|
||||
|
||||
GraphicBuffer* android_hardware_HardwareBuffer_getNativeGraphicBuffer(
|
||||
JNIEnv* env, jobject hardwareBufferObj) {
|
||||
if (env->IsInstanceOf(hardwareBufferObj, gHardwareBufferClassInfo.clazz)) {
|
||||
return GraphicBufferWrapper_to_GraphicBuffer(
|
||||
env->GetLongField(hardwareBufferObj, gHardwareBufferClassInfo.mNativeObject));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
jobject android_hardware_HardwareBuffer_createFromAHardwareBuffer(
|
||||
JNIEnv* env, AHardwareBuffer* hardwareBuffer) {
|
||||
GraphicBuffer* buffer = AHardwareBuffer_to_GraphicBuffer(hardwareBuffer);
|
||||
|
||||
@@ -249,6 +249,32 @@ static jint CameraMetadata_getEntryCount(JNIEnv *env, jclass thiz, jlong ptr) {
|
||||
return metadata->entryCount();
|
||||
}
|
||||
|
||||
static void CameraMetadata_update(JNIEnv *env, jclass thiz, jlong dst, jlong src) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
CameraMetadata* metadataDst = CameraMetadata_getPointerThrow(env, dst);
|
||||
CameraMetadata* metadataSrc = CameraMetadata_getPointerThrow(env, src);
|
||||
|
||||
if (((metadataDst == NULL) || (metadataDst->isEmpty())) ||
|
||||
((metadataSrc == NULL) || metadataSrc->isEmpty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto metaBuffer = metadataSrc->getAndLock();
|
||||
camera_metadata_ro_entry_t entry;
|
||||
auto entryCount = get_camera_metadata_entry_count(metaBuffer);
|
||||
for (size_t i = 0; i < entryCount; i++) {
|
||||
auto stat = get_camera_metadata_ro_entry(metaBuffer, i, &entry);
|
||||
if (stat != NO_ERROR) {
|
||||
ALOGE("%s: Failed to retrieve source metadata!", __func__);
|
||||
metadataSrc->unlock(metaBuffer);
|
||||
return;
|
||||
}
|
||||
metadataDst->update(entry.tag, entry.data.u8, entry.count);
|
||||
}
|
||||
metadataSrc->unlock(metaBuffer);
|
||||
}
|
||||
|
||||
static jlong CameraMetadata_getBufferSize(JNIEnv *env, jclass thiz, jlong ptr) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
|
||||
@@ -565,6 +591,9 @@ static const JNINativeMethod gCameraMetadataMethods[] = {
|
||||
{ "nativeAllocateCopy",
|
||||
"(J)J",
|
||||
(void *)CameraMetadata_allocateCopy },
|
||||
{ "nativeUpdate",
|
||||
"(JJ)V",
|
||||
(void*)CameraMetadata_update },
|
||||
{ "nativeIsEmpty",
|
||||
"(J)Z",
|
||||
(void*)CameraMetadata_isEmpty },
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define _ANDROID_HARDWARE_HARDWAREBUFFER_H
|
||||
|
||||
#include <android/hardware_buffer.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
@@ -27,6 +28,10 @@ namespace android {
|
||||
extern AHardwareBuffer* android_hardware_HardwareBuffer_getNativeHardwareBuffer(
|
||||
JNIEnv* env, jobject hardwareBufferObj);
|
||||
|
||||
/* Gets the underlying GraphicBuffer for a HardwareBuffer. */
|
||||
extern GraphicBuffer* android_hardware_HardwareBuffer_getNativeGraphicBuffer(
|
||||
JNIEnv* env, jobject hardwareBufferObj);
|
||||
|
||||
/* Returns a HardwareBuffer wrapper for the underlying AHardwareBuffer. */
|
||||
extern jobject android_hardware_HardwareBuffer_createFromAHardwareBuffer(
|
||||
JNIEnv* env, AHardwareBuffer* hardwareBuffer);
|
||||
|
||||
@@ -211,6 +211,23 @@ public abstract class Image implements AutoCloseable {
|
||||
@SuppressWarnings("HiddenAbstractMethod")
|
||||
public abstract int getScalingMode();
|
||||
|
||||
/**
|
||||
* Get the fence file descriptor associated with this frame.
|
||||
* @return The fence file descriptor for this frame.
|
||||
* @hide
|
||||
*/
|
||||
public int getFenceFd() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of planes.
|
||||
* @return The number of expected planes.
|
||||
* @hide
|
||||
*/
|
||||
public int getPlaneCount() {
|
||||
return -1;
|
||||
}
|
||||
/**
|
||||
* Get the {@link android.hardware.HardwareBuffer HardwareBuffer} handle of the input image
|
||||
* intended for GPU and/or hardware access.
|
||||
@@ -330,8 +347,9 @@ public abstract class Image implements AutoCloseable {
|
||||
* @return true if the image is attachable to a new owner, false if the image is still attached
|
||||
* to its current owner, or the image is a stand-alone image and is not attachable to
|
||||
* a new owner.
|
||||
* @hide
|
||||
*/
|
||||
boolean isAttachable() {
|
||||
public boolean isAttachable() {
|
||||
throwISEIfImageIsInvalid();
|
||||
|
||||
return false;
|
||||
|
||||
@@ -18,8 +18,10 @@ package android.media;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.ImageFormat.Format;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.hardware.HardwareBuffer.Usage;
|
||||
import android.os.Handler;
|
||||
@@ -715,8 +717,9 @@ public class ImageReader implements AutoCloseable {
|
||||
* @throws IllegalStateException If the ImageReader or image have been
|
||||
* closed, or the has been detached, or has not yet been
|
||||
* acquired.
|
||||
* @hide
|
||||
*/
|
||||
void detachImage(Image image) {
|
||||
public void detachImage(Image image) {
|
||||
if (image == null) {
|
||||
throw new IllegalArgumentException("input image must not be null");
|
||||
}
|
||||
@@ -897,6 +900,18 @@ public class ImageReader implements AutoCloseable {
|
||||
return mScalingMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlaneCount() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return ImageReader.this.mNumPlanes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFenceFd() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return nativeGetFenceFd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HardwareBuffer getHardwareBuffer() {
|
||||
throwISEIfImageIsInvalid();
|
||||
@@ -931,7 +946,7 @@ public class ImageReader implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isAttachable() {
|
||||
public boolean isAttachable() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return mIsDetached.get();
|
||||
}
|
||||
@@ -1048,6 +1063,7 @@ public class ImageReader implements AutoCloseable {
|
||||
private synchronized native int nativeGetWidth();
|
||||
private synchronized native int nativeGetHeight();
|
||||
private synchronized native int nativeGetFormat(int readerFormat);
|
||||
private synchronized native int nativeGetFenceFd();
|
||||
private synchronized native HardwareBuffer nativeGetHardwareBuffer();
|
||||
}
|
||||
|
||||
@@ -1068,6 +1084,67 @@ public class ImageReader implements AutoCloseable {
|
||||
*/
|
||||
private synchronized native int nativeImageSetup(Image i);
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static class ImagePlane extends android.media.Image.Plane {
|
||||
private ImagePlane(int rowStride, int pixelStride, ByteBuffer buffer) {
|
||||
mRowStride = rowStride;
|
||||
mPixelStride = pixelStride;
|
||||
mBuffer = buffer;
|
||||
/**
|
||||
* Set the byteBuffer order according to host endianness (native
|
||||
* order), otherwise, the byteBuffer order defaults to
|
||||
* ByteOrder.BIG_ENDIAN.
|
||||
*/
|
||||
mBuffer.order(ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getBuffer() {
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPixelStride() {
|
||||
return mPixelStride;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowStride() {
|
||||
return mRowStride;
|
||||
}
|
||||
|
||||
final private int mPixelStride;
|
||||
final private int mRowStride;
|
||||
|
||||
private ByteBuffer mBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static ImagePlane[] initializeImagePlanes(int numPlanes,
|
||||
GraphicBuffer buffer, int fenceFd, int format, long timestamp, int transform,
|
||||
int scalingMode, Rect crop) {
|
||||
|
||||
return nativeCreateImagePlanes(numPlanes, buffer, fenceFd, format, crop.left, crop.top,
|
||||
crop.right, crop.bottom);
|
||||
}
|
||||
|
||||
private synchronized static native ImagePlane[] nativeCreateImagePlanes(int numPlanes,
|
||||
GraphicBuffer buffer, int fenceFd, int format, int cropLeft, int cropTop,
|
||||
int cropRight, int cropBottom);
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static void unlockGraphicBuffer(GraphicBuffer buffer) {
|
||||
nativeUnlockGraphicBuffer(buffer);
|
||||
}
|
||||
|
||||
private synchronized static native void nativeUnlockGraphicBuffer(GraphicBuffer buffer);
|
||||
|
||||
/**
|
||||
* We use a class initializer to allow the native code to cache some
|
||||
* field offsets.
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.media;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.ImageFormat.Format;
|
||||
import android.graphics.PixelFormat;
|
||||
@@ -360,16 +361,20 @@ public class ImageWriter implements AutoCloseable {
|
||||
throw new IllegalStateException("Image from ImageWriter is invalid");
|
||||
}
|
||||
|
||||
// For images from other components, need to detach first, then attach.
|
||||
// For images from other components that have non-null owner, need to detach first,
|
||||
// then attach. Images without owners must already be attachable.
|
||||
if (!ownedByMe) {
|
||||
if (!(image.getOwner() instanceof ImageReader)) {
|
||||
if (image.getOwner() == null) {
|
||||
|
||||
} else if ((image.getOwner() instanceof ImageReader)) {
|
||||
ImageReader prevOwner = (ImageReader) image.getOwner();
|
||||
|
||||
prevOwner.detachImage(image);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Only images from ImageReader can be queued to"
|
||||
+ " ImageWriter, other image source is not supported yet!");
|
||||
}
|
||||
|
||||
ImageReader prevOwner = (ImageReader) image.getOwner();
|
||||
|
||||
prevOwner.detachImage(image);
|
||||
attachAndQueueInputImage(image);
|
||||
// This clears the native reference held by the original owner.
|
||||
// When this Image is detached later by this ImageWriter, the
|
||||
@@ -565,9 +570,18 @@ public class ImageWriter implements AutoCloseable {
|
||||
// need do some cleanup to make sure no orphaned
|
||||
// buffer caused leak.
|
||||
Rect crop = image.getCropRect();
|
||||
nativeAttachAndQueueImage(mNativeContext, image.getNativeContext(), image.getFormat(),
|
||||
image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
|
||||
image.getTransform(), image.getScalingMode());
|
||||
if (image.getNativeContext() != 0) {
|
||||
nativeAttachAndQueueImage(mNativeContext, image.getNativeContext(), image.getFormat(),
|
||||
image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
|
||||
image.getTransform(), image.getScalingMode());
|
||||
} else {
|
||||
GraphicBuffer gb = GraphicBuffer.createFromHardwareBuffer(image.getHardwareBuffer());
|
||||
nativeAttachAndQueueGraphicBuffer(mNativeContext, gb, image.getFormat(),
|
||||
image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
|
||||
image.getTransform(), image.getScalingMode());
|
||||
gb.destroy();
|
||||
image.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -771,7 +785,7 @@ public class ImageWriter implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isAttachable() {
|
||||
public boolean isAttachable() {
|
||||
throwISEIfImageIsInvalid();
|
||||
// Don't allow Image to be detached from ImageWriter for now, as no
|
||||
// detach API is exposed.
|
||||
@@ -898,6 +912,9 @@ public class ImageWriter implements AutoCloseable {
|
||||
private synchronized native int nativeAttachAndQueueImage(long nativeCtx,
|
||||
long imageNativeBuffer, int imageFormat, long timestampNs, int left,
|
||||
int top, int right, int bottom, int transform, int scalingMode);
|
||||
private synchronized native int nativeAttachAndQueueGraphicBuffer(long nativeCtx,
|
||||
GraphicBuffer graphicBuffer, int imageFormat, long timestampNs, int left,
|
||||
int top, int right, int bottom, int transform, int scalingMode);
|
||||
|
||||
private synchronized native void cancelImage(long nativeCtx, Image image);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
#include <android_runtime/android_view_Surface.h>
|
||||
#include <android_runtime/android_graphics_GraphicBuffer.h>
|
||||
#include <android_runtime/android_hardware_HardwareBuffer.h>
|
||||
#include <grallocusage/GrallocUsageConversion.h>
|
||||
|
||||
@@ -42,6 +43,8 @@
|
||||
#include <inttypes.h>
|
||||
#include <android/hardware_buffer_jni.h>
|
||||
|
||||
#include <ui/Rect.h>
|
||||
|
||||
#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext"
|
||||
#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mNativeBuffer"
|
||||
#define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID "mTimestamp"
|
||||
@@ -78,6 +81,11 @@ static struct {
|
||||
jmethodID ctor;
|
||||
} gSurfacePlaneClassInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
jmethodID ctor;
|
||||
} gImagePlaneClassInfo;
|
||||
|
||||
// Get an ID that's unique within this process.
|
||||
static int32_t createProcessUniqueId() {
|
||||
static volatile int32_t globalCounter = 0;
|
||||
@@ -347,6 +355,15 @@ static void ImageReader_classInit(JNIEnv* env, jclass clazz)
|
||||
"(Landroid/media/ImageReader$SurfaceImage;IILjava/nio/ByteBuffer;)V");
|
||||
LOG_ALWAYS_FATAL_IF(gSurfacePlaneClassInfo.ctor == NULL,
|
||||
"Can not find SurfacePlane constructor");
|
||||
|
||||
planeClazz = env->FindClass("android/media/ImageReader$ImagePlane");
|
||||
LOG_ALWAYS_FATAL_IF(planeClazz == NULL, "Can not find ImagePlane class");
|
||||
// FindClass only gives a local reference of jclass object.
|
||||
gImagePlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz);
|
||||
gImagePlaneClassInfo.ctor = env->GetMethodID(gImagePlaneClassInfo.clazz, "<init>",
|
||||
"(IILjava/nio/ByteBuffer;)V");
|
||||
LOG_ALWAYS_FATAL_IF(gImagePlaneClassInfo.ctor == NULL,
|
||||
"Can not find ImagePlane constructor");
|
||||
}
|
||||
|
||||
static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint width, jint height,
|
||||
@@ -729,6 +746,89 @@ static bool Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ImageReader_unlockGraphicBuffer(JNIEnv* env, jobject /*thiz*/,
|
||||
jobject buffer) {
|
||||
sp<GraphicBuffer> graphicBuffer =
|
||||
android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env, buffer);
|
||||
if (graphicBuffer.get() == NULL) {
|
||||
jniThrowRuntimeException(env, "Invalid graphic buffer!");
|
||||
}
|
||||
|
||||
status_t res = graphicBuffer->unlock();
|
||||
if (res != OK) {
|
||||
jniThrowRuntimeException(env, "unlock buffer failed");
|
||||
}
|
||||
}
|
||||
|
||||
static jobjectArray ImageReader_createImagePlanes(JNIEnv* env, jobject /*thiz*/,
|
||||
int numPlanes, jobject buffer, int fenceFd, int format, int cropLeft, int cropTop,
|
||||
int cropRight, int cropBottom)
|
||||
{
|
||||
ALOGV("%s: create ImagePlane array with size %d", __FUNCTION__, numPlanes);
|
||||
int rowStride = 0;
|
||||
int pixelStride = 0;
|
||||
uint8_t *pData = NULL;
|
||||
uint32_t dataSize = 0;
|
||||
jobject byteBuffer = NULL;
|
||||
|
||||
PublicFormat publicReaderFormat = static_cast<PublicFormat>(format);
|
||||
int halReaderFormat = mapPublicFormatToHalFormat(publicReaderFormat);
|
||||
|
||||
if (isFormatOpaque(halReaderFormat) && numPlanes > 0) {
|
||||
String8 msg;
|
||||
msg.appendFormat("Format 0x%x is opaque, thus not writable, the number of planes (%d)"
|
||||
" must be 0", halReaderFormat, numPlanes);
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException", msg.string());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobjectArray imagePlanes = env->NewObjectArray(numPlanes, gImagePlaneClassInfo.clazz,
|
||||
/*initial_element*/NULL);
|
||||
if (imagePlanes == NULL) {
|
||||
jniThrowRuntimeException(env, "Failed to create ImagePlane arrays,"
|
||||
" probably out of memory");
|
||||
return NULL;
|
||||
}
|
||||
if (isFormatOpaque(halReaderFormat)) {
|
||||
// Return 0 element surface array.
|
||||
return imagePlanes;
|
||||
}
|
||||
|
||||
LockedImage lockedImg = LockedImage();
|
||||
uint32_t lockUsage = GRALLOC_USAGE_SW_READ_OFTEN;
|
||||
|
||||
Rect cropRect(cropLeft, cropTop, cropRight, cropBottom);
|
||||
status_t res = lockImageFromBuffer(
|
||||
android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env, buffer), lockUsage,
|
||||
cropRect, fenceFd, &lockedImg);
|
||||
if (res != OK) {
|
||||
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
|
||||
"lock buffer failed for format 0x%x", format);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create all ImagePlanes
|
||||
for (int i = 0; i < numPlanes; i++) {
|
||||
if (!Image_getLockedImageInfo(env, &lockedImg, i, halReaderFormat,
|
||||
&pData, &dataSize, &pixelStride, &rowStride)) {
|
||||
return NULL;
|
||||
}
|
||||
byteBuffer = env->NewDirectByteBuffer(pData, dataSize);
|
||||
if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"Failed to allocate ByteBuffer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Finally, create this ImagePlane.
|
||||
jobject imagePlane = env->NewObject(gImagePlaneClassInfo.clazz,
|
||||
gImagePlaneClassInfo.ctor, rowStride, pixelStride, byteBuffer);
|
||||
env->SetObjectArrayElement(imagePlanes, i, imagePlane);
|
||||
}
|
||||
|
||||
return imagePlanes;
|
||||
}
|
||||
|
||||
static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz,
|
||||
int numPlanes, int readerFormat, uint64_t ndkReaderUsage)
|
||||
{
|
||||
@@ -801,6 +901,16 @@ static jint Image_getHeight(JNIEnv* env, jobject thiz)
|
||||
return getBufferHeight(buffer);
|
||||
}
|
||||
|
||||
static jint Image_getFenceFd(JNIEnv* env, jobject thiz)
|
||||
{
|
||||
BufferItem* buffer = Image_getBufferItem(env, thiz);
|
||||
if ((buffer != NULL) && (buffer->mFence.get() != NULL)){
|
||||
return buffer->mFence->get();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static jint Image_getFormat(JNIEnv* env, jobject thiz, jint readerFormat)
|
||||
{
|
||||
if (isFormatOpaque(readerFormat)) {
|
||||
@@ -844,6 +954,11 @@ static const JNINativeMethod gImageReaderMethods[] = {
|
||||
{"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup },
|
||||
{"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
|
||||
{"nativeDetachImage", "(Landroid/media/Image;)I", (void*)ImageReader_detachImage },
|
||||
{"nativeCreateImagePlanes",
|
||||
"(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
|
||||
(void*)ImageReader_createImagePlanes },
|
||||
{"nativeUnlockGraphicBuffer",
|
||||
"(Landroid/graphics/GraphicBuffer;)V", (void*)ImageReader_unlockGraphicBuffer },
|
||||
{"nativeDiscardFreeBuffers", "()V", (void*)ImageReader_discardFreeBuffers }
|
||||
};
|
||||
|
||||
@@ -853,6 +968,7 @@ static const JNINativeMethod gImageMethods[] = {
|
||||
{"nativeGetWidth", "()I", (void*)Image_getWidth },
|
||||
{"nativeGetHeight", "()I", (void*)Image_getHeight },
|
||||
{"nativeGetFormat", "(I)I", (void*)Image_getFormat },
|
||||
{"nativeGetFenceFd", "()I", (void*)Image_getFenceFd },
|
||||
{"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
|
||||
(void*)Image_getHardwareBuffer },
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <ui/PublicFormat.h>
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
#include <android_runtime/android_view_Surface.h>
|
||||
#include <android_runtime/android_graphics_GraphicBuffer.h>
|
||||
#include <android_runtime/android_hardware_HardwareBuffer.h>
|
||||
#include <private/android/AHardwareBufferHelpers.h>
|
||||
#include <jni.h>
|
||||
@@ -673,36 +674,12 @@ static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, j
|
||||
Image_setNativeContext(env, image, NULL, -1);
|
||||
}
|
||||
|
||||
static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nativeCtx,
|
||||
jlong nativeBuffer, jint imageFormat, jlong timestampNs, jint left, jint top,
|
||||
static status_t attachAndQeueuGraphicBuffer(JNIEnv* env, JNIImageWriterContext *ctx,
|
||||
sp<Surface> surface, sp<GraphicBuffer> gb, jlong timestampNs, jint left, jint top,
|
||||
jint right, jint bottom, jint transform, jint scalingMode) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
|
||||
if (ctx == NULL || thiz == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"ImageWriterContext is not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sp<Surface> surface = ctx->getProducer();
|
||||
status_t res = OK;
|
||||
if (isFormatOpaque(imageFormat) != isFormatOpaque(ctx->getBufferFormat())) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Image is guaranteed to be from ImageReader at this point, so it is safe to
|
||||
// cast to BufferItem pointer.
|
||||
BufferItem* buffer = reinterpret_cast<BufferItem*>(nativeBuffer);
|
||||
if (buffer == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"Image is not initialized or already closed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 1. Attach Image
|
||||
res = surface->attachBuffer(buffer->mGraphicBuffer.get());
|
||||
res = surface->attachBuffer(gb.get());
|
||||
if (res != OK) {
|
||||
ALOGE("Attach image failed: %s (%d)", strerror(-res), res);
|
||||
switch (res) {
|
||||
@@ -756,7 +733,7 @@ static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nat
|
||||
// queue the "attached" flag before calling queueBuffer. In case
|
||||
// queueBuffer() fails, remove it from the queue.
|
||||
ctx->queueAttachedFlag(true);
|
||||
res = anw->queueBuffer(anw.get(), buffer->mGraphicBuffer.get(), /*fenceFd*/
|
||||
res = anw->queueBuffer(anw.get(), gb.get(), /*fenceFd*/
|
||||
-1);
|
||||
if (res != OK) {
|
||||
ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
|
||||
@@ -779,6 +756,67 @@ static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nat
|
||||
return res;
|
||||
}
|
||||
|
||||
static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nativeCtx,
|
||||
jlong nativeBuffer, jint imageFormat, jlong timestampNs, jint left, jint top,
|
||||
jint right, jint bottom, jint transform, jint scalingMode) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
|
||||
if (ctx == NULL || thiz == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"ImageWriterContext is not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sp<Surface> surface = ctx->getProducer();
|
||||
if (isFormatOpaque(imageFormat) != isFormatOpaque(ctx->getBufferFormat())) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Image is guaranteed to be from ImageReader at this point, so it is safe to
|
||||
// cast to BufferItem pointer.
|
||||
BufferItem* buffer = reinterpret_cast<BufferItem*>(nativeBuffer);
|
||||
if (buffer == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"Image is not initialized or already closed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return attachAndQeueuGraphicBuffer(env, ctx, surface, buffer->mGraphicBuffer, timestampNs, left,
|
||||
top, right, bottom, transform, scalingMode);
|
||||
}
|
||||
|
||||
static jint ImageWriter_attachAndQueueGraphicBuffer(JNIEnv* env, jobject thiz, jlong nativeCtx,
|
||||
jobject buffer, jint format, jlong timestampNs, jint left, jint top,
|
||||
jint right, jint bottom, jint transform, jint scalingMode) {
|
||||
ALOGV("%s", __FUNCTION__);
|
||||
JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
|
||||
if (ctx == NULL || thiz == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"ImageWriterContext is not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sp<Surface> surface = ctx->getProducer();
|
||||
if (isFormatOpaque(format) != isFormatOpaque(ctx->getBufferFormat())) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sp<GraphicBuffer> graphicBuffer = android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env,
|
||||
buffer);
|
||||
if (graphicBuffer.get() == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"Trying to attach an invalid graphic buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return attachAndQeueuGraphicBuffer(env, ctx, surface, graphicBuffer, timestampNs, left,
|
||||
top, right, bottom, transform, scalingMode);
|
||||
}
|
||||
|
||||
// --------------------------Image methods---------------------------------------
|
||||
|
||||
static void Image_getNativeContext(JNIEnv* env, jobject thiz,
|
||||
@@ -1011,6 +1049,9 @@ static JNINativeMethod gImageWriterMethods[] = {
|
||||
(void*)ImageWriter_init },
|
||||
{"nativeClose", "(J)V", (void*)ImageWriter_close },
|
||||
{"nativeAttachAndQueueImage", "(JJIJIIIIII)I", (void*)ImageWriter_attachAndQueueImage },
|
||||
{"nativeAttachAndQueueGraphicBuffer",
|
||||
"(JLandroid/graphics/GraphicBuffer;IJIIIIII)I",
|
||||
(void*)ImageWriter_attachAndQueueGraphicBuffer },
|
||||
{"nativeDequeueInputImage", "(JLandroid/media/Image;)V", (void*)ImageWriter_dequeueImage },
|
||||
{"nativeQueueInputImage", "(JLandroid/media/Image;JIIIIII)V", (void*)ImageWriter_queueImage },
|
||||
{"cancelImage", "(JLandroid/media/Image;)V", (void*)ImageWriter_cancelImage },
|
||||
|
||||
29
packages/services/CameraExtensionsProxy/Android.bp
Normal file
29
packages/services/CameraExtensionsProxy/Android.bp
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright (C) 2020 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.
|
||||
//
|
||||
|
||||
android_app {
|
||||
name: "CameraExtensionsProxy",
|
||||
srcs: ["src/**/*.java"],
|
||||
libs: ["androidx.camera.extensions.stub"],
|
||||
platform_apis: true,
|
||||
certificate: "platform",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "CameraExtensionsProxy-aidl-sources",
|
||||
srcs: ["src/**/*.aidl"],
|
||||
path: "src",
|
||||
}
|
||||
18
packages/services/CameraExtensionsProxy/AndroidManifest.xml
Normal file
18
packages/services/CameraExtensionsProxy/AndroidManifest.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.camera">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:defaultToDeviceProtectedStorage="true"
|
||||
android:directBootAware="true">
|
||||
|
||||
<service android:name=".CameraExtensionsProxyService"
|
||||
android:exported="true">
|
||||
</service>
|
||||
<uses-library android:name="androidx.camera.extensions.impl" android:required="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">CameraExtensionsProxy</string>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,857 @@
|
||||
/**
|
||||
* Copyright (c) 2020, 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 com.android.camera;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraExtensionCharacteristics;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.extension.CaptureBundle;
|
||||
import android.hardware.camera2.extension.CaptureStageImpl;
|
||||
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
|
||||
import android.hardware.camera2.extension.ICaptureProcessorImpl;
|
||||
import android.hardware.camera2.extension.IPreviewExtenderImpl;
|
||||
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
|
||||
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
|
||||
import android.hardware.camera2.extension.ParcelImage;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
import android.hardware.camera2.extension.SizeList;
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
import android.hardware.camera2.TotalCaptureResult;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.media.Image;
|
||||
import android.media.ImageReader;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.Size;
|
||||
import android.view.Surface;
|
||||
|
||||
import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
|
||||
import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
|
||||
import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
|
||||
import androidx.camera.extensions.impl.BeautyPreviewExtenderImpl;
|
||||
import androidx.camera.extensions.impl.BokehImageCaptureExtenderImpl;
|
||||
import androidx.camera.extensions.impl.BokehPreviewExtenderImpl;
|
||||
import androidx.camera.extensions.impl.CaptureProcessorImpl;
|
||||
import androidx.camera.extensions.impl.ExtensionVersionImpl;
|
||||
import androidx.camera.extensions.impl.HdrImageCaptureExtenderImpl;
|
||||
import androidx.camera.extensions.impl.HdrPreviewExtenderImpl;
|
||||
import androidx.camera.extensions.impl.ImageCaptureExtenderImpl;
|
||||
import androidx.camera.extensions.impl.InitializerImpl;
|
||||
import androidx.camera.extensions.impl.NightImageCaptureExtenderImpl;
|
||||
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.RequestUpdateProcessorImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.List;
|
||||
|
||||
public class CameraExtensionsProxyService extends Service {
|
||||
private static final String TAG = "CameraExtensionsProxyService";
|
||||
|
||||
private static final String CAMERA_EXTENSION_VERSION_NAME =
|
||||
"androidx.camera.extensions.impl.ExtensionVersionImpl";
|
||||
private static final String LATEST_VERSION = "1.1.0";
|
||||
private static final String[] SUPPORTED_VERSION_PREFIXES = {"1.1.", "1.0."};
|
||||
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 LATEST_VERSION_SUPPORTED =
|
||||
EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(SUPPORTED_VERSION_PREFIXES[0]);
|
||||
|
||||
private static boolean checkForExtensions() {
|
||||
try {
|
||||
Class.forName(CAMERA_EXTENSION_VERSION_NAME);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String extensionVersion = (new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION);
|
||||
for (String supportedVersion : SUPPORTED_VERSION_PREFIXES) {
|
||||
if (extensionVersion.startsWith(supportedVersion)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A per-process global camera extension manager instance, to track and
|
||||
* initialize/release extensions depending on client activity.
|
||||
*/
|
||||
private static final class CameraExtensionManagerGlobal {
|
||||
private static final String TAG = "CameraExtensionManagerGlobal";
|
||||
private final int EXTENSION_DELAY_MS = 1000;
|
||||
|
||||
private final Handler mHandler;
|
||||
private final HandlerThread mHandlerThread;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private long mCurrentClientCount = 0;
|
||||
private ArraySet<Long> mActiveClients = new ArraySet<>();
|
||||
|
||||
// Singleton instance
|
||||
private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
|
||||
new CameraExtensionManagerGlobal();
|
||||
|
||||
// Singleton, don't allow construction
|
||||
private CameraExtensionManagerGlobal() {
|
||||
mHandlerThread = new HandlerThread(TAG);
|
||||
mHandlerThread.start();
|
||||
mHandler = new Handler(mHandlerThread.getLooper());
|
||||
}
|
||||
|
||||
private final static class InitializeHandler
|
||||
implements InitializerImpl.OnExtensionsInitializedCallback {
|
||||
private final InitializerFuture mStatusFuture;
|
||||
|
||||
public InitializeHandler(InitializerFuture statusFuture) {
|
||||
mStatusFuture = statusFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
mStatusFuture.setStatus(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int error) {
|
||||
mStatusFuture.setStatus(false);
|
||||
}
|
||||
}
|
||||
|
||||
private final static class ReleaseHandler
|
||||
implements InitializerImpl.OnExtensionsDeinitializedCallback {
|
||||
private final InitializerFuture mStatusFuture;
|
||||
|
||||
public ReleaseHandler(InitializerFuture statusFuture) {
|
||||
mStatusFuture = statusFuture;
|
||||
}
|
||||
|
||||
@Override public void onSuccess() {
|
||||
mStatusFuture.setStatus(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(int i) {
|
||||
mStatusFuture.setStatus(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static class InitializerFuture implements Future<Boolean> {
|
||||
private volatile Boolean mStatus;
|
||||
ConditionVariable mCondVar = new ConditionVariable(/*opened*/false);
|
||||
|
||||
public void setStatus(boolean status) {
|
||||
mStatus = status;
|
||||
mCondVar.open();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return false; // don't allow canceling this task
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return false; // can never cancel this task
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return mStatus != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get() {
|
||||
mCondVar.block();
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(long timeout, TimeUnit unit) throws TimeoutException {
|
||||
long timeoutMs = unit.convert(timeout, TimeUnit.MILLISECONDS);
|
||||
if (!mCondVar.block(timeoutMs)) {
|
||||
throw new TimeoutException(
|
||||
"Failed to receive status after " + timeout + " " + unit);
|
||||
}
|
||||
|
||||
if (mStatus == null) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static CameraExtensionManagerGlobal get() {
|
||||
return GLOBAL_CAMERA_MANAGER;
|
||||
}
|
||||
|
||||
public long registerClient(Context ctx) {
|
||||
synchronized (mLock) {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
if (mActiveClients.isEmpty()) {
|
||||
InitializerFuture status = new InitializerFuture();
|
||||
InitializerImpl.init(EXTENSIONS_VERSION, ctx, new InitializeHandler(status),
|
||||
new HandlerExecutor(mHandler));
|
||||
boolean initSuccess;
|
||||
try {
|
||||
initSuccess = status.get(EXTENSION_DELAY_MS,
|
||||
TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
Log.e(TAG, "Timed out while initializing camera extensions!");
|
||||
return -1;
|
||||
}
|
||||
if (!initSuccess) {
|
||||
Log.e(TAG, "Failed while initializing camera extensions!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long ret = mCurrentClientCount;
|
||||
mCurrentClientCount++;
|
||||
if (mCurrentClientCount < 0) {
|
||||
mCurrentClientCount = 0;
|
||||
}
|
||||
mActiveClients.add(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterClient(long clientId) {
|
||||
synchronized (mLock) {
|
||||
if (mActiveClients.remove(clientId) && mActiveClients.isEmpty() &&
|
||||
LATEST_VERSION_SUPPORTED) {
|
||||
InitializerFuture status = new InitializerFuture();
|
||||
InitializerImpl.deinit(new ReleaseHandler(status),
|
||||
new HandlerExecutor(mHandler));
|
||||
boolean releaseSuccess;
|
||||
try {
|
||||
releaseSuccess = status.get(EXTENSION_DELAY_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
Log.e(TAG, "Timed out while releasing camera extensions!");
|
||||
return;
|
||||
}
|
||||
if (!releaseSuccess) {
|
||||
Log.e(TAG, "Failed while releasing camera extensions!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
private static long registerClient(Context ctx) {
|
||||
if (!EXTENSIONS_PRESENT) {
|
||||
return -1;
|
||||
}
|
||||
return CameraExtensionManagerGlobal.get().registerClient(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static void unregisterClient(long clientId) {
|
||||
if (!EXTENSIONS_PRESENT) {
|
||||
return;
|
||||
}
|
||||
CameraExtensionManagerGlobal.get().unregisterClient(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> initializeExtension(
|
||||
int extensionType) {
|
||||
switch (extensionType) {
|
||||
case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC:
|
||||
return new Pair<>(new AutoPreviewExtenderImpl(),
|
||||
new AutoImageCaptureExtenderImpl());
|
||||
case CameraExtensionCharacteristics.EXTENSION_BEAUTY:
|
||||
return new Pair<>(new BeautyPreviewExtenderImpl(),
|
||||
new BeautyImageCaptureExtenderImpl());
|
||||
case CameraExtensionCharacteristics.EXTENSION_BOKEH:
|
||||
return new Pair<>(new BokehPreviewExtenderImpl(),
|
||||
new BokehImageCaptureExtenderImpl());
|
||||
case CameraExtensionCharacteristics.EXTENSION_HDR:
|
||||
return new Pair<>(new HdrPreviewExtenderImpl(), new HdrImageCaptureExtenderImpl());
|
||||
case CameraExtensionCharacteristics.EXTENSION_NIGHT:
|
||||
return new Pair<>(new NightPreviewExtenderImpl(),
|
||||
new NightImageCaptureExtenderImpl());
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown extension: " + extensionType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// This will setup the camera vendor tag descriptor in the service process
|
||||
try {
|
||||
CameraManager manager = getSystemService(CameraManager.class);
|
||||
|
||||
String [] cameraIds = manager.getCameraIdListNoLazy();
|
||||
if (cameraIds != null) {
|
||||
for (String cameraId : cameraIds) {
|
||||
CameraCharacteristics ch = manager.getCameraCharacteristics(cameraId);
|
||||
}
|
||||
}
|
||||
} catch (CameraAccessException e) {
|
||||
Log.e(TAG, "Failed to query camera characteristics!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new CameraExtensionsProxyServiceStub();
|
||||
}
|
||||
|
||||
private static List<SizeList> initializeParcelable(
|
||||
List<Pair<Integer, android.util.Size[]>> sizes) {
|
||||
if (sizes == null) {
|
||||
return null;
|
||||
}
|
||||
ArrayList<SizeList> ret = new ArrayList<>(sizes.size());
|
||||
for (Pair<Integer, Size[]> entry : sizes) {
|
||||
SizeList sizeList = new SizeList();
|
||||
sizeList.format = entry.first;
|
||||
sizeList.sizes = new ArrayList<>();
|
||||
for (android.util.Size size : entry.second) {
|
||||
android.hardware.camera2.extension.Size sz =
|
||||
new android.hardware.camera2.extension.Size();
|
||||
sz.width = size.getWidth();
|
||||
sz.height = size.getHeight();
|
||||
sizeList.sizes.add(sz);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static CameraMetadataNative initializeParcelableMetadata(
|
||||
List<Pair<CaptureRequest.Key, Object>> paramList) {
|
||||
if (paramList == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CameraMetadataNative ret = new CameraMetadataNative();
|
||||
for (Pair<CaptureRequest.Key, Object> param : paramList) {
|
||||
ret.set(param.first, param.second);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static android.hardware.camera2.extension.CaptureStageImpl initializeParcelable(
|
||||
androidx.camera.extensions.impl.CaptureStageImpl captureStage) {
|
||||
if (captureStage == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
android.hardware.camera2.extension.CaptureStageImpl ret =
|
||||
new android.hardware.camera2.extension.CaptureStageImpl();
|
||||
ret.id = captureStage.getId();
|
||||
ret.parameters = initializeParcelableMetadata(captureStage.getParameters());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private class CameraExtensionsProxyServiceStub extends ICameraExtensionsProxyService.Stub {
|
||||
@Override
|
||||
public long registerClient() {
|
||||
return CameraExtensionsProxyService.registerClient(CameraExtensionsProxyService.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterClient(long clientId) {
|
||||
CameraExtensionsProxyService.unregisterClient(clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPreviewExtenderImpl initializePreviewExtension(int extensionType) {
|
||||
Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> extension;
|
||||
try {
|
||||
extension = initializeExtension(extensionType);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PreviewExtenderImplStub(extension.first);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IImageCaptureExtenderImpl initializeImageExtension(int extensionType) {
|
||||
Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> extension;
|
||||
try {
|
||||
extension = initializeExtension(extensionType);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ImageCaptureExtenderImplStub(extension.second);
|
||||
}
|
||||
}
|
||||
|
||||
private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub {
|
||||
private final PreviewExtenderImpl mPreviewExtender;
|
||||
|
||||
public PreviewExtenderImplStub(PreviewExtenderImpl previewExtender) {
|
||||
mPreviewExtender = previewExtender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
|
||||
mPreviewExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
|
||||
CameraExtensionsProxyService.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeInit() {
|
||||
mPreviewExtender.onDeInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureStageImpl onPresetSession() {
|
||||
return initializeParcelable(mPreviewExtender.onPresetSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureStageImpl onEnableSession() {
|
||||
return initializeParcelable(mPreviewExtender.onEnableSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureStageImpl onDisableSession() {
|
||||
return initializeParcelable(mPreviewExtender.onDisableSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(String cameraId, CameraMetadataNative chars) {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
mPreviewExtender.init(cameraId, new CameraCharacteristics(chars));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExtensionAvailable(String cameraId, CameraMetadataNative chars) {
|
||||
return mPreviewExtender.isExtensionAvailable(cameraId,
|
||||
new CameraCharacteristics(chars));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureStageImpl getCaptureStage() {
|
||||
return initializeParcelable(mPreviewExtender.getCaptureStage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProcessorType() {
|
||||
ProcessorType processorType = mPreviewExtender.getProcessorType();
|
||||
if (processorType == ProcessorType.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) {
|
||||
return IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY;
|
||||
} else if (processorType == ProcessorType.PROCESSOR_TYPE_IMAGE_PROCESSOR) {
|
||||
return IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR;
|
||||
} else {
|
||||
return IPreviewExtenderImpl.PROCESSOR_TYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPreviewImageProcessorImpl getPreviewImageProcessor() {
|
||||
PreviewImageProcessorImpl processor;
|
||||
try {
|
||||
processor = (PreviewImageProcessorImpl) mPreviewExtender.getProcessor();
|
||||
} catch (ClassCastException e) {
|
||||
Log.e(TAG, "Failed casting preview processor!");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (processor != null) {
|
||||
return new PreviewImageProcessorImplStub(processor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRequestUpdateProcessorImpl getRequestUpdateProcessor() {
|
||||
RequestUpdateProcessorImpl processor;
|
||||
try {
|
||||
processor = (RequestUpdateProcessorImpl) mPreviewExtender.getProcessor();
|
||||
} catch (ClassCastException e) {
|
||||
Log.e(TAG, "Failed casting preview processor!");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (processor != null) {
|
||||
return new RequestUpdateProcessorImplStub(processor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SizeList> getSupportedResolutions() {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
List<Pair<Integer, android.util.Size[]>> sizes =
|
||||
mPreviewExtender.getSupportedResolutions();
|
||||
if ((sizes != null) && !sizes.isEmpty()) {
|
||||
return initializeParcelable(sizes);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class ImageCaptureExtenderImplStub extends IImageCaptureExtenderImpl.Stub {
|
||||
private final ImageCaptureExtenderImpl mImageExtender;
|
||||
|
||||
public ImageCaptureExtenderImplStub(ImageCaptureExtenderImpl imageExtender) {
|
||||
mImageExtender = imageExtender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
|
||||
mImageExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
|
||||
CameraExtensionsProxyService.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeInit() {
|
||||
mImageExtender.onDeInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureStageImpl onPresetSession() {
|
||||
return initializeParcelable(mImageExtender.onPresetSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureStageImpl onEnableSession() {
|
||||
return initializeParcelable(mImageExtender.onEnableSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureStageImpl onDisableSession() {
|
||||
return initializeParcelable(mImageExtender.onDisableSession());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(String cameraId, CameraMetadataNative chars) {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
mImageExtender.init(cameraId, new CameraCharacteristics(chars));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExtensionAvailable(String cameraId, CameraMetadataNative chars) {
|
||||
return mImageExtender.isExtensionAvailable(cameraId,
|
||||
new CameraCharacteristics(chars));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICaptureProcessorImpl getCaptureProcessor() {
|
||||
CaptureProcessorImpl captureProcessor = mImageExtender.getCaptureProcessor();
|
||||
if (captureProcessor != null) {
|
||||
return new CaptureProcessorImplStub(captureProcessor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<CaptureStageImpl> getCaptureStages() {
|
||||
List<androidx.camera.extensions.impl.CaptureStageImpl> captureStages =
|
||||
mImageExtender.getCaptureStages();
|
||||
if (captureStages != null) {
|
||||
ArrayList<android.hardware.camera2.extension.CaptureStageImpl> ret =
|
||||
new ArrayList<>();
|
||||
for (androidx.camera.extensions.impl.CaptureStageImpl stage : captureStages) {
|
||||
ret.add(initializeParcelable(stage));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxCaptureStage() {
|
||||
return mImageExtender.getMaxCaptureStage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SizeList> getSupportedResolutions() {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
List<Pair<Integer, android.util.Size[]>> sizes =
|
||||
mImageExtender.getSupportedResolutions();
|
||||
if ((sizes != null) && !sizes.isEmpty()) {
|
||||
return initializeParcelable(sizes);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class CaptureProcessorImplStub extends ICaptureProcessorImpl.Stub {
|
||||
private final CaptureProcessorImpl mCaptureProcessor;
|
||||
|
||||
public CaptureProcessorImplStub(CaptureProcessorImpl captureProcessor) {
|
||||
mCaptureProcessor = captureProcessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOutputSurface(Surface surface, int imageFormat) {
|
||||
mCaptureProcessor.onOutputSurface(surface, imageFormat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResolutionUpdate(android.hardware.camera2.extension.Size size) {
|
||||
mCaptureProcessor.onResolutionUpdate(new android.util.Size(size.width, size.height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageFormatUpdate(int imageFormat) {
|
||||
mCaptureProcessor.onImageFormatUpdate(imageFormat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(List<CaptureBundle> captureList) {
|
||||
HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap = new HashMap<>();
|
||||
for (CaptureBundle captureBundle : captureList) {
|
||||
captureMap.put(captureBundle.stage, new Pair<> (
|
||||
new ExtensionImage(captureBundle.captureImage),
|
||||
new TotalCaptureResult(captureBundle.captureResult,
|
||||
captureBundle.sequenceId)));
|
||||
}
|
||||
if (!captureMap.isEmpty()) {
|
||||
mCaptureProcessor.process(captureMap);
|
||||
} else {
|
||||
Log.e(TAG, "Process request with absent capture stages!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PreviewImageProcessorImplStub extends IPreviewImageProcessorImpl.Stub {
|
||||
private final PreviewImageProcessorImpl mProcessor;
|
||||
|
||||
public PreviewImageProcessorImplStub(PreviewImageProcessorImpl processor) {
|
||||
mProcessor = processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOutputSurface(Surface surface, int imageFormat) {
|
||||
mProcessor.onOutputSurface(surface, imageFormat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResolutionUpdate(android.hardware.camera2.extension.Size size) {
|
||||
mProcessor.onResolutionUpdate(new android.util.Size(size.width, size.height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageFormatUpdate(int imageFormat) {
|
||||
mProcessor.onImageFormatUpdate(imageFormat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(android.hardware.camera2.extension.ParcelImage image,
|
||||
CameraMetadataNative result, int sequenceId) {
|
||||
mProcessor.process(new ExtensionImage(image),
|
||||
new TotalCaptureResult(result, sequenceId));
|
||||
}
|
||||
}
|
||||
|
||||
private class RequestUpdateProcessorImplStub extends IRequestUpdateProcessorImpl.Stub {
|
||||
private final RequestUpdateProcessorImpl mProcessor;
|
||||
|
||||
public RequestUpdateProcessorImplStub(RequestUpdateProcessorImpl processor) {
|
||||
mProcessor = processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOutputSurface(Surface surface, int imageFormat) {
|
||||
mProcessor.onOutputSurface(surface, imageFormat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResolutionUpdate(android.hardware.camera2.extension.Size size) {
|
||||
mProcessor.onResolutionUpdate(new android.util.Size(size.width, size.height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageFormatUpdate(int imageFormat) {
|
||||
mProcessor.onImageFormatUpdate(imageFormat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptureStageImpl process(CameraMetadataNative result, int sequenceId) {
|
||||
return initializeParcelable(
|
||||
mProcessor.process(new TotalCaptureResult(result, sequenceId)));
|
||||
}
|
||||
}
|
||||
|
||||
private class ExtensionImage extends android.media.Image {
|
||||
private final android.hardware.camera2.extension.ParcelImage mParcelImage;
|
||||
private GraphicBuffer mGraphicBuffer;
|
||||
private ImageReader.ImagePlane[] mPlanes;
|
||||
|
||||
private ExtensionImage(android.hardware.camera2.extension.ParcelImage parcelImage) {
|
||||
mParcelImage = parcelImage;
|
||||
mIsImageValid = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFormat() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return mParcelImage.format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return mParcelImage.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HardwareBuffer getHardwareBuffer() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return mParcelImage.buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return mParcelImage.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return mParcelImage.timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransform() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return mParcelImage.transform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScalingMode() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return mParcelImage.scalingMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plane[] getPlanes() {
|
||||
throwISEIfImageIsInvalid();
|
||||
if (mPlanes == null) {
|
||||
int fenceFd = mParcelImage.fence != null ? mParcelImage.fence.getFd() : -1;
|
||||
mGraphicBuffer = GraphicBuffer.createFromHardwareBuffer(mParcelImage.buffer);
|
||||
mPlanes = ImageReader.initializeImagePlanes(mParcelImage.planeCount, mGraphicBuffer,
|
||||
fenceFd, mParcelImage.format, mParcelImage.timestamp,
|
||||
mParcelImage.transform, mParcelImage.scalingMode, mParcelImage.crop);
|
||||
}
|
||||
// Shallow copy is fine.
|
||||
return mPlanes.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void finalize() throws Throwable {
|
||||
try {
|
||||
close();
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttachable() {
|
||||
throwISEIfImageIsInvalid();
|
||||
// Clients must always detach parcelable images
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getCropRect() {
|
||||
throwISEIfImageIsInvalid();
|
||||
return mParcelImage.crop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mIsImageValid = false;
|
||||
|
||||
if (mGraphicBuffer != null) {
|
||||
ImageReader.unlockGraphicBuffer(mGraphicBuffer);
|
||||
mGraphicBuffer.destroy();
|
||||
mGraphicBuffer = null;
|
||||
}
|
||||
|
||||
if (mPlanes != null) {
|
||||
mPlanes = null;
|
||||
}
|
||||
|
||||
if (mParcelImage.buffer != null) {
|
||||
mParcelImage.buffer.close();
|
||||
mParcelImage.buffer = null;
|
||||
}
|
||||
|
||||
if (mParcelImage.fence != null) {
|
||||
try {
|
||||
mParcelImage.fence.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mParcelImage.fence = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user