blob: c1091c665b8702f4f62f49d945c38815326ec804 [file] [log] [blame]
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001/*
2 * Copyright (C) 2014 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
17package android.hardware.camera2.legacy;
18
Ruben Brunkfeb50af2014-05-09 19:58:49 -070019import android.graphics.SurfaceTexture;
20import android.hardware.Camera;
Igor Murashkindf6242e2014-07-01 18:06:13 -070021import android.hardware.camera2.CameraCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070022import android.hardware.camera2.CaptureRequest;
Ruben Brunke663cb772014-09-16 13:18:31 -070023import android.hardware.camera2.impl.CameraDeviceImpl;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080024import android.hardware.camera2.utils.SubmitInfo;
Igor Murashkindf6242e2014-07-01 18:06:13 -070025import android.hardware.camera2.utils.SizeAreaComparator;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070026import android.hardware.camera2.impl.CameraMetadataNative;
27import android.os.ConditionVariable;
28import android.os.Handler;
29import android.os.Message;
30import android.os.SystemClock;
31import android.util.Log;
Igor Murashkin83d86392014-07-18 14:37:19 -070032import android.util.MutableLong;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070033import android.util.Pair;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070034import android.util.Size;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070035import android.view.Surface;
36
Ruben Brunkfeb50af2014-05-09 19:58:49 -070037import java.io.IOException;
38import java.util.ArrayList;
39import java.util.Collection;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070040import java.util.Collections;
Ruben Brunkf4a637d2014-11-20 18:01:36 -080041import java.util.Iterator;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070042import java.util.List;
Ruben Brunk91838de2014-07-16 17:24:17 -070043import java.util.concurrent.TimeUnit;
Ruben Brunke3c04342015-02-04 17:26:17 -080044import java.util.concurrent.atomic.AtomicBoolean;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070045
Igor Murashkindf6242e2014-07-01 18:06:13 -070046import static com.android.internal.util.Preconditions.*;
47
Ruben Brunkfeb50af2014-05-09 19:58:49 -070048/**
49 * This class executes requests to the {@link Camera}.
50 *
51 * <p>
52 * The main components of this class are:
53 * - A message queue of requests to the {@link Camera}.
54 * - A thread that consumes requests to the {@link Camera} and executes them.
55 * - A {@link GLThreadManager} that draws to the configured output {@link Surface}s.
56 * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations.
57 * </p>
58 */
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070059@SuppressWarnings("deprecation")
Ruben Brunkfeb50af2014-05-09 19:58:49 -070060public class RequestThreadManager {
61 private final String TAG;
62 private final int mCameraId;
63 private final RequestHandlerThread mRequestThread;
64
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070065 private static final boolean DEBUG = false;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070066 // For slightly more spammy messages that will get repeated every frame
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070067 private static final boolean VERBOSE = false;
Ruben Brunke3c04342015-02-04 17:26:17 -080068 private Camera mCamera;
Igor Murashkindf6242e2014-07-01 18:06:13 -070069 private final CameraCharacteristics mCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070070
71 private final CameraDeviceState mDeviceState;
Ruben Brunk91838de2014-07-16 17:24:17 -070072 private final CaptureCollector mCaptureCollector;
Igor Murashkin83d86392014-07-18 14:37:19 -070073 private final LegacyFocusStateMapper mFocusStateMapper;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070074 private final LegacyFaceDetectMapper mFaceDetectMapper;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070075
76 private static final int MSG_CONFIGURE_OUTPUTS = 1;
77 private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
78 private static final int MSG_CLEANUP = 3;
79
Ruben Brunk91838de2014-07-16 17:24:17 -070080 private static final int MAX_IN_FLIGHT_REQUESTS = 2;
81
Igor Murashkin3e2c14f2014-09-24 13:48:09 -070082 private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms
Ruben Brunke3c04342015-02-04 17:26:17 -080083 private static final int JPEG_FRAME_TIMEOUT = 4000; // ms (same as CTS for API2)
Ruben Brunk45441f32015-02-05 11:55:40 -080084 private static final int REQUEST_COMPLETE_TIMEOUT = JPEG_FRAME_TIMEOUT;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070085
Ruben Brunkd85e1a62014-06-11 10:35:45 -070086 private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070087 private boolean mPreviewRunning = false;
88
Ruben Brunk7f2372b2014-07-02 11:05:08 -070089 private final List<Surface> mPreviewOutputs = new ArrayList<>();
90 private final List<Surface> mCallbackOutputs = new ArrayList<>();
Ruben Brunkfeb50af2014-05-09 19:58:49 -070091 private GLThreadManager mGLThreadManager;
92 private SurfaceTexture mPreviewTexture;
Ruben Brunk3e4fed22014-06-18 17:08:42 -070093 private Camera.Parameters mParams;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070094
Ruben Brunk0fd198a2014-09-23 23:35:43 -070095 private final List<Long> mJpegSurfaceIds = new ArrayList<>();
96
Ruben Brunkd85e1a62014-06-11 10:35:45 -070097 private Size mIntermediateBufferSize;
98
Ruben Brunk0fd198a2014-09-23 23:35:43 -070099 private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds);
Igor Murashkindf6242e2014-07-01 18:06:13 -0700100 private LegacyRequest mLastRequest = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700101 private SurfaceTexture mDummyTexture;
102 private Surface mDummySurface;
103
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700104 private final Object mIdleLock = new Object();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700105 private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700106 private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700107
Ruben Brunke3c04342015-02-04 17:26:17 -0800108 private final AtomicBoolean mQuit = new AtomicBoolean(false);
109
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700110 // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write
111 // limitations for (b/17379185).
112 private static final boolean USE_BLOB_FORMAT_OVERRIDE = true;
113
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700114 /**
115 * Container object for Configure messages.
116 */
117 private static class ConfigureHolder {
118 public final ConditionVariable condition;
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800119 public final Collection<Pair<Surface, Size>> surfaces;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700120
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800121 public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface,
122 Size>> surfaces) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700123 this.condition = condition;
124 this.surfaces = surfaces;
125 }
126 }
127
128 /**
129 * Counter class used to calculate and log the current FPS of frame production.
130 */
131 public static class FpsCounter {
132 //TODO: Hook this up to SystTrace?
133 private static final String TAG = "FpsCounter";
134 private int mFrameCount = 0;
135 private long mLastTime = 0;
136 private long mLastPrintTime = 0;
137 private double mLastFps = 0;
138 private final String mStreamType;
139 private static final long NANO_PER_SECOND = 1000000000; //ns
140
141 public FpsCounter(String streamType) {
142 mStreamType = streamType;
143 }
144
145 public synchronized void countFrame() {
146 mFrameCount++;
147 long nextTime = SystemClock.elapsedRealtimeNanos();
148 if (mLastTime == 0) {
149 mLastTime = nextTime;
150 }
151 if (nextTime > mLastTime + NANO_PER_SECOND) {
152 long elapsed = nextTime - mLastTime;
153 mLastFps = mFrameCount * (NANO_PER_SECOND / (double) elapsed);
154 mFrameCount = 0;
155 mLastTime = nextTime;
156 }
157 }
158
159 public synchronized double checkFps() {
160 return mLastFps;
161 }
162
163 public synchronized void staggeredLog() {
164 if (mLastTime > mLastPrintTime + 5 * NANO_PER_SECOND) {
165 mLastPrintTime = mLastTime;
166 Log.d(TAG, "FPS for " + mStreamType + " stream: " + mLastFps );
167 }
168 }
169
170 public synchronized void countAndLog() {
171 countFrame();
172 staggeredLog();
173 }
174 }
175 /**
176 * Fake preview for jpeg captures when there is no active preview
177 */
178 private void createDummySurface() {
179 if (mDummyTexture == null || mDummySurface == null) {
180 mDummyTexture = new SurfaceTexture(/*ignored*/0);
181 // TODO: use smallest default sizes
182 mDummyTexture.setDefaultBufferSize(640, 480);
183 mDummySurface = new Surface(mDummyTexture);
184 }
185 }
186
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700187 private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
188 @Override
189 public void onError(int i, Camera camera) {
Ruben Brunkb44864e2015-06-12 14:39:12 -0700190 switch(i) {
191 case Camera.CAMERA_ERROR_EVICTED: {
192 flush();
193 mDeviceState.setError(
194 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED);
195 } break;
Yin-Chia Yeha07cd322018-05-31 12:11:42 -0700196 case Camera.CAMERA_ERROR_DISABLED: {
197 flush();
198 mDeviceState.setError(
199 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISABLED);
200 } break;
Ruben Brunkb44864e2015-06-12 14:39:12 -0700201 default: {
202 Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
203 mDeviceState.setError(
204 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
205 } break;
206 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700207 }
208 };
209
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700210 private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700211
212 private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
213 @Override
214 public void onPictureTaken(byte[] data, Camera camera) {
215 Log.i(TAG, "Received jpeg.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700216 Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced();
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700217 if (captureInfo == null || captureInfo.first == null) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700218 Log.e(TAG, "Dropping jpeg frame.");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700219 return;
220 }
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700221 RequestHolder holder = captureInfo.first;
222 long timestamp = captureInfo.second;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700223 for (Surface s : holder.getHolderTargets()) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700224 try {
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700225 if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700226 Log.i(TAG, "Producing jpeg buffer...");
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700227
228 int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
Ruben Brunk31798f32014-09-25 19:56:54 -0700229 totalSize = (totalSize + 3) & ~0x3; // round up to nearest octonibble
Ruben Brunk0c798842014-09-30 03:42:13 -0700230 LegacyCameraDevice.setNextTimestamp(s, timestamp);
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700231
Ruben Brunk31798f32014-09-25 19:56:54 -0700232 if (USE_BLOB_FORMAT_OVERRIDE) {
233 // Override to RGBA_8888 format.
234 LegacyCameraDevice.setSurfaceFormat(s,
235 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
Ruben Brunk0c798842014-09-30 03:42:13 -0700236
237 int dimen = (int) Math.ceil(Math.sqrt(totalSize));
238 dimen = (dimen + 0xf) & ~0xf; // round up to nearest multiple of 16
239 LegacyCameraDevice.setSurfaceDimens(s, dimen, dimen);
240 LegacyCameraDevice.produceFrame(s, data, dimen, dimen,
241 CameraMetadataNative.NATIVE_JPEG_FORMAT);
242 } else {
243 LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1);
244 LegacyCameraDevice.produceFrame(s, data, totalSize, /*height*/1,
245 CameraMetadataNative.NATIVE_JPEG_FORMAT);
Ruben Brunk31798f32014-09-25 19:56:54 -0700246 }
Ruben Brunkef14da32014-06-24 16:06:54 -0700247 }
248 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
249 Log.w(TAG, "Surface abandoned, dropping frame. ", e);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700250 }
251 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700252
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700253 mReceivedJpeg.open();
254 }
255 };
256
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700257 private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
258 @Override
259 public void onShutter() {
Ruben Brunk91838de2014-07-16 17:24:17 -0700260 mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700261 }
262 };
263
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700264 private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
265 new SurfaceTexture.OnFrameAvailableListener() {
266 @Override
267 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700268 if (DEBUG) {
269 mPrevCounter.countAndLog();
270 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700271 mGLThreadManager.queueNewFrame();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700272 }
273 };
274
275 private void stopPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700276 if (VERBOSE) {
277 Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
278 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700279 if (mPreviewRunning) {
280 mCamera.stopPreview();
281 mPreviewRunning = false;
282 }
283 }
284
285 private void startPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700286 if (VERBOSE) {
287 Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
288 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700289 if (!mPreviewRunning) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700290 // XX: CameraClient:;startPreview is not getting called after a stop
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700291 mCamera.startPreview();
292 mPreviewRunning = true;
293 }
294 }
295
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700296 private void doJpegCapturePrepare(RequestHolder request) throws IOException {
297 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
Igor Murashkina296fec2014-06-23 14:44:09 -0700298
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700299 if (!mPreviewRunning) {
Igor Murashkina296fec2014-06-23 14:44:09 -0700300 if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
301
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700302 createDummySurface();
303 mCamera.setPreviewTexture(mDummyTexture);
304 startPreview();
305 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700306 }
307
308 private void doJpegCapture(RequestHolder request) {
309 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
310
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700311 mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700312 mPreviewRunning = false;
313 }
314
315 private void doPreviewCapture(RequestHolder request) throws IOException {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700316 if (VERBOSE) {
317 Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
318 }
319
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700320 if (mPreviewRunning) {
321 return; // Already running
322 }
323
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700324 if (mPreviewTexture == null) {
325 throw new IllegalStateException(
326 "Preview capture called with no preview surfaces configured.");
327 }
328
329 mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
330 mIntermediateBufferSize.getHeight());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700331 mCamera.setPreviewTexture(mPreviewTexture);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700332
333 startPreview();
334 }
335
Shuzhen Wangde95aaf2019-03-25 22:07:42 -0700336 private void disconnectCallbackSurfaces() {
337 for (Surface s : mCallbackOutputs) {
338 try {
339 LegacyCameraDevice.disconnectSurface(s);
340 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
341 Log.d(TAG, "Surface abandoned, skipping...", e);
342 }
343 }
344 }
345
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800346 private void configureOutputs(Collection<Pair<Surface, Size>> outputs) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700347 if (DEBUG) {
348 String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700349 Log.d(TAG, "configureOutputs with " + outputsStr);
350 }
351
Ruben Brunke3c04342015-02-04 17:26:17 -0800352 try {
353 stopPreview();
354 } catch (RuntimeException e) {
355 Log.e(TAG, "Received device exception in configure call: ", e);
356 mDeviceState.setError(
357 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
358 return;
359 }
360
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700361 /*
362 * Try to release the previous preview's surface texture earlier if we end up
363 * using a different one; this also reduces the likelihood of getting into a deadlock
364 * when disconnecting from the old previous texture at a later time.
365 */
Ruben Brunke663cb772014-09-16 13:18:31 -0700366 try {
367 mCamera.setPreviewTexture(/*surfaceTexture*/null);
368 } catch (IOException e) {
369 Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
Ruben Brunke3c04342015-02-04 17:26:17 -0800370 } catch (RuntimeException e) {
371 Log.e(TAG, "Received device exception in configure call: ", e);
372 mDeviceState.setError(
373 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
374 return;
Ruben Brunke663cb772014-09-16 13:18:31 -0700375 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700376
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700377 if (mGLThreadManager != null) {
378 mGLThreadManager.waitUntilStarted();
379 mGLThreadManager.ignoreNewFrames();
380 mGLThreadManager.waitUntilIdle();
381 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700382 resetJpegSurfaceFormats(mCallbackOutputs);
Shuzhen Wangde95aaf2019-03-25 22:07:42 -0700383 disconnectCallbackSurfaces();
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800384
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700385 mPreviewOutputs.clear();
386 mCallbackOutputs.clear();
Eino-Ville Talvala52571b92014-09-25 12:52:20 -0700387 mJpegSurfaceIds.clear();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700388 mPreviewTexture = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700389
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800390 List<Size> previewOutputSizes = new ArrayList<>();
391 List<Size> callbackOutputSizes = new ArrayList<>();
392
Ruben Brunk28c49c92014-06-16 18:43:59 -0700393 int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
394 int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700395 if (outputs != null) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800396 for (Pair<Surface, Size> outPair : outputs) {
397 Surface s = outPair.first;
398 Size outSize = outPair.second;
Ruben Brunkef14da32014-06-24 16:06:54 -0700399 try {
400 int format = LegacyCameraDevice.detectSurfaceType(s);
Ruben Brunk28c49c92014-06-16 18:43:59 -0700401 LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
Ruben Brunkef14da32014-06-24 16:06:54 -0700402 switch (format) {
403 case CameraMetadataNative.NATIVE_JPEG_FORMAT:
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700404 if (USE_BLOB_FORMAT_OVERRIDE) {
405 // Override to RGBA_8888 format.
406 LegacyCameraDevice.setSurfaceFormat(s,
407 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
408 }
409 mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
Ruben Brunkef14da32014-06-24 16:06:54 -0700410 mCallbackOutputs.add(s);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800411 callbackOutputSizes.add(outSize);
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800412
413 // LegacyCameraDevice is the producer of JPEG output surfaces
414 // so LegacyCameraDevice needs to connect to the surfaces.
415 LegacyCameraDevice.connectSurface(s);
Ruben Brunkef14da32014-06-24 16:06:54 -0700416 break;
417 default:
Ruben Brunka94c6032015-06-10 16:44:28 -0700418 LegacyCameraDevice.setScalingMode(s, LegacyCameraDevice.
419 NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
Ruben Brunkef14da32014-06-24 16:06:54 -0700420 mPreviewOutputs.add(s);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800421 previewOutputSizes.add(outSize);
Ruben Brunkef14da32014-06-24 16:06:54 -0700422 break;
423 }
424 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
425 Log.w(TAG, "Surface abandoned, skipping...", e);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700426 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700427 }
428 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700429 try {
430 mParams = mCamera.getParameters();
431 } catch (RuntimeException e) {
432 Log.e(TAG, "Received device exception: ", e);
433 mDeviceState.setError(
434 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
435 return;
436 }
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700437
438 List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
439 int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
440 if (DEBUG) {
441 Log.d(TAG, "doPreviewCapture - Selected range [" +
442 bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
443 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
444 }
445 mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
446 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700447
Ruben Brunkc64e80d2015-06-10 17:14:45 -0700448 Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs,
449 callbackOutputSizes, mParams);
450
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800451 if (previewOutputSizes.size() > 0) {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700452
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800453 Size largestOutput = SizeAreaComparator.findLargestByArea(previewOutputSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700454
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700455 // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700456 Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700457
Ruben Brunkc64e80d2015-06-10 17:14:45 -0700458 Size chosenJpegDimen = (smallestSupportedJpegSize != null) ? smallestSupportedJpegSize
459 : largestJpegDimen;
460
Igor Murashkindf6242e2014-07-01 18:06:13 -0700461 List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
462 mParams.getSupportedPreviewSizes());
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700463
464 // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
465 // of the configured output dimensions. If none exists, fall back to using the largest
466 // supported preview size.
467 long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
Igor Murashkindf6242e2014-07-01 18:06:13 -0700468 Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700469 for (Size s : supportedPreviewSizes) {
470 long currArea = s.getWidth() * s.getHeight();
471 long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
Ruben Brunkc64e80d2015-06-10 17:14:45 -0700472 if (checkAspectRatiosMatch(chosenJpegDimen, s) && (currArea < bestArea &&
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700473 currArea >= largestOutputArea)) {
474 bestPreviewDimen = s;
475 }
476 }
477
478 mIntermediateBufferSize = bestPreviewDimen;
Ruben Brunk059d7662014-09-09 18:46:00 -0700479 mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
480 mIntermediateBufferSize.getHeight());
481
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700482 if (DEBUG) {
483 Log.d(TAG, "Intermediate buffer selected with dimens: " +
484 bestPreviewDimen.toString());
485 }
486 } else {
487 mIntermediateBufferSize = null;
488 if (DEBUG) {
489 Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
490 }
491 }
492
Igor Murashkina296fec2014-06-23 14:44:09 -0700493 if (smallestSupportedJpegSize != null) {
494 /*
495 * Set takePicture size to the smallest supported JPEG size large enough
496 * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
497 */
498
499 Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
500 mParams.setPictureSize(
501 smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
502 }
503
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700504 // TODO: Detect and optimize single-output paths here to skip stream teeing.
505 if (mGLThreadManager == null) {
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700506 mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700507 mGLThreadManager.start();
508 }
509 mGLThreadManager.waitUntilStarted();
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800510 List<Pair<Surface, Size>> previews = new ArrayList<>();
511 Iterator<Size> previewSizeIter = previewOutputSizes.iterator();
512 for (Surface p : mPreviewOutputs) {
513 previews.add(new Pair<>(p, previewSizeIter.next()));
514 }
515 mGLThreadManager.setConfigurationAndWait(previews, mCaptureCollector);
Sheng-Hao Tsaobb39c852017-08-10 13:41:20 +0800516
517 for (Surface p : mPreviewOutputs) {
518 try {
519 LegacyCameraDevice.setSurfaceOrientation(p, facing, orientation);
520 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
521 Log.e(TAG, "Surface abandoned, skipping setSurfaceOrientation()", e);
522 }
523 }
524
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700525 mGLThreadManager.allowNewFrames();
526 mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700527 if (mPreviewTexture != null) {
528 mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
529 }
Igor Murashkin5096def2014-06-24 10:49:11 -0700530
Ruben Brunke3c04342015-02-04 17:26:17 -0800531 try {
532 mCamera.setParameters(mParams);
533 } catch (RuntimeException e) {
534 Log.e(TAG, "Received device exception while configuring: ", e);
535 mDeviceState.setError(
536 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
537
538 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700539 }
540
541 private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
542 if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
543 return;
544 }
545 for(Surface s : surfaces) {
Ruben Brunk443ab2c2015-03-12 20:54:03 -0700546 if (s == null || !s.isValid()) {
547 Log.w(TAG, "Jpeg surface is invalid, skipping...");
548 continue;
549 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700550 try {
551 LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
552 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
553 Log.w(TAG, "Surface abandoned, skipping...", e);
554 }
555 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700556 }
557
Igor Murashkina296fec2014-06-23 14:44:09 -0700558 /**
559 * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
560 * than all of the configured {@code JPEG} outputs (by both width and height).
561 *
562 * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
563 * still satisfies the above constraint.</p>
564 *
565 * <p>As a result, the returned size is guaranteed to be usable without needing
566 * to upscale any of the outputs. If only one {@code JPEG} surface is used,
567 * then no scaling/cropping is necessary between the taken picture and
568 * the {@code JPEG} output surface.</p>
569 *
570 * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
571 * @param params api1 parameters (used for reading only)
572 *
573 * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
574 * {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
575 * surfaces.
576 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800577 private Size calculatePictureSize( List<Surface> callbackOutputs,
578 List<Size> callbackSizes, Camera.Parameters params) {
Igor Murashkina296fec2014-06-23 14:44:09 -0700579 /*
580 * Find the largest JPEG size (if any), from the configured outputs:
581 * - the api1 picture size should be set to the smallest legal size that's at least as large
582 * as the largest configured JPEG size
583 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800584 if (callbackOutputs.size() != callbackSizes.size()) {
585 throw new IllegalStateException("Input collections must be same length");
586 }
587 List<Size> configuredJpegSizes = new ArrayList<>();
588 Iterator<Size> sizeIterator = callbackSizes.iterator();
Igor Murashkina296fec2014-06-23 14:44:09 -0700589 for (Surface callbackSurface : callbackOutputs) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800590 Size jpegSize = sizeIterator.next();
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700591 if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700592 continue; // Ignore non-JPEG callback formats
593 }
594
Ruben Brunkef14da32014-06-24 16:06:54 -0700595 configuredJpegSizes.add(jpegSize);
Igor Murashkina296fec2014-06-23 14:44:09 -0700596 }
597 if (!configuredJpegSizes.isEmpty()) {
598 /*
599 * Find the largest configured JPEG width, and height, independently
600 * of the rest.
601 *
602 * The rest of the JPEG streams can be cropped out of this smallest bounding
603 * rectangle.
604 */
605 int maxConfiguredJpegWidth = -1;
606 int maxConfiguredJpegHeight = -1;
607 for (Size jpegSize : configuredJpegSizes) {
608 maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
609 jpegSize.getWidth() : maxConfiguredJpegWidth;
610 maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
611 jpegSize.getHeight() : maxConfiguredJpegHeight;
612 }
613 Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
614
Igor Murashkindf6242e2014-07-01 18:06:13 -0700615 List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
616 params.getSupportedPictureSizes());
Igor Murashkina296fec2014-06-23 14:44:09 -0700617
618 /*
619 * Find the smallest supported JPEG size that can fit the smallest bounding
620 * rectangle for the configured JPEG sizes.
621 */
622 List<Size> candidateSupportedJpegSizes = new ArrayList<>();
623 for (Size supportedJpegSize : supportedJpegSizes) {
624 if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
625 supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
626 candidateSupportedJpegSizes.add(supportedJpegSize);
627 }
628 }
629
630 if (candidateSupportedJpegSizes.isEmpty()) {
631 throw new AssertionError(
632 "Could not find any supported JPEG sizes large enough to fit " +
633 smallestBoundJpegSize);
634 }
635
636 Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
637 new SizeAreaComparator());
638
639 if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
640 Log.w(TAG,
641 String.format(
642 "configureOutputs - Will need to crop picture %s into "
643 + "smallest bound size %s",
644 smallestSupportedJpegSize, smallestBoundJpegSize));
645 }
646
647 return smallestSupportedJpegSize;
648 }
649
650 return null;
651 }
652
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700653 private static boolean checkAspectRatiosMatch(Size a, Size b) {
654 float aAspect = a.getWidth() / (float) a.getHeight();
655 float bAspect = b.getWidth() / (float) b.getHeight();
656
657 return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
658 }
659
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700660 // Calculate the highest FPS range supported
661 private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
662 if (frameRates.size() == 0) {
663 Log.e(TAG, "No supported frame rates returned!");
664 return null;
665 }
666
667 int bestMin = 0;
668 int bestMax = 0;
669 int bestIndex = 0;
670 int index = 0;
671 for (int[] rate : frameRates) {
672 int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
673 int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
674 if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
675 bestMin = minFps;
676 bestMax = maxFps;
677 bestIndex = index;
678 }
679 index++;
680 }
681
682 return frameRates.get(bestIndex);
683 }
684
685 private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
686 private boolean mCleanup = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700687 private final LegacyResultMapper mMapper = new LegacyResultMapper();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700688
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700689 @Override
690 public boolean handleMessage(Message msg) {
691 if (mCleanup) {
692 return true;
693 }
694
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700695 if (DEBUG) {
696 Log.d(TAG, "Request thread handling message:" + msg.what);
697 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700698 long startTime = 0;
699 if (DEBUG) {
700 startTime = SystemClock.elapsedRealtimeNanos();
701 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700702 switch (msg.what) {
703 case MSG_CONFIGURE_OUTPUTS:
704 ConfigureHolder config = (ConfigureHolder) msg.obj;
Igor Murashkin49b2b132014-06-18 19:03:00 -0700705 int sizes = config.surfaces != null ? config.surfaces.size() : 0;
Ruben Brunke663cb772014-09-16 13:18:31 -0700706 Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700707
708 try {
709 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
710 TimeUnit.MILLISECONDS);
711 if (!success) {
712 Log.e(TAG, "Timed out while queueing configure request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700713 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700714 }
715 } catch (InterruptedException e) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700716 Log.e(TAG, "Interrupted while waiting for requests to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700717 mDeviceState.setError(
718 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
719 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700720 }
721
Ruben Brunke663cb772014-09-16 13:18:31 -0700722 configureOutputs(config.surfaces);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700723 config.condition.open();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700724 if (DEBUG) {
725 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
726 Log.d(TAG, "Configure took " + totalTime + " ns");
727 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700728 break;
729 case MSG_SUBMIT_CAPTURE_REQUEST:
730 Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700731 boolean anyRequestOutputAbandoned = false;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700732
733 // Get the next burst from the request queue.
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700734 RequestQueue.RequestQueueEntry nextBurst = mRequestQueue.getNext();
Ruben Brunke663cb772014-09-16 13:18:31 -0700735
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700736 if (nextBurst == null) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700737 // If there are no further requests queued, wait for any currently executing
738 // requests to complete, then switch to idle state.
Ruben Brunk91838de2014-07-16 17:24:17 -0700739 try {
740 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
741 TimeUnit.MILLISECONDS);
742 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700743 Log.e(TAG,
744 "Timed out while waiting for prior requests to complete.");
745 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700746 }
747 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700748 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
749 mDeviceState.setError(
750 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
751 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700752 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700753
754 synchronized (mIdleLock) {
755 // Retry the the request queue.
756 nextBurst = mRequestQueue.getNext();
757
758 // If we still have no queued requests, go idle.
759 if (nextBurst == null) {
760 mDeviceState.setIdle();
761 break;
762 }
763 }
764 }
765
766 if (nextBurst != null) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700767 // Queue another capture if we did not get the last burst.
768 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700769
770 // Check whether capture queue becomes empty
771 if (nextBurst.isQueueEmpty()) {
772 mDeviceState.setRequestQueueEmpty();
773 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700774 }
775
776 // Complete each request in the burst
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700777 BurstHolder burstHolder = nextBurst.getBurstHolder();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700778 List<RequestHolder> requests =
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700779 burstHolder.produceRequestHolders(nextBurst.getFrameNumber());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700780 for (RequestHolder holder : requests) {
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700781 CaptureRequest request = holder.getRequest();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700782
783 boolean paramsChanged = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700784
Ruben Brunke663cb772014-09-16 13:18:31 -0700785 // Only update parameters if the request has changed
Igor Murashkindf6242e2014-07-01 18:06:13 -0700786 if (mLastRequest == null || mLastRequest.captureRequest != request) {
787
788 // The intermediate buffer is sometimes null, but we always need
Ruben Brunke663cb772014-09-16 13:18:31 -0700789 // the Camera1 API configured preview size
Igor Murashkindf6242e2014-07-01 18:06:13 -0700790 Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
791
Ruben Brunke663cb772014-09-16 13:18:31 -0700792 LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
793 request, previewSize, mParams); // params are copied
Igor Murashkindf6242e2014-07-01 18:06:13 -0700794
Ruben Brunke663cb772014-09-16 13:18:31 -0700795
Igor Murashkindf6242e2014-07-01 18:06:13 -0700796 // Parameters are mutated as a side-effect
797 LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
798
Ruben Brunke663cb772014-09-16 13:18:31 -0700799 // If the parameters have changed, set them in the Camera1 API.
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700800 if (!mParams.same(legacyRequest.parameters)) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700801 try {
802 mCamera.setParameters(legacyRequest.parameters);
803 } catch (RuntimeException e) {
804 // If setting the parameters failed, report a request error to
805 // the camera client, and skip any further work for this request
806 Log.e(TAG, "Exception while setting camera parameters: ", e);
807 holder.failRequest();
808 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
809 CameraDeviceImpl.CameraDeviceCallbacks.
810 ERROR_CAMERA_REQUEST);
811 continue;
812 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700813 paramsChanged = true;
Ruben Brunke663cb772014-09-16 13:18:31 -0700814 mParams = legacyRequest.parameters;
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700815 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700816
817 mLastRequest = legacyRequest;
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700818 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700819
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700820 try {
Ruben Brunk91838de2014-07-16 17:24:17 -0700821 boolean success = mCaptureCollector.queueRequest(holder,
822 mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
823
824 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700825 // Report a request error if we timed out while queuing this.
Ruben Brunk91838de2014-07-16 17:24:17 -0700826 Log.e(TAG, "Timed out while queueing capture request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700827 holder.failRequest();
828 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
829 CameraDeviceImpl.CameraDeviceCallbacks.
830 ERROR_CAMERA_REQUEST);
831 continue;
Ruben Brunk91838de2014-07-16 17:24:17 -0700832 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700833
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700834 // Starting the preview needs to happen before enabling
835 // face detection or auto focus
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700836 if (holder.hasPreviewTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700837 doPreviewCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700838 }
839 if (holder.hasJpegTargets()) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700840 while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
841 TimeUnit.MILLISECONDS)) {
842 // Fail preview requests until the queue is empty.
843 Log.e(TAG, "Timed out while waiting for preview requests to " +
844 "complete.");
845 mCaptureCollector.failNextPreview();
Ruben Brunk91838de2014-07-16 17:24:17 -0700846 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700847 mReceivedJpeg.close();
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700848 doJpegCapturePrepare(holder);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700849 }
850
851 /*
852 * Do all the actions that require a preview to have been started
853 */
854
855 // Toggle face detection on/off
856 // - do this before AF to give AF a chance to use faces
857 mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
858
859 // Unconditionally process AF triggers, since they're non-idempotent
860 // - must be done after setting the most-up-to-date AF mode
861 mFocusStateMapper.processRequestTriggers(request, mParams);
862
863 if (holder.hasJpegTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700864 doJpegCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700865 if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700866 Log.e(TAG, "Hit timeout for jpeg callback!");
Ruben Brunke663cb772014-09-16 13:18:31 -0700867 mCaptureCollector.failNextJpeg();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700868 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700869 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700870
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700871 } catch (IOException e) {
Ruben Brunke3c04342015-02-04 17:26:17 -0800872 Log.e(TAG, "Received device exception during capture call: ", e);
Ruben Brunke663cb772014-09-16 13:18:31 -0700873 mDeviceState.setError(
874 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
875 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700876 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700877 Log.e(TAG, "Interrupted during capture: ", e);
878 mDeviceState.setError(
879 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
880 break;
Ruben Brunke3c04342015-02-04 17:26:17 -0800881 } catch (RuntimeException e) {
882 Log.e(TAG, "Received device exception during capture call: ", e);
883 mDeviceState.setError(
884 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
885 break;
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700886 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700887
888 if (paramsChanged) {
889 if (DEBUG) {
890 Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
891 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700892 try {
893 mParams = mCamera.getParameters();
894 } catch (RuntimeException e) {
895 Log.e(TAG, "Received device exception: ", e);
896 mDeviceState.setError(
897 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
898 break;
899 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700900
901 // Update parameters to the latest that we think the camera is using
902 mLastRequest.setParameters(mParams);
903 }
Igor Murashkin83d86392014-07-18 14:37:19 -0700904
905 MutableLong timestampMutable = new MutableLong(/*value*/0L);
906 try {
907 boolean success = mCaptureCollector.waitForRequestCompleted(holder,
908 REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
909 /*out*/timestampMutable);
910
911 if (!success) {
912 Log.e(TAG, "Timed out while waiting for request to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700913 mCaptureCollector.failAll();
Igor Murashkin83d86392014-07-18 14:37:19 -0700914 }
915 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700916 Log.e(TAG, "Interrupted waiting for request completion: ", e);
917 mDeviceState.setError(
918 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
919 break;
Igor Murashkin83d86392014-07-18 14:37:19 -0700920 }
921
922 CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
Igor Murashkin56678d82014-07-28 10:50:08 -0700923 mLastRequest, timestampMutable.value);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700924 /*
925 * Order matters: The default result mapper is state-less; the
926 * other mappers carry state and may override keys set by the default
927 * mapper with their own values.
928 */
929
Igor Murashkin83d86392014-07-18 14:37:19 -0700930 // Update AF state
931 mFocusStateMapper.mapResultTriggers(result);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700932 // Update face-related results
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700933 mFaceDetectMapper.mapResultFaces(result, mLastRequest);
Igor Murashkin83d86392014-07-18 14:37:19 -0700934
Ruben Brunke663cb772014-09-16 13:18:31 -0700935 if (!holder.requestFailed()) {
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700936 mDeviceState.setCaptureResult(holder, result);
Ruben Brunke663cb772014-09-16 13:18:31 -0700937 }
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700938
939 if (holder.isOutputAbandoned()) {
940 anyRequestOutputAbandoned = true;
941 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700942 }
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700943
944 // Stop the repeating request if any of its output surfaces is abandoned.
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700945 if (anyRequestOutputAbandoned && burstHolder.isRepeating()) {
946 long lastFrameNumber = cancelRepeating(burstHolder.getRequestId());
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700947 if (DEBUG) {
948 Log.d(TAG, "Stopped repeating request. Last frame number is " +
949 lastFrameNumber);
950 }
Emilian Peev6f3e5a02019-11-04 13:25:05 -0800951 if (lastFrameNumber != RequestQueue.INVALID_FRAME) {
952 mDeviceState.setRepeatingRequestError(lastFrameNumber,
953 burstHolder.getRequestId());
954 } else {
955 Log.e(TAG, "Repeating request id: " + burstHolder.getRequestId() +
956 " already canceled!");
957 }
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700958 }
959
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700960 if (DEBUG) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700961 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
962 Log.d(TAG, "Capture request took " + totalTime + " ns");
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700963 mRequestCounter.countAndLog();
964 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700965 break;
966 case MSG_CLEANUP:
967 mCleanup = true;
Ruben Brunk91838de2014-07-16 17:24:17 -0700968 try {
969 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
970 TimeUnit.MILLISECONDS);
971 if (!success) {
972 Log.e(TAG, "Timed out while queueing cleanup request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700973 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700974 }
975 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700976 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
977 mDeviceState.setError(
978 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
Ruben Brunk91838de2014-07-16 17:24:17 -0700979 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700980 if (mGLThreadManager != null) {
981 mGLThreadManager.quit();
Ruben Brunke3c04342015-02-04 17:26:17 -0800982 mGLThreadManager = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700983 }
Shuzhen Wangde95aaf2019-03-25 22:07:42 -0700984 disconnectCallbackSurfaces();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700985 if (mCamera != null) {
986 mCamera.release();
Ruben Brunke3c04342015-02-04 17:26:17 -0800987 mCamera = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700988 }
989 break;
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700990 case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
991 // OK: Ignore message.
992 break;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700993 default:
994 throw new AssertionError("Unhandled message " + msg.what +
995 " on RequestThread.");
996 }
997 return true;
998 }
999 };
1000
1001 /**
1002 * Create a new RequestThreadManager.
1003 *
1004 * @param cameraId the id of the camera to use.
1005 * @param camera an open camera object. The RequestThreadManager takes ownership of this camera
1006 * object, and is responsible for closing it.
Igor Murashkindf6242e2014-07-01 18:06:13 -07001007 * @param characteristics the static camera characteristics corresponding to this camera device
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001008 * @param deviceState a {@link CameraDeviceState} state machine.
1009 */
Igor Murashkindf6242e2014-07-01 18:06:13 -07001010 public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001011 CameraDeviceState deviceState) {
Igor Murashkindf6242e2014-07-01 18:06:13 -07001012 mCamera = checkNotNull(camera, "camera must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001013 mCameraId = cameraId;
Igor Murashkindf6242e2014-07-01 18:06:13 -07001014 mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001015 String name = String.format("RequestThread-%d", cameraId);
1016 TAG = name;
Igor Murashkindf6242e2014-07-01 18:06:13 -07001017 mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
Igor Murashkin83d86392014-07-18 14:37:19 -07001018 mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -07001019 mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
Ruben Brunk91838de2014-07-16 17:24:17 -07001020 mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001021 mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
Yin-Chia Yeha07cd322018-05-31 12:11:42 -07001022 mCamera.setDetailedErrorCallback(mErrorCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001023 }
1024
1025 /**
1026 * Start the request thread.
1027 */
1028 public void start() {
1029 mRequestThread.start();
1030 }
1031
1032 /**
Ruben Brunke663cb772014-09-16 13:18:31 -07001033 * Flush any pending requests.
1034 *
1035 * @return the last frame number.
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001036 */
Ruben Brunke663cb772014-09-16 13:18:31 -07001037 public long flush() {
1038 Log.i(TAG, "Flushing all pending requests.");
1039 long lastFrame = mRequestQueue.stopRepeating();
1040 mCaptureCollector.failAll();
1041 return lastFrame;
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001042 }
1043
1044 /**
1045 * Quit the request thread, and clean up everything.
1046 */
1047 public void quit() {
Ruben Brunke3c04342015-02-04 17:26:17 -08001048 if (!mQuit.getAndSet(true)) { // Avoid sending messages on dead thread's handler.
1049 Handler handler = mRequestThread.waitAndGetHandler();
1050 handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
1051 mRequestThread.quitSafely();
1052 try {
1053 mRequestThread.join();
1054 } catch (InterruptedException e) {
1055 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
1056 mRequestThread.getName(), mRequestThread.getId()));
1057 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -07001058 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001059 }
1060
1061 /**
1062 * Submit the given burst of requests to be captured.
1063 *
1064 * <p>If the burst is repeating, replace the current repeating burst.</p>
1065 *
1066 * @param requests the burst of requests to add to the queue.
1067 * @param repeating true if the burst is repeating.
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001068 * @return the submission info, including the new request id, and the last frame number, which
1069 * contains either the frame number of the last frame that will be returned for this request,
1070 * or the frame number of the last frame that will be returned for the current repeating
1071 * request if this burst is set to be repeating.
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001072 */
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001073 public SubmitInfo submitCaptureRequests(CaptureRequest[] requests, boolean repeating) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001074 Handler handler = mRequestThread.waitAndGetHandler();
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001075 SubmitInfo info;
Ruben Brunk4aed87a2014-09-21 18:35:31 -07001076 synchronized (mIdleLock) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001077 info = mRequestQueue.submit(requests, repeating);
Ruben Brunk4aed87a2014-09-21 18:35:31 -07001078 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
1079 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001080 return info;
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001081 }
1082
1083 /**
1084 * Cancel a repeating request.
1085 *
1086 * @param requestId the id of the repeating request to cancel.
1087 * @return the last frame to be returned from the HAL for the given repeating request, or
1088 * {@code INVALID_FRAME} if none exists.
1089 */
1090 public long cancelRepeating(int requestId) {
1091 return mRequestQueue.stopRepeating(requestId);
1092 }
1093
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001094 /**
Igor Murashkin49b2b132014-06-18 19:03:00 -07001095 * Configure with the current list of output Surfaces.
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001096 *
1097 * <p>
1098 * This operation blocks until the configuration is complete.
1099 * </p>
1100 *
Igor Murashkin49b2b132014-06-18 19:03:00 -07001101 * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
1102 *
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001103 * @param outputs a {@link java.util.Collection} of outputs to configure.
1104 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -08001105 public void configure(Collection<Pair<Surface, Size>> outputs) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001106 Handler handler = mRequestThread.waitAndGetHandler();
1107 final ConditionVariable condition = new ConditionVariable(/*closed*/false);
1108 ConfigureHolder holder = new ConfigureHolder(condition, outputs);
1109 handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
1110 condition.block();
1111 }
1112}