• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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