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.graphics.Rect; 20 import android.hardware.camera2.CameraAccessException; 21 import android.hardware.camera2.CameraCaptureSession; 22 import android.hardware.camera2.CameraCharacteristics; 23 import android.hardware.camera2.CaptureRequest; 24 import android.hardware.camera2.params.MeteringRectangle; 25 import android.os.ConditionVariable; 26 import android.os.Handler; 27 28 import androidx.annotation.NonNull; 29 30 import org.json.JSONArray; 31 32 import java.util.Arrays; 33 import java.util.concurrent.CountDownLatch; 34 import java.util.Locale; 35 36 /** 37 * An action to be executed during preview recordings, controlling 38 * a {@link CameraCaptureSession} if needed. 39 */ 40 abstract class IntraPreviewAction { 41 /** 42 * Time to sleep after a preview recording action that sends new {@link CaptureRequest}s. 43 */ 44 static final long PREVIEW_RECORDING_FINAL_SLEEP_MS = 200; 45 46 /** 47 * Time to wait for {@link CameraCaptureSession} initialization. 48 */ 49 static final long SESSION_INITIALIZATION_WAIT_MS = 500; 50 51 /** 52 * Time to wait for {@link CaptureRequest.Builder} initialization. 53 */ 54 static final long CAPTURE_REQUEST_BUILDER_INITIALIZATION_WAIT_MS = 500; 55 56 /** 57 * Initialized after {@link ItsService} configures and creates the session. 58 */ 59 volatile CameraCaptureSession mSession; 60 61 /** 62 * {@link ConditionVariable} that tracks when the {@link CameraCaptureSession} 63 * has been initialized. 64 */ 65 ConditionVariable mSessionInitialized = new ConditionVariable(); 66 67 /** 68 * Initialized after {@link ItsService} configures and creates the session. 69 */ 70 volatile CaptureRequest.Builder mCaptureRequestBuilder; 71 72 /** 73 * {@link ConditionVariable} that tracks when the {@link CaptureRequest.Builder} 74 * has been initialized. 75 */ 76 ConditionVariable mCaptureRequestBuilderInitialized = new ConditionVariable(); 77 78 /** 79 * {@link CameraCharacteristics} that are initialized when the camera is opened. 80 */ 81 CameraCharacteristics mCameraCharacteristics; 82 83 /** 84 * The {@link android.os.Handler} on which the listener should be invoked. 85 */ 86 Handler mCameraHandler; 87 88 /** 89 * The {@link com.android.cts.verifier.camera.its.ItsService.RecordingResultListener} that 90 * tracks certain {@link android.hardware.camera2.TotalCaptureResult} values received 91 * during recording. 92 */ 93 ItsService.RecordingResultListener mRecordingResultListener; 94 IntraPreviewAction( CameraCharacteristics cameraCharacteristics, Handler handler, ItsService.RecordingResultListener recordingResultListener)95 protected IntraPreviewAction( 96 CameraCharacteristics cameraCharacteristics, 97 Handler handler, 98 ItsService.RecordingResultListener recordingResultListener) { 99 mCameraCharacteristics = cameraCharacteristics; 100 mCameraHandler = handler; 101 mRecordingResultListener = recordingResultListener; 102 } 103 104 /** 105 * Sets the value for the current {@link CameraCaptureSession}. 106 */ setSession(@onNull CameraCaptureSession session)107 void setSession(@NonNull CameraCaptureSession session) { 108 mSession = session; 109 mSessionInitialized.open(); 110 } 111 112 /** 113 * Sets the value for the current {@link CaptureRequest.Builder}, so that 114 * {@link IntraPreviewAction} can update the same {@link CaptureRequest} used during 115 * configuration. 116 */ setCaptureRequestBuilder(@onNull CaptureRequest.Builder builder)117 void setCaptureRequestBuilder(@NonNull CaptureRequest.Builder builder) { 118 mCaptureRequestBuilder = builder; 119 mCaptureRequestBuilderInitialized.open(); 120 } 121 122 /** 123 * Gets the value for the current 124 * {@link com.android.cts.verifier.camera.its.ItsService.RecordingResultListener}. 125 */ getRecordingResultListener()126 ItsService.RecordingResultListener getRecordingResultListener() { 127 return mRecordingResultListener; 128 } 129 130 /** 131 * Perform actions between {@link PreviewRecorder#startRecording()} and 132 * {@link CameraCaptureSession#stopRepeating()}. The execute() method can be used to define the 133 * duration of the recording, using {@link Thread#sleep(long)}. The method can also call 134 * {@link CameraCaptureSession#setRepeatingRequest(CaptureRequest, CameraCaptureSession.CaptureCallback, Handler)} 135 * to change the requests during the recording. 136 * 137 * @throws InterruptedException if {@link Thread#sleep(long)} was interrupted. 138 * @throws CameraAccessException if a camera device could not be opened to set requests. 139 * @throws ItsException if parsing a JSONObject or JSONArray was unsuccessful. 140 */ execute()141 public abstract void execute() throws 142 InterruptedException, CameraAccessException, ItsException; 143 } 144 145 /** 146 * A simple action that sleeps for a given duration during a preview recording. 147 */ 148 class PreviewSleepAction extends IntraPreviewAction { 149 long mRecordingDuration; 150 PreviewSleepAction( CameraCharacteristics cameraCharacteristics, Handler handler, ItsService.RecordingResultListener recordingResultListener, long recordingDuration)151 PreviewSleepAction( 152 CameraCharacteristics cameraCharacteristics, 153 Handler handler, 154 ItsService.RecordingResultListener recordingResultListener, 155 long recordingDuration) { 156 super(cameraCharacteristics, handler, recordingResultListener); 157 mRecordingDuration = recordingDuration; 158 } 159 160 @Override execute()161 public void execute() throws InterruptedException { 162 Thread.sleep(mRecordingDuration); 163 } 164 } 165 166 /** 167 * An action that sets new repeating {@link CaptureRequest}s to change zoom ratios during recording. 168 */ 169 class PreviewDynamicZoomAction extends IntraPreviewAction { 170 double mZoomStart; 171 double mZoomEnd; 172 double mStepSize; 173 long mStepDuration; PreviewDynamicZoomAction( CameraCharacteristics cameraCharacteristics, Handler handler, ItsService.RecordingResultListener recordingResultListener, double zoomStart, double zoomEnd, double stepSize, long stepDuration)174 PreviewDynamicZoomAction( 175 CameraCharacteristics cameraCharacteristics, 176 Handler handler, 177 ItsService.RecordingResultListener recordingResultListener, 178 double zoomStart, double zoomEnd, double stepSize, long stepDuration) { 179 super(cameraCharacteristics, handler, recordingResultListener); 180 mZoomStart = zoomStart; 181 mZoomEnd = zoomEnd; 182 mStepSize = stepSize; 183 mStepDuration = stepDuration; 184 } 185 186 @Override execute()187 public void execute() throws ItsException, InterruptedException, CameraAccessException { 188 mSessionInitialized.block(SESSION_INITIALIZATION_WAIT_MS); 189 mCaptureRequestBuilderInitialized.block(CAPTURE_REQUEST_BUILDER_INITIALIZATION_WAIT_MS); 190 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 191 CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO); 192 mSession.setRepeatingRequest(mCaptureRequestBuilder.build(), 193 mRecordingResultListener, mCameraHandler); 194 // Wait for autofocus to converge 195 if (!ItsUtils.isFixedFocusLens(mCameraCharacteristics) && 196 !mRecordingResultListener.waitForAfConvergence()) { 197 throw new ItsException( 198 "AF failed to converge before dynamic zoom requests sent."); 199 } 200 for (double z = mZoomStart; z <= mZoomEnd; z += mStepSize) { 201 Logt.i(ItsService.TAG, String.format( 202 Locale.getDefault(), 203 "zoomRatio set to %.4f during preview recording.", z)); 204 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, (float) z); 205 mSession.setRepeatingRequest(mCaptureRequestBuilder.build(), 206 mRecordingResultListener, mCameraHandler); 207 Logt.i(ItsService.TAG, String.format( 208 Locale.getDefault(), 209 "Sleeping %d ms during video recording", mStepDuration)); 210 Thread.sleep(mStepDuration); 211 } 212 Thread.sleep(PREVIEW_RECORDING_FINAL_SLEEP_MS); 213 } 214 } 215 216 /** 217 * An action that sets new repeating {@link CaptureRequest}s to change metering regions during 218 * recording. 219 */ 220 class PreviewDynamicMeteringAction extends IntraPreviewAction { 221 JSONArray mAeAwbRegionOne; 222 JSONArray mAeAwbRegionTwo; 223 JSONArray mAeAwbRegionThree; 224 JSONArray mAeAwbRegionFour; 225 long mAeAwbRegionDuration; 226 PreviewDynamicMeteringAction( CameraCharacteristics cameraCharacteristics, Handler handler, ItsService.RecordingResultListener recordingResultListener, JSONArray aeAwbRegionOne, JSONArray aeAwbRegionTwo, JSONArray aeAwbRegionThree, JSONArray aeAwbRegionFour, long aeAwbRegionDuration)227 PreviewDynamicMeteringAction( 228 CameraCharacteristics cameraCharacteristics, 229 Handler handler, 230 ItsService.RecordingResultListener recordingResultListener, 231 JSONArray aeAwbRegionOne, 232 JSONArray aeAwbRegionTwo, 233 JSONArray aeAwbRegionThree, 234 JSONArray aeAwbRegionFour, 235 long aeAwbRegionDuration) { 236 super(cameraCharacteristics, handler, recordingResultListener); 237 mAeAwbRegionOne = aeAwbRegionOne; 238 mAeAwbRegionTwo = aeAwbRegionTwo; 239 mAeAwbRegionThree = aeAwbRegionThree; 240 mAeAwbRegionFour = aeAwbRegionFour; 241 mAeAwbRegionDuration = aeAwbRegionDuration; 242 } 243 244 @Override execute()245 public void execute() throws ItsException, InterruptedException, CameraAccessException { 246 mSessionInitialized.block(SESSION_INITIALIZATION_WAIT_MS); 247 mCaptureRequestBuilderInitialized.block(CAPTURE_REQUEST_BUILDER_INITIALIZATION_WAIT_MS); 248 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 249 CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO); 250 mSession.setRepeatingRequest(mCaptureRequestBuilder.build(), 251 mRecordingResultListener, mCameraHandler); 252 // Wait for autofocus to converge 253 if (!ItsUtils.isFixedFocusLens(mCameraCharacteristics) && 254 !mRecordingResultListener.waitForAfConvergence()) { 255 throw new ItsException( 256 "AF failed to converge before dynamic metering requests sent."); 257 } 258 Rect activeArray = mCameraCharacteristics.get( 259 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 260 assert activeArray != null; 261 int aaWidth = activeArray.right - activeArray.left; 262 int aaHeight = activeArray.bottom - activeArray.top; 263 JSONArray[] aeAwbRegionRoutine = { 264 mAeAwbRegionOne, mAeAwbRegionTwo, mAeAwbRegionThree, mAeAwbRegionFour}; 265 for (JSONArray aeAwbRegion : aeAwbRegionRoutine) { 266 MeteringRectangle[] region = ItsUtils.getJsonWeightedRectsFromArray( 267 aeAwbRegion, /*normalized=*/true, aaWidth, aaHeight); 268 Logt.i(ItsService.TAG, String.format( 269 Locale.getDefault(), 270 "AE/AWB region set to %s during preview recording.", 271 Arrays.toString(region))); 272 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, region); 273 mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AWB_REGIONS, region); 274 mSession.setRepeatingRequest(mCaptureRequestBuilder.build(), 275 mRecordingResultListener, mCameraHandler); 276 Logt.i(ItsService.TAG, String.format( 277 Locale.getDefault(), 278 "Sleeping %d ms during recording", mAeAwbRegionDuration)); 279 Thread.sleep(mAeAwbRegionDuration); 280 } 281 Thread.sleep(PREVIEW_RECORDING_FINAL_SLEEP_MS); 282 } 283 } 284