blob: b0893cc360b7ae5083bfc534f70571eb90753840 [file] [log] [blame]
/**
* 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.cameraextensions;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
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.CaptureResult;
import android.hardware.camera2.extension.CameraOutputConfig;
import android.hardware.camera2.extension.CameraSessionConfig;
import android.hardware.camera2.extension.CaptureBundle;
import android.hardware.camera2.extension.CaptureFailure;
import android.hardware.camera2.extension.CaptureStageImpl;
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
import android.hardware.camera2.extension.ICaptureCallback;
import android.hardware.camera2.extension.ICaptureProcessorImpl;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
import android.hardware.camera2.extension.IRequestCallback;
import android.hardware.camera2.extension.IRequestProcessorImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IImageProcessorImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.ISessionProcessorImpl;
import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.OutputConfigId;
import android.hardware.camera2.extension.OutputSurface;
import android.hardware.camera2.extension.ParcelCaptureResult;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.extension.ParcelTotalCaptureResult;
import android.hardware.camera2.extension.Request;
import android.hardware.camera2.extension.SizeList;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.HardwareBuffer;
import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
import android.media.Image;
import android.media.ImageReader;
import android.os.Binder;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Range;
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 androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.BeautyAdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.BokehAdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl;
import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl;
import androidx.camera.extensions.impl.advanced.HdrAdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.ImageProcessorImpl;
import androidx.camera.extensions.impl.advanced.ImageReaderOutputConfigImpl;
import androidx.camera.extensions.impl.advanced.MultiResolutionImageReaderOutputConfigImpl;
import androidx.camera.extensions.impl.advanced.NightAdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl;
import androidx.camera.extensions.impl.advanced.RequestProcessorImpl;
import androidx.camera.extensions.impl.advanced.SessionProcessorImpl;
import androidx.camera.extensions.impl.advanced.SurfaceOutputConfigImpl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
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.2.0";
private static final String NON_INIT_VERSION_PREFIX = "1.0";
private static final String ADVANCED_VERSION_PREFIX = "1.2";
private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX,
"1.1", NON_INIT_VERSION_PREFIX};
private static final boolean EXTENSIONS_PRESENT = checkForExtensions();
private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ?
(new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null;
private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI();
private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT &&
(!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX));
private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
private static boolean checkForAdvancedAPI() {
if (EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX)) {
try {
return (new ExtensionVersionImpl()).isAdvancedExtenderImplemented();
} catch (NoSuchMethodError e) {
// This could happen in case device specific extension implementations are using an
// older extension API but incorrectly set the extension version.
}
}
return false;
}
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<>();
private IInitializeSessionCallback mInitializeCb = null;
// 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 (INIT_API_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() &&
INIT_API_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!");
}
}
}
}
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
synchronized (mLock) {
mInitializeCb = null;
}
}
};
public boolean initializeSession(IInitializeSessionCallback cb) {
synchronized (mLock) {
if (mInitializeCb == null) {
mInitializeCb = cb;
try {
mInitializeCb.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
return false;
}
}
return true;
}
public void releaseSession() {
synchronized (mLock) {
if (mInitializeCb != null) {
mInitializeCb.asBinder().unlinkToDeath(mDeathRecipient, 0);
mInitializeCb = null;
}
}
}
}
/**
* @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 boolean initializeSession(IInitializeSessionCallback cb) {
if (!EXTENSIONS_PRESENT) {
return false;
}
return CameraExtensionManagerGlobal.get().initializeSession(cb);
}
/**
* @hide
*/
public static void releaseSession() {
if (!EXTENSIONS_PRESENT) {
return;
}
CameraExtensionManagerGlobal.get().releaseSession();
}
/**
* @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);
}
}
/**
* @hide
*/
public static AdvancedExtenderImpl initializeAdvancedExtensionImpl(int extensionType) {
switch (extensionType) {
case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC:
return new AutoAdvancedExtenderImpl();
case CameraExtensionCharacteristics.EXTENSION_BEAUTY:
return new BeautyAdvancedExtenderImpl();
case CameraExtensionCharacteristics.EXTENSION_BOKEH:
return new BokehAdvancedExtenderImpl();
case CameraExtensionCharacteristics.EXTENSION_HDR:
return new HdrAdvancedExtenderImpl();
case CameraExtensionCharacteristics.EXTENSION_NIGHT:
return new NightAdvancedExtenderImpl();
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
// along with all camera characteristics.
try {
CameraManager manager = getSystemService(CameraManager.class);
String [] cameraIds = manager.getCameraIdListNoLazy();
if (cameraIds != null) {
for (String cameraId : cameraIds) {
CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
mCharacteristicsHashMap.put(cameraId, chars);
Object thisClass = CameraCharacteristics.Key.class;
Class<CameraCharacteristics.Key<?>> keyClass =
(Class<CameraCharacteristics.Key<?>>)thisClass;
ArrayList<CameraCharacteristics.Key<?>> vendorKeys =
chars.getNativeMetadata().getAllVendorKeys(keyClass);
if ((vendorKeys != null) && !vendorKeys.isEmpty()) {
mMetadataVendorIdMap.put(cameraId, vendorKeys.get(0).getVendorId());
}
}
}
} 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 List<SizeList> initializeParcelable(
Map<Integer, List<android.util.Size>> sizes) {
if (sizes == null) {
return null;
}
ArrayList<SizeList> ret = new ArrayList<>(sizes.size());
for (Map.Entry<Integer, List<android.util.Size>> entry : sizes.entrySet()) {
SizeList sizeList = new SizeList();
sizeList.format = entry.getKey();
sizeList.sizes = new ArrayList<>();
for (android.util.Size size : entry.getValue()) {
android.hardware.camera2.extension.Size sz =
new android.hardware.camera2.extension.Size();
sz.width = size.getWidth();
sz.height = size.getHeight();
sizeList.sizes.add(sz);
}
ret.add(sizeList);
}
return ret;
}
private CameraMetadataNative initializeParcelableMetadata(
List<Pair<CaptureRequest.Key, Object>> paramList, String cameraId) {
if (paramList == null) {
return null;
}
CameraMetadataNative ret = new CameraMetadataNative();
if (mMetadataVendorIdMap.containsKey(cameraId)) {
ret.setVendorId(mMetadataVendorIdMap.get(cameraId));
}
for (Pair<CaptureRequest.Key, Object> param : paramList) {
ret.set(param.first, param.second);
}
return ret;
}
private CameraMetadataNative initializeParcelableMetadata(
Map<CaptureRequest.Key<?>, Object> paramMap, String cameraId) {
if (paramMap == null) {
return null;
}
CameraMetadataNative ret = new CameraMetadataNative();
if (mMetadataVendorIdMap.containsKey(cameraId)) {
ret.setVendorId(mMetadataVendorIdMap.get(cameraId));
}
for (Map.Entry<CaptureRequest.Key<?>, Object> param : paramMap.entrySet()) {
ret.set(((CaptureRequest.Key) param.getKey()), param.getValue());
}
return ret;
}
private android.hardware.camera2.extension.CaptureStageImpl initializeParcelable(
androidx.camera.extensions.impl.CaptureStageImpl captureStage, String cameraId) {
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(), cameraId);
return ret;
}
private Request initializeParcelable(RequestProcessorImpl.Request request, int requestId,
String cameraId) {
Request ret = new Request();
ret.targetOutputConfigIds = new ArrayList<>();
for (int id : request.getTargetOutputConfigIds()) {
OutputConfigId configId = new OutputConfigId();
configId.id = id;
ret.targetOutputConfigIds.add(configId);
}
ret.templateId = request.getTemplateId();
ret.parameters = initializeParcelableMetadata(request.getParameters(), cameraId);
ret.requestId = requestId;
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);
}
private boolean checkCameraPermission() {
int allowed = CameraExtensionsProxyService.this.checkPermission(
android.Manifest.permission.CAMERA, Binder.getCallingPid(),
Binder.getCallingUid());
return (PackageManager.PERMISSION_GRANTED == allowed);
}
@Override
public void initializeSession(IInitializeSessionCallback cb) {
try {
if (!checkCameraPermission()) {
Log.i(TAG, "Camera permission required for initializing capture session");
cb.onFailure();
return;
}
if (CameraExtensionsProxyService.initializeSession(cb)) {
cb.onSuccess();
} else {
cb.onFailure();
}
} catch (RemoteException e) {
Log.e(TAG, "Client doesn't respond!");
}
}
@Override
public void releaseSession() {
if (checkCameraPermission()) {
CameraExtensionsProxyService.releaseSession();
}
}
@Override
public boolean advancedExtensionsSupported() {
return ADVANCED_API_SUPPORTED;
}
@Override
public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType) {
AdvancedExtenderImpl extension;
try {
extension = initializeAdvancedExtensionImpl(extensionType);
} catch (IllegalArgumentException e) {
return null;
}
return new AdvancedExtenderImplStub(extension);
}
@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 AdvancedExtenderImplStub extends IAdvancedExtenderImpl.Stub {
private final AdvancedExtenderImpl mAdvancedExtender;
public AdvancedExtenderImplStub(AdvancedExtenderImpl advancedExtender) {
mAdvancedExtender = advancedExtender;
}
@Override
public boolean isExtensionAvailable(String cameraId) {
return mAdvancedExtender.isExtensionAvailable(cameraId, mCharacteristicsHashMap);
}
@Override
public void init(String cameraId) {
mAdvancedExtender.init(cameraId, mCharacteristicsHashMap);
}
@Override
public List<SizeList> getSupportedPreviewOutputResolutions(String cameraId) {
Map<Integer, List<Size>> supportedSizesMap =
mAdvancedExtender.getSupportedPreviewOutputResolutions(cameraId);
if (supportedSizesMap != null) {
return initializeParcelable(supportedSizesMap);
}
return null;
}
@Override
public List<SizeList> getSupportedCaptureOutputResolutions(String cameraId) {
Map<Integer, List<Size>> supportedSizesMap =
mAdvancedExtender.getSupportedCaptureOutputResolutions(cameraId);
if (supportedSizesMap != null) {
return initializeParcelable(supportedSizesMap);
}
return null;
}
@Override
public LatencyRange getEstimatedCaptureLatencyRange(String cameraId,
android.hardware.camera2.extension.Size outputSize, int format) {
Size sz = new Size(outputSize.width, outputSize.height);
Range<Long> latencyRange = mAdvancedExtender.getEstimatedCaptureLatencyRange(cameraId,
sz, format);
if (latencyRange != null) {
LatencyRange ret = new LatencyRange();
ret.min = latencyRange.getLower();
ret.max = latencyRange.getUpper();
return ret;
}
return null;
}
@Override
public ISessionProcessorImpl getSessionProcessor() {
return new SessionProcessorImplStub(mAdvancedExtender.createSessionProcessor());
}
}
private class CaptureCallbackStub implements SessionProcessorImpl.CaptureCallback {
private final ICaptureCallback mCaptureCallback;
private CaptureCallbackStub(ICaptureCallback captureCallback) {
mCaptureCallback = captureCallback;
}
@Override
public void onCaptureStarted(int captureSequenceId, long timestamp) {
if (mCaptureCallback != null) {
try {
mCaptureCallback.onCaptureStarted(captureSequenceId, timestamp);
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify capture start due to remote " +
"exception!");
}
}
}
@Override
public void onCaptureProcessStarted(int captureSequenceId) {
if (mCaptureCallback != null) {
try {
mCaptureCallback.onCaptureProcessStarted(captureSequenceId);
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify capture process start due to remote " +
"exception!");
}
}
}
@Override
public void onCaptureFailed(int captureSequenceId) {
if (mCaptureCallback != null) {
try {
mCaptureCallback.onCaptureFailed(captureSequenceId);
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify capture failure due to remote " +
"exception!");
}
}
}
@Override
public void onCaptureSequenceCompleted(int captureSequenceId) {
if (mCaptureCallback != null) {
try {
mCaptureCallback.onCaptureSequenceCompleted(captureSequenceId);
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify capture sequence end due to remote " +
"exception!");
}
}
}
@Override
public void onCaptureSequenceAborted(int captureSequenceId) {
if (mCaptureCallback != null) {
try {
mCaptureCallback.onCaptureSequenceAborted(captureSequenceId);
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify capture sequence abort due to remote " +
"exception!");
}
}
}
}
private class RequestCallbackStub extends IRequestCallback.Stub {
private final List<RequestProcessorImpl.Request> mRequests;
private final RequestProcessorImpl.Callback mCallback;
public RequestCallbackStub(List<RequestProcessorImpl.Request> requests,
RequestProcessorImpl.Callback callback) {
mCallback = callback;
if (mCallback != null) {
mRequests = requests;
} else {
Log.w(TAG, "No valid request callbacks!");
mRequests = new ArrayList<>();
}
}
@Override
public void onCaptureStarted(int requestId, long frameNumber, long timestamp) {
if (mCallback != null) {
if (mRequests.get(requestId) != null) {
mCallback.onCaptureStarted(mRequests.get(requestId), frameNumber, timestamp);
} else {
Log.e(TAG,"Request id: " + requestId + " not found!");
}
}
}
@Override
public void onCaptureProgressed(int requestId, ParcelCaptureResult partialResult) {
if (mCallback != null) {
if (mRequests.get(requestId) != null) {
CaptureResult result = new CaptureResult(partialResult.cameraId,
partialResult.results, partialResult.parent, partialResult.sequenceId,
partialResult.frameNumber);
mCallback.onCaptureProgressed(mRequests.get(requestId), result);
} else {
Log.e(TAG,"Request id: " + requestId + " not found!");
}
}
}
@Override
public void onCaptureCompleted(int requestId, ParcelTotalCaptureResult totalCaptureResult) {
if (mCallback != null) {
if (mRequests.get(requestId) != null) {
PhysicalCaptureResultInfo[] physicalResults = new PhysicalCaptureResultInfo[0];
if ((totalCaptureResult.physicalResult != null) &&
(!totalCaptureResult.physicalResult.isEmpty())) {
int count = totalCaptureResult.physicalResult.size();
physicalResults = new PhysicalCaptureResultInfo[count];
physicalResults = totalCaptureResult.physicalResult.toArray(
physicalResults);
}
ArrayList<CaptureResult> partials = new ArrayList<>(
totalCaptureResult.partials.size());
for (ParcelCaptureResult parcelResult : totalCaptureResult.partials) {
partials.add(new CaptureResult(parcelResult.cameraId, parcelResult.results,
parcelResult.parent, parcelResult.sequenceId,
parcelResult.frameNumber));
}
TotalCaptureResult result = new TotalCaptureResult(
totalCaptureResult.logicalCameraId, totalCaptureResult.results,
totalCaptureResult.parent, totalCaptureResult.sequenceId,
totalCaptureResult.frameNumber, partials, totalCaptureResult.sessionId,
physicalResults);
mCallback.onCaptureCompleted(mRequests.get(requestId), result);
} else {
Log.e(TAG,"Request id: " + requestId + " not found!");
}
}
}
@Override
public void onCaptureFailed(int requestId, CaptureFailure captureFailure) {
if (mCallback != null) {
if (mRequests.get(requestId) != null) {
android.hardware.camera2.CaptureFailure failure =
new android.hardware.camera2.CaptureFailure(captureFailure.request,
captureFailure.reason, captureFailure.dropped,
captureFailure.sequenceId, captureFailure.frameNumber,
captureFailure.errorPhysicalCameraId);
mCallback.onCaptureFailed(mRequests.get(requestId), failure);
} else {
Log.e(TAG,"Request id: " + requestId + " not found!");
}
}
}
@Override
public void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId) {
if (mCallback != null) {
if (mRequests.get(requestId) != null) {
mCallback.onCaptureBufferLost(mRequests.get(requestId), frameNumber,
outputStreamId);
} else {
Log.e(TAG,"Request id: " + requestId + " not found!");
}
}
}
@Override
public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
if (mCallback != null) {
mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber);
}
}
@Override
public void onCaptureSequenceAborted(int sequenceId) {
if (mCallback != null) {
mCallback.onCaptureSequenceAborted(sequenceId);
}
}
}
private class ImageProcessorImplStub extends IImageProcessorImpl.Stub {
private final ImageProcessorImpl mImageProcessor;
public ImageProcessorImplStub(ImageProcessorImpl imageProcessor) {
mImageProcessor = imageProcessor;
}
@Override
public void onNextImageAvailable(OutputConfigId outputConfigId, ParcelImage img,
String physicalCameraId) {
if (mImageProcessor != null) {
mImageProcessor.onNextImageAvailable(outputConfigId.id, img.timestamp,
new ImageReferenceImpl(img), physicalCameraId);
}
}
}
private class RequestProcessorStub implements RequestProcessorImpl {
private final IRequestProcessorImpl mRequestProcessor;
private final String mCameraId;
public RequestProcessorStub(IRequestProcessorImpl requestProcessor, String cameraId) {
mRequestProcessor = requestProcessor;
mCameraId = cameraId;
}
@Override
public void setImageProcessor(int outputConfigId,
ImageProcessorImpl imageProcessor) {
OutputConfigId configId = new OutputConfigId();
configId.id = outputConfigId;
try {
mRequestProcessor.setImageProcessor(configId,
new ImageProcessorImplStub(imageProcessor));
} catch (RemoteException e) {
Log.e(TAG, "Failed to set image processor due to remote exception!");
}
}
@Override
public int submit(Request request, Callback callback) {
ArrayList<Request> requests = new ArrayList<>();
requests.add(request);
return submit(requests, callback);
}
@Override
public int submit(List<Request> requests, Callback callback) {
ArrayList<android.hardware.camera2.extension.Request> captureRequests =
new ArrayList<>();
int requestId = 0;
for (Request request : requests) {
captureRequests.add(initializeParcelable(request, requestId, mCameraId));
requestId++;
}
try {
return mRequestProcessor.submitBurst(captureRequests,
new RequestCallbackStub(requests, callback));
} catch (RemoteException e) {
Log.e(TAG, "Failed to submit request due to remote exception!");
}
return -1;
}
@Override
public int setRepeating(Request request, Callback callback) {
try {
ArrayList<Request> requests = new ArrayList<>();
requests.add(request);
return mRequestProcessor.setRepeating(
initializeParcelable(request, 0, mCameraId),
new RequestCallbackStub(requests, callback));
} catch (RemoteException e) {
Log.e(TAG, "Failed to submit repeating request due to remote exception!");
}
return -1;
}
@Override
public void abortCaptures() {
try {
mRequestProcessor.abortCaptures();
} catch (RemoteException e) {
Log.e(TAG, "Failed to abort requests due to remote exception!");
}
}
@Override
public void stopRepeating() {
try {
mRequestProcessor.stopRepeating();
} catch (RemoteException e) {
Log.e(TAG, "Failed to stop repeating request due to remote exception!");
}
}
}
private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub {
private final SessionProcessorImpl mSessionProcessor;
private String mCameraId = null;
public SessionProcessorImplStub(SessionProcessorImpl sessionProcessor) {
mSessionProcessor = sessionProcessor;
}
@Override
public CameraSessionConfig initSession(String cameraId, OutputSurface previewSurface,
OutputSurface burstSurface) {
OutputSurfaceImplStub outputPreviewSurfaceImpl =
new OutputSurfaceImplStub(previewSurface);
OutputSurfaceImplStub outputBurstSurfaceImpl =
new OutputSurfaceImplStub(burstSurface);
Camera2SessionConfigImpl sessionConfig = mSessionProcessor.initSession(cameraId,
mCharacteristicsHashMap, getApplicationContext(), outputPreviewSurfaceImpl,
outputBurstSurfaceImpl, null /*imageAnalysisSurfaceConfig*/);
List<Camera2OutputConfigImpl> outputConfigs = sessionConfig.getOutputConfigs();
CameraSessionConfig ret = new CameraSessionConfig();
ret.outputConfigs = new ArrayList<>();
for (Camera2OutputConfigImpl output : outputConfigs) {
CameraOutputConfig entry = new CameraOutputConfig();
entry.outputId = new OutputConfigId();
entry.outputId.id = output.getId();
entry.physicalCameraId = output.getPhysicalCameraId();
entry.surfaceGroupId = output.getSurfaceGroupId();
if (output instanceof SurfaceOutputConfigImpl) {
SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output;
entry.type = CameraOutputConfig.TYPE_SURFACE;
entry.surface = surfaceConfig.getSurface();
} else if (output instanceof ImageReaderOutputConfigImpl) {
ImageReaderOutputConfigImpl imageReaderOutputConfig =
(ImageReaderOutputConfigImpl) output;
entry.type = CameraOutputConfig.TYPE_IMAGEREADER;
entry.size = new android.hardware.camera2.extension.Size();
entry.size.width = imageReaderOutputConfig.getSize().getWidth();
entry.size.height = imageReaderOutputConfig.getSize().getHeight();
entry.imageFormat = imageReaderOutputConfig.getImageFormat();
entry.capacity = imageReaderOutputConfig.getMaxImages();
} else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) {
MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig =
(MultiResolutionImageReaderOutputConfigImpl) output;
entry.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER;
entry.imageFormat = multiResReaderConfig.getImageFormat();
entry.capacity = multiResReaderConfig.getMaxImages();
} else {
throw new IllegalStateException("Unknown output config type!");
}
List<Camera2OutputConfigImpl> sharedOutputs =
output.getSurfaceSharingOutputConfigs();
if ((sharedOutputs != null) && (!sharedOutputs.isEmpty())) {
entry.surfaceSharingOutputConfigs = new ArrayList<>();
for (Camera2OutputConfigImpl sharedOutput : sharedOutputs) {
OutputConfigId outputId = new OutputConfigId();
outputId.id = sharedOutput.getId();
entry.surfaceSharingOutputConfigs.add(outputId);
}
}
ret.outputConfigs.add(entry);
}
ret.sessionTemplateId = sessionConfig.getSessionTemplateId();
ret.sessionParameter = initializeParcelableMetadata(
sessionConfig.getSessionParameters(), cameraId);
mCameraId = cameraId;
return ret;
}
@Override
public void deInitSession() {
mSessionProcessor.deInitSession();
}
@Override
public void onCaptureSessionStart(IRequestProcessorImpl requestProcessor) {
mSessionProcessor.onCaptureSessionStart(
new RequestProcessorStub(requestProcessor, mCameraId));
}
@Override
public void onCaptureSessionEnd() {
mSessionProcessor.onCaptureSessionEnd();
}
@Override
public int startRepeating(ICaptureCallback callback) {
return mSessionProcessor.startRepeating(new CaptureCallbackStub(callback));
}
@Override
public void stopRepeating() {
mSessionProcessor.stopRepeating();
}
@Override
public int startCapture(ICaptureCallback callback, int jpegRotation, int jpegQuality) {
HashMap<CaptureRequest.Key<?>, Object> paramMap = new HashMap<>();
paramMap.put(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
paramMap.put(CaptureRequest.JPEG_QUALITY, jpegQuality);
mSessionProcessor.setParameters(paramMap);
return mSessionProcessor.startCapture(new CaptureCallbackStub(callback));
}
}
private class OutputSurfaceImplStub implements OutputSurfaceImpl {
private final Surface mSurface;
private final Size mSize;
private final int mImageFormat;
public OutputSurfaceImplStub(OutputSurface outputSurface) {
mSurface = outputSurface.surface;
mSize = new Size(outputSurface.size.width, outputSurface.size.height);
mImageFormat = outputSurface.imageFormat;
}
@Override
public Surface getSurface() {
return mSurface;
}
@Override
public Size getSize() {
return mSize;
}
@Override
public int getImageFormat() {
return mImageFormat;
}
}
private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub {
private final PreviewExtenderImpl mPreviewExtender;
private String mCameraId = null;
public PreviewExtenderImplStub(PreviewExtenderImpl previewExtender) {
mPreviewExtender = previewExtender;
}
@Override
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
mCameraId = cameraId;
mPreviewExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
CameraExtensionsProxyService.this);
}
@Override
public void onDeInit() {
mPreviewExtender.onDeInit();
}
@Override
public CaptureStageImpl onPresetSession() {
return initializeParcelable(mPreviewExtender.onPresetSession(), mCameraId);
}
@Override
public CaptureStageImpl onEnableSession() {
return initializeParcelable(mPreviewExtender.onEnableSession(), mCameraId);
}
@Override
public CaptureStageImpl onDisableSession() {
return initializeParcelable(mPreviewExtender.onDisableSession(), mCameraId);
}
@Override
public void init(String cameraId, CameraMetadataNative chars) {
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(), mCameraId);
}
@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, mCameraId);
}
return null;
}
@Override
public List<SizeList> getSupportedResolutions() {
if (INIT_API_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;
private String mCameraId = null;
public ImageCaptureExtenderImplStub(ImageCaptureExtenderImpl imageExtender) {
mImageExtender = imageExtender;
}
@Override
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
mImageExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
CameraExtensionsProxyService.this);
mCameraId = cameraId;
}
@Override
public void onDeInit() {
mImageExtender.onDeInit();
}
@Override
public CaptureStageImpl onPresetSession() {
return initializeParcelable(mImageExtender.onPresetSession(), mCameraId);
}
@Override
public CaptureStageImpl onEnableSession() {
return initializeParcelable(mImageExtender.onEnableSession(), mCameraId);
}
@Override
public CaptureStageImpl onDisableSession() {
return initializeParcelable(mImageExtender.onDisableSession(), mCameraId);
}
@Override
public void init(String cameraId, CameraMetadataNative chars) {
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, mCameraId));
}
return ret;
}
return null;
}
@Override
public int getMaxCaptureStage() {
return mImageExtender.getMaxCaptureStage();
}
@Override
public List<SizeList> getSupportedResolutions() {
if (INIT_API_SUPPORTED) {
List<Pair<Integer, android.util.Size[]>> sizes =
mImageExtender.getSupportedResolutions();
if ((sizes != null) && !sizes.isEmpty()) {
return initializeParcelable(sizes);
}
}
return null;
}
@Override
public LatencyRange getEstimatedCaptureLatencyRange(
android.hardware.camera2.extension.Size outputSize) {
if (EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX)) {
Size sz = new Size(outputSize.width, outputSize.height);
Range<Long> latencyRange = mImageExtender.getEstimatedCaptureLatencyRange(sz);
if (latencyRange != null) {
LatencyRange ret = new LatencyRange();
ret.min = latencyRange.getLower();
ret.max = latencyRange.getUpper();
return ret;
}
}
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;
private final String mCameraId;
public RequestUpdateProcessorImplStub(RequestUpdateProcessorImpl processor,
String cameraId) {
mProcessor = processor;
mCameraId = cameraId;
}
@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)), mCameraId);
}
}
private class ImageReferenceImpl extends ExtensionImage
implements androidx.camera.extensions.impl.advanced.ImageReferenceImpl {
private final Object mImageLock = new Object();
private int mReferenceCount;
private ImageReferenceImpl(ParcelImage parcelImage) {
super(parcelImage);
mReferenceCount = 1;
}
@Override
public boolean increment() {
synchronized (mImageLock) {
if (mReferenceCount <= 0) {
return false;
}
mReferenceCount++;
}
return true;
}
@Override
public boolean decrement() {
synchronized (mImageLock) {
if (mReferenceCount <= 0) {
return false;
}
mReferenceCount--;
if (mReferenceCount <= 0) {
close();
}
}
return true;
}
@Override
public Image get() {
return this;
}
}
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;
}
}
}
}