1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.verifier.camera.its; 18 19 import android.annotation.NonNull; 20 import android.graphics.Rect; 21 import android.hardware.camera2.CameraAccessException; 22 import android.hardware.camera2.CameraCaptureSession; 23 import android.hardware.camera2.CameraCharacteristics; 24 import android.hardware.camera2.CaptureRequest; 25 import android.hardware.camera2.params.MeteringRectangle; 26 import android.os.Handler; 27 28 import org.json.JSONArray; 29 30 import java.util.Arrays; 31 import java.util.Locale; 32 33 /** 34 * An action to be executed during preview recordings, controlling 35 * a {@link CameraCaptureSession} if needed. 36 */ 37 abstract class IntraPreviewAction { 38 /** 39 * Time to sleep after a preview recording action that sends new {@link CaptureRequest}s. 40 */ 41 static final long PREVIEW_RECORDING_FINAL_SLEEP_MS = 200; 42 43 /** 44 * Time to sleep for AutoFocus to converge. 45 */ 46 static final long PREVIEW_AUTOFOCUS_SLEEP_MS = 400; 47 48 /** 49 * Initialized after {@link ItsService} configures and creates the session. 50 */ 51 volatile CameraCaptureSession mSession; 52 53 /** 54 * Initialized after {@link ItsService} configures and creates the session. 55 */ 56 volatile CaptureRequest.Builder mCaptureRequestBuilder; 57 58 /** 59 * {@link CameraCharacteristics} that are initialized when the camera is opened. 60 */ 61 CameraCharacteristics mCameraCharacteristics; 62 63 /** 64 * The {@link android.os.Handler} on which the listener should be invoked. 65 */ 66 Handler mCameraHandler; 67 68 /** 69 * The {@link com.android.cts.verifier.camera.its.ItsService.RecordingResultListener} that 70 * tracks certain {@link android.hardware.camera2.TotalCaptureResult} values received 71 * during recording. 72 */ 73 ItsService.RecordingResultListener mRecordingResultListener; 74 IntraPreviewAction( CameraCharacteristics cameraCharacteristics, Handler handler, ItsService.RecordingResultListener recordingResultListener)75 protected IntraPreviewAction( 76 CameraCharacteristics cameraCharacteristics, 77 Handler handler, 78 ItsService.RecordingResultListener recordingResultListener) { 79 mCameraCharacteristics = cameraCharacteristics; 80 mCameraHandler = handler; 81 mRecordingResultListener = recordingResultListener; 82 } 83 84 /** 85 * Sets the value for the current {@link CameraCaptureSession}. 86 */ setSession(@onNull CameraCaptureSession session)87 void setSession(@NonNull CameraCaptureSession session) { 88 mSession = session; 89 } 90 91 /** 92 * Sets the value for the current {@link CaptureRequest.Builder}, so that 93 * {@link IntraPreviewAction} can update the same {@link CaptureRequest} used during 94 * configuration. 95 */ setCaptureRequestBuilder(@onNull CaptureRequest.Builder builder)96 void setCaptureRequestBuilder(@NonNull CaptureRequest.Builder builder) { 97 mCaptureRequestBuilder = builder; 98 } 99 100 /** 101 * Gets the value for the current 102 * {@link com.android.cts.verifier.camera.its.ItsService.RecordingResultListener}. 103 */ getRecordingResultListener()104 ItsService.RecordingResultListener getRecordingResultListener() { 105 return mRecordingResultListener; 106 } 107 108 /** 109 * Perform actions between {@link PreviewRecorder#startRecording()} and 110 * {@link CameraCaptureSession#stopRepeating()}. The execute() method can be used to define the 111 * duration of the recording, using {@link Thread#sleep(long)}. The method can also call 112 * {@link CameraCaptureSession#setRepeatingRequest(CaptureRequest, CameraCaptureSession.CaptureCallback, Handler)} 113 * to change the requests during the recording. 114 * 115 * @throws InterruptedException if {@link Thread#sleep(long)} was interrupted. 116 * @throws CameraAccessException if a camera device could not be opened to set requests. 117 * @throws ItsException if parsing a JSONObject or JSONArray was unsuccessful. 118 */ execute()119 public abstract void execute() throws 120 InterruptedException, CameraAccessException, ItsException; 121 } 122 123 /** 124 * A simple action that sleeps for a given duration during a preview recording. 125 */ 126 class PreviewSleepAction extends IntraPreviewAction { 127 long mRecordingDuration; 128 PreviewSleepAction( CameraCharacteristics cameraCharacteristics, Handler handler, ItsService.RecordingResultListener recordingResultListener, long recordingDuration)129 PreviewSleepAction( 130 CameraCharacteristics cameraCharacteristics, 131 Handler handler, 132 ItsService.RecordingResultListener recordingResultListener, 133 long recordingDuration) { 134 super(cameraCharacteristics, handler, recordingResultListener); 135 mRecordingDuration = recordingDuration; 136 } 137 138 @Override execute()139 public void execute() throws InterruptedException { 140 Thread.sleep(mRecordingDuration); 141 } 142 } 143 144 /** 145 * An action that sets new repeating {@link CaptureRequest}s to change zoom ratios during recording. 146 */ 147 class PreviewDynamicZoomAction extends IntraPreviewAction { 148 double mZoomStart; 149 double mZoomEnd; 150 double mStepSize; 151 long mStepDuration; PreviewDynamicZoomAction( CameraCharacteristics cameraCharacteristics, Handler handler, ItsService.RecordingResultListener recordingResultListener, double zoomStart, double zoomEnd, double stepSize, long stepDuration)152 PreviewDynamicZoomAction( 153 CameraCharacteristics cameraCharacteristics, 154 Handler handler, 155 ItsService.RecordingResultListener recordingResultListener, 156 double zoomStart, double zoomEnd, double stepSize, long stepDuration) { 157 super(cameraCharacteristics, handler, recordingResultListener); 158 mZoomStart = zoomStart; 159 mZoomEnd = zoomEnd; 160 mStepSize = stepSize; 161 mStepDuration = stepDuration; 162 } 163 164 @Override execute()165 public void execute() throws InterruptedException, CameraAccessException { 166 // Allow autofocus to converge 167 // TODO: b/333791992 - replace with waiting for desired AF state in CaptureResult 168 Thread.sleep(PREVIEW_AUTOFOCUS_SLEEP_MS); 169 for (double z = mZoomStart; z <= mZoomEnd; z += mStepSize) { 170 Logt.i(ItsService.TAG, String.format( 171 Locale.getDefault(), 172 "zoomRatio set to %.4f during preview recording.", z)); 173 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, (float) z); 174 mSession.setRepeatingRequest(mCaptureRequestBuilder.build(), 175 mRecordingResultListener, mCameraHandler); 176 Logt.i(ItsService.TAG, String.format( 177 Locale.getDefault(), 178 "Sleeping %d ms during video recording", mStepDuration)); 179 Thread.sleep(mStepDuration); 180 } 181 Thread.sleep(PREVIEW_RECORDING_FINAL_SLEEP_MS); 182 } 183 } 184 185 /** 186 * An action that sets new repeating {@link CaptureRequest}s to change metering regions during 187 * recording. 188 */ 189 class PreviewDynamicMeteringAction extends IntraPreviewAction { 190 JSONArray mAeAwbRegionOne; 191 JSONArray mAeAwbRegionTwo; 192 JSONArray mAeAwbRegionThree; 193 JSONArray mAeAwbRegionFour; 194 long mAeAwbRegionDuration; 195 PreviewDynamicMeteringAction( CameraCharacteristics cameraCharacteristics, Handler handler, ItsService.RecordingResultListener recordingResultListener, JSONArray aeAwbRegionOne, JSONArray aeAwbRegionTwo, JSONArray aeAwbRegionThree, JSONArray aeAwbRegionFour, long aeAwbRegionDuration)196 PreviewDynamicMeteringAction( 197 CameraCharacteristics cameraCharacteristics, 198 Handler handler, 199 ItsService.RecordingResultListener recordingResultListener, 200 JSONArray aeAwbRegionOne, 201 JSONArray aeAwbRegionTwo, 202 JSONArray aeAwbRegionThree, 203 JSONArray aeAwbRegionFour, 204 long aeAwbRegionDuration) { 205 super(cameraCharacteristics, handler, recordingResultListener); 206 mAeAwbRegionOne = aeAwbRegionOne; 207 mAeAwbRegionTwo = aeAwbRegionTwo; 208 mAeAwbRegionThree = aeAwbRegionThree; 209 mAeAwbRegionFour = aeAwbRegionFour; 210 mAeAwbRegionDuration = aeAwbRegionDuration; 211 } 212 213 @Override execute()214 public void execute() throws InterruptedException, CameraAccessException, ItsException { 215 // Allow autofocus to converge 216 // TODO: b/333791992 - replace with waiting for desired AF state in CaptureResult 217 Thread.sleep(PREVIEW_AUTOFOCUS_SLEEP_MS); 218 Rect activeArray = mCameraCharacteristics.get( 219 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 220 assert activeArray != null; 221 int aaWidth = activeArray.right - activeArray.left; 222 int aaHeight = activeArray.bottom - activeArray.top; 223 JSONArray[] aeAwbRegionRoutine = { 224 mAeAwbRegionOne, mAeAwbRegionTwo, mAeAwbRegionThree, mAeAwbRegionFour}; 225 for (JSONArray aeAwbRegion : aeAwbRegionRoutine) { 226 MeteringRectangle[] region = ItsUtils.getJsonWeightedRectsFromArray( 227 aeAwbRegion, /*normalized=*/true, aaWidth, aaHeight); 228 Logt.i(ItsService.TAG, String.format( 229 Locale.getDefault(), 230 "AE/AWB region set to %s during preview recording.", 231 Arrays.toString(region))); 232 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, region); 233 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AWB_REGIONS, region); 234 mSession.setRepeatingRequest(mCaptureRequestBuilder.build(), 235 mRecordingResultListener, mCameraHandler); 236 Logt.i(ItsService.TAG, String.format( 237 Locale.getDefault(), 238 "Sleeping %d ms during recording", mAeAwbRegionDuration)); 239 Thread.sleep(mAeAwbRegionDuration); 240 } 241 Thread.sleep(PREVIEW_RECORDING_FINAL_SLEEP_MS); 242 } 243 } 244