diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:06:25 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:06:25 +0000 |
commit | 40fbee9a4da7f72c49385faac9364cd2ad45a9a6 (patch) | |
tree | da0417ebd1cd17c2ec214fb4e75bfb8c80d5b089 | |
parent | c9231e3a3d5364344c2f711fedb19226518e954b (diff) | |
parent | cb5a327830ee0325116392b3dc3d796afa1c4215 (diff) | |
download | ex-android14-mainline-resolv-release.tar.gz |
Snap for 10447354 from cb5a327830ee0325116392b3dc3d796afa1c4215 to mainline-resolv-releaseaml_res_341510000aml_res_341410010aml_res_341311030aml_res_341110000aml_res_340912000android14-mainline-resolv-release
Change-Id: Ifc0569919577dd5c261f0dbd93c60ff666840d58
190 files changed, 15415 insertions, 79 deletions
diff --git a/camera2/extensions/advancedSample/Android.bp b/camera2/extensions/advancedSample/Android.bp new file mode 100644 index 00000000..9f0df8e4 --- /dev/null +++ b/camera2/extensions/advancedSample/Android.bp @@ -0,0 +1,37 @@ +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library { + name: "androidx.camera.extensions.impl.advanced", + installable: true, + static_libs: [ + "androidx.annotation_annotation" + ], + exclude_kotlinc_generated_files: true, + srcs: ["src/**/*.java"], + sdk_version: "current", + vendor: true, + jarjar_rules: "jarjar-rules.txt", +} + +prebuilt_etc { + name: "advancedSample_camera_extensions.xml", + src: "advancedSample_camera_extensions.xml", + sub_dir: "permissions", + vendor: true, +} diff --git a/camera2/extensions/advancedSample/advancedSample_camera_extensions.xml b/camera2/extensions/advancedSample/advancedSample_camera_extensions.xml new file mode 100644 index 00000000..4fcbb486 --- /dev/null +++ b/camera2/extensions/advancedSample/advancedSample_camera_extensions.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<permissions> + <library name="androidx.camera.extensions.impl" + file="/vendor/framework/androidx.camera.extensions.impl.advanced.jar"/> +</permissions> diff --git a/camera2/extensions/advancedSample/jarjar-rules.txt b/camera2/extensions/advancedSample/jarjar-rules.txt new file mode 100644 index 00000000..7442cef5 --- /dev/null +++ b/camera2/extensions/advancedSample/jarjar-rules.txt @@ -0,0 +1 @@ +rule kotlin.** androidx.camera.extensions.impl.advanced.@0
\ No newline at end of file diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java new file mode 100755 index 00000000..bd605708 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for auto image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class AutoImageCaptureExtenderImpl implements ImageCaptureExtenderImpl { + public AutoImageCaptureExtenderImpl() {} + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureProcessorImpl getCaptureProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<CaptureStageImpl> getCaptureStages() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int getMaxCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java new file mode 100755 index 00000000..0c4577a4 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java @@ -0,0 +1,101 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for auto preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class AutoPreviewExtenderImpl implements PreviewExtenderImpl { + public AutoPreviewExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl getCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorType getProcessorType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorImpl getProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java new file mode 100755 index 00000000..50c80407 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for beauty image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class BeautyImageCaptureExtenderImpl implements ImageCaptureExtenderImpl { + public BeautyImageCaptureExtenderImpl() {} + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureProcessorImpl getCaptureProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<CaptureStageImpl> getCaptureStages() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int getMaxCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java new file mode 100755 index 00000000..1f501745 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java @@ -0,0 +1,101 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for beauty preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class BeautyPreviewExtenderImpl implements PreviewExtenderImpl { + public BeautyPreviewExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl getCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorType getProcessorType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorImpl getProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java new file mode 100644 index 00000000..ee777cf9 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for bokeh image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtenderImpl { + public BokehImageCaptureExtenderImpl() {} + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureProcessorImpl getCaptureProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<CaptureStageImpl> getCaptureStages() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int getMaxCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java new file mode 100644 index 00000000..1dc5ed79 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java @@ -0,0 +1,99 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for bokeh preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class BokehPreviewExtenderImpl implements PreviewExtenderImpl { + public BokehPreviewExtenderImpl() {} + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl getCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorType getProcessorType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorImpl getProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java new file mode 100644 index 00000000..f4719b8b --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java @@ -0,0 +1,116 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.annotation.SuppressLint; +import android.graphics.ImageFormat; +import android.hardware.camera2.TotalCaptureResult; +import android.media.Image; +import android.util.Pair; +import android.util.Size; +import android.view.Surface; + +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * The interface for processing a set of {@link Image}s that have captured. + * + * @since 1.0 + */ +@SuppressLint("UnknownNullness") +public interface CaptureProcessorImpl extends ProcessorImpl { + /** + * Process a set images captured that were requested. + * + * <p> The result of the processing step should be written to the {@link Surface} that was + * received by {@link #onOutputSurface(Surface, int)}. + * + * @param results The map of {@link ImageFormat#YUV_420_888} format images and metadata to + * process. The {@link Image} that are contained within the map will become + * invalid after this method completes, so no references to them should be kept. + */ + void process(Map<Integer, Pair<Image, TotalCaptureResult>> results); + + /** + * Informs the CaptureProcessorImpl where it should write the postview output to. + * This will only be invoked once if a valid postview surface was set. + * + * @param surface A valid {@link ImageFormat#YUV_420_888} {@link Surface} + * that the CaptureProcessorImpl should write data into. + * @since 1.4 + */ + void onPostviewOutputSurface(Surface surface); + + /** + * Invoked when the Camera Framework changes the configured output resolution for + * still capture and postview. + * + * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as + * input for still capture and postview to be at the specified resolutions. + * + * @param size for the surface for still capture. + * @param postviewSize for the surface for postview. + * @since 1.4 + */ + void onResolutionUpdate(Size size, Size postviewSize); + + /** + * Process a set images captured that were requested. + * + * <p> The result of the processing step should be written to the {@link Surface} that was + * received by {@link #onOutputSurface(Surface, int)}. + * + * @param results The map of {@link ImageFormat#YUV_420_888} format images and metadata + * to process. The {@link Image} that are contained within the map will + * become invalid after this method completes, so no references to them + * should be kept. + * @param resultCallback Capture result callback to be called once the capture result + * values of the processed image are ready. + * @param executor The executor to run the callback on. If null then the callback will + * run on any arbitrary executor. + * @since 1.3 + */ + void process(Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor); + + /** + * Process a set images captured that were requested for both postview and + * still capture. + * + * <p> This processing method will be called if a postview was requested, therefore the + * processed postview should be written to the + * {@link Surface} received by {@link #onPostviewOutputSurface(Surface, int)}. + * The final result of the processing step should be written to the {@link Surface} that was + * received by {@link #onOutputSurface(Surface, int)}. Since postview should be available + * before the capture, it should be processed and written to the surface before + * the final capture is processed. + * + * @param results The map of {@link ImageFormat#YUV_420_888} format images and + * metadata to process. The {@link Image} that are contained within + * the map will become invalid after this method completes, so no + * references to them should be kept. + * @param resultCallback Capture result callback to be called once the capture result + * values of the processed image are ready. + * @param executor The executor to run the callback on. If null then the callback + * will run on any arbitrary executor. + * @throws RuntimeException if postview feature is not supported + * @since 1.4 + */ + void processWithPostview(Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureStageImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureStageImpl.java new file mode 100644 index 00000000..268a49da --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/CaptureStageImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.hardware.camera2.CaptureRequest; +import android.util.Pair; + +import java.util.List; + +/** + * The set of parameters that defines a single capture that will be sent to the camera. + * + * @since 1.0 + * @hide + */ +public interface CaptureStageImpl { + /** Returns the identifier for the {@link CaptureStageImpl}. */ + /** + * @hide + */ + int getId(); + + /** + * Returns the set of {@link CaptureRequest.Key} and the corresponding values that will be + * set for a single {@link CaptureRequest}. + */ + /** + * @hide + */ + List<Pair<CaptureRequest.Key, Object>> getParameters(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java new file mode 100644 index 00000000..3a88ab2e --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java @@ -0,0 +1,100 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.SessionConfiguration; + +/** + * Provides interfaces that the OEM needs to implement to handle the state change. + * + * @since 1.0 + */ +public interface ExtenderStateListener { + + /** + * Notify to initialize the extension. This will be called after bindToLifeCycle. This is + * where the use case is started and would be able to allocate resources here. After onInit() is + * called, the camera ID, cameraCharacteristics and context will not change until onDeInit() + * has been called. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + * @param context The {@link Context} used for CameraX. + */ + void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, Context context); + + /** + * Notify to de-initialize the extension. This callback will be invoked after unbind. + * After onDeInit() was called, it is expected that the camera ID, cameraCharacteristics will + * no longer hold, this should be where to clear all resources allocated for this use case. + */ + void onDeInit(); + + /** + * This will be invoked before creating a + * {@link android.hardware.camera2.CameraCaptureSession}. The {@link CaptureRequest} + * parameters returned via {@link CaptureStageImpl} will be passed to the camera device as + * part of the capture session initialization via + * {@link SessionConfiguration#setSessionParameters(CaptureRequest)} which only supported from + * API level 28. The valid parameter is a subset of the available capture request parameters. + * + * @return The request information to set the session wide camera parameters. + */ + CaptureStageImpl onPresetSession(); + + /** + * This will be invoked once after the {@link android.hardware.camera2.CameraCaptureSession} + * has been created. The {@link CaptureRequest} parameters returned via + * {@link CaptureStageImpl} will be used to generate a single request to the current + * configured {@link CameraDevice}. The generated request will be submitted to camera before + * processing other single requests. + * + * @return The request information to create a single capture request to camera device. + */ + CaptureStageImpl onEnableSession(); + + /** + * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is + * closed. The {@link CaptureRequest} parameters returned via {@link CaptureStageImpl} will + * be used to generate a single request to the currently configured {@link CameraDevice}. The + * generated request will be submitted to camera before the CameraCaptureSession is closed. + * + * @return The request information to customize the session. + */ + CaptureStageImpl onDisableSession(); + + /** + * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is + * initialized and must return a valid camera session type + * {@link android.hardware.camera2.params.SessionConfiguration#getSessionType} + * to be used to configure camera capture session. Both the preview and the image capture + * extender must return the same session type value for a specific extension type. If there + * is inconsistency between the session type values from preview and image extenders, then + * the session configuration will fail. + * + * + * @since 1.4 + * @return Camera capture session type. Regular and vendor specific types are supported but + * not high speed values. The extension can return -1 in which case the camera capture session + * will be configured to use the default regular type. + */ + int onSessionType(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java new file mode 100644 index 00000000..1f285720 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java @@ -0,0 +1,74 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.util.Log; + +/** + * Implementation for extension version check. + * + * <p>This class should be implemented by OEM and deployed to the target devices. 3P developers + * don't need to implement this, unless this is used for related testing usage. + * + * @since 1.0 + * @hide + */ +public class ExtensionVersionImpl { + private static final String TAG = "ExtenderVersionImpl"; + private static final String VERSION = "1.4.0"; + + /** + * @hide + */ + public ExtensionVersionImpl() { + } + + /** + * Provide the current CameraX extension library version to vendor library and vendor would + * need to return the supported version for this device. If the returned version is not + * supported by CameraX library, the Preview and ImageCapture would not be able to enable the + * specific effects provided by the vendor. + * + * <p>CameraX library provides the Semantic Versioning string in a form of + * MAJOR.MINOR.PATCH-description + * We will increment the + * MAJOR version when make incompatible API changes, + * MINOR version when add functionality in a backwards-compatible manner, and + * PATCH version when make backwards-compatible bug fixes. And the description can be ignored. + * + * <p>Vendor library should provide MAJOR.MINOR.PATCH to CameraX. The MAJOR and MINOR + * version is used to map to the version of CameraX that it supports, and CameraX extension + * would only available when MAJOR version is matched with CameraX current version. The PATCH + * version does not indicate compatibility. The patch version should be incremented whenever + * the vendor library makes bug fixes or updates to the algorithm. + * + * @param version the version of CameraX library formatted as MAJOR.MINOR.PATCH-description. + * @return the version that vendor supported in this device. The MAJOR.MINOR.PATCH format + * should be used. + */ + /** + * @hide + */ + public String checkApiVersion(String version) { + Log.d(TAG, "Extension device library version " + VERSION); + return VERSION; + } + + public boolean isAdvancedExtenderImplemented() { + return true; + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java new file mode 100644 index 00000000..f3fd2f3b --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for HDR image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderImpl { + public HdrImageCaptureExtenderImpl() {} + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureProcessorImpl getCaptureProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<CaptureStageImpl> getCaptureStages() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int getMaxCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java new file mode 100644 index 00000000..af484646 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java @@ -0,0 +1,101 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for HDR preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl { + public HdrPreviewExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl getCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorType getProcessorType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorImpl getProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java new file mode 100644 index 00000000..70c1804e --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java @@ -0,0 +1,217 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.annotation.SuppressLint; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import java.util.List; + +/** + * Provides abstract methods that the OEM needs to implement to enable extensions for image capture. + * + * @since 1.0 + */ +@SuppressLint("UnknownNullness") +public interface ImageCaptureExtenderImpl extends ExtenderStateListener { + /** + * Indicates whether the extension is supported on the device. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + * @return true if the extension is supported, otherwise false + */ + boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics); + + /** + * Initializes the extender to be used with the specified camera. + * + * <p>This should be called before any other method on the extender. The exception is {@link + * #isExtensionAvailable(String, CameraCharacteristics)}. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + */ + void init(String cameraId, CameraCharacteristics cameraCharacteristics); + + /** + * The processing that will be done on a set of captures to create and image with the effect. + */ + CaptureProcessorImpl getCaptureProcessor(); + + /** The set of captures that are needed to create an image with the effect. */ + List<CaptureStageImpl> getCaptureStages(); + + /** + * Returns the maximum size of the list returned by {@link #getCaptureStages()}. + * @return the maximum count. + */ + int getMaxCaptureStage(); + + /** + * Returns the customized supported resolutions. + * + * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned. + * + * <p>The returned resolutions should be subset of the supported sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the + * returned list is not null, it will be used to find the best resolutions combination for + * the bound use cases. + * + * @return the customized supported resolutions, or null to support all sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap}. + * @since 1.1 + */ + List<Pair<Integer, Size[]>> getSupportedResolutions(); + + /** + * Returns the customized supported postview resolutions for a still capture using + * its size. + * + * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned. + * + * <p>The returned resolutions should be subset of the supported sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. + * + * @return the customized supported resolutions, or null to support all sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap}. + * @since 1.4 + */ + List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize); + + /** + * Returns the estimated capture latency range in milliseconds for the target capture + * resolution. + * + * <p>This includes the time spent processing the multi-frame capture request along with any + * additional time for encoding of the processed buffer in the framework if necessary.</p> + * + * @param captureOutputSize size of the capture output surface. If it is null or not in the + * supported output sizes, maximum capture output size is used for + * the estimation. + * @return the range of estimated minimal and maximal capture latency in milliseconds, or + * null if no capture latency info can be provided. + * @since 1.2 + */ + Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize); + + /** + * Return a list of orthogonal capture request keys. + * + * <p>Any keys included in the list will be configurable by clients of the extension and will + * affect the extension functionality.</p> + * + * <p>Do note that the list of keys applies to {@link PreviewExtenderImpl} as well.</p> + * + * <p>Also note that the keys {@link CaptureRequest#JPEG_QUALITY} and + * {@link CaptureRequest#JPEG_ORIENTATION} are always supported regardless being added in the + * list or not. To support common camera operations like zoom, tap-to-focus, flash and + * exposure compensation, we recommend supporting the following keys if possible. + * <pre> + * zoom: {@link CaptureRequest#CONTROL_ZOOM_RATIO} + * {@link CaptureRequest#SCALER_CROP_REGION} + * tap-to-focus: + * {@link CaptureRequest#CONTROL_AF_MODE} + * {@link CaptureRequest#CONTROL_AF_TRIGGER} + * {@link CaptureRequest#CONTROL_AF_REGIONS} + * {@link CaptureRequest#CONTROL_AE_REGIONS} + * {@link CaptureRequest#CONTROL_AWB_REGIONS} + * flash: + * {@link CaptureRequest#CONTROL_AE_MODE} + * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER} + * {@link CaptureRequest#FLASH_MODE} + * exposure compensation: + * {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION} + * </pre> + * On basic extensions that implement 1.2 or prior version, the above keys are all supported + * explicitly. When migrating from 1.2 or prior to 1.3, please note that both CameraX and + * Camera2 will honor the returned list and support only the keys contained in it. For + * example, if OEM decides to return only {@link CaptureRequest#CONTROL_ZOOM_RATIO} and + * {@link CaptureRequest#SCALER_CROP_REGION} in the 1.3 implementation, it means only zoom is + * supported for the app while tap-to-focus , flash and exposure compensation are not allowed. + * + * @return List of supported orthogonal capture keys, or an empty list if no capture settings + * are not supported. + * @since 1.3 + */ + List<CaptureRequest.Key> getAvailableCaptureRequestKeys(); + + /** + * Return a list of supported capture result keys. + * + * <p>Any keys included in this list must be available as part of the registered + * {@link ProcessResultImpl} callback. In case frame processing is not supported, + * then the Camera2/CameraX framework will use the list to filter and notify camera clients + * using the respective camera results.</p> + * + * <p>At the very minimum, it is expected that the result key list is a superset of the + * capture request keys.</p> + * + * <p>Do note that the list of keys applies to {@link PreviewExtenderImpl} as well.</p> + * + * @return List of supported capture result keys, or an empty list if capture results are not + * supported. + * @since 1.3 + */ + List<CaptureResult.Key> getAvailableCaptureResultKeys(); + + /** + * Advertise support for {@link ProcessResultImpl#onCaptureProcessProgressed}. + * + * @return {@code true} in case the process progress callback is supported and is expected to + * be triggered, {@code false} otherwise. + * @since 1.4 + */ + boolean isCaptureProcessProgressAvailable(); + + /** + * Returns the dynamically calculated capture latency pair in milliseconds. + * + * <p>In contrast to {@link #getEstimatedCaptureLatencyRange} this method is guaranteed to be + * called after the camera capture session is initialized and camera preview is enabled. + * The measurement is expected to take in to account dynamic parameters such as the current + * scene, the state of 3A algorithms, the state of internal HW modules and return a more + * accurate assessment of the still capture latency.</p> + * + * @return pair that includes the estimated input frame/frames camera capture latency as the + * first field and the estimated post-processing latency {@link CaptureProcessorImpl#process} + * as the second pair field. Both first and second fields will be in milliseconds. The total + * still capture latency will be the sum of both the first and second values. + * The pair is expected to be null if the dynamic latency estimation is not supported. + * If clients have not configured a still capture output, then this method can also return a + * null pair. + * @since 1.4 + */ + Pair<Long, Long> getRealtimeCaptureLatency(); + + /** + * Indicates whether the extension supports the postview for still capture feature. + * If the extension is using HAL processing, false should be returned since the + * postview feature is not currently supported for this case. + * + * @return {@code true} in case postview for still capture is supported + * {@code false} otherwise. + * @since 1.4 + */ + boolean isPostviewAvailable(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/InitializerImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/InitializerImpl.java new file mode 100644 index 00000000..fe2afd52 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/InitializerImpl.java @@ -0,0 +1,129 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.util.Log; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +/** + * Used for initializing the extensions library. + * + * @since 1.1 + * @hide + */ +public class InitializerImpl { + private InitializerImpl() { + } + + private static final String TAG = "InitializerImpl"; + /** + * An unknown error has occurred. + * @hide + */ + public static final int ERROR_UNKNOWN = 0; + /** + * Error reported if the application version of extensions is incompatible with the on device + * library version. + * @hide + */ + public static final int ERROR_INITIALIZE_VERSION_INCOMPATIBLE = 1; + private static Executor sExecutor = Executors.newSingleThreadExecutor(); + + /** + * Initializes the {@link Context}. + * + * <p>Before this call has been made no calls to the extensions library should be made except + * for {@link ExtensionVersionImpl#checkApiVersion(String)}. + * + * @param version The version of the extension used by the application. + * @param context The {@link Context} of the calling application. + * @param executor The executor to run the callback on. If null then the callback will run on + * any arbitrary executor. + * @hide + */ + public static void init(String version, Context context, + OnExtensionsInitializedCallback callback, Executor executor) { + Log.d(TAG, "initializing extensions"); + if (executor == null) { + sExecutor.execute(callback::onSuccess); + } else { + executor.execute(callback::onSuccess); + } + } + + /** + * Deinitializes the extensions to release resources. + * + * <p>After this call has been made no calls to the extensions library should be made except + * for {@link ExtensionVersionImpl#checkApiVersion(String)}. + * + * @param executor The executor to run the callback on. If null then the callback will run on + * any arbitrary executor. + * @hide + */ + public static void deinit(OnExtensionsDeinitializedCallback callback, + Executor executor) { + Log.d(TAG, "deinitializing extensions"); + if (executor == null) { + sExecutor.execute(callback::onSuccess); + } else { + executor.execute(callback::onSuccess); + } + } + + /** + * Callback that gets called when the library has finished initializing and is ready for used. + * @hide + */ + public interface OnExtensionsInitializedCallback { + /** Called if the library successfully initializes. */ + void onSuccess(); + + /** + * Called if the library is unable to successfully initialize. + * + * @param error The reason for failing to initialize. + */ + void onFailure(int error); + } + + /** + * Callback that gets called when the library has finished deinitialized. + * + * <p> Once this interface has been called then + * {@link #init(String, Context, OnExtensionsInitializedCallback, Executor)} can be called + * again regardless of whether or not the deinitialization has succeeded or failed. + * @hide + */ + public interface OnExtensionsDeinitializedCallback { + /** Called if the library successfully deinitializes. */ + void onSuccess(); + + /** + * Called if the library encountered some error during the deinitialization. + * + * <p>Even if the library fails to deinitialize it is now valid for {@link + * #init(String, Context, OnExtensionsInitializedCallback, Executor)} to be called + * again. + * + * @param error The reason for failing to deinitialize. + */ + void onFailure(int error); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java new file mode 100755 index 00000000..6f0eaef9 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for night image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class NightImageCaptureExtenderImpl implements ImageCaptureExtenderImpl { + public NightImageCaptureExtenderImpl() {} + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureProcessorImpl getCaptureProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<CaptureStageImpl> getCaptureStages() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int getMaxCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Nullable + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java new file mode 100755 index 00000000..825994f5 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java @@ -0,0 +1,101 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Stub implementation for night preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class NightPreviewExtenderImpl implements PreviewExtenderImpl { + public NightPreviewExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @Nullable CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl getCaptureStage() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorType getProcessorType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public ProcessorImpl getProcessor() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public void onDeInit() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onPresetSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onEnableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public CaptureStageImpl onDisableSession() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java new file mode 100644 index 00000000..43249873 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.TotalCaptureResult; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Provides abstract methods that the OEM needs to implement to enable extensions in the preview. + * + * @since 1.0 + */ +public interface PreviewExtenderImpl extends ExtenderStateListener { + /** The different types of the preview processing. */ + enum ProcessorType { + /** Processor which only updates the {@link CaptureStageImpl}. */ + PROCESSOR_TYPE_REQUEST_UPDATE_ONLY, + /** Processor which updates the received {@link android.media.Image}. */ + PROCESSOR_TYPE_IMAGE_PROCESSOR, + /** No processor, only a {@link CaptureStageImpl} is defined. */ + PROCESSOR_TYPE_NONE + } + + /** + * Indicates whether the extension is supported on the device. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + * @return true if the extension is supported, otherwise false + */ + boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics); + + /** + * Initializes the extender to be used with the specified camera. + * + * <p>This should be called before any other method on the extender. The exception is {@link + * #isExtensionAvailable(String, CameraCharacteristics)}. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + */ + void init(String cameraId, CameraCharacteristics cameraCharacteristics); + + /** + * The set of parameters required to produce the effect on the preview stream. + * + * <p> This will be the initial set of parameters used for the preview + * {@link android.hardware.camera2.CaptureRequest}. If the {@link ProcessorType} is defined as + * {@link ProcessorType#PROCESSOR_TYPE_REQUEST_UPDATE_ONLY} then this will be updated when + * the {@link RequestUpdateProcessorImpl#process(TotalCaptureResult)} from {@link + * #getProcessor()} has been called, this should be updated to reflect the new {@link + * CaptureStageImpl}. If the processing step returns a {@code null}, meaning the required + * parameters has not changed, then calling this will return the previous non-null value. + */ + CaptureStageImpl getCaptureStage(); + + /** The type of preview processing to use. */ + ProcessorType getProcessorType(); + + /** + * Returns a processor which only updates the {@link CaptureStageImpl}. + * + * <p>The type of processor is dependent on the return of {@link #getProcessorType()}. The + * type of ProcessorImpl returned will be according to the following table. + * + * <table> + * <tr><th> ProcessorType </th> <th> ProcessorImpl </th> </tr> + * <tr><td> PROCESSOR_TYPE_REQUEST_UPDATE_ONLY </td> <td> RequestUpdateProcessorImpl </td> </tr> + * <tr><td> PROCESSOR_TYPE_IMAGE_PROCESSOR </td> <td> PreviewImageProcessorImpl </td> </tr> + * <tr><td> PROCESSOR_TYPE_NONE </td> <td> null </td> </tr> + * </table> + */ + ProcessorImpl getProcessor(); + + /** + * Returns the customized supported resolutions. + * + * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned. + * + * <p>The returned resolutions should be subset of the supported sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the + * returned list is not null, it will be used to find the best resolutions combination for + * the bound use cases. + * + * @return the customized supported resolutions. + * @since 1.1 + */ + @Nullable + List<Pair<Integer, Size[]>> getSupportedResolutions(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java new file mode 100644 index 00000000..f203ebad --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java @@ -0,0 +1,64 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.annotation.SuppressLint; +import android.graphics.ImageFormat; +import android.hardware.camera2.TotalCaptureResult; +import android.media.Image; + +import java.util.concurrent.Executor; + +/** + * Processes a single {@link Image} and {@link TotalCaptureResult} to produce an output to a + * stream. + * + * @since 1.0 + */ +@SuppressLint("UnknownNullness") +public interface PreviewImageProcessorImpl extends ProcessorImpl { + /** + * Processes the requested image capture. + * + * <p> The result of the processing step should be written to the {@link android.view.Surface} + * that was received by {@link ProcessorImpl#onOutputSurface(android.view.Surface, int)}. + * + * @param image The {@link ImageFormat#YUV_420_888} format image to process. This will be + * invalid after the method completes so no reference to it should be kept. + * @param result The metadata associated with the image to process. + */ + void process(Image image, TotalCaptureResult result); + + /** + * Processes the requested image capture. + * + * <p> The result of the processing step should be written to the {@link android.view.Surface} + * that was received by {@link ProcessorImpl#onOutputSurface(android.view.Surface, int)}. + * + * @param image The {@link ImageFormat#YUV_420_888} format image to process. This will + * be invalid after the method completes so no reference to it should be + * kept. + * @param result The metadata associated with the image to process. + * @param resultCallback Capture result callback to be called once the capture result + * values of the processed image are ready. + * @param executor The executor to run the callback on. If null then the callback will + * run on any arbitrary executor. + * @since 1.3 + */ + void process(Image image, TotalCaptureResult result, ProcessResultImpl resultCallback, + Executor executor); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java new file mode 100644 index 00000000..0e154450 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; + +import java.util.List; + +/** + * Allows clients to receive information about the capture result values of processed frames. + * + */ +@SuppressLint("UnknownNullness") +public interface ProcessResultImpl { + /** + * Capture result callback that needs to be called when the process capture results are + * ready as part of frame post-processing. + * + * @param shutterTimestamp The shutter time stamp of the processed frame. + * @param result Key value pairs for all supported capture results. Do note that + * if results 'android.jpeg.quality' and 'android.jpeg.orientation' + * are present in the process capture input results, then the values + * must also be passed as part of this callback. Both Camera2 and + * CameraX guarantee that those two settings and results are always + * supported and applied by the corresponding framework. + * @since 1.3 + */ + void onCaptureCompleted(long shutterTimestamp, List<Pair<CaptureResult.Key, Object>> result); + + /** + * Capture progress callback that needs to be called when the process capture is + * ongoing and includes the estimated progress of the processing. + * + * <p>Extensions must ensure that they always call this callback with monotonically increasing + * values.</p> + * + * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the + * callback is expected to be called once when processing is done with value 100.</p> + * + * @param progress Value between 0 and 100. + * @since 1.4 + */ + void onCaptureProcessProgressed(int progress); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessorImpl.java new file mode 100644 index 00000000..6be328be --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/ProcessorImpl.java @@ -0,0 +1,55 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.util.Size; +import android.view.Surface; + +/** + * Processes an input image stream and produces an output image stream. + * + * @since 1.0 + */ +public interface ProcessorImpl { + /** + * Updates where the ProcessorImpl should write the output to. + * + * @param surface The {@link Surface} that the ProcessorImpl should write data into. + * @param imageFormat The format of that the surface expects. + */ + void onOutputSurface(Surface surface, int imageFormat); + + /** + * Invoked when CameraX changes the configured output resolution. + * + * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as + * input to be at the specified resolution. + * + * @param size for the surface. + */ + void onResolutionUpdate(Size size); + + /** + * Invoked when CameraX changes the configured input image format. + * + * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as + * input to have the specified image format. + * + * @param imageFormat for the surface. + */ + void onImageFormatUpdate(int imageFormat); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java new file mode 100644 index 00000000..14637d7c --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.hardware.camera2.TotalCaptureResult; + +/** + * Processes a {@link TotalCaptureResult} to update a CaptureStage. + * + * @since 1.0 + */ +public interface RequestUpdateProcessorImpl extends ProcessorImpl { + /** + * Process the {@link TotalCaptureResult} to update the {@link CaptureStageImpl} + * + * @param result The metadata associated with the image. Can be null if the image and meta have + * not been synced. + * @return The updated parameters used for the repeating requests. If this is {@code null} then + * the previous parameters will be used. + */ + CaptureStageImpl process(TotalCaptureResult result); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java new file mode 100644 index 00000000..abcbf5fd --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java @@ -0,0 +1,219 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Range; +import android.util.Size; + +import androidx.camera.extensions.impl.ExtensionVersionImpl; + +import java.util.List; +import java.util.Map; + +/** + * Advanced OEM contract for implementing Extensions. ImageCapture/Preview Extensions are both + * implemented on this interface. + * + * <p>This advanced OEM contract empowers OEM to gain access to more Camera2 capability. This + * includes: (1) Add custom surfaces with specific formats like YUV, RAW, RAW_DEPTH. (2) Access to + * the capture request callbacks as well as all the images retrieved of various image formats. (3) + * Able to triggers single or repeating request with the capabilities to specify target surfaces, + * template id and parameters. + * + * <p>OEM needs to implement it with class name HdrAdvancedExtenderImpl for HDR, + * NightAdvancedExtenderImpl for night mode, BeautyAdvancedExtenderImpl for beauty mode, + * BokehAdvancedExtenderImpl for bokeh mode and AutoAdvancedExtenderImpl for auto mode. + * + * <p>OEMs are required to return true in + * {@link ExtensionVersionImpl#isAdvancedExtenderImplemented()} in order to request CameraX to + * use advanced extender over basic extender. OEM is okay to implement advanced + * extender only Or basic extender only. However the caveat of advanced-only implementation is, + * extensions will be unavailable on the apps using interfaces prior to 1.2. + * + * @since 1.2 + */ +@SuppressLint("UnknownNullness") +public interface AdvancedExtenderImpl { + + /** + * Indicates whether the extension is supported on the device. + * + * @param cameraId The camera2 id string of the camera. + * @param characteristicsMap A map consisting of the camera ids and the + * {@link CameraCharacteristics}s. For every camera, the map + * contains at least the CameraCharacteristics for the camera id. + * If the camera is logical camera, it will also contain associated + * physical camera ids and their CameraCharacteristics. + * @return true if the extension is supported, otherwise false + */ + boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap); + + /** + * Initializes the extender to be used with the specified camera. + * + * <p>This should be called before any other method on the extender. The exception is {@link + * #isExtensionAvailable}. + * + * @param cameraId The camera2 id string of the camera. + * @param characteristicsMap A map consisting of the camera ids and the + * {@link CameraCharacteristics}s. For every camera, the map + * contains at least the CameraCharacteristics for the camera id. + * If the camera is logical camera, it will also contain associated + * physical camera ids and their CameraCharacteristics. + */ + void init(String cameraId, Map<String, CameraCharacteristics> characteristicsMap); + + /** + * Returns the estimated capture latency range in milliseconds for the + * target capture resolution during the calls to + * {@link SessionProcessorImpl#startCapture}. This + * includes the time spent processing the multi-frame capture request along with any additional + * time for encoding of the processed buffer in the framework if necessary. + * + * @param cameraId the camera id + * @param captureOutputSize size of the capture output surface. If it is null or not in the + * supported output sizes, maximum capture output size is used for + * the estimation. + * @param imageFormat the image format of the capture output surface. + * @return the range of estimated minimal and maximal capture latency in milliseconds. + * Returns null if no capture latency info can be provided. + */ + Range<Long> getEstimatedCaptureLatencyRange(String cameraId, + Size captureOutputSize, int imageFormat); + + /** + * Returns supported output format/size map for preview. The format could be PRIVATE or + * YUV_420_888. OEM must support PRIVATE format at least. CameraX will only use resolutions + * for preview from the list. + * + * <p>The preview surface format in the CameraCaptureSession may not be identical to the + * supported preview output format returned here. Like in the basic extender interface, the + * preview PRIVATE surface could be added to the CameraCaptureSession and OEM processes it in + * the HAL. Alternatively OEM can configure a intermediate YUV surface of the same size and + * writes the output to the preview output surface. + */ + Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(String cameraId); + + /** + * Returns supported output format/size map for image capture. OEM is required to support + * both JPEG and YUV_420_888 format output. + * + * <p>Like in the basic extender interface, the surface created with this supported + * format/size could be either added in CameraCaptureSession with HAL processing OR it + * configures intermediate surfaces(YUV/RAW..) and writes the output to the output surface. + */ + Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId); + + /** + * Returns supported output format/size map for postview image. OEM is required to support + * both JPEG and YUV_420_888 format output. + * + * <p>The surface created with this supported format/size could configure + * intermediate surfaces(YUV/RAW..) and write the output to the output surface.</p> + * + * @since 1.4 + */ + Map<Integer, List<Size>> getSupportedPostviewResolutions(Size captureSize); + + /** + * Returns supported output sizes for Image Analysis (YUV_420_888 format). + * + * <p>OEM can optionally support a YUV surface for ImageAnalysis along with Preview/ImageCapture + * output surfaces. If imageAnalysis YUV surface is not supported, OEM should return null or + * empty list. + */ + List<Size> getSupportedYuvAnalysisResolutions(String cameraId); + + /** + * Returns a processor for activating extension sessions. It implements all the interactions + * required for starting a extension and cleanup. + */ + SessionProcessorImpl createSessionProcessor(); + + /** + * Returns a list of orthogonal capture request keys. + * + * <p>Any keys included in the list will be configurable by clients of the extension and will + * affect the extension functionality.</p> + * + * <p>Please note that the keys {@link CaptureRequest#JPEG_QUALITY} and + * {@link CaptureRequest#JPEG_ORIENTATION} are always supported regardless being added in the + * list or not. To support common camera operations like zoom, tap-to-focus, flash and + * exposure compensation, we recommend supporting the following keys if possible. + * <pre> + * zoom: {@link CaptureRequest#CONTROL_ZOOM_RATIO} + * {@link CaptureRequest#SCALER_CROP_REGION} + * tap-to-focus: + * {@link CaptureRequest#CONTROL_AF_MODE} + * {@link CaptureRequest#CONTROL_AF_TRIGGER} + * {@link CaptureRequest#CONTROL_AF_REGIONS} + * {@link CaptureRequest#CONTROL_AE_REGIONS} + * {@link CaptureRequest#CONTROL_AWB_REGIONS} + * flash: + * {@link CaptureRequest#CONTROL_AE_MODE} + * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER} + * {@link CaptureRequest#FLASH_MODE} + * exposure compensation: + * {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION} + * </pre> + * + * @return List of supported orthogonal capture keys, or an empty list if no capture settings + * are not supported. + * @since 1.3 + */ + List<CaptureRequest.Key> getAvailableCaptureRequestKeys(); + + /** + * Returns a list of supported capture result keys. + * + * <p>Any keys included in this list must be available as part of the registered + * {@link SessionProcessorImpl.CaptureCallback#onCaptureCompleted} callback.</p> + * + * <p>At the very minimum, it is expected that the result key list is a superset of the + * capture request keys.</p> + * + * @return List of supported capture result keys, or + * an empty list if capture results are not supported. + * @since 1.3 + */ + List<CaptureResult.Key> getAvailableCaptureResultKeys(); + + /** + * Advertise support for {@link SessionProcessorImpl#onCaptureProcessProgressed}. + * + * @return {@code true} in case the process progress callback is supported and is expected to + * be triggered, {@code false} otherwise. + * @since 1.4 + */ + boolean isCaptureProcessProgressAvailable(); + + /** + * Indicates whether the extension supports the postview for still capture feature. + * If the extension is using HAL processing, false should be returned since the + * postview feature is not currently supported for this case. + * + * @return {@code true} in case postview for still capture is supported + * {@code false} otherwise. + * @since 1.4 + */ + boolean isPostviewAvailable(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java new file mode 100644 index 00000000..06c8c44a --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import androidx.camera.extensions.impl.advanced.BaseAdvancedExtenderImpl.BaseAdvancedSessionProcessor; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.os.Build; +import android.util.Log; + +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; +import java.util.Map; + +@SuppressLint("UnknownNullness") +public class AutoAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { + + protected static final int AWB_MODE_DAYLIGHT = CaptureRequest.CONTROL_AWB_MODE_DAYLIGHT; + + public AutoAdvancedExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + // Requires API 23 for ImageWriter + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return false; + } + + CameraCharacteristics cameraCharacteristics = characteristicsMap.get(cameraId); + + if (cameraCharacteristics == null) { + return false; + } + + return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, + AWB_MODE_DAYLIGHT); + } + + public class AutoAdvancedSessionProcessor extends BaseAdvancedSessionProcessor { + + public AutoAdvancedSessionProcessor() { + appendTag("::Auto"); + } + + @Override + protected void addSessionParameter(Camera2SessionConfigImplBuilder builder) { + builder.addSessionParameter(CaptureRequest.CONTROL_AWB_MODE, AWB_MODE_DAYLIGHT); + } + + @Override + protected void addCaptureRequestParameters(List<RequestProcessorImpl.Request> requestList) { + RequestBuilder build = new RequestBuilder(mCaptureOutputConfig.getId(), + CameraDevice.TEMPLATE_STILL_CAPTURE, DEFAULT_CAPTURE_ID); + build.setParameters(CaptureRequest.CONTROL_AWB_MODE, AWB_MODE_DAYLIGHT); + applyParameters(build); + + requestList.add(build.build()); + } + } + + @Override + public SessionProcessorImpl createSessionProcessor() { + return new AutoAdvancedSessionProcessor(); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java new file mode 100644 index 00000000..00b57058 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BaseAdvancedExtenderImpl.java @@ -0,0 +1,699 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import androidx.camera.extensions.impl.advanced.JpegEncoder; + +import static androidx.camera.extensions.impl.advanced.JpegEncoder.JPEG_DEFAULT_QUALITY; +import static androidx.camera.extensions.impl.advanced.JpegEncoder.JPEG_DEFAULT_ROTATION; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.Image; +import android.media.Image.Plane; +import android.media.ImageWriter; +import android.os.Build; +import android.util.Log; +import android.util.Pair; +import android.util.Range; +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.GuardedBy; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.Executor; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@SuppressLint("UnknownNullness") +public abstract class BaseAdvancedExtenderImpl implements AdvancedExtenderImpl { + + static { + try { + System.loadLibrary("encoderjpeg_jni"); + } catch (UnsatisfiedLinkError e) { + Log.e("BaseAdvancedExtenderImpl", "libencoderjpeg_jni not loaded"); + } + } + + protected CameraCharacteristics mCameraCharacteristics; + + public BaseAdvancedExtenderImpl() { + } + + @Override + public abstract boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap); + + @Override + public void init(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + mCameraCharacteristics = characteristicsMap.get(cameraId); + } + + @Override + public Range<Long> getEstimatedCaptureLatencyRange( + String cameraId, Size size, int imageFormat) { + return null; + } + + protected Map<Integer, List<Size>> filterOutputResolutions(List<Integer> formats) { + Map<Integer, List<Size>> formatResolutions = new HashMap<>(); + + StreamConfigurationMap map = + mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + + if (map != null) { + for (Integer format : formats) { + if (map.getOutputSizes(format) != null) { + formatResolutions.put(format, Arrays.asList(map.getOutputSizes(format))); + } + } + } + + return formatResolutions; + } + + @Override + public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(String cameraId) { + return filterOutputResolutions(Arrays.asList(ImageFormat.PRIVATE, ImageFormat.YUV_420_888)); + } + + @Override + public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId) { + return filterOutputResolutions(Arrays.asList(ImageFormat.JPEG, ImageFormat.YUV_420_888)); + } + + @Override + public Map<Integer, List<Size>> getSupportedPostviewResolutions(Size captureSize) { + return new HashMap<>(); + } + + @Override + public List<Size> getSupportedYuvAnalysisResolutions( + String cameraId) { + return null; + } + + public class BaseAdvancedSessionProcessor implements SessionProcessorImpl { + protected String TAG = "BaseAdvancedSessionProcessor"; + + protected static final int DEFAULT_CAPTURE_ID = 0; + protected static final int BASIC_CAPTURE_PROCESS_MAX_IMAGES = 3; + protected static final int MAX_NUM_IMAGES = 1; + + protected Camera2OutputConfigImpl mPreviewOutputConfig; + protected Camera2OutputConfigImpl mCaptureOutputConfig; + + protected OutputSurfaceImpl mPreviewOutputSurfaceConfig; + protected OutputSurfaceImpl mCaptureOutputSurfaceConfig; + + protected final Object mLock = new Object(); + @GuardedBy("mLock") + protected Map<CaptureRequest.Key<?>, Object> mParameters = new LinkedHashMap<>(); + + protected final Object mLockCaptureSurfaceImageWriter = new Object(); + @GuardedBy("mLockCaptureSurfaceImageWriter") + protected ImageWriter mCaptureSurfaceImageWriter; + + protected CaptureResultImageMatcher mImageCaptureCaptureResultImageMatcher = + new CaptureResultImageMatcher(); + protected HashMap<Integer, Pair<ImageReferenceImpl, TotalCaptureResult>> mCaptureResults = + new HashMap<>(); + protected RequestProcessorImpl mRequestProcessor; + + protected List<Integer> mCaptureIdList = List.of(DEFAULT_CAPTURE_ID); + + protected AtomicInteger mNextCaptureSequenceId = new AtomicInteger(1); + + protected void appendTag(String tag) { + TAG += tag; + } + + @Override + @NonNull + public Camera2SessionConfigImpl initSession(@NonNull String cameraId, + @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap, + @NonNull Context context, + @NonNull OutputSurfaceConfigurationImpl surfaceConfigs) { + + Log.d(TAG, "initSession cameraId=" + cameraId); + + mPreviewOutputSurfaceConfig = surfaceConfigs.getPreviewOutputSurface(); + mCaptureOutputSurfaceConfig = surfaceConfigs.getImageCaptureOutputSurface(); + + Camera2SessionConfigImplBuilder builder = + new Camera2SessionConfigImplBuilder() + .setSessionTemplateId(CameraDevice.TEMPLATE_PREVIEW); + + // Preview + if (mPreviewOutputSurfaceConfig.getSurface() != null) { + Camera2OutputConfigImplBuilder previewOutputConfigBuilder; + + previewOutputConfigBuilder = + Camera2OutputConfigImplBuilder.newSurfaceConfig( + mPreviewOutputSurfaceConfig.getSurface()); + + mPreviewOutputConfig = previewOutputConfigBuilder.build(); + + builder.addOutputConfig(mPreviewOutputConfig); + } + + // Image Capture + if (mCaptureOutputSurfaceConfig.getSurface() != null) { + Camera2OutputConfigImplBuilder captureOutputConfigBuilder; + + captureOutputConfigBuilder = + Camera2OutputConfigImplBuilder.newImageReaderConfig( + mCaptureOutputSurfaceConfig.getSize(), + ImageFormat.YUV_420_888, + BASIC_CAPTURE_PROCESS_MAX_IMAGES); + + mCaptureOutputConfig = captureOutputConfigBuilder.build(); + + builder.addOutputConfig(mCaptureOutputConfig); + } + + addSessionParameter(builder); + + return builder.build(); + } + + @Override + public Camera2SessionConfigImpl initSession(@NonNull String cameraId, + @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap, + @NonNull Context context, + @NonNull OutputSurfaceImpl previewSurfaceConfig, + @NonNull OutputSurfaceImpl imageCaptureSurfaceConfig, + @Nullable OutputSurfaceImpl imageAnalysisSurfaceConfig) { + + // Since this sample impl uses version 1.4, the other initSession method will be + // called. This is just a sample for earlier versions if wanting to redirect this call. + OutputSurfaceConfigurationImplImpl surfaceConfigs = + new OutputSurfaceConfigurationImplImpl(previewSurfaceConfig, + imageCaptureSurfaceConfig, imageAnalysisSurfaceConfig, + null /*postviewSurfaceConfig*/); + + return initSession(cameraId, cameraCharacteristicsMap, context, surfaceConfigs); + } + + protected void addSessionParameter(Camera2SessionConfigImplBuilder builder) { + // default empty implementation + } + + @Override + public void deInitSession() { + synchronized (mLockCaptureSurfaceImageWriter) { + if (mCaptureSurfaceImageWriter != null) { + mCaptureSurfaceImageWriter.close(); + mCaptureSurfaceImageWriter = null; + } + } + } + + @Override + public void setParameters(@NonNull Map<CaptureRequest.Key<?>, Object> parameters) { + synchronized (mLock) { + for (CaptureRequest.Key<?> key : parameters.keySet()) { + Object value = parameters.get(key); + if (value != null) { + mParameters.put(key, value); + } + } + } + } + + protected void applyParameters(RequestBuilder builder) { + synchronized (mLock) { + for (CaptureRequest.Key<?> key : mParameters.keySet()) { + Object value = mParameters.get(key); + builder.setParameters(key, value); + } + } + } + + protected void addTriggerRequestKeys(RequestBuilder builder, + Map<CaptureRequest.Key<?>, Object> triggers) { + HashSet<CaptureRequest.Key> supportedCaptureRequestKeys = + new HashSet<>(getAvailableCaptureRequestKeys()); + + for (CaptureRequest.Key<?> key : triggers.keySet()) { + if (supportedCaptureRequestKeys.contains(key)) { + Object value = triggers.get(key); + builder.setParameters(key, value); + } + } + } + + @Override + public int startTrigger(Map<CaptureRequest.Key<?>, Object> triggers, + CaptureCallback captureCallback) { + RequestBuilder builder = new RequestBuilder(mPreviewOutputConfig.getId(), + CameraDevice.TEMPLATE_PREVIEW, 0); + addTriggerRequestKeys(builder, triggers); + + final int seqId = mNextCaptureSequenceId.getAndIncrement(); + + RequestProcessorImpl.Callback callback = new RequestProcessorImpl.Callback() { + @Override + public void onCaptureStarted(RequestProcessorImpl.Request request, long frameNumber, + long timestamp) { + captureCallback.onCaptureStarted(seqId, timestamp); + } + + @Override + public void onCaptureProgressed(RequestProcessorImpl.Request request, + CaptureResult partialResult) { + + } + + @Override + public void onCaptureCompleted(RequestProcessorImpl.Request request, + TotalCaptureResult totalCaptureResult) { + addCaptureResultKeys(seqId, totalCaptureResult, captureCallback); + + captureCallback.onCaptureProcessStarted(seqId); + } + + @Override + public void onCaptureFailed(RequestProcessorImpl.Request request, + CaptureFailure captureFailure) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureBufferLost(RequestProcessorImpl.Request request, + long frameNumber, int outputStreamId) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) { + captureCallback.onCaptureSequenceCompleted(seqId); + } + + @Override + public void onCaptureSequenceAborted(int sequenceId) { + captureCallback.onCaptureSequenceAborted(seqId); + } + }; + + mRequestProcessor.submit(builder.build(), callback); + + return seqId; + } + + @Override + public void onCaptureSessionStart(@NonNull RequestProcessorImpl requestProcessor) { + mRequestProcessor = requestProcessor; + + if (mCaptureOutputSurfaceConfig.getSurface() != null) { + synchronized (mLockCaptureSurfaceImageWriter) { + if (JpegEncoder.imageFormatToPublic(mCaptureOutputSurfaceConfig + .getImageFormat()) == ImageFormat.JPEG) { + mCaptureSurfaceImageWriter = new ImageWriter + .Builder(mCaptureOutputSurfaceConfig.getSurface()) + .setImageFormat(ImageFormat.JPEG) + .setMaxImages(MAX_NUM_IMAGES) + // For JPEG format, width x height should be set to (w*h) x 1 + // since the JPEG image is returned as a 1D byte array + .setWidthAndHeight(mCaptureOutputSurfaceConfig.getSize().getWidth() + * mCaptureOutputSurfaceConfig.getSize().getHeight(), 1) + .build(); + } else { + mCaptureSurfaceImageWriter = new ImageWriter + .Builder(mCaptureOutputSurfaceConfig.getSurface()) + .setImageFormat(mCaptureOutputSurfaceConfig.getImageFormat()) + .setMaxImages(MAX_NUM_IMAGES) + .build(); + } + } + } + } + + @Override + public void onCaptureSessionEnd() { + synchronized (this) { + mImageCaptureCaptureResultImageMatcher.clear(); + } + + mRequestProcessor = null; + } + + @Override + public int startRepeating(@NonNull CaptureCallback captureCallback) { + RequestBuilder builder = new RequestBuilder(mPreviewOutputConfig.getId(), + CameraDevice.TEMPLATE_PREVIEW, 0); + applyParameters(builder); + final int seqId = mNextCaptureSequenceId.getAndIncrement(); + + RequestProcessorImpl.Callback callback = new RequestProcessorImpl.Callback() { + @Override + public void onCaptureStarted(RequestProcessorImpl.Request request, long frameNumber, + long timestamp) { + captureCallback.onCaptureStarted(seqId, timestamp); + } + + @Override + public void onCaptureProgressed(RequestProcessorImpl.Request request, + CaptureResult partialResult) { + + } + + @Override + public void onCaptureCompleted(RequestProcessorImpl.Request request, + TotalCaptureResult totalCaptureResult) { + addCaptureResultKeys(seqId, totalCaptureResult, captureCallback); + + captureCallback.onCaptureProcessStarted(seqId); + } + + @Override + public void onCaptureFailed(RequestProcessorImpl.Request request, + CaptureFailure captureFailure) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureBufferLost(RequestProcessorImpl.Request request, + long frameNumber, int outputStreamId) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) { + captureCallback.onCaptureSequenceCompleted(seqId); + } + + @Override + public void onCaptureSequenceAborted(int sequenceId) { + captureCallback.onCaptureSequenceAborted(seqId); + } + }; + + mRequestProcessor.setRepeating(builder.build(), callback); + + return seqId; + } + + protected void addCaptureResultKeys( + @NonNull int seqId, + @NonNull TotalCaptureResult result, + @NonNull CaptureCallback captureCallback) { + HashMap<CaptureResult.Key, Object> captureResults = new HashMap<>(); + + Long shutterTimestamp = result.get(CaptureResult.SENSOR_TIMESTAMP); + + if (shutterTimestamp != null) { + + List<CaptureResult.Key> captureResultKeys = getAvailableCaptureResultKeys(); + for (CaptureResult.Key key : captureResultKeys) { + if (result.get(key) != null) { + captureResults.put(key, result.get(key)); + } + } + + captureCallback.onCaptureCompleted(shutterTimestamp, seqId, + captureResults); + } + } + + protected void addCaptureRequestParameters(List<RequestProcessorImpl.Request> requestList) { + RequestBuilder build = new RequestBuilder(mCaptureOutputConfig.getId(), + CameraDevice.TEMPLATE_STILL_CAPTURE, DEFAULT_CAPTURE_ID); + applyParameters(build); + + requestList.add(build.build()); + } + + @Override + public int startCaptureWithPostview(@NonNull CaptureCallback captureCallback) { + return startCapture(captureCallback); + } + + @Override + public int startCapture(@NonNull CaptureCallback captureCallback) { + List<RequestProcessorImpl.Request> requestList = new ArrayList<>(); + addCaptureRequestParameters(requestList); + final int seqId = mNextCaptureSequenceId.getAndIncrement(); + + RequestProcessorImpl.Callback callback = new RequestProcessorImpl.Callback() { + + @Override + public void onCaptureStarted(RequestProcessorImpl.Request request, + long frameNumber, long timestamp) { + captureCallback.onCaptureStarted(seqId, timestamp); + } + + @Override + public void onCaptureProgressed(RequestProcessorImpl.Request request, + CaptureResult partialResult) { + + } + + @Override + public void onCaptureCompleted(RequestProcessorImpl.Request request, + TotalCaptureResult totalCaptureResult) { + RequestBuilder.RequestProcessorRequest requestProcessorRequest = + (RequestBuilder.RequestProcessorRequest) request; + + addCaptureResultKeys(seqId, totalCaptureResult, captureCallback); + + mImageCaptureCaptureResultImageMatcher.setCameraCaptureCallback( + totalCaptureResult, + requestProcessorRequest.getCaptureStageId()); + } + + @Override + public void onCaptureFailed(RequestProcessorImpl.Request request, + CaptureFailure captureFailure) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureBufferLost(RequestProcessorImpl.Request request, + long frameNumber, int outputStreamId) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) { + captureCallback.onCaptureSequenceCompleted(seqId); + captureCallback.onCaptureProcessProgressed(100); + } + + @Override + public void onCaptureSequenceAborted(int sequenceId) { + captureCallback.onCaptureSequenceAborted(seqId); + } + }; + + Log.d(TAG, "startCapture"); + + mRequestProcessor.submit(requestList, callback); + + if (mCaptureOutputSurfaceConfig.getSurface() != null) { + mRequestProcessor.setImageProcessor(mCaptureOutputConfig.getId(), + new ImageProcessorImpl() { + @Override + public void onNextImageAvailable(int outputStreamId, + long timestampNs, + @NonNull ImageReferenceImpl imgReferenceImpl, + @Nullable String physicalCameraId) { + mImageCaptureCaptureResultImageMatcher + .setInputImage(imgReferenceImpl); + } + }); + + mImageCaptureCaptureResultImageMatcher.setImageReferenceListener( + new CaptureResultImageMatcher.ImageReferenceListener() { + @Override + public void onImageReferenceIncoming( + @NonNull ImageReferenceImpl imageReferenceImpl, + @NonNull TotalCaptureResult totalCaptureResult, + int captureId) { + captureCallback.onCaptureProcessStarted(seqId); + processImageCapture(imageReferenceImpl, totalCaptureResult, + captureId); + } + }); + } + + return seqId; + } + + protected void processImageCapture(@NonNull ImageReferenceImpl imageReferenceImpl, + @NonNull TotalCaptureResult totalCaptureResult, + int captureId) { + + mCaptureResults.put(captureId, new Pair<>(imageReferenceImpl, totalCaptureResult)); + + if (mCaptureResults.keySet().containsAll(mCaptureIdList)) { + List<Pair<ImageReferenceImpl, TotalCaptureResult>> imageDataPairs = + new ArrayList<>(mCaptureResults.values()); + + Image resultImage = null; + int captureSurfaceWriterImageFormat = ImageFormat.UNKNOWN; + synchronized (mLockCaptureSurfaceImageWriter) { + resultImage = mCaptureSurfaceImageWriter.dequeueInputImage(); + captureSurfaceWriterImageFormat = mCaptureSurfaceImageWriter.getFormat(); + } + + if (captureSurfaceWriterImageFormat == ImageFormat.JPEG) { + // Simple processing sample that encodes image from YUV to JPEG + Image yuvImage = imageDataPairs.get(DEFAULT_CAPTURE_ID).first.get(); + + Integer jpegOrientation = JPEG_DEFAULT_ROTATION; + + synchronized (mLock) { + if (mParameters.get(CaptureRequest.JPEG_ORIENTATION) != null) { + jpegOrientation = + (Integer) mParameters.get(CaptureRequest.JPEG_ORIENTATION); + } + } + + JpegEncoder.encodeToJpeg(yuvImage, resultImage, jpegOrientation, + JPEG_DEFAULT_QUALITY); + + resultImage.setTimestamp(yuvImage.getTimestamp()); + + } else { + // Simple processing sample that transfers bytes and returns image as is + ByteBuffer yByteBuffer = resultImage.getPlanes()[0].getBuffer(); + ByteBuffer uByteBuffer = resultImage.getPlanes()[2].getBuffer(); + ByteBuffer vByteBuffer = resultImage.getPlanes()[1].getBuffer(); + + yByteBuffer.put(imageDataPairs.get( + DEFAULT_CAPTURE_ID).first.get().getPlanes()[0].getBuffer()); + uByteBuffer.put(imageDataPairs.get( + DEFAULT_CAPTURE_ID).first.get().getPlanes()[2].getBuffer()); + vByteBuffer.put(imageDataPairs.get( + DEFAULT_CAPTURE_ID).first.get().getPlanes()[1].getBuffer()); + + resultImage.setTimestamp(imageDataPairs.get( + DEFAULT_CAPTURE_ID).first.get().getTimestamp()); + } + + synchronized (mLockCaptureSurfaceImageWriter) { + mCaptureSurfaceImageWriter.queueInputImage(resultImage); + } + + for (Pair<ImageReferenceImpl, TotalCaptureResult> val : mCaptureResults.values()) { + val.first.decrement(); + } + } else { + Log.w(TAG, "Unable to process, waiting for all images"); + } + } + + @Override + public void stopRepeating() { + mRequestProcessor.stopRepeating(); + } + + @Override + public void abortCapture(int captureSequenceId) { + + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + return null; + } + } + + public static class OutputSurfaceConfigurationImplImpl implements OutputSurfaceConfigurationImpl { + private OutputSurfaceImpl mOutputPreviewSurfaceImpl; + private OutputSurfaceImpl mOutputImageCaptureSurfaceImpl; + private OutputSurfaceImpl mOutputImageAnalysisSurfaceImpl; + private OutputSurfaceImpl mOutputPostviewSurfaceImpl; + + public OutputSurfaceConfigurationImplImpl(OutputSurfaceImpl previewSurfaceConfig, + OutputSurfaceImpl imageCaptureSurfaceConfig, + OutputSurfaceImpl imageAnalysisSurfaceConfig, + OutputSurfaceImpl postviewSurfaceConfig) { + mOutputPreviewSurfaceImpl = previewSurfaceConfig; + mOutputImageCaptureSurfaceImpl = imageCaptureSurfaceConfig; + mOutputImageAnalysisSurfaceImpl = imageAnalysisSurfaceConfig; + mOutputPostviewSurfaceImpl = postviewSurfaceConfig; + } + + @Override + public OutputSurfaceImpl getPreviewOutputSurface() { + return mOutputPreviewSurfaceImpl; + } + + @Override + public OutputSurfaceImpl getImageCaptureOutputSurface() { + return mOutputImageCaptureSurfaceImpl; + } + + @Override + public OutputSurfaceImpl getImageAnalysisOutputSurface() { + return mOutputImageAnalysisSurfaceImpl; + } + + @Override + public OutputSurfaceImpl getPostviewOutputSurface() { + return mOutputPostviewSurfaceImpl; + } + } + + @Override + public abstract SessionProcessorImpl createSessionProcessor(); + + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + final CaptureRequest.Key [] CAPTURE_REQUEST_SET = {CaptureRequest.JPEG_QUALITY, + CaptureRequest.JPEG_ORIENTATION}; + return Arrays.asList(CAPTURE_REQUEST_SET); + } + + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + final CaptureResult.Key [] CAPTURE_RESULT_SET = {CaptureResult.JPEG_QUALITY, + CaptureResult.JPEG_ORIENTATION}; + return Arrays.asList(CAPTURE_RESULT_SET); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return true; + } + + @Override + public boolean isPostviewAvailable() { + return false; + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java new file mode 100644 index 00000000..d8ff59f9 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import androidx.camera.extensions.impl.advanced.BaseAdvancedExtenderImpl.BaseAdvancedSessionProcessor; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.os.Build; +import android.util.Log; + +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; +import java.util.Map; + +@SuppressLint("UnknownNullness") +public class BeautyAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { + + protected static final int AWB_MODE_TWILIGHT = CaptureRequest.CONTROL_AWB_MODE_TWILIGHT; + + public BeautyAdvancedExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + // Requires API 23 for ImageWriter + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return false; + } + + CameraCharacteristics cameraCharacteristics = characteristicsMap.get(cameraId); + + if (cameraCharacteristics == null) { + return false; + } + + return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, + AWB_MODE_TWILIGHT); + } + + public class BeautyAdvancedSessionProcessor extends BaseAdvancedSessionProcessor { + + public BeautyAdvancedSessionProcessor() { + appendTag("::Beauty"); + } + + @Override + protected void addSessionParameter(Camera2SessionConfigImplBuilder builder) { + builder.addSessionParameter(CaptureRequest.CONTROL_AWB_MODE, AWB_MODE_TWILIGHT); + } + + @Override + protected void addCaptureRequestParameters(List<RequestProcessorImpl.Request> requestList) { + RequestBuilder build = new RequestBuilder(mCaptureOutputConfig.getId(), + CameraDevice.TEMPLATE_STILL_CAPTURE, DEFAULT_CAPTURE_ID); + build.setParameters(CaptureRequest.CONTROL_AWB_MODE, AWB_MODE_TWILIGHT); + applyParameters(build); + + requestList.add(build.build()); + } + } + + @Override + public SessionProcessorImpl createSessionProcessor() { + return new BeautyAdvancedSessionProcessor(); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java new file mode 100644 index 00000000..01569911 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java @@ -0,0 +1,115 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import androidx.camera.extensions.impl.advanced.BaseAdvancedExtenderImpl.BaseAdvancedSessionProcessor; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.os.Build; +import android.util.Log; + +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@SuppressLint("UnknownNullness") +public class BokehAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { + + protected static final int AWB_MODE_SHADE = CaptureRequest.CONTROL_AWB_MODE_SHADE; + + public BokehAdvancedExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + // Requires API 23 for ImageWriter + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) { + return false; + } + + CameraCharacteristics cameraCharacteristics = characteristicsMap.get(cameraId); + + if (cameraCharacteristics == null) { + return false; + } + + return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, + AWB_MODE_SHADE) && + CameraCharacteristicAvailability.hasFlashUnit(cameraCharacteristics); + } + + public class BokehAdvancedSessionProcessor extends BaseAdvancedSessionProcessor { + + public BokehAdvancedSessionProcessor() { + appendTag("::Bokeh"); + } + + @Override + protected void addSessionParameter(Camera2SessionConfigImplBuilder builder) { + builder.addSessionParameter(CaptureRequest.CONTROL_AWB_MODE, AWB_MODE_SHADE); + } + + @Override + protected void addCaptureRequestParameters(List<RequestProcessorImpl.Request> requestList) { + RequestBuilder build = new RequestBuilder(mCaptureOutputConfig.getId(), + CameraDevice.TEMPLATE_STILL_CAPTURE, DEFAULT_CAPTURE_ID); + build.setParameters(CaptureRequest.CONTROL_AWB_MODE, AWB_MODE_SHADE); + applyParameters(build); + + requestList.add(build.build()); + } + } + + @Override + public SessionProcessorImpl createSessionProcessor() { + return new BokehAdvancedSessionProcessor(); + } + + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + final CaptureRequest.Key [] CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_AE_MODE, + CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_LOCK, + CaptureRequest.FLASH_MODE, CaptureRequest.JPEG_QUALITY, + CaptureRequest.JPEG_ORIENTATION}; + return Arrays.asList(CAPTURE_REQUEST_SET); + } + + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + final CaptureResult.Key [] CAPTURE_RESULT_SET = {CaptureResult.CONTROL_AE_MODE, + CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureResult.CONTROL_AE_LOCK, + CaptureResult.CONTROL_AE_STATE, CaptureResult.FLASH_MODE, + CaptureResult.FLASH_STATE, CaptureResult.JPEG_QUALITY, CaptureResult.JPEG_ORIENTATION}; + return Arrays.asList(CAPTURE_RESULT_SET); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java new file mode 100644 index 00000000..68de01b6 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java @@ -0,0 +1,51 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; + +import java.util.List; + +/** + * A config representing a {@link android.hardware.camera2.params.OutputConfiguration} where + * Surface will be created by the information in this config. + */ +@SuppressLint("UnknownNullness") +public interface Camera2OutputConfigImpl { + /** + * Gets thd id of this output config. The id can be used to identify the stream in vendor + * implementations. + */ + int getId(); + + /** + * Gets the surface group id. Vendor can use the surface group id to share memory between + * Surfaces. + */ + int getSurfaceGroupId(); + + /** + * Gets the physical camera id. Returns null if not specified. + */ + String getPhysicalCameraId(); + + /** + * If non-null, enable surface sharing and add the surface constructed by the return + * Camera2OutputConfig. + */ + List<Camera2OutputConfigImpl> getSurfaceSharingOutputConfigs(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java new file mode 100644 index 00000000..868be834 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java @@ -0,0 +1,227 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.params.OutputConfiguration; +import android.util.Size; +import android.view.Surface; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A builder implementation to help OEM build the {@link Camera2OutputConfigImpl} instance. + */ +@SuppressLint("UnknownNullness") +public class Camera2OutputConfigImplBuilder { + static AtomicInteger sLastId = new AtomicInteger(0); + private OutputConfigImplImpl mOutputConfig; + private int mSurfaceGroupId = OutputConfiguration.SURFACE_GROUP_ID_NONE; + private String mPhysicalCameraId; + private List<Camera2OutputConfigImpl> mSurfaceSharingConfigs; + + private Camera2OutputConfigImplBuilder(OutputConfigImplImpl outputConfig) { + mOutputConfig = outputConfig; + } + + private int getNextId() { + return sLastId.getAndIncrement(); + } + + /** + * Creates a {@link Camera2OutputConfigImpl} that represents a {@link android.media.ImageReader} + * with the given parameters. + */ + public static Camera2OutputConfigImplBuilder newImageReaderConfig( + Size size, int imageFormat, int maxImages) { + return new Camera2OutputConfigImplBuilder( + new ImageReaderOutputConfigImplImpl(size, imageFormat, maxImages)); + } + + /** + * Creates a {@link Camera2OutputConfigImpl} that represents a MultiResolutionImageReader with + * the given parameters. + */ + public static Camera2OutputConfigImplBuilder newMultiResolutionImageReaderConfig( + int imageFormat, int maxImages) { + return new Camera2OutputConfigImplBuilder( + new MultiResolutionImageReaderOutputConfigImplImpl(imageFormat, maxImages)); + } + + /** + * Creates a {@link Camera2OutputConfigImpl} that contains the Surface directly. + */ + public static Camera2OutputConfigImplBuilder newSurfaceConfig(Surface surface) { + return new Camera2OutputConfigImplBuilder(new SurfaceOutputConfigImplImpl(surface)); + } + + /** + * Adds a {@link Camera2SessionConfigImpl} to be shared with current config. + */ + public Camera2OutputConfigImplBuilder addSurfaceSharingOutputConfig( + Camera2OutputConfigImpl camera2OutputConfig) { + if (mSurfaceSharingConfigs == null) { + mSurfaceSharingConfigs = new ArrayList<>(); + } + + mSurfaceSharingConfigs.add(camera2OutputConfig); + return this; + } + + /** + * Sets a physical camera id. + */ + public Camera2OutputConfigImplBuilder setPhysicalCameraId(String physicalCameraId) { + mPhysicalCameraId = physicalCameraId; + return this; + } + + /** + * Sets surface group id. + */ + public Camera2OutputConfigImplBuilder setSurfaceGroupId(int surfaceGroupId) { + mSurfaceGroupId = surfaceGroupId; + return this; + } + + /** + * Build a {@link Camera2OutputConfigImpl} instance. + */ + public Camera2OutputConfigImpl build() { + mOutputConfig.setId(getNextId()); + mOutputConfig.setPhysicalCameraId(mPhysicalCameraId); + mOutputConfig.setSurfaceGroup(mSurfaceGroupId); + mOutputConfig.setSurfaceSharingConfigs(mSurfaceSharingConfigs); + return mOutputConfig; + } + + private static class OutputConfigImplImpl implements Camera2OutputConfigImpl { + private int mId; + private int mSurfaceGroup; + private String mPhysicalCameraId; + private List<Camera2OutputConfigImpl> mSurfaceSharingConfigs; + + OutputConfigImplImpl() { + mId = -1; + mSurfaceGroup = 0; + mPhysicalCameraId = null; + mSurfaceSharingConfigs = null; + } + + @Override + public int getId() { + return mId; + } + + @Override + public int getSurfaceGroupId() { + return mSurfaceGroup; + } + + @Override + public String getPhysicalCameraId() { + return mPhysicalCameraId; + } + + @Override + public List<Camera2OutputConfigImpl> getSurfaceSharingOutputConfigs() { + return mSurfaceSharingConfigs; + } + + public void setId(int id) { + mId = id; + } + + public void setSurfaceGroup(int surfaceGroup) { + mSurfaceGroup = surfaceGroup; + } + + public void setPhysicalCameraId(String physicalCameraId) { + mPhysicalCameraId = physicalCameraId; + } + + public void setSurfaceSharingConfigs( + List<Camera2OutputConfigImpl> surfaceSharingConfigs) { + mSurfaceSharingConfigs = surfaceSharingConfigs; + } + } + + private static class SurfaceOutputConfigImplImpl extends OutputConfigImplImpl + implements SurfaceOutputConfigImpl { + private Surface mSurface; + + SurfaceOutputConfigImplImpl(Surface surface) { + mSurface = surface; + } + + @Override + public Surface getSurface() { + return mSurface; + } + } + + private static class ImageReaderOutputConfigImplImpl extends OutputConfigImplImpl + implements ImageReaderOutputConfigImpl { + private Size mSize; + private int mImageFormat; + private int mMaxImages; + + ImageReaderOutputConfigImplImpl(Size size, int imageFormat, int maxImages) { + mSize = size; + mImageFormat = imageFormat; + mMaxImages = maxImages; + } + + @Override + public Size getSize() { + return mSize; + } + + @Override + public int getImageFormat() { + return mImageFormat; + } + + @Override + public int getMaxImages() { + return mMaxImages; + } + } + + private static class MultiResolutionImageReaderOutputConfigImplImpl extends OutputConfigImplImpl + implements MultiResolutionImageReaderOutputConfigImpl { + private int mImageFormat; + private int mMaxImages; + + MultiResolutionImageReaderOutputConfigImplImpl(int imageFormat, int maxImages) { + mImageFormat = imageFormat; + mMaxImages = maxImages; + } + + @Override + public int getImageFormat() { + return mImageFormat; + } + + @Override + public int getMaxImages() { + return mMaxImages; + } + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java new file mode 100644 index 00000000..6fb45bca --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CaptureRequest; + +import java.util.List; +import java.util.Map; + +/** + * A config representing a {@link android.hardware.camera2.params.SessionConfiguration} + */ +@SuppressLint("UnknownNullness") +public interface Camera2SessionConfigImpl { + /** + * Returns all the {@link Camera2OutputConfigImpl}s that will be used to create + * {@link android.hardware.camera2.params.OutputConfiguration}. + */ + List<Camera2OutputConfigImpl> getOutputConfigs(); + + /** + * Gets all the parameters to create the session parameters with. + */ + Map<CaptureRequest.Key<?>, Object> getSessionParameters(); + + /** + * Gets the template id used for creating {@link CaptureRequest}s to be passed in + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters}. + */ + int getSessionTemplateId(); + + /** + * Retrieves the session type to be used when initializing the + * {@link android.hardware.camera2.CameraCaptureSession}. + * + * @since 1.4 + * @return Camera capture session type. Regular and vendor specific types are supported but + * not high speed values. The extension can return -1 in which case the camera capture session + * will be configured to use the default regular type. + */ + int getSessionType(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java new file mode 100644 index 00000000..dc1feccd --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java @@ -0,0 +1,139 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.SessionConfiguration; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A builder implementation to help OEM build the {@link Camera2SessionConfigImpl} instance. + */ +@SuppressLint("UnknownNullness") +public class Camera2SessionConfigImplBuilder { + private int mSessionTemplateId = CameraDevice.TEMPLATE_PREVIEW; + private int mSessionType = SessionConfiguration.SESSION_REGULAR; + Map<CaptureRequest.Key<?>, Object> mSessionParameters = new HashMap<>(); + List<Camera2OutputConfigImpl> mCamera2OutputConfigs = new ArrayList<>(); + + public Camera2SessionConfigImplBuilder() { + } + + /** + * Adds a output config. + */ + public Camera2SessionConfigImplBuilder addOutputConfig( + Camera2OutputConfigImpl outputConfig) { + mCamera2OutputConfigs.add(outputConfig); + return this; + } + + /** + * Sets session parameters. + */ + public <T> Camera2SessionConfigImplBuilder addSessionParameter( + CaptureRequest.Key<T> key, T value) { + mSessionParameters.put(key, value); + return this; + } + + /** + * Sets the template id for session parameters request. + */ + public Camera2SessionConfigImplBuilder setSessionTemplateId(int templateId) { + mSessionTemplateId = templateId; + return this; + } + + /** + * Gets the session template id. + */ + public int getSessionTemplateId() { + return mSessionTemplateId; + } + + /** + * Gets the session parameters. + */ + public Map<CaptureRequest.Key<?>, Object> getSessionParameters() { + return mSessionParameters; + } + + /** + * Gets all the output configs. + */ + public List<Camera2OutputConfigImpl> getCamera2OutputConfigs() { + return mCamera2OutputConfigs; + } + + /** + * Gets the camera capture session type. + */ + public int getSessionType() { + return mSessionType; + } + + /** + * Builds a {@link Camera2SessionConfigImpl} instance. + */ + public Camera2SessionConfigImpl build() { + return new Camera2SessionConfigImplImpl(this); + } + + private static class Camera2SessionConfigImplImpl implements + Camera2SessionConfigImpl { + int mSessionTemplateId; + int mSessionType; + Map<CaptureRequest.Key<?>, Object> mSessionParameters; + List<Camera2OutputConfigImpl> mCamera2OutputConfigs; + + Camera2SessionConfigImplImpl(Camera2SessionConfigImplBuilder builder) { + mSessionTemplateId = builder.getSessionTemplateId(); + mSessionParameters = builder.getSessionParameters(); + mCamera2OutputConfigs = builder.getCamera2OutputConfigs(); + mSessionType = builder.getSessionType(); + } + + @Override + public List<Camera2OutputConfigImpl> getOutputConfigs() { + return mCamera2OutputConfigs; + } + + @Override + public Map<CaptureRequest.Key<?>, Object> getSessionParameters() { + return mSessionParameters; + } + + @Override + public int getSessionTemplateId() { + return mSessionTemplateId; + } + + @Override + public int getSessionType() { + return mSessionType; + } + } +} + diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/CameraCharacteristicAvailability.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/CameraCharacteristicAvailability.java new file mode 100644 index 00000000..6a77e6c9 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/CameraCharacteristicAvailability.java @@ -0,0 +1,136 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl.advanced; + +import android.hardware.camera2.CameraCharacteristics; +import android.util.Log; +import android.util.Range; + +import java.util.Arrays; + +/** + * A utility class to check the availabilities of camera characteristics. + */ +final class CameraCharacteristicAvailability { + private static final String TAG = "CharacteristicAbility"; + + private CameraCharacteristicAvailability() { + } + + /** + * Check if the given device supports flash. + * + * @param cameraCharacteristics the camera characteristics. + * @return {@code true} if the device supports flash + * {@code false} otherwise. + */ + static boolean hasFlashUnit(CameraCharacteristics cameraCharacteristics) { + Boolean flashInfo = cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + + return (flashInfo != null) ? flashInfo : false; + } + + /** + * Check if the given device supports zoom ratio. + * + * @param cameraCharacteristics the camera characteristics. + * @return {@code true} if the device supports zoom ratio + * {@code false} otherwise. + */ + static boolean supportsZoomRatio(CameraCharacteristics cameraCharacteristics) { + Range<Float> zoomRatioRange = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE); + + return (zoomRatioRange != null) && (zoomRatioRange.getUpper() > 1.f); + } + + /** + * Check if the given device is fixed focus or not. + * + * @param cameraCharacteristics the camera characteristics. + * @return {@code true} if the device is not fixed focus + * {@code false} otherwise. + */ + static boolean hasFocuser(CameraCharacteristics cameraCharacteristics) { + Float minFocusDistance = cameraCharacteristics.get( + CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); + if (minFocusDistance == null) { + Log.d(TAG, "No LENS_INFO_MINIMUM_FOCUS_DISTANCE info"); + return false; + } + + if (minFocusDistance > 0.f) { + return true; + } + return false; + } + + /** + * Check if the given white balance mode id is available in the camera characteristics. + * + * @param cameraCharacteristics the camera characteristics. + * @param mode white balance mode id. + * @return {@code true} if the given white balance mode id is available in the camera + * characteristics. + * {@code false} otherwise. + */ + static boolean isWBModeAvailable(CameraCharacteristics cameraCharacteristics, + int mode) { + int[] availableModes = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES); + if (availableModes == null) { + Log.d(TAG, "No CONTROL_AWB_AVAILABLE_MODES info"); + return false; + } + + for (int availableMode : availableModes) { + if (availableMode == mode) { + return true; + } + } + Log.d(TAG, "wb mode: " + mode + " is not in available list " + + Arrays.toString(availableModes)); + return false; + } + + /** + * Check if the given effect id is available in the camera characteristics. + * + * @param cameraCharacteristics the camera characteristics. + * @param effect the effect id. + * @return {@code true} if the given effect id is available in the camera characteristics. + * {@code false} otherwise. + */ + static boolean isEffectAvailable(CameraCharacteristics cameraCharacteristics, + int effect) { + int[] availableEffects = cameraCharacteristics.get( + CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS); + if (availableEffects == null) { + Log.d(TAG, "No CONTROL_AVAILABLE_EFFECTS info"); + return false; + } + + for (int availableEffect : availableEffects) { + if (availableEffect == effect) { + return true; + } + } + Log.d(TAG, "effect: " + effect + " is not in available list " + + Arrays.toString(availableEffects)); + return false; + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/CaptureResultImageMatcher.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/CaptureResultImageMatcher.java new file mode 100644 index 00000000..f93271b6 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/CaptureResultImageMatcher.java @@ -0,0 +1,204 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl.advanced; + +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.media.Image; +import android.util.LongSparseArray; + +import androidx.annotation.GuardedBy; +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CaptureResultImageMatcher { + private static final String TAG = "CaptureResultImageReader"; + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private boolean mClosed = false; + + /** ImageInfos haven't been matched with Image. */ + @GuardedBy("mLock") + private final LongSparseArray<TotalCaptureResult> mPendingImageInfos = new LongSparseArray<>(); + + Map<TotalCaptureResult, Integer> mCaptureStageIdMap = new HashMap<>(); + + + /** Images haven't been matched with ImageInfo. */ + @GuardedBy("mLock") + private final LongSparseArray<ImageReferenceImpl> mPendingImages = new LongSparseArray<>(); + + ImageReferenceListener mImageReferenceListener; + + public CaptureResultImageMatcher() { + + } + + public void clear() { + synchronized (mLock) { + mPendingImageInfos.clear(); + for (int i = 0; i < mPendingImages.size(); i++) { + long key = mPendingImages.keyAt(i); + mPendingImages.get(key).decrement(); + } + mPendingImages.clear(); + mCaptureStageIdMap.clear(); + mClosed = false; + } + } + + public void setImageReferenceListener( + @NonNull ImageReferenceListener imageReferenceImplListener) { + synchronized (mLock) { + mImageReferenceListener = imageReferenceImplListener; + } + } + + public void setInputImage(@NonNull ImageReferenceImpl imageReferenceImpl) { + synchronized (mLock) { + if (mClosed) { + return; + } + + Image image = imageReferenceImpl.get(); + // Add the incoming Image to pending list and do the matching logic. + mPendingImages.put(image.getTimestamp(), imageReferenceImpl); + matchImages(); + } + } + + public void setCameraCaptureCallback(@NonNull TotalCaptureResult captureResult) { + setCameraCaptureCallback(captureResult, 0); + } + + public void setCameraCaptureCallback(@NonNull TotalCaptureResult captureResult, + int captureStageId) { + synchronized (mLock) { + if (mClosed) { + return; + } + + long timestamp = getTimeStampFromCaptureResult(captureResult); + + // Add the incoming CameraCaptureResult to pending list and do the matching logic. + mPendingImageInfos.put(timestamp, captureResult); + mCaptureStageIdMap.put(captureResult, captureStageId); + matchImages(); + } + } + + + private long getTimeStampFromCaptureResult(TotalCaptureResult captureResult) { + Long timestamp = captureResult.get(CaptureResult.SENSOR_TIMESTAMP); + long timestampValue = -1; + if (timestamp != null) { + timestampValue = timestamp; + } + + return timestampValue; + } + + + private void notifyImage(ImageReferenceImpl imageReferenceImpl, + TotalCaptureResult totalCaptureResult) { + synchronized (mLock) { + if (mImageReferenceListener != null) { + mImageReferenceListener.onImageReferenceIncoming(imageReferenceImpl, + totalCaptureResult, mCaptureStageIdMap.get(totalCaptureResult)); + } else { + imageReferenceImpl.decrement(); + } + } + } + + // Remove the stale {@link ImageProxy} and {@link ImageInfo} from the pending queue if there are + // any missing which can happen if the camera is momentarily shut off. + // The ImageProxy and ImageInfo timestamps are assumed to be monotonically increasing. This + // means any ImageProxy or ImageInfo which has a timestamp older (smaller in value) than the + // oldest timestamp in the other queue will never get matched, so they should be removed. + // + // This should only be called at the end of matchImages(). The assumption is that there are no + // matching timestamps. + private void removeStaleData() { + synchronized (mLock) { + // No stale data to remove + if (mPendingImages.size() == 0 || mPendingImageInfos.size() == 0) { + return; + } + + Long minImageProxyTimestamp = mPendingImages.keyAt(0); + Long minImageInfoTimestamp = mPendingImageInfos.keyAt(0); + + // If timestamps are equal then matchImages did not correctly match up the ImageInfo + // and ImageProxy + if (minImageInfoTimestamp.equals(minImageProxyTimestamp)) { + throw new IllegalArgumentException(); + } + + if (minImageInfoTimestamp > minImageProxyTimestamp) { + for (int i = mPendingImages.size() - 1; i >= 0; i--) { + if (mPendingImages.keyAt(i) < minImageInfoTimestamp) { + ImageReferenceImpl imageReferenceImpl = mPendingImages.valueAt(i); + imageReferenceImpl.decrement(); + mPendingImages.removeAt(i); + } + } + } else { + for (int i = mPendingImageInfos.size() - 1; i >= 0; i--) { + if (mPendingImageInfos.keyAt(i) < minImageProxyTimestamp) { + mPendingImageInfos.removeAt(i); + } + } + } + + } + } + + // Match incoming Image from the ImageReader with the corresponding ImageInfo. + private void matchImages() { + synchronized (mLock) { + // Iterate in reverse order so that ImageInfo can be removed in place + for (int i = mPendingImageInfos.size() - 1; i >= 0; i--) { + TotalCaptureResult captureResult = mPendingImageInfos.valueAt(i); + long timestamp = getTimeStampFromCaptureResult(captureResult); + + ImageReferenceImpl imageReferenceImpl = mPendingImages.get(timestamp); + + if (imageReferenceImpl != null) { + mPendingImages.remove(timestamp); + mPendingImageInfos.removeAt(i); + // Got a match. Add the ImageProxy to matched list and invoke + // onImageAvailableListener. + notifyImage(imageReferenceImpl, captureResult); + } + } + + removeStaleData(); + } + } + + public interface ImageReferenceListener { + void onImageReferenceIncoming(@NonNull ImageReferenceImpl imageReferenceImpl, + @NonNull TotalCaptureResult totalCaptureResult, int captureStageId); + } + +}
\ No newline at end of file diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java new file mode 100644 index 00000000..d8b99289 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java @@ -0,0 +1,321 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl.advanced; + +import androidx.camera.extensions.impl.advanced.JpegEncoder; +import androidx.camera.extensions.impl.advanced.BaseAdvancedExtenderImpl.BaseAdvancedSessionProcessor; + +import static androidx.camera.extensions.impl.advanced.JpegEncoder.JPEG_DEFAULT_QUALITY; +import static androidx.camera.extensions.impl.advanced.JpegEncoder.JPEG_DEFAULT_ROTATION; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.MeteringRectangle; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.Image; +import android.media.Image.Plane; +import android.media.ImageWriter; +import android.util.Log; +import android.util.Pair; +import android.util.Range; +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.GuardedBy; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.io.Closeable; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@SuppressLint("UnknownNullness") +public class HdrAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { + + public HdrAdvancedExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + CameraCharacteristics cameraCharacteristics = characteristicsMap.get(cameraId); + + if (cameraCharacteristics == null) { + return false; + } + + boolean zoomRatioSupported = + CameraCharacteristicAvailability.supportsZoomRatio(cameraCharacteristics); + boolean hasFocuser = + CameraCharacteristicAvailability.hasFocuser(cameraCharacteristics); + + // Requires API 23 for ImageWriter + return (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) && + zoomRatioSupported && hasFocuser; + } + + public class HDRAdvancedSessionProcessor extends BaseAdvancedSessionProcessor { + protected static final int UNDER_EXPOSED_CAPTURE_ID = 0; + protected static final int NORMAL_EXPOSED_CAPTURE_ID = 1; + protected static final int OVER_EXPOSED_CAPTURE_ID = 2; + + List<Integer> mCaptureIdsList = List.of(UNDER_EXPOSED_CAPTURE_ID, + NORMAL_EXPOSED_CAPTURE_ID, OVER_EXPOSED_CAPTURE_ID); + + public HDRAdvancedSessionProcessor() { + appendTag("::HDR"); + } + + @Override + protected void addCaptureRequestParameters(List<RequestProcessorImpl.Request> requestList) { + // Under exposed capture + RequestBuilder builderUnder = new RequestBuilder(mCaptureOutputConfig.getId(), + CameraDevice.TEMPLATE_STILL_CAPTURE, UNDER_EXPOSED_CAPTURE_ID); + // Turn off AE so that ISO sensitivity can be controlled + builderUnder.setParameters(CaptureRequest.CONTROL_AE_MODE, + CaptureRequest.CONTROL_AE_MODE_OFF); + builderUnder.setParameters(CaptureRequest.SENSOR_EXPOSURE_TIME, + TimeUnit.MILLISECONDS.toNanos(8)); + applyParameters(builderUnder); + + // Normal exposed capture + RequestBuilder builderNormal = new RequestBuilder(mCaptureOutputConfig.getId(), + CameraDevice.TEMPLATE_STILL_CAPTURE, NORMAL_EXPOSED_CAPTURE_ID); + builderNormal.setParameters(CaptureRequest.SENSOR_EXPOSURE_TIME, + TimeUnit.MILLISECONDS.toNanos(16)); + applyParameters(builderNormal); + + // Over exposed capture + RequestBuilder builderOver = new RequestBuilder(mCaptureOutputConfig.getId(), + CameraDevice.TEMPLATE_STILL_CAPTURE, OVER_EXPOSED_CAPTURE_ID); + builderOver.setParameters(CaptureRequest.SENSOR_EXPOSURE_TIME, + TimeUnit.MILLISECONDS.toNanos(32)); + applyParameters(builderOver); + + requestList.add(builderUnder.build()); + requestList.add(builderNormal.build()); + requestList.add(builderOver.build()); + } + + @Override + public int startCapture(@NonNull CaptureCallback captureCallback) { + List<RequestProcessorImpl.Request> requestList = new ArrayList<>(); + addCaptureRequestParameters(requestList); + final int seqId = mNextCaptureSequenceId.getAndIncrement(); + + RequestProcessorImpl.Callback callback = new RequestProcessorImpl.Callback() { + boolean mCaptureStarted = false; + + @Override + public void onCaptureStarted(RequestProcessorImpl.Request request, + long frameNumber, long timestamp) { + if (!mCaptureStarted) { + mCaptureStarted = true; + captureCallback.onCaptureStarted(seqId, timestamp); + } + } + + @Override + public void onCaptureProgressed(RequestProcessorImpl.Request request, + CaptureResult partialResult) { + + } + + @Override + public void onCaptureCompleted(RequestProcessorImpl.Request request, + TotalCaptureResult totalCaptureResult) { + RequestBuilder.RequestProcessorRequest requestProcessorRequest = + (RequestBuilder.RequestProcessorRequest) request; + + mImageCaptureCaptureResultImageMatcher.setCameraCaptureCallback( + totalCaptureResult, + requestProcessorRequest.getCaptureStageId()); + } + + @Override + public void onCaptureFailed(RequestProcessorImpl.Request request, + CaptureFailure captureFailure) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureBufferLost(RequestProcessorImpl.Request request, + long frameNumber, int outputStreamId) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) { + captureCallback.onCaptureSequenceCompleted(seqId); + captureCallback.onCaptureProcessProgressed(100); + } + + @Override + public void onCaptureSequenceAborted(int sequenceId) { + captureCallback.onCaptureSequenceAborted(seqId); + } + }; + + Log.d(TAG, "startCapture"); + + mRequestProcessor.submit(requestList, callback); + + if (mCaptureOutputSurfaceConfig.getSurface() != null) { + mRequestProcessor.setImageProcessor(mCaptureOutputConfig.getId(), + new ImageProcessorImpl() { + boolean mCaptureStarted = false; + @Override + public void onNextImageAvailable(int outputStreamId, + long timestampNs, + @NonNull ImageReferenceImpl imgReferenceImpl, + @Nullable String physicalCameraId) { + mImageCaptureCaptureResultImageMatcher + .setInputImage(imgReferenceImpl); + + if (!mCaptureStarted) { + mCaptureStarted = true; + captureCallback.onCaptureProcessStarted(seqId); + } + } + }); + + mImageCaptureCaptureResultImageMatcher.setImageReferenceListener( + new CaptureResultImageMatcher.ImageReferenceListener() { + @Override + public void onImageReferenceIncoming( + @NonNull ImageReferenceImpl imageReferenceImpl, + @NonNull TotalCaptureResult totalCaptureResult, + int captureId) { + processImageCapture(imageReferenceImpl, totalCaptureResult, + captureId, seqId, captureCallback); + } + }); + } + + return seqId; + } + + private void processImageCapture(@NonNull ImageReferenceImpl imageReferenceImpl, + @NonNull TotalCaptureResult totalCaptureResult, + int captureId, + int seqId, + @NonNull CaptureCallback captureCallback) { + + mCaptureResults.put(captureId, new Pair<>(imageReferenceImpl, totalCaptureResult)); + + if (mCaptureResults.keySet().containsAll(mCaptureIdsList)) { + List<Pair<ImageReferenceImpl, TotalCaptureResult>> imageDataPairs = + new ArrayList<>(mCaptureResults.values()); + + Image resultImage = null; + int captureSurfaceWriterImageFormat = ImageFormat.UNKNOWN; + synchronized (mLockCaptureSurfaceImageWriter) { + resultImage = mCaptureSurfaceImageWriter.dequeueInputImage(); + captureSurfaceWriterImageFormat = mCaptureSurfaceImageWriter.getFormat(); + } + + if (captureSurfaceWriterImageFormat == ImageFormat.JPEG) { + Image yuvImage = imageDataPairs.get(NORMAL_EXPOSED_CAPTURE_ID).first.get(); + + Integer jpegOrientation = JPEG_DEFAULT_ROTATION; + + synchronized (mLock) { + if (mParameters.get(CaptureRequest.JPEG_ORIENTATION) != null) { + jpegOrientation = + (Integer) mParameters.get(CaptureRequest.JPEG_ORIENTATION); + } + } + + JpegEncoder.encodeToJpeg(yuvImage, resultImage, jpegOrientation, + JPEG_DEFAULT_QUALITY); + + addCaptureResultKeys(seqId, imageDataPairs.get(UNDER_EXPOSED_CAPTURE_ID) + .second, captureCallback); + resultImage.setTimestamp(imageDataPairs.get(UNDER_EXPOSED_CAPTURE_ID) + .first.get().getTimestamp()); + + } else { + ByteBuffer yByteBuffer = resultImage.getPlanes()[0].getBuffer(); + ByteBuffer uByteBuffer = resultImage.getPlanes()[2].getBuffer(); + ByteBuffer vByteBuffer = resultImage.getPlanes()[1].getBuffer(); + + yByteBuffer.put(imageDataPairs.get( + NORMAL_EXPOSED_CAPTURE_ID).first.get().getPlanes()[0].getBuffer()); + uByteBuffer.put(imageDataPairs.get( + NORMAL_EXPOSED_CAPTURE_ID).first.get().getPlanes()[2].getBuffer()); + vByteBuffer.put(imageDataPairs.get( + NORMAL_EXPOSED_CAPTURE_ID).first.get().getPlanes()[1].getBuffer()); + + addCaptureResultKeys(seqId, imageDataPairs.get(UNDER_EXPOSED_CAPTURE_ID) + .second, captureCallback); + resultImage.setTimestamp(imageDataPairs.get( + UNDER_EXPOSED_CAPTURE_ID).first.get().getTimestamp()); + } + + synchronized (mLockCaptureSurfaceImageWriter) { + mCaptureSurfaceImageWriter.queueInputImage(resultImage); + } + + for (Pair<ImageReferenceImpl, TotalCaptureResult> val : mCaptureResults.values()) { + val.first.decrement(); + } + + mCaptureResults.clear(); + } else { + Log.w(TAG, "Unable to process, waiting for all images"); + } + } + } + + @Override + public SessionProcessorImpl createSessionProcessor() { + return new HDRAdvancedSessionProcessor(); + } + + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + final CaptureRequest.Key [] CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_ZOOM_RATIO, + CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_REGIONS, + CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.JPEG_QUALITY, + CaptureRequest.JPEG_ORIENTATION}; + return Arrays.asList(CAPTURE_REQUEST_SET); + } + + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + final CaptureResult.Key [] CAPTURE_RESULT_SET = {CaptureResult.CONTROL_ZOOM_RATIO, + CaptureResult.CONTROL_AF_MODE, CaptureResult.CONTROL_AF_REGIONS, + CaptureResult.CONTROL_AF_TRIGGER, CaptureResult.CONTROL_AF_STATE, + CaptureResult.JPEG_QUALITY, CaptureResult.JPEG_ORIENTATION}; + return Arrays.asList(CAPTURE_RESULT_SET); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java new file mode 100644 index 00000000..037e9479 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; + +/** + * A interface to receive and process the upcoming next available Image. + * + * <p>Implemented by OEM. + */ +@SuppressLint("UnknownNullness") +public interface ImageProcessorImpl { + /** + * The reference count will not be decremented when this method returns. Extensions must + * decrement it when the image is no longer needed. + * + * <p>If OEM is not closing(decrement) the image fast enough, the imageReference passed + * in this method might contain null image meaning that the Image was closed to prevent + * preview from stalling. + * + * @param outputConfigId the id of {@link Camera2OutputConfigImpl} which identifies + * corresponding Surface + * @param timestampNs the timestamp in nanoseconds associated with this image + * @param imageReference A reference to the {@link android.media.Image} which might contain + * null if OEM close(decrement) the image too slowly + * @param physicalCameraId used to distinguish which physical camera id the image comes from + * when the output configuration is + * MultiResolutionImageReaderOutputConfigImpl. It is also set if + * physicalCameraId is set in other Camera2OutputConfigImpl types. + * + */ + void onNextImageAvailable( + int outputConfigId, + long timestampNs, + ImageReferenceImpl imageReference, + String physicalCameraId + ); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java new file mode 100644 index 00000000..ca4dcafa --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java @@ -0,0 +1,41 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.util.Size; + +/** + * Surface will be created by constructing a ImageReader. + */ +@SuppressLint("UnknownNullness") +public interface ImageReaderOutputConfigImpl extends Camera2OutputConfigImpl { + /** + * Returns the size of the surface. + */ + Size getSize(); + + /** + * Gets the image format of the surface. + */ + int getImageFormat(); + + /** + * Gets the capacity for TYPE_IMAGEREADER. + */ + int getMaxImages(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java new file mode 100644 index 00000000..95f2c3b9 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.media.Image; + +/** + * A Image reference container that enables the Image sharing between Camera2/CameraX and OEM + * using reference counting. The wrapped Image will be closed once the reference count + * reaches 0. + * + * <p>Implemented by Camera2/CameraX. + */ +@SuppressLint("UnknownNullness") +public interface ImageReferenceImpl { + + /** + * Increment the reference count. Returns true if the value was incremented. + * (returns false if the reference count has already reached zero.) + */ + boolean increment(); + + /** + * Decrement the reference count. Image will be closed if reference count reaches 0. + * Returns true if the value was decremented (returns false if the reference count has + * already reached zero) + */ + boolean decrement(); + + /** + * Return the Android image. This object MUST not be closed directly. + * Returns null when the reference count is zero. + */ + Image get(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/JpegEncoder.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/JpegEncoder.java new file mode 100644 index 00000000..f3eb7eab --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/JpegEncoder.java @@ -0,0 +1,136 @@ +/* + * 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.ImageFormat; +import android.media.Image; +import android.media.Image.Plane; +import android.media.ImageWriter; +import android.util.Log; + +import java.nio.ByteBuffer; + +// Jpeg compress input YUV and queue back in the client target surface. +public class JpegEncoder { + + public final static int JPEG_DEFAULT_QUALITY = 100; + public final static int JPEG_DEFAULT_ROTATION = 0; + public static final int HAL_PIXEL_FORMAT_BLOB = 0x21; + + /** + * Compresses a YCbCr image to jpeg, applying a crop and rotation. + * <p> + * The input is defined as a set of 3 planes of 8-bit samples, one plane for + * each channel of Y, Cb, Cr.<br> + * The Y plane is assumed to have the same width and height of the entire + * image.<br> + * The Cb and Cr planes are assumed to be downsampled by a factor of 2, to + * have dimensions (floor(width / 2), floor(height / 2)).<br> + * Each plane is specified by a direct java.nio.ByteBuffer, a pixel-stride, + * and a row-stride. So, the sample at coordinate (x, y) can be retrieved + * from byteBuffer[x * pixel_stride + y * row_stride]. + * <p> + * The pre-compression transformation is applied as follows: + * <ol> + * <li>The image is cropped to the rectangle from (cropLeft, cropTop) to + * (cropRight - 1, cropBottom - 1). So, a cropping-rectangle of (0, 0) - + * (width, height) is a no-op.</li> + * <li>The rotation is applied counter-clockwise relative to the coordinate + * space of the image, so a CCW rotation will appear CW when the image is + * rendered in scanline order. Only rotations which are multiples of + * 90-degrees are suppored, so the parameter 'rot90' specifies which + * multiple of 90 to rotate the image.</li> + * </ol> + * + * @param width the width of the image to compress + * @param height the height of the image to compress + * @param yBuf the buffer containing the Y component of the image + * @param yPStride the stride between adjacent pixels in the same row in + * yBuf + * @param yRStride the stride between adjacent rows in yBuf + * @param cbBuf the buffer containing the Cb component of the image + * @param cbPStride the stride between adjacent pixels in the same row in + * cbBuf + * @param cbRStride the stride between adjacent rows in cbBuf + * @param crBuf the buffer containing the Cr component of the image + * @param crPStride the stride between adjacent pixels in the same row in + * crBuf + * @param crRStride the stride between adjacent rows in crBuf + * @param outBuf a direct java.nio.ByteBuffer to hold the compressed jpeg. + * This must have enough capacity to store the result, or an + * error code will be returned. + * @param outBufCapacity the capacity of outBuf + * @param quality the jpeg-quality (1-100) to use + * @param cropLeft left-edge of the bounds of the image to crop to before + * rotation + * @param cropTop top-edge of the bounds of the image to crop to before + * rotation + * @param cropRight right-edge of the bounds of the image to crop to before + * rotation + * @param cropBottom bottom-edge of the bounds of the image to crop to + * before rotation + * @param rot90 the multiple of 90 to rotate the image CCW (after cropping) + */ + public static native int compressJpegFromYUV420pNative( + int width, int height, + ByteBuffer yBuf, int yPStride, int yRStride, + ByteBuffer cbBuf, int cbPStride, int cbRStride, + ByteBuffer crBuf, int crPStride, int crRStride, + ByteBuffer outBuf, int outBufCapacity, + int quality, + int cropLeft, int cropTop, int cropRight, int cropBottom, + int rot90); + + public static void encodeToJpeg(Image yuvImage, Image jpegImage, + int jpegOrientation, int jpegQuality) { + + jpegOrientation = (360 - (jpegOrientation % 360)) / 90; + ByteBuffer jpegBuffer = jpegImage.getPlanes()[0].getBuffer(); + + jpegBuffer.clear(); + + int jpegCapacity = jpegImage.getWidth(); + + Plane lumaPlane = yuvImage.getPlanes()[0]; + + Plane crPlane = yuvImage.getPlanes()[1]; + Plane cbPlane = yuvImage.getPlanes()[2]; + + JpegEncoder.compressJpegFromYUV420pNative( + yuvImage.getWidth(), yuvImage.getHeight(), + lumaPlane.getBuffer(), lumaPlane.getPixelStride(), lumaPlane.getRowStride(), + crPlane.getBuffer(), crPlane.getPixelStride(), crPlane.getRowStride(), + cbPlane.getBuffer(), cbPlane.getPixelStride(), cbPlane.getRowStride(), + jpegBuffer, jpegCapacity, jpegQuality, + 0, 0, yuvImage.getWidth(), yuvImage.getHeight(), + jpegOrientation); + } + + public static int imageFormatToPublic(int format) { + switch (format) { + case HAL_PIXEL_FORMAT_BLOB: + return ImageFormat.JPEG; + case ImageFormat.JPEG: + throw new IllegalArgumentException( + "ImageFormat.JPEG is an unknown internal format"); + default: + return format; + } + } +}
\ No newline at end of file diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/MultiResolutionImageReaderOutputConfigImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/MultiResolutionImageReaderOutputConfigImpl.java new file mode 100644 index 00000000..c3ad61bc --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/MultiResolutionImageReaderOutputConfigImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +/** + * Surface will be created by constructing a MultiResolutionImageReader. + */ +public interface MultiResolutionImageReaderOutputConfigImpl extends Camera2OutputConfigImpl { + /** + * Gets the image format of the surface. + */ + int getImageFormat(); + + /** + * Gets the max images of the ImageReader. + */ + int getMaxImages(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java new file mode 100644 index 00000000..82940635 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java @@ -0,0 +1,281 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import androidx.camera.extensions.impl.advanced.BaseAdvancedExtenderImpl.BaseAdvancedSessionProcessor; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.media.Image; +import android.media.Image.Plane; +import android.media.ImageWriter; +import android.os.Build; +import android.util.Log; +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.GuardedBy; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; +import java.util.Map; + +@SuppressLint("UnknownNullness") +public class NightAdvancedExtenderImpl extends BaseAdvancedExtenderImpl { + + protected static final int AWB_MODE_INCANDESCENT = CaptureRequest.CONTROL_AWB_MODE_INCANDESCENT; + + public NightAdvancedExtenderImpl() { + } + + @Override + public boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + // Requires API 23 for ImageWriter + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return false; + } + + CameraCharacteristics cameraCharacteristics = characteristicsMap.get(cameraId); + + if (cameraCharacteristics == null) { + return false; + } + + return CameraCharacteristicAvailability.isWBModeAvailable(cameraCharacteristics, + AWB_MODE_INCANDESCENT); + } + + public class NightAdvancedSessionProcessor extends BaseAdvancedSessionProcessor { + + public NightAdvancedSessionProcessor() { + appendTag("::Night"); + } + + protected final Object mLockPreviewSurfaceImageWriter = new Object(); + @GuardedBy("mLockPreviewSurfaceImageWriter") + private ImageWriter mPreviewSurfaceImageWriter; + + CaptureResultImageMatcher mCaptureResultImageMatcher = + new CaptureResultImageMatcher(); + + @Override + @NonNull + public Camera2SessionConfigImpl initSession(@NonNull String cameraId, + @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap, + @NonNull Context context, + @NonNull OutputSurfaceConfigurationImpl surfaceConfigs) { + + Log.d(TAG, "initSession cameraId=" + cameraId); + + mPreviewOutputSurfaceConfig = surfaceConfigs.getPreviewOutputSurface(); + mCaptureOutputSurfaceConfig = surfaceConfigs.getImageCaptureOutputSurface(); + + Camera2SessionConfigImplBuilder builder = + new Camera2SessionConfigImplBuilder() + .setSessionTemplateId(CameraDevice.TEMPLATE_PREVIEW); + + // Preview + if (mPreviewOutputSurfaceConfig.getSurface() != null) { + Camera2OutputConfigImplBuilder previewOutputConfigBuilder; + + previewOutputConfigBuilder = + Camera2OutputConfigImplBuilder.newImageReaderConfig( + mPreviewOutputSurfaceConfig.getSize(), + ImageFormat.YUV_420_888, + BASIC_CAPTURE_PROCESS_MAX_IMAGES); + + mPreviewOutputConfig = previewOutputConfigBuilder.build(); + + builder.addOutputConfig(mPreviewOutputConfig); + } + + // Image Capture + if (mCaptureOutputSurfaceConfig.getSurface() != null) { + Camera2OutputConfigImplBuilder captureOutputConfigBuilder; + + captureOutputConfigBuilder = + Camera2OutputConfigImplBuilder.newImageReaderConfig( + mCaptureOutputSurfaceConfig.getSize(), + ImageFormat.YUV_420_888, + BASIC_CAPTURE_PROCESS_MAX_IMAGES); + + mCaptureOutputConfig = captureOutputConfigBuilder.build(); + + builder.addOutputConfig(mCaptureOutputConfig); + } + + addSessionParameter(builder); + + return builder.build(); + } + + @Override + protected void addSessionParameter(Camera2SessionConfigImplBuilder builder) { + builder.addSessionParameter(CaptureRequest.CONTROL_AWB_MODE, AWB_MODE_INCANDESCENT); + } + + @Override + public void deInitSession() { + super.deInitSession(); + + synchronized (mLockPreviewSurfaceImageWriter) { + if (mPreviewSurfaceImageWriter != null) { + mPreviewSurfaceImageWriter.close(); + mPreviewSurfaceImageWriter = null; + } + } + } + + @Override + public void onCaptureSessionStart(@NonNull RequestProcessorImpl requestProcessor) { + super.onCaptureSessionStart(requestProcessor); + + if (mPreviewOutputSurfaceConfig.getSurface() != null) { + synchronized (mLockPreviewSurfaceImageWriter) { + mPreviewSurfaceImageWriter = new ImageWriter + .Builder(mPreviewOutputSurfaceConfig.getSurface()) + .setMaxImages(MAX_NUM_IMAGES) + .build(); + } + } + + if (mPreviewOutputSurfaceConfig.getSurface() != null) { + requestProcessor.setImageProcessor(mPreviewOutputConfig.getId(), + new ImageProcessorImpl() { + @Override + public void onNextImageAvailable(int outputStreamId, long timestampNs, + @NonNull ImageReferenceImpl imageReferenceImpl, + @Nullable String physicalCameraId) { + mCaptureResultImageMatcher.setInputImage(imageReferenceImpl); + } + }); + + mCaptureResultImageMatcher.setImageReferenceListener( + new CaptureResultImageMatcher.ImageReferenceListener() { + @Override + public void onImageReferenceIncoming( + @NonNull ImageReferenceImpl imageReferenceImpl, + @NonNull TotalCaptureResult totalCaptureResult, + int captureId) { + processCapture(imageReferenceImpl); + } + }); + } + } + + private void processCapture(@NonNull ImageReferenceImpl imageReferenceImpl) { + synchronized (mLockPreviewSurfaceImageWriter) { + mPreviewSurfaceImageWriter.queueInputImage(imageReferenceImpl.get()); + } + + imageReferenceImpl.decrement(); + } + + @Override + public void onCaptureSessionEnd() { + super.onCaptureSessionEnd(); + + synchronized (this) { + mCaptureResultImageMatcher.clear(); + } + } + + @Override + public int startRepeating(@NonNull CaptureCallback captureCallback) { + RequestBuilder builder = new RequestBuilder(mPreviewOutputConfig.getId(), + CameraDevice.TEMPLATE_PREVIEW, 0); + applyParameters(builder); + final int seqId = mNextCaptureSequenceId.getAndIncrement(); + + RequestProcessorImpl.Callback callback = new RequestProcessorImpl.Callback() { + @Override + public void onCaptureStarted(RequestProcessorImpl.Request request, long frameNumber, + long timestamp) { + captureCallback.onCaptureStarted(seqId, timestamp); + } + + @Override + public void onCaptureProgressed(RequestProcessorImpl.Request request, + CaptureResult partialResult) { + + } + + @Override + public void onCaptureCompleted(RequestProcessorImpl.Request request, + TotalCaptureResult totalCaptureResult) { + + addCaptureResultKeys(seqId, totalCaptureResult, captureCallback); + + mCaptureResultImageMatcher.setCameraCaptureCallback( + totalCaptureResult); + + captureCallback.onCaptureProcessStarted(seqId); + } + + @Override + public void onCaptureFailed(RequestProcessorImpl.Request request, + CaptureFailure captureFailure) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureBufferLost(RequestProcessorImpl.Request request, + long frameNumber, int outputStreamId) { + captureCallback.onCaptureFailed(seqId); + } + + @Override + public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) { + captureCallback.onCaptureSequenceCompleted(seqId); + } + + @Override + public void onCaptureSequenceAborted(int sequenceId) { + captureCallback.onCaptureSequenceAborted(seqId); + } + }; + + mRequestProcessor.setRepeating(builder.build(), callback); + + return seqId; + } + + @Override + protected void addCaptureRequestParameters(List<RequestProcessorImpl.Request> requestList) { + RequestBuilder build = new RequestBuilder(mCaptureOutputConfig.getId(), + CameraDevice.TEMPLATE_STILL_CAPTURE, DEFAULT_CAPTURE_ID); + build.setParameters(CaptureRequest.CONTROL_AWB_MODE, AWB_MODE_INCANDESCENT); + applyParameters(build); + + requestList.add(build.build()); + } + } + + @Override + public SessionProcessorImpl createSessionProcessor() { + return new NightAdvancedSessionProcessor(); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java new file mode 100644 index 00000000..ca3832e3 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java @@ -0,0 +1,35 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; + +/** + * For specifying the output surface configurations for the extension. + * + * since 1.4 + */ +@SuppressLint("UnknownNullness") +public interface OutputSurfaceConfigurationImpl { + public OutputSurfaceImpl getPreviewOutputSurface(); + + public OutputSurfaceImpl getImageCaptureOutputSurface(); + + public OutputSurfaceImpl getImageAnalysisOutputSurface(); + + public OutputSurfaceImpl getPostviewOutputSurface(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java new file mode 100644 index 00000000..f6920296 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.util.Size; +import android.view.Surface; + +/** + * For specifying output surface of the extension. + */ +@SuppressLint("UnknownNullness") +public interface OutputSurfaceImpl { + /** + * Gets the surface. + */ + Surface getSurface(); + + /** + * Gets the size. + */ + Size getSize(); + + /** + * Gets the image format. + */ + int getImageFormat(); +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/RequestBuilder.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/RequestBuilder.java new file mode 100644 index 00000000..ccb4ec84 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/RequestBuilder.java @@ -0,0 +1,110 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RequestBuilder { + List<Integer> mTargetOutputConfigIds = new ArrayList<>(); + Map<CaptureRequest.Key<?>, Object> mParameters = new HashMap<>(); + int mTemplateId = CameraDevice.TEMPLATE_PREVIEW; + int mCaptureStageId; + + public RequestBuilder() { + } + + public RequestBuilder(int targetOutputConfigId, int templateId, int captureStageId) { + addTargetOutputConfigIds(targetOutputConfigId); + setTemplateId(templateId); + setCaptureStageId(captureStageId); + } + + @NonNull + public RequestBuilder addTargetOutputConfigIds(int targetOutputConfigId) { + mTargetOutputConfigIds.add(targetOutputConfigId); + return this; + } + + @NonNull + public RequestBuilder setParameters(@NonNull CaptureRequest.Key<?> key, + @NonNull Object value) { + mParameters.put(key, value); + return this; + } + + @NonNull + public RequestBuilder setTemplateId(int templateId) { + mTemplateId = templateId; + return this; + } + + @NonNull + public RequestBuilder setCaptureStageId(int captureStageId) { + mCaptureStageId = captureStageId; + return this; + } + + @NonNull + public RequestProcessorImpl.Request build() { + return new RequestProcessorRequest( + mTargetOutputConfigIds, mParameters, mTemplateId, mCaptureStageId); + } + + static class RequestProcessorRequest implements RequestProcessorImpl.Request { + final List<Integer> mTargetOutputConfigIds; + final Map<CaptureRequest.Key<?>, Object> mParameters; + final int mTemplateId; + final int mCaptureStageId; + + RequestProcessorRequest(List<Integer> targetOutputConfigIds, + Map<CaptureRequest.Key<?>, Object> parameters, + int templateId, + int captureStageId) { + mTargetOutputConfigIds = targetOutputConfigIds; + mParameters = parameters; + mTemplateId = templateId; + mCaptureStageId = captureStageId; + } + + @Override + public List<Integer> getTargetOutputConfigIds() { + return mTargetOutputConfigIds; + } + + @Override + public Map<CaptureRequest.Key<?>, Object> getParameters() { + return mParameters; + } + + @Override + public Integer getTemplateId() { + return mTemplateId; + } + + public int getCaptureStageId() { + return mCaptureStageId; + } + } +}
\ No newline at end of file diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java new file mode 100644 index 00000000..51853334 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java @@ -0,0 +1,123 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; + +import java.util.List; +import java.util.Map; + +/** + * An Interface to execute Camera2 capture requests. + */ +@SuppressLint("UnknownNullness") +public interface RequestProcessorImpl { + /** + * Sets a {@link ImageProcessorImpl} to receive {@link ImageReferenceImpl} to process. + */ + void setImageProcessor(int outputconfigId, ImageProcessorImpl imageProcessor); + + /** + * Submits a request. + * @return the id of the capture sequence or -1 in case the processor encounters a fatal error + * or receives an invalid argument. + */ + int submit(Request request, Callback callback); + + /** + * Submits a list of requests. + * @return the id of the capture sequence or -1 in case the processor encounters a fatal error + * or receives an invalid argument. + */ + int submit(List<Request> requests, Callback callback); + + /** + * Set repeating requests. + * @return the id of the capture sequence or -1 in case the processor encounters a fatal error + * or receives an invalid argument. + */ + int setRepeating(Request request, Callback callback); + + + /** + * Abort captures. + */ + void abortCaptures(); + + /** + * Stop Repeating. + */ + void stopRepeating(); + + /** + * A interface representing a capture request configuration used for submitting requests in + * {@link RequestProcessorImpl}. + */ + interface Request { + /** + * Gets the target ids of {@link Camera2OutputConfigImpl} which identifies corresponding + * Surface to be the targeted for the request. + */ + List<Integer> getTargetOutputConfigIds(); + + /** + * Gets all the parameters. + */ + Map<CaptureRequest.Key<?>, Object> getParameters(); + + /** + * Gets the template id. + */ + Integer getTemplateId(); + } + + /** + * Callback to be invoked during the capture. + */ + interface Callback { + void onCaptureStarted( + Request request, + long frameNumber, + long timestamp); + + void onCaptureProgressed( + Request request, + CaptureResult partialResult); + + void onCaptureCompleted( + Request request, + TotalCaptureResult totalCaptureResult); + + void onCaptureFailed( + Request request, + CaptureFailure captureFailure); + + void onCaptureBufferLost( + Request request, + long frameNumber, + int outputStreamId); + + void onCaptureSequenceCompleted(int sequenceId, long frameNumber); + + void onCaptureSequenceAborted(int sequenceId); + + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java new file mode 100644 index 00000000..8dbfadc2 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java @@ -0,0 +1,390 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.view.Surface; + +import java.util.Map; + +/** + * Interface for creating Camera2 CameraCaptureSessions with extension enabled based on + * advanced vendor implementation. + * + * <p><pre> + * The flow of a extension session is shown below: + * (1) {@link #initSession}: CameraX prepares streams configuration for creating + * CameraCaptureSession. Output surfaces for Preview, ImageCapture and ImageAnalysis are passed + * in and vendor is responsible for outputting the results to these surfaces. + * + * (2) {@link #onCaptureSessionStart}: It is called after CameraCaptureSession is configured. + * A {@link RequestProcessorImpl} is passed for vendor to send repeating requests and + * single requests. + * + * (3) {@link #startRepeating}: CameraX will call this method to start the repeating request + * after CameraCaptureSession is called. Vendor should start the repeating request by + * {@link RequestProcessorImpl}. Vendor can also update the repeating request if needed later. + * + * (4) {@link #setParameters(Map)}: The passed parameters will be attached to the repeating request + * and single requests but vendor can choose to apply some of them only. + * + * (5) {@link #startCapture(CaptureCallback)}: It is called when apps want to + * start a multi-frame image capture. {@link CaptureCallback} will be called + * to report the status and the output image will be written to the capture output surface + * specified in {@link #initSession}. + * + * (5) {@link #onCaptureSessionEnd}: It is called right BEFORE CameraCaptureSession.close() is + * called. + * + * (6) {@link #deInitSession}: called when CameraCaptureSession is closed. + * </pre> + */ +@SuppressLint("UnknownNullness") +public interface SessionProcessorImpl { + /** + * Initializes the session for the extension. This is where the OEMs allocate resources for + * preparing a CameraCaptureSession. After initSession() is called, the camera ID, + * cameraCharacteristics and context will not change until deInitSession() has been called. + * + * <p>CameraX / Camera2 specifies the output surface configurations for preview using + * {@link OutputSurfaceConfigurationImpl#getPreviewOutputSurface}, image capture using + * {@link OutputSurfaceConfigurationImpl#getImageCaptureOutputSurface}, and image analysis + * [optional] using {@link OutputSurfaceConfigurationImpl#getImageAnalysisOutputSurface}. + * And OEM returns a {@link Camera2SessionConfigImpl} which consists of a list of + * {@link Camera2OutputConfigImpl} and session parameters. The {@link Camera2SessionConfigImpl} + * will be used to configure the CameraCaptureSession. + * + * <p>OEM is responsible for outputting correct camera images output to these output surfaces. + * OEM can have the following options to enable the output: + * <pre> + * (1) Add these output surfaces in CameraCaptureSession directly using + * {@link Camera2OutputConfigImplBuilder#newSurfaceConfig(Surface)} }. Processing is done in + * HAL. + * + * (2) Use surface sharing with other surface by calling + * {@link Camera2OutputConfigImplBuilder#addSurfaceSharingOutputConfig(Camera2OutputConfigImpl)} + * to add the output surface to the other {@link Camera2OutputConfigImpl}. + * + * (3) Process output from other surfaces (RAW, YUV..) and write the result to the output + * surface. The output surface won't be contained in the returned + * {@link Camera2SessionConfigImpl}. + * </pre> + * + * <p>{@link Camera2OutputConfigImplBuilder} and {@link Camera2SessionConfigImplBuilder} + * implementations are provided in the stub for OEM to construct the + * {@link Camera2OutputConfigImpl} and {@link Camera2SessionConfigImpl} instances. + * + * @param surfaceConfigs contains output surfaces for preview, image capture, and an + * optional output config for image analysis (YUV_420_888). + * @return a {@link Camera2SessionConfigImpl} consisting of a list of + * {@link Camera2OutputConfigImpl} and session parameters which will decide the + * {@link android.hardware.camera2.params.SessionConfiguration} for configuring the + * CameraCaptureSession. Please note that the OutputConfiguration list may not be part of any + * supported or mandatory stream combination BUT OEM must ensure this list will always + * produce a valid camera capture session. + * + * @since 1.4 + */ + Camera2SessionConfigImpl initSession( + String cameraId, + Map<String, CameraCharacteristics> cameraCharacteristicsMap, + Context context, + OutputSurfaceConfigurationImpl surfaceConfigs); + + /** + * Initializes the session for the extension. This is where the OEMs allocate resources for + * preparing a CameraCaptureSession. After initSession() is called, the camera ID, + * cameraCharacteristics and context will not change until deInitSession() has been called. + * + * <p>CameraX / Camera2 specifies the output surface configurations for preview, image capture + * and image analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which + * consists of a list of {@link Camera2OutputConfigImpl} and session parameters. The + * {@link Camera2SessionConfigImpl} will be used to configure the CameraCaptureSession. + * + * <p>OEM is responsible for outputting correct camera images output to these output surfaces. + * OEM can have the following options to enable the output: + * <pre> + * (1) Add these output surfaces in CameraCaptureSession directly using + * {@link Camera2OutputConfigImplBuilder#newSurfaceConfig(Surface)} }. Processing is done in + * HAL. + * + * (2) Use surface sharing with other surface by calling + * {@link Camera2OutputConfigImplBuilder#addSurfaceSharingOutputConfig(Camera2OutputConfigImpl)} + * to add the output surface to the other {@link Camera2OutputConfigImpl}. + * + * (3) Process output from other surfaces (RAW, YUV..) and write the result to the output + * surface. The output surface won't be contained in the returned + * {@link Camera2SessionConfigImpl}. + * </pre> + * + * <p>{@link Camera2OutputConfigImplBuilder} and {@link Camera2SessionConfigImplBuilder} + * implementations are provided in the stub for OEM to construct the + * {@link Camera2OutputConfigImpl} and {@link Camera2SessionConfigImpl} instances. + * + * @param previewSurfaceConfig output surface for preview + * @param imageCaptureSurfaceConfig output surface for image capture. + * @param imageAnalysisSurfaceConfig an optional output config for image analysis + * (YUV_420_888). + * @return a {@link Camera2SessionConfigImpl} consisting of a list of + * {@link Camera2OutputConfigImpl} and session parameters which will decide the + * {@link android.hardware.camera2.params.SessionConfiguration} for configuring the + * CameraCaptureSession. Please note that the OutputConfiguration list may not be part of any + * supported or mandatory stream combination BUT OEM must ensure this list will always + * produce a valid camera capture session. + */ + Camera2SessionConfigImpl initSession( + String cameraId, + Map<String, CameraCharacteristics> cameraCharacteristicsMap, + Context context, + OutputSurfaceImpl previewSurfaceConfig, + OutputSurfaceImpl imageCaptureSurfaceConfig, + OutputSurfaceImpl imageAnalysisSurfaceConfig); + + /** + * Notify to de-initialize the extension. This callback will be invoked after + * CameraCaptureSession is closed. After onDeInit() was called, it is expected that the + * camera ID, cameraCharacteristics will no longer hold and tear down any resources allocated + * for this extension. Aborts all pending captures. + */ + void deInitSession(); + + /** + * CameraX / Camera2 would call these API’s to pass parameters from the app to the OEM. It’s + * expected that the OEM would (eventually) update the repeating request if the keys are + * supported. Setting a value to null explicitly un-sets the value. + */ + void setParameters(Map<CaptureRequest.Key<?>, Object> parameters); + + /** + * CameraX / Camera2 will call this interface in response to client requests involving + * the output preview surface. Typical examples include requests that include AF/AE triggers. + * Extensions can disregard any capture request keys that were not advertised in + * {@link AdvancedExtenderImpl#getAvailableCaptureRequestKeys}. + * + * @param triggers Capture request key value map. + * @param callback a callback to report the status. + * @return the id of the capture sequence. + * + * @throws IllegalArgumentException If there are no valid settings that can be applied + * + * @since 1.3 + */ + int startTrigger(Map<CaptureRequest.Key<?>, Object> triggers, CaptureCallback callback); + + /** + * This will be invoked once after the {@link android.hardware.camera2.CameraCaptureSession} + * has been created. {@link RequestProcessorImpl} is passed for OEM to submit single + * requests or set repeating requests. This ExtensionRequestProcessor will be valid to use + * until onCaptureSessionEnd is called. + */ + void onCaptureSessionStart(RequestProcessorImpl requestProcessor); + + /** + * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is + * closed. {@link RequestProcessorImpl} passed in onCaptureSessionStart will no longer + * accept any requests after onCaptureSessionEnd() returns. + */ + void onCaptureSessionEnd(); + + /** + * Starts the repeating request after CameraCaptureSession is called. Vendor should start the + * repeating request by {@link RequestProcessorImpl}. Vendor can also update the + * repeating request when needed later. + * + * @param callback a callback to report the status. + * @return the id of the capture sequence. + */ + int startRepeating(CaptureCallback callback); + + /** + * Stop the repeating request. To prevent OEM from not calling stopRepeating, CameraX will + * first stop the repeating request of current CameraCaptureSession and call this API to signal + * OEM that the repeating request was stopped and going forward calling + * {@link RequestProcessorImpl#setRepeating} will simply do nothing. + */ + void stopRepeating(); + + /** + * Start a multi-frame capture with a postview. {@link #startCapture(CaptureCallback)} + * will be used for captures without a postview request. + * + * Postview will be available before the capture. Upon postview completion, + * {@code OnImageAvailableListener#onImageAvailable} will be called on the ImageReader + * that creates the postview output surface. When the capture is completed, + * {@link CaptureCallback#onCaptureSequenceCompleted} is called and + * {@code OnImageAvailableListener#onImageAvailable} will also be called on the ImageReader + * that creates the image capture output surface. + * + * <p>Only one capture can perform at a time. Starting a capture when another capture is + * running will cause onCaptureFailed to be called immediately. + * + * @param callback a callback to report the status. + * @return the id of the capture sequence. + * @since 1.4 + */ + int startCaptureWithPostview(CaptureCallback callback); + + /** + * Start a multi-frame capture. + * + * When the capture is completed, {@link CaptureCallback#onCaptureSequenceCompleted} + * is called and {@code OnImageAvailableListener#onImageAvailable} + * will also be called on the ImageReader that creates the image capture output surface. + * + * <p>Only one capture can perform at a time. Starting a capture when another capture is running + * will cause onCaptureFailed to be called immediately. + * + * @param callback a callback to report the status. + * @return the id of the capture sequence. + */ + int startCapture(CaptureCallback callback); + + /** + * Abort all capture tasks. + */ + void abortCapture(int captureSequenceId); + + /** + * Returns the dynamically calculated capture latency pair in milliseconds. + * + * <p>In contrast to {@link AdvancedExtenderImpl#getEstimatedCaptureLatencyRange} this method is + * guaranteed to be called after {@link #onCaptureSessionStart}. + * The measurement is expected to take in to account dynamic parameters such as the current + * scene, the state of 3A algorithms, the state of internal HW modules and return a more + * accurate assessment of the still capture latency.</p> + * + * @return pair that includes the estimated input frame/frames camera capture latency as the + * first field. This is the time between {@link #onCaptureStarted} and + * {@link #onCaptureProcessStarted}. The second field value includes the estimated + * post-processing latency. This is the time between {@link #onCaptureProcessStarted} until + * the processed frame returns back to the client registered surface. + * Both first and second values will be in milliseconds. The total still capture latency will be + * the sum of both the first and second values of the pair. + * The pair is expected to be null if the dynamic latency estimation is not supported. + * If clients have not configured a still capture output, then this method can also return a + * null pair. + * @since 1.4 + */ + Pair<Long, Long> getRealtimeCaptureLatency(); + + /** + * Callback for notifying the status of {@link #startCapture(CaptureCallback)} and + * {@link #startRepeating(CaptureCallback)}. + */ + interface CaptureCallback { + /** + * This method is called when the camera device has started capturing the initial input + * image. + * + * For a multi-frame capture, the method is called when the + * CameraCaptureSession.CaptureCallback onCaptureStarted of first frame is called and its + * timestamp is directly forwarded to timestamp parameter of + * this method. + * + * @param captureSequenceId id of the current capture sequence + * @param timestamp the timestamp at start of capture for repeating + * request or the timestamp at start of capture of the + * first frame in a multi-frame capture, in nanoseconds. + */ + void onCaptureStarted(int captureSequenceId, long timestamp); + + /** + * This method is called when an image (or images in case of multi-frame + * capture) is captured and device-specific extension processing is triggered. + * + * @param captureSequenceId id of the current capture sequence + */ + void onCaptureProcessStarted(int captureSequenceId); + + /** + * This method is called instead of + * {@link #onCaptureProcessStarted} when the camera device failed + * to produce the required input for the device-specific extension. The + * cause could be a failed camera capture request, a failed + * capture result or dropped camera frame. + * + * @param captureSequenceId id of the current capture sequence + */ + void onCaptureFailed(int captureSequenceId); + + /** + * This method is called independently of the others in the CaptureCallback, when a capture + * sequence finishes. + * + * <p>In total, there will be at least one + * {@link #onCaptureProcessStarted}/{@link #onCaptureFailed} + * invocation before this callback is triggered. If the capture + * sequence is aborted before any requests have begun processing, + * {@link #onCaptureSequenceAborted} is invoked instead.</p> + * + * @param captureSequenceId id of the current capture sequence + */ + void onCaptureSequenceCompleted(int captureSequenceId); + + /** + * This method is called when a capture sequence aborts. + * + * @param captureSequenceId id of the current capture sequence + */ + void onCaptureSequenceAborted(int captureSequenceId); + + /** + * Capture result callback that needs to be called when the process capture results are + * ready as part of frame post-processing. + * + * This callback will fire after {@link #onCaptureStarted}, {@link #onCaptureProcessStarted} + * and before {@link #onCaptureSequenceCompleted}. The callback is not expected to fire + * in case of capture failure {@link #onCaptureFailed} or capture abort + * {@link #onCaptureSequenceAborted}. + * + * @param timestamp The timestamp at start of capture. The same timestamp value + * passed to {@link #onCaptureStarted}. + * @param captureSequenceId the capture id of the request that generated the capture + * results. This is the return value of either + * {@link #startRepeating} or {@link #startCapture}. + * @param result Map containing the supported capture results. Do note + * that if results 'android.jpeg.quality' and + * 'android.jpeg.orientation' are present in the process + * capture input results, then the values must also be passed + * as part of this callback. Both Camera2 and CameraX guarantee + * that those two settings and results are always supported and + * applied by the corresponding framework. + */ + void onCaptureCompleted(long timestamp, int captureSequenceId, + Map<CaptureResult.Key, Object> result); + + /** + * Capture progress callback that needs to be called when the process capture is + * ongoing and includes the estimated progress of the processing. + * + * <p>Extensions must ensure that they always call this callback with monotonically + * increasing values.</p> + * + * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the + * callback is expected to be called once when processing is done with value 100.</p> + * + * @param progress Value between 0 and 100. + * @since 1.4 + */ + void onCaptureProcessProgressed(int progress); + } +} diff --git a/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java new file mode 100644 index 00000000..7b8d83c1 --- /dev/null +++ b/camera2/extensions/advancedSample/src/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java @@ -0,0 +1,31 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.view.Surface; + +/** + * Use Surface directly to create the OutputConfiguration. + */ +@SuppressLint("UnknownNullness") +public interface SurfaceOutputConfigImpl extends Camera2OutputConfigImpl { + /** + * Get the {@link Surface}. It'll return valid surface only when type is TYPE_SURFACE. + */ + Surface getSurface(); +} diff --git a/camera2/extensions/jni/Android.bp b/camera2/extensions/jni/Android.bp new file mode 100644 index 00000000..b30253e8 --- /dev/null +++ b/camera2/extensions/jni/Android.bp @@ -0,0 +1,44 @@ +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_library_shared { + name: "libencoderjpeg_jni", + srcs: [ + "JpegEncoder.cpp" + ], + include_dirs: ["external/libjpeg-turbo"], + + header_libs: [ + "jni_headers", + ], + + shared_libs: [ + "libnativehelper", + "liblog", + "libjpeg", + ], + static_libs: [ + "android.hardware.camera.device@3.2", + ], + + cflags: [ + "-Wno-unused-parameter", + ], + + system_ext_specific: true, +} diff --git a/camera2/extensions/jni/JpegEncoder.cpp b/camera2/extensions/jni/JpegEncoder.cpp new file mode 100644 index 00000000..dac6d00f --- /dev/null +++ b/camera2/extensions/jni/JpegEncoder.cpp @@ -0,0 +1,647 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <array> +#include <cstring> +#include <cstdio> +#include <inttypes.h> +#include <memory.h> +#include <vector> +#include <iostream> +#include <utils/Log.h> +#include <setjmp.h> + +#include <android/hardware/camera/device/3.2/types.h> + +#include "jni.h" +#include <nativehelper/JNIHelp.h> + +extern "C" { +#include "jpeglib.h" +} + +using namespace std; +using namespace android; + +using android::hardware::camera::device::V3_2::CameraBlob; +using android::hardware::camera::device::V3_2::CameraBlobId; + +class Transform; +struct Plane; + +inline int sgn(int val) { return (0 < val) - (val < 0); } + +inline int min(int a, int b) { return a < b ? a : b; } + +inline int max(int a, int b) { return a > b ? a : b; } + +/** + * Represents a combined cropping and rotation transformation. + * + * The transformation maps the coordinates (mOrigX, mOrigY) and (mOneX, mOneY) + * in the input image to the origin and (mOutputWidth, mOutputHeight) + * respectively. + */ +class Transform { + public: + Transform(int origX, int origY, int oneX, int oneY); + + static Transform forCropFollowedByRotation(int cropLeft, int cropTop, + int cropRight, int cropBottom, int rot90); + + inline int getOutputWidth() const { return mOutputWidth; } + + inline int getOutputHeight() const { return mOutputHeight; } + + bool operator==(const Transform& other) const; + + /** + * Transforms the input coordinates. Coordinates outside the cropped region + * are clamped to valid values. + */ + void map(int x, int y, int* outX, int* outY) const; + + private: + int mOutputWidth; + int mOutputHeight; + + // The coordinates of the point to map the origin to. + const int mOrigX, mOrigY; + // The coordinates of the point to map the point (getOutputWidth(), + // getOutputHeight()) to. + const int mOneX, mOneY; + + // A matrix for the rotational component. + int mMat00, mMat01; + int mMat10, mMat11; +}; + +/** + * Represents a model for accessing pixel data for a single plane of an image. + * Note that the actual data is not owned by this class, and the underlying + * data does not need to be stored in separate planes. + */ +struct Plane { + // The dimensions of this plane of the image + int width; + int height; + + // A pointer to raw pixel data + const unsigned char* data; + // The difference in address between consecutive pixels in the same row + int pixelStride; + // The difference in address between the start of consecutive rows + int rowStride; +}; + +/** + * Provides an interface for simultaneously reading a certain number of rows of + * an image plane as contiguous arrays, suitable for use with libjpeg. + */ +template <unsigned int ROWS> +class RowIterator { + public: + /** + * Creates a new RowIterator which will crop and rotate with the given + * transform. + * + * @param plane the plane to iterate over + * @param transform the transformation to map output values into the + * coordinate space of the plane + * @param rowLength the length of the rows returned via LoadAt(). If this is + * longer than the width of the output (after applying the transform), then + * the right-most value is repeated. + */ + inline RowIterator(Plane plane, Transform transform, int rowLength); + + /** + * Returns an array of pointers into consecutive rows of contiguous image + * data starting at y. That is, samples within each row are contiguous. + * However, the individual arrays pointed-to may be separate. + * When the end of the image is reached, the last row of the image is + * repeated. + * The returned pointers are valid until the next call to loadAt(). + */ + inline const std::array<unsigned char*, ROWS> loadAt(int baseY); + + private: + Plane mPlane; + Transform mTransform; + // The length of a row, with padding to the next multiple of 64. + int mPaddedRowLength; + std::vector<unsigned char> mBuffer; +}; + +template <unsigned int ROWS> +RowIterator<ROWS>::RowIterator(Plane plane, Transform transform, + int rowLength) + : mPlane(plane), mTransform(transform) { + mPaddedRowLength = rowLength; + mBuffer = std::vector<unsigned char>(rowLength * ROWS); +} + +template <unsigned int ROWS> +const std::array<unsigned char*, ROWS> RowIterator<ROWS>::loadAt(int baseY) { + std::array<unsigned char*, ROWS> bufPtrs; + for (unsigned int i = 0; i < ROWS; i++) { + bufPtrs[i] = &mBuffer[mPaddedRowLength * i]; + } + + if (mPlane.width == 0 || mPlane.height == 0) { + return bufPtrs; + } + + for (unsigned int i = 0; i < ROWS; i++) { + int y = i + baseY; + y = min(y, mTransform.getOutputHeight() - 1); + + int output_width = mPaddedRowLength; + output_width = min(output_width, mTransform.getOutputWidth()); + output_width = min(output_width, mPlane.width); + + // Each row in the output image will be copied into buf_ by gathering pixels + // along an axis-aligned line in the plane. + // The line is defined by (startX, startY) -> (endX, endY), computed via the + // current Transform. + int startX; + int startY; + mTransform.map(0, y, &startX, &startY); + + int endX; + int endY; + mTransform.map(output_width - 1, y, &endX, &endY); + + // Clamp (startX, startY) and (endX, endY) to the valid bounds of the plane. + startX = min(startX, mPlane.width - 1); + startY = min(startY, mPlane.height - 1); + endX = min(endX, mPlane.width - 1); + endY = min(endY, mPlane.height - 1); + startX = max(startX, 0); + startY = max(startY, 0); + endX = max(endX, 0); + endY = max(endY, 0); + + // To reduce work inside the copy-loop, precompute the start, end, and + // stride relating the values to be gathered from mPlane into buf + // for this particular scan-line. + int dx = sgn(endX - startX); + int dy = sgn(endY - startY); + if (!(dx == 0 || dy == 0)) { + ALOGE("%s: Unexpected bounds: %dx%d %dx%d!", __FUNCTION__, startX, endX, startY, endY); + return bufPtrs; + } + + // The index into mPlane.data of (startX, startY) + int plane_start = startX * mPlane.pixelStride + startY * mPlane.rowStride; + // The index into mPlane.data of (endX, endY) + int plane_end = endX * mPlane.pixelStride + endY * mPlane.rowStride; + // The stride, in terms of indices in plane_data, required to enumerate the + // samples between the start and end points. + int stride = dx * mPlane.pixelStride + dy * mPlane.rowStride; + // In the degenerate-case of a 1x1 plane, startX and endX are equal, so + // stride would be 0, resulting in an infinite-loop. To avoid this case, + // use a stride of at-least 1. + if (stride == 0) { + stride = 1; + } + + int outX = 0; + for (int idx = plane_start; idx >= min(plane_start, plane_end) && + idx <= max(plane_start, plane_end); idx += stride) { + bufPtrs[i][outX] = mPlane.data[idx]; + outX++; + } + + // Fill the remaining right-edge of the buffer by extending the last + // value. + unsigned char right_padding_value = bufPtrs[i][outX - 1]; + for (; outX < mPaddedRowLength; outX++) { + bufPtrs[i][outX] = right_padding_value; + } + } + + return bufPtrs; +} + +template <typename T> +void safeDelete(T& t) { + delete t; + t = nullptr; +} + +template <typename T> +void safeDeleteArray(T& t) { + delete[] t; + t = nullptr; +} + +Transform::Transform(int origX, int origY, int oneX, int oneY) + : mOrigX(origX), mOrigY(origY), mOneX(oneX), mOneY(oneY) { + if (origX == oneX || origY == oneY) { + // Handle the degenerate case of cropping to a 0x0 rectangle. + mMat00 = 0; + mMat01 = 0; + mMat10 = 0; + mMat11 = 0; + return; + } + + if (oneX > origX && oneY > origY) { + // 0-degree rotation + mMat00 = 1; + mMat01 = 0; + mMat10 = 0; + mMat11 = 1; + mOutputWidth = abs(oneX - origX); + mOutputHeight = abs(oneY - origY); + } else if (oneX < origX && oneY > origY) { + // 90-degree CCW rotation + mMat00 = 0; + mMat01 = -1; + mMat10 = 1; + mMat11 = 0; + mOutputWidth = abs(oneY - origY); + mOutputHeight = abs(oneX - origX); + } else if (oneX > origX && oneY < origY) { + // 270-degree CCW rotation + mMat00 = 0; + mMat01 = 1; + mMat10 = -1; + mMat11 = 0; + mOutputWidth = abs(oneY - origY); + mOutputHeight = abs(oneX - origX);; + } else if (oneX < origX && oneY < origY) { + // 180-degree CCW rotation + mMat00 = -1; + mMat01 = 0; + mMat10 = 0; + mMat11 = -1; + mOutputWidth = abs(oneX - origX); + mOutputHeight = abs(oneY - origY); + } +} + +Transform Transform::forCropFollowedByRotation(int cropLeft, int cropTop, int cropRight, + int cropBottom, int rot90) { + // The input crop-region excludes cropRight and cropBottom, so transform the + // crop rect such that it defines the entire valid region of pixels + // inclusively. + cropRight -= 1; + cropBottom -= 1; + + int cropXLow = min(cropLeft, cropRight); + int cropYLow = min(cropTop, cropBottom); + int cropXHigh = max(cropLeft, cropRight); + int cropYHigh = max(cropTop, cropBottom); + rot90 %= 4; + if (rot90 == 0) { + return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1); + } else if (rot90 == 1) { + return Transform(cropXHigh, cropYLow, cropXLow - 1, cropYHigh + 1); + } else if (rot90 == 2) { + return Transform(cropXHigh, cropYHigh, cropXLow - 1, cropYLow - 1); + } else if (rot90 == 3) { + return Transform(cropXLow, cropYHigh, cropXHigh + 1, cropYLow - 1); + } + // Impossible case. + return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1); +} + +bool Transform::operator==(const Transform& other) const { + return other.mOrigX == mOrigX && // + other.mOrigY == mOrigY && // + other.mOneX == mOneX && // + other.mOneY == mOneY; +} + +/** + * Transforms the input coordinates. Coordinates outside the cropped region + * are clamped to valid values. + */ +void Transform::map(int x, int y, int* outX, int* outY) const { + x = max(x, 0); + y = max(y, 0); + x = min(x, getOutputWidth() - 1); + y = min(y, getOutputHeight() - 1); + *outX = x * mMat00 + y * mMat01 + mOrigX; + *outY = x * mMat10 + y * mMat11 + mOrigY; +} + +int compress(int img_width, int img_height, RowIterator<16>& y_row_generator, + RowIterator<8>& cb_row_generator, RowIterator<8>& cr_row_generator, + unsigned char* out_buf, size_t out_buf_capacity, std::function<void(size_t)> flush, + int quality) { + // libjpeg requires the use of setjmp/longjmp to recover from errors. Since + // this doesn't play well with RAII, we must use pointers and manually call + // delete. See POSIX documentation for longjmp() for details on why the + // volatile keyword is necessary. + volatile jpeg_compress_struct cinfov; + + jpeg_compress_struct& cinfo = + *const_cast<struct jpeg_compress_struct*>(&cinfov); + + JSAMPROW* volatile yArr = nullptr; + JSAMPROW* volatile cbArr = nullptr; + JSAMPROW* volatile crArr = nullptr; + + JSAMPARRAY imgArr[3]; + + // Error handling + + struct my_error_mgr { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; + } err; + + cinfo.err = jpeg_std_error(&err.pub); + + // Default error_exit will call exit(), so override + // to return control via setjmp/longjmp. + err.pub.error_exit = [](j_common_ptr cinfo) { + my_error_mgr* myerr = reinterpret_cast<my_error_mgr*>(cinfo->err); + + (*cinfo->err->output_message)(cinfo); + + // Return control to the setjmp point (see call to setjmp()). + longjmp(myerr->setjmp_buffer, 1); + }; + + cinfo.err = (struct jpeg_error_mgr*)&err; + + // Set the setjmp point to return to in case of error. + if (setjmp(err.setjmp_buffer)) { + // If libjpeg hits an error, control will jump to this point (see call to + // longjmp()). + jpeg_destroy_compress(&cinfo); + + safeDeleteArray(yArr); + safeDeleteArray(cbArr); + safeDeleteArray(crArr); + + return -1; + } + + // Create jpeg compression context + jpeg_create_compress(&cinfo); + + // Stores data needed by our c-style callbacks into libjpeg + struct ClientData { + unsigned char* out_buf; + size_t out_buf_capacity; + std::function<void(size_t)> flush; + int totalOutputBytes; + } clientData{out_buf, out_buf_capacity, flush, 0}; + + cinfo.client_data = &clientData; + + // Initialize destination manager + jpeg_destination_mgr dest; + + dest.init_destination = [](j_compress_ptr cinfo) { + ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data); + + cinfo->dest->next_output_byte = cdata.out_buf; + cinfo->dest->free_in_buffer = cdata.out_buf_capacity; + }; + + dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean { + ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data); + + size_t numBytesInBuffer = cdata.out_buf_capacity; + cdata.flush(numBytesInBuffer); + cdata.totalOutputBytes += numBytesInBuffer; + + // Reset the buffer + cinfo->dest->next_output_byte = cdata.out_buf; + cinfo->dest->free_in_buffer = cdata.out_buf_capacity; + + return true; + }; + + dest.term_destination = [](j_compress_ptr cinfo __unused) { + // do nothing to terminate the output buffer + }; + + cinfo.dest = &dest; + + // Set jpeg parameters + cinfo.image_width = img_width; + cinfo.image_height = img_height; + cinfo.input_components = 3; + + // Set defaults based on the above values + jpeg_set_defaults(&cinfo); + + jpeg_set_quality(&cinfo, quality, true); + + cinfo.dct_method = JDCT_IFAST; + + cinfo.raw_data_in = true; + + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + jpeg_start_compress(&cinfo, true); + + yArr = new JSAMPROW[cinfo.comp_info[0].v_samp_factor * DCTSIZE]; + cbArr = new JSAMPROW[cinfo.comp_info[1].v_samp_factor * DCTSIZE]; + crArr = new JSAMPROW[cinfo.comp_info[2].v_samp_factor * DCTSIZE]; + + imgArr[0] = const_cast<JSAMPARRAY>(yArr); + imgArr[1] = const_cast<JSAMPARRAY>(cbArr); + imgArr[2] = const_cast<JSAMPARRAY>(crArr); + + for (int y = 0; y < img_height; y += DCTSIZE * 2) { + std::array<unsigned char*, 16> yData = y_row_generator.loadAt(y); + std::array<unsigned char*, 8> cbData = cb_row_generator.loadAt(y / 2); + std::array<unsigned char*, 8> crData = cr_row_generator.loadAt(y / 2); + + for (int row = 0; row < DCTSIZE * 2; row++) { + yArr[row] = yData[row]; + } + for (int row = 0; row < DCTSIZE; row++) { + cbArr[row] = cbData[row]; + crArr[row] = crData[row]; + } + + jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2); + } + + jpeg_finish_compress(&cinfo); + + int numBytesInBuffer = cinfo.dest->next_output_byte - out_buf; + + flush(numBytesInBuffer); + + clientData.totalOutputBytes += numBytesInBuffer; + + safeDeleteArray(yArr); + safeDeleteArray(cbArr); + safeDeleteArray(crArr); + + jpeg_destroy_compress(&cinfo); + + return clientData.totalOutputBytes; +} + +int compress( + /** Input image dimensions */ + int width, int height, + /** Y Plane */ + unsigned char* yBuf, int yPStride, int yRStride, + /** Cb Plane */ + unsigned char* cbBuf, int cbPStride, int cbRStride, + /** Cr Plane */ + unsigned char* crBuf, int crPStride, int crRStride, + /** Output */ + unsigned char* outBuf, size_t outBufCapacity, + /** Jpeg compression parameters */ + int quality, + /** Crop */ + int cropLeft, int cropTop, int cropRight, int cropBottom, + /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree + * rotation. */ + int rot90) { + int finalWidth; + int finalHeight; + finalWidth = cropRight - cropLeft; + finalHeight = cropBottom - cropTop; + + rot90 %= 4; + // for 90 and 270-degree rotations, flip the final width and height + if (rot90 == 1) { + finalWidth = cropBottom - cropTop; + finalHeight = cropRight - cropLeft; + } else if (rot90 == 3) { + finalWidth = cropBottom - cropTop; + finalHeight = cropRight - cropLeft; + } + + const Plane yP = {width, height, yBuf, yPStride, yRStride}; + const Plane cbP = {width / 2, height / 2, cbBuf, cbPStride, cbRStride}; + const Plane crP = {width / 2, height / 2, crBuf, crPStride, crRStride}; + + auto flush = [](size_t numBytes __unused) { + // do nothing + }; + + // Round up to the nearest multiple of 64. + int y_row_length = (finalWidth + 16 + 63) & ~63; + int cb_row_length = (finalWidth / 2 + 16 + 63) & ~63; + int cr_row_length = (finalWidth / 2 + 16 + 63) & ~63; + + Transform yTrans = Transform::forCropFollowedByRotation( + cropLeft, cropTop, cropRight, cropBottom, rot90); + + Transform chromaTrans = Transform::forCropFollowedByRotation( + cropLeft / 2, cropTop / 2, cropRight / 2, cropBottom / 2, rot90); + + RowIterator<16> yIter(yP, yTrans, y_row_length); + RowIterator<8> cbIter(cbP, chromaTrans, cb_row_length); + RowIterator<8> crIter(crP, chromaTrans, cr_row_length); + + return compress(finalWidth, finalHeight, yIter, cbIter, crIter, outBuf, outBufCapacity, flush, + quality); +} + +extern "C" { + +static jint JpegEncoder_compressJpegFromYUV420p( + JNIEnv* env, jclass clazz __unused, + /** Input image dimensions */ + jint width, jint height, + /** Y Plane */ + jobject yBuf, jint yPStride, jint yRStride, + /** Cb Plane */ + jobject cbBuf, jint cbPStride, jint cbRStride, + /** Cr Plane */ + jobject crBuf, jint crPStride, jint crRStride, + /** Output */ + jobject outBuf, jint outBufCapacity, + /** Jpeg compression parameters */ + jint quality, + /** Crop */ + jint cropLeft, jint cropTop, jint cropRight, jint cropBottom, + /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree + * rotation. */ + jint rot90) { + // ALOGE("Insert log inside JpegEncoder_compressJpegFromYUV420p"); + jbyte* y = (jbyte*)env->GetDirectBufferAddress(yBuf); + jbyte* cb = (jbyte*)env->GetDirectBufferAddress(cbBuf); + jbyte* cr = (jbyte*)env->GetDirectBufferAddress(crBuf); + jbyte* out = (jbyte*)env->GetDirectBufferAddress(outBuf); + + size_t actualJpegSize = compress(width, height, + (unsigned char*)y, yPStride, yRStride, + (unsigned char*)cb, cbPStride, cbRStride, + (unsigned char*)cr, crPStride, crRStride, + (unsigned char*)out, (size_t)outBufCapacity, + quality, cropLeft, cropTop, cropRight, cropBottom, rot90); + + size_t finalJpegSize = actualJpegSize + sizeof(CameraBlob); + if (finalJpegSize > outBufCapacity) { + // ALOGE("%s: Final jpeg buffer %zu not large enough for the jpeg blob header with "\ + // "capacity %d", __FUNCTION__, finalJpegSize, outBufCapacity); + return actualJpegSize; + } + + int8_t* header = static_cast<int8_t *> (out) + + (outBufCapacity - sizeof(CameraBlob)); + CameraBlob *blob = reinterpret_cast<CameraBlob *> (header); + blob->blobId = CameraBlobId::JPEG; + blob->blobSize = actualJpegSize; + + return actualJpegSize; +} + +} // extern "C" + +static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className, + const JNINativeMethod* gMethods, int numMethods) { + int res = jniRegisterNativeMethods(env, className, gMethods, numMethods); + return res; +} + +static const JNINativeMethod gJpegEncoderMethods[] = { + {"compressJpegFromYUV420pNative", + "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIIIIII)I", + (void*)JpegEncoder_compressJpegFromYUV420p}}; + +int register_android_hardware_camera2_impl_JpegEncoder(JNIEnv* env) { + return RegisterMethodsOrDie(env, "androidx/camera/extensions/impl/advanced/JpegEncoder", + gJpegEncoderMethods, NELEM(gJpegEncoderMethods)); +} + +jint JNI_OnLoad(JavaVM* vm, void* reserved) { + JNIEnv* env; + + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return -1; + } + + if (register_android_hardware_camera2_impl_JpegEncoder(env) < 0) { + ALOGE("ERROR: JpegEncoder native registration failed"); + return -1; + } + + return JNI_VERSION_1_6; +}
\ No newline at end of file diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java index 5f6cb2ba..30a79a6b 100755 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java @@ -20,6 +20,7 @@ import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.SessionConfiguration; import android.media.Image; import android.media.ImageWriter; import android.os.Build; @@ -116,6 +117,26 @@ public final class AutoImageCaptureExtenderImpl implements ImageCaptureExtenderI } @Override + public void onPostviewOutputSurface(Surface surface) { + + } + + @Override + public void processWithPostview( + Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor) { + if (!isPostviewAvailable()) { + throw new RuntimeException("The extension doesn't support postview"); + } + + if (resultCallback != null) { + process(results, resultCallback, executor); + } else { + process(results); + } + } + + @Override public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results, ProcessResultImpl resultCallback, Executor executor) { throw new RuntimeException("The extension doesn't support capture " + @@ -164,6 +185,11 @@ public final class AutoImageCaptureExtenderImpl implements ImageCaptureExtenderI } @Override + public void onResolutionUpdate(Size size, Size postviewSize) { + + } + + @Override public void onImageFormatUpdate(int imageFormat) { } @@ -244,6 +270,11 @@ public final class AutoImageCaptureExtenderImpl implements ImageCaptureExtenderI } @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + return new ArrayList<>(); + } + + @Override public Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize) { return null; } @@ -257,4 +288,24 @@ public final class AutoImageCaptureExtenderImpl implements ImageCaptureExtenderI public List<CaptureResult.Key> getAvailableCaptureResultKeys() { return new ArrayList<>(); } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return false; + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + return null; + } + + @Override + public boolean isPostviewAvailable() { + return false; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java index 5018df8a..aa95a4a7 100755 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java @@ -18,6 +18,7 @@ package androidx.camera.extensions.impl; import android.content.Context; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.SessionConfiguration; import android.util.Pair; import android.util.Size; @@ -162,4 +163,9 @@ public final class AutoPreviewExtenderImpl implements PreviewExtenderImpl { return captureStage; } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java index 5c9b2d39..c9b24206 100755 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java @@ -21,6 +21,7 @@ import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageWriter; @@ -118,6 +119,26 @@ public final class BeautyImageCaptureExtenderImpl implements ImageCaptureExtende } @Override + public void onPostviewOutputSurface(Surface surface) { + + } + + @Override + public void processWithPostview( + Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor) { + if (!isPostviewAvailable()) { + throw new RuntimeException("The extension doesn't support postview"); + } + + if (resultCallback != null) { + process(results, resultCallback, executor); + } else { + process(results); + } + } + + @Override public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results, ProcessResultImpl resultCallback, Executor executor) { throw new RuntimeException("The extension doesn't support capture " + @@ -166,6 +187,11 @@ public final class BeautyImageCaptureExtenderImpl implements ImageCaptureExtende } @Override + public void onResolutionUpdate(Size size, Size postviewSize) { + + } + + @Override public void onImageFormatUpdate(int imageFormat) { } @@ -267,6 +293,11 @@ public final class BeautyImageCaptureExtenderImpl implements ImageCaptureExtende } @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + return new ArrayList<>(); + } + + @Override public Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize) { return null; } @@ -280,4 +311,24 @@ public final class BeautyImageCaptureExtenderImpl implements ImageCaptureExtende public List<CaptureResult.Key> getAvailableCaptureResultKeys() { return new ArrayList<>(); } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return false; + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + return null; + } + + @Override + public boolean isPostviewAvailable() { + return false; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java index fcc78d55..2ef357c3 100755 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java @@ -19,6 +19,7 @@ import android.content.Context; import android.graphics.ImageFormat; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.util.Pair; import android.util.Size; @@ -183,4 +184,9 @@ public final class BeautyPreviewExtenderImpl implements PreviewExtenderImpl { return captureStage; } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java index 5c3882b8..a72deef6 100644 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java @@ -16,10 +16,13 @@ package androidx.camera.extensions.impl; import android.content.Context; +import android.graphics.ImageFormat; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageWriter; import android.os.Build; @@ -51,6 +54,8 @@ public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtender private static final int SESSION_STAGE_ID = 101; private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_SHADE; + private CameraCharacteristics mCameraCharacteristics; + /** * @hide */ @@ -62,6 +67,7 @@ public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtender */ @Override public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + mCameraCharacteristics = cameraCharacteristics; } /** @@ -104,6 +110,7 @@ public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtender CaptureProcessorImpl captureProcessor = new CaptureProcessorImpl() { private ImageWriter mImageWriter; + private ImageWriter mImageWriterPostview; @Override public void onOutputSurface(Surface surface, int imageFormat) { @@ -113,6 +120,61 @@ public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtender } @Override + public void onPostviewOutputSurface(Surface surface) { + if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) && + mImageWriterPostview == null) { + mImageWriterPostview = ImageWriter.newInstance(surface, 1); + } + } + + @Override + public void processWithPostview( + Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor) { + + Pair<Image, TotalCaptureResult> result = results.get(DEFAULT_STAGE_ID); + if (result == null) { + Log.w(TAG, + "Unable to process since images does not contain all " + + "stages."); + return; + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Image image = mImageWriterPostview.dequeueInputImage(); + + // Postview processing here + ByteBuffer yByteBuffer = image.getPlanes()[0].getBuffer(); + ByteBuffer uByteBuffer = image.getPlanes()[2].getBuffer(); + ByteBuffer vByteBuffer = image.getPlanes()[1].getBuffer(); + + // For sample, allocate empty buffer to match postview size + yByteBuffer.put(ByteBuffer.allocate(image.getPlanes()[0] + .getBuffer().capacity())); + uByteBuffer.put(ByteBuffer.allocate(image.getPlanes()[2] + .getBuffer().capacity())); + vByteBuffer.put(ByteBuffer.allocate(image.getPlanes()[1] + .getBuffer().capacity())); + Long sensorTimestamp = + result.second.get(CaptureResult.SENSOR_TIMESTAMP); + if (sensorTimestamp != null) { + image.setTimestamp(sensorTimestamp); + } else { + Log.e(TAG, "Sensor timestamp absent using default!"); + } + + mImageWriterPostview.queueInputImage(image); + } + } + + // Process still capture + if (resultCallback != null) { + process(results, resultCallback, executor); + } else { + process(results); + } + } + + @Override public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results, ProcessResultImpl resultCallback, Executor executor) { Pair<Image, TotalCaptureResult> result = results.get(DEFAULT_STAGE_ID); @@ -241,6 +303,11 @@ public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtender } @Override + public void onResolutionUpdate(Size size, Size postviewSize) { + + } + + @Override public void onImageFormatUpdate(int imageFormat) { } @@ -317,7 +384,59 @@ public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtender */ @Override public List<Pair<Integer, Size[]>> getSupportedResolutions() { - return null; + List<Pair<Integer, Size[]>> formatResolutionsPairList = new ArrayList<>(); + + StreamConfigurationMap map = + mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + + if (map != null) { + // The sample implementation only retrieves originally supported resolutions from + // CameraCharacteristics for JPEG and YUV_420_888 formats to return. + Size[] outputSizes = map.getOutputSizes(ImageFormat.JPEG); + + if (outputSizes != null) { + formatResolutionsPairList.add(Pair.create(ImageFormat.JPEG, outputSizes)); + } + + outputSizes = map.getOutputSizes(ImageFormat.YUV_420_888); + + if (outputSizes != null) { + formatResolutionsPairList.add(Pair.create(ImageFormat.YUV_420_888, outputSizes)); + } + } + + return formatResolutionsPairList; + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + // Sample for supported postview sizes, returns subset of supported resolutions for + // still capture that are less than its size and match the aspect ratio + List<Pair<Integer, Size[]>> res = new ArrayList<>(); + List<Pair<Integer, Size[]>> captureSupportedResolutions = getSupportedResolutions(); + float targetAr = ((float) captureSize.getWidth()) / captureSize.getHeight(); + + for (Pair<Integer, Size[]> elem : captureSupportedResolutions) { + Integer currFormat = elem.first; + Size[] currFormatSizes = elem.second; + List<Size> postviewSizes = new ArrayList<>(); + + for (Size s : currFormatSizes) { + if ((s.equals(captureSize)) || (s.getWidth() > captureSize.getWidth()) + || (s.getHeight() > captureSize.getHeight())) continue; + float currentAr = ((float) s.getWidth()) / s.getHeight(); + if (Math.abs(targetAr - currentAr) < 0.01) { + postviewSizes.add(s); + } + } + + if (!postviewSizes.isEmpty()) { + res.add(new Pair<Integer, Size[]>(currFormat, + postviewSizes.toArray(new Size[postviewSizes.size()]))); + } + } + + return res; } @Override @@ -341,4 +460,24 @@ public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtender CaptureResult.FLASH_STATE}; return Arrays.asList(CAPTURE_RESULT_SET); } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return false; + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + return null; + } + + @Override + public boolean isPostviewAvailable() { + return true; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java index 45c7f47c..ace54c8b 100644 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java @@ -19,6 +19,7 @@ import android.content.Context; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.SessionConfiguration; import android.util.Pair; import android.util.Size; import android.view.Surface; @@ -202,4 +203,9 @@ public final class BokehPreviewExtenderImpl implements PreviewExtenderImpl { return captureStage; } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java index efd0afac..f4719b8b 100644 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java @@ -16,46 +16,60 @@ package androidx.camera.extensions.impl; +import android.annotation.SuppressLint; +import android.graphics.ImageFormat; import android.hardware.camera2.TotalCaptureResult; import android.media.Image; import android.util.Pair; import android.util.Size; import android.view.Surface; -import java.util.concurrent.Executor; import java.util.Map; +import java.util.concurrent.Executor; /** * The interface for processing a set of {@link Image}s that have captured. * * @since 1.0 - * @hide */ -public interface CaptureProcessorImpl { - /** - * This gets called to update where the CaptureProcessor should write the output of {@link - * #process(Map)}. - * - * @param surface The {@link Surface} that the CaptureProcessor should write data into. - * @param imageFormat The format of that the surface expects. - * @hide - */ - void onOutputSurface(Surface surface, int imageFormat); - +@SuppressLint("UnknownNullness") +public interface CaptureProcessorImpl extends ProcessorImpl { /** * Process a set images captured that were requested. * * <p> The result of the processing step should be written to the {@link Surface} that was * received by {@link #onOutputSurface(Surface, int)}. * - * @param results The map of images and metadata to process. The {@link Image} that are - * contained within the map will become invalid after this method completes, - * so no references to them should be kept. - * @hide + * @param results The map of {@link ImageFormat#YUV_420_888} format images and metadata to + * process. The {@link Image} that are contained within the map will become + * invalid after this method completes, so no references to them should be kept. */ void process(Map<Integer, Pair<Image, TotalCaptureResult>> results); /** + * Informs the CaptureProcessorImpl where it should write the postview output to. + * This will only be invoked once if a valid postview surface was set. + * + * @param surface A valid {@link ImageFormat#YUV_420_888} {@link Surface} + * that the CaptureProcessorImpl should write data into. + * @since 1.4 + */ + void onPostviewOutputSurface(Surface surface); + + /** + * Invoked when the Camera Framework changes the configured output resolution for + * still capture and postview. + * + * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as + * input for still capture and postview to be at the specified resolutions. + * + * @param size for the surface for still capture. + * @param postviewSize for the surface for postview. + * @since 1.4 + */ + void onResolutionUpdate(Size size, Size postviewSize); + + /** * Process a set images captured that were requested. * * <p> The result of the processing step should be written to the {@link Surface} that was @@ -66,7 +80,7 @@ public interface CaptureProcessorImpl { * become invalid after this method completes, so no references to them * should be kept. * @param resultCallback Capture result callback to be called once the capture result - * values are ready. + * values of the processed image are ready. * @param executor The executor to run the callback on. If null then the callback will * run on any arbitrary executor. * @since 1.3 @@ -75,22 +89,28 @@ public interface CaptureProcessorImpl { ProcessResultImpl resultCallback, Executor executor); /** - * This callback will be invoked when CameraX changes the configured input resolution. After - * this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as input - * to be at the specified resolution. + * Process a set images captured that were requested for both postview and + * still capture. * - * @param size for the surface. - * @hide - */ - void onResolutionUpdate(Size size); - - /** - * This callback will be invoked when CameraX changes the configured input image format. - * After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as - * input to have the specified image format. + * <p> This processing method will be called if a postview was requested, therefore the + * processed postview should be written to the + * {@link Surface} received by {@link #onPostviewOutputSurface(Surface, int)}. + * The final result of the processing step should be written to the {@link Surface} that was + * received by {@link #onOutputSurface(Surface, int)}. Since postview should be available + * before the capture, it should be processed and written to the surface before + * the final capture is processed. * - * @param imageFormat for the surface. - * @hide + * @param results The map of {@link ImageFormat#YUV_420_888} format images and + * metadata to process. The {@link Image} that are contained within + * the map will become invalid after this method completes, so no + * references to them should be kept. + * @param resultCallback Capture result callback to be called once the capture result + * values of the processed image are ready. + * @param executor The executor to run the callback on. If null then the callback + * will run on any arbitrary executor. + * @throws RuntimeException if postview feature is not supported + * @since 1.4 */ - void onImageFormatUpdate(int imageFormat); + void processWithPostview(Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor); } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java index f926cff9..23570c4b 100644 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java @@ -84,4 +84,21 @@ public interface ExtenderStateListener { * @hide */ CaptureStageImpl onDisableSession(); + + /** + * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is + * initialized and must return a valid camera session type + * {@link android.hardware.camera2.params.SessionConfiguration#getSessionType} + * to be used to configure camera capture session. Both the preview and the image capture + * extender must return the same session type value for a specific extension type. If there + * is inconsistency between the session type values from preview and image extenders, then + * the session configuration will fail. + * + * + * @since 1.4 + * @return Camera capture session type. Regular and vendor specific types are supported but + * not high speed values. The extension can return -1 in which case the camera capture session + * will be configured to use the default regular type. + */ + int onSessionType(); } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java index af147ed7..75739ce0 100644 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java @@ -29,7 +29,7 @@ import android.util.Log; */ public class ExtensionVersionImpl { private static final String TAG = "ExtenderVersionImpl"; - private static final String VERSION = "1.3.0"; + private static final String VERSION = "1.4.0"; /** * @hide diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java index 57b7fe65..25232635 100644 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java @@ -17,10 +17,12 @@ package androidx.camera.extensions.impl; import android.content.Context; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.MeteringRectangle; +import android.hardware.camera2.params.SessionConfiguration; import android.media.Image; import android.media.ImageWriter; import android.os.Build; @@ -130,6 +132,26 @@ public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderIm } @Override + public void onPostviewOutputSurface(Surface surface) { + + } + + @Override + public void processWithPostview( + Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor) { + if (!isPostviewAvailable()) { + throw new RuntimeException("The extension doesn't support postview"); + } + + if (resultCallback != null) { + process(results, resultCallback, executor); + } else { + process(results); + } + } + + @Override public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results, ProcessResultImpl resultCallback, Executor executor) { Pair<Image, TotalCaptureResult> result = results.get(NORMAL_STAGE_ID); @@ -184,12 +206,25 @@ public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderIm jpegOrientation)); } + Integer strength = result.second.get( + CaptureResult.EXTENSION_STRENGTH); + if (strength != null) { + captureResults.add(new Pair<>(CaptureResult.EXTENSION_STRENGTH, + strength)); + } + + captureResults.add(new Pair<>(CaptureResult.EXTENSION_CURRENT_TYPE, + CameraExtensionCharacteristics.EXTENSION_HDR)); + if (executor != null) { executor.execute(() -> resultCallback.onCaptureCompleted( shutterTimestamp, captureResults)); + executor.execute(() -> + resultCallback.onCaptureProcessProgressed(100)); } else { resultCallback.onCaptureCompleted(shutterTimestamp, captureResults); + resultCallback.onCaptureProcessProgressed(100); } } } @@ -264,6 +299,11 @@ public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderIm } @Override + public void onResolutionUpdate(Size size, Size postviewSize) { + + } + + @Override public void onImageFormatUpdate(int imageFormat) { } @@ -332,6 +372,11 @@ public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderIm } @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + return new ArrayList<>(); + } + + @Override public Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize) { return null; } @@ -340,7 +385,7 @@ public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderIm public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { final CaptureRequest.Key [] CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_ZOOM_RATIO, CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_REGIONS, - CaptureRequest.CONTROL_AF_TRIGGER}; + CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.EXTENSION_STRENGTH}; return Arrays.asList(CAPTURE_REQUEST_SET); } @@ -348,7 +393,28 @@ public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderIm public List<CaptureResult.Key> getAvailableCaptureResultKeys() { final CaptureResult.Key [] CAPTURE_RESULT_SET = {CaptureResult.CONTROL_ZOOM_RATIO, CaptureResult.CONTROL_AF_MODE, CaptureResult.CONTROL_AF_REGIONS, - CaptureResult.CONTROL_AF_TRIGGER, CaptureResult.CONTROL_AF_STATE}; + CaptureResult.CONTROL_AF_TRIGGER, CaptureResult.CONTROL_AF_STATE, + CaptureResult.EXTENSION_CURRENT_TYPE, CaptureResult.EXTENSION_STRENGTH}; return Arrays.asList(CAPTURE_RESULT_SET); } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return true; + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + return null; + } + + @Override + public boolean isPostviewAvailable() { + return false; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java index 78b0a9d0..7777bfa2 100644 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java @@ -18,15 +18,18 @@ package androidx.camera.extensions.impl; import android.content.Context; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.MeteringRectangle; +import android.hardware.camera2.params.SessionConfiguration; import android.media.ImageWriter; import android.media.Image; import android.util.Pair; import android.util.Size; import android.view.Surface; +import java.io.Closeable; import java.util.ArrayList; import java.util.concurrent.Executor; import java.util.List; @@ -43,8 +46,6 @@ import java.util.List; public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl { private static final int DEFAULT_STAGE_ID = 0; - ImageWriter mWriter; - /** * @hide */ @@ -109,17 +110,32 @@ public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl { return null; } - private PreviewImageProcessorImpl mProcessor = new PreviewImageProcessorImpl() { + private HdrPreviewProcessor mProcessor = new HdrPreviewProcessor(); + + private static class HdrPreviewProcessor implements PreviewImageProcessorImpl, Closeable { Surface mSurface; int mFormat = -1; + final Object mLock = new Object(); // Synchronize access to 'mWriter' + ImageWriter mWriter; - private void setWindowSurface() { - if (mSurface != null && mFormat >= 0) { + public void close() { + synchronized(mLock) { if (mWriter != null) { mWriter.close(); + mWriter = null; } + } + } - mWriter = ImageWriter.newInstance(mSurface, 2, mFormat); + private void setWindowSurface() { + synchronized(mLock) { + if (mSurface != null && mFormat >= 0) { + if (mWriter != null) { + mWriter.close(); + } + + mWriter = ImageWriter.newInstance(mSurface, 2, mFormat); + } } } @@ -132,7 +148,11 @@ public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl { @Override public void process(Image image, TotalCaptureResult result) { - mWriter.queueInputImage(image); + synchronized(mLock) { + if (mWriter != null) { + mWriter.queueInputImage(image); + } + } } @Override @@ -175,6 +195,14 @@ public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl { jpegOrientation)); } + Integer strength = result.get(CaptureResult.EXTENSION_STRENGTH); + if (strength != null) { + captureResults.add(new Pair<>(CaptureResult.EXTENSION_STRENGTH, strength)); + } + + captureResults.add(new Pair<>(CaptureResult.EXTENSION_CURRENT_TYPE, + CameraExtensionCharacteristics.EXTENSION_HDR)); + if (executor != null) { executor.execute(() -> resultCallback.onCaptureCompleted(shutterTimestamp, captureResults)); @@ -209,10 +237,7 @@ public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl { */ @Override public void onDeInit() { - if (mWriter != null) { - mWriter.close(); - mWriter = null; - } + mProcessor.close(); } /** @@ -238,4 +263,9 @@ public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl { public CaptureStageImpl onDisableSession() { return null; } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java index c27a5dbb..37e7baf3 100644 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java @@ -91,6 +91,21 @@ public interface ImageCaptureExtenderImpl extends ExtenderStateListener { List<Pair<Integer, Size[]>> getSupportedResolutions(); /** + * Returns the customized supported postview resolutions for a still capture using + * its size. + * + * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned. + * + * <p>The returned resolutions should be subset of the supported sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. + * + * @return the customized supported resolutions, or null to support all sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap}. + * @since 1.4 + */ + List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize); + + /** * Returns the estimated capture latency range in milliseconds for the target capture * resolution. * @@ -138,5 +153,44 @@ public interface ImageCaptureExtenderImpl extends ExtenderStateListener { * @since 1.3 */ List<CaptureResult.Key> getAvailableCaptureResultKeys(); -} + /** + * Advertise support for {@link ProcessResultImpl#onCaptureProcessProgressed}. + * + * @return {@code true} in case the process progress callback is supported and is expected to + * be triggered, {@code false} otherwise. + * @since 1.4 + */ + boolean isCaptureProcessProgressAvailable(); + + /** + * Returns the dynamically calculated capture latency pair in milliseconds. + * + * <p>In contrast to {@link #getEstimatedCaptureLatencyRange} this method is guaranteed to be + * called after the camera capture session is initialized and camera preview is enabled. + * The measurement is expected to take in to account dynamic parameters such as the current + * scene, the state of 3A algorithms, the state of internal HW modules and return a more + * accurate assessment of the still capture latency.</p> + * + * @return pair that includes the estimated input frame/frames camera capture latency as the + * first field and the estimated post-processing latency {@link CaptureProcessorImpl#process} + * as the second pair field. Both first and second fields will be in milliseconds. The total + * still capture latency will be the sum of both the first and second values. + * The pair is expected to be null if the dynamic latency estimation is not supported. + * If clients have not configured a still capture output, then this method can also return a + * null pair. + * @since 1.4 + */ + Pair<Long, Long> getRealtimeCaptureLatency(); + + /** + * Indicates whether the extension supports the postview for still capture feature. + * If the extension is using HAL processing, false should be returned since the + * postview feature is not currently supported for this case. + * + * @return {@code true} in case postview for still capture is supported + * {@code false} otherwise. + * @since 1.4 + */ + boolean isPostviewAvailable(); +} diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java index f0821ed7..e3317d94 100755 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java @@ -20,6 +20,7 @@ import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.SessionConfiguration; import android.media.Image; import android.media.ImageWriter; import android.os.Build; @@ -116,6 +117,26 @@ public final class NightImageCaptureExtenderImpl implements ImageCaptureExtender } @Override + public void onPostviewOutputSurface(Surface surface) { + + } + + @Override + public void processWithPostview( + Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor) { + if (!isPostviewAvailable()) { + throw new RuntimeException("The extension doesn't support postview"); + } + + if (resultCallback != null) { + process(results, resultCallback, executor); + } else { + process(results); + } + } + + @Override public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results, ProcessResultImpl resultCallback, Executor executor) { throw new RuntimeException("The extension doesn't support capture " + @@ -164,6 +185,11 @@ public final class NightImageCaptureExtenderImpl implements ImageCaptureExtender } @Override + public void onResolutionUpdate(Size size, Size postviewSize) { + + } + + @Override public void onImageFormatUpdate(int imageFormat) { } @@ -244,6 +270,11 @@ public final class NightImageCaptureExtenderImpl implements ImageCaptureExtender } @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + return new ArrayList<>(); + } + + @Override public Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize) { return null; } @@ -257,4 +288,24 @@ public final class NightImageCaptureExtenderImpl implements ImageCaptureExtender public List<CaptureResult.Key> getAvailableCaptureResultKeys() { return new ArrayList<>(); } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return false; + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + return null; + } + + @Override + public boolean isPostviewAvailable() { + return false; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java index e29abec1..46be86ab 100755 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java @@ -18,6 +18,7 @@ package androidx.camera.extensions.impl; import android.content.Context; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.SessionConfiguration; import android.util.Pair; import android.util.Size; @@ -162,4 +163,9 @@ public final class NightPreviewExtenderImpl implements PreviewExtenderImpl { return captureStage; } + + @Override + public int onSessionType() { + return SessionConfiguration.SESSION_REGULAR; + } } diff --git a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java index d4c2014d..518942e9 100644 --- a/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java +++ b/camera2/extensions/sample/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java @@ -24,7 +24,6 @@ import java.util.List; /** * Allows clients to receive information about the capture result values of processed frames. * - * @since 1.3 */ public interface ProcessResultImpl { /** @@ -38,6 +37,23 @@ public interface ProcessResultImpl { * must also be passed as part of this callback. Both Camera2 and * CameraX guarantee that those two settings and results are always * supported and applied by the corresponding framework. + * @since 1.3 */ void onCaptureCompleted(long shutterTimestamp, List<Pair<CaptureResult.Key, Object>> result); + + + /** + * Capture progress callback that needs to be called when the process capture is + * ongoing and includes the estimated progress of the processing. + * + * <p>Extensions must ensure that they always call this callback with monotonically increasing + * values.</p> + * + * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the + * callback is expected to be called once when processing is done with value 100.</p> + * + * @param progress Value between 0 and 100. + * @since 1.4 + */ + void onCaptureProcessProgressed(int progress); } diff --git a/camera2/extensions/service_based_sample/README.txt b/camera2/extensions/service_based_sample/README.txt new file mode 100644 index 00000000..a212a39a --- /dev/null +++ b/camera2/extensions/service_based_sample/README.txt @@ -0,0 +1,26 @@ +Service-Based reference implementation +This reference implementation demonstrates how to implement the Extensions in a standalone service. + +It contains the following components: +1) oem_library: + A Camera Extensions OEM library that implements the Extensions-Interface to enable both Camera2 + and CameraX Extensions APIS. It is basically a pass-through that forwards all calls from + Extensions-Interface to the service. If it works well for you, you don't have to modify it. + + It also contains the AIDL and wrapper classes for communicating with the service. AIDL and + wrapper classes are located in androidx.camera.extensions.impl.service package. + + Both Advanced Extender and Basic Extender is supported, however, Advanced Extender is enabled + by default. If you want to use Basic Extender to implement, change + ExtensionsVersionImpl#isAdvancedExtenderImplemented to return false. + +2) extensions_service: + A sample implementation of extensions service is provided. You should add your real implementation + here. The sample service is built using Android.bp, but you can transform it into a gradle + project by adding the stub jar of oem_library (located in + out/target/common/obj/JAVA_LIBRARIES/service_based_camera_extensions_intermediates/) to the + dependencies. + +In this service-based architecture, all functionalities of the Extensions-Interface are supposed to +be implemented in extensions_service except for ExtensionVersionImpl#checkApiVersion and +#isAdvancedExtenderImplemented which require you to implement it in the oem_library.
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/extensions_service/Android.bp b/camera2/extensions/service_based_sample/extensions_service/Android.bp new file mode 100644 index 00000000..383a3c0b --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/Android.bp @@ -0,0 +1,31 @@ +// +// Copyright (C) 2023 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_app { + name: "ExtensionsService", + srcs: ["src/**/*.java"], + libs: ["service_based_camera_extensions"], + static_libs: [ + "androidx.annotation_annotation", + ], + platform_apis: true, + certificate: "platform", + enforce_uses_libs: false, +} diff --git a/camera2/extensions/service_based_sample/extensions_service/AndroidManifest.xml b/camera2/extensions/service_based_sample/extensions_service/AndroidManifest.xml new file mode 100644 index 00000000..abd40763 --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/AndroidManifest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.oemextensions"> + <queries> + <intent> + <action android:name="androidx.camera.extensions.action.VENDOR_ACTION" /> + </intent> + </queries> + + <application + android:label="@string/app_name" + android:defaultToDeviceProtectedStorage="true" + android:directBootAware="true"> + <uses-library android:name="androidx.camera.extensions.impl" android:required="true" /> + <service + android:name="ExtensionsService" + android:enabled="true" + android:exported="true"> + <intent-filter> + <action android:name="androidx.camera.extensions.action.VENDOR_ACTION" /> + </intent-filter> + </service> + </application> + +</manifest> diff --git a/camera2/extensions/service_based_sample/extensions_service/res/values/strings.xml b/camera2/extensions/service_based_sample/extensions_service/res/values/strings.xml new file mode 100644 index 00000000..7a8f9d4a --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/res/values/strings.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">Camera Extensions Service</string> + +</resources> diff --git a/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/AdvancedExtenderImplStub.java b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/AdvancedExtenderImplStub.java new file mode 100644 index 00000000..960f9a4c --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/AdvancedExtenderImplStub.java @@ -0,0 +1,268 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.oemextensions; + +import android.content.Context; +import android.graphics.ImageFormat; +import android.graphics.Rect; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.params.MeteringRectangle; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.os.RemoteException; +import android.util.Log; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.IAdvancedExtenderImpl; +import androidx.camera.extensions.impl.service.ISessionProcessorImpl; +import androidx.camera.extensions.impl.service.LatencyRange; +import androidx.camera.extensions.impl.service.SizeList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +public class AdvancedExtenderImplStub extends IAdvancedExtenderImpl.Stub { + private static final String TAG = "AdvancedExtenderStub"; + private Context mContext; + private int mExtensionType; + private String mCurrentCameraId; + private CameraCharacteristics mCameraCharacteristics; + + /** + * Construct the AdvancedExtenderImplStub instance. + * + * @param context a context. + * @param extensionType CameraExtensionCharacteristics#EXTENSION_AUTOMATIC for Auto, + * CameraExtensionCharacteristics#EXTENSION_NIGHT for Night, + * CameraExtensionCharacteristics#EXTENSION_HDR for HDR, + * CameraExtensionCharacteristics#EXTENSION_BOKEH for Bokeh, + * CameraExtensionCharacteristics#EXTENSION_FACE_RETOUCH for face retouch. + */ + public AdvancedExtenderImplStub(@NonNull Context context, int extensionType) { + mContext = context; + mExtensionType = extensionType; + } + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId) throws RemoteException { + return true; + } + + @Override + public void init(@NonNull String cameraId) throws RemoteException { + mCurrentCameraId = cameraId; + try { + CameraManager cameraManager = mContext.getSystemService(CameraManager.class); + mCameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId); + } catch (CameraAccessException e) { + throw new IllegalStateException("Cannot get CameraCharacteristics", e); + } + } + + @Override + @NonNull + public LatencyRange getEstimatedCaptureLatencyRange(@NonNull String cameraId, + @Nullable androidx.camera.extensions.impl.service.Size outputSize, + int format) throws RemoteException { + Log.d(TAG, "getEstimatedCaptureLatencyRange format" + format); + + LatencyRange latencyRange = new LatencyRange(); + latencyRange.min = 100; + latencyRange.max = 1000; + return latencyRange; + } + + private static SizeList getSupportedSizeByFormat( + CameraCharacteristics cameraCharacteristics, int imageFormat) { + StreamConfigurationMap streamConfigMap = + cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + + Size[] sizes = streamConfigMap.getOutputSizes(imageFormat); + SizeList sizeList = new SizeList(); + sizeList.sizes = new ArrayList<>(); + for (Size size : sizes) { + androidx.camera.extensions.impl.service.Size sz = + new androidx.camera.extensions.impl.service.Size(); + sz.width = size.getWidth(); + sz.height = size.getHeight(); + sizeList.sizes.add(sz); + } + sizeList.format = imageFormat; + return sizeList; + } + + @Override + @NonNull + public List<SizeList> getSupportedPreviewOutputResolutions(@NonNull String cameraId) + throws RemoteException { + return Arrays.asList( + getSupportedSizeByFormat(mCameraCharacteristics, ImageFormat.PRIVATE)); + } + + @Override + @NonNull + public List<SizeList> getSupportedCaptureOutputResolutions(@NonNull String cameraId) + throws RemoteException { + return Arrays.asList( + getSupportedSizeByFormat(mCameraCharacteristics, + ImageFormat.JPEG), + getSupportedSizeByFormat(mCameraCharacteristics, + ImageFormat.YUV_420_888)); + } + + @Override + @NonNull + public List<SizeList> getSupportedYuvAnalysisResolutions(@NonNull String cameraId) + throws RemoteException { + return Arrays.asList( + getSupportedSizeByFormat(mCameraCharacteristics, ImageFormat.YUV_420_888)); + } + + @Override + @NonNull + public ISessionProcessorImpl getSessionProcessor() throws RemoteException { + Log.d(TAG, "getSessionProcessor"); + return new SimpleSessionProcessorStub(mCameraCharacteristics, + getSupportedCaptureRequestKeys().keySet(), + getSupportedCaptureResultKeys().keySet()); + } + + private Map<CaptureRequest.Key, Object> getSupportedCaptureRequestKeys() { + Map<CaptureRequest.Key, Object> map = new HashMap<>(); + map.put(CaptureRequest.CONTROL_ZOOM_RATIO, + 1.0f /* don't care, must not be null */); + map.put(CaptureRequest.SCALER_CROP_REGION, + new Rect() /* don't care, must not be null */); + map.put(CaptureRequest.CONTROL_AE_REGIONS, + new MeteringRectangle[0] /* don't care, must not be null */); + map.put(CaptureRequest.CONTROL_AWB_REGIONS, + new MeteringRectangle[0] /* don't care, must not be null */); + map.put(CaptureRequest.JPEG_QUALITY, + (byte)0 /* don't care, must not be null */); + map.put(CaptureRequest.JPEG_ORIENTATION, + 0 /* don't care, must not be null */); + if (isAfAutoSupported()) { + map.put(CaptureRequest.CONTROL_AF_TRIGGER, + 0 /* don't care, must not be null */); + map.put(CaptureRequest.CONTROL_AF_MODE, + 0 /* don't care, must not be null */); + map.put(CaptureRequest.CONTROL_AF_REGIONS, + new MeteringRectangle[0] /* don't care, must not be null */); + } + + + // Filters out unsupported keys + List<CaptureRequest.Key<?>> camera2SupportKeys= + mCameraCharacteristics.getAvailableCaptureRequestKeys(); + for (CaptureRequest.Key key : new HashSet<>(map.keySet())) { + if (!camera2SupportKeys.contains(key)) { + map.remove(key); + } + } + + return map; + } + + private Map<CaptureResult.Key, Object> getSupportedCaptureResultKeys() { + Map<CaptureResult.Key, Object> map = new HashMap<>(); + map.put(CaptureResult.CONTROL_ZOOM_RATIO, + 1.0f /* don't care, must not be null */); + map.put(CaptureResult.SCALER_CROP_REGION, + new Rect() /* don't care, must not be null */); + map.put(CaptureResult.CONTROL_AE_REGIONS, + new MeteringRectangle[0] /* don't care, must not be null */); + map.put(CaptureResult.CONTROL_AWB_REGIONS, + new MeteringRectangle[0] /* don't care, must not be null */); + map.put(CaptureResult.JPEG_QUALITY, + (byte)0 /* don't care, must not be null */); + map.put(CaptureResult.JPEG_ORIENTATION, + 0 /* don't care, must not be null */); + if (isAfAutoSupported()) { + map.put(CaptureResult.CONTROL_AF_REGIONS, + new MeteringRectangle[0] /* don't care, must not be null */); + map.put(CaptureResult.CONTROL_AF_TRIGGER, + 0 /* don't care, must not be null */); + map.put(CaptureResult.CONTROL_AF_MODE, + 0 /* don't care, must not be null */); + map.put(CaptureResult.CONTROL_AF_STATE, + 0 /* don't care, must not be null */); + } + + // Filters out unsupported keys + List<CaptureResult.Key<?>> camera2SupportKeys= + mCameraCharacteristics.getAvailableCaptureResultKeys(); + for (CaptureResult.Key key : new HashSet<>(map.keySet())) { + if (!camera2SupportKeys.contains(key)) { + map.remove(key); + } + } + return map; + } + + + private boolean isAfAutoSupported() { + int[] afModes = mCameraCharacteristics + .get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); + if (afModes == null) { + return false; + } + + for (int afMode : afModes) { + if (afMode == CameraCharacteristics.CONTROL_AF_MODE_AUTO) { + return true; + } + } + return false; + } + + @Override + public CameraMetadataWrapper getAvailableCaptureRequestKeys() + throws RemoteException { + CameraMetadataWrapper cameraMetadataWrapper = + new CameraMetadataWrapper(mCameraCharacteristics); + Map<CaptureRequest.Key, Object> keysmap = getSupportedCaptureRequestKeys(); + for (CaptureRequest.Key key : keysmap.keySet()) { + cameraMetadataWrapper.set(key, keysmap.get(key)); + } + + return cameraMetadataWrapper; + } + + @Override + public CameraMetadataWrapper getAvailableCaptureResultKeys() + throws RemoteException { + CameraMetadataWrapper cameraMetadataWrapper = + new CameraMetadataWrapper(mCameraCharacteristics); + Map<CaptureResult.Key, Object> keysmap = getSupportedCaptureResultKeys(); + for (CaptureResult.Key key : keysmap.keySet()) { + cameraMetadataWrapper.set(key, keysmap.get(key)); + } + + return cameraMetadataWrapper; + } +} diff --git a/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/CameraOutputConfigBuilder.java b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/CameraOutputConfigBuilder.java new file mode 100644 index 00000000..c339ed98 --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/CameraOutputConfigBuilder.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.oemextensions; + +import android.annotation.NonNull; +import android.hardware.camera2.params.OutputConfiguration; +import android.view.Surface; + +import androidx.camera.extensions.impl.service.CameraOutputConfig; +import androidx.camera.extensions.impl.service.Size; + +import java.util.ArrayList; + +public class CameraOutputConfigBuilder { + private final CameraOutputConfig mConfig; + + private CameraOutputConfigBuilder(CameraOutputConfig config) { + mConfig = config; + mConfig.surfaceGroupId = OutputConfiguration.SURFACE_GROUP_ID_NONE; + } + + public static CameraOutputConfigBuilder createSurfaceOutput( + int outputId, @NonNull Surface surface) { + CameraOutputConfig config = new CameraOutputConfig(); + config.type = CameraOutputConfig.TYPE_SURFACE; + config.outputId = outputId; + config.surface = surface; + return new CameraOutputConfigBuilder(config); + } + + public static CameraOutputConfigBuilder createImageReaderOutput( + int outputId, int width, int height, int imageFormat, int maxImages) { + CameraOutputConfig config = new CameraOutputConfig(); + config.type = CameraOutputConfig.TYPE_IMAGEREADER; + config.outputId = outputId; + config.size = new Size(); + config.size.width = width; + config.size.height = height; + config.imageFormat = imageFormat; + config.capacity = maxImages; + return new CameraOutputConfigBuilder(config); + } + + public CameraOutputConfigBuilder setPhysicalCameraId(String physicalCameraId) { + mConfig.physicalCameraId = physicalCameraId; + return this; + } + + public CameraOutputConfigBuilder setSurfaceGroupId(int surfaceGroupId) { + mConfig.surfaceGroupId = surfaceGroupId; + return this; + } + + public CameraOutputConfigBuilder addSharedOutputConfig(CameraOutputConfig cameraOutputConfig) { + if (mConfig.sharedSurfaceConfigs == null) { + mConfig.sharedSurfaceConfigs = new ArrayList<>(); + } + mConfig.sharedSurfaceConfigs.add(cameraOutputConfig); + return this; + } + + public CameraOutputConfig build() { + CameraOutputConfig result = new CameraOutputConfig(); + result.outputId = mConfig.outputId; + result.type = mConfig.type; + result.surface = mConfig.surface; + result.physicalCameraId = mConfig.physicalCameraId; + result.surfaceGroupId = mConfig.surfaceGroupId; + result.capacity = mConfig.capacity; + result.imageFormat = mConfig.imageFormat; + if (mConfig.size != null) { + result.size = new Size(); + result.size.width = mConfig.size.width; + result.size.height = mConfig.size.height; + } + if (mConfig.sharedSurfaceConfigs != null) { + result.sharedSurfaceConfigs = new ArrayList<>(mConfig.sharedSurfaceConfigs); + } + return result; + } +} diff --git a/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/ExtensionsService.java b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/ExtensionsService.java new file mode 100644 index 00000000..3a279716 --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/ExtensionsService.java @@ -0,0 +1,83 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.oemextensions; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.camera.extensions.impl.PreviewExtenderImpl; +import androidx.camera.extensions.impl.service.IAdvancedExtenderImpl; +import androidx.camera.extensions.impl.service.IExtensionsService; +import androidx.camera.extensions.impl.service.IOnExtensionsDeinitializedCallback; +import androidx.camera.extensions.impl.service.IOnExtensionsInitializedCallback; +import androidx.camera.extensions.impl.service.IImageCaptureExtenderImpl; +import androidx.camera.extensions.impl.service.IPreviewExtenderImpl; + +public class ExtensionsService extends Service { + private static final String TAG = "ExtensionsService"; + + public ExtensionsService() { + } + + @Override + @NonNull + public IBinder onBind(Intent intent) { + return new ExtensionsServiceStub(); + } + + class ExtensionsServiceStub extends IExtensionsService.Stub { + @Override + public boolean isAdvancedExtenderImplemented() throws RemoteException { + return true; + } + + @Override + public void initialize(String version, IOnExtensionsInitializedCallback callback) + throws RemoteException { + Log.d(TAG, "initialize"); + callback.onSuccess(); + } + + @Override + public void deInitialize(IOnExtensionsDeinitializedCallback callback) + throws RemoteException { + Log.d(TAG, "deInitialize"); + callback.onSuccess(); + } + + @Override + public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType) + throws RemoteException { + Log.d(TAG, "initializeAdvancedExtension"); + return new AdvancedExtenderImplStub(ExtensionsService.this, extensionType); + } + + @Override + public IPreviewExtenderImpl initializePreviewExtension(int extensionType) { + return new PreviewExtenderImplStub(ExtensionsService.this, extensionType); + } + + @Override + public IImageCaptureExtenderImpl initializeImageCaptureExtension(int extensionType) { + return new ImageCaptureExtenderImplStub(ExtensionsService.this, extensionType); + } + } +} diff --git a/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/ImageCaptureExtenderImplStub.java b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/ImageCaptureExtenderImplStub.java new file mode 100644 index 00000000..75cd99c9 --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/ImageCaptureExtenderImplStub.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.oemextensions; + +import android.content.Context; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.media.Image; +import android.media.ImageWriter; +import android.os.RemoteException; +import android.util.Log; +import android.view.Surface; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.CaptureBundle; +import androidx.camera.extensions.impl.service.CaptureStageImplWrapper; +import androidx.camera.extensions.impl.service.ICaptureProcessorImpl; +import androidx.camera.extensions.impl.service.IImageCaptureExtenderImpl; +import androidx.camera.extensions.impl.service.IProcessResultImpl; +import androidx.camera.extensions.impl.service.LatencyRange; +import androidx.camera.extensions.impl.service.Size; +import androidx.camera.extensions.impl.service.SizeList; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; + +public class ImageCaptureExtenderImplStub extends IImageCaptureExtenderImpl.Stub { + private static final String TAG = "ImageCaptureExtenderImplStub"; + + private static final int DEFAULT_STAGE_ID = 0; + private static final int SESSION_STAGE_ID = 101; + private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_SHADE; + + private final Context mContext; + private final List<CaptureResult.Key> mResultKeyList = Arrays.asList( + CaptureResult.CONTROL_AE_MODE, + CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER, + CaptureResult.CONTROL_AE_LOCK, + CaptureResult.CONTROL_AE_STATE, + CaptureResult.FLASH_MODE, + CaptureResult.FLASH_STATE, + CaptureResult.JPEG_QUALITY, + CaptureResult.JPEG_ORIENTATION + ); + + private CameraCharacteristics mCameraCharacteristics; + ICaptureProcessorImpl mCaptureProcessor = + new ICaptureProcessorImpl.Stub() { + private ImageWriter mImageWriter; + + @Override + public void onOutputSurface(Surface surface, int imageFormat) { + mImageWriter = ImageWriter.newInstance(surface, 1); + } + + @Override + public void process(List<CaptureBundle> captureList, + IProcessResultImpl resultCallback) { + CaptureBundle captureBundle = captureList.get(0); + TotalCaptureResult captureResult = + captureBundle.captureResult.toTotalCaptureResult(); + + if (resultCallback != null) { + CameraMetadataWrapper cameraMetadataWrapper = + new CameraMetadataWrapper(mCameraCharacteristics); + Long shutterTimestamp = captureResult.get(CaptureResult.SENSOR_TIMESTAMP); + if (shutterTimestamp != null) { + for (CaptureResult.Key key : mResultKeyList) { + if (captureResult.get(key) != null) { + cameraMetadataWrapper.set(key, captureResult.get(key)); + } + } + try { + resultCallback.onCaptureCompleted(shutterTimestamp, + cameraMetadataWrapper); + } catch (RemoteException e) { + + } + } + } + Image image = mImageWriter.dequeueInputImage(); + + // Do processing here + ByteBuffer yByteBuffer = image.getPlanes()[0].getBuffer(); + ByteBuffer uByteBuffer = image.getPlanes()[2].getBuffer(); + ByteBuffer vByteBuffer = image.getPlanes()[1].getBuffer(); + + Image captureImage = captureBundle.captureImage.get(); + + // Sample here just simply copy/paste the capture image result + yByteBuffer.put(captureImage.getPlanes()[0].getBuffer()); + uByteBuffer.put(captureImage.getPlanes()[2].getBuffer()); + vByteBuffer.put(captureImage.getPlanes()[1].getBuffer()); + Long sensorTimestamp = + captureResult.get(CaptureResult.SENSOR_TIMESTAMP); + if (sensorTimestamp != null) { + image.setTimestamp(sensorTimestamp); + } else { + Log.e(TAG, "Sensor timestamp absent using default!"); + } + + mImageWriter.queueInputImage(image); + + for (CaptureBundle bundle : captureList) { + bundle.captureImage.decrement(); + } + } + + @Override + public void onResolutionUpdate(androidx.camera.extensions.impl.service.Size size) { + + } + + @Override + public void onImageFormatUpdate(int imageFormat) { + + } + }; + + public ImageCaptureExtenderImplStub(@NonNull Context context, int extensionType) { + mContext = context; + + } + + @Override + public void onInit(String cameraId) { + + } + + @Override + public void onDeInit() { + + } + + @Override + @Nullable + public CaptureStageImplWrapper onPresetSession() { + return null; + } + + @Override + @Nullable + public CaptureStageImplWrapper onEnableSession() { + return null; + } + + @Override + @Nullable + public CaptureStageImplWrapper onDisableSession() { + return null; + } + + @Override + public boolean isExtensionAvailable(String cameraId) { + return true; + } + + @Override + public void init(String cameraId) { + try { + CameraManager cameraManager = mContext.getSystemService(CameraManager.class); + mCameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId); + } catch (CameraAccessException e) { + Log.e(TAG, "Cannot get CameraCharacteristics", e); + } + + } + + @Override + public ICaptureProcessorImpl getCaptureProcessor() { + return mCaptureProcessor; + } + + @Override + public List<CaptureStageImplWrapper> getCaptureStages() { + CaptureStageImplWrapper captureStage = new CaptureStageImplWrapper(); + captureStage.id = DEFAULT_STAGE_ID; + captureStage.parameters = new CameraMetadataWrapper(mCameraCharacteristics); + return Arrays.asList(captureStage); + } + + @Override + public int getMaxCaptureStage() { + return 1; + } + + @Override + public List<SizeList> getSupportedResolutions() { + return null; + } + + @Override + @Nullable + public LatencyRange getEstimatedCaptureLatencyRange(Size outputSize) { + return null; + } + + @Override + public CameraMetadataWrapper getAvailableCaptureRequestKeys() { + return null; + } + + @Override + public CameraMetadataWrapper getAvailableCaptureResultKeys() { + return null; + } +} diff --git a/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/PreviewExtenderImplStub.java b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/PreviewExtenderImplStub.java new file mode 100644 index 00000000..c9add1c7 --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/PreviewExtenderImplStub.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.oemextensions; + +import android.content.Context; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CaptureRequest; +import android.media.ImageWriter; +import android.util.Log; +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.CaptureStageImplWrapper; +import androidx.camera.extensions.impl.service.IPreviewExtenderImpl; +import androidx.camera.extensions.impl.service.IPreviewImageProcessorImpl; +import androidx.camera.extensions.impl.service.IProcessResultImpl; +import androidx.camera.extensions.impl.service.IRequestUpdateProcessorImpl; +import androidx.camera.extensions.impl.service.ImageWrapper; +import androidx.camera.extensions.impl.service.SizeList; +import androidx.camera.extensions.impl.service.TotalCaptureResultWrapper; + +import java.util.List; + +public class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub { + private static final String TAG = "PreviewExtenderImplStub"; + + private static final int DEFAULT_STAGE_ID = 0; + private static final int SESSION_STAGE_ID = 101; + private static final int MODE = CaptureRequest.CONTROL_AWB_MODE_SHADE; + private final int mProcessorType; + private final Context mContext; + private CaptureStageImplWrapper mCaptureStage; + private CameraCharacteristics mCameraCharacteristics; + private final SimplePreviewImageProcessor mPreviewImageProcessor; + private final SimpleRequestUpdateProcessor mRequestUpdateProcessor; + + public PreviewExtenderImplStub(@NonNull Context context, int extensionType) { + mContext = context; + switch (extensionType) { + case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC: + case CameraExtensionCharacteristics.EXTENSION_BOKEH: + mRequestUpdateProcessor = new SimpleRequestUpdateProcessor(); + mPreviewImageProcessor = null; + mProcessorType = IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY; + break; + case CameraExtensionCharacteristics.EXTENSION_HDR: + case CameraExtensionCharacteristics.EXTENSION_NIGHT: + mRequestUpdateProcessor = null; + mPreviewImageProcessor = new SimplePreviewImageProcessor(); + mProcessorType = IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR; + break; + case CameraExtensionCharacteristics.EXTENSION_FACE_RETOUCH: + default: + mRequestUpdateProcessor = null; + mPreviewImageProcessor = null; + mProcessorType = IPreviewExtenderImpl.PROCESSOR_TYPE_NONE; + break; + } + } + + private class SimpleRequestUpdateProcessor extends IRequestUpdateProcessorImpl.Stub { + private int mFrameCount = 0; + private Integer mWBMode = CaptureRequest.CONTROL_AWB_MODE_AUTO; + + @Override + public CaptureStageImplWrapper process(TotalCaptureResultWrapper result) { + mFrameCount++; + if (mFrameCount % 90 == 0) { + mCaptureStage = new CaptureStageImplWrapper(); + mCaptureStage.id = DEFAULT_STAGE_ID; + switch (mWBMode) { + case CaptureRequest.CONTROL_AWB_MODE_AUTO: + mWBMode = MODE; + break; + case MODE: + mWBMode = CaptureRequest.CONTROL_AWB_MODE_AUTO; + break; default: + } + mCaptureStage.parameters = new CameraMetadataWrapper( + mCameraCharacteristics); + mCaptureStage.parameters.set(CaptureRequest.CONTROL_AWB_MODE, + mWBMode); + mFrameCount = 0; + + return mCaptureStage; + } + return null; + } + }; + private static class SimplePreviewImageProcessor extends IPreviewImageProcessorImpl.Stub { + private ImageWriter mWriter; + + public void close() { + if (mWriter != null) { + mWriter.close(); + } + } + @Override + public void onOutputSurface(Surface surface, int imageFormat) { + mWriter = ImageWriter.newInstance(surface, imageFormat); + } + + @Override + public void onResolutionUpdate( + androidx.camera.extensions.impl.service.Size size) { + } + + @Override + public void onImageFormatUpdate(int imageFormat) { + } + + @Override + public void process(ImageWrapper image, TotalCaptureResultWrapper result, + IProcessResultImpl resultCallback) { + mWriter.queueInputImage(image.get()); + image.decrement(); + } + } + + @Override + public void onInit(String cameraId) { + + } + + @Override + public void onDeInit() { + if (mPreviewImageProcessor != null) { + mPreviewImageProcessor.close(); + } + } + + @Override + @Nullable + public CaptureStageImplWrapper onPresetSession() { + CaptureStageImplWrapper captureStage = new CaptureStageImplWrapper(); + captureStage.id = SESSION_STAGE_ID; + captureStage.parameters = new CameraMetadataWrapper(mCameraCharacteristics); + captureStage.parameters.set(CaptureRequest.CONTROL_AWB_MODE, MODE); + return captureStage; + } + + @Override + @Nullable + public CaptureStageImplWrapper onEnableSession() { + CaptureStageImplWrapper captureStage = new CaptureStageImplWrapper(); + captureStage.id = SESSION_STAGE_ID; + captureStage.parameters = new CameraMetadataWrapper(mCameraCharacteristics); + captureStage.parameters.set(CaptureRequest.CONTROL_AWB_MODE, MODE); + return captureStage; + } + + @Override + @Nullable + public CaptureStageImplWrapper onDisableSession() { + CaptureStageImplWrapper captureStage = new CaptureStageImplWrapper(); + captureStage.id = SESSION_STAGE_ID; + captureStage.parameters = new CameraMetadataWrapper(mCameraCharacteristics); + captureStage.parameters.set(CaptureRequest.CONTROL_AWB_MODE, MODE); + return captureStage; + } + + @Override + public boolean isExtensionAvailable(String cameraId) { + return true; + } + + @Override + public void init(String cameraId) { + try { + CameraManager cameraManager = mContext.getSystemService(CameraManager.class); + mCameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId); + } catch (CameraAccessException e) { + Log.e(TAG, "Cannot get CameraCharacteristics", e); + } + + mCaptureStage = new CaptureStageImplWrapper(); + mCaptureStage.id = DEFAULT_STAGE_ID; + mCaptureStage.parameters = new CameraMetadataWrapper(mCameraCharacteristics); + mCaptureStage.parameters.set(CaptureRequest.CONTROL_AWB_MODE, + CaptureRequest.CONTROL_AWB_MODE_AUTO); + } + + @Override + public CaptureStageImplWrapper getCaptureStage() { + return mCaptureStage; + } + + @Override + public int getProcessorType() { + return mProcessorType; + } + + @Override + @Nullable + public IPreviewImageProcessorImpl getPreviewImageProcessor() { + return mPreviewImageProcessor; + } + + @Override + @Nullable + public IRequestUpdateProcessorImpl getRequestUpdateProcessor() { + return mRequestUpdateProcessor; + } + + @Override + @Nullable + public List<SizeList> getSupportedResolutions() { + return null; + } +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/RequestBuilder.java b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/RequestBuilder.java new file mode 100644 index 00000000..52bb26b4 --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/RequestBuilder.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.oemextensions; + +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.Request; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class RequestBuilder { + private static final AtomicInteger sNextRequestId = new AtomicInteger(0); + private final List<Integer> mTargetOutputConfigIds = new ArrayList<>(); + private final CameraMetadataWrapper mParameters; + private int mTemplateId = CameraDevice.TEMPLATE_PREVIEW; + private final int mRequestId = 0; + + public RequestBuilder(CameraCharacteristics cameraCharacteristics) { + mParameters = new CameraMetadataWrapper(cameraCharacteristics); + } + + public RequestBuilder addTargetOutputConfigId(int... outputConfigIds) { + for (int id : outputConfigIds) { + mTargetOutputConfigIds.add(id); + } + return this; + } + + public <T> RequestBuilder setParameter(CaptureRequest.Key<T> key, T value) { + mParameters.set(key, value); + return this; + } + + public RequestBuilder setTemplateId(int templateId) { + mTemplateId = templateId; + return this; + } + + public Request build() { + Request request = new Request(); + int[] idArray = new int[mTargetOutputConfigIds.size()]; + for (int i = 0; i < idArray.length; i++) { + idArray[i] = mTargetOutputConfigIds.get(i); + } + request.targetOutputConfigIds = idArray; + request.requestId = mRequestId; + request.parameters = mParameters; + request.templateId = mTemplateId; + request.requestId = sNextRequestId.getAndIncrement(); + return request; + } +} diff --git a/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/SimpleSessionProcessorStub.java b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/SimpleSessionProcessorStub.java new file mode 100644 index 00000000..ed749aec --- /dev/null +++ b/camera2/extensions/service_based_sample/extensions_service/src/com/android/oemextensions/SimpleSessionProcessorStub.java @@ -0,0 +1,383 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.oemextensions; + +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.os.RemoteException; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.CameraOutputConfig; +import androidx.camera.extensions.impl.service.CameraSessionConfig; +import androidx.camera.extensions.impl.service.CaptureFailureWrapper; +import androidx.camera.extensions.impl.service.CaptureResultWrapper; +import androidx.camera.extensions.impl.service.ICaptureCallback; +import androidx.camera.extensions.impl.service.IRequestCallback; +import androidx.camera.extensions.impl.service.IRequestProcessorImpl; +import androidx.camera.extensions.impl.service.ISessionProcessorImpl; +import androidx.camera.extensions.impl.service.OutputSurface; +import androidx.camera.extensions.impl.service.TotalCaptureResultWrapper; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Demonstrates a very simple SessionProcessor which just pass all the output surface into the + * camera pipeline directly. No postprocessing is performed. + */ +public class SimpleSessionProcessorStub extends ISessionProcessorImpl.Stub { + private static final String TAG = "SimpleSessionProcessor"; + private final CameraCharacteristics mCameraCharacteristics; + private final Set<CaptureRequest.Key> mSupportedRequestKeys; + private final Set<CaptureResult.Key> mSupportedResultKeys; + + private IRequestProcessorImpl mIRequestProcessor; + private AtomicInteger mNextCaptureSequenceId = new AtomicInteger(1); + private Map<CaptureRequest.Key, Object> mParameters = new HashMap<>(); + private boolean mHasAnalysisOutput = false; + + private static final int PREVIEW_OUTPUT_ID = 1; + private static final int CAPTURE_OUTPUT_ID = 2; + private static final int ANALYSIS_OUTPUT_ID = 3; + + public SimpleSessionProcessorStub(@NonNull CameraCharacteristics cameraCharacteristics, + @NonNull Set<CaptureRequest.Key> supportedRequestKeys, + @NonNull Set<CaptureResult.Key> supportedResultKeys) { + mCameraCharacteristics = cameraCharacteristics; + mSupportedRequestKeys = supportedRequestKeys; + mSupportedResultKeys = supportedResultKeys; + } + + @Override + public CameraSessionConfig initSession(@NonNull String cameraId, + @NonNull OutputSurface outputSurfacePreview, + @NonNull OutputSurface outputSurfaceStillCapture, + @Nullable OutputSurface outputSurfaceAnalysis) throws RemoteException { + Log.d(TAG, "initSession cameraId=" + cameraId); + CameraSessionConfig cameraSessionConfig = new CameraSessionConfig(); + cameraSessionConfig.sessionParameter = new CameraMetadataWrapper(mCameraCharacteristics); + // if needed, invoke cameraSessionConfig.sessionParameter.set(...) to set session parameters + cameraSessionConfig.sessionTemplateId = CameraDevice.TEMPLATE_PREVIEW; + List<CameraOutputConfig> outputConfigList = new ArrayList<>(); + + // Preview + CameraOutputConfig previewOutputConfig = + CameraOutputConfigBuilder + .createSurfaceOutput(PREVIEW_OUTPUT_ID, outputSurfacePreview.surface) + .build(); + outputConfigList.add(previewOutputConfig); + + // Still capture + CameraOutputConfig captureOutputConfig = + CameraOutputConfigBuilder + .createSurfaceOutput(CAPTURE_OUTPUT_ID, outputSurfaceStillCapture.surface) + .build(); + outputConfigList.add(captureOutputConfig); + + // ImageAnalysis + if (outputSurfaceAnalysis != null) { + mHasAnalysisOutput = true; + CameraOutputConfig analysisOutputConfig = + CameraOutputConfigBuilder + .createSurfaceOutput(ANALYSIS_OUTPUT_ID, outputSurfaceAnalysis.surface) + .build(); + outputConfigList.add(analysisOutputConfig); + } + + cameraSessionConfig.outputConfigs = outputConfigList; + return cameraSessionConfig; + } + + @Override + public void deInitSession() throws RemoteException { + Log.d(TAG, "deInitSession"); + } + + @Override + public void onCaptureSessionStart(IRequestProcessorImpl requestProcessor) + throws RemoteException { + Log.d(TAG, "onCaptureSessionStart"); + mIRequestProcessor = requestProcessor; + } + + @Override + public void onCaptureSessionEnd() throws RemoteException { + Log.d(TAG, "onCaptureSessionEnd"); + + mIRequestProcessor = null; + } + + @Override + public void setParameters(CaptureRequest captureRequest) throws RemoteException { + Log.d(TAG, "setParameters"); + + for (CaptureRequest.Key<?> key : captureRequest.getKeys()) { + if (mSupportedRequestKeys.contains(key)) { + mParameters.put(key, captureRequest.get(key)); + } + } + } + + protected void notifyOnCaptureCompleted( + @NonNull int seqId, + @NonNull TotalCaptureResult result, + @NonNull ICaptureCallback captureCallback) { + Long shutterTimestamp = result.get(CaptureResult.SENSOR_TIMESTAMP); + if (shutterTimestamp != null) { + CameraMetadataWrapper cameraMetadataWrapper = + new CameraMetadataWrapper(mCameraCharacteristics); + + for (CaptureResult.Key key : mSupportedResultKeys) { + if (result.get(key) != null) { + cameraMetadataWrapper.set(key, result.get(key)); + } + } + + try { + captureCallback.onCaptureCompleted(shutterTimestamp, seqId, + cameraMetadataWrapper); + } catch (RemoteException e) { + Log.e(TAG, "cannot notify onCaptureCompleted", e); + } + } + } + + @Override + public int startTrigger(CaptureRequest captureRequest, ICaptureCallback captureCallback) + throws RemoteException { + Log.d(TAG, "startTrigger"); + + int captureSequenceId = mNextCaptureSequenceId.getAndIncrement(); + + RequestBuilder requestBuilder = new RequestBuilder(mCameraCharacteristics) + .addTargetOutputConfigId(PREVIEW_OUTPUT_ID) + .setTemplateId(CameraDevice.TEMPLATE_PREVIEW); + + if (mHasAnalysisOutput) { + requestBuilder.addTargetOutputConfigId(ANALYSIS_OUTPUT_ID); + } + applyParameters(requestBuilder); + + for (CaptureRequest.Key key : captureRequest.getKeys()) { + if (mSupportedRequestKeys.contains(key)) { + requestBuilder.setParameter(key, captureRequest.get(key)); + } else { + Log.e(TAG, "startTrigger: key " + key + " not supported"); + } + } + + mIRequestProcessor.submit(requestBuilder.build(), new IRequestCallback.Stub() { + @Override + public void onCaptureStarted(int requestId, long frameNumber, long timestamp) + throws RemoteException { + captureCallback.onCaptureStarted(captureSequenceId, timestamp); + } + + @Override + public void onCaptureProgressed(int requestId, CaptureResultWrapper partialResult) + throws RemoteException { + CaptureResult captureResult = partialResult.toCaptureResult(); + } + + @Override + public void onCaptureCompleted(int requestId, + TotalCaptureResultWrapper totalCaptureResult) throws RemoteException { + TotalCaptureResult captureResult = totalCaptureResult.toTotalCaptureResult(); + captureCallback.onCaptureProcessStarted(captureSequenceId); + notifyOnCaptureCompleted(captureSequenceId, captureResult, captureCallback); + } + + @Override + public void onCaptureFailed(int requestId, CaptureFailureWrapper captureFailureWrapper) + throws RemoteException { + CaptureFailure captureFailure = captureFailureWrapper.toCaptureFailure(); + captureCallback.onCaptureFailed(captureSequenceId); + } + + @Override + public void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId) + throws RemoteException { + captureCallback.onCaptureFailed(captureSequenceId); + } + + @Override + public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) + throws RemoteException { + captureCallback.onCaptureSequenceCompleted(captureSequenceId); + } + + @Override + public void onCaptureSequenceAborted(int sequenceId) throws RemoteException { + captureCallback.onCaptureSequenceAborted(captureSequenceId); + } + }); + return captureSequenceId; + } + + private void applyParameters(RequestBuilder requestBuilder) { + for (CaptureRequest.Key key : mParameters.keySet()) { + requestBuilder.setParameter(key, mParameters.get(key)); + } + } + + @Override + public int startRepeating(ICaptureCallback captureCallback) throws RemoteException { + Log.d(TAG, "startRepeating"); + + int captureSequenceId = mNextCaptureSequenceId.getAndIncrement(); + + RequestBuilder requestBuilder = new RequestBuilder(mCameraCharacteristics) + .addTargetOutputConfigId(PREVIEW_OUTPUT_ID) + .setTemplateId(CameraDevice.TEMPLATE_PREVIEW); + if (mHasAnalysisOutput) { + requestBuilder.addTargetOutputConfigId(ANALYSIS_OUTPUT_ID); + } + applyParameters(requestBuilder); + + mIRequestProcessor.setRepeating(requestBuilder.build(), new IRequestCallback.Stub() { + @Override + public void onCaptureStarted(int requestId, long frameNumber, long timestamp) + throws RemoteException { + captureCallback.onCaptureStarted(captureSequenceId, timestamp); + + } + + @Override + public void onCaptureProgressed(int requestId, CaptureResultWrapper partialResult) + throws RemoteException { + CaptureResult captureResult = partialResult.toCaptureResult(); + } + + @Override + public void onCaptureCompleted(int requestId, + TotalCaptureResultWrapper totalCaptureResult) throws RemoteException { + TotalCaptureResult captureResult = totalCaptureResult.toTotalCaptureResult(); + captureCallback.onCaptureProcessStarted(captureSequenceId); + notifyOnCaptureCompleted(captureSequenceId, captureResult, captureCallback); + } + + @Override + public void onCaptureFailed(int requestId, CaptureFailureWrapper captureFailureWrapper) + throws RemoteException { + CaptureFailure captureFailure = captureFailureWrapper.toCaptureFailure(); + captureCallback.onCaptureFailed(captureSequenceId); + } + + @Override + public void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId) + throws RemoteException { + captureCallback.onCaptureFailed(captureSequenceId); + } + + @Override + public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) + throws RemoteException { + captureCallback.onCaptureSequenceCompleted(captureSequenceId); + } + + @Override + public void onCaptureSequenceAborted(int sequenceId) throws RemoteException { + captureCallback.onCaptureSequenceAborted(captureSequenceId); + } + }); + return captureSequenceId; + } + + @Override + public void stopRepeating() throws RemoteException { + Log.d(TAG, "stopRepeating"); + + mIRequestProcessor.stopRepeating(); + } + + @Override + public int startCapture(ICaptureCallback captureCallback) throws RemoteException { + Log.d(TAG, "startCapture"); + + int captureSequenceId = mNextCaptureSequenceId.getAndIncrement(); + + RequestBuilder requestBuilder = new RequestBuilder(mCameraCharacteristics) + .addTargetOutputConfigId(CAPTURE_OUTPUT_ID) + .setTemplateId(CameraDevice.TEMPLATE_STILL_CAPTURE); + + applyParameters(requestBuilder); + + mIRequestProcessor.submit(requestBuilder.build(), new IRequestCallback.Stub() { + @Override + public void onCaptureStarted(int requestId, long frameNumber, long timestamp) + throws RemoteException { + captureCallback.onCaptureStarted(captureSequenceId, timestamp); + } + + @Override + public void onCaptureProgressed(int requestId, + CaptureResultWrapper captureResultWrapper) + throws RemoteException { + } + + @Override + public void onCaptureCompleted(int requestId, + TotalCaptureResultWrapper totalCaptureResultWrapper) throws RemoteException { + TotalCaptureResult captureResult = totalCaptureResultWrapper.toTotalCaptureResult(); + captureCallback.onCaptureProcessStarted(captureSequenceId); + notifyOnCaptureCompleted(captureSequenceId, captureResult, captureCallback); + } + + @Override + public void onCaptureFailed(int requestId, CaptureFailureWrapper captureFailureWrapper) + throws RemoteException { + captureCallback.onCaptureFailed(captureSequenceId); + } + + @Override + public void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId) + throws RemoteException { + captureCallback.onCaptureFailed(captureSequenceId); + } + + @Override + public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) + throws RemoteException { + captureCallback.onCaptureSequenceCompleted(captureSequenceId); + } + + @Override + public void onCaptureSequenceAborted(int sequenceId) throws RemoteException { + captureCallback.onCaptureSequenceAborted(captureSequenceId); + + } + }); + return captureSequenceId; + } + + @Override + public void abortCapture(int i) throws RemoteException { + Log.d(TAG, "abortCapture"); + mIRequestProcessor.abortCaptures(); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/Android.bp b/camera2/extensions/service_based_sample/oem_library/Android.bp new file mode 100644 index 00000000..adfb1b51 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2023 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library { + name: "service_based_camera_extensions", + installable: true, + srcs: [ + "src/java/**/*.java", + "src/java/**/*.aidl", + ], + static_libs: [ + "androidx.annotation_annotation", + ], + aidl: { + local_include_dirs: ["src/java"], + include_dirs: [ + "frameworks/native/aidl/gui", // For SurfaceView AIDL + "frameworks/av/camera/aidl", // For CaptureRequest AIDL + ] + }, + system_ext_specific: true, + platform_apis: true, +} + +prebuilt_etc { + name: "service_based_camera_extensions.xml", + src: "service_based_camera_extensions.xml", + sub_dir: "permissions", + system_ext_specific: true, +} diff --git a/camera2/extensions/service_based_sample/oem_library/AndroidManifest.xml b/camera2/extensions/service_based_sample/oem_library/AndroidManifest.xml new file mode 100644 index 00000000..54b5b10a --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + package="androidx.camera.extensions.impl" > +</manifest> diff --git a/camera2/extensions/service_based_sample/oem_library/service_based_camera_extensions.xml b/camera2/extensions/service_based_sample/oem_library/service_based_camera_extensions.xml new file mode 100644 index 00000000..6501b9b4 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/service_based_camera_extensions.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<permissions> + <library name="androidx.camera.extensions.impl" + file="/system_ext/framework/service_based_camera_extensions.jar"/> +</permissions> diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java new file mode 100755 index 00000000..55486a3c --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardImageCaptureExtender; + +import java.util.List; + +/** + * Stub implementation for auto image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class AutoImageCaptureExtenderImpl extends ForwardImageCaptureExtender { + public AutoImageCaptureExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_AUTOMATIC); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java new file mode 100755 index 00000000..94b8808a --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardPreviewExtender; + +import java.util.List; + +/** + * Stub implementation for auto preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class AutoPreviewExtenderImpl extends ForwardPreviewExtender { + public AutoPreviewExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_AUTOMATIC); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java new file mode 100755 index 00000000..ce9e9ea1 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardImageCaptureExtender; + +import java.util.List; + +/** + * Stub implementation for beauty image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class BeautyImageCaptureExtenderImpl extends ForwardImageCaptureExtender { + public BeautyImageCaptureExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_FACE_RETOUCH); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java new file mode 100755 index 00000000..0a452c33 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardPreviewExtender; + +import java.util.List; + +/** + * Stub implementation for beauty preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class BeautyPreviewExtenderImpl extends ForwardPreviewExtender { + public BeautyPreviewExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_FACE_RETOUCH); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java new file mode 100644 index 00000000..f24e9f1d --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardImageCaptureExtender; + +import java.util.List; + +/** + * Stub implementation for bokeh image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class BokehImageCaptureExtenderImpl extends ForwardImageCaptureExtender { + public BokehImageCaptureExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_BOKEH); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java new file mode 100644 index 00000000..e17d9415 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardPreviewExtender; + +import java.util.List; + +/** + * Stub implementation for bokeh preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class BokehPreviewExtenderImpl extends ForwardPreviewExtender { + public BokehPreviewExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_BOKEH); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java new file mode 100644 index 00000000..3eee146a --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java @@ -0,0 +1,66 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.annotation.SuppressLint; +import android.graphics.ImageFormat; +import android.hardware.camera2.TotalCaptureResult; +import android.media.Image; +import android.util.Pair; +import android.view.Surface; + +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * The interface for processing a set of {@link Image}s that have captured. + * + * @since 1.0 + */ +@SuppressLint("UnknownNullness") +public interface CaptureProcessorImpl extends ProcessorImpl { + /** + * Process a set images captured that were requested. + * + * <p> The result of the processing step should be written to the {@link Surface} that was + * received by {@link #onOutputSurface(Surface, int)}. + * + * @param results The map of {@link ImageFormat#YUV_420_888} format images and metadata to + * process. The {@link Image} that are contained within the map will become + * invalid after this method completes, so no references to them should be kept. + */ + void process(Map<Integer, Pair<Image, TotalCaptureResult>> results); + + /** + * Process a set images captured that were requested. + * + * <p> The result of the processing step should be written to the {@link Surface} that was + * received by {@link #onOutputSurface(Surface, int)}. + * + * @param results The map of {@link ImageFormat#YUV_420_888} format images and metadata + * to process. The {@link Image} that are contained within the map will + * become invalid after this method completes, so no references to them + * should be kept. + * @param resultCallback Capture result callback to be called once the capture result + * values of the processed image are ready. + * @param executor The executor to run the callback on. If null then the callback will + * run on any arbitrary executor. + * @since 1.3 + */ + void process(Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/CaptureStageImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/CaptureStageImpl.java new file mode 100644 index 00000000..c4f4a47d --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/CaptureStageImpl.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.hardware.camera2.CaptureRequest; +import android.util.Pair; + +import androidx.annotation.NonNull; + +import java.util.List; + +/** + * The set of parameters that defines a single capture that will be sent to the camera. + * + * @since 1.0 + */ +public interface CaptureStageImpl { + /** Returns the identifier for the {@link CaptureStageImpl}. */ + int getId(); + + /** + * Returns the set of {@link CaptureRequest.Key} and the corresponding values that will be + * set for a single {@link CaptureRequest}. + */ + @NonNull + List<Pair<CaptureRequest.Key, Object>> getParameters(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java new file mode 100644 index 00000000..a74a8804 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ExtenderStateListener.java @@ -0,0 +1,91 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.SessionConfiguration; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Provides interfaces that the OEM needs to implement to handle the state change. + * + * @since 1.0 + */ +public interface ExtenderStateListener { + + /** + * Notify to initialize the extension. This will be called after bindToLifeCycle. This is + * where the use case is started and would be able to allocate resources here. After onInit() is + * called, the camera ID, cameraCharacteristics and context will not change until onDeInit() + * has been called. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + * @param context The {@link Context} used for CameraX. + */ + void onInit(@NonNull String cameraId, @NonNull CameraCharacteristics cameraCharacteristics, + @NonNull Context context); + + /** + * Notify to de-initialize the extension. This callback will be invoked after unbind. + * After onDeInit() was called, it is expected that the camera ID, cameraCharacteristics will + * no longer hold, this should be where to clear all resources allocated for this use case. + */ + void onDeInit(); + + /** + * This will be invoked before creating a + * {@link CameraCaptureSession}. The {@link CaptureRequest} + * parameters returned via {@link CaptureStageImpl} will be passed to the camera device as + * part of the capture session initialization via + * {@link SessionConfiguration#setSessionParameters(CaptureRequest)} which only supported from + * API level 28. The valid parameter is a subset of the available capture request parameters. + * + * @return The request information to set the session wide camera parameters. + */ + @Nullable + CaptureStageImpl onPresetSession(); + + /** + * This will be invoked once after the {@link CameraCaptureSession} + * has been created. The {@link CaptureRequest} parameters returned via + * {@link CaptureStageImpl} will be used to generate a single request to the current + * configured {@link CameraDevice}. The generated request will be submitted to camera before + * processing other single requests. + * + * @return The request information to create a single capture request to camera device. + */ + @Nullable + CaptureStageImpl onEnableSession(); + + /** + * This will be invoked before the {@link CameraCaptureSession} is + * closed. The {@link CaptureRequest} parameters returned via {@link CaptureStageImpl} will + * be used to generate a single request to the currently configured {@link CameraDevice}. The + * generated request will be submitted to camera before the CameraCaptureSession is closed. + * + * @return The request information to customize the session. + */ + @Nullable + CaptureStageImpl onDisableSession(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java new file mode 100644 index 00000000..cc357b1a --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java @@ -0,0 +1,84 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import androidx.annotation.NonNull; + +/** + * Stub implementation for the extension version check. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public class ExtensionVersionImpl { + public ExtensionVersionImpl() { + } + + /** + * Provide the current CameraX extension library version to vendor library and vendor would + * need to return the supported version for this device. If the returned version is not + * supported by CameraX library, the Preview and ImageCapture would not be able to enable the + * specific effects provided by the vendor. + * + * <p>CameraX library provides the Semantic Versioning string in a form of + * MAJOR.MINOR.PATCH-description + * We will increment the + * MAJOR version when make incompatible API changes, + * MINOR version when add functionality in a backwards-compatible manner, and + * PATCH version when make backwards-compatible bug fixes. And the description can be ignored. + * + * <p>Vendor library should provide MAJOR.MINOR.PATCH to CameraX. The MAJOR and MINOR + * version is used to map to the version of CameraX that it supports, and CameraX extension + * would only available when MAJOR version is matched with CameraX current version. The PATCH + * version does not indicate compatibility. The patch version should be incremented whenever + * the vendor library makes bug fixes or updates to the algorithm. + * + * @param version the version of CameraX library formatted as MAJOR.MINOR.PATCH-description. + * @return the version that vendor supported in this device. The MAJOR.MINOR.PATCH format + * should be used. + */ + @SuppressWarnings("unused") + @NonNull + public String checkApiVersion(@NonNull String version) { + // This sample implements Extensions-Interface 1.3.0. + return "1.3.0"; + } + + /** + * Specify whether or not CameraX should invoke the AdvancedExtenderImpl instead of + * PreviewExtenderImpl/ImageCaptureExtenderImpl. + * + * <p>Starting from version 1.2, a set of alternative interfaces called advanced extender for + * implementing extensions are provided to OEMs as another option. OEMs can continue using + * previous interfaces (PreviewExtenderImpl/ImageCaptureExtenderImpl, also called basic + * extender). + * + * <p>OEMs should return false here if only basic extender is implemented. When returning true, + * CameraX will invoke the AdvancedExtenderImpl implementation in advanced package for all + * types of extension modes. + * + * <p>ExtensionVersionImpl, InitializerImpl will still be called for both basic and advanced + * extender implementation paths. + * + * @return true if AdvancedExtenderImpl is implemented + * @since 1.2 + */ + public boolean isAdvancedExtenderImplemented() { + return true; + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java new file mode 100644 index 00000000..264a714e --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardImageCaptureExtender; + +import java.util.List; + +/** + * Stub implementation for HDR image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class HdrImageCaptureExtenderImpl extends ForwardImageCaptureExtender { + public HdrImageCaptureExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_HDR); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java new file mode 100644 index 00000000..8e7bbd85 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardPreviewExtender; + +import java.util.List; + +/** + * Stub implementation for HDR preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class HdrPreviewExtenderImpl extends ForwardPreviewExtender { + public HdrPreviewExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_HDR); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java new file mode 100644 index 00000000..10b45134 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java @@ -0,0 +1,171 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Provides abstract methods that the OEM needs to implement to enable extensions for image capture. + * + * @since 1.0 + */ +public interface ImageCaptureExtenderImpl extends ExtenderStateListener { + /** + * Indicates whether the extension is supported on the device. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + * @return true if the extension is supported, otherwise false + */ + boolean isExtensionAvailable(@NonNull String cameraId, + @NonNull CameraCharacteristics cameraCharacteristics); + + /** + * Initializes the extender to be used with the specified camera. + * + * <p>This should be called before any other method on the extender. The exception is {@link + * #isExtensionAvailable(String, CameraCharacteristics)}. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + */ + void init(@NonNull String cameraId, @NonNull CameraCharacteristics cameraCharacteristics); + + /** + * The processing that will be done on a set of captures to create and image with the effect. + */ + @Nullable + CaptureProcessorImpl getCaptureProcessor(); + + /** The set of captures that are needed to create an image with the effect. */ + @Nullable + List<CaptureStageImpl> getCaptureStages(); + + /** + * Returns the maximum size of the list returned by {@link #getCaptureStages()}. + * @return the maximum count. + */ + int getMaxCaptureStage(); + + /** + * Returns the customized supported resolutions. + * + * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned. + * + * <p>The returned resolutions should be subset of the supported sizes retrieved from + * {@link StreamConfigurationMap} for the camera device. If the + * returned list is not null, it will be used to find the best resolutions combination for + * the bound use cases. + * + * @return the customized supported resolutions, or null to support all sizes retrieved from + * {@link StreamConfigurationMap}. + * @since 1.1 + */ + @Nullable + List<Pair<Integer, Size[]>> getSupportedResolutions(); + + /** + * Returns the estimated capture latency range in milliseconds for the target capture + * resolution. + * + * <p>This includes the time spent processing the multi-frame capture request along with any + * additional time for encoding of the processed buffer in the framework if necessary.</p> + * + * @param captureOutputSize size of the capture output surface. If it is null or not in the + * supported output sizes, maximum capture output size is used for + * the estimation. + * @return the range of estimated minimal and maximal capture latency in milliseconds, or + * null if no capture latency info can be provided. + * @since 1.2 + */ + @Nullable + Range<Long> getEstimatedCaptureLatencyRange(@Nullable Size captureOutputSize); + + /** + * Return a list of orthogonal capture request keys. + * + * <p>Any keys included in the list will be configurable by clients of the extension and will + * affect the extension functionality.</p> + * + * <p>Do note that the list of keys applies to {@link PreviewExtenderImpl} as well.</p> + * + * <p>Also note that the keys {@link CaptureRequest#JPEG_QUALITY} and + * {@link CaptureRequest#JPEG_ORIENTATION} are always supported regardless being added in the + * list or not. To support common camera operations like zoom, tap-to-focus, flash and + * exposure compensation, we recommend supporting the following keys if possible. + * <pre> + * zoom: {@link CaptureRequest#CONTROL_ZOOM_RATIO} + * {@link CaptureRequest#SCALER_CROP_REGION} + * tap-to-focus: + * {@link CaptureRequest#CONTROL_AF_MODE} + * {@link CaptureRequest#CONTROL_AF_TRIGGER} + * {@link CaptureRequest#CONTROL_AF_REGIONS} + * {@link CaptureRequest#CONTROL_AE_REGIONS} + * {@link CaptureRequest#CONTROL_AWB_REGIONS} + * flash: + * {@link CaptureRequest#CONTROL_AE_MODE} + * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER} + * {@link CaptureRequest#FLASH_MODE} + * exposure compensation: + * {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION} + * </pre> + * On basic extensions that implement 1.2 or prior version, the above keys are all supported + * explicitly. When migrating from 1.2 or prior to 1.3, please note that both CameraX and + * Camera2 will honor the returned list and support only the keys contained in it. For + * example, if OEM decides to return only {@link CaptureRequest#CONTROL_ZOOM_RATIO} and + * {@link CaptureRequest#SCALER_CROP_REGION} in the 1.3 implementation, it means only zoom is + * supported for the app while tap-to-focus , flash and exposure compensation are not allowed. + * + * @return List of supported orthogonal capture keys, or an empty list if no capture settings + * are not supported. + * @since 1.3 + */ + @NonNull + List<CaptureRequest.Key> getAvailableCaptureRequestKeys(); + + /** + * Return a list of supported capture result keys. + * + * <p>Any keys included in this list must be available as part of the registered + * {@link ProcessResultImpl} callback. In case frame processing is not supported, + * then the Camera2/CameraX framework will use the list to filter and notify camera clients + * using the respective camera results.</p> + * + * <p>At the very minimum, it is expected that the result key list is a superset of the + * capture request keys.</p> + * + * <p>Do note that the list of keys applies to {@link PreviewExtenderImpl} as well.</p> + * + * @return List of supported capture result keys, or an empty list if capture results are not + * supported. + * @since 1.3 + */ + @NonNull + List<CaptureResult.Key> getAvailableCaptureResultKeys(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/InitializerImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/InitializerImpl.java new file mode 100644 index 00000000..5a9f0ffc --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/InitializerImpl.java @@ -0,0 +1,115 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ServiceManager; + +import java.util.concurrent.Executor; + +/** + * Used for initializing the extensions library. + * + * @since 1.1 + */ +public class InitializerImpl { + private InitializerImpl() { + } + + /** An unknown error has occurred. */ + public static final int ERROR_UNKNOWN = 0; + + /** + * Error reported if the application version of extensions is incompatible with the on device + * library version. + */ + public static final int ERROR_INITIALIZE_VERSION_INCOMPATIBLE = 1; + + /** + * Initializes the {@link Context}. + * + * <p>Before this call has been made no calls to the extensions library should be made except + * for {@link ExtensionVersionImpl#checkApiVersion(String)}. + * + * @param version The version of the extension used by the application. + * @param context The {@link Context} of the calling application. + * @param executor The executor to run the callback on. If null then the callback will run on + * any arbitrary executor. + */ + public static void init(@NonNull String version, @NonNull Context context, + @NonNull OnExtensionsInitializedCallback callback, @Nullable Executor executor) { + ServiceManager.init(context, version, callback, executor); + } + + /** + * Deinitializes the extensions to release resources. + * + * <p>After this call has been made no calls to the extensions library should be made except + * for {@link ExtensionVersionImpl#checkApiVersion(String)}. + * + * @param executor The executor to run the callback on. If null then the callback will run on + * any arbitrary executor. + */ + public static void deinit(@NonNull OnExtensionsDeinitializedCallback callback, + @Nullable Executor executor) { + ServiceManager.getInstance().deinit(callback, executor); + } + + /** + * Callback that gets called when the library has finished initializing and is ready for used. + */ + public interface OnExtensionsInitializedCallback { + /** Called if the library successfully initializes. */ + void onSuccess(); + + /** + * Called if the library is unable to successfully initialize. + * + * @param error The reason for failing to initialize. + */ + void onFailure(int error); + } + + /** + * Callback that gets called when the library has finished deinitialized. + * + * <p> Once this interface has been called then + * {@link #init(String, Context, OnExtensionsInitializedCallback, Executor)} can be called + * again regardless of whether or not the deinitialization has succeeded or failed. + */ + public interface OnExtensionsDeinitializedCallback { + /** + * Called if the library successfully deinitializes. + */ + void onSuccess(); + + /** + * Called if the library encountered some error during the deinitialization. + * + * <p>Even if the library fails to deinitialize it is now valid for + * {@link #init(String, Context, OnExtensionsInitializedCallback, Executor)} to be called + * again. + * + * @param error The reason for failing to deinitialize. + */ + void onFailure(int error); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java new file mode 100755 index 00000000..36c554fb --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardImageCaptureExtender; + +import java.util.List; + +/** + * Stub implementation for night image capture use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class NightImageCaptureExtenderImpl extends ForwardImageCaptureExtender { + public NightImageCaptureExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_NIGHT); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java new file mode 100755 index 00000000..973e79cf --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraExtensionCharacteristics; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.serviceforward.ForwardPreviewExtender; + +import java.util.List; + +/** + * Stub implementation for night preview use case. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.0 + */ +public final class NightPreviewExtenderImpl extends ForwardPreviewExtender { + public NightPreviewExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_NIGHT); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java new file mode 100644 index 00000000..b3208137 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java @@ -0,0 +1,117 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.TotalCaptureResult; +import android.util.Pair; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +/** + * Provides abstract methods that the OEM needs to implement to enable extensions in the preview. + * + * @since 1.0 + */ +public interface PreviewExtenderImpl extends ExtenderStateListener { + /** The different types of the preview processing. */ + enum ProcessorType { + /** Processor which only updates the {@link CaptureStageImpl}. */ + PROCESSOR_TYPE_REQUEST_UPDATE_ONLY, + /** Processor which updates the received {@link android.media.Image}. */ + PROCESSOR_TYPE_IMAGE_PROCESSOR, + /** No processor, only a {@link CaptureStageImpl} is defined. */ + PROCESSOR_TYPE_NONE + } + + /** + * Indicates whether the extension is supported on the device. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + * @return true if the extension is supported, otherwise false + */ + boolean isExtensionAvailable(@NonNull String cameraId, + @NonNull CameraCharacteristics cameraCharacteristics); + + /** + * Initializes the extender to be used with the specified camera. + * + * <p>This should be called before any other method on the extender. The exception is {@link + * #isExtensionAvailable(String, CameraCharacteristics)}. + * + * @param cameraId The camera2 id string of the camera. + * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera. + */ + void init(@NonNull String cameraId, @NonNull CameraCharacteristics cameraCharacteristics); + + /** + * The set of parameters required to produce the effect on the preview stream. + * + * <p> This will be the initial set of parameters used for the preview + * {@link CaptureRequest}. If the {@link ProcessorType} is defined as + * {@link ProcessorType#PROCESSOR_TYPE_REQUEST_UPDATE_ONLY} then this will be updated when + * the {@link RequestUpdateProcessorImpl#process(TotalCaptureResult)} from {@link + * #getProcessor()} has been called, this should be updated to reflect the new {@link + * CaptureStageImpl}. If the processing step returns a {@code null}, meaning the required + * parameters has not changed, then calling this will return the previous non-null value. + */ + @NonNull + CaptureStageImpl getCaptureStage(); + + /** The type of preview processing to use. */ + @NonNull + ProcessorType getProcessorType(); + + /** + * Returns a processor which only updates the {@link CaptureStageImpl}. + * + * <p>The type of processor is dependent on the return of {@link #getProcessorType()}. The + * type of ProcessorImpl returned will be according to the following table. + * + * <table> + * <tr><th> ProcessorType </th> <th> ProcessorImpl </th> </tr> + * <tr><td> PROCESSOR_TYPE_REQUEST_UPDATE_ONLY </td> <td> RequestUpdateProcessorImpl </td> </tr> + * <tr><td> PROCESSOR_TYPE_IMAGE_PROCESSOR </td> <td> PreviewImageProcessorImpl </td> </tr> + * <tr><td> PROCESSOR_TYPE_NONE </td> <td> null </td> </tr> + * </table> + */ + @Nullable + ProcessorImpl getProcessor(); + + /** + * Returns the customized supported resolutions. + * + * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned. + * + * <p>The returned resolutions should be subset of the supported sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. If the + * returned list is not null, it will be used to find the best resolutions combination for + * the bound use cases. + * + * @return the customized supported resolutions. + * @since 1.1 + */ + @Nullable + List<Pair<Integer, Size[]>> getSupportedResolutions(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java new file mode 100644 index 00000000..f203ebad --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/PreviewImageProcessorImpl.java @@ -0,0 +1,64 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.annotation.SuppressLint; +import android.graphics.ImageFormat; +import android.hardware.camera2.TotalCaptureResult; +import android.media.Image; + +import java.util.concurrent.Executor; + +/** + * Processes a single {@link Image} and {@link TotalCaptureResult} to produce an output to a + * stream. + * + * @since 1.0 + */ +@SuppressLint("UnknownNullness") +public interface PreviewImageProcessorImpl extends ProcessorImpl { + /** + * Processes the requested image capture. + * + * <p> The result of the processing step should be written to the {@link android.view.Surface} + * that was received by {@link ProcessorImpl#onOutputSurface(android.view.Surface, int)}. + * + * @param image The {@link ImageFormat#YUV_420_888} format image to process. This will be + * invalid after the method completes so no reference to it should be kept. + * @param result The metadata associated with the image to process. + */ + void process(Image image, TotalCaptureResult result); + + /** + * Processes the requested image capture. + * + * <p> The result of the processing step should be written to the {@link android.view.Surface} + * that was received by {@link ProcessorImpl#onOutputSurface(android.view.Surface, int)}. + * + * @param image The {@link ImageFormat#YUV_420_888} format image to process. This will + * be invalid after the method completes so no reference to it should be + * kept. + * @param result The metadata associated with the image to process. + * @param resultCallback Capture result callback to be called once the capture result + * values of the processed image are ready. + * @param executor The executor to run the callback on. If null then the callback will + * run on any arbitrary executor. + * @since 1.3 + */ + void process(Image image, TotalCaptureResult result, ProcessResultImpl resultCallback, + Executor executor); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java new file mode 100644 index 00000000..d0e3605d --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ProcessResultImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CaptureResult; +import android.util.Pair; + +import java.util.List; + +/** + * Allows clients to receive information about the capture result values of processed frames. + * + * @since 1.3 + */ +@SuppressLint("UnknownNullness") +public interface ProcessResultImpl { + /** + * Capture result callback that needs to be called when the process capture results are + * ready as part of frame post-processing. + * + * @param shutterTimestamp The shutter time stamp of the processed frame. + * @param result Key value pairs for all supported capture results. Do note that + * if results 'android.jpeg.quality' and 'android.jpeg.orientation' + * are present in the process capture input results, then the values + * must also be passed as part of this callback. Both Camera2 and + * CameraX guarantee that those two settings and results are always + * supported and applied by the corresponding framework. + */ + void onCaptureCompleted(long shutterTimestamp, List<Pair<CaptureResult.Key, Object>> result); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ProcessorImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ProcessorImpl.java new file mode 100644 index 00000000..e5ca19e9 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/ProcessorImpl.java @@ -0,0 +1,57 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.NonNull; + +/** + * Processes an input image stream and produces an output image stream. + * + * @since 1.0 + */ +public interface ProcessorImpl { + /** + * Updates where the ProcessorImpl should write the output to. + * + * @param surface The {@link Surface} that the ProcessorImpl should write data into. + * @param imageFormat The format of that the surface expects. + */ + void onOutputSurface(@NonNull Surface surface, int imageFormat); + + /** + * Invoked when CameraX changes the configured output resolution. + * + * <p>After this call, {@link CaptureProcessorImpl} should expect any + * {@link android.media.Image} received as input to be at the specified resolution. + * + * @param size for the surface. + */ + void onResolutionUpdate(@NonNull Size size); + + /** + * Invoked when CameraX changes the configured input image format. + * + * <p>After this call, {@link CaptureProcessorImpl} should expect any + * {@link android.media.Image} received as input to have the specified image format. + * + * @param imageFormat for the surface. + */ + void onImageFormatUpdate(int imageFormat); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java new file mode 100644 index 00000000..ac3bfb3e --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/RequestUpdateProcessorImpl.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019 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 androidx.camera.extensions.impl; + +import android.hardware.camera2.TotalCaptureResult; + +import androidx.annotation.Nullable; + +/** + * Processes a {@link TotalCaptureResult} to update a CaptureStage. + * + * @since 1.0 + */ +public interface RequestUpdateProcessorImpl extends ProcessorImpl { + /** + * Process the {@link TotalCaptureResult} to update the {@link CaptureStageImpl} + * + * @param result The metadata associated with the image. Can be null if the image and meta have + * not been synced. + * @return The updated parameters used for the repeating requests. If this is {@code null} then + * the previous parameters will be used. + */ + @Nullable + CaptureStageImpl process(@Nullable TotalCaptureResult result); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java new file mode 100644 index 00000000..465bfe88 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java @@ -0,0 +1,188 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Range; +import android.util.Size; + +import androidx.camera.extensions.impl.ExtensionVersionImpl; + +import java.util.List; +import java.util.Map; + +/** + * Advanced OEM contract for implementing Extensions. ImageCapture/Preview Extensions are both + * implemented on this interface. + * + * <p>This advanced OEM contract empowers OEM to gain access to more Camera2 capability. This + * includes: (1) Add custom surfaces with specific formats like YUV, RAW, RAW_DEPTH. (2) Access to + * the capture request callbacks as well as all the images retrieved of various image formats. (3) + * Able to triggers single or repeating request with the capabilities to specify target surfaces, + * template id and parameters. + * + * <p>OEM needs to implement it with class name HdrAdvancedExtenderImpl for HDR, + * NightAdvancedExtenderImpl for night mode, BeautyAdvancedExtenderImpl for beauty mode, + * BokehAdvancedExtenderImpl for bokeh mode and AutoAdvancedExtenderImpl for auto mode. + * + * <p>OEMs are required to return true in + * {@link ExtensionVersionImpl#isAdvancedExtenderImplemented()} in order to request CameraX to + * use advanced extender over basic extender. OEM is okay to implement advanced + * extender only Or basic extender only. However the caveat of advanced-only implementation is, + * extensions will be unavailable on the apps using interfaces prior to 1.2. + * + * @since 1.2 + */ +@SuppressLint("UnknownNullness") +public interface AdvancedExtenderImpl { + + /** + * Indicates whether the extension is supported on the device. + * + * @param cameraId The camera2 id string of the camera. + * @param characteristicsMap A map consisting of the camera ids and the + * {@link CameraCharacteristics}s. For every camera, the map + * contains at least the CameraCharacteristics for the camera id. + * If the camera is logical camera, it will also contain associated + * physical camera ids and their CameraCharacteristics. + * @return true if the extension is supported, otherwise false + */ + boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap); + + /** + * Initializes the extender to be used with the specified camera. + * + * <p>This should be called before any other method on the extender. The exception is {@link + * #isExtensionAvailable}. + * + * @param cameraId The camera2 id string of the camera. + * @param characteristicsMap A map consisting of the camera ids and the + * {@link CameraCharacteristics}s. For every camera, the map + * contains at least the CameraCharacteristics for the camera id. + * If the camera is logical camera, it will also contain associated + * physical camera ids and their CameraCharacteristics. + */ + void init(String cameraId, Map<String, CameraCharacteristics> characteristicsMap); + + /** + * Returns the estimated capture latency range in milliseconds for the + * target capture resolution during the calls to + * {@link SessionProcessorImpl#startCapture}. This + * includes the time spent processing the multi-frame capture request along with any additional + * time for encoding of the processed buffer in the framework if necessary. + * + * @param cameraId the camera id + * @param captureOutputSize size of the capture output surface. If it is null or not in the + * supported output sizes, maximum capture output size is used for + * the estimation. + * @param imageFormat the image format of the capture output surface. + * @return the range of estimated minimal and maximal capture latency in milliseconds. + * Returns null if no capture latency info can be provided. + */ + Range<Long> getEstimatedCaptureLatencyRange(String cameraId, + Size captureOutputSize, int imageFormat); + + /** + * Returns supported output format/size map for preview. The format could be PRIVATE or + * YUV_420_888. OEM must support PRIVATE format at least. CameraX will only use resolutions + * for preview from the list. + * + * <p>The preview surface format in the CameraCaptureSession may not be identical to the + * supported preview output format returned here. Like in the basic extender interface, the + * preview PRIVATE surface could be added to the CameraCaptureSession and OEM processes it in + * the HAL. Alternatively OEM can configure a intermediate YUV surface of the same size and + * writes the output to the preview output surface. + */ + Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(String cameraId); + + /** + * Returns supported output format/size map for image capture. OEM is required to support + * both JPEG and YUV_420_888 format output. + * + * <p>Like in the basic extender interface, the surface created with this supported + * format/size could be either added in CameraCaptureSession with HAL processing OR it + * configures intermediate surfaces(YUV/RAW..) and writes the output to the output surface. + */ + Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId); + + /** + * Returns supported output sizes for Image Analysis (YUV_420_888 format). + * + * <p>OEM can optionally support a YUV surface for ImageAnalysis along with Preview/ImageCapture + * output surfaces. If imageAnalysis YUV surface is not supported, OEM should return null or + * empty list. + */ + List<Size> getSupportedYuvAnalysisResolutions(String cameraId); + + /** + * Returns a processor for activating extension sessions. It implements all the interactions + * required for starting a extension and cleanup. + */ + SessionProcessorImpl createSessionProcessor(); + + /** + * Returns a list of orthogonal capture request keys. + * + * <p>Any keys included in the list will be configurable by clients of the extension and will + * affect the extension functionality.</p> + * + * <p>Please note that the keys {@link CaptureRequest#JPEG_QUALITY} and + * {@link CaptureRequest#JPEG_ORIENTATION} are always supported regardless being added in the + * list or not. To support common camera operations like zoom, tap-to-focus, flash and + * exposure compensation, we recommend supporting the following keys if possible. + * <pre> + * zoom: {@link CaptureRequest#CONTROL_ZOOM_RATIO} + * {@link CaptureRequest#SCALER_CROP_REGION} + * tap-to-focus: + * {@link CaptureRequest#CONTROL_AF_MODE} + * {@link CaptureRequest#CONTROL_AF_TRIGGER} + * {@link CaptureRequest#CONTROL_AF_REGIONS} + * {@link CaptureRequest#CONTROL_AE_REGIONS} + * {@link CaptureRequest#CONTROL_AWB_REGIONS} + * flash: + * {@link CaptureRequest#CONTROL_AE_MODE} + * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER} + * {@link CaptureRequest#FLASH_MODE} + * exposure compensation: + * {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION} + * </pre> + * + * @return List of supported orthogonal capture keys, or an empty list if no capture settings + * are not supported. + * @since 1.3 + */ + List<CaptureRequest.Key> getAvailableCaptureRequestKeys(); + + /** + * Returns a list of supported capture result keys. + * + * <p>Any keys included in this list must be available as part of the registered + * {@link SessionProcessorImpl.CaptureCallback#onCaptureCompleted} callback.</p> + * + * <p>At the very minimum, it is expected that the result key list is a superset of the + * capture request keys.</p> + * + * @return List of supported capture result keys, or + * an empty list if capture results are not supported. + * @since 1.3 + */ + List<CaptureResult.Key> getAvailableCaptureResultKeys(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java new file mode 100644 index 00000000..c67dc799 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java @@ -0,0 +1,36 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraExtensionCharacteristics; + +import androidx.camera.extensions.impl.serviceforward.ForwardAdvancedExtender; + +/** + * Stub advanced extender implementation for auto. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.2 + */ +@SuppressLint("UnknownNullness") +public class AutoAdvancedExtenderImpl extends ForwardAdvancedExtender { + public AutoAdvancedExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_AUTOMATIC); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java new file mode 100644 index 00000000..2e10e255 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java @@ -0,0 +1,36 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraExtensionCharacteristics; + +import androidx.camera.extensions.impl.serviceforward.ForwardAdvancedExtender; + +/** + * Stub advanced extender implementation for beauty. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.2 + */ +@SuppressLint("UnknownNullness") +public class BeautyAdvancedExtenderImpl extends ForwardAdvancedExtender { + public BeautyAdvancedExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_BEAUTY); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java new file mode 100644 index 00000000..ae55790b --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java @@ -0,0 +1,36 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraExtensionCharacteristics; + +import androidx.camera.extensions.impl.serviceforward.ForwardAdvancedExtender; + +/** + * Stub advanced extender implementation for bokeh. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.2 + */ +@SuppressLint("UnknownNullness") +public class BokehAdvancedExtenderImpl extends ForwardAdvancedExtender { + public BokehAdvancedExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_BOKEH); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java new file mode 100644 index 00000000..68de01b6 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImpl.java @@ -0,0 +1,51 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; + +import java.util.List; + +/** + * A config representing a {@link android.hardware.camera2.params.OutputConfiguration} where + * Surface will be created by the information in this config. + */ +@SuppressLint("UnknownNullness") +public interface Camera2OutputConfigImpl { + /** + * Gets thd id of this output config. The id can be used to identify the stream in vendor + * implementations. + */ + int getId(); + + /** + * Gets the surface group id. Vendor can use the surface group id to share memory between + * Surfaces. + */ + int getSurfaceGroupId(); + + /** + * Gets the physical camera id. Returns null if not specified. + */ + String getPhysicalCameraId(); + + /** + * If non-null, enable surface sharing and add the surface constructed by the return + * Camera2OutputConfig. + */ + List<Camera2OutputConfigImpl> getSurfaceSharingOutputConfigs(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java new file mode 100644 index 00000000..718870f4 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2OutputConfigImplBuilder.java @@ -0,0 +1,243 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.params.OutputConfiguration; +import android.util.Size; +import android.view.Surface; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A builder implementation to help OEM build the {@link Camera2OutputConfigImpl} instance. + */ +@SuppressLint("UnknownNullness") +public class Camera2OutputConfigImplBuilder { + static AtomicInteger sLastId = new AtomicInteger(0); + private OutputConfigImplImpl mOutputConfig; + private int mSurfaceGroupId = OutputConfiguration.SURFACE_GROUP_ID_NONE; + private int mOutputConfigId = -1; + private String mPhysicalCameraId; + private List<Camera2OutputConfigImpl> mSurfaceSharingConfigs; + + private Camera2OutputConfigImplBuilder(OutputConfigImplImpl outputConfig) { + mOutputConfig = outputConfig; + } + + private int getNextId() { + return sLastId.getAndIncrement(); + } + + /** + * Creates a {@link Camera2OutputConfigImpl} that represents a {@link android.media.ImageReader} + * with the given parameters. + */ + public static Camera2OutputConfigImplBuilder newImageReaderConfig( + Size size, int imageFormat, int maxImages) { + return new Camera2OutputConfigImplBuilder( + new ImageReaderOutputConfigImplImpl(size, imageFormat, maxImages)); + } + + /** + * Creates a {@link Camera2OutputConfigImpl} that represents a MultiResolutionImageReader with + * the given parameters. + */ + public static Camera2OutputConfigImplBuilder newMultiResolutionImageReaderConfig( + int imageFormat, int maxImages) { + return new Camera2OutputConfigImplBuilder( + new MultiResolutionImageReaderOutputConfigImplImpl(imageFormat, maxImages)); + } + + /** + * Creates a {@link Camera2OutputConfigImpl} that contains the Surface directly. + */ + public static Camera2OutputConfigImplBuilder newSurfaceConfig(Surface surface) { + return new Camera2OutputConfigImplBuilder(new SurfaceOutputConfigImplImpl(surface)); + } + + /** + * Adds a {@link Camera2SessionConfigImpl} to be shared with current config. + */ + public Camera2OutputConfigImplBuilder addSurfaceSharingOutputConfig( + Camera2OutputConfigImpl camera2OutputConfig) { + if (mSurfaceSharingConfigs == null) { + mSurfaceSharingConfigs = new ArrayList<>(); + } + + mSurfaceSharingConfigs.add(camera2OutputConfig); + return this; + } + + /** + * Sets a physical camera id. + */ + public Camera2OutputConfigImplBuilder setPhysicalCameraId(String physicalCameraId) { + mPhysicalCameraId = physicalCameraId; + return this; + } + + /** + * Sets surface group id. + */ + public Camera2OutputConfigImplBuilder setSurfaceGroupId(int surfaceGroupId) { + mSurfaceGroupId = surfaceGroupId; + return this; + } + + /** + * Sets output config id. + */ + public Camera2OutputConfigImplBuilder setOutputConfigId(int outputConfigId) { + mOutputConfigId = outputConfigId; + return this; + } + + + /** + * Build a {@link Camera2OutputConfigImpl} instance. + */ + public Camera2OutputConfigImpl build() { + // Use the Atomic Integer if setOutputConfigId is never called + if(mOutputConfigId == -1) { + mOutputConfig.setId(getNextId()); + } + else { + mOutputConfig.setId(mOutputConfigId); + } + mOutputConfig.setPhysicalCameraId(mPhysicalCameraId); + mOutputConfig.setSurfaceGroup(mSurfaceGroupId); + mOutputConfig.setSurfaceSharingConfigs(mSurfaceSharingConfigs); + return mOutputConfig; + } + + private static class OutputConfigImplImpl implements Camera2OutputConfigImpl { + private int mId; + private int mSurfaceGroup; + private String mPhysicalCameraId; + private List<Camera2OutputConfigImpl> mSurfaceSharingConfigs; + + OutputConfigImplImpl() { + mId = -1; + mSurfaceGroup = 0; + mPhysicalCameraId = null; + mSurfaceSharingConfigs = null; + } + + @Override + public int getId() { + return mId; + } + + @Override + public int getSurfaceGroupId() { + return mSurfaceGroup; + } + + @Override + public String getPhysicalCameraId() { + return mPhysicalCameraId; + } + + @Override + public List<Camera2OutputConfigImpl> getSurfaceSharingOutputConfigs() { + return mSurfaceSharingConfigs; + } + + public void setId(int id) { + mId = id; + } + + public void setSurfaceGroup(int surfaceGroup) { + mSurfaceGroup = surfaceGroup; + } + + public void setPhysicalCameraId(String physicalCameraId) { + mPhysicalCameraId = physicalCameraId; + } + + public void setSurfaceSharingConfigs( + List<Camera2OutputConfigImpl> surfaceSharingConfigs) { + mSurfaceSharingConfigs = surfaceSharingConfigs; + } + } + + private static class SurfaceOutputConfigImplImpl extends OutputConfigImplImpl + implements SurfaceOutputConfigImpl { + private Surface mSurface; + + SurfaceOutputConfigImplImpl(Surface surface) { + mSurface = surface; + } + + @Override + public Surface getSurface() { + return mSurface; + } + } + + private static class ImageReaderOutputConfigImplImpl extends OutputConfigImplImpl + implements ImageReaderOutputConfigImpl { + private Size mSize; + private int mImageFormat; + private int mMaxImages; + + ImageReaderOutputConfigImplImpl(Size size, int imageFormat, int maxImages) { + mSize = size; + mImageFormat = imageFormat; + mMaxImages = maxImages; + } + + @Override + public Size getSize() { + return mSize; + } + + @Override + public int getImageFormat() { + return mImageFormat; + } + + @Override + public int getMaxImages() { + return mMaxImages; + } + } + + private static class MultiResolutionImageReaderOutputConfigImplImpl extends OutputConfigImplImpl + implements MultiResolutionImageReaderOutputConfigImpl { + private int mImageFormat; + private int mMaxImages; + + MultiResolutionImageReaderOutputConfigImplImpl(int imageFormat, int maxImages) { + mImageFormat = imageFormat; + mMaxImages = maxImages; + } + + @Override + public int getImageFormat() { + return mImageFormat; + } + + @Override + public int getMaxImages() { + return mMaxImages; + } + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java new file mode 100644 index 00000000..d1217177 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CaptureRequest; + +import java.util.List; +import java.util.Map; + +/** + * A config representing a {@link android.hardware.camera2.params.SessionConfiguration} + */ +@SuppressLint("UnknownNullness") +public interface Camera2SessionConfigImpl { + /** + * Returns all the {@link Camera2OutputConfigImpl}s that will be used to create + * {@link android.hardware.camera2.params.OutputConfiguration}. + */ + List<Camera2OutputConfigImpl> getOutputConfigs(); + + /** + * Gets all the parameters to create the session parameters with. + */ + Map<CaptureRequest.Key<?>, Object> getSessionParameters(); + + /** + * Gets the template id used for creating {@link CaptureRequest}s to be passed in + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters}. + */ + int getSessionTemplateId(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java new file mode 100644 index 00000000..a3011666 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java @@ -0,0 +1,123 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A builder implementation to help OEM build the {@link Camera2SessionConfigImpl} instance. + */ +@SuppressLint("UnknownNullness") +public class Camera2SessionConfigImplBuilder { + private int mSessionTemplateId = CameraDevice.TEMPLATE_PREVIEW; + Map<CaptureRequest.Key<?>, Object> mSessionParameters = new HashMap<>(); + List<Camera2OutputConfigImpl> mCamera2OutputConfigs = new ArrayList<>(); + + public Camera2SessionConfigImplBuilder() { + } + + /** + * Adds a output config. + */ + public Camera2SessionConfigImplBuilder addOutputConfig( + Camera2OutputConfigImpl outputConfig) { + mCamera2OutputConfigs.add(outputConfig); + return this; + } + + /** + * Sets session parameters. + */ + public <T> Camera2SessionConfigImplBuilder addSessionParameter( + CaptureRequest.Key<T> key, T value) { + mSessionParameters.put(key, value); + return this; + } + + /** + * Sets the template id for session parameters request. + */ + public Camera2SessionConfigImplBuilder setSessionTemplateId(int templateId) { + mSessionTemplateId = templateId; + return this; + } + + /** + * Gets the session template id. + */ + public int getSessionTemplateId() { + return mSessionTemplateId; + } + + /** + * Gets the session parameters. + */ + public Map<CaptureRequest.Key<?>, Object> getSessionParameters() { + return mSessionParameters; + } + + /** + * Gets all the output configs. + */ + public List<Camera2OutputConfigImpl> getCamera2OutputConfigs() { + return mCamera2OutputConfigs; + } + + /** + * Builds a {@link Camera2SessionConfigImpl} instance. + */ + public Camera2SessionConfigImpl build() { + return new Camera2SessionConfigImplImpl(this); + } + + private static class Camera2SessionConfigImplImpl implements + Camera2SessionConfigImpl { + int mSessionTemplateId; + Map<CaptureRequest.Key<?>, Object> mSessionParameters; + List<Camera2OutputConfigImpl> mCamera2OutputConfigs; + + Camera2SessionConfigImplImpl(Camera2SessionConfigImplBuilder builder) { + mSessionTemplateId = builder.getSessionTemplateId(); + mSessionParameters = builder.getSessionParameters(); + mCamera2OutputConfigs = builder.getCamera2OutputConfigs(); + } + + @Override + public List<Camera2OutputConfigImpl> getOutputConfigs() { + return mCamera2OutputConfigs; + } + + @Override + public Map<CaptureRequest.Key<?>, Object> getSessionParameters() { + return mSessionParameters; + } + + @Override + public int getSessionTemplateId() { + return mSessionTemplateId; + } + } +} + diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java new file mode 100644 index 00000000..e7dbb7d8 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java @@ -0,0 +1,36 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraExtensionCharacteristics; + +import androidx.camera.extensions.impl.serviceforward.ForwardAdvancedExtender; + +/** + * Stub advanced extender implementation for hdr. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.2 + */ +@SuppressLint("UnknownNullness") +public class HdrAdvancedExtenderImpl extends ForwardAdvancedExtender { + public HdrAdvancedExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_HDR); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java new file mode 100644 index 00000000..037e9479 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; + +/** + * A interface to receive and process the upcoming next available Image. + * + * <p>Implemented by OEM. + */ +@SuppressLint("UnknownNullness") +public interface ImageProcessorImpl { + /** + * The reference count will not be decremented when this method returns. Extensions must + * decrement it when the image is no longer needed. + * + * <p>If OEM is not closing(decrement) the image fast enough, the imageReference passed + * in this method might contain null image meaning that the Image was closed to prevent + * preview from stalling. + * + * @param outputConfigId the id of {@link Camera2OutputConfigImpl} which identifies + * corresponding Surface + * @param timestampNs the timestamp in nanoseconds associated with this image + * @param imageReference A reference to the {@link android.media.Image} which might contain + * null if OEM close(decrement) the image too slowly + * @param physicalCameraId used to distinguish which physical camera id the image comes from + * when the output configuration is + * MultiResolutionImageReaderOutputConfigImpl. It is also set if + * physicalCameraId is set in other Camera2OutputConfigImpl types. + * + */ + void onNextImageAvailable( + int outputConfigId, + long timestampNs, + ImageReferenceImpl imageReference, + String physicalCameraId + ); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java new file mode 100644 index 00000000..ca4dcafa --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/ImageReaderOutputConfigImpl.java @@ -0,0 +1,41 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.util.Size; + +/** + * Surface will be created by constructing a ImageReader. + */ +@SuppressLint("UnknownNullness") +public interface ImageReaderOutputConfigImpl extends Camera2OutputConfigImpl { + /** + * Returns the size of the surface. + */ + Size getSize(); + + /** + * Gets the image format of the surface. + */ + int getImageFormat(); + + /** + * Gets the capacity for TYPE_IMAGEREADER. + */ + int getMaxImages(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java new file mode 100644 index 00000000..95f2c3b9 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/ImageReferenceImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.media.Image; + +/** + * A Image reference container that enables the Image sharing between Camera2/CameraX and OEM + * using reference counting. The wrapped Image will be closed once the reference count + * reaches 0. + * + * <p>Implemented by Camera2/CameraX. + */ +@SuppressLint("UnknownNullness") +public interface ImageReferenceImpl { + + /** + * Increment the reference count. Returns true if the value was incremented. + * (returns false if the reference count has already reached zero.) + */ + boolean increment(); + + /** + * Decrement the reference count. Image will be closed if reference count reaches 0. + * Returns true if the value was decremented (returns false if the reference count has + * already reached zero) + */ + boolean decrement(); + + /** + * Return the Android image. This object MUST not be closed directly. + * Returns null when the reference count is zero. + */ + Image get(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/MultiResolutionImageReaderOutputConfigImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/MultiResolutionImageReaderOutputConfigImpl.java new file mode 100644 index 00000000..c3ad61bc --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/MultiResolutionImageReaderOutputConfigImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +/** + * Surface will be created by constructing a MultiResolutionImageReader. + */ +public interface MultiResolutionImageReaderOutputConfigImpl extends Camera2OutputConfigImpl { + /** + * Gets the image format of the surface. + */ + int getImageFormat(); + + /** + * Gets the max images of the ImageReader. + */ + int getMaxImages(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java new file mode 100644 index 00000000..be298443 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java @@ -0,0 +1,36 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CameraExtensionCharacteristics; + +import androidx.camera.extensions.impl.serviceforward.ForwardAdvancedExtender; + +/** + * Stub advanced extender implementation for night. + * + * <p>This class should be implemented by OEM and deployed to the target devices. + * + * @since 1.2 + */ +@SuppressLint("UnknownNullness") +public class NightAdvancedExtenderImpl extends ForwardAdvancedExtender { + public NightAdvancedExtenderImpl() { + super(CameraExtensionCharacteristics.EXTENSION_NIGHT); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java new file mode 100644 index 00000000..f6920296 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.util.Size; +import android.view.Surface; + +/** + * For specifying output surface of the extension. + */ +@SuppressLint("UnknownNullness") +public interface OutputSurfaceImpl { + /** + * Gets the surface. + */ + Surface getSurface(); + + /** + * Gets the size. + */ + Size getSize(); + + /** + * Gets the image format. + */ + int getImageFormat(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java new file mode 100644 index 00000000..51853334 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/RequestProcessorImpl.java @@ -0,0 +1,123 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; + +import java.util.List; +import java.util.Map; + +/** + * An Interface to execute Camera2 capture requests. + */ +@SuppressLint("UnknownNullness") +public interface RequestProcessorImpl { + /** + * Sets a {@link ImageProcessorImpl} to receive {@link ImageReferenceImpl} to process. + */ + void setImageProcessor(int outputconfigId, ImageProcessorImpl imageProcessor); + + /** + * Submits a request. + * @return the id of the capture sequence or -1 in case the processor encounters a fatal error + * or receives an invalid argument. + */ + int submit(Request request, Callback callback); + + /** + * Submits a list of requests. + * @return the id of the capture sequence or -1 in case the processor encounters a fatal error + * or receives an invalid argument. + */ + int submit(List<Request> requests, Callback callback); + + /** + * Set repeating requests. + * @return the id of the capture sequence or -1 in case the processor encounters a fatal error + * or receives an invalid argument. + */ + int setRepeating(Request request, Callback callback); + + + /** + * Abort captures. + */ + void abortCaptures(); + + /** + * Stop Repeating. + */ + void stopRepeating(); + + /** + * A interface representing a capture request configuration used for submitting requests in + * {@link RequestProcessorImpl}. + */ + interface Request { + /** + * Gets the target ids of {@link Camera2OutputConfigImpl} which identifies corresponding + * Surface to be the targeted for the request. + */ + List<Integer> getTargetOutputConfigIds(); + + /** + * Gets all the parameters. + */ + Map<CaptureRequest.Key<?>, Object> getParameters(); + + /** + * Gets the template id. + */ + Integer getTemplateId(); + } + + /** + * Callback to be invoked during the capture. + */ + interface Callback { + void onCaptureStarted( + Request request, + long frameNumber, + long timestamp); + + void onCaptureProgressed( + Request request, + CaptureResult partialResult); + + void onCaptureCompleted( + Request request, + TotalCaptureResult totalCaptureResult); + + void onCaptureFailed( + Request request, + CaptureFailure captureFailure); + + void onCaptureBufferLost( + Request request, + long frameNumber, + int outputStreamId); + + void onCaptureSequenceCompleted(int sequenceId, long frameNumber); + + void onCaptureSequenceAborted(int sequenceId); + + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java new file mode 100644 index 00000000..fabfc2bf --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java @@ -0,0 +1,281 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.view.Surface; + +import java.util.Map; + +/** + * Interface for creating Camera2 CameraCaptureSessions with extension enabled based on + * advanced vendor implementation. + * + * <p><pre> + * The flow of a extension session is shown below: + * (1) {@link #initSession}: CameraX prepares streams configuration for creating + * CameraCaptureSession. Output surfaces for Preview, ImageCapture and ImageAnalysis are passed + * in and vendor is responsible for outputting the results to these surfaces. + * + * (2) {@link #onCaptureSessionStart}: It is called after CameraCaptureSession is configured. + * A {@link RequestProcessorImpl} is passed for vendor to send repeating requests and + * single requests. + * + * (3) {@link #startRepeating}: CameraX will call this method to start the repeating request + * after CameraCaptureSession is called. Vendor should start the repeating request by + * {@link RequestProcessorImpl}. Vendor can also update the repeating request if needed later. + * + * (4) {@link #setParameters(Map)}: The passed parameters will be attached to the repeating request + * and single requests but vendor can choose to apply some of them only. + * + * (5) {@link #startCapture(CaptureCallback)}: It is called when apps want to + * start a multi-frame image capture. {@link CaptureCallback} will be called + * to report the status and the output image will be written to the capture output surface + * specified in {@link #initSession}. + * + * (5) {@link #onCaptureSessionEnd}: It is called right BEFORE CameraCaptureSession.close() is + * called. + * + * (6) {@link #deInitSession}: called when CameraCaptureSession is closed. + * </pre> + */ +@SuppressLint("UnknownNullness") +public interface SessionProcessorImpl { + /** + * Initializes the session for the extension. This is where the OEMs allocate resources for + * preparing a CameraCaptureSession. After initSession() is called, the camera ID, + * cameraCharacteristics and context will not change until deInitSession() has been called. + * + * <p>CameraX specifies the output surface configurations for preview, image capture and image + * analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which consists of a + * list of {@link Camera2OutputConfigImpl} and session parameters. The + * {@link Camera2SessionConfigImpl} will be used to configure the CameraCaptureSession. + * + * <p>OEM is responsible for outputting correct camera images output to these output surfaces. + * OEM can have the following options to enable the output: + * <pre> + * (1) Add these output surfaces in CameraCaptureSession directly using + * {@link Camera2OutputConfigImplBuilder#newSurfaceConfig(Surface)} }. Processing is done in + * HAL. + * + * (2) Use surface sharing with other surface by calling + * {@link Camera2OutputConfigImplBuilder#addSurfaceSharingOutputConfig(Camera2OutputConfigImpl)} + * to add the output surface to the other {@link Camera2OutputConfigImpl}. + * + * (3) Process output from other surfaces (RAW, YUV..) and write the result to the output + * surface. The output surface won't be contained in the returned + * {@link Camera2SessionConfigImpl}. + * </pre> + * + * <p>{@link Camera2OutputConfigImplBuilder} and {@link Camera2SessionConfigImplBuilder} + * implementations are provided in the stub for OEM to construct the + * {@link Camera2OutputConfigImpl} and {@link Camera2SessionConfigImpl} instances. + * + * @param previewSurfaceConfig output surface for preview + * @param imageCaptureSurfaceConfig output surface for image capture. + * @param imageAnalysisSurfaceConfig an optional output config for image analysis + * (YUV_420_888). + * @return a {@link Camera2SessionConfigImpl} consisting of a list of + * {@link Camera2OutputConfigImpl} and session parameters which will decide the + * {@link android.hardware.camera2.params.SessionConfiguration} for configuring the + * CameraCaptureSession. Please note that the OutputConfiguration list may not be part of any + * supported or mandatory stream combination BUT OEM must ensure this list will always + * produce a valid camera capture session. + */ + Camera2SessionConfigImpl initSession( + String cameraId, + Map<String, CameraCharacteristics> cameraCharacteristicsMap, + Context context, + OutputSurfaceImpl previewSurfaceConfig, + OutputSurfaceImpl imageCaptureSurfaceConfig, + OutputSurfaceImpl imageAnalysisSurfaceConfig); + + /** + * Notify to de-initialize the extension. This callback will be invoked after + * CameraCaptureSession is closed. After onDeInit() was called, it is expected that the + * camera ID, cameraCharacteristics will no longer hold and tear down any resources allocated + * for this extension. Aborts all pending captures. + */ + void deInitSession(); + + /** + * CameraX / Camera2 would call these API’s to pass parameters from the app to the OEM. It’s + * expected that the OEM would (eventually) update the repeating request if the keys are + * supported. Setting a value to null explicitly un-sets the value. + */ + void setParameters(Map<CaptureRequest.Key<?>, Object> parameters); + + /** + * CameraX / Camera2 will call this interface in response to client requests involving + * the output preview surface. Typical examples include requests that include AF/AE triggers. + * Extensions can disregard any capture request keys that were not advertised in + * {@link AdvancedExtenderImpl#getAvailableCaptureRequestKeys}. + * + * @param triggers Capture request key value map. + * @param callback a callback to report the status. + * @return the id of the capture sequence. + * + * @throws IllegalArgumentException If there are no valid settings that can be applied + * + * @since 1.3 + */ + int startTrigger(Map<CaptureRequest.Key<?>, Object> triggers, CaptureCallback callback); + + /** + * This will be invoked once after the {@link android.hardware.camera2.CameraCaptureSession} + * has been created. {@link RequestProcessorImpl} is passed for OEM to submit single + * requests or set repeating requests. This ExtensionRequestProcessor will be valid to use + * until onCaptureSessionEnd is called. + */ + void onCaptureSessionStart(RequestProcessorImpl requestProcessor); + + /** + * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is + * closed. {@link RequestProcessorImpl} passed in onCaptureSessionStart will no longer + * accept any requests after onCaptureSessionEnd() returns. + */ + void onCaptureSessionEnd(); + + /** + * Starts the repeating request after CameraCaptureSession is called. Vendor should start the + * repeating request by {@link RequestProcessorImpl}. Vendor can also update the + * repeating request when needed later. + * + * @param callback a callback to report the status. + * @return the id of the capture sequence. + */ + int startRepeating(CaptureCallback callback); + + /** + * Stop the repeating request. To prevent OEM from not calling stopRepeating, CameraX will + * first stop the repeating request of current CameraCaptureSession and call this API to signal + * OEM that the repeating request was stopped and going forward calling + * {@link RequestProcessorImpl#setRepeating} will simply do nothing. + */ + void stopRepeating(); + + /** + * Start a multi-frame capture. + * + * When the capture is completed, {@link CaptureCallback#onCaptureSequenceCompleted} + * is called and {@code OnImageAvailableListener#onImageAvailable} + * will also be called on the ImageReader that creates the image capture output surface. + * + * <p>Only one capture can perform at a time. Starting a capture when another capture is running + * will cause onCaptureFailed to be called immediately. + * + * @param callback a callback to report the status. + * @return the id of the capture sequence. + */ + int startCapture(CaptureCallback callback); + + /** + * Abort all capture tasks. + */ + void abortCapture(int captureSequenceId); + + /** + * Callback for notifying the status of {@link #startCapture(CaptureCallback)} and + * {@link #startRepeating(CaptureCallback)}. + */ + interface CaptureCallback { + /** + * This method is called when the camera device has started capturing the initial input + * image. + * + * For a multi-frame capture, the method is called when the + * CameraCaptureSession.CaptureCallback onCaptureStarted of first frame is called and its + * timestamp is directly forwarded to timestamp parameter of + * this method. + * + * @param captureSequenceId id of the current capture sequence + * @param timestamp the timestamp at start of capture for repeating + * request or the timestamp at start of capture of the + * first frame in a multi-frame capture, in nanoseconds. + */ + void onCaptureStarted(int captureSequenceId, long timestamp); + + /** + * This method is called when an image (or images in case of multi-frame + * capture) is captured and device-specific extension processing is triggered. + * + * @param captureSequenceId id of the current capture sequence + */ + void onCaptureProcessStarted(int captureSequenceId); + + /** + * This method is called instead of + * {@link #onCaptureProcessStarted} when the camera device failed + * to produce the required input for the device-specific extension. The + * cause could be a failed camera capture request, a failed + * capture result or dropped camera frame. + * + * @param captureSequenceId id of the current capture sequence + */ + void onCaptureFailed(int captureSequenceId); + + /** + * This method is called independently of the others in the CaptureCallback, when a capture + * sequence finishes. + * + * <p>In total, there will be at least one + * {@link #onCaptureProcessStarted}/{@link #onCaptureFailed} + * invocation before this callback is triggered. If the capture + * sequence is aborted before any requests have begun processing, + * {@link #onCaptureSequenceAborted} is invoked instead.</p> + * + * @param captureSequenceId id of the current capture sequence + */ + void onCaptureSequenceCompleted(int captureSequenceId); + + /** + * This method is called when a capture sequence aborts. + * + * @param captureSequenceId id of the current capture sequence + */ + void onCaptureSequenceAborted(int captureSequenceId); + + /** + * Capture result callback that needs to be called when the process capture results are + * ready as part of frame post-processing. + * + * This callback will fire after {@link #onCaptureStarted}, {@link #onCaptureProcessStarted} + * and before {@link #onCaptureSequenceCompleted}. The callback is not expected to fire + * in case of capture failure {@link #onCaptureFailed} or capture abort + * {@link #onCaptureSequenceAborted}. + * + * @param timestamp The timestamp at start of capture. The same timestamp value + * passed to {@link #onCaptureStarted}. + * @param captureSequenceId the capture id of the request that generated the capture + * results. This is the return value of either + * {@link #startRepeating} or {@link #startCapture}. + * @param result Map containing the supported capture results. Do note + * that if results 'android.jpeg.quality' and + * 'android.jpeg.orientation' are present in the process + * capture input results, then the values must also be passed + * as part of this callback. Both Camera2 and CameraX guarantee + * that those two settings and results are always supported and + * applied by the corresponding framework. + */ + void onCaptureCompleted(long timestamp, int captureSequenceId, + Map<CaptureResult.Key, Object> result); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java new file mode 100644 index 00000000..7b8d83c1 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/advanced/SurfaceOutputConfigImpl.java @@ -0,0 +1,31 @@ +/* + * Copyright 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 androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; +import android.view.Surface; + +/** + * Use Surface directly to create the OutputConfiguration. + */ +@SuppressLint("UnknownNullness") +public interface SurfaceOutputConfigImpl extends Camera2OutputConfigImpl { + /** + * Get the {@link Surface}. It'll return valid surface only when type is TYPE_SURFACE. + */ + Surface getSurface(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraMetadataWrapper.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraMetadataWrapper.aidl new file mode 100644 index 00000000..5be2e737 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraMetadataWrapper.aidl @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; + +parcelable CameraMetadataWrapper;
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraMetadataWrapper.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraMetadataWrapper.java new file mode 100644 index 00000000..02f83142 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraMetadataWrapper.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.service; + +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.impl.CameraMetadataNative; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.camera.extensions.impl.serviceforward.PlatformApi; + +import java.util.ArrayList; + +public class CameraMetadataWrapper implements Parcelable { + private CameraMetadataNative mCameraMetadataNative; + private long mVendorId = Long.MAX_VALUE; + + public CameraMetadataWrapper(CameraCharacteristics cameraCharacteristics) { + mCameraMetadataNative = new CameraMetadataNative(); + if (cameraCharacteristics != null) { + setVendorId(cameraCharacteristics); + } + } + + public CameraMetadataWrapper(CameraMetadataNative cameraMetadataNative) { + mCameraMetadataNative = cameraMetadataNative; + } + + protected CameraMetadataWrapper(Parcel in) { + mCameraMetadataNative = in.readParcelable(CameraMetadataNative.class.getClassLoader()); + } + + public static final Creator<CameraMetadataWrapper> CREATOR = + new Creator<CameraMetadataWrapper>() { + @Override + public CameraMetadataWrapper createFromParcel(Parcel in) { + return new CameraMetadataWrapper(in); + } + + @Override + public CameraMetadataWrapper[] newArray(int size) { + return new CameraMetadataWrapper[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mCameraMetadataNative, flags); + } + + private void setVendorId(CameraCharacteristics chars) { + Object thisClass = CameraCharacteristics.Key.class; + Class<CameraCharacteristics.Key<?>> keyClass = + (Class<CameraCharacteristics.Key<?>>) thisClass; + ArrayList<CameraCharacteristics.Key<?>> vendorKeys = + chars.getNativeMetadata().getAllVendorKeys(keyClass); + if ((vendorKeys != null) && !vendorKeys.isEmpty()) { + mVendorId = vendorKeys.get(0).getVendorId(); + mCameraMetadataNative.setVendorId(mVendorId); + } + } + + public <T> T get(CaptureRequest.Key<T> key) { + return mCameraMetadataNative.get(key); + } + + public <T> void set(CaptureRequest.Key<T> key, T value) { + mCameraMetadataNative.set(key, value); + } + + public <T> void set(CaptureResult.Key<T> key, T value) { + mCameraMetadataNative.set(key, value); + } + + public CaptureRequest toCaptureRequest() { + CameraMetadataNative cameraMetadataNative = new CameraMetadataNative(mCameraMetadataNative); + return PlatformApi.createCaptureRequest(cameraMetadataNative); + } + + public TotalCaptureResult toTotalCaptureResult() { + CameraMetadataNative cameraMetadataNative = new CameraMetadataNative(mCameraMetadataNative); + return PlatformApi.createTotalCaptureResult(cameraMetadataNative); + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraOutputConfig.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraOutputConfig.aidl new file mode 100644 index 00000000..0c008487 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraOutputConfig.aidl @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.Size; +import android.view.Surface; + +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; + + int outputId; + int surfaceGroupId; + String physicalCameraId; + List<CameraOutputConfig> sharedSurfaceConfigs; +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraSessionConfig.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraSessionConfig.aidl new file mode 100644 index 00000000..c0d1dece --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CameraSessionConfig.aidl @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.CameraOutputConfig; + +parcelable CameraSessionConfig +{ + List<CameraOutputConfig> outputConfigs; + CameraMetadataWrapper sessionParameter; + int sessionTemplateId; +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureBundle.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureBundle.aidl new file mode 100644 index 00000000..6b21485d --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureBundle.aidl @@ -0,0 +1,27 @@ + +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.ImageWrapper; + +parcelable CaptureBundle +{ + int stageId; + CameraMetadataWrapper captureResult; + ImageWrapper captureImage; +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureFailureWrapper.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureFailureWrapper.aidl new file mode 100644 index 00000000..bf3b331e --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureFailureWrapper.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +parcelable CaptureFailureWrapper;
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureFailureWrapper.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureFailureWrapper.java new file mode 100644 index 00000000..7deb089b --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureFailureWrapper.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.service; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; + +import android.util.Log; + +import android.os.Parcelable; +import android.os.Parcel; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureFailure; + +public class CaptureFailureWrapper implements Parcelable { + private CaptureRequest mRequest; + private int mReason; + private boolean mWasImageCaptured; + private int mSequenceId; + private long mFrameNumber; + private String mErrorPhysicalCameraId; + + public CaptureFailureWrapper(CaptureFailure captureFailure) { + mRequest = captureFailure.getRequest(); + mReason = captureFailure.getReason(); + mWasImageCaptured = captureFailure.wasImageCaptured(); + mSequenceId = captureFailure.getSequenceId(); + mFrameNumber = captureFailure.getFrameNumber(); + mErrorPhysicalCameraId = captureFailure.getPhysicalCameraId(); + } + + public CaptureFailureWrapper(Parcel parcel) { + mRequest = parcel.readParcelable(CaptureRequest.class.getClassLoader()); + mReason = parcel.readInt(); + mWasImageCaptured = parcel.readBoolean(); + mSequenceId = parcel.readInt(); + mFrameNumber = parcel.readLong(); + mErrorPhysicalCameraId = parcel.readString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeParcelable(mRequest, flags); + parcel.writeInt(mReason); + parcel.writeBoolean(mWasImageCaptured); + parcel.writeInt(mSequenceId); + parcel.writeLong(mFrameNumber); + parcel.writeString(mErrorPhysicalCameraId); + } + + public static final Parcelable.Creator<CaptureFailureWrapper> CREATOR + = new Parcelable.Creator<CaptureFailureWrapper>() { + @Override + public CaptureFailureWrapper createFromParcel(Parcel parcel) { + return new CaptureFailureWrapper(parcel); + } + + @Override + public CaptureFailureWrapper[] newArray(int size) { + return new CaptureFailureWrapper[size]; + } + }; + + public CaptureFailure toCaptureFailure() { + return new CaptureFailure(mRequest, mReason, mWasImageCaptured, mSequenceId, + mFrameNumber, mErrorPhysicalCameraId); + } +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureResultWrapper.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureResultWrapper.aidl new file mode 100644 index 00000000..6a01aa3a --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureResultWrapper.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +parcelable CaptureResultWrapper; diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureResultWrapper.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureResultWrapper.java new file mode 100644 index 00000000..44b5bfa2 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureResultWrapper.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.service; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; + +import android.util.Log; + + +import android.os.Parcelable; +import android.os.Parcel; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.impl.PhysicalCaptureResultInfo; +import android.hardware.camera2.impl.CameraMetadataNative; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; + +public class CaptureResultWrapper implements Parcelable { + private String mCameraId; + private CameraMetadataNative mResults; + private CaptureRequest mRequest; + private int mSequenceId; + private long mFrameNumber; + + public CaptureResultWrapper(CaptureResult captureResult) { + mCameraId = captureResult.getCameraId(); + mResults = captureResult.getNativeMetadata(); + mRequest = captureResult.getRequest(); + mSequenceId = captureResult.getSequenceId(); + mFrameNumber = captureResult.getFrameNumber(); + } + + public CaptureResultWrapper(Parcel parcel) { + mCameraId = parcel.readString(); + mResults = parcel.readParcelable(CameraMetadataNative.class.getClassLoader()); + mRequest = parcel.readParcelable(CaptureRequest.class.getClassLoader()); + mSequenceId = parcel.readInt(); + mFrameNumber = parcel.readLong(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mCameraId); + parcel.writeParcelable(mResults, flags); + parcel.writeParcelable(mRequest, flags); + parcel.writeInt(mSequenceId); + parcel.writeLong(mFrameNumber); + } + + public static final Parcelable.Creator<CaptureResultWrapper> CREATOR = + new Parcelable.Creator<CaptureResultWrapper>() { + @Override + public CaptureResultWrapper createFromParcel(Parcel parcel) { + return new CaptureResultWrapper(parcel); + } + + @Override + public CaptureResultWrapper[] newArray(int size) { + return new CaptureResultWrapper[size]; + } + }; + + public CaptureResult toCaptureResult() { + return new CaptureResult(mCameraId, mResults, mRequest, mSequenceId, mFrameNumber); + } +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureStageImplWrapper.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureStageImplWrapper.aidl new file mode 100644 index 00000000..3092d766 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/CaptureStageImplWrapper.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; + +parcelable CaptureStageImplWrapper +{ + int id; + CameraMetadataWrapper parameters; +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IAdvancedExtenderImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IAdvancedExtenderImpl.aidl new file mode 100644 index 00000000..35956093 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IAdvancedExtenderImpl.aidl @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.LatencyRange; +import androidx.camera.extensions.impl.service.SizeList; +import androidx.camera.extensions.impl.service.Size; +import androidx.camera.extensions.impl.service.ISessionProcessorImpl; +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; + +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); + @nullable List<SizeList> getSupportedYuvAnalysisResolutions(in String cameraId); + ISessionProcessorImpl getSessionProcessor(); + CameraMetadataWrapper getAvailableCaptureRequestKeys(); + CameraMetadataWrapper getAvailableCaptureResultKeys(); +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ICaptureCallback.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ICaptureCallback.aidl new file mode 100644 index 00000000..0cf067fa --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ICaptureCallback.aidl @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +// Declare any non-default types here with import statements + +interface ICaptureCallback { + void onCaptureStarted(int captureSequenceId, long timestamp); + void onCaptureProcessStarted(int captureSequenceId); + void onCaptureFailed(int captureSequenceId); + void onCaptureSequenceCompleted(int captureSequenceId); + void onCaptureSequenceAborted(int captureSequenceId); + void onCaptureCompleted(long shutterTimestamp, int captureSequenceId, in CameraMetadataWrapper results); +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ICaptureProcessorImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ICaptureProcessorImpl.aidl new file mode 100644 index 00000000..d3d73589 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ICaptureProcessorImpl.aidl @@ -0,0 +1,30 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CaptureBundle; +import androidx.camera.extensions.impl.service.IProcessResultImpl; +import androidx.camera.extensions.impl.service.Size; + +import android.view.Surface; + +interface ICaptureProcessorImpl +{ + void onOutputSurface(in Surface surface, int imageFormat); + void onResolutionUpdate(in Size size); + void onImageFormatUpdate(int imageFormat); + void process(in List<CaptureBundle> capturelist, in IProcessResultImpl resultCallback); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IExtensionsService.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IExtensionsService.aidl new file mode 100644 index 00000000..5f0bc57d --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IExtensionsService.aidl @@ -0,0 +1,34 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.IOnExtensionsInitializedCallback; +import androidx.camera.extensions.impl.service.IOnExtensionsDeinitializedCallback; +import androidx.camera.extensions.impl.service.IAdvancedExtenderImpl; +import androidx.camera.extensions.impl.service.IPreviewExtenderImpl; +import androidx.camera.extensions.impl.service.IImageCaptureExtenderImpl; + +import androidx.camera.extensions.impl.service.Size; + +interface IExtensionsService { + boolean isAdvancedExtenderImplemented(); + void initialize(in String version, in IOnExtensionsInitializedCallback callback); + void deInitialize(in IOnExtensionsDeinitializedCallback callback); + IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType); + IPreviewExtenderImpl initializePreviewExtension(int extensionType); + IImageCaptureExtenderImpl initializeImageCaptureExtension(int extensionType); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IImageCaptureExtenderImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IImageCaptureExtenderImpl.aidl new file mode 100644 index 00000000..0ce4856e --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IImageCaptureExtenderImpl.aidl @@ -0,0 +1,42 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.SizeList; +import androidx.camera.extensions.impl.service.ICaptureProcessorImpl; +import androidx.camera.extensions.impl.service.CaptureStageImplWrapper; +import androidx.camera.extensions.impl.service.LatencyRange; +import androidx.camera.extensions.impl.service.Size; + + +interface IImageCaptureExtenderImpl +{ + void onInit(in String cameraId); + void onDeInit(); + @nullable CaptureStageImplWrapper onPresetSession(); + @nullable CaptureStageImplWrapper onEnableSession(); + @nullable CaptureStageImplWrapper onDisableSession(); + boolean isExtensionAvailable(in String cameraId); + void init(in String cameraId); + @nullable ICaptureProcessorImpl getCaptureProcessor(); + List<CaptureStageImplWrapper> getCaptureStages(); + int getMaxCaptureStage(); + @nullable List<SizeList> getSupportedResolutions(); + @nullable LatencyRange getEstimatedCaptureLatencyRange(in Size outputSize); + CameraMetadataWrapper getAvailableCaptureRequestKeys(); + CameraMetadataWrapper getAvailableCaptureResultKeys(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IImageProcessorImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IImageProcessorImpl.aidl new file mode 100644 index 00000000..940e0f14 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IImageProcessorImpl.aidl @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.ImageWrapper; + +interface IImageProcessorImpl +{ + void onNextImageAvailable(int outputConfigId, in ImageWrapper imageWrapper, + in String physicalCameraId); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IOnExtensionsDeinitializedCallback.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IOnExtensionsDeinitializedCallback.aidl new file mode 100644 index 00000000..c8aa43c8 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IOnExtensionsDeinitializedCallback.aidl @@ -0,0 +1,22 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +interface IOnExtensionsDeinitializedCallback { + void onSuccess(); + void onFailure(int error); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IOnExtensionsInitializedCallback.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IOnExtensionsInitializedCallback.aidl new file mode 100644 index 00000000..ada15d89 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IOnExtensionsInitializedCallback.aidl @@ -0,0 +1,22 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +interface IOnExtensionsInitializedCallback { + void onSuccess(); + void onFailure(int error); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IPreviewExtenderImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IPreviewExtenderImpl.aidl new file mode 100644 index 00000000..193c4dc7 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IPreviewExtenderImpl.aidl @@ -0,0 +1,41 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.SizeList; +import androidx.camera.extensions.impl.service.CaptureStageImplWrapper; +import androidx.camera.extensions.impl.service.IPreviewImageProcessorImpl; +import androidx.camera.extensions.impl.service.IRequestUpdateProcessorImpl; + +interface IPreviewExtenderImpl +{ + void onInit(in String cameraId); + void onDeInit(); + @nullable CaptureStageImplWrapper onPresetSession(); + @nullable CaptureStageImplWrapper onEnableSession(); + @nullable CaptureStageImplWrapper onDisableSession(); + boolean isExtensionAvailable(in String cameraId); + void init(in String cameraId); + @nullable CaptureStageImplWrapper getCaptureStage(); + const int PROCESSOR_TYPE_REQUEST_UPDATE_ONLY = 0; + const int PROCESSOR_TYPE_IMAGE_PROCESSOR = 1; + const int PROCESSOR_TYPE_NONE = 2; + int getProcessorType(); + @nullable IPreviewImageProcessorImpl getPreviewImageProcessor(); + @nullable IRequestUpdateProcessorImpl getRequestUpdateProcessor(); + @nullable List<SizeList> getSupportedResolutions(); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IPreviewImageProcessorImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IPreviewImageProcessorImpl.aidl new file mode 100644 index 00000000..22ccfd14 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IPreviewImageProcessorImpl.aidl @@ -0,0 +1,33 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.TotalCaptureResultWrapper; +import androidx.camera.extensions.impl.service.IProcessResultImpl; +import androidx.camera.extensions.impl.service.Size; +import androidx.camera.extensions.impl.service.ImageWrapper; +import android.view.Surface; + + +interface IPreviewImageProcessorImpl +{ + void onOutputSurface(in Surface surface, int imageFormat); + void onResolutionUpdate(in Size size); + void onImageFormatUpdate(int imageFormat); + void process(in ImageWrapper image, in TotalCaptureResultWrapper result, + in IProcessResultImpl resultCallback); + +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IProcessResultImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IProcessResultImpl.aidl new file mode 100644 index 00000000..f921ea02 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IProcessResultImpl.aidl @@ -0,0 +1,23 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; + +interface IProcessResultImpl +{ + void onCaptureCompleted(long shutterTimestamp, in CameraMetadataWrapper results); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IRequestCallback.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IRequestCallback.aidl new file mode 100644 index 00000000..8abed9e9 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IRequestCallback.aidl @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import android.hardware.camera2.CaptureRequest; +import androidx.camera.extensions.impl.service.CaptureFailureWrapper; +import androidx.camera.extensions.impl.service.CaptureResultWrapper; +import androidx.camera.extensions.impl.service.TotalCaptureResultWrapper; + +interface IRequestCallback +{ + void onCaptureStarted(int requestId, long frameNumber, long timestamp); + void onCaptureProgressed(int requestId, in CaptureResultWrapper partialResult); + void onCaptureCompleted(int requestId, in TotalCaptureResultWrapper totalCaptureResult); + void onCaptureFailed(int requestId, in CaptureFailureWrapper captureFailure); + void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId); + void onCaptureSequenceCompleted(int sequenceId, long frameNumber); + void onCaptureSequenceAborted(int sequenceId); +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IRequestProcessorImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IRequestProcessorImpl.aidl new file mode 100644 index 00000000..70b30fa7 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IRequestProcessorImpl.aidl @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.IImageProcessorImpl; +import androidx.camera.extensions.impl.service.IRequestCallback; +import androidx.camera.extensions.impl.service.Request; + +interface IRequestProcessorImpl { + void setImageProcessor(int outputConfigId, in IImageProcessorImpl imageProcessor); + int submit(in Request request, in IRequestCallback callback); + int submitBurst(in List<Request> requests, in IRequestCallback callback); + int setRepeating(in Request request, in IRequestCallback callback); + void abortCaptures(); + void stopRepeating(); +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IRequestUpdateProcessorImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IRequestUpdateProcessorImpl.aidl new file mode 100644 index 00000000..f6e78d2b --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/IRequestUpdateProcessorImpl.aidl @@ -0,0 +1,27 @@ +/* + * Copyright 2023 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.TotalCaptureResultWrapper; +import androidx.camera.extensions.impl.service.Size; +import androidx.camera.extensions.impl.service.CaptureStageImplWrapper; +import android.view.Surface; + + +interface IRequestUpdateProcessorImpl +{ + @nullable CaptureStageImplWrapper process(in TotalCaptureResultWrapper result); +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ISessionProcessorImpl.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ISessionProcessorImpl.aidl new file mode 100644 index 00000000..376a4a44 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ISessionProcessorImpl.aidl @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraSessionConfig; +import androidx.camera.extensions.impl.service.OutputSurface; +import androidx.camera.extensions.impl.service.IRequestProcessorImpl; +import androidx.camera.extensions.impl.service.ICaptureCallback; +import android.hardware.camera2.CaptureRequest; + +interface ISessionProcessorImpl { + CameraSessionConfig initSession(in String cameraId, + in OutputSurface previewSurface, + in OutputSurface imageCaptureSurface, + in OutputSurface imageAnalysisSurface); + void deInitSession(); + void onCaptureSessionStart(IRequestProcessorImpl requestProcessor); + void onCaptureSessionEnd(); + int startRepeating(in ICaptureCallback callback); + void stopRepeating(); + int startCapture(in ICaptureCallback callback); + void setParameters(in CaptureRequest captureRequest); + int startTrigger(in CaptureRequest captureRequest, in ICaptureCallback callback); + void abortCapture(int captureSequenceId); +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ImageWrapper.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ImageWrapper.aidl new file mode 100644 index 00000000..09b7b485 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ImageWrapper.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +parcelable ImageWrapper;
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ImageWrapper.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ImageWrapper.java new file mode 100644 index 00000000..882980d1 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/ImageWrapper.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.service; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; + +import android.util.Log; +import android.graphics.Rect; + +import android.graphics.GraphicBuffer; +import android.media.ImageReader; +import android.hardware.SyncFence; + + +import android.os.Parcelable; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureFailure; +import android.hardware.HardwareBuffer; +import android.media.Image; + +import androidx.camera.extensions.impl.advanced.ImageReferenceImpl; + +public class ImageWrapper implements Parcelable, ImageReferenceImpl { + private static final String TAG = "ImageWrapper"; + private int mFormat; + private int mWidth; + private int mHeight; + private int mTransform; + private int mScalingMode; + private long mTimestamp; + private int mPlaneCount; + private Rect mCrop; + private HardwareBuffer mBuffer; + private ParcelFileDescriptor mFence; + + public ImageWrapper(Image image) { + mFormat = image.getFormat(); + mWidth = image.getWidth(); + mHeight = image.getHeight(); + mTransform = image.getTransform(); + mScalingMode = image.getScalingMode(); + mTimestamp = image.getTimestamp(); + if (image.getPlaneCount() <= 0) { + mPlaneCount = image.getPlanes().length; + } else { + mPlaneCount = image.getPlaneCount(); + } + + mCrop = image.getCropRect(); + mBuffer = image.getHardwareBuffer(); + try { + SyncFence fd = image.getFence(); + if (fd.isValid()) { + mFence = fd.getFdDup(); + } + } catch (java.io.IOException e) { + Log.e(TAG, "Failed to parcel buffer fence!"); + } + } + + public ImageWrapper(Parcel parcel) { + mFormat = parcel.readInt(); + mWidth = parcel.readInt(); + mHeight = parcel.readInt(); + mTransform = parcel.readInt(); + mScalingMode = parcel.readInt(); + mTimestamp = parcel.readLong(); + mPlaneCount = parcel.readInt(); + mCrop = parcel.readParcelable(Rect.class.getClassLoader()); + mBuffer = parcel.readParcelable(HardwareBuffer.class.getClassLoader()); + mFence = parcel.readParcelable(ParcelFileDescriptor.class.getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mFormat); + parcel.writeInt(mWidth); + parcel.writeInt(mHeight); + parcel.writeInt(mTransform); + parcel.writeInt(mScalingMode); + parcel.writeLong(mTimestamp); + parcel.writeInt(mPlaneCount); + parcel.writeParcelable(mCrop, flags); + parcel.writeParcelable(mBuffer, flags); + parcel.writeParcelable(mFence, flags); + } + + public static final Parcelable.Creator<ImageWrapper> CREATOR + = new Parcelable.Creator<ImageWrapper>() { + @Override + public ImageWrapper createFromParcel(Parcel parcel) { + return new ImageWrapper(parcel); + } + + @Override + public ImageWrapper[] newArray(int size) { + return new ImageWrapper[size]; + } + }; + + + // ImageReferenceImpl implementations. + private Image mImage = null; + private int mRefCount = 1; + private final Object mImageLock = new Object(); + + @Override + public Image get() { + if (mImage == null) { + mImage = new ExtensionImage(this); + } + return mImage; + } + + @Override + public boolean increment() { + synchronized (mImageLock) { + if (mRefCount <= 0) { + return false; + } + mRefCount++; + } + return true; + } + + @Override + public boolean decrement() { + synchronized (mImageLock) { + if (mRefCount <= 0) { + return false; + } + mRefCount--; + if (mRefCount <= 0) { + mImage.close(); + } + } + return true; + } + + private static class ExtensionImage extends android.media.Image { + private final ImageWrapper mImageWrapper; + private GraphicBuffer mGraphicBuffer; + private ImageReader.ImagePlane[] mPlanes; + + private ExtensionImage(ImageWrapper imageWrapper) { + mImageWrapper = imageWrapper; + mIsImageValid = true; + } + + @Override + public int getFormat() { + throwISEIfImageIsInvalid(); + return mImageWrapper.mFormat; + } + + @Override + public int getWidth() { + throwISEIfImageIsInvalid(); + return mImageWrapper.mWidth; + } + + @Override + public HardwareBuffer getHardwareBuffer() { + throwISEIfImageIsInvalid(); + return mImageWrapper.mBuffer; + } + + @Override + public int getHeight() { + throwISEIfImageIsInvalid(); + return mImageWrapper.mHeight; + } + + @Override + public long getTimestamp() { + throwISEIfImageIsInvalid(); + return mImageWrapper.mTimestamp; + } + + @Override + public int getTransform() { + throwISEIfImageIsInvalid(); + return mImageWrapper.mTransform; + } + + @Override + public int getScalingMode() { + throwISEIfImageIsInvalid(); + return mImageWrapper.mScalingMode; + } + + @Override + public Plane[] getPlanes() { + throwISEIfImageIsInvalid(); + if (mPlanes == null) { + int fenceFd = mImageWrapper.mFence != null ? mImageWrapper.mFence.getFd() : -1; + mGraphicBuffer = GraphicBuffer.createFromHardwareBuffer(mImageWrapper.mBuffer); + mPlanes = ImageReader.initializeImagePlanes(mImageWrapper.mPlaneCount, + mGraphicBuffer, + fenceFd, mImageWrapper.mFormat, mImageWrapper.mTimestamp, + mImageWrapper.mTransform, mImageWrapper.mScalingMode, mImageWrapper.mCrop); + + } + // Shallow copy is fine. + return mPlanes.clone(); + } + + @Override + protected final void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + @Override + public boolean isAttachable() { + throwISEIfImageIsInvalid(); + // Clients must always detach parcelable images + return true; + } + + @Override + public Rect getCropRect() { + throwISEIfImageIsInvalid(); + return mImageWrapper.mCrop; + } + + @Override + public void close() { + mIsImageValid = false; + if (mGraphicBuffer != null) { + ImageReader.unlockGraphicBuffer(mGraphicBuffer); + mGraphicBuffer.destroy(); + mGraphicBuffer = null; + } + + if (mPlanes != null) { + mPlanes = null; + } + + if (mImageWrapper.mBuffer != null) { + mImageWrapper.mBuffer.close(); + mImageWrapper.mBuffer = null; + } + + if (mImageWrapper.mFence != null) { + try { + mImageWrapper.mFence.close(); + } catch (java.io.IOException e) { + e.printStackTrace(); + } + mImageWrapper.mFence = null; + } + } + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/LatencyRange.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/LatencyRange.aidl new file mode 100644 index 00000000..9d9e22cf --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/LatencyRange.aidl @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +parcelable LatencyRange +{ + long min; + long max; +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/OutputSurface.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/OutputSurface.aidl new file mode 100644 index 00000000..558928c3 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/OutputSurface.aidl @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +// Declare any non-default types here with import statements +import androidx.camera.extensions.impl.service.Size; +import android.view.Surface; + +parcelable OutputSurface { + Surface surface; + Size size; + int imageFormat; +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/Request.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/Request.aidl new file mode 100644 index 00000000..08dff963 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/Request.aidl @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; + +parcelable Request +{ + int[] targetOutputConfigIds; + CameraMetadataWrapper parameters; + int templateId; + int requestId; +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/Size.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/Size.aidl new file mode 100644 index 00000000..5341b604 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/Size.aidl @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +parcelable Size +{ + int width; + int height; +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/SizeList.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/SizeList.aidl new file mode 100644 index 00000000..443c769e --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/SizeList.aidl @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2023, 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 androidx.camera.extensions.impl.service; + +import androidx.camera.extensions.impl.service.Size; + +parcelable SizeList +{ + int format; + List<Size> sizes; +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/TotalCaptureResultWrapper.aidl b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/TotalCaptureResultWrapper.aidl new file mode 100644 index 00000000..b28861d5 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/TotalCaptureResultWrapper.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2023 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 androidx.camera.extensions.impl.service; + +parcelable TotalCaptureResultWrapper; diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/TotalCaptureResultWrapper.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/TotalCaptureResultWrapper.java new file mode 100644 index 00000000..28bbcb23 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/service/TotalCaptureResultWrapper.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.service; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; + +import android.os.Parcelable; +import android.os.Parcel; +import android.util.Log; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.impl.PhysicalCaptureResultInfo; +import android.hardware.camera2.impl.CameraMetadataNative; + +import androidx.camera.extensions.impl.service.CaptureResultWrapper; +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; + +public class TotalCaptureResultWrapper implements Parcelable { + private String mLogicalCameraId; + private CameraMetadataNative mResults; + private CaptureRequest mRequest; + private int mSequenceId; + private long mFrameNumber; + private List<CaptureResultWrapper> mPartials = new ArrayList<>(); + private int mSessionId; + private List<PhysicalCaptureResultInfo> mPhysicalResultList = new ArrayList<>(); + + public TotalCaptureResultWrapper(TotalCaptureResult totalResult) { + mLogicalCameraId = totalResult.getCameraId(); + mResults = totalResult.getNativeMetadata(); + mRequest = totalResult.getRequest(); + mSequenceId = totalResult.getSequenceId(); + mFrameNumber = totalResult.getFrameNumber(); + mSessionId = totalResult.getSessionId(); + for (CaptureResult partial : totalResult.getPartialResults()) { + mPartials.add(new CaptureResultWrapper(partial)); + } + Map<String, TotalCaptureResult> physicalResults = + totalResult.getPhysicalCameraTotalResults(); + for (TotalCaptureResult physicalResult : physicalResults.values()) { + mPhysicalResultList.add(new PhysicalCaptureResultInfo(physicalResult.getCameraId(), + physicalResult.getNativeMetadata())); + } + } + + public TotalCaptureResultWrapper(Parcel parcel) { + mLogicalCameraId = parcel.readString(); + mResults = parcel.readParcelable(CameraMetadataNative.class.getClassLoader()); + mRequest = parcel.readParcelable(CaptureRequest.class.getClassLoader()); + mSequenceId = parcel.readInt(); + mFrameNumber = parcel.readLong(); + parcel.readParcelableList(mPartials, CaptureResultWrapper.class.getClassLoader()); + mSessionId = parcel.readInt(); + parcel.readParcelableList(mPhysicalResultList, + PhysicalCaptureResultInfo.class.getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mLogicalCameraId); + parcel.writeParcelable(mResults, flags); + parcel.writeParcelable(mRequest, flags); + parcel.writeInt(mSequenceId); + parcel.writeLong(mFrameNumber); + parcel.writeParcelableList(mPartials, flags); + parcel.writeInt(mSessionId); + parcel.writeParcelableList(mPhysicalResultList, flags); + } + + public static final Parcelable.Creator<TotalCaptureResultWrapper> CREATOR + = new Parcelable.Creator<TotalCaptureResultWrapper>() { + @Override + public TotalCaptureResultWrapper createFromParcel(Parcel parcel) { + return new TotalCaptureResultWrapper(parcel); + } + + @Override + public TotalCaptureResultWrapper[] newArray(int size) { + return new TotalCaptureResultWrapper[size]; + } + }; + + public TotalCaptureResult toTotalCaptureResult() { + PhysicalCaptureResultInfo[] physicalResults = new PhysicalCaptureResultInfo[0]; + if ((mPhysicalResultList != null) && (!mPhysicalResultList.isEmpty())) { + physicalResults = new PhysicalCaptureResultInfo[mPhysicalResultList.size()]; + physicalResults = mPhysicalResultList.toArray(physicalResults); + } + ArrayList<CaptureResult> partials = new ArrayList<>(mPartials.size()); + for (CaptureResultWrapper resultWrapper : mPartials) { + partials.add(resultWrapper.toCaptureResult()); + } + return new TotalCaptureResult( + mLogicalCameraId, mResults, + mRequest, mSequenceId, + mFrameNumber, partials, mSessionId, + physicalResults); + } +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/CaptureStageImplAdapter.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/CaptureStageImplAdapter.java new file mode 100644 index 00000000..43ddf4a9 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/CaptureStageImplAdapter.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.serviceforward; + +import android.hardware.camera2.CaptureRequest; +import android.util.Pair; + +import androidx.camera.extensions.impl.CaptureStageImpl; +import androidx.camera.extensions.impl.service.CaptureStageImplWrapper; + +import java.util.ArrayList; +import java.util.List; + +class CaptureStageImplAdapter implements CaptureStageImpl { + private final CaptureStageImplWrapper mCaptureStageImplWrapper; + CaptureStageImplAdapter(CaptureStageImplWrapper wrapper) { + mCaptureStageImplWrapper = wrapper; + } + + @Override + public int getId() { + return mCaptureStageImplWrapper.id; + } + + @Override + public List<Pair<CaptureRequest.Key, Object>> getParameters() { + CaptureRequest request = mCaptureStageImplWrapper.parameters.toCaptureRequest(); + List<Pair<CaptureRequest.Key, Object>> result = new ArrayList<>(); + for (CaptureRequest.Key<?> key : request.getKeys()) { + result.add(new Pair(key, request.get(key))); + } + return result; + } +}
\ No newline at end of file diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardAdvancedExtender.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardAdvancedExtender.java new file mode 100644 index 00000000..dc57590a --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardAdvancedExtender.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.serviceforward; + +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.os.RemoteException; +import android.util.Log; +import android.util.Range; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl; +import androidx.camera.extensions.impl.advanced.SessionProcessorImpl; +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.IAdvancedExtenderImpl; +import androidx.camera.extensions.impl.service.ISessionProcessorImpl; +import androidx.camera.extensions.impl.service.LatencyRange; +import androidx.camera.extensions.impl.service.SizeList; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ForwardAdvancedExtender implements AdvancedExtenderImpl { + private static final String TAG = "ForwardAdvancedExtender"; + private IAdvancedExtenderImpl mIAdvancedExtender; + private String mCameraId; + private final int mExtensionType; + private ForwardSessionProcessor mForwardSessionProcessor; + + public ForwardAdvancedExtender(int extensionType) { + mIAdvancedExtender = ServiceManager.getInstance().createAdvancedExtenderImpl( + extensionType); + mExtensionType = extensionType; + } + + private static ArrayList<Size> getSupportedSizes(SizeList sizesList) { + ArrayList<Size> ret = new ArrayList<>(); + for (androidx.camera.extensions.impl.service.Size size : sizesList.sizes) { + ret.add(new Size(size.width, size.height)); + } + return ret; + } + + @Override + public boolean isExtensionAvailable(@NonNull String cameraId, + @NonNull Map<String, CameraCharacteristics> characteristicsMap) { + try { + return mIAdvancedExtender.isExtensionAvailable(cameraId); + } catch (RemoteException e) { + Log.e(TAG, "isExtensionAvailable failed", e); + throw new IllegalStateException("isExtensionAvailable failed", e); + } + } + + @Override + public void init(@NonNull String cameraId, + @NonNull Map<String, CameraCharacteristics> characteristicsMap) { + try { + mCameraId = cameraId; + mIAdvancedExtender.init(cameraId); + } catch (RemoteException e) { + Log.e(TAG, "init failed", e); + throw new IllegalStateException("init failed", e); + } + } + @Override + @Nullable + public Range<Long> getEstimatedCaptureLatencyRange(@NonNull String cameraId, + @Nullable Size captureOutputSize, + int imageFormat) { + try { + androidx.camera.extensions.impl.service.Size size = null; + if (captureOutputSize != null) { + size = new androidx.camera.extensions.impl.service.Size(); + size.width = captureOutputSize.getWidth(); + size.height = captureOutputSize.getHeight(); + } + LatencyRange latencyRange = + mIAdvancedExtender.getEstimatedCaptureLatencyRange(cameraId, + size, imageFormat); + return new Range<>(latencyRange.min, latencyRange.max); + } catch (RemoteException e) { + Log.e(TAG, "getEstimatedCaptureLatencyRange failed", e); + throw new IllegalStateException("getEstimatedCaptureLatencyRange failed", e); + } + } + + @Override + public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions(String cameraId) { + try { + List<SizeList> sizeLists = mIAdvancedExtender.getSupportedPreviewOutputResolutions( + cameraId); + Map<Integer, List<Size>> result = new HashMap<>(); + for (SizeList sizeList : sizeLists) { + result.put(sizeList.format, getSupportedSizes(sizeList)); + } + return result; + } catch (RemoteException e) { + Log.e(TAG, "getSupportedPreviewOutputResolutions failed", e); + throw new IllegalStateException("getSupportedPreviewOutputResolutions failed", e); + } + } + + @Override + public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId) { + try { + List<SizeList> sizeLists = mIAdvancedExtender.getSupportedCaptureOutputResolutions( + cameraId); + Map<Integer, List<Size>> result = new HashMap<>(); + for (SizeList sizeList : sizeLists) { + result.put(sizeList.format, getSupportedSizes(sizeList)); + } + return result; + } catch (RemoteException e) { + Log.e(TAG, "getSupportedCaptureOutputResolutions failed", e); + throw new IllegalStateException("getSupportedCaptureOutputResolutions failed", e); + } + } + + @Override + public List<Size> getSupportedYuvAnalysisResolutions(String cameraId) { + try { + List<SizeList> sizeLists = mIAdvancedExtender.getSupportedYuvAnalysisResolutions( + cameraId); + + if (sizeLists == null) { + return null; + } + + for (SizeList sizeList : sizeLists) { + if (sizeList.format == ImageFormat.YUV_420_888) { + return getSupportedSizes(sizeList); + } + } + return null; + } catch (RemoteException e) { + Log.e(TAG, "getSupportedYuvAnalysisResolutions failed", e); + throw new IllegalStateException("getSupportedYuvAnalysisResolutions failed", e); + } + } + + /** + * Re-initialize IAdvancedExtenderImpl when binder died. + */ + private void ensureIAdvancedExtenderImplAlive() { + try { + if (!mIAdvancedExtender.asBinder().pingBinder()) { + Log.e(TAG, "IAdvancedExtenderImpl binder died, recreate"); + mIAdvancedExtender = ServiceManager.getInstance().createAdvancedExtenderImpl( + mExtensionType); + mIAdvancedExtender.init(mCameraId); + } + } catch (RemoteException e) { + Log.e(TAG, "can't create IAdvancedExtenderImpl", e); + throw new IllegalStateException("can't create IAdvancedExtenderImpl", e); + } + } + /** + * Re-initialize ISessionProcessImpl when binder died. + */ + ISessionProcessorImpl recreateISessionProcessor() { + Log.e(TAG, "Recreating ISessionProcessorImpl"); + try { + ensureIAdvancedExtenderImplAlive(); + return mIAdvancedExtender.getSessionProcessor(); + } catch (RemoteException e) { + Log.e(TAG, "can't get the SessionProcessor from IAdvancedExtenderImpl", e); + throw new IllegalStateException( + "can't get the SessionProcessor from IAdvancedExtenderImpl", e); + } + } + + @Override + @NonNull + public SessionProcessorImpl createSessionProcessor() { + try { + ISessionProcessorImpl sessionProcessor = mIAdvancedExtender.getSessionProcessor(); + mForwardSessionProcessor = + new ForwardSessionProcessor(this, sessionProcessor); + return mForwardSessionProcessor; + } catch (RemoteException e) { + Log.e(TAG, "can't get the SessionProcessor from IAdvancedExtenderImpl", e); + throw new IllegalStateException( + "can't get the SessionProcessor from IAdvancedExtenderImpl", e); } + } + + @Override + @Nullable + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + try { + CameraMetadataWrapper cameraMetadataWrapper + = mIAdvancedExtender.getAvailableCaptureRequestKeys(); + + CaptureRequest captureRequest = cameraMetadataWrapper.toCaptureRequest(); + + List<CaptureRequest.Key> result = new ArrayList<>(); + for (CaptureRequest.Key<?> key : captureRequest.getKeys()) { + result.add(key); + } + return result; + } catch (RemoteException e) { + Log.e(TAG, "getAvailableCaptureRequestKeys failed", e); + throw new IllegalStateException("getAvailableCaptureRequestKeys failed", e); + } + } + + @Override + @Nullable + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + try { + CameraMetadataWrapper cameraMetadataWrapper + = mIAdvancedExtender.getAvailableCaptureResultKeys(); + TotalCaptureResult captureResult = cameraMetadataWrapper.toTotalCaptureResult(); + + List<CaptureResult.Key> result = new ArrayList<>(); + for (CaptureResult.Key<?> key : captureResult.getKeys()) { + result.add(key); + } + return result; + } catch (RemoteException e) { + Log.e(TAG, "getAvailableCaptureRequestKeys failed", e); + throw new IllegalStateException("getAvailableCaptureRequestKeys failed", e); + } + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardImageCaptureExtender.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardImageCaptureExtender.java new file mode 100644 index 00000000..03de94bf --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardImageCaptureExtender.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.serviceforward; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.media.Image; +import android.os.RemoteException; +import android.util.Log; +import android.util.Pair; +import android.util.Range; +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.CaptureProcessorImpl; +import androidx.camera.extensions.impl.CaptureStageImpl; +import androidx.camera.extensions.impl.ImageCaptureExtenderImpl; +import androidx.camera.extensions.impl.ProcessResultImpl; +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.CaptureBundle; +import androidx.camera.extensions.impl.service.CaptureStageImplWrapper; +import androidx.camera.extensions.impl.service.ICaptureProcessorImpl; +import androidx.camera.extensions.impl.service.IImageCaptureExtenderImpl; +import androidx.camera.extensions.impl.service.IProcessResultImpl; +import androidx.camera.extensions.impl.service.ImageWrapper; +import androidx.camera.extensions.impl.service.LatencyRange; +import androidx.camera.extensions.impl.service.SizeList; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; + +public class ForwardImageCaptureExtender implements ImageCaptureExtenderImpl { + + private static final String TAG = "ForwardPreviewExtender"; + + private final int mExtensionType; + private IImageCaptureExtenderImpl mIImageCaptureExtender; + + public ForwardImageCaptureExtender(int extensionType) { + mExtensionType = extensionType; + mIImageCaptureExtender = ServiceManager.getInstance() + .createImageCaptureExtenderImpl(extensionType); + } + + @Nullable + private static CaptureStageImpl convertToCaptureStageImpl( + @Nullable CaptureStageImplWrapper wrapper) { + if (wrapper == null) { + return null; + } + + return new CaptureStageImplAdapter(wrapper); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + try { + mIImageCaptureExtender.onInit(cameraId); + } catch (RemoteException e) { + Log.e(TAG, "onInit failed", e); + throw new IllegalStateException("onInit failed", e); + } + } + + @Override + public void onDeInit() { + try { + mIImageCaptureExtender.onDeInit(); + } catch (RemoteException e) { + Log.e(TAG, "onDeInit failed", e); + throw new IllegalStateException("onDeInit failed", e); + } + } + + @Override + public CaptureStageImpl onPresetSession() { + try { + return convertToCaptureStageImpl(mIImageCaptureExtender.onPresetSession()); + } catch (RemoteException e) { + Log.e(TAG, "onPresetSession failed", e); + throw new IllegalStateException("onPresetSession failed", e); + } + } + + @Override + public CaptureStageImpl onEnableSession() { + try { + return convertToCaptureStageImpl(mIImageCaptureExtender.onEnableSession()); + } catch (RemoteException e) { + Log.e(TAG, "onEnableSession failed", e); + throw new IllegalStateException("onEnableSession failed", e); + } + } + + @Override + public CaptureStageImpl onDisableSession() { + try { + return convertToCaptureStageImpl(mIImageCaptureExtender.onDisableSession()); + } catch (RemoteException e) { + Log.e(TAG, "onDisableSession failed", e); + throw new IllegalStateException("onDisableSession failed", e); + } + } + + @Override + public boolean isExtensionAvailable(String cameraId, + CameraCharacteristics cameraCharacteristics) { + try { + return mIImageCaptureExtender.isExtensionAvailable(cameraId); + } catch (RemoteException e) { + Log.e(TAG, "isExtensionAvailable failed", e); + throw new IllegalStateException("isExtensionAvailable failed", e); + } + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + try { + mIImageCaptureExtender.init(cameraId); + } catch (RemoteException e) { + Log.e(TAG, "init failed", e); + throw new IllegalStateException("init failed", e); + } + } + + @Override + public CaptureProcessorImpl getCaptureProcessor() { + try { + ICaptureProcessorImpl captureProcessor = mIImageCaptureExtender.getCaptureProcessor(); + if (captureProcessor == null) { + return null; + } + return new CaptureProcessorImplAdapter(captureProcessor); + } catch (RemoteException e) { + Log.e(TAG, "getCaptureProcessor failed", e); + throw new IllegalStateException("getCaptureProcessor failed", e); + } + } + + @Override + public List<CaptureStageImpl> getCaptureStages() { + try { + List<CaptureStageImpl> results = new ArrayList<>(); + for (CaptureStageImplWrapper wrapper : mIImageCaptureExtender.getCaptureStages()) { + results.add(convertToCaptureStageImpl(wrapper)); + } + return results; + } catch (RemoteException e) { + Log.e(TAG, "getCaptureStages failed", e); + throw new IllegalStateException("getCaptureStages failed", e); + } + } + + @Override + public int getMaxCaptureStage() { + try { + return mIImageCaptureExtender.getMaxCaptureStage(); + } catch (RemoteException e) { + Log.e(TAG, "getMaxCaptureStage failed", e); + throw new IllegalStateException("getMaxCaptureStage failed", e); + } + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + try { + List<SizeList> sizes = mIImageCaptureExtender.getSupportedResolutions(); + if (sizes == null) { + return null; + } + + List<Pair<Integer, Size[]>> list = new ArrayList<>(); + for (SizeList sizeList : sizes) { + Size[] sizeArray = new Size[sizeList.sizes.size()]; + for (int i = 0; i < sizeList.sizes.size(); i++) { + sizeArray[i] = new Size(sizeList.sizes.get(i).width, + sizeList.sizes.get(i).height); + } + list.add(new Pair(sizeList.format, sizeArray)); + } + return list; + } catch (RemoteException e) { + Log.e(TAG, "getSupportedResolutions failed", e); + throw new IllegalStateException("getSupportedResolutions failed", e); + } + } + + @Override + public Range<Long> getEstimatedCaptureLatencyRange(Size captureOutputSize) { + try { + androidx.camera.extensions.impl.service.Size size = null; + if (captureOutputSize != null) { + size = new androidx.camera.extensions.impl.service.Size(); + size.width = captureOutputSize.getWidth(); + size.height = captureOutputSize.getHeight(); + } + LatencyRange latencyRange = + mIImageCaptureExtender.getEstimatedCaptureLatencyRange(size); + if (latencyRange == null) { + return null; + } + + return new Range<Long>(latencyRange.min, latencyRange.max); + } catch (RemoteException e) { + Log.e(TAG, "getEstimatedCaptureLatencyRange failed", e); + throw new IllegalStateException("getEstimatedCaptureLatencyRange failed", e); + } + } + + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + try { + CameraMetadataWrapper cameraMetadataWrapper + = mIImageCaptureExtender.getAvailableCaptureRequestKeys(); + if (cameraMetadataWrapper == null) { + return null; + } + + CaptureRequest captureRequest = cameraMetadataWrapper.toCaptureRequest(); + List<CaptureRequest.Key> result = new ArrayList<>(); + for (CaptureRequest.Key<?> key : captureRequest.getKeys()) { + result.add(key); + } + return result; + } catch (RemoteException e) { + Log.e(TAG, "getAvailableCaptureRequestKeys failed", e); + throw new IllegalStateException("getAvailableCaptureRequestKeys failed", e); + } + } + + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + try { + CameraMetadataWrapper cameraMetadataWrapper + = mIImageCaptureExtender.getAvailableCaptureResultKeys(); + if (cameraMetadataWrapper == null) { + return null; + } + + TotalCaptureResult captureResult = cameraMetadataWrapper.toTotalCaptureResult(); + List<CaptureResult.Key> result = new ArrayList<>(); + for (CaptureResult.Key<?> key : captureResult.getKeys()) { + result.add(key); + } + return result; + } catch (RemoteException e) { + Log.e(TAG, "getAvailableCaptureResultKeys failed", e); + throw new IllegalStateException("getAvailableCaptureResultKeys failed", e); + } + } + + private static class CaptureProcessorImplAdapter implements CaptureProcessorImpl { + private ICaptureProcessorImpl mICaptureProcessor; + + private CaptureProcessorImplAdapter(ICaptureProcessorImpl iCaptureProcessor) { + mICaptureProcessor = iCaptureProcessor; + } + + @Override + public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results) { + process(results, null, null); + } + + @Override + public void process(Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor) { + + try { + List<CaptureBundle> captureBundleList = new ArrayList<>(); + for (Integer captureStageId : results.keySet()) { + CaptureBundle bundle = new CaptureBundle(); + bundle.stageId = captureStageId; + Pair<Image, TotalCaptureResult> pair = results.get(captureStageId); + + bundle.captureResult = + new CameraMetadataWrapper(pair.second.getNativeMetadata()); + bundle.captureImage = new ImageWrapper(pair.first); + captureBundleList.add(bundle); + } + + IProcessResultImpl.Stub iProcessResultImpl = null; + if (resultCallback != null) { + iProcessResultImpl = new IProcessResultImpl.Stub() { + @Override + public void onCaptureCompleted(long shutterTimestamp, + CameraMetadataWrapper result) { + List<Pair<CaptureResult.Key, Object>> resultList = new ArrayList<>(); + TotalCaptureResult captureResult = result.toTotalCaptureResult(); + for (CaptureResult.Key<?> key : captureResult.getKeys()) { + resultList.add(new Pair(key, captureResult.get(key))); + } + if (executor == null) { + resultCallback.onCaptureCompleted(shutterTimestamp, + resultList); + } else { + executor.execute(() -> { + resultCallback.onCaptureCompleted(shutterTimestamp, + resultList); + }); + } + } + }; + } + mICaptureProcessor.process(captureBundleList, iProcessResultImpl); + } catch (RemoteException e) { + + } + } + + @Override + public void onOutputSurface(Surface surface, int imageFormat) { + try { + mICaptureProcessor.onOutputSurface(surface, imageFormat); + } catch (RemoteException e) { + Log.e(TAG, "CaptureProcessor onOutputSurface failed", e); + throw new IllegalStateException("CaptureProcessor onOutputSurface failed", e); + } + } + + @Override + public void onResolutionUpdate(Size size) { + try { + androidx.camera.extensions.impl.service.Size serviceSize + = new androidx.camera.extensions.impl.service.Size(); + serviceSize.width = size.getWidth(); + serviceSize.height = size.getHeight(); + mICaptureProcessor.onResolutionUpdate(serviceSize); + } catch (RemoteException e) { + Log.e(TAG, "CaptureProcessor onResolutionUpdate failed", e); + throw new IllegalStateException("CaptureProcessor onResolutionUpdate failed", e); + } + } + + @Override + public void onImageFormatUpdate(int imageFormat) { + try { + mICaptureProcessor.onImageFormatUpdate(imageFormat); + } catch (RemoteException e) { + Log.e(TAG, "CaptureProcessor imageFormat failed", e); + throw new IllegalStateException("CaptureProcessor imageFormat failed", e); + } + } + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardPreviewExtender.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardPreviewExtender.java new file mode 100644 index 00000000..81969ca4 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardPreviewExtender.java @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.serviceforward; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.media.Image; +import android.os.RemoteException; +import android.util.Log; +import android.util.Pair; +import android.util.Size; +import android.view.Surface; + +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.CaptureStageImpl; +import androidx.camera.extensions.impl.PreviewExtenderImpl; +import androidx.camera.extensions.impl.PreviewImageProcessorImpl; +import androidx.camera.extensions.impl.ProcessResultImpl; +import androidx.camera.extensions.impl.ProcessorImpl; +import androidx.camera.extensions.impl.RequestUpdateProcessorImpl; +import androidx.camera.extensions.impl.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.CaptureStageImplWrapper; +import androidx.camera.extensions.impl.service.IPreviewExtenderImpl; +import androidx.camera.extensions.impl.service.IPreviewImageProcessorImpl; +import androidx.camera.extensions.impl.service.IProcessResultImpl; +import androidx.camera.extensions.impl.service.IRequestUpdateProcessorImpl; +import androidx.camera.extensions.impl.service.ImageWrapper; +import androidx.camera.extensions.impl.service.TotalCaptureResultWrapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +public class ForwardPreviewExtender implements PreviewExtenderImpl { + private static final String TAG = "ForwardPreviewExtender"; + + private final int mExtensionType; + private IPreviewExtenderImpl mIPreviewExtender; + + public ForwardPreviewExtender(int extensionType) { + mExtensionType = extensionType; + mIPreviewExtender = ServiceManager.getInstance().createPreviewExtenderImpl(extensionType); + } + + @Nullable + private static CaptureStageImpl convertToCaptureStageImpl( + @Nullable CaptureStageImplWrapper wrapper) { + if (wrapper == null) { + return null; + } + + return new CaptureStageImplAdapter(wrapper); + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { + try { + mIPreviewExtender.onInit(cameraId); + } catch (RemoteException e) { + Log.e(TAG, "onInit failed", e); + throw new IllegalStateException("onInit failed", e); + } + } + + @Override + public void onDeInit() { + try { + mIPreviewExtender.onDeInit(); + } catch (RemoteException e) { + Log.e(TAG, "onDeInit failed", e); + throw new IllegalStateException("onDeInit failed", e); + } + } + + @Override + public CaptureStageImpl onPresetSession() { + try { + return convertToCaptureStageImpl(mIPreviewExtender.onPresetSession()); + } catch (RemoteException e) { + Log.e(TAG, "onPresetSession failed", e); + throw new IllegalStateException("onDeInit failed", e); + } + } + + @Override + public CaptureStageImpl onEnableSession() { + try { + return convertToCaptureStageImpl(mIPreviewExtender.onEnableSession()); + } catch (RemoteException e) { + Log.e(TAG, "onEnableSession failed", e); + throw new IllegalStateException("onEnableSession failed", e); + } + } + + @Override + public CaptureStageImpl onDisableSession() { + try { + return convertToCaptureStageImpl(mIPreviewExtender.onDisableSession()); + } catch (RemoteException e) { + Log.e(TAG, "onDisableSession failed", e); + throw new IllegalStateException("onDisableSession failed", e); + } + } + + @Override + public boolean isExtensionAvailable(String cameraId, + CameraCharacteristics cameraCharacteristics) { + try { + return mIPreviewExtender.isExtensionAvailable(cameraId); + } catch (RemoteException e) { + Log.e(TAG, "isExtensionAvailable failed", e); + throw new IllegalStateException("isExtensionAvailable failed", e); + } + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + try { + mIPreviewExtender.init(cameraId); + } catch (RemoteException e) { + Log.e(TAG, "init failed", e); + throw new IllegalStateException("init failed", e); + } + } + + @Override + public CaptureStageImpl getCaptureStage() { + try { + return convertToCaptureStageImpl(mIPreviewExtender.getCaptureStage()); + } catch (RemoteException e) { + Log.e(TAG, "getCaptureStage failed", e); + throw new IllegalStateException("getCaptureStage failed", e); + } + } + + @Override + public ProcessorType getProcessorType() { + try { + switch (mIPreviewExtender.getProcessorType()) { + case IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY: + return ProcessorType.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY; + case IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR: + return ProcessorType.PROCESSOR_TYPE_IMAGE_PROCESSOR; + case IPreviewExtenderImpl.PROCESSOR_TYPE_NONE: + default: + return ProcessorType.PROCESSOR_TYPE_NONE; + } + } catch (RemoteException e) { + Log.e(TAG, "getProcessorType failed", e); + throw new IllegalStateException("getProcessorType failed", e); + } + } + + @Override + public ProcessorImpl getProcessor() { + try { + switch (getProcessorType()) { + case PROCESSOR_TYPE_REQUEST_UPDATE_ONLY: + return new RequestUpdateProcessorAdapter( + mIPreviewExtender.getRequestUpdateProcessor()); + case PROCESSOR_TYPE_IMAGE_PROCESSOR: + return new PreviewImageProcessorAdapter( + mIPreviewExtender.getPreviewImageProcessor()); + case PROCESSOR_TYPE_NONE: + default: + return null; + } + } catch (RemoteException e) { + Log.e(TAG, "getProcessorType failed", e); + throw new IllegalStateException("getProcessorType failed", e); + } + } + + @Nullable + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + return null; + } + + private static class PreviewImageProcessorAdapter implements PreviewImageProcessorImpl { + + private final IPreviewImageProcessorImpl mIPreviewImageProcessor; + + private PreviewImageProcessorAdapter(IPreviewImageProcessorImpl iPreviewImageProcessor) { + mIPreviewImageProcessor = iPreviewImageProcessor; + } + + @Override + public void process(Image image, TotalCaptureResult result) { + try { + mIPreviewImageProcessor.process( + new ImageWrapper(image), new TotalCaptureResultWrapper(result), null); + } catch (RemoteException e) { + + } + } + + @Override + public void process(Image image, TotalCaptureResult result, + ProcessResultImpl resultCallback, @Nullable Executor executor) { + try { + + IProcessResultImpl.Stub iProcessResultImpl = null; + if (resultCallback != null) { + iProcessResultImpl = new IProcessResultImpl.Stub() { + @Override + public void onCaptureCompleted(long shutterTimestamp, + CameraMetadataWrapper result) { + List<Pair<CaptureResult.Key, Object>> resultList = new ArrayList<>(); + TotalCaptureResult captureResult = result.toTotalCaptureResult(); + for (CaptureResult.Key<?> key : captureResult.getKeys()) { + resultList.add(new Pair(key, captureResult.get(key))); + } + if (executor == null) { + resultCallback.onCaptureCompleted(shutterTimestamp, + resultList); + } else { + executor.execute(() -> { + resultCallback.onCaptureCompleted(shutterTimestamp, + resultList); + }); + } + } + }; + } + mIPreviewImageProcessor.process( + new ImageWrapper(image), new TotalCaptureResultWrapper(result), + iProcessResultImpl); + image.close(); + } catch (RemoteException e) { + + } + } + + @Override + public void onOutputSurface(Surface surface, int imageFormat) { + try { + mIPreviewImageProcessor.onOutputSurface(surface, imageFormat); + } catch (RemoteException e) { + Log.e(TAG, "PreviewImageProcessorAdapter onOutputSurface failed", e); + throw new IllegalStateException( + "PreviewImageProcessorAdapter onOutputSurface failed", e); + } + } + + @Override + public void onResolutionUpdate(Size size) { + try { + androidx.camera.extensions.impl.service.Size serviceSize = + new androidx.camera.extensions.impl.service.Size(); + serviceSize.width = size.getWidth(); + serviceSize.height = size.getHeight(); + mIPreviewImageProcessor.onResolutionUpdate(serviceSize); + } catch (RemoteException e) { + Log.e(TAG, "PreviewImageProcessorAdapter onResolutionUpdate", e); + throw new IllegalStateException( + "PreviewImageProcessorAdapter onResolutionUpdate failed", e); + } + } + + @Override + public void onImageFormatUpdate(int imageFormat) { + try { + mIPreviewImageProcessor.onImageFormatUpdate(imageFormat); + } catch (RemoteException e) { + Log.e(TAG, "PreviewImageProcessorAdapter onImageFormatUpdate failed", e); + throw new IllegalStateException( + "PreviewImageProcessorAdapter onImageFormatUpdate failed", e); + } + } + } + + private static class RequestUpdateProcessorAdapter + implements RequestUpdateProcessorImpl { + private IRequestUpdateProcessorImpl mIRequestUpdateProcessor; + + private RequestUpdateProcessorAdapter(IRequestUpdateProcessorImpl iRequestUpdateProcessor) { + mIRequestUpdateProcessor = iRequestUpdateProcessor; + } + + @Override + public void onOutputSurface(Surface surface, int imageFormat) { + } + + @Override + public void onResolutionUpdate(Size size) { + } + + @Override + public void onImageFormatUpdate(int imageFormat) { + } + + @Nullable + @Override + public CaptureStageImpl process(TotalCaptureResult result) { + try { + return convertToCaptureStageImpl( + mIRequestUpdateProcessor.process(new TotalCaptureResultWrapper(result))); + } catch (RemoteException e) { + Log.e(TAG, "RequestUpdateProcessorAdapter process failed", e); + throw new IllegalStateException("RequestUpdateProcessorAdapter process failed", e); + } + } + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardSessionProcessor.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardSessionProcessor.java new file mode 100644 index 00000000..bdb87be7 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ForwardSessionProcessor.java @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.serviceforward; + +import static androidx.camera.extensions.impl.service.CameraOutputConfig.TYPE_IMAGEREADER; +import static androidx.camera.extensions.impl.service.CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER; +import static androidx.camera.extensions.impl.service.CameraOutputConfig.TYPE_SURFACE; + +import android.content.Context; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.os.Binder; +import android.os.DeadObjectException; +import android.os.RemoteException; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImplBuilder; +import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl; +import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImplBuilder; +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.service.CameraMetadataWrapper; +import androidx.camera.extensions.impl.service.CameraOutputConfig; +import androidx.camera.extensions.impl.service.CameraSessionConfig; +import androidx.camera.extensions.impl.service.ICaptureCallback; +import androidx.camera.extensions.impl.service.ISessionProcessorImpl; +import androidx.camera.extensions.impl.service.OutputSurface; + +import java.util.HashMap; +import java.util.Map; + +public class ForwardSessionProcessor implements SessionProcessorImpl { + private static final String TAG = "ForwardSessionProcessor"; + private final ForwardAdvancedExtender mForwardAdvancedExtender; + + private ISessionProcessorImpl mISessionProcessor; + public ForwardSessionProcessor(@NonNull ForwardAdvancedExtender forwardAdvancedExtender, + @NonNull ISessionProcessorImpl sessionProcessor) { + mForwardAdvancedExtender = forwardAdvancedExtender; + mISessionProcessor = sessionProcessor; + } + + private OutputSurface getOutputSurface(OutputSurfaceImpl outputSurfaceImpl) { + OutputSurface outputSurface = new OutputSurface(); + + androidx.camera.extensions.impl.service.Size extSize = + new androidx.camera.extensions.impl.service.Size(); + extSize.width = outputSurfaceImpl.getSize().getWidth(); + extSize.height = outputSurfaceImpl.getSize().getHeight(); + outputSurface.size = extSize; + outputSurface.imageFormat = outputSurfaceImpl.getImageFormat(); + outputSurface.surface = outputSurfaceImpl.getSurface(); + return outputSurface; + } + + @Override + @NonNull + public Camera2SessionConfigImpl initSession(@NonNull String cameraId, + @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap, + @NonNull Context context, + @NonNull OutputSurfaceImpl previewSurfaceConfig, + @NonNull OutputSurfaceImpl imageCaptureSurfaceConfig, + @Nullable OutputSurfaceImpl imageAnalysisSurfaceConfig) { + return initSession(cameraId, cameraCharacteristicsMap, context, previewSurfaceConfig, + imageCaptureSurfaceConfig, imageAnalysisSurfaceConfig, + /* isRecoveringFromBinderDeath */ false); + } + + @NonNull + private Camera2SessionConfigImpl initSession(@NonNull String cameraId, + @NonNull Map<String, CameraCharacteristics> cameraCharacteristicsMap, + @NonNull Context context, + @NonNull OutputSurfaceImpl previewSurfaceConfig, + @NonNull OutputSurfaceImpl imageCaptureSurfaceConfig, + @Nullable OutputSurfaceImpl imageAnalysisSurfaceConfig, + boolean isRecoveringFromBinderDeath) { + try { + OutputSurface outputSurfacePreview = getOutputSurface(previewSurfaceConfig); + OutputSurface outputSurfaceCapture = getOutputSurface(imageCaptureSurfaceConfig); + OutputSurface outputSurfaceAnalysis = null; + if (imageAnalysisSurfaceConfig != null) { + outputSurfaceAnalysis = getOutputSurface(imageAnalysisSurfaceConfig); + } + + CameraSessionConfig sessionConfig = mISessionProcessor.initSession( + cameraId, + outputSurfacePreview, + outputSurfaceCapture, + outputSurfaceAnalysis); + + Camera2SessionConfigImplBuilder sessionConfigBuilder = + new Camera2SessionConfigImplBuilder(); + CaptureRequest captureRequest = sessionConfig.sessionParameter.toCaptureRequest(); + for (CaptureRequest.Key<?> key : captureRequest.getKeys()) { + CaptureRequest.Key<Object> objKey = (CaptureRequest.Key<Object>) key; + sessionConfigBuilder.addSessionParameter(objKey, captureRequest.get(objKey)); + } + for (CameraOutputConfig outputConfig : sessionConfig.outputConfigs) { + Camera2OutputConfigImplBuilder builder = + getCamera2OutputConfigImplBuilder(outputConfig); + if (outputConfig.sharedSurfaceConfigs != null && + (!outputConfig.sharedSurfaceConfigs.isEmpty())) { + for (CameraOutputConfig sharedSurfaceConfig : + outputConfig.sharedSurfaceConfigs) { + builder.addSurfaceSharingOutputConfig( + getCamera2OutputConfigImplBuilder(sharedSurfaceConfig).build()); + } + } + sessionConfigBuilder.addOutputConfig(builder.build()); + } + + sessionConfigBuilder.setSessionTemplateId(sessionConfig.sessionTemplateId); + return sessionConfigBuilder.build(); + } catch (RemoteException e) { + if ((e instanceof DeadObjectException) && !isRecoveringFromBinderDeath) { + // service died, reinitialize. + mISessionProcessor = mForwardAdvancedExtender.recreateISessionProcessor(); + return initSession(cameraId, cameraCharacteristicsMap, context, previewSurfaceConfig + , imageCaptureSurfaceConfig, imageAnalysisSurfaceConfig, + /* isRecoveringFromBinderDeath */ true ); + } + Log.e(TAG, "initSession failed", e); + throw new IllegalStateException("initSession failed", e); + } + } + + private static Camera2OutputConfigImplBuilder getCamera2OutputConfigImplBuilder( + CameraOutputConfig outputConfig) { + switch (outputConfig.type) { + case TYPE_SURFACE: + return Camera2OutputConfigImplBuilder + .newSurfaceConfig(outputConfig.surface) + .setPhysicalCameraId(outputConfig.physicalCameraId) + .setSurfaceGroupId(outputConfig.surfaceGroupId) + .setOutputConfigId(outputConfig.outputId); + case TYPE_IMAGEREADER: + android.util.Size size = new android.util.Size( + outputConfig.size.width, outputConfig.size.height); + return Camera2OutputConfigImplBuilder + .newImageReaderConfig(size, outputConfig.imageFormat, outputConfig.capacity) + .setPhysicalCameraId(outputConfig.physicalCameraId) + .setSurfaceGroupId(outputConfig.surfaceGroupId) + .setOutputConfigId(outputConfig.outputId); + case TYPE_MULTIRES_IMAGEREADER: + default: + throw new UnsupportedOperationException("Output config type not supported"); + } + } + + @Override + public void deInitSession() { + try { + mISessionProcessor.deInitSession(); + } catch (RemoteException e) { + Log.e(TAG, "deInitSession failed", e); + throw new IllegalStateException("deInitSession failed", e); + } + } + + @Override + public void setParameters(Map<CaptureRequest.Key<?>, Object> parameters) { + try { + mISessionProcessor.setParameters(PlatformApi.createCaptureRequest(parameters)); + } catch (RemoteException e) { + Log.e(TAG, "setParameters failed", e); + // still capture normally will invoke setParameters first and then startCapture. + // We want to fail the startCapture not the setParameters so that the capture failure + // can be propagated to the app. + } + } + + @Override + public int startTrigger(Map<CaptureRequest.Key<?>, Object> triggers, CaptureCallback callback) { + try { + return mISessionProcessor.startTrigger(PlatformApi.createCaptureRequest(triggers), + new CaptureCallbackAdapter(callback)); + } catch (RemoteException e) { + Log.e(TAG, "startTrigger failed", e); + throw new IllegalStateException("startTrigger failed", e); + } + } + + @Override + public void onCaptureSessionStart(RequestProcessorImpl requestProcessor) { + try { + mISessionProcessor.onCaptureSessionStart( + new RequestProcessorAdapter(requestProcessor)); + } catch (RemoteException e) { + Log.e(TAG, "onCaptureSessionStart failed", e); + throw new IllegalStateException("onCaptureSessionStart failed", e); + } + } + + @Override + public void onCaptureSessionEnd() { + try { + mISessionProcessor.onCaptureSessionEnd(); + } catch (RemoteException e) { + Log.e(TAG, "onCaptureSessionEnd failed", e); + throw new IllegalStateException("onCaptureSessionEnd failed", e); + } + } + + @Override + public int startRepeating(CaptureCallback callback) { + try { + return mISessionProcessor.startRepeating(new CaptureCallbackAdapter(callback)); + } catch (RemoteException e) { + Log.e(TAG, "startRepeating failed", e); + // notify the onCaptureFailed callback so that app is notified of the error. + callback.onCaptureFailed(0); + return 0; + } + } + + @Override + public void stopRepeating() { + try { + mISessionProcessor.stopRepeating(); + } catch (RemoteException e) { + Log.e(TAG, "stopRepeating failed", e); + throw new IllegalStateException("startRepeating failed", e); + } + } + + @Override + public int startCapture(CaptureCallback callback) { + try { + return mISessionProcessor.startCapture(new CaptureCallbackAdapter(callback)); + } catch (RemoteException e) { + Log.e(TAG, "startCapture failed", e); + // notify the onCaptureFailed callback so that app is notified of the error. + callback.onCaptureFailed(0); + return 0; + } + } + + @Override + public void abortCapture(int captureSequenceId) { + try { + mISessionProcessor.abortCapture(captureSequenceId); + } catch (RemoteException e) { + Log.e(TAG, "abortCapture failed", e); + throw new IllegalStateException("abortCapture failed", e); + } + } + + private static class CaptureCallbackAdapter extends ICaptureCallback.Stub { + private final SessionProcessorImpl.CaptureCallback mImplCaptureCallback; + + CaptureCallbackAdapter(SessionProcessorImpl.CaptureCallback implCaptureCallback) { + mImplCaptureCallback = implCaptureCallback; + } + + @Override + public void onCaptureStarted(int captureSequenceId, long timeStamp) throws RemoteException { + final long token = Binder.clearCallingIdentity(); + try { + mImplCaptureCallback.onCaptureStarted(captureSequenceId, timeStamp); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onCaptureProcessStarted(int captureSequenceId) throws RemoteException { + final long token = Binder.clearCallingIdentity(); + try { + mImplCaptureCallback.onCaptureProcessStarted(captureSequenceId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onCaptureFailed(int captureSequenceId) throws RemoteException { + final long token = Binder.clearCallingIdentity(); + try { + mImplCaptureCallback.onCaptureFailed(captureSequenceId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onCaptureSequenceCompleted(int captureSequenceId) throws RemoteException { + final long token = Binder.clearCallingIdentity(); + try { + mImplCaptureCallback.onCaptureSequenceCompleted(captureSequenceId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onCaptureSequenceAborted(int captureSequenceId) throws RemoteException { + final long token = Binder.clearCallingIdentity(); + try { + mImplCaptureCallback.onCaptureSequenceAborted(captureSequenceId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onCaptureCompleted(long shutterTimestamp, int captureSequenceId, + CameraMetadataWrapper cameraMetadataWrapper) + throws RemoteException { + TotalCaptureResult captureResult = cameraMetadataWrapper.toTotalCaptureResult(); + + Map<CaptureResult.Key, Object> resultmap = new HashMap<>(); + for (CaptureResult.Key key : captureResult.getKeys()) { + resultmap.put(key, captureResult.get(key)); + } + + final long token = Binder.clearCallingIdentity(); + try { + mImplCaptureCallback.onCaptureCompleted(shutterTimestamp, captureSequenceId, + resultmap); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/PlatformApi.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/PlatformApi.java new file mode 100644 index 00000000..a40a16d0 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/PlatformApi.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.serviceforward; + +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.impl.CameraMetadataNative; + +import java.util.Map; + +public class PlatformApi { + public static CaptureRequest createCaptureRequest( + Map<CaptureRequest.Key<?>, Object> parameters) { + CameraMetadataNative metadataNative = new CameraMetadataNative(); + CaptureRequest.Builder builder = + new CaptureRequest.Builder(metadataNative, false, -1, "0", null); + + for (CaptureRequest.Key<?> key : parameters.keySet()) { + CaptureRequest.Key<Object> objKey = (CaptureRequest.Key<Object>) key; + builder.set(objKey, parameters.get(objKey)); + } + return builder.build(); + } + + public static CaptureRequest createCaptureRequest(CameraMetadataNative cameraMetadataNative) { + CaptureRequest.Builder builder = + new CaptureRequest.Builder(cameraMetadataNative, false, -1, "0", null); + return builder.build(); + } + + public static TotalCaptureResult createTotalCaptureResult( + CameraMetadataNative cameraMetadataNative) { + return new TotalCaptureResult(cameraMetadataNative, 0); + } + + public static CameraMetadataNative createCameraMetadataNative( + Map<CaptureRequest.Key<?>, Object> parameters) { + CameraMetadataNative metadataNative = new CameraMetadataNative(); + for (CaptureRequest.Key<?> key : parameters.keySet()) { + CaptureRequest.Key<Object> objKey = (CaptureRequest.Key<Object>) key; + metadataNative.set(objKey, parameters.get(objKey)); + } + return metadataNative; + } +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/RequestProcessorAdapter.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/RequestProcessorAdapter.java new file mode 100644 index 00000000..3cc1d24c --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/RequestProcessorAdapter.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.serviceforward; + +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.os.RemoteException; +import android.util.Log; + +import androidx.camera.extensions.impl.advanced.ImageProcessorImpl; +import androidx.camera.extensions.impl.advanced.ImageReferenceImpl; +import androidx.camera.extensions.impl.advanced.RequestProcessorImpl; +import androidx.camera.extensions.impl.service.CaptureFailureWrapper; +import androidx.camera.extensions.impl.service.CaptureResultWrapper; +import androidx.camera.extensions.impl.service.IImageProcessorImpl; +import androidx.camera.extensions.impl.service.IRequestCallback; +import androidx.camera.extensions.impl.service.IRequestProcessorImpl; +import androidx.camera.extensions.impl.service.ImageWrapper; +import androidx.camera.extensions.impl.service.Request; +import androidx.camera.extensions.impl.service.TotalCaptureResultWrapper; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class RequestProcessorAdapter extends IRequestProcessorImpl.Stub { + private static final String TAG = "RequestProcessorAdapter"; + + private RequestProcessorImpl mRequestProcessorImpl; + RequestProcessorAdapter(RequestProcessorImpl requestProcessorImpl) { + mRequestProcessorImpl = requestProcessorImpl; + } + @Override + public void setImageProcessor(int outputfigId, IImageProcessorImpl imageProcessor) + throws RemoteException { + mRequestProcessorImpl.setImageProcessor(outputfigId, new ImageProcessorImpl() { + @Override + public void onNextImageAvailable(int outputConfigId, long timestampNs, + ImageReferenceImpl imageReference, String physicalCameraId) { + try { + imageProcessor.onNextImageAvailable(outputConfigId, + new ImageWrapper(imageReference.get()), physicalCameraId); + imageReference.decrement(); + } catch (RemoteException e) { + Log.e(TAG, "Can't connect to the binder!", e); + } + } + }); + } + + @Override + public int submit(Request request, IRequestCallback requestCallback) + throws RemoteException { + return submitBurst(Arrays.asList(request), requestCallback); + } + + @Override + public int submitBurst(List<Request> requests, IRequestCallback requestCallback) + throws RemoteException { + List<RequestProcessorImpl.Request> implRequests = new ArrayList<>(); + Map<RequestProcessorImpl.Request, Request> requestsMap = new HashMap<>(); + for (Request request : requests) { + RequestProcessorImpl.Request implRequest = new ImplRequestAdapter(request); + implRequests.add(implRequest); + requestsMap.put(implRequest, request); + } + return mRequestProcessorImpl.submit(implRequests, + new ImplCaptureCallbackAdapter(requestsMap, requestCallback)); + } + + @Override + public int setRepeating(Request request, IRequestCallback requestCallback) + throws RemoteException { + Map<RequestProcessorImpl.Request, Request> requestsMap = new HashMap<>(); + RequestProcessorImpl.Request implRequest = new ImplRequestAdapter(request); + requestsMap.put(implRequest, request); + + return mRequestProcessorImpl.setRepeating(implRequest, + new ImplCaptureCallbackAdapter(requestsMap, requestCallback)); + } + + @Override + public void abortCaptures() throws RemoteException { + mRequestProcessorImpl.abortCaptures(); + } + + @Override + public void stopRepeating() throws RemoteException { + mRequestProcessorImpl.stopRepeating(); + } + + private static class ImplRequestAdapter implements RequestProcessorImpl.Request { + private Request mRequest; + ImplRequestAdapter(Request request) { + mRequest = request; + } + + @Override + public List<Integer> getTargetOutputConfigIds() { + List<Integer> result = new ArrayList<>(mRequest.targetOutputConfigIds.length); + for (int id : mRequest.targetOutputConfigIds) { + result.add(id); + } + return result; + } + + @Override + public Map<CaptureRequest.Key<?>, Object> getParameters() { + CaptureRequest captureRequest = mRequest.parameters.toCaptureRequest(); + Map<CaptureRequest.Key<?>, Object> parameters = new HashMap<>(); + for (CaptureRequest.Key<?> key : captureRequest.getKeys()) { + parameters.put(key, captureRequest.get(key)); + } + return parameters; + } + + @Override + public Integer getTemplateId() { + return mRequest.templateId; + } + } + + private static class ImplCaptureCallbackAdapter implements RequestProcessorImpl.Callback { + private Map<RequestProcessorImpl.Request, Request> mRequestsMap; + private IRequestCallback mRequestCallback; + ImplCaptureCallbackAdapter(Map<RequestProcessorImpl.Request, Request> requestsMap, + IRequestCallback requestCallback) { + mRequestCallback = requestCallback; + mRequestsMap = requestsMap; + } + + private Request getRequest(RequestProcessorImpl.Request implRequest) { + return mRequestsMap.get(implRequest); + } + + @Override + public void onCaptureStarted(RequestProcessorImpl.Request implRequest, long frameNumber, + long timestamp) { + try { + mRequestCallback.onCaptureStarted(getRequest(implRequest).requestId, + frameNumber, timestamp); + } catch (RemoteException e) { + Log.e(TAG, "Can't connect to the binder!", e); + } + } + + @Override + public void onCaptureProgressed(RequestProcessorImpl.Request implRequest, + CaptureResult partialResult) { + try { + mRequestCallback.onCaptureProgressed(getRequest(implRequest).requestId, + new CaptureResultWrapper(partialResult)); + } catch(RemoteException e) { + Log.e(TAG, "Can't connect to the binder!", e); + } + } + + @Override + public void onCaptureCompleted(RequestProcessorImpl.Request implRequest, + TotalCaptureResult totalCaptureResult) { + try { + mRequestCallback.onCaptureCompleted(getRequest(implRequest).requestId, + new TotalCaptureResultWrapper(totalCaptureResult)); + } catch(RemoteException e) { + Log.e(TAG, "Can't connect to the binder!", e); + } + } + + @Override + public void onCaptureFailed(RequestProcessorImpl.Request implRequest, + CaptureFailure captureFailure) { + try { + + mRequestCallback.onCaptureFailed(getRequest(implRequest).requestId, + new CaptureFailureWrapper(captureFailure)); + } catch(RemoteException e) { + Log.e(TAG, "Can't connect to the binder!", e); + } + } + + @Override + public void onCaptureBufferLost(RequestProcessorImpl.Request implRequest, long frameNumber, + int outputStreamId) { + try { + mRequestCallback.onCaptureBufferLost(getRequest(implRequest).requestId, + frameNumber, outputStreamId); + } catch(RemoteException e) { + Log.e(TAG, "Can't connect to the binder!", e); + } + } + + @Override + public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) { + try { + mRequestCallback.onCaptureSequenceCompleted(sequenceId, frameNumber); + } catch(RemoteException e) { + Log.e(TAG, "Can't connect to the binder!", e); + } + } + + @Override + public void onCaptureSequenceAborted(int sequenceId) { + try { + mRequestCallback.onCaptureSequenceAborted(sequenceId); + } catch(RemoteException e) { + Log.e(TAG, "Can't connect to the binder!", e); + } + } + } + +} diff --git a/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ServiceManager.java b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ServiceManager.java new file mode 100644 index 00000000..76de1c92 --- /dev/null +++ b/camera2/extensions/service_based_sample/oem_library/src/java/androidx/camera/extensions/impl/serviceforward/ServiceManager.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2023 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 androidx.camera.extensions.impl.serviceforward; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.AsyncTask; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.camera.extensions.impl.InitializerImpl; +import androidx.camera.extensions.impl.service.IAdvancedExtenderImpl; +import androidx.camera.extensions.impl.service.IImageCaptureExtenderImpl; +import androidx.camera.extensions.impl.service.IPreviewExtenderImpl; +import androidx.camera.extensions.impl.service.IExtensionsService; +import androidx.camera.extensions.impl.service.IOnExtensionsDeinitializedCallback; +import androidx.camera.extensions.impl.service.IOnExtensionsInitializedCallback; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +public class ServiceManager { + private static final String TAG = "ServiceManager"; + private static final int SERVICE_DELAY_MS = 1000; + private static final String SERVICE_PACKAGE_NAME = "com.android.oemextensions"; + private static final String SERVICE_SERVICE_NAME = + "com.android.oemextensions.ExtensionsService"; + + private static ServiceManager sServiceManager; + private static final Object mLock = new Object(); + + public static void init(@Nullable Context context, @NonNull String version, + @Nullable InitializerImpl.OnExtensionsInitializedCallback callback, + @Nullable Executor executor) { + synchronized (mLock) { + if (sServiceManager == null) { + sServiceManager = new ServiceManager(context, version); + } + sServiceManager.bindServiceSync(context); + } + + try { + Executor executorForCallback = + (executor != null)? executor: (cmd) -> cmd.run(); + + sServiceManager.mExtensionService.initialize(version, + new IOnExtensionsInitializedCallback.Stub() { + @Override + public void onSuccess() throws RemoteException { + executorForCallback.execute( () -> { + if (callback != null) { + callback.onSuccess(); + } + Log.d(TAG, "initialize success!"); + }); + } + + @Override + public void onFailure(int error) throws RemoteException { + executorForCallback.execute( () -> { + if (callback != null) { + callback.onFailure(error); + } + Log.d(TAG, "initialize failed! error=" + error); + }); + } + }); + } catch (RemoteException e){ + throw new IllegalStateException("Failed to connect to extensions service", e); + } + } + + @NonNull + public static ServiceManager getInstance() { + return sServiceManager; + } + + public ServiceManager(@NonNull Context context, @NonNull String version) { + mContext = context; + mVersion = version; + } + + private final Context mContext; + private final String mVersion; + + private ServiceConnection mServiceConnection; + private IExtensionsService mExtensionService; + + void bindServiceSync(Context context) { + if (mServiceConnection == null) { + CountDownLatch countDownLatch = new CountDownLatch(1); + mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, IBinder binder) { + mExtensionService = IExtensionsService.Stub.asInterface(binder); + Log.d(TAG, "service connected"); + countDownLatch.countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + Log.e(TAG, "service disconnected"); + mExtensionService = null; + mServiceConnection = null; + } + }; + + Intent intent = new Intent(); + intent.setClassName(SERVICE_PACKAGE_NAME, SERVICE_SERVICE_NAME); + Log.d(TAG, "bindService start. intent = " + intent); + context.bindService(intent, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | + Context.BIND_ABOVE_CLIENT, AsyncTask.THREAD_POOL_EXECUTOR, + mServiceConnection); + + try { + boolean success = countDownLatch.await(SERVICE_DELAY_MS, TimeUnit.MILLISECONDS); + if (!success) { + Log.e(TAG, "Timed out while initializing proxy service!"); + } + } catch (InterruptedException e) { + Log.e(TAG, "Interrupted while initializing proxy service!"); + } + } + } + + public void deinit(@NonNull InitializerImpl.OnExtensionsDeinitializedCallback callback, + @NonNull Executor executor) { + try { + mExtensionService.deInitialize(new IOnExtensionsDeinitializedCallback.Stub() { + @Override + public void onSuccess() throws RemoteException { + executor.execute( () -> { + callback.onSuccess(); + }); + } + + @Override + public void onFailure(int error) throws RemoteException { + executor.execute( () -> { + callback.onFailure(error); + }); + } + }); + } catch (RemoteException e) { + throw new IllegalStateException("Failed to connect to extensions service", e); + } + } + + @NonNull + public IAdvancedExtenderImpl createAdvancedExtenderImpl(int extensionType) { + try { + synchronized (mLock) { + if (mExtensionService == null) { + init(mContext, mVersion, null, null); + } + } + return mExtensionService.initializeAdvancedExtension(extensionType); + } catch (RemoteException e) { + Log.e(TAG, "initializeAdvancedExtension failed", e); + throw new IllegalStateException("initializeAdvancedExtension failed", e); + } + } + + @NonNull + public IImageCaptureExtenderImpl createImageCaptureExtenderImpl(int extensionType) { + try { + synchronized (mLock) { + if (mExtensionService == null) { + bindServiceSync(mContext); + } + } + return mExtensionService.initializeImageCaptureExtension(extensionType); + } catch (RemoteException e) { + Log.e(TAG, "initializeImageCaptureExtender failed", e); + throw new IllegalStateException("initializeImageCaptureExtender failed", e); + } + } + + @NonNull + public IPreviewExtenderImpl createPreviewExtenderImpl(int extensionType) { + try { + synchronized (mLock) { + if (mExtensionService == null) { + bindServiceSync(mContext); + } + } + return mExtensionService.initializePreviewExtension(extensionType); + } catch (RemoteException e) { + Log.e(TAG, "initializePreviewExtension failed", e); + throw new IllegalStateException("initializePreviewExtension failed", e); + } + } +} diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java index ccb0dacf..bd605708 100755 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoImageCaptureExtenderImpl.java @@ -95,6 +95,11 @@ public final class AutoImageCaptureExtenderImpl implements ImageCaptureExtenderI throw new RuntimeException("Stub, replace with implementation."); } + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + @Nullable @Override public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { @@ -113,4 +118,23 @@ public final class AutoImageCaptureExtenderImpl implements ImageCaptureExtenderI throw new RuntimeException("Stub, replace with implementation."); } + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java index 100f6658..0c4577a4 100755 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/AutoPreviewExtenderImpl.java @@ -93,4 +93,9 @@ public final class AutoPreviewExtenderImpl implements PreviewExtenderImpl { public List<Pair<Integer, Size[]>> getSupportedResolutions() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java index 2d266390..50c80407 100755 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyImageCaptureExtenderImpl.java @@ -95,6 +95,11 @@ public final class BeautyImageCaptureExtenderImpl implements ImageCaptureExtende throw new RuntimeException("Stub, replace with implementation."); } + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + @Nullable @Override public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { @@ -112,4 +117,24 @@ public final class BeautyImageCaptureExtenderImpl implements ImageCaptureExtende public List<CaptureResult.Key> getAvailableCaptureResultKeys() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java index bc3e48dd..1f501745 100755 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BeautyPreviewExtenderImpl.java @@ -93,4 +93,9 @@ public final class BeautyPreviewExtenderImpl implements PreviewExtenderImpl { public List<Pair<Integer, Size[]>> getSupportedResolutions() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java index 66c5839d..ee777cf9 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehImageCaptureExtenderImpl.java @@ -95,6 +95,11 @@ public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtender throw new RuntimeException("Stub, replace with implementation."); } + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + @Nullable @Override public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { @@ -113,4 +118,23 @@ public final class BokehImageCaptureExtenderImpl implements ImageCaptureExtender throw new RuntimeException("Stub, replace with implementation."); } + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java index ff588623..1dc5ed79 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/BokehPreviewExtenderImpl.java @@ -91,4 +91,9 @@ public final class BokehPreviewExtenderImpl implements PreviewExtenderImpl { public List<Pair<Integer, Size[]>> getSupportedResolutions() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java index 3eee146a..f4719b8b 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/CaptureProcessorImpl.java @@ -21,6 +21,7 @@ import android.graphics.ImageFormat; import android.hardware.camera2.TotalCaptureResult; import android.media.Image; import android.util.Pair; +import android.util.Size; import android.view.Surface; import java.util.Map; @@ -46,6 +47,29 @@ public interface CaptureProcessorImpl extends ProcessorImpl { void process(Map<Integer, Pair<Image, TotalCaptureResult>> results); /** + * Informs the CaptureProcessorImpl where it should write the postview output to. + * This will only be invoked once if a valid postview surface was set. + * + * @param surface A valid {@link ImageFormat#YUV_420_888} {@link Surface} + * that the CaptureProcessorImpl should write data into. + * @since 1.4 + */ + void onPostviewOutputSurface(Surface surface); + + /** + * Invoked when the Camera Framework changes the configured output resolution for + * still capture and postview. + * + * <p>After this call, {@link CaptureProcessorImpl} should expect any {@link Image} received as + * input for still capture and postview to be at the specified resolutions. + * + * @param size for the surface for still capture. + * @param postviewSize for the surface for postview. + * @since 1.4 + */ + void onResolutionUpdate(Size size, Size postviewSize); + + /** * Process a set images captured that were requested. * * <p> The result of the processing step should be written to the {@link Surface} that was @@ -63,4 +87,30 @@ public interface CaptureProcessorImpl extends ProcessorImpl { */ void process(Map<Integer, Pair<Image, TotalCaptureResult>> results, ProcessResultImpl resultCallback, Executor executor); + + /** + * Process a set images captured that were requested for both postview and + * still capture. + * + * <p> This processing method will be called if a postview was requested, therefore the + * processed postview should be written to the + * {@link Surface} received by {@link #onPostviewOutputSurface(Surface, int)}. + * The final result of the processing step should be written to the {@link Surface} that was + * received by {@link #onOutputSurface(Surface, int)}. Since postview should be available + * before the capture, it should be processed and written to the surface before + * the final capture is processed. + * + * @param results The map of {@link ImageFormat#YUV_420_888} format images and + * metadata to process. The {@link Image} that are contained within + * the map will become invalid after this method completes, so no + * references to them should be kept. + * @param resultCallback Capture result callback to be called once the capture result + * values of the processed image are ready. + * @param executor The executor to run the callback on. If null then the callback + * will run on any arbitrary executor. + * @throws RuntimeException if postview feature is not supported + * @since 1.4 + */ + void processWithPostview(Map<Integer, Pair<Image, TotalCaptureResult>> results, + ProcessResultImpl resultCallback, Executor executor); } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java index 2879568f..4a3b01cd 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ExtenderStateListener.java @@ -80,4 +80,20 @@ public interface ExtenderStateListener { * @return The request information to customize the session. */ CaptureStageImpl onDisableSession(); + + /** + * This will be invoked before the {@link android.hardware.camera2.CameraCaptureSession} is + * initialized and must return a valid camera session type + * {@link android.hardware.camera2.params.SessionConfiguration#getSessionType} + * to be used to configure camera capture session. Both the preview and the image capture + * extender must return the same session type value for a specific extension type. If there + * is inconsistency between the session type values from preview and image extenders, then + * the session configuration will fail. + * + * @since 1.4 + * @return Camera capture session type. Regular and vendor specific types are supported but + * not high speed values. The extension can return -1 in which case the camera capture session + * will be configured to use the default regular type. + */ + int onSessionType(); } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java index f1191dcf..f3fd2f3b 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrImageCaptureExtenderImpl.java @@ -95,6 +95,11 @@ public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderIm throw new RuntimeException("Stub, replace with implementation."); } + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + @Nullable @Override public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { @@ -112,4 +117,24 @@ public final class HdrImageCaptureExtenderImpl implements ImageCaptureExtenderIm public List<CaptureResult.Key> getAvailableCaptureResultKeys() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java index 0eb4a610..af484646 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/HdrPreviewExtenderImpl.java @@ -93,4 +93,9 @@ public final class HdrPreviewExtenderImpl implements PreviewExtenderImpl { public List<Pair<Integer, Size[]>> getSupportedResolutions() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java index 88bd105a..70c1804e 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java @@ -85,6 +85,21 @@ public interface ImageCaptureExtenderImpl extends ExtenderStateListener { List<Pair<Integer, Size[]>> getSupportedResolutions(); /** + * Returns the customized supported postview resolutions for a still capture using + * its size. + * + * <p>Pair list composed with {@link ImageFormat} and {@link Size} array will be returned. + * + * <p>The returned resolutions should be subset of the supported sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap} for the camera device. + * + * @return the customized supported resolutions, or null to support all sizes retrieved from + * {@link android.hardware.camera2.params.StreamConfigurationMap}. + * @since 1.4 + */ + List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize); + + /** * Returns the estimated capture latency range in milliseconds for the target capture * resolution. * @@ -159,4 +174,44 @@ public interface ImageCaptureExtenderImpl extends ExtenderStateListener { * @since 1.3 */ List<CaptureResult.Key> getAvailableCaptureResultKeys(); + + /** + * Advertise support for {@link ProcessResultImpl#onCaptureProcessProgressed}. + * + * @return {@code true} in case the process progress callback is supported and is expected to + * be triggered, {@code false} otherwise. + * @since 1.4 + */ + boolean isCaptureProcessProgressAvailable(); + + /** + * Returns the dynamically calculated capture latency pair in milliseconds. + * + * <p>In contrast to {@link #getEstimatedCaptureLatencyRange} this method is guaranteed to be + * called after the camera capture session is initialized and camera preview is enabled. + * The measurement is expected to take in to account dynamic parameters such as the current + * scene, the state of 3A algorithms, the state of internal HW modules and return a more + * accurate assessment of the still capture latency.</p> + * + * @return pair that includes the estimated input frame/frames camera capture latency as the + * first field and the estimated post-processing latency {@link CaptureProcessorImpl#process} + * as the second pair field. Both first and second fields will be in milliseconds. The total + * still capture latency will be the sum of both the first and second values. + * The pair is expected to be null if the dynamic latency estimation is not supported. + * If clients have not configured a still capture output, then this method can also return a + * null pair. + * @since 1.4 + */ + Pair<Long, Long> getRealtimeCaptureLatency(); + + /** + * Indicates whether the extension supports the postview for still capture feature. + * If the extension is using HAL processing, false should be returned since the + * postview feature is not currently supported for this case. + * + * @return {@code true} in case postview for still capture is supported + * {@code false} otherwise. + * @since 1.4 + */ + boolean isPostviewAvailable(); } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java index c8ac9788..6f0eaef9 100755 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightImageCaptureExtenderImpl.java @@ -95,6 +95,11 @@ public final class NightImageCaptureExtenderImpl implements ImageCaptureExtender throw new RuntimeException("Stub, replace with implementation."); } + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions(Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + @Nullable @Override public Range<Long> getEstimatedCaptureLatencyRange(@NonNull Size captureOutputSize) { @@ -112,4 +117,24 @@ public final class NightImageCaptureExtenderImpl implements ImageCaptureExtender public List<CaptureResult.Key> getAvailableCaptureResultKeys() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java index a5809f6b..825994f5 100755 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/NightPreviewExtenderImpl.java @@ -93,4 +93,9 @@ public final class NightPreviewExtenderImpl implements PreviewExtenderImpl { public List<Pair<Integer, Size[]>> getSupportedResolutions() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public int onSessionType() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java index d0e3605d..0e154450 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/ProcessResultImpl.java @@ -25,7 +25,6 @@ import java.util.List; /** * Allows clients to receive information about the capture result values of processed frames. * - * @since 1.3 */ @SuppressLint("UnknownNullness") public interface ProcessResultImpl { @@ -40,6 +39,22 @@ public interface ProcessResultImpl { * must also be passed as part of this callback. Both Camera2 and * CameraX guarantee that those two settings and results are always * supported and applied by the corresponding framework. + * @since 1.3 */ void onCaptureCompleted(long shutterTimestamp, List<Pair<CaptureResult.Key, Object>> result); + + /** + * Capture progress callback that needs to be called when the process capture is + * ongoing and includes the estimated progress of the processing. + * + * <p>Extensions must ensure that they always call this callback with monotonically increasing + * values.</p> + * + * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the + * callback is expected to be called once when processing is done with value 100.</p> + * + * @param progress Value between 0 and 100. + * @since 1.4 + */ + void onCaptureProcessProgressed(int progress); } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java index 465bfe88..d13efc85 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AdvancedExtenderImpl.java @@ -124,6 +124,17 @@ public interface AdvancedExtenderImpl { Map<Integer, List<Size>> getSupportedCaptureOutputResolutions(String cameraId); /** + * Returns supported output format/size map for postview image. OEM is required to support + * both JPEG and YUV_420_888 format output. + * + * <p>The surface created with this supported format/size could configure + * intermediate surfaces(YUV/RAW..) and write the output to the output surface.</p> + * + * @since 1.4 + */ + Map<Integer, List<Size>> getSupportedPostviewResolutions(Size captureSize); + + /** * Returns supported output sizes for Image Analysis (YUV_420_888 format). * * <p>OEM can optionally support a YUV surface for ImageAnalysis along with Preview/ImageCapture @@ -185,4 +196,24 @@ public interface AdvancedExtenderImpl { * @since 1.3 */ List<CaptureResult.Key> getAvailableCaptureResultKeys(); + + /** + * Advertise support for {@link SessionProcessorImpl#onCaptureProcessProgressed}. + * + * @return {@code true} in case the process progress callback is supported and is expected to + * be triggered, {@code false} otherwise. + * @since 1.4 + */ + public boolean isCaptureProcessProgressAvailable(); + + /** + * Indicates whether the extension supports the postview for still capture feature. + * If the extension is using HAL processing, false should be returned since the + * postview feature is not currently supported for this case. + * + * @return {@code true} in case postview for still capture is supported + * {@code false} otherwise. + * @since 1.4 + */ + boolean isPostviewAvailable(); } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java index 0d3bd4a0..8c3ac11c 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/AutoAdvancedExtenderImpl.java @@ -69,6 +69,12 @@ public class AutoAdvancedExtenderImpl implements AdvancedExtenderImpl { } @Override + public Map<Integer, List<Size>> getSupportedPostviewResolutions( + Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override public List<Size> getSupportedYuvAnalysisResolutions( String cameraId) { throw new RuntimeException("Stub, replace with implementation."); @@ -88,4 +94,14 @@ public class AutoAdvancedExtenderImpl implements AdvancedExtenderImpl { public List<CaptureResult.Key> getAvailableCaptureResultKeys() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java index 1dec3266..135306c8 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BeautyAdvancedExtenderImpl.java @@ -69,6 +69,12 @@ public class BeautyAdvancedExtenderImpl implements AdvancedExtenderImpl { } @Override + public Map<Integer, List<Size>> getSupportedPostviewResolutions( + Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override public List<Size> getSupportedYuvAnalysisResolutions( String cameraId) { throw new RuntimeException("Stub, replace with implementation."); @@ -88,4 +94,14 @@ public class BeautyAdvancedExtenderImpl implements AdvancedExtenderImpl { public List<CaptureResult.Key> getAvailableCaptureResultKeys() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java index bc41b4e0..fa4ad0dc 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/BokehAdvancedExtenderImpl.java @@ -69,6 +69,12 @@ public class BokehAdvancedExtenderImpl implements AdvancedExtenderImpl { } @Override + public Map<Integer, List<Size>> getSupportedPostviewResolutions( + Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override public List<Size> getSupportedYuvAnalysisResolutions( String cameraId) { throw new RuntimeException("Stub, replace with implementation."); @@ -88,4 +94,14 @@ public class BokehAdvancedExtenderImpl implements AdvancedExtenderImpl { public List<CaptureResult.Key> getAvailableCaptureResultKeys() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java index d1217177..850f0e1b 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImpl.java @@ -43,4 +43,16 @@ public interface Camera2SessionConfigImpl { * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters}. */ int getSessionTemplateId(); + + + /** + * Retrieves the session type to be used when initializing the + * {@link android.hardware.camera2.CameraCaptureSession}. + * + * @since 1.4 + * @return Camera capture session type. Regular and vendor specific types are supported but + * not high speed values. The extension can return -1 in which case the camera capture session + * will be configured to use the default regular type. + */ + int getSessionType(); } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java index a3011666..dc1feccd 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/Camera2SessionConfigImplBuilder.java @@ -20,6 +20,7 @@ package androidx.camera.extensions.impl.advanced; import android.annotation.SuppressLint; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.SessionConfiguration; import java.util.ArrayList; import java.util.HashMap; @@ -32,6 +33,7 @@ import java.util.Map; @SuppressLint("UnknownNullness") public class Camera2SessionConfigImplBuilder { private int mSessionTemplateId = CameraDevice.TEMPLATE_PREVIEW; + private int mSessionType = SessionConfiguration.SESSION_REGULAR; Map<CaptureRequest.Key<?>, Object> mSessionParameters = new HashMap<>(); List<Camera2OutputConfigImpl> mCamera2OutputConfigs = new ArrayList<>(); @@ -86,6 +88,13 @@ public class Camera2SessionConfigImplBuilder { } /** + * Gets the camera capture session type. + */ + public int getSessionType() { + return mSessionType; + } + + /** * Builds a {@link Camera2SessionConfigImpl} instance. */ public Camera2SessionConfigImpl build() { @@ -95,6 +104,7 @@ public class Camera2SessionConfigImplBuilder { private static class Camera2SessionConfigImplImpl implements Camera2SessionConfigImpl { int mSessionTemplateId; + int mSessionType; Map<CaptureRequest.Key<?>, Object> mSessionParameters; List<Camera2OutputConfigImpl> mCamera2OutputConfigs; @@ -102,6 +112,7 @@ public class Camera2SessionConfigImplBuilder { mSessionTemplateId = builder.getSessionTemplateId(); mSessionParameters = builder.getSessionParameters(); mCamera2OutputConfigs = builder.getCamera2OutputConfigs(); + mSessionType = builder.getSessionType(); } @Override @@ -118,6 +129,11 @@ public class Camera2SessionConfigImplBuilder { public int getSessionTemplateId() { return mSessionTemplateId; } + + @Override + public int getSessionType() { + return mSessionType; + } } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java index 06157dc3..dc5b2b60 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/HdrAdvancedExtenderImpl.java @@ -70,6 +70,12 @@ public class HdrAdvancedExtenderImpl implements AdvancedExtenderImpl { } @Override + public Map<Integer, List<Size>> getSupportedPostviewResolutions( + Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override public List<Size> getSupportedYuvAnalysisResolutions( String cameraId) { throw new RuntimeException("Stub, replace with implementation."); @@ -89,4 +95,14 @@ public class HdrAdvancedExtenderImpl implements AdvancedExtenderImpl { public List<CaptureResult.Key> getAvailableCaptureResultKeys() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java index ce17c4f7..037e9479 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/ImageProcessorImpl.java @@ -26,8 +26,7 @@ import android.annotation.SuppressLint; @SuppressLint("UnknownNullness") public interface ImageProcessorImpl { /** - * The reference count will be decremented when this method returns. If an extension wants - * to hold onto the image it should increment the reference count in this method and + * The reference count will not be decremented when this method returns. Extensions must * decrement it when the image is no longer needed. * * <p>If OEM is not closing(decrement) the image fast enough, the imageReference passed diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java index 97da5c14..5b0ed8ee 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/NightAdvancedExtenderImpl.java @@ -69,6 +69,12 @@ public class NightAdvancedExtenderImpl implements AdvancedExtenderImpl { } @Override + public Map<Integer, List<Size>> getSupportedPostviewResolutions( + Size captureSize) { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override public List<Size> getSupportedYuvAnalysisResolutions( String cameraId) { throw new RuntimeException("Stub, replace with implementation."); @@ -88,4 +94,14 @@ public class NightAdvancedExtenderImpl implements AdvancedExtenderImpl { public List<CaptureResult.Key> getAvailableCaptureResultKeys() { throw new RuntimeException("Stub, replace with implementation."); } + + @Override + public boolean isCaptureProcessProgressAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } + + @Override + public boolean isPostviewAvailable() { + throw new RuntimeException("Stub, replace with implementation."); + } } diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java new file mode 100644 index 00000000..723f0f4e --- /dev/null +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java @@ -0,0 +1,35 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.camera.extensions.impl.advanced; + +import android.annotation.SuppressLint; + +/** + * For specifying the output surface configurations for the extension. + * + * @since 1.4 + */ +@SuppressLint("UnknownNullness") +public interface OutputSurfaceConfigurationImpl { + public OutputSurfaceImpl getPreviewOutputSurface(); + + public OutputSurfaceImpl getImageCaptureOutputSurface(); + + public OutputSurfaceImpl getImageAnalysisOutputSurface(); + + public OutputSurfaceImpl getPostviewOutputSurface(); +} diff --git a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java index fabfc2bf..06270812 100644 --- a/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java +++ b/camera2/extensions/stub/src/main/java/androidx/camera/extensions/impl/advanced/SessionProcessorImpl.java @@ -21,6 +21,7 @@ import android.content.Context; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.util.Pair; import android.view.Surface; import java.util.Map; @@ -64,9 +65,59 @@ public interface SessionProcessorImpl { * preparing a CameraCaptureSession. After initSession() is called, the camera ID, * cameraCharacteristics and context will not change until deInitSession() has been called. * - * <p>CameraX specifies the output surface configurations for preview, image capture and image - * analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which consists of a - * list of {@link Camera2OutputConfigImpl} and session parameters. The + * <p>CameraX / Camera2 specifies the output surface configurations for preview using + * {@link OutputSurfaceConfigurationImpl#getPreviewOutputSurface}, image capture using + * {@link OutputSurfaceConfigurationImpl#getImageCaptureOutputSurface}, and image analysis + * [optional] using {@link OutputSurfaceConfigurationImpl#getImageAnalysisOutputSurface}. + * And OEM returns a {@link Camera2SessionConfigImpl} which consists of a list of + * {@link Camera2OutputConfigImpl} and session parameters. The {@link Camera2SessionConfigImpl} + * will be used to configure the CameraCaptureSession. + * + * <p>OEM is responsible for outputting correct camera images output to these output surfaces. + * OEM can have the following options to enable the output: + * <pre> + * (1) Add these output surfaces in CameraCaptureSession directly using + * {@link Camera2OutputConfigImplBuilder#newSurfaceConfig(Surface)} }. Processing is done in + * HAL. + * + * (2) Use surface sharing with other surface by calling + * {@link Camera2OutputConfigImplBuilder#addSurfaceSharingOutputConfig(Camera2OutputConfigImpl)} + * to add the output surface to the other {@link Camera2OutputConfigImpl}. + * + * (3) Process output from other surfaces (RAW, YUV..) and write the result to the output + * surface. The output surface won't be contained in the returned + * {@link Camera2SessionConfigImpl}. + * </pre> + * + * <p>{@link Camera2OutputConfigImplBuilder} and {@link Camera2SessionConfigImplBuilder} + * implementations are provided in the stub for OEM to construct the + * {@link Camera2OutputConfigImpl} and {@link Camera2SessionConfigImpl} instances. + * + * @param surfaceConfigs contains output surfaces for preview, image capture, and an + * optional output config for image analysis (YUV_420_888). + * @return a {@link Camera2SessionConfigImpl} consisting of a list of + * {@link Camera2OutputConfigImpl} and session parameters which will decide the + * {@link android.hardware.camera2.params.SessionConfiguration} for configuring the + * CameraCaptureSession. Please note that the OutputConfiguration list may not be part of any + * supported or mandatory stream combination BUT OEM must ensure this list will always + * produce a valid camera capture session. + * + * @since 1.4 + */ + Camera2SessionConfigImpl initSession( + String cameraId, + Map<String, CameraCharacteristics> cameraCharacteristicsMap, + Context context, + OutputSurfaceConfigurationImpl surfaceConfigs); + + /** + * Initializes the session for the extension. This is where the OEMs allocate resources for + * preparing a CameraCaptureSession. After initSession() is called, the camera ID, + * cameraCharacteristics and context will not change until deInitSession() has been called. + * + * <p>CameraX / Camera 2 specifies the output surface configurations for preview, image capture + * and image analysis[optional]. And OEM returns a {@link Camera2SessionConfigImpl} which + * consists of a list of {@link Camera2OutputConfigImpl} and session parameters. The * {@link Camera2SessionConfigImpl} will be used to configure the CameraCaptureSession. * * <p>OEM is responsible for outputting correct camera images output to these output surfaces. @@ -188,11 +239,54 @@ public interface SessionProcessorImpl { int startCapture(CaptureCallback callback); /** + * Start a multi-frame capture with a postview. {@link #startCapture(CaptureCallback)} + * will be used for captures without a postview request. + * + * Postview will be available before the capture. Upon postview completion, + * {@code OnImageAvailableListener#onImageAvailable} will be called on the ImageReader + * that creates the postview output surface. When the capture is completed, + * {@link CaptureCallback#onCaptureSequenceCompleted} is called and + * {@code OnImageAvailableListener#onImageAvailable} will also be called on the ImageReader + * that creates the image capture output surface. + * + * <p>Only one capture can perform at a time. Starting a capture when another capture is + * running will cause onCaptureFailed to be called immediately. + * + * @param callback a callback to report the status. + * @return the id of the capture sequence. + * @since 1.4 + */ + int startCaptureWithPostview(CaptureCallback callback); + + /** * Abort all capture tasks. */ void abortCapture(int captureSequenceId); /** + * Returns the dynamically calculated capture latency pair in milliseconds. + * + * <p>In contrast to {@link AdvancedExtenderImpl#getEstimatedCaptureLatencyRange} this method is + * guaranteed to be called after {@link #onCaptureSessionStart}. + * The measurement is expected to take in to account dynamic parameters such as the current + * scene, the state of 3A algorithms, the state of internal HW modules and return a more + * accurate assessment of the still capture latency.</p> + * + * @return pair that includes the estimated input frame/frames camera capture latency as the + * first field. This is the time between {@link #onCaptureStarted} and + * {@link #onCaptureProcessStarted}. The second field value includes the estimated + * post-processing latency. This is the time between {@link #onCaptureProcessStarted} until + * the processed frame returns back to the client registered surface. + * Both first and second values will be in milliseconds. The total still capture latency will be + * the sum of both the first and second values of the pair. + * The pair is expected to be null if the dynamic latency estimation is not supported. + * If clients have not configured a still capture output, then this method can also return a + * null pair. + * @since 1.4 + */ + Pair<Long, Long> getRealtimeCaptureLatency(); + + /** * Callback for notifying the status of {@link #startCapture(CaptureCallback)} and * {@link #startRepeating(CaptureCallback)}. */ @@ -277,5 +371,20 @@ public interface SessionProcessorImpl { */ void onCaptureCompleted(long timestamp, int captureSequenceId, Map<CaptureResult.Key, Object> result); + + /** + * Capture progress callback that needs to be called when the process capture is + * ongoing and includes the estimated progress of the processing. + * + * <p>Extensions must ensure that they always call this callback with monotonically + * increasing values.</p> + * + * <p>Extensions are allowed to trigger this callback multiple times but at the minimum the + * callback is expected to be called once when processing is done with value 100.</p> + * + * @param progress Value between 0 and 100. + * @since 1.4 + */ + void onCaptureProcessProgressed(int progress); } } diff --git a/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java b/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java index bc772595..bf9e6b12 100644 --- a/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java +++ b/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java @@ -22,6 +22,7 @@ import android.os.SystemClock; import com.android.ex.camera2.portability.debug.Log; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.LinkedList; import java.util.Queue; @@ -30,14 +31,14 @@ public class DispatchThread extends Thread { private static final long MAX_MESSAGE_QUEUE_LENGTH = 256; private final Queue<Runnable> mJobQueue; - private Boolean mIsEnded; + private AtomicBoolean mIsEnded; private Handler mCameraHandler; private HandlerThread mCameraHandlerThread; public DispatchThread(Handler cameraHandler, HandlerThread cameraHandlerThread) { super("Camera Job Dispatch Thread"); mJobQueue = new LinkedList<Runnable>(); - mIsEnded = new Boolean(false); + mIsEnded = new AtomicBoolean(false); mCameraHandler = cameraHandler; mCameraHandlerThread = cameraHandlerThread; } @@ -92,18 +93,14 @@ public class DispatchThread extends Thread { * Gracefully ends this thread. Will stop after all jobs are processed. */ public void end() { - synchronized (mIsEnded) { - mIsEnded = true; - } + mIsEnded.set(true); synchronized(mJobQueue) { mJobQueue.notifyAll(); } } private boolean isEnded() { - synchronized (mIsEnded) { - return mIsEnded; - } + return mIsEnded.get(); } @Override diff --git a/camera2/public/src/com/android/ex/camera2/blocking/BlockingCameraManager.java b/camera2/public/src/com/android/ex/camera2/blocking/BlockingCameraManager.java index 21014d03..5b5a38d9 100644 --- a/camera2/public/src/com/android/ex/camera2/blocking/BlockingCameraManager.java +++ b/camera2/public/src/com/android/ex/camera2/blocking/BlockingCameraManager.java @@ -89,7 +89,7 @@ public class BlockingCameraManager { } } - private final CameraManager mManager; + protected final CameraManager mManager; /** * Create a new blocking camera manager. @@ -168,7 +168,7 @@ public class BlockingCameraManager { * <p>Time out after {@link #OPEN_TIME_OUT_MS} and unblock. Clean up camera if it arrives * later.</p> */ - private class OpenListener extends CameraDevice.StateCallback { + protected class OpenListener extends CameraDevice.StateCallback { private static final int ERROR_UNINITIALIZED = -1; private final String mCameraId; @@ -186,9 +186,13 @@ public class BlockingCameraManager { private boolean mNoReply = true; // Start with no reply until proven otherwise private boolean mTimedOut = false; - OpenListener(CameraManager manager, String cameraId, - CameraDevice.StateCallback listener, Handler handler) - throws CameraAccessException { + protected OpenListener(String cameraId, CameraDevice.StateCallback listener) { + mCameraId = cameraId; + mProxy = listener; + } + + OpenListener(CameraManager manager, String cameraId, CameraDevice.StateCallback listener, + Handler handler) throws CameraAccessException { mCameraId = cameraId; mProxy = listener; manager.openCamera(cameraId, this, handler); @@ -281,7 +285,7 @@ public class BlockingCameraManager { if (mProxy != null) mProxy.onClosed(camera); } - CameraDevice blockUntilOpen() throws BlockingOpenException { + public CameraDevice blockUntilOpen() throws BlockingOpenException { /** * Block until onOpened, onError, or onDisconnected */ diff --git a/common/BUILD b/common/BUILD index 80893a06..149da996 100644 --- a/common/BUILD +++ b/common/BUILD @@ -1,6 +1,6 @@ # TODO(b/198224074): auto-generate this file using bp2build. load("@rules_android//rules:rules.bzl", "android_library") -load("//build/make/tools:event_log_tags.bzl", "event_log_tags") +load("//build/bazel/rules/java:event_log_tags.bzl", "event_log_tags") event_log_tags( name = "genlogtags", diff --git a/framesequence/Android.bp b/framesequence/Android.bp index 9a6d0f55..8d44f1d2 100644 --- a/framesequence/Android.bp +++ b/framesequence/Android.bp @@ -22,7 +22,4 @@ java_library { name: "android-common-framesequence", sdk_version: "8", srcs: ["src/**/*.java"], - optimize: { - proguard_flags_files: ["proguard.flags"], - }, } diff --git a/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java b/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java index c67b83c2..36cc784c 100644 --- a/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java +++ b/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java @@ -41,11 +41,11 @@ public class SamplesList extends ListActivity { return ret; } - @SuppressWarnings("serial") - static final ArrayList<Map<String,?>> SAMPLES = new ArrayList<Map<String,?>>() {{ - add(makeSample("GIF animation", FrameSequenceTest.class, R.raw.animated_gif)); - add(makeSample("WEBP animation", FrameSequenceTest.class, R.raw.animated_webp)); - }}; + static final ArrayList<Map<String,?>> SAMPLES = new ArrayList<>(); + static { + SAMPLES.add(makeSample("GIF animation", FrameSequenceTest.class, R.raw.animated_gif)); + SAMPLES.add(makeSample("WEBP animation", FrameSequenceTest.class, R.raw.animated_webp)); + } @Override protected void onCreate(Bundle savedInstanceState) { |