Camera: Support advanced extensions
Bug: 179819443 Test: Camera CTS Change-Id: Ia14f7b4ad40e354b1a433000b1731e648f6398bd
This commit is contained in:
@@ -21,9 +21,11 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
|
||||
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
import android.hardware.camera2.extension.IPreviewExtenderImpl;
|
||||
import android.hardware.camera2.extension.LatencyRange;
|
||||
import android.hardware.camera2.extension.SizeList;
|
||||
import android.hardware.camera2.params.ExtensionSessionConfiguration;
|
||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
@@ -220,6 +222,7 @@ public final class CameraExtensionCharacteristics {
|
||||
private InitializerFuture mInitFuture = null;
|
||||
private ServiceConnection mConnection = null;
|
||||
private ICameraExtensionsProxyService mProxy = null;
|
||||
private boolean mSupportsAdvancedExtensions = false;
|
||||
|
||||
// Singleton, don't allow construction
|
||||
private CameraExtensionManagerGlobal() {}
|
||||
@@ -245,6 +248,11 @@ public final class CameraExtensionCharacteristics {
|
||||
public void onServiceConnected(ComponentName component, IBinder binder) {
|
||||
mProxy = ICameraExtensionsProxyService.Stub.asInterface(binder);
|
||||
mInitFuture.setStatus(true);
|
||||
try {
|
||||
mSupportsAdvancedExtensions = mProxy.advancedExtensionsSupported();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Remote IPC failed!");
|
||||
}
|
||||
}
|
||||
};
|
||||
ctx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE |
|
||||
@@ -334,6 +342,10 @@ public final class CameraExtensionCharacteristics {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean areAdvancedExtensionsSupported() {
|
||||
return mSupportsAdvancedExtensions;
|
||||
}
|
||||
|
||||
public IPreviewExtenderImpl initializePreviewExtension(int extensionType)
|
||||
throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
@@ -355,6 +367,17 @@ public final class CameraExtensionCharacteristics {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType)
|
||||
throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
if (mProxy != null) {
|
||||
return mProxy.initializeAdvancedExtension(extensionType);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,26 +394,63 @@ public final class CameraExtensionCharacteristics {
|
||||
CameraExtensionManagerGlobal.get().unregisterClient(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static boolean areAdvancedExtensionsSupported() {
|
||||
return CameraExtensionManagerGlobal.get().areAdvancedExtensionsSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static boolean isExtensionSupported(String cameraId, int extensionType,
|
||||
CameraCharacteristics chars) {
|
||||
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
|
||||
if (areAdvancedExtensionsSupported()) {
|
||||
try {
|
||||
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extensionType);
|
||||
return extender.isExtensionAvailable(cameraId);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to query extension availability! Extension service does not"
|
||||
+ " respond!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
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 IAdvancedExtenderImpl initializeAdvancedExtension(@Extension int extensionType) {
|
||||
IAdvancedExtenderImpl extender;
|
||||
try {
|
||||
extenders = initializeExtension(extensionType);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
extender = CameraExtensionManagerGlobal.get().initializeAdvancedExtension(
|
||||
extensionType);
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException("Failed to initialize extension: " + extensionType);
|
||||
}
|
||||
|
||||
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;
|
||||
if (extender == null) {
|
||||
throw new IllegalArgumentException("Unknown extension: " + extensionType);
|
||||
}
|
||||
|
||||
return extender;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -487,13 +547,21 @@ public final class CameraExtensionCharacteristics {
|
||||
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);
|
||||
if (areAdvancedExtensionsSupported()) {
|
||||
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
|
||||
extender.init(mCameraId);
|
||||
return generateSupportedSizes(
|
||||
extender.getSupportedPreviewOutputResolutions(mCameraId),
|
||||
ImageFormat.PRIVATE, streamMap);
|
||||
} else {
|
||||
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
|
||||
initializeExtension(extension);
|
||||
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!");
|
||||
@@ -536,31 +604,47 @@ public final class CameraExtensionCharacteristics {
|
||||
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) {
|
||||
// The framework will perform the additional encoding pass on the
|
||||
// processed YUV_420 buffers.
|
||||
return generateJpegSupportedSizes(
|
||||
extenders.second.getSupportedResolutions(), streamMap);
|
||||
} else {
|
||||
return generateSupportedSizes(null, format, streamMap);
|
||||
if (areAdvancedExtensionsSupported()) {
|
||||
switch(format) {
|
||||
case ImageFormat.YUV_420_888:
|
||||
case ImageFormat.JPEG:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported format: " + format);
|
||||
}
|
||||
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
|
||||
extender.init(mCameraId);
|
||||
return generateSupportedSizes(extender.getSupportedCaptureOutputResolutions(
|
||||
mCameraId), format, streamMap);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported format: " + format);
|
||||
if (format == ImageFormat.YUV_420_888) {
|
||||
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
|
||||
initializeExtension(extension);
|
||||
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) {
|
||||
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
|
||||
initializeExtension(extension);
|
||||
extenders.second.init(mCameraId, mChars.getNativeMetadata());
|
||||
if (extenders.second.getCaptureProcessor() != null) {
|
||||
// The framework will perform the additional encoding pass on the
|
||||
// processed YUV_420 buffers.
|
||||
return generateJpegSupportedSizes(
|
||||
extenders.second.getSupportedResolutions(), streamMap);
|
||||
} else {
|
||||
return generateSupportedSizes(null, format, streamMap);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported format: " + format);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unregisterClient(clientId);
|
||||
@@ -608,6 +692,23 @@ public final class CameraExtensionCharacteristics {
|
||||
if (!isExtensionSupported(mCameraId, extension, mChars)) {
|
||||
throw new IllegalArgumentException("Unsupported extension");
|
||||
}
|
||||
|
||||
if (areAdvancedExtensionsSupported()) {
|
||||
IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
|
||||
extender.init(mCameraId);
|
||||
android.hardware.camera2.extension.Size sz =
|
||||
new android.hardware.camera2.extension.Size();
|
||||
sz.width = captureOutputSize.getWidth();
|
||||
sz.height = captureOutputSize.getHeight();
|
||||
LatencyRange latencyRange = extender.getEstimatedCaptureLatencyRange(mCameraId,
|
||||
sz, format);
|
||||
if (latencyRange != null) {
|
||||
return new Range(latencyRange.min, latencyRange.max);
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
|
||||
+ " not respond!");
|
||||
} finally {
|
||||
unregisterClient(clientId);
|
||||
}
|
||||
|
||||
@@ -229,6 +229,33 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
|
||||
mFrameNumber = extras.getFrameNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes ownership of the passed-in properties object
|
||||
*
|
||||
* <p>For internal use only</p>
|
||||
* @hide
|
||||
*/
|
||||
public CaptureResult(String cameraId, CameraMetadataNative results, CaptureRequest parent,
|
||||
int requestId, long frameNumber) {
|
||||
if (results == null) {
|
||||
throw new IllegalArgumentException("results was null");
|
||||
}
|
||||
|
||||
if (parent == null) {
|
||||
throw new IllegalArgumentException("parent was null");
|
||||
}
|
||||
|
||||
mResults = CameraMetadataNative.move(results);
|
||||
if (mResults.isEmpty()) {
|
||||
throw new AssertionError("Results must not be empty");
|
||||
}
|
||||
setNativeInstance(mResults);
|
||||
mCameraId = cameraId;
|
||||
mRequest = parent;
|
||||
mSequenceId = requestId;
|
||||
mFrameNumber = frameNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the underlying {@link CameraMetadataNative}.
|
||||
* @hide
|
||||
|
||||
@@ -93,6 +93,36 @@ public final class TotalCaptureResult extends CaptureResult {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes ownership of the passed-in camera metadata and the partial results
|
||||
*
|
||||
* @param partials a list of partial results; {@code null} will be substituted for an empty list
|
||||
* @hide
|
||||
*/
|
||||
public TotalCaptureResult(String logicalCameraId, CameraMetadataNative results,
|
||||
CaptureRequest parent, int requestId, long frameNumber, List<CaptureResult> partials,
|
||||
int sessionId, PhysicalCaptureResultInfo[] physicalResults) {
|
||||
super(logicalCameraId, results, parent, requestId, frameNumber);
|
||||
|
||||
if (partials == null) {
|
||||
mPartialResults = new ArrayList<>();
|
||||
} else {
|
||||
mPartialResults = partials;
|
||||
}
|
||||
|
||||
mSessionId = sessionId;
|
||||
|
||||
mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
|
||||
for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
|
||||
TotalCaptureResult physicalResult = new TotalCaptureResult(
|
||||
onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(),
|
||||
parent, requestId, frameNumber, /*partials*/null, sessionId,
|
||||
new PhysicalCaptureResultInfo[0]);
|
||||
mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
|
||||
physicalResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request-less result.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.Size;
|
||||
import android.hardware.camera2.extension.OutputConfigId;
|
||||
import android.view.Surface;
|
||||
|
||||
/** @hide */
|
||||
parcelable CameraOutputConfig
|
||||
{
|
||||
Size size;
|
||||
Surface surface;
|
||||
int imageFormat;
|
||||
int capacity;
|
||||
|
||||
const int TYPE_SURFACE = 0;
|
||||
const int TYPE_IMAGEREADER = 1;
|
||||
const int TYPE_MULTIRES_IMAGEREADER = 2;
|
||||
int type;
|
||||
|
||||
OutputConfigId outputId;
|
||||
int surfaceGroupId;
|
||||
String physicalCameraId;
|
||||
List<OutputConfigId> surfaceSharingOutputConfigs;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.CameraOutputConfig;
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
|
||||
/** @hide */
|
||||
parcelable CameraSessionConfig
|
||||
{
|
||||
List<CameraOutputConfig> outputConfigs;
|
||||
CameraMetadataNative sessionParameter;
|
||||
int sessionTemplateId;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
|
||||
/** @hide */
|
||||
parcelable CaptureFailure
|
||||
{
|
||||
CaptureRequest request;
|
||||
int reason;
|
||||
boolean dropped;
|
||||
int sequenceId;
|
||||
long frameNumber;
|
||||
String errorPhysicalCameraId;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.ISessionProcessorImpl;
|
||||
import android.hardware.camera2.extension.LatencyRange;
|
||||
import android.hardware.camera2.extension.Size;
|
||||
import android.hardware.camera2.extension.SizeList;
|
||||
|
||||
/** @hide */
|
||||
interface IAdvancedExtenderImpl
|
||||
{
|
||||
boolean isExtensionAvailable(in String cameraId);
|
||||
void init(in String cameraId);
|
||||
LatencyRange getEstimatedCaptureLatencyRange(in String cameraId, in Size outputSize,
|
||||
int format);
|
||||
@nullable List<SizeList> getSupportedPreviewOutputResolutions(in String cameraId);
|
||||
@nullable List<SizeList> getSupportedCaptureOutputResolutions(in String cameraId);
|
||||
ISessionProcessorImpl getSessionProcessor();
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
|
||||
import android.hardware.camera2.extension.IPreviewExtenderImpl;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
|
||||
@@ -23,6 +24,8 @@ interface ICameraExtensionsProxyService
|
||||
{
|
||||
long registerClient();
|
||||
void unregisterClient(long clientId);
|
||||
boolean advancedExtensionsSupported();
|
||||
@nullable IPreviewExtenderImpl initializePreviewExtension(int extensionType);
|
||||
@nullable IImageCaptureExtenderImpl initializeImageExtension(int extensionType);
|
||||
@nullable IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.Request;
|
||||
|
||||
/** @hide */
|
||||
interface ICaptureCallback
|
||||
{
|
||||
void onCaptureStarted(int captureSequenceId, long timestamp);
|
||||
void onCaptureProcessStarted(int captureSequenceId);
|
||||
void onCaptureFailed(int captureSequenceId);
|
||||
void onCaptureSequenceCompleted(int captureSequenceId);
|
||||
void onCaptureSequenceAborted(int captureSequenceId);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.OutputConfigId;
|
||||
import android.hardware.camera2.extension.ParcelImage;
|
||||
|
||||
/** @hide */
|
||||
interface IImageProcessorImpl
|
||||
{
|
||||
void onNextImageAvailable(in OutputConfigId outputConfigId, in ParcelImage image);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.CaptureFailure;
|
||||
import android.hardware.camera2.extension.ParcelCaptureResult;
|
||||
import android.hardware.camera2.extension.ParcelTotalCaptureResult;
|
||||
|
||||
/** @hide */
|
||||
interface IRequestCallback
|
||||
{
|
||||
void onCaptureStarted(int requestId, long frameNumber, long timestamp);
|
||||
void onCaptureProgressed(int requestId, in ParcelCaptureResult partialResult);
|
||||
void onCaptureCompleted(int requestId, in ParcelTotalCaptureResult totalCaptureResult);
|
||||
void onCaptureFailed(int requestId, in CaptureFailure captureFailure);
|
||||
void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId);
|
||||
void onCaptureSequenceCompleted(int sequenceId, long frameNumber);
|
||||
void onCaptureSequenceAborted(int sequenceId);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.IImageProcessorImpl;
|
||||
import android.hardware.camera2.extension.IRequestCallback;
|
||||
import android.hardware.camera2.extension.OutputConfigId;
|
||||
import android.hardware.camera2.extension.Request;
|
||||
|
||||
/** @hide */
|
||||
interface IRequestProcessorImpl
|
||||
{
|
||||
void setImageProcessor(in OutputConfigId outputConfigId, in IImageProcessorImpl imageProcessor);
|
||||
boolean submit(in Request request, in IRequestCallback callback);
|
||||
boolean submitBurst(in List<Request> requests, in IRequestCallback callback);
|
||||
boolean setRepeating(in Request request, in IRequestCallback callback);
|
||||
void abortCaptures();
|
||||
void stopRepeating();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.CameraSessionConfig;
|
||||
import android.hardware.camera2.extension.ICaptureCallback;
|
||||
import android.hardware.camera2.extension.IRequestProcessorImpl;
|
||||
import android.hardware.camera2.extension.OutputSurface;
|
||||
|
||||
/** @hide */
|
||||
interface ISessionProcessorImpl
|
||||
{
|
||||
CameraSessionConfig initSession(in String cameraId, in OutputSurface previewSurface,
|
||||
in OutputSurface imageCaptureSurface);
|
||||
void deInitSession();
|
||||
void onCaptureSessionStart(IRequestProcessorImpl requestProcessor);
|
||||
void onCaptureSessionEnd();
|
||||
int startRepeating(in ICaptureCallback callback);
|
||||
void stopRepeating();
|
||||
int startCapture(in ICaptureCallback callback, int jpegRotation, int jpegQuality);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
/** @hide */
|
||||
parcelable LatencyRange
|
||||
{
|
||||
long min;
|
||||
long max;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
/** @hide */
|
||||
parcelable OutputConfigId
|
||||
{
|
||||
int id;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.Size;
|
||||
import android.view.Surface;
|
||||
|
||||
/** @hide */
|
||||
parcelable OutputSurface
|
||||
{
|
||||
Surface surface;
|
||||
Size size;
|
||||
int imageFormat;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
|
||||
/** @hide */
|
||||
parcelable ParcelCaptureResult
|
||||
{
|
||||
String cameraId;
|
||||
CameraMetadataNative results;
|
||||
CaptureRequest parent;
|
||||
int sequenceId;
|
||||
long frameNumber;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.extension.ParcelCaptureResult;
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
|
||||
|
||||
/** @hide */
|
||||
parcelable ParcelTotalCaptureResult
|
||||
{
|
||||
String logicalCameraId;
|
||||
CameraMetadataNative results;
|
||||
CaptureRequest parent;
|
||||
int sequenceId;
|
||||
long frameNumber;
|
||||
List<ParcelCaptureResult> partials;
|
||||
int sessionId;
|
||||
List<PhysicalCaptureResultInfo> physicalResult;
|
||||
}
|
||||
28
core/java/android/hardware/camera2/extension/Request.aidl
Normal file
28
core/java/android/hardware/camera2/extension/Request.aidl
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.hardware.camera2.extension;
|
||||
|
||||
import android.hardware.camera2.extension.OutputConfigId;
|
||||
import android.hardware.camera2.impl.CameraMetadataNative;
|
||||
|
||||
/** @hide */
|
||||
parcelable Request
|
||||
{
|
||||
List<OutputConfigId> targetOutputConfigIds;
|
||||
CameraMetadataNative parameters;
|
||||
int templateId;
|
||||
int requestId;
|
||||
}
|
||||
@@ -0,0 +1,917 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.camera2.impl;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.content.Context;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCaptureSession;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraDevice;
|
||||
import android.hardware.camera2.CameraExtensionCharacteristics;
|
||||
import android.hardware.camera2.CameraExtensionSession;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.camera2.CaptureFailure;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.hardware.camera2.CaptureResult;
|
||||
import android.hardware.camera2.TotalCaptureResult;
|
||||
import android.hardware.camera2.extension.CameraOutputConfig;
|
||||
import android.hardware.camera2.extension.CameraSessionConfig;
|
||||
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
|
||||
import android.hardware.camera2.extension.ICaptureCallback;
|
||||
import android.hardware.camera2.extension.IImageProcessorImpl;
|
||||
import android.hardware.camera2.extension.IRequestCallback;
|
||||
import android.hardware.camera2.extension.IRequestProcessorImpl;
|
||||
import android.hardware.camera2.extension.ISessionProcessorImpl;
|
||||
import android.hardware.camera2.extension.OutputConfigId;
|
||||
import android.hardware.camera2.extension.OutputSurface;
|
||||
import android.hardware.camera2.extension.ParcelCaptureResult;
|
||||
import android.hardware.camera2.extension.ParcelImage;
|
||||
import android.hardware.camera2.extension.ParcelTotalCaptureResult;
|
||||
import android.hardware.camera2.extension.Request;
|
||||
import android.hardware.camera2.params.ExtensionSessionConfiguration;
|
||||
import android.hardware.camera2.params.OutputConfiguration;
|
||||
import android.hardware.camera2.params.SessionConfiguration;
|
||||
import android.hardware.camera2.utils.SurfaceUtils;
|
||||
import android.media.Image;
|
||||
import android.media.ImageReader;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSession {
|
||||
private static final String TAG = "CameraAdvancedExtensionSessionImpl";
|
||||
|
||||
private final Executor mExecutor;
|
||||
private final CameraDevice mCameraDevice;
|
||||
private final long mExtensionClientId;
|
||||
private final Handler mHandler;
|
||||
private final HandlerThread mHandlerThread;
|
||||
private final CameraExtensionSession.StateCallback mCallbacks;
|
||||
private final IAdvancedExtenderImpl mAdvancedExtender;
|
||||
// maps camera outputs to extension output ids
|
||||
private final HashMap<Surface, Integer> mSurfaceIdMap = new HashMap<>();
|
||||
// maps camera extension output ids to camera registered image readers
|
||||
private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
|
||||
private final RequestProcessor mRequestProcessor = new RequestProcessor();
|
||||
|
||||
private Surface mClientRepeatingRequestSurface;
|
||||
private Surface mClientCaptureSurface;
|
||||
private CameraCaptureSession mCaptureSession = null;
|
||||
private ISessionProcessorImpl mSessionProcessor = null;
|
||||
|
||||
private boolean mInitialized;
|
||||
|
||||
|
||||
// Lock to synchronize cross-thread access to device public interface
|
||||
final Object mInterfaceLock = new Object(); // access from this class and Session only!
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.CAMERA)
|
||||
public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
|
||||
@NonNull CameraDevice cameraDevice, @NonNull Context ctx,
|
||||
@NonNull ExtensionSessionConfiguration config)
|
||||
throws CameraAccessException, RemoteException {
|
||||
long clientId = CameraExtensionCharacteristics.registerClient(ctx);
|
||||
if (clientId < 0) {
|
||||
throw new UnsupportedOperationException("Unsupported extension!");
|
||||
}
|
||||
|
||||
String cameraId = cameraDevice.getId();
|
||||
CameraManager manager = ctx.getSystemService(CameraManager.class);
|
||||
CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
|
||||
CameraExtensionCharacteristics extensionChars = new CameraExtensionCharacteristics(ctx,
|
||||
cameraId, chars);
|
||||
|
||||
if (!CameraExtensionCharacteristics.isExtensionSupported(cameraDevice.getId(),
|
||||
config.getExtension(), chars)) {
|
||||
throw new UnsupportedOperationException("Unsupported extension type: " +
|
||||
config.getExtension());
|
||||
}
|
||||
|
||||
if (config.getOutputConfigurations().isEmpty() ||
|
||||
config.getOutputConfigurations().size() > 2) {
|
||||
throw new IllegalArgumentException("Unexpected amount of output surfaces, received: " +
|
||||
config.getOutputConfigurations().size() + " expected <= 2");
|
||||
}
|
||||
|
||||
int suitableSurfaceCount = 0;
|
||||
List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
|
||||
config.getExtension(), SurfaceTexture.class);
|
||||
Surface repeatingRequestSurface = CameraExtensionUtils.getRepeatingRequestSurface(
|
||||
config.getOutputConfigurations(), supportedPreviewSizes);
|
||||
if (repeatingRequestSurface != null) {
|
||||
suitableSurfaceCount++;
|
||||
}
|
||||
|
||||
HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>();
|
||||
for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
|
||||
List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes(
|
||||
config.getExtension(), format);
|
||||
if (supportedSizes != null) {
|
||||
supportedCaptureSizes.put(format, supportedSizes);
|
||||
}
|
||||
}
|
||||
Surface burstCaptureSurface = CameraExtensionUtils.getBurstCaptureSurface(
|
||||
config.getOutputConfigurations(), supportedCaptureSizes);
|
||||
if (burstCaptureSurface != null) {
|
||||
suitableSurfaceCount++;
|
||||
}
|
||||
|
||||
if (suitableSurfaceCount != config.getOutputConfigurations().size()) {
|
||||
throw new IllegalArgumentException("One or more unsupported output surfaces found!");
|
||||
}
|
||||
|
||||
IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension(
|
||||
config.getExtension());
|
||||
extender.init(cameraId);
|
||||
|
||||
CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId,
|
||||
extender, cameraDevice, repeatingRequestSurface, burstCaptureSurface,
|
||||
config.getStateCallback(), config.getExecutor());
|
||||
ret.initialize();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private CameraAdvancedExtensionSessionImpl(long extensionClientId,
|
||||
@NonNull IAdvancedExtenderImpl extender, @NonNull CameraDevice cameraDevice,
|
||||
@Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
|
||||
@NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor) {
|
||||
mExtensionClientId = extensionClientId;
|
||||
mAdvancedExtender = extender;
|
||||
mCameraDevice = cameraDevice;
|
||||
mCallbacks = callback;
|
||||
mExecutor = executor;
|
||||
mClientRepeatingRequestSurface = repeatingRequestSurface;
|
||||
mClientCaptureSurface = burstCaptureSurface;
|
||||
mHandlerThread = new HandlerThread(TAG);
|
||||
mHandlerThread.start();
|
||||
mHandler = new Handler(mHandlerThread.getLooper());
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public synchronized void initialize() throws CameraAccessException, RemoteException {
|
||||
if (mInitialized) {
|
||||
Log.d(TAG, "Session already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
OutputSurface previewSurface = initializeParcelable(mClientRepeatingRequestSurface);
|
||||
OutputSurface captureSurface = initializeParcelable(mClientCaptureSurface);
|
||||
mSessionProcessor = mAdvancedExtender.getSessionProcessor();
|
||||
CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(),
|
||||
previewSurface, captureSurface);
|
||||
List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
|
||||
// map camera output ids to output configurations
|
||||
HashMap<Integer, OutputConfiguration> cameraOutputs = new HashMap<>();
|
||||
for (CameraOutputConfig output : outputConfigs) {
|
||||
OutputConfiguration cameraOutput = null;
|
||||
switch(output.type) {
|
||||
case CameraOutputConfig.TYPE_SURFACE:
|
||||
if (output.surface == null) {
|
||||
Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
|
||||
", skipping!");
|
||||
continue;
|
||||
}
|
||||
cameraOutput = new OutputConfiguration(output.surfaceGroupId,
|
||||
output.surface);
|
||||
break;
|
||||
case CameraOutputConfig.TYPE_IMAGEREADER:
|
||||
if ((output.imageFormat == ImageFormat.UNKNOWN) || (output.size.width <= 0) ||
|
||||
(output.size.height <= 0)) {
|
||||
Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
|
||||
", skipping!");
|
||||
continue;
|
||||
}
|
||||
ImageReader reader = ImageReader.newInstance(output.size.width,
|
||||
output.size.height, output.imageFormat, output.capacity);
|
||||
mReaderMap.put(output.outputId.id, reader);
|
||||
cameraOutput = new OutputConfiguration(output.surfaceGroupId,
|
||||
reader.getSurface());
|
||||
break;
|
||||
case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER:
|
||||
// TBD
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported output config type: " +
|
||||
output.type);
|
||||
}
|
||||
cameraOutput.setPhysicalCameraId(output.physicalCameraId);
|
||||
cameraOutputs.put(output.outputId.id, cameraOutput);
|
||||
}
|
||||
|
||||
ArrayList<OutputConfiguration> outputList = new ArrayList<>();
|
||||
for (CameraOutputConfig output : outputConfigs) {
|
||||
if (!cameraOutputs.containsKey(output.outputId.id)) {
|
||||
// Shared surface already removed by a previous iteration
|
||||
continue;
|
||||
}
|
||||
OutputConfiguration outConfig = cameraOutputs.get(output.outputId.id);
|
||||
if ((output.surfaceSharingOutputConfigs != null) &&
|
||||
!output.surfaceSharingOutputConfigs.isEmpty()) {
|
||||
outConfig.enableSurfaceSharing();
|
||||
for (OutputConfigId outputId : output.surfaceSharingOutputConfigs) {
|
||||
outConfig.addSurface(cameraOutputs.get(outputId.id).getSurface());
|
||||
cameraOutputs.remove(outputId.id);
|
||||
}
|
||||
}
|
||||
outputList.add(outConfig);
|
||||
mSurfaceIdMap.put(outConfig.getSurface(), output.outputId.id);
|
||||
}
|
||||
|
||||
SessionConfiguration sessionConfiguration = new SessionConfiguration(
|
||||
SessionConfiguration.SESSION_REGULAR, outputList,
|
||||
new CameraExtensionUtils.HandlerExecutor(mHandler), new SessionStateHandler());
|
||||
|
||||
if ((sessionConfig.sessionParameter != null) &&
|
||||
(!sessionConfig.sessionParameter.isEmpty())) {
|
||||
CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(
|
||||
sessionConfig.sessionTemplateId);
|
||||
CaptureRequest sessionRequest = requestBuilder.build();
|
||||
CameraMetadataNative.update(sessionRequest.getNativeMetadata(),
|
||||
sessionConfig.sessionParameter);
|
||||
sessionConfiguration.setSessionParameters(sessionRequest);
|
||||
}
|
||||
|
||||
mCameraDevice.createCaptureSession(sessionConfiguration);
|
||||
}
|
||||
|
||||
private static ParcelCaptureResult initializeParcelable(CaptureResult result) {
|
||||
ParcelCaptureResult ret = new ParcelCaptureResult();
|
||||
ret.cameraId = result.getCameraId();
|
||||
ret.results = result.getNativeMetadata();
|
||||
ret.parent = result.getRequest();
|
||||
ret.sequenceId = result.getSequenceId();
|
||||
ret.frameNumber = result.getFrameNumber();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static ParcelTotalCaptureResult initializeParcelable(TotalCaptureResult totalResult) {
|
||||
ParcelTotalCaptureResult ret = new ParcelTotalCaptureResult();
|
||||
ret.logicalCameraId = totalResult.getCameraId();
|
||||
ret.results = totalResult.getNativeMetadata();
|
||||
ret.parent = totalResult.getRequest();
|
||||
ret.sequenceId = totalResult.getSequenceId();
|
||||
ret.frameNumber = totalResult.getFrameNumber();
|
||||
ret.sessionId = totalResult.getSessionId();
|
||||
ret.partials = new ArrayList<>(totalResult.getPartialResults().size());
|
||||
for (CaptureResult partial : totalResult.getPartialResults()) {
|
||||
ret.partials.add(initializeParcelable(partial));
|
||||
}
|
||||
Map<String, TotalCaptureResult> physicalResults =
|
||||
totalResult.getPhysicalCameraTotalResults();
|
||||
ret.physicalResult = new ArrayList<>(physicalResults.size());
|
||||
for (TotalCaptureResult physicalResult : physicalResults.values()) {
|
||||
ret.physicalResult.add(new PhysicalCaptureResultInfo(physicalResult.getCameraId(),
|
||||
physicalResult.getNativeMetadata()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static OutputSurface initializeParcelable(Surface s) {
|
||||
OutputSurface ret = new OutputSurface();
|
||||
if (s != null) {
|
||||
ret.surface = s;
|
||||
ret.size = new android.hardware.camera2.extension.Size();
|
||||
Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
|
||||
ret.size.width = surfaceSize.getWidth();
|
||||
ret.size.height = surfaceSize.getHeight();
|
||||
ret.imageFormat = SurfaceUtils.getSurfaceFormat(s);
|
||||
} else {
|
||||
ret.surface = null;
|
||||
ret.size = new android.hardware.camera2.extension.Size();
|
||||
ret.size.width = -1;
|
||||
ret.size.height = -1;
|
||||
ret.imageFormat = ImageFormat.UNKNOWN;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CameraDevice getDevice() {
|
||||
synchronized (mInterfaceLock) {
|
||||
return mCameraDevice;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setRepeatingRequest(@NonNull CaptureRequest request, @NonNull Executor executor,
|
||||
@NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
|
||||
int seqId = -1;
|
||||
synchronized (mInterfaceLock) {
|
||||
if (!mInitialized) {
|
||||
throw new IllegalStateException("Uninitialized component");
|
||||
}
|
||||
|
||||
if (mClientRepeatingRequestSurface == null) {
|
||||
throw new IllegalArgumentException("No registered preview surface");
|
||||
}
|
||||
|
||||
if (!request.containsTarget(mClientRepeatingRequestSurface) ||
|
||||
(request.getTargets().size() != 1)) {
|
||||
throw new IllegalArgumentException("Invalid repeating request output target!");
|
||||
}
|
||||
|
||||
try {
|
||||
seqId = mSessionProcessor.startRepeating(new RequestCallbackHandler(request,
|
||||
executor, listener));
|
||||
} catch (RemoteException e) {
|
||||
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
|
||||
"Failed to enable repeating request, extension service failed to respond!");
|
||||
}
|
||||
}
|
||||
|
||||
return seqId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int capture(@NonNull CaptureRequest request,
|
||||
@NonNull Executor executor,
|
||||
@NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
|
||||
int seqId = -1;
|
||||
synchronized (mInterfaceLock) {
|
||||
if (!mInitialized) {
|
||||
throw new IllegalStateException("Uninitialized component");
|
||||
}
|
||||
|
||||
if (mClientCaptureSurface == null) {
|
||||
throw new IllegalArgumentException("No output surface registered for single"
|
||||
+ " requests!");
|
||||
}
|
||||
|
||||
if (!request.containsTarget(mClientCaptureSurface) ||
|
||||
(request.getTargets().size() != 1)) {
|
||||
throw new IllegalArgumentException("Invalid single capture output target!");
|
||||
}
|
||||
|
||||
try {
|
||||
// This will override the extension capture stage jpeg parameters with the user set
|
||||
// jpeg quality and rotation. This will guarantee that client configured jpeg
|
||||
// parameters always have highest priority.
|
||||
Integer jpegRotation = request.get(CaptureRequest.JPEG_ORIENTATION);
|
||||
if (jpegRotation == null) {
|
||||
jpegRotation = CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
|
||||
}
|
||||
Byte jpegQuality = request.get(CaptureRequest.JPEG_QUALITY);
|
||||
if (jpegQuality == null) {
|
||||
jpegQuality = CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
|
||||
}
|
||||
|
||||
seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
|
||||
executor, listener), jpegRotation, jpegQuality);
|
||||
} catch (RemoteException e) {
|
||||
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
|
||||
"Failed to submit capture request, extension service failed to respond!");
|
||||
}
|
||||
}
|
||||
|
||||
return seqId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRepeating() throws CameraAccessException {
|
||||
synchronized (mInterfaceLock) {
|
||||
if (!mInitialized) {
|
||||
throw new IllegalStateException("Uninitialized component");
|
||||
}
|
||||
|
||||
mCaptureSession.stopRepeating();
|
||||
|
||||
try {
|
||||
mSessionProcessor.stopRepeating();
|
||||
} catch (RemoteException e) {
|
||||
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
|
||||
"Failed to notify about the end of repeating request, extension service"
|
||||
+ " failed to respond!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws CameraAccessException {
|
||||
synchronized (mInterfaceLock) {
|
||||
if (mInitialized) {
|
||||
try {
|
||||
mCaptureSession.stopRepeating();
|
||||
mSessionProcessor.stopRepeating();
|
||||
mSessionProcessor.onCaptureSessionEnd();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to stop the repeating request or end the session,"
|
||||
+ " , extension service does not respond!") ;
|
||||
}
|
||||
mCaptureSession.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
synchronized (mInterfaceLock) {
|
||||
mInitialized = false;
|
||||
mHandlerThread.quitSafely();
|
||||
|
||||
if (mSessionProcessor != null) {
|
||||
try {
|
||||
mSessionProcessor.deInitSession();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to de-initialize session processor, extension service"
|
||||
+ " does not respond!") ;
|
||||
}
|
||||
mSessionProcessor = null;
|
||||
}
|
||||
|
||||
if (mExtensionClientId >= 0) {
|
||||
CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
|
||||
}
|
||||
|
||||
for (ImageReader reader : mReaderMap.values()) {
|
||||
reader.close();
|
||||
}
|
||||
mReaderMap.clear();
|
||||
|
||||
mClientRepeatingRequestSurface = null;
|
||||
mClientCaptureSurface = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyConfigurationFailure() {
|
||||
synchronized (mInterfaceLock) {
|
||||
if (mInitialized) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
release();
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mExecutor.execute(
|
||||
() -> mCallbacks.onConfigureFailed(
|
||||
CameraAdvancedExtensionSessionImpl.this));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
private class SessionStateHandler extends
|
||||
android.hardware.camera2.CameraCaptureSession.StateCallback {
|
||||
@Override
|
||||
public void onClosed(@NonNull CameraCaptureSession session) {
|
||||
release();
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mExecutor.execute(() -> mCallbacks.onClosed(
|
||||
CameraAdvancedExtensionSessionImpl.this));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
|
||||
notifyConfigurationFailure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(@NonNull CameraCaptureSession session) {
|
||||
boolean status = true;
|
||||
synchronized (mInterfaceLock) {
|
||||
mCaptureSession = session;
|
||||
try {
|
||||
mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
|
||||
mInitialized = true;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to start capture session,"
|
||||
+ " extension service does not respond!");
|
||||
status = false;
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (status) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mExecutor.execute(
|
||||
() -> mCallbacks.onConfigured(CameraAdvancedExtensionSessionImpl.this));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
} else {
|
||||
notifyConfigurationFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class RequestCallbackHandler extends ICaptureCallback.Stub {
|
||||
private final CaptureRequest mClientRequest;
|
||||
private final Executor mClientExecutor;
|
||||
private final ExtensionCaptureCallback mClientCallbacks;
|
||||
|
||||
private RequestCallbackHandler(@NonNull CaptureRequest clientRequest,
|
||||
@NonNull Executor clientExecutor,
|
||||
@NonNull ExtensionCaptureCallback clientCallbacks) {
|
||||
mClientRequest = clientRequest;
|
||||
mClientExecutor = clientExecutor;
|
||||
mClientCallbacks = clientCallbacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureStarted(int captureSequenceId, long timestamp) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mClientExecutor.execute(
|
||||
() -> mClientCallbacks.onCaptureStarted(
|
||||
CameraAdvancedExtensionSessionImpl.this, mClientRequest,
|
||||
timestamp));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureProcessStarted(int captureSequenceId) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mClientExecutor.execute(
|
||||
() -> mClientCallbacks.onCaptureProcessStarted(
|
||||
CameraAdvancedExtensionSessionImpl.this, mClientRequest));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureFailed(int captureSequenceId) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mClientExecutor.execute(
|
||||
() -> mClientCallbacks.onCaptureFailed(
|
||||
CameraAdvancedExtensionSessionImpl.this, mClientRequest));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureSequenceCompleted(int captureSequenceId) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mClientExecutor.execute(
|
||||
() -> mClientCallbacks.onCaptureSequenceCompleted(
|
||||
CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureSequenceAborted(int captureSequenceId) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mClientExecutor.execute(
|
||||
() -> mClientCallbacks.onCaptureSequenceAborted(
|
||||
CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class CaptureCallbackHandler extends CameraCaptureSession.CaptureCallback {
|
||||
private final IRequestCallback mCallback;
|
||||
|
||||
public CaptureCallbackHandler(IRequestCallback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureBufferLost(CameraCaptureSession session, CaptureRequest request,
|
||||
Surface target, long frameNumber) {
|
||||
try {
|
||||
if (request.getTag() instanceof Integer) {
|
||||
Integer requestId = (Integer) request.getTag();
|
||||
mCallback.onCaptureBufferLost(requestId, frameNumber,
|
||||
mSurfaceIdMap.get(target));
|
||||
} else {
|
||||
Log.e(TAG, "Invalid capture request tag!");
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to notify lost capture buffer, extension service doesn't"
|
||||
+ " respond!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
|
||||
TotalCaptureResult result) {
|
||||
try {
|
||||
if (request.getTag() instanceof Integer) {
|
||||
Integer requestId = (Integer) request.getTag();
|
||||
mCallback.onCaptureCompleted(requestId, initializeParcelable(result));
|
||||
} else {
|
||||
Log.e(TAG, "Invalid capture request tag!");
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to notify capture result, extension service doesn't"
|
||||
+ " respond!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
|
||||
CaptureFailure failure) {
|
||||
try {
|
||||
if (request.getTag() instanceof Integer) {
|
||||
Integer requestId = (Integer) request.getTag();
|
||||
android.hardware.camera2.extension.CaptureFailure captureFailure =
|
||||
new android.hardware.camera2.extension.CaptureFailure();
|
||||
captureFailure.request = request;
|
||||
captureFailure.reason = failure.getReason();
|
||||
captureFailure.errorPhysicalCameraId = failure.getPhysicalCameraId();
|
||||
captureFailure.frameNumber = failure.getFrameNumber();
|
||||
captureFailure.sequenceId = failure.getSequenceId();
|
||||
captureFailure.dropped = !failure.wasImageCaptured();
|
||||
mCallback.onCaptureFailed(requestId, captureFailure);
|
||||
} else {
|
||||
Log.e(TAG, "Invalid capture request tag!");
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to notify capture failure, extension service doesn't"
|
||||
+ " respond!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
|
||||
CaptureResult partialResult) {
|
||||
try {
|
||||
if (request.getTag() instanceof Integer) {
|
||||
Integer requestId = (Integer) request.getTag();
|
||||
mCallback.onCaptureProgressed(requestId, initializeParcelable(partialResult));
|
||||
} else {
|
||||
Log.e(TAG, "Invalid capture request tag!");
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to notify capture partial result, extension service doesn't"
|
||||
+ " respond!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureSequenceAborted(CameraCaptureSession session, int sequenceId) {
|
||||
try {
|
||||
mCallback.onCaptureSequenceAborted(sequenceId);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to notify aborted sequence, extension service doesn't"
|
||||
+ " respond!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
|
||||
long frameNumber) {
|
||||
try {
|
||||
mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to notify sequence complete, extension service doesn't"
|
||||
+ " respond!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
|
||||
long timestamp, long frameNumber) {
|
||||
try {
|
||||
if (request.getTag() instanceof Integer) {
|
||||
Integer requestId = (Integer) request.getTag();
|
||||
mCallback.onCaptureStarted(requestId, frameNumber, timestamp);
|
||||
} else {
|
||||
Log.e(TAG, "Invalid capture request tag!");
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to notify capture started, extension service doesn't"
|
||||
+ " respond!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ImageReaderHandler implements ImageReader.OnImageAvailableListener {
|
||||
private final OutputConfigId mOutputConfigId;
|
||||
private final IImageProcessorImpl mIImageProcessor;
|
||||
|
||||
private ImageReaderHandler(int outputConfigId,
|
||||
IImageProcessorImpl iImageProcessor) {
|
||||
mOutputConfigId = new OutputConfigId();
|
||||
mOutputConfigId.id = outputConfigId;
|
||||
mIImageProcessor = iImageProcessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageAvailable(ImageReader reader) {
|
||||
if (mIImageProcessor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Image img;
|
||||
try {
|
||||
img = reader.acquireNextImage();
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Failed to acquire image, too many images pending!");
|
||||
return;
|
||||
}
|
||||
if (img == null) {
|
||||
Log.e(TAG, "Invalid image!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
reader.detachImage(img);
|
||||
} catch(Exception e) {
|
||||
Log.e(TAG, "Failed to detach image");
|
||||
img.close();
|
||||
return;
|
||||
}
|
||||
|
||||
ParcelImage parcelImage = new ParcelImage();
|
||||
parcelImage.buffer = img.getHardwareBuffer();
|
||||
if (img.getFenceFd() >= 0) {
|
||||
try {
|
||||
parcelImage.fence = ParcelFileDescriptor.fromFd(img.getFenceFd());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG,"Failed to parcel buffer fence!");
|
||||
}
|
||||
}
|
||||
parcelImage.format = img.getFormat();
|
||||
parcelImage.timestamp = img.getTimestamp();
|
||||
parcelImage.transform = img.getTransform();
|
||||
parcelImage.scalingMode = img.getScalingMode();
|
||||
parcelImage.planeCount = img.getPlaneCount();
|
||||
parcelImage.crop = img.getCropRect();
|
||||
|
||||
try {
|
||||
mIImageProcessor.onNextImageAvailable(mOutputConfigId, parcelImage);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to propagate image buffer on output surface id: " +
|
||||
mOutputConfigId + " extension service does not respond!");
|
||||
} finally {
|
||||
parcelImage.buffer.close();
|
||||
img.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class RequestProcessor extends IRequestProcessorImpl.Stub {
|
||||
@Override
|
||||
public void setImageProcessor(OutputConfigId outputConfigId,
|
||||
IImageProcessorImpl imageProcessor) {
|
||||
synchronized (mInterfaceLock) {
|
||||
if (mReaderMap.containsKey(outputConfigId.id)) {
|
||||
mReaderMap.get(outputConfigId.id).setOnImageAvailableListener(
|
||||
new ImageReaderHandler(outputConfigId.id, imageProcessor), mHandler);
|
||||
} else {
|
||||
Log.e(TAG, "ImageReader with output config id: " + outputConfigId.id +
|
||||
" not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean submit(Request request, IRequestCallback callback) {
|
||||
ArrayList<Request> captureList = new ArrayList<>();
|
||||
captureList.add(request);
|
||||
return submitBurst(captureList, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean submitBurst(List<Request> requests, IRequestCallback callback) {
|
||||
synchronized (mInterfaceLock) {
|
||||
try {
|
||||
CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
|
||||
ArrayList<CaptureRequest> captureRequests = new ArrayList<>();
|
||||
for (Request request : requests) {
|
||||
captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
|
||||
mSurfaceIdMap));
|
||||
}
|
||||
mCaptureSession.captureBurstRequests(captureRequests,
|
||||
new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
|
||||
} catch (CameraAccessException e) {
|
||||
Log.e(TAG, "Failed to submit capture requests!");
|
||||
return false;
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Capture session closed!");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setRepeating(Request request, IRequestCallback callback) {
|
||||
synchronized (mInterfaceLock) {
|
||||
try {
|
||||
CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
|
||||
request, mSurfaceIdMap);
|
||||
CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
|
||||
mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
|
||||
new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
|
||||
} catch (CameraAccessException e) {
|
||||
Log.e(TAG, "Failed to enable repeating request!");
|
||||
return false;
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Capture session closed!");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortCaptures() {
|
||||
synchronized (mInterfaceLock) {
|
||||
try {
|
||||
mCaptureSession.abortCaptures();
|
||||
} catch (CameraAccessException e) {
|
||||
Log.e(TAG, "Failed during capture abort!");
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Capture session closed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRepeating() {
|
||||
synchronized (mInterfaceLock) {
|
||||
try {
|
||||
mCaptureSession.stopRepeating();
|
||||
} catch (CameraAccessException e) {
|
||||
Log.e(TAG, "Failed during repeating capture stop!");
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Capture session closed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static CaptureRequest initializeCaptureRequest(CameraDevice cameraDevice,
|
||||
Request request, HashMap<Surface, Integer> surfaceIdMap) throws CameraAccessException {
|
||||
CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(request.templateId);
|
||||
for (OutputConfigId configId : request.targetOutputConfigIds) {
|
||||
boolean found = false;
|
||||
for (Map.Entry<Surface, Integer> entry : surfaceIdMap.entrySet()) {
|
||||
if (entry.getValue() == configId.id) {
|
||||
builder.addTarget(entry.getKey());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Log.e(TAG, "Surface with output id: " + configId.id +
|
||||
" not found among registered camera outputs!");
|
||||
}
|
||||
}
|
||||
|
||||
builder.setTag(request.requestId);
|
||||
CaptureRequest ret = builder.build();
|
||||
CameraMetadataNative.update(ret.getNativeMetadata(), request.parameters);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import android.hardware.ICameraService;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCaptureSession;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraExtensionCharacteristics;
|
||||
import android.hardware.camera2.CameraExtensionSession;
|
||||
import android.hardware.camera2.CameraOfflineSession;
|
||||
import android.hardware.camera2.CameraDevice;
|
||||
@@ -138,6 +139,7 @@ public class CameraDeviceImpl extends CameraDevice
|
||||
|
||||
private CameraCaptureSessionCore mCurrentSession;
|
||||
private CameraExtensionSessionImpl mCurrentExtensionSession;
|
||||
private CameraAdvancedExtensionSessionImpl mCurrentAdvancedExtensionSession;
|
||||
private int mNextSessionId = 0;
|
||||
|
||||
private final int mAppTargetSdkVersion;
|
||||
@@ -1343,6 +1345,12 @@ public class CameraDeviceImpl extends CameraDevice
|
||||
mCurrentExtensionSession.release();
|
||||
mCurrentExtensionSession = null;
|
||||
}
|
||||
|
||||
if (mCurrentAdvancedExtensionSession != null) {
|
||||
mCurrentAdvancedExtensionSession.release();
|
||||
mCurrentAdvancedExtensionSession = 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)
|
||||
@@ -2395,9 +2403,14 @@ public class CameraDeviceImpl extends CameraDevice
|
||||
public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
|
||||
throws CameraAccessException {
|
||||
try {
|
||||
mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(this,
|
||||
mContext,
|
||||
extensionConfiguration);
|
||||
if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) {
|
||||
mCurrentAdvancedExtensionSession =
|
||||
CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession(
|
||||
this, mContext, extensionConfiguration);
|
||||
} else {
|
||||
mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(
|
||||
this, mContext, extensionConfiguration);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package android.hardware.camera2.impl;
|
||||
|
||||
import static android.hardware.camera2.impl.CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
|
||||
import static android.hardware.camera2.impl.CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.hardware.camera2.CaptureResult;
|
||||
@@ -42,8 +45,6 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
|
||||
public final static String TAG = "CameraExtensionJpeg";
|
||||
private final static int JPEG_QUEUE_SIZE = 1;
|
||||
private final static int JPEG_DEFAULT_QUALITY = 100;
|
||||
private final static int JPEG_DEFAULT_ROTATION = 0;
|
||||
|
||||
private final Handler mHandler;
|
||||
private final HandlerThread mHandlerThread;
|
||||
|
||||
@@ -110,79 +110,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
// Lock to synchronize cross-thread access to device public interface
|
||||
final Object mInterfaceLock = new Object(); // access from this class and Session only!
|
||||
|
||||
private static class SurfaceInfo {
|
||||
public int mWidth = 0;
|
||||
public int mHeight = 0;
|
||||
public int mFormat = PixelFormat.RGBA_8888;
|
||||
public long mUsage = 0;
|
||||
}
|
||||
|
||||
private static final int SUPPORTED_CAPTURE_OUTPUT_FORMATS[] = {
|
||||
CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
|
||||
ImageFormat.JPEG
|
||||
};
|
||||
|
||||
private static int nativeGetSurfaceWidth(Surface surface) {
|
||||
return SurfaceUtils.getSurfaceSize(surface).getWidth();
|
||||
}
|
||||
|
||||
private static int nativeGetSurfaceHeight(Surface surface) {
|
||||
return SurfaceUtils.getSurfaceSize(surface).getHeight();
|
||||
}
|
||||
|
||||
private static int nativeGetSurfaceFormat(Surface surface) {
|
||||
return SurfaceUtils.getSurfaceFormat(surface);
|
||||
}
|
||||
|
||||
private static Surface getBurstCaptureSurface(
|
||||
@NonNull List<OutputConfiguration> outputConfigs,
|
||||
@NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
|
||||
for (OutputConfiguration config : outputConfigs) {
|
||||
SurfaceInfo surfaceInfo = querySurface(config.getSurface());
|
||||
for (int supportedFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
|
||||
if (surfaceInfo.mFormat == supportedFormat) {
|
||||
Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
|
||||
if (supportedCaptureSizes.containsKey(supportedFormat)) {
|
||||
if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
|
||||
return config.getSurface();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Capture size not supported!");
|
||||
}
|
||||
}
|
||||
return config.getSurface();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @Nullable Surface getRepeatingRequestSurface(
|
||||
@NonNull List<OutputConfiguration> outputConfigs,
|
||||
@Nullable List<Size> supportedPreviewSizes) {
|
||||
for (OutputConfiguration config : outputConfigs) {
|
||||
SurfaceInfo surfaceInfo = querySurface(config.getSurface());
|
||||
if ((surfaceInfo.mFormat ==
|
||||
CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
|
||||
// The default RGBA_8888 is also implicitly supported because camera will
|
||||
// internally override it to
|
||||
// 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
|
||||
(surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
|
||||
Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
|
||||
surfaceInfo.mHeight);
|
||||
if ((supportedPreviewSizes == null) ||
|
||||
(!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
|
||||
throw new IllegalArgumentException("Repeating request surface size " +
|
||||
repeatingRequestSurfaceSize + " not supported!");
|
||||
}
|
||||
|
||||
return config.getSurface();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -221,22 +152,22 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
int suitableSurfaceCount = 0;
|
||||
List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
|
||||
config.getExtension(), SurfaceTexture.class);
|
||||
Surface repeatingRequestSurface = getRepeatingRequestSurface(
|
||||
Surface repeatingRequestSurface = CameraExtensionUtils.getRepeatingRequestSurface(
|
||||
config.getOutputConfigurations(), supportedPreviewSizes);
|
||||
if (repeatingRequestSurface != null) {
|
||||
suitableSurfaceCount++;
|
||||
}
|
||||
|
||||
HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>();
|
||||
for (int format : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
|
||||
for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
|
||||
List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes(
|
||||
config.getExtension(), format);
|
||||
if (supportedSizes != null) {
|
||||
supportedCaptureSizes.put(format, supportedSizes);
|
||||
}
|
||||
}
|
||||
Surface burstCaptureSurface = getBurstCaptureSurface(config.getOutputConfigurations(),
|
||||
supportedCaptureSizes);
|
||||
Surface burstCaptureSurface = CameraExtensionUtils.getBurstCaptureSurface(
|
||||
config.getOutputConfigurations(), supportedCaptureSizes);
|
||||
if (burstCaptureSurface != null) {
|
||||
suitableSurfaceCount++;
|
||||
}
|
||||
@@ -266,15 +197,15 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
return session;
|
||||
}
|
||||
|
||||
private CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
|
||||
@NonNull IPreviewExtenderImpl previewExtender,
|
||||
@NonNull List<Size> previewSizes,
|
||||
long extensionClientId,
|
||||
@NonNull CameraDevice cameraDevice,
|
||||
@Nullable Surface repeatingRequestSurface,
|
||||
@Nullable Surface burstCaptureSurface,
|
||||
@NonNull StateCallback callback,
|
||||
@NonNull Executor executor) {
|
||||
public CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
|
||||
@NonNull IPreviewExtenderImpl previewExtender,
|
||||
@NonNull List<Size> previewSizes,
|
||||
long extensionClientId,
|
||||
@NonNull CameraDevice cameraDevice,
|
||||
@Nullable Surface repeatingRequestSurface,
|
||||
@Nullable Surface burstCaptureSurface,
|
||||
@NonNull StateCallback callback,
|
||||
@NonNull Executor executor) {
|
||||
mExtensionClientId = extensionClientId;
|
||||
mImageExtender = imageExtender;
|
||||
mPreviewExtender = previewExtender;
|
||||
@@ -290,57 +221,17 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
private static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
|
||||
ImageWriter writer = null;
|
||||
Image img = null;
|
||||
SurfaceInfo surfaceInfo = new SurfaceInfo();
|
||||
int nativeFormat = nativeGetSurfaceFormat(s);
|
||||
int dataspace = SurfaceUtils.getSurfaceDataspace(s);
|
||||
// Jpeg surfaces cannot be queried for their usage and other parameters
|
||||
// in the usual way below. A buffer can only be de-queued after the
|
||||
// producer overrides the surface dimensions to (width*height) x 1.
|
||||
if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
|
||||
(dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
|
||||
surfaceInfo.mFormat = ImageFormat.JPEG;
|
||||
surfaceInfo.mWidth = nativeGetSurfaceWidth(s);
|
||||
surfaceInfo.mHeight = nativeGetSurfaceHeight(s);
|
||||
return surfaceInfo;
|
||||
}
|
||||
|
||||
HardwareBuffer buffer = null;
|
||||
try {
|
||||
writer = ImageWriter.newInstance(s, 1);
|
||||
img = writer.dequeueInputImage();
|
||||
buffer = img.getHardwareBuffer();
|
||||
surfaceInfo.mFormat = buffer.getFormat();
|
||||
surfaceInfo.mWidth = buffer.getWidth();
|
||||
surfaceInfo.mHeight = buffer.getHeight();
|
||||
surfaceInfo.mUsage = buffer.getUsage();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to query surface, returning defaults!");
|
||||
} finally {
|
||||
if (buffer != null) {
|
||||
buffer.close();
|
||||
}
|
||||
if (img != null) {
|
||||
img.close();
|
||||
}
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
return surfaceInfo;
|
||||
}
|
||||
|
||||
private void initializeRepeatingRequestPipeline() throws RemoteException {
|
||||
SurfaceInfo repeatingSurfaceInfo = new SurfaceInfo();
|
||||
CameraExtensionUtils.SurfaceInfo repeatingSurfaceInfo =
|
||||
new CameraExtensionUtils.SurfaceInfo();
|
||||
mPreviewProcessorType = mPreviewExtender.getProcessorType();
|
||||
if (mClientRepeatingRequestSurface != null) {
|
||||
repeatingSurfaceInfo = querySurface(mClientRepeatingRequestSurface);
|
||||
repeatingSurfaceInfo = CameraExtensionUtils.querySurface(
|
||||
mClientRepeatingRequestSurface);
|
||||
} else {
|
||||
// Make the intermediate surface behave as any regular 'SurfaceTexture'
|
||||
SurfaceInfo captureSurfaceInfo = querySurface(mClientCaptureSurface);
|
||||
CameraExtensionUtils.SurfaceInfo captureSurfaceInfo = CameraExtensionUtils.querySurface(
|
||||
mClientCaptureSurface);
|
||||
Size captureSize = new Size(captureSurfaceInfo.mWidth, captureSurfaceInfo.mHeight);
|
||||
Size previewSize = findSmallestAspectMatchedSize(mSupportedPreviewSizes, captureSize);
|
||||
repeatingSurfaceInfo.mWidth = previewSize.getWidth();
|
||||
@@ -418,7 +309,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
|
||||
if (mImageProcessor != null) {
|
||||
if (mClientCaptureSurface != null) {
|
||||
SurfaceInfo surfaceInfo = querySurface(mClientCaptureSurface);
|
||||
CameraExtensionUtils.SurfaceInfo surfaceInfo = CameraExtensionUtils.querySurface(
|
||||
mClientCaptureSurface);
|
||||
if (surfaceInfo.mFormat == ImageFormat.JPEG) {
|
||||
mImageJpegProcessor = new CameraExtensionJpegProcessor(mImageProcessor);
|
||||
mImageProcessor = mImageJpegProcessor;
|
||||
@@ -501,7 +393,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
SessionConfiguration sessionConfig = new SessionConfiguration(
|
||||
SessionConfiguration.SESSION_REGULAR,
|
||||
outputList,
|
||||
new HandlerExecutor(mHandler),
|
||||
new CameraExtensionUtils.HandlerExecutor(mHandler),
|
||||
new SessionStateHandler());
|
||||
|
||||
if (!sessionParamsList.isEmpty()) {
|
||||
@@ -656,7 +548,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
throw new UnsupportedOperationException("Failed to create still capture burst request");
|
||||
}
|
||||
|
||||
return mCaptureSession.captureBurstRequests(burstRequest, new HandlerExecutor(mHandler),
|
||||
return mCaptureSession.captureBurstRequests(burstRequest,
|
||||
new CameraExtensionUtils.HandlerExecutor(mHandler),
|
||||
new BurstRequestHandler(request, executor, listener, requestMap,
|
||||
mBurstCaptureImageCallback));
|
||||
}
|
||||
@@ -724,7 +617,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
CaptureRequest repeatingRequest = createRequest(mCameraDevice,
|
||||
captureStageList, mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW);
|
||||
return mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
|
||||
new HandlerExecutor(mHandler), requestHandler);
|
||||
new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@@ -1705,23 +1598,6 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
|
||||
return ret;
|
||||
}
|
||||
|
||||
private final class HandlerExecutor implements Executor {
|
||||
private final Handler mHandler;
|
||||
|
||||
public HandlerExecutor(Handler handler) {
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable runCmd) {
|
||||
try {
|
||||
mHandler.post(runCmd);
|
||||
} catch (RejectedExecutionException e) {
|
||||
Log.w(TAG, "Handler thread unavailable, skipping message!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ParcelImage initializeParcelImage(Image img) {
|
||||
ParcelImage parcelImage = new ParcelImage();
|
||||
parcelImage.buffer = img.getHardwareBuffer();
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.camera2.impl;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.hardware.camera2.CameraExtensionCharacteristics;
|
||||
import android.hardware.camera2.params.OutputConfiguration;
|
||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
import android.hardware.camera2.utils.SurfaceUtils;
|
||||
import android.media.Image;
|
||||
import android.media.ImageWriter;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
public final class CameraExtensionUtils {
|
||||
private static final String TAG = "CameraExtensionUtils";
|
||||
|
||||
public final static int JPEG_DEFAULT_QUALITY = 100;
|
||||
public final static int JPEG_DEFAULT_ROTATION = 0;
|
||||
|
||||
public static final int[] SUPPORTED_CAPTURE_OUTPUT_FORMATS = {
|
||||
CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
|
||||
ImageFormat.JPEG
|
||||
};
|
||||
|
||||
public static class SurfaceInfo {
|
||||
public int mWidth = 0;
|
||||
public int mHeight = 0;
|
||||
public int mFormat = PixelFormat.RGBA_8888;
|
||||
public long mUsage = 0;
|
||||
}
|
||||
|
||||
public static final class HandlerExecutor implements Executor {
|
||||
private final Handler mHandler;
|
||||
|
||||
public HandlerExecutor(Handler handler) {
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable runCmd) {
|
||||
try {
|
||||
mHandler.post(runCmd);
|
||||
} catch (RejectedExecutionException e) {
|
||||
Log.w(TAG, "Handler thread unavailable, skipping message!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
|
||||
ImageWriter writer = null;
|
||||
Image img = null;
|
||||
SurfaceInfo surfaceInfo = new SurfaceInfo();
|
||||
int nativeFormat = SurfaceUtils.getSurfaceFormat(s);
|
||||
int dataspace = SurfaceUtils.getSurfaceDataspace(s);
|
||||
// Jpeg surfaces cannot be queried for their usage and other parameters
|
||||
// in the usual way below. A buffer can only be de-queued after the
|
||||
// producer overrides the surface dimensions to (width*height) x 1.
|
||||
if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
|
||||
(dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
|
||||
surfaceInfo.mFormat = ImageFormat.JPEG;
|
||||
Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
|
||||
surfaceInfo.mWidth = surfaceSize.getWidth();
|
||||
surfaceInfo.mHeight = surfaceSize.getHeight();
|
||||
return surfaceInfo;
|
||||
}
|
||||
|
||||
HardwareBuffer buffer = null;
|
||||
try {
|
||||
writer = ImageWriter.newInstance(s, 1);
|
||||
img = writer.dequeueInputImage();
|
||||
buffer = img.getHardwareBuffer();
|
||||
surfaceInfo.mFormat = buffer.getFormat();
|
||||
surfaceInfo.mWidth = buffer.getWidth();
|
||||
surfaceInfo.mHeight = buffer.getHeight();
|
||||
surfaceInfo.mUsage = buffer.getUsage();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to query surface, returning defaults!");
|
||||
} finally {
|
||||
if (buffer != null) {
|
||||
buffer.close();
|
||||
}
|
||||
if (img != null) {
|
||||
img.close();
|
||||
}
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
return surfaceInfo;
|
||||
}
|
||||
|
||||
public static Surface getBurstCaptureSurface(
|
||||
@NonNull List<OutputConfiguration> outputConfigs,
|
||||
@NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
|
||||
for (OutputConfiguration config : outputConfigs) {
|
||||
SurfaceInfo surfaceInfo = querySurface(config.getSurface());
|
||||
for (int supportedFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
|
||||
if (surfaceInfo.mFormat == supportedFormat) {
|
||||
Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
|
||||
if (supportedCaptureSizes.containsKey(supportedFormat)) {
|
||||
if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
|
||||
return config.getSurface();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Capture size not supported!");
|
||||
}
|
||||
}
|
||||
return config.getSurface();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @Nullable Surface getRepeatingRequestSurface(
|
||||
@NonNull List<OutputConfiguration> outputConfigs,
|
||||
@Nullable List<Size> supportedPreviewSizes) {
|
||||
for (OutputConfiguration config : outputConfigs) {
|
||||
SurfaceInfo surfaceInfo = querySurface(config.getSurface());
|
||||
if ((surfaceInfo.mFormat ==
|
||||
CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
|
||||
// The default RGBA_8888 is also implicitly supported because camera will
|
||||
// internally override it to
|
||||
// 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
|
||||
(surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
|
||||
Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
|
||||
surfaceInfo.mHeight);
|
||||
if ((supportedPreviewSizes == null) ||
|
||||
(!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
|
||||
throw new IllegalArgumentException("Repeating request surface size " +
|
||||
repeatingRequestSurfaceSize + " not supported!");
|
||||
}
|
||||
|
||||
return config.getSurface();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,28 @@ static void CameraMetadata_update(JNIEnv *env, jclass thiz, jlong dst, jlong src
|
||||
metadataSrc->unlock(metaBuffer);
|
||||
return;
|
||||
}
|
||||
metadataDst->update(entry.tag, entry.data.u8, entry.count);
|
||||
switch (entry.type) {
|
||||
case TYPE_BYTE:
|
||||
metadataDst->update(entry.tag, entry.data.u8, entry.count);
|
||||
break;
|
||||
case TYPE_INT32:
|
||||
metadataDst->update(entry.tag, entry.data.i32, entry.count);
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
metadataDst->update(entry.tag, entry.data.f, entry.count);
|
||||
break;
|
||||
case TYPE_INT64:
|
||||
metadataDst->update(entry.tag, entry.data.i64, entry.count);
|
||||
break;
|
||||
case TYPE_DOUBLE:
|
||||
metadataDst->update(entry.tag, entry.data.d, entry.count);
|
||||
break;
|
||||
case TYPE_RATIONAL:
|
||||
metadataDst->update(entry.tag, entry.data.r, entry.count);
|
||||
break;
|
||||
default:
|
||||
ALOGE("%s: Unsupported tag type: %d!", __func__, entry.type);
|
||||
}
|
||||
}
|
||||
metadataSrc->unlock(metaBuffer);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
<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"
|
||||
|
||||
@@ -25,19 +25,36 @@ 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.ParcelImage;
|
||||
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
|
||||
import android.hardware.camera2.extension.IImageProcessorImpl;
|
||||
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.ConditionVariable;
|
||||
@@ -45,9 +62,11 @@ 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;
|
||||
|
||||
@@ -69,10 +88,26 @@ 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;
|
||||
@@ -83,13 +118,20 @@ public class CameraExtensionsProxyService extends Service {
|
||||
|
||||
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 String LATEST_VERSION = "1.2.0";
|
||||
private static final String LEGACY_VERSION_PREFIX = "1.1";
|
||||
private static final String ADVANCED_VERSION_PREFIX = "1.2";
|
||||
private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX,
|
||||
LEGACY_VERSION_PREFIX, "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 final boolean LEGACY_VERSION_SUPPORTED =
|
||||
EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(LEGACY_VERSION_PREFIX);
|
||||
private static final boolean ADVANCED_VERSION_SUPPORTED =
|
||||
EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX);
|
||||
|
||||
private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
|
||||
|
||||
private static boolean checkForExtensions() {
|
||||
try {
|
||||
@@ -223,7 +265,7 @@ public class CameraExtensionsProxyService extends Service {
|
||||
|
||||
public long registerClient(Context ctx) {
|
||||
synchronized (mLock) {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
if (LEGACY_VERSION_SUPPORTED) {
|
||||
if (mActiveClients.isEmpty()) {
|
||||
InitializerFuture status = new InitializerFuture();
|
||||
InitializerImpl.init(EXTENSIONS_VERSION, ctx, new InitializeHandler(status),
|
||||
@@ -257,7 +299,7 @@ public class CameraExtensionsProxyService extends Service {
|
||||
public void unregisterClient(long clientId) {
|
||||
synchronized (mLock) {
|
||||
if (mActiveClients.remove(clientId) && mActiveClients.isEmpty() &&
|
||||
LATEST_VERSION_SUPPORTED) {
|
||||
LEGACY_VERSION_SUPPORTED) {
|
||||
InitializerFuture status = new InitializerFuture();
|
||||
InitializerImpl.deinit(new ReleaseHandler(status),
|
||||
new HandlerExecutor(mHandler));
|
||||
@@ -321,17 +363,39 @@ public class CameraExtensionsProxyService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 ch = manager.getCameraCharacteristics(cameraId);
|
||||
mCharacteristicsHashMap.put(cameraId,
|
||||
manager.getCameraCharacteristics(cameraId));
|
||||
}
|
||||
}
|
||||
} catch (CameraAccessException e) {
|
||||
@@ -372,6 +436,29 @@ public class CameraExtensionsProxyService extends Service {
|
||||
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 static CameraMetadataNative initializeParcelableMetadata(
|
||||
List<Pair<CaptureRequest.Key, Object>> paramList) {
|
||||
if (paramList == null) {
|
||||
@@ -386,6 +473,20 @@ public class CameraExtensionsProxyService extends Service {
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static CameraMetadataNative initializeParcelableMetadata(
|
||||
Map<CaptureRequest.Key<?>, Object> paramMap) {
|
||||
if (paramMap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CameraMetadataNative ret = new CameraMetadataNative();
|
||||
for (Map.Entry<CaptureRequest.Key<?>, Object> param : paramMap.entrySet()) {
|
||||
ret.set(((CaptureRequest.Key) param.getKey()), param.getValue());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static android.hardware.camera2.extension.CaptureStageImpl initializeParcelable(
|
||||
androidx.camera.extensions.impl.CaptureStageImpl captureStage) {
|
||||
if (captureStage == null) {
|
||||
@@ -400,6 +501,20 @@ public class CameraExtensionsProxyService extends Service {
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Request initializeParcelable(RequestProcessorImpl.Request request, int requestId) {
|
||||
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());
|
||||
ret.requestId = requestId;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private class CameraExtensionsProxyServiceStub extends ICameraExtensionsProxyService.Stub {
|
||||
@Override
|
||||
public long registerClient() {
|
||||
@@ -411,6 +526,23 @@ public class CameraExtensionsProxyService extends Service {
|
||||
CameraExtensionsProxyService.unregisterClient(clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advancedExtensionsSupported() {
|
||||
return ADVANCED_VERSION_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;
|
||||
@@ -436,6 +568,475 @@ public class CameraExtensionsProxyService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (mImageProcessor != null) {
|
||||
mImageProcessor.onNextImageAvailable(outputConfigId.id, img.timestamp,
|
||||
new ImageReferenceImpl(img));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RequestProcessorStub implements RequestProcessorImpl {
|
||||
private final IRequestProcessorImpl mRequestProcessor;
|
||||
|
||||
public RequestProcessorStub(IRequestProcessorImpl requestProcessor) {
|
||||
mRequestProcessor = requestProcessor;
|
||||
}
|
||||
|
||||
@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 boolean submit(Request request, Callback callback) {
|
||||
ArrayList<Request> requests = new ArrayList<>();
|
||||
requests.add(request);
|
||||
return submit(requests, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean 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));
|
||||
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 false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setRepeating(Request request, Callback callback) {
|
||||
try {
|
||||
ArrayList<Request> requests = new ArrayList<>();
|
||||
requests.add(request);
|
||||
return mRequestProcessor.setRepeating(initializeParcelable(request, 0),
|
||||
new RequestCallbackStub(requests, callback));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to submit repeating request due to remote exception!");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
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());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deInitSession() {
|
||||
mSessionProcessor.deInitSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureSessionStart(IRequestProcessorImpl requestProcessor) {
|
||||
mSessionProcessor.onCaptureSessionStart(new RequestProcessorStub(requestProcessor));
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
@@ -471,7 +1072,7 @@ public class CameraExtensionsProxyService extends Service {
|
||||
|
||||
@Override
|
||||
public void init(String cameraId, CameraMetadataNative chars) {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
if (LEGACY_VERSION_SUPPORTED) {
|
||||
mPreviewExtender.init(cameraId, new CameraCharacteristics(chars));
|
||||
}
|
||||
}
|
||||
@@ -535,7 +1136,7 @@ public class CameraExtensionsProxyService extends Service {
|
||||
|
||||
@Override
|
||||
public List<SizeList> getSupportedResolutions() {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
if (LEGACY_VERSION_SUPPORTED) {
|
||||
List<Pair<Integer, android.util.Size[]>> sizes =
|
||||
mPreviewExtender.getSupportedResolutions();
|
||||
if ((sizes != null) && !sizes.isEmpty()) {
|
||||
@@ -581,7 +1182,7 @@ public class CameraExtensionsProxyService extends Service {
|
||||
|
||||
@Override
|
||||
public void init(String cameraId, CameraMetadataNative chars) {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
if (LEGACY_VERSION_SUPPORTED) {
|
||||
mImageExtender.init(cameraId, new CameraCharacteristics(chars));
|
||||
}
|
||||
}
|
||||
@@ -627,7 +1228,7 @@ public class CameraExtensionsProxyService extends Service {
|
||||
|
||||
@Override
|
||||
public List<SizeList> getSupportedResolutions() {
|
||||
if (LATEST_VERSION_SUPPORTED) {
|
||||
if (LEGACY_VERSION_SUPPORTED) {
|
||||
List<Pair<Integer, android.util.Size[]>> sizes =
|
||||
mImageExtender.getSupportedResolutions();
|
||||
if ((sizes != null) && !sizes.isEmpty()) {
|
||||
@@ -737,6 +1338,51 @@ public class CameraExtensionsProxyService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user