Camera: Support advanced extensions

Bug: 179819443
Test: Camera CTS
Change-Id: Ia14f7b4ad40e354b1a433000b1731e648f6398bd
This commit is contained in:
Emilian Peev
2021-03-03 18:32:55 -08:00
parent fdd8754d88
commit 21b1302c51
27 changed files with 2448 additions and 206 deletions

View File

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

View File

@@ -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

View File

@@ -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.
*

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -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;

View File

@@ -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();

View File

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

View File

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

View File

@@ -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"

View File

@@ -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;