blob: aaf07e60bef56cf138c4e9779a8abeada43cb8dc [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;
196 default: {
197 Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
198 mDeviceState.setError(
199 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
200 } break;
201 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700202 }
203 };
204
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700205 private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700206
207 private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
208 @Override
209 public void onPictureTaken(byte[] data, Camera camera) {
210 Log.i(TAG, "Received jpeg.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700211 Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced();
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700212 if (captureInfo == null || captureInfo.first == null) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700213 Log.e(TAG, "Dropping jpeg frame.");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700214 return;
215 }
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700216 RequestHolder holder = captureInfo.first;
217 long timestamp = captureInfo.second;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700218 for (Surface s : holder.getHolderTargets()) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700219 try {
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700220 if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700221 Log.i(TAG, "Producing jpeg buffer...");
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700222
223 int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
Ruben Brunk31798f32014-09-25 19:56:54 -0700224 totalSize = (totalSize + 3) & ~0x3; // round up to nearest octonibble
Ruben Brunk0c798842014-09-30 03:42:13 -0700225 LegacyCameraDevice.setNextTimestamp(s, timestamp);
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700226
Ruben Brunk31798f32014-09-25 19:56:54 -0700227 if (USE_BLOB_FORMAT_OVERRIDE) {
228 // Override to RGBA_8888 format.
229 LegacyCameraDevice.setSurfaceFormat(s,
230 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
Ruben Brunk0c798842014-09-30 03:42:13 -0700231
232 int dimen = (int) Math.ceil(Math.sqrt(totalSize));
233 dimen = (dimen + 0xf) & ~0xf; // round up to nearest multiple of 16
234 LegacyCameraDevice.setSurfaceDimens(s, dimen, dimen);
235 LegacyCameraDevice.produceFrame(s, data, dimen, dimen,
236 CameraMetadataNative.NATIVE_JPEG_FORMAT);
237 } else {
238 LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1);
239 LegacyCameraDevice.produceFrame(s, data, totalSize, /*height*/1,
240 CameraMetadataNative.NATIVE_JPEG_FORMAT);
Ruben Brunk31798f32014-09-25 19:56:54 -0700241 }
Ruben Brunkef14da32014-06-24 16:06:54 -0700242 }
243 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
244 Log.w(TAG, "Surface abandoned, dropping frame. ", e);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700245 }
246 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700247
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700248 mReceivedJpeg.open();
249 }
250 };
251
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700252 private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
253 @Override
254 public void onShutter() {
Ruben Brunk91838de2014-07-16 17:24:17 -0700255 mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700256 }
257 };
258
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700259 private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
260 new SurfaceTexture.OnFrameAvailableListener() {
261 @Override
262 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700263 if (DEBUG) {
264 mPrevCounter.countAndLog();
265 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700266 mGLThreadManager.queueNewFrame();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700267 }
268 };
269
270 private void stopPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700271 if (VERBOSE) {
272 Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
273 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700274 if (mPreviewRunning) {
275 mCamera.stopPreview();
276 mPreviewRunning = false;
277 }
278 }
279
280 private void startPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700281 if (VERBOSE) {
282 Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
283 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700284 if (!mPreviewRunning) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700285 // XX: CameraClient:;startPreview is not getting called after a stop
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700286 mCamera.startPreview();
287 mPreviewRunning = true;
288 }
289 }
290
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700291 private void doJpegCapturePrepare(RequestHolder request) throws IOException {
292 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
Igor Murashkina296fec2014-06-23 14:44:09 -0700293
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700294 if (!mPreviewRunning) {
Igor Murashkina296fec2014-06-23 14:44:09 -0700295 if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
296
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700297 createDummySurface();
298 mCamera.setPreviewTexture(mDummyTexture);
299 startPreview();
300 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700301 }
302
303 private void doJpegCapture(RequestHolder request) {
304 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
305
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700306 mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700307 mPreviewRunning = false;
308 }
309
310 private void doPreviewCapture(RequestHolder request) throws IOException {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700311 if (VERBOSE) {
312 Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
313 }
314
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700315 if (mPreviewRunning) {
316 return; // Already running
317 }
318
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700319 if (mPreviewTexture == null) {
320 throw new IllegalStateException(
321 "Preview capture called with no preview surfaces configured.");
322 }
323
324 mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
325 mIntermediateBufferSize.getHeight());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700326 mCamera.setPreviewTexture(mPreviewTexture);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700327
328 startPreview();
329 }
330
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800331 private void configureOutputs(Collection<Pair<Surface, Size>> outputs) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700332 if (DEBUG) {
333 String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700334 Log.d(TAG, "configureOutputs with " + outputsStr);
335 }
336
Ruben Brunke3c04342015-02-04 17:26:17 -0800337 try {
338 stopPreview();
339 } catch (RuntimeException e) {
340 Log.e(TAG, "Received device exception in configure call: ", e);
341 mDeviceState.setError(
342 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
343 return;
344 }
345
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700346 /*
347 * Try to release the previous preview's surface texture earlier if we end up
348 * using a different one; this also reduces the likelihood of getting into a deadlock
349 * when disconnecting from the old previous texture at a later time.
350 */
Ruben Brunke663cb772014-09-16 13:18:31 -0700351 try {
352 mCamera.setPreviewTexture(/*surfaceTexture*/null);
353 } catch (IOException e) {
354 Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
Ruben Brunke3c04342015-02-04 17:26:17 -0800355 } catch (RuntimeException e) {
356 Log.e(TAG, "Received device exception in configure call: ", e);
357 mDeviceState.setError(
358 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
359 return;
Ruben Brunke663cb772014-09-16 13:18:31 -0700360 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700361
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700362 if (mGLThreadManager != null) {
363 mGLThreadManager.waitUntilStarted();
364 mGLThreadManager.ignoreNewFrames();
365 mGLThreadManager.waitUntilIdle();
366 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700367 resetJpegSurfaceFormats(mCallbackOutputs);
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800368
369 for (Surface s : mCallbackOutputs) {
370 try {
371 LegacyCameraDevice.disconnectSurface(s);
372 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
373 Log.w(TAG, "Surface abandoned, skipping...", e);
374 }
375 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700376 mPreviewOutputs.clear();
377 mCallbackOutputs.clear();
Eino-Ville Talvala52571b92014-09-25 12:52:20 -0700378 mJpegSurfaceIds.clear();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700379 mPreviewTexture = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700380
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800381 List<Size> previewOutputSizes = new ArrayList<>();
382 List<Size> callbackOutputSizes = new ArrayList<>();
383
Ruben Brunk28c49c92014-06-16 18:43:59 -0700384 int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
385 int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700386 if (outputs != null) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800387 for (Pair<Surface, Size> outPair : outputs) {
388 Surface s = outPair.first;
389 Size outSize = outPair.second;
Ruben Brunkef14da32014-06-24 16:06:54 -0700390 try {
391 int format = LegacyCameraDevice.detectSurfaceType(s);
Ruben Brunk28c49c92014-06-16 18:43:59 -0700392 LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
Ruben Brunkef14da32014-06-24 16:06:54 -0700393 switch (format) {
394 case CameraMetadataNative.NATIVE_JPEG_FORMAT:
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700395 if (USE_BLOB_FORMAT_OVERRIDE) {
396 // Override to RGBA_8888 format.
397 LegacyCameraDevice.setSurfaceFormat(s,
398 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
399 }
400 mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
Ruben Brunkef14da32014-06-24 16:06:54 -0700401 mCallbackOutputs.add(s);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800402 callbackOutputSizes.add(outSize);
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800403
404 // LegacyCameraDevice is the producer of JPEG output surfaces
405 // so LegacyCameraDevice needs to connect to the surfaces.
406 LegacyCameraDevice.connectSurface(s);
Ruben Brunkef14da32014-06-24 16:06:54 -0700407 break;
408 default:
Ruben Brunka94c6032015-06-10 16:44:28 -0700409 LegacyCameraDevice.setScalingMode(s, LegacyCameraDevice.
410 NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
Ruben Brunkef14da32014-06-24 16:06:54 -0700411 mPreviewOutputs.add(s);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800412 previewOutputSizes.add(outSize);
Ruben Brunkef14da32014-06-24 16:06:54 -0700413 break;
414 }
415 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
416 Log.w(TAG, "Surface abandoned, skipping...", e);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700417 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700418 }
419 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700420 try {
421 mParams = mCamera.getParameters();
422 } catch (RuntimeException e) {
423 Log.e(TAG, "Received device exception: ", e);
424 mDeviceState.setError(
425 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
426 return;
427 }
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700428
429 List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
430 int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
431 if (DEBUG) {
432 Log.d(TAG, "doPreviewCapture - Selected range [" +
433 bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
434 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
435 }
436 mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
437 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700438
Ruben Brunkc64e80d2015-06-10 17:14:45 -0700439 Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs,
440 callbackOutputSizes, mParams);
441
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800442 if (previewOutputSizes.size() > 0) {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700443
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800444 Size largestOutput = SizeAreaComparator.findLargestByArea(previewOutputSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700445
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700446 // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700447 Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700448
Ruben Brunkc64e80d2015-06-10 17:14:45 -0700449 Size chosenJpegDimen = (smallestSupportedJpegSize != null) ? smallestSupportedJpegSize
450 : largestJpegDimen;
451
Igor Murashkindf6242e2014-07-01 18:06:13 -0700452 List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
453 mParams.getSupportedPreviewSizes());
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700454
455 // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
456 // of the configured output dimensions. If none exists, fall back to using the largest
457 // supported preview size.
458 long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
Igor Murashkindf6242e2014-07-01 18:06:13 -0700459 Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700460 for (Size s : supportedPreviewSizes) {
461 long currArea = s.getWidth() * s.getHeight();
462 long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
Ruben Brunkc64e80d2015-06-10 17:14:45 -0700463 if (checkAspectRatiosMatch(chosenJpegDimen, s) && (currArea < bestArea &&
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700464 currArea >= largestOutputArea)) {
465 bestPreviewDimen = s;
466 }
467 }
468
469 mIntermediateBufferSize = bestPreviewDimen;
Ruben Brunk059d7662014-09-09 18:46:00 -0700470 mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
471 mIntermediateBufferSize.getHeight());
472
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700473 if (DEBUG) {
474 Log.d(TAG, "Intermediate buffer selected with dimens: " +
475 bestPreviewDimen.toString());
476 }
477 } else {
478 mIntermediateBufferSize = null;
479 if (DEBUG) {
480 Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
481 }
482 }
483
Igor Murashkina296fec2014-06-23 14:44:09 -0700484 if (smallestSupportedJpegSize != null) {
485 /*
486 * Set takePicture size to the smallest supported JPEG size large enough
487 * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
488 */
489
490 Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
491 mParams.setPictureSize(
492 smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
493 }
494
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700495 // TODO: Detect and optimize single-output paths here to skip stream teeing.
496 if (mGLThreadManager == null) {
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700497 mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700498 mGLThreadManager.start();
499 }
500 mGLThreadManager.waitUntilStarted();
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800501 List<Pair<Surface, Size>> previews = new ArrayList<>();
502 Iterator<Size> previewSizeIter = previewOutputSizes.iterator();
503 for (Surface p : mPreviewOutputs) {
504 previews.add(new Pair<>(p, previewSizeIter.next()));
505 }
506 mGLThreadManager.setConfigurationAndWait(previews, mCaptureCollector);
Sheng-Hao Tsaobb39c852017-08-10 13:41:20 +0800507
508 for (Surface p : mPreviewOutputs) {
509 try {
510 LegacyCameraDevice.setSurfaceOrientation(p, facing, orientation);
511 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
512 Log.e(TAG, "Surface abandoned, skipping setSurfaceOrientation()", e);
513 }
514 }
515
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700516 mGLThreadManager.allowNewFrames();
517 mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700518 if (mPreviewTexture != null) {
519 mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
520 }
Igor Murashkin5096def2014-06-24 10:49:11 -0700521
Ruben Brunke3c04342015-02-04 17:26:17 -0800522 try {
523 mCamera.setParameters(mParams);
524 } catch (RuntimeException e) {
525 Log.e(TAG, "Received device exception while configuring: ", e);
526 mDeviceState.setError(
527 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
528
529 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700530 }
531
532 private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
533 if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
534 return;
535 }
536 for(Surface s : surfaces) {
Ruben Brunk443ab2c2015-03-12 20:54:03 -0700537 if (s == null || !s.isValid()) {
538 Log.w(TAG, "Jpeg surface is invalid, skipping...");
539 continue;
540 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700541 try {
542 LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
543 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
544 Log.w(TAG, "Surface abandoned, skipping...", e);
545 }
546 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700547 }
548
Igor Murashkina296fec2014-06-23 14:44:09 -0700549 /**
550 * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
551 * than all of the configured {@code JPEG} outputs (by both width and height).
552 *
553 * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
554 * still satisfies the above constraint.</p>
555 *
556 * <p>As a result, the returned size is guaranteed to be usable without needing
557 * to upscale any of the outputs. If only one {@code JPEG} surface is used,
558 * then no scaling/cropping is necessary between the taken picture and
559 * the {@code JPEG} output surface.</p>
560 *
561 * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
562 * @param params api1 parameters (used for reading only)
563 *
564 * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
565 * {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
566 * surfaces.
567 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800568 private Size calculatePictureSize( List<Surface> callbackOutputs,
569 List<Size> callbackSizes, Camera.Parameters params) {
Igor Murashkina296fec2014-06-23 14:44:09 -0700570 /*
571 * Find the largest JPEG size (if any), from the configured outputs:
572 * - the api1 picture size should be set to the smallest legal size that's at least as large
573 * as the largest configured JPEG size
574 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800575 if (callbackOutputs.size() != callbackSizes.size()) {
576 throw new IllegalStateException("Input collections must be same length");
577 }
578 List<Size> configuredJpegSizes = new ArrayList<>();
579 Iterator<Size> sizeIterator = callbackSizes.iterator();
Igor Murashkina296fec2014-06-23 14:44:09 -0700580 for (Surface callbackSurface : callbackOutputs) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800581 Size jpegSize = sizeIterator.next();
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700582 if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700583 continue; // Ignore non-JPEG callback formats
584 }
585
Ruben Brunkef14da32014-06-24 16:06:54 -0700586 configuredJpegSizes.add(jpegSize);
Igor Murashkina296fec2014-06-23 14:44:09 -0700587 }
588 if (!configuredJpegSizes.isEmpty()) {
589 /*
590 * Find the largest configured JPEG width, and height, independently
591 * of the rest.
592 *
593 * The rest of the JPEG streams can be cropped out of this smallest bounding
594 * rectangle.
595 */
596 int maxConfiguredJpegWidth = -1;
597 int maxConfiguredJpegHeight = -1;
598 for (Size jpegSize : configuredJpegSizes) {
599 maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
600 jpegSize.getWidth() : maxConfiguredJpegWidth;
601 maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
602 jpegSize.getHeight() : maxConfiguredJpegHeight;
603 }
604 Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
605
Igor Murashkindf6242e2014-07-01 18:06:13 -0700606 List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
607 params.getSupportedPictureSizes());
Igor Murashkina296fec2014-06-23 14:44:09 -0700608
609 /*
610 * Find the smallest supported JPEG size that can fit the smallest bounding
611 * rectangle for the configured JPEG sizes.
612 */
613 List<Size> candidateSupportedJpegSizes = new ArrayList<>();
614 for (Size supportedJpegSize : supportedJpegSizes) {
615 if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
616 supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
617 candidateSupportedJpegSizes.add(supportedJpegSize);
618 }
619 }
620
621 if (candidateSupportedJpegSizes.isEmpty()) {
622 throw new AssertionError(
623 "Could not find any supported JPEG sizes large enough to fit " +
624 smallestBoundJpegSize);
625 }
626
627 Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
628 new SizeAreaComparator());
629
630 if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
631 Log.w(TAG,
632 String.format(
633 "configureOutputs - Will need to crop picture %s into "
634 + "smallest bound size %s",
635 smallestSupportedJpegSize, smallestBoundJpegSize));
636 }
637
638 return smallestSupportedJpegSize;
639 }
640
641 return null;
642 }
643
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700644 private static boolean checkAspectRatiosMatch(Size a, Size b) {
645 float aAspect = a.getWidth() / (float) a.getHeight();
646 float bAspect = b.getWidth() / (float) b.getHeight();
647
648 return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
649 }
650
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700651 // Calculate the highest FPS range supported
652 private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
653 if (frameRates.size() == 0) {
654 Log.e(TAG, "No supported frame rates returned!");
655 return null;
656 }
657
658 int bestMin = 0;
659 int bestMax = 0;
660 int bestIndex = 0;
661 int index = 0;
662 for (int[] rate : frameRates) {
663 int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
664 int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
665 if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
666 bestMin = minFps;
667 bestMax = maxFps;
668 bestIndex = index;
669 }
670 index++;
671 }
672
673 return frameRates.get(bestIndex);
674 }
675
676 private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
677 private boolean mCleanup = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700678 private final LegacyResultMapper mMapper = new LegacyResultMapper();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700679
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700680 @Override
681 public boolean handleMessage(Message msg) {
682 if (mCleanup) {
683 return true;
684 }
685
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700686 if (DEBUG) {
687 Log.d(TAG, "Request thread handling message:" + msg.what);
688 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700689 long startTime = 0;
690 if (DEBUG) {
691 startTime = SystemClock.elapsedRealtimeNanos();
692 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700693 switch (msg.what) {
694 case MSG_CONFIGURE_OUTPUTS:
695 ConfigureHolder config = (ConfigureHolder) msg.obj;
Igor Murashkin49b2b132014-06-18 19:03:00 -0700696 int sizes = config.surfaces != null ? config.surfaces.size() : 0;
Ruben Brunke663cb772014-09-16 13:18:31 -0700697 Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700698
699 try {
700 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
701 TimeUnit.MILLISECONDS);
702 if (!success) {
703 Log.e(TAG, "Timed out while queueing configure request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700704 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700705 }
706 } catch (InterruptedException e) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700707 Log.e(TAG, "Interrupted while waiting for requests to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700708 mDeviceState.setError(
709 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
710 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700711 }
712
Ruben Brunke663cb772014-09-16 13:18:31 -0700713 configureOutputs(config.surfaces);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700714 config.condition.open();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700715 if (DEBUG) {
716 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
717 Log.d(TAG, "Configure took " + totalTime + " ns");
718 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700719 break;
720 case MSG_SUBMIT_CAPTURE_REQUEST:
721 Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700722 boolean anyRequestOutputAbandoned = false;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700723
724 // Get the next burst from the request queue.
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700725 RequestQueue.RequestQueueEntry nextBurst = mRequestQueue.getNext();
Ruben Brunke663cb772014-09-16 13:18:31 -0700726
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700727 if (nextBurst == null) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700728 // If there are no further requests queued, wait for any currently executing
729 // requests to complete, then switch to idle state.
Ruben Brunk91838de2014-07-16 17:24:17 -0700730 try {
731 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
732 TimeUnit.MILLISECONDS);
733 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700734 Log.e(TAG,
735 "Timed out while waiting for prior requests to complete.");
736 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700737 }
738 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700739 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
740 mDeviceState.setError(
741 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
742 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700743 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700744
745 synchronized (mIdleLock) {
746 // Retry the the request queue.
747 nextBurst = mRequestQueue.getNext();
748
749 // If we still have no queued requests, go idle.
750 if (nextBurst == null) {
751 mDeviceState.setIdle();
752 break;
753 }
754 }
755 }
756
757 if (nextBurst != null) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700758 // Queue another capture if we did not get the last burst.
759 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700760
761 // Check whether capture queue becomes empty
762 if (nextBurst.isQueueEmpty()) {
763 mDeviceState.setRequestQueueEmpty();
764 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700765 }
766
767 // Complete each request in the burst
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700768 BurstHolder burstHolder = nextBurst.getBurstHolder();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700769 List<RequestHolder> requests =
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700770 burstHolder.produceRequestHolders(nextBurst.getFrameNumber());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700771 for (RequestHolder holder : requests) {
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700772 CaptureRequest request = holder.getRequest();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700773
774 boolean paramsChanged = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700775
Ruben Brunke663cb772014-09-16 13:18:31 -0700776 // Only update parameters if the request has changed
Igor Murashkindf6242e2014-07-01 18:06:13 -0700777 if (mLastRequest == null || mLastRequest.captureRequest != request) {
778
779 // The intermediate buffer is sometimes null, but we always need
Ruben Brunke663cb772014-09-16 13:18:31 -0700780 // the Camera1 API configured preview size
Igor Murashkindf6242e2014-07-01 18:06:13 -0700781 Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
782
Ruben Brunke663cb772014-09-16 13:18:31 -0700783 LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
784 request, previewSize, mParams); // params are copied
Igor Murashkindf6242e2014-07-01 18:06:13 -0700785
Ruben Brunke663cb772014-09-16 13:18:31 -0700786
Igor Murashkindf6242e2014-07-01 18:06:13 -0700787 // Parameters are mutated as a side-effect
788 LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
789
Ruben Brunke663cb772014-09-16 13:18:31 -0700790 // If the parameters have changed, set them in the Camera1 API.
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700791 if (!mParams.same(legacyRequest.parameters)) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700792 try {
793 mCamera.setParameters(legacyRequest.parameters);
794 } catch (RuntimeException e) {
795 // If setting the parameters failed, report a request error to
796 // the camera client, and skip any further work for this request
797 Log.e(TAG, "Exception while setting camera parameters: ", e);
798 holder.failRequest();
799 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
800 CameraDeviceImpl.CameraDeviceCallbacks.
801 ERROR_CAMERA_REQUEST);
802 continue;
803 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700804 paramsChanged = true;
Ruben Brunke663cb772014-09-16 13:18:31 -0700805 mParams = legacyRequest.parameters;
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700806 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700807
808 mLastRequest = legacyRequest;
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700809 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700810
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700811 try {
Ruben Brunk91838de2014-07-16 17:24:17 -0700812 boolean success = mCaptureCollector.queueRequest(holder,
813 mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
814
815 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700816 // Report a request error if we timed out while queuing this.
Ruben Brunk91838de2014-07-16 17:24:17 -0700817 Log.e(TAG, "Timed out while queueing capture request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700818 holder.failRequest();
819 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
820 CameraDeviceImpl.CameraDeviceCallbacks.
821 ERROR_CAMERA_REQUEST);
822 continue;
Ruben Brunk91838de2014-07-16 17:24:17 -0700823 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700824
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700825 // Starting the preview needs to happen before enabling
826 // face detection or auto focus
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700827 if (holder.hasPreviewTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700828 doPreviewCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700829 }
830 if (holder.hasJpegTargets()) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700831 while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
832 TimeUnit.MILLISECONDS)) {
833 // Fail preview requests until the queue is empty.
834 Log.e(TAG, "Timed out while waiting for preview requests to " +
835 "complete.");
836 mCaptureCollector.failNextPreview();
Ruben Brunk91838de2014-07-16 17:24:17 -0700837 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700838 mReceivedJpeg.close();
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700839 doJpegCapturePrepare(holder);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700840 }
841
842 /*
843 * Do all the actions that require a preview to have been started
844 */
845
846 // Toggle face detection on/off
847 // - do this before AF to give AF a chance to use faces
848 mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
849
850 // Unconditionally process AF triggers, since they're non-idempotent
851 // - must be done after setting the most-up-to-date AF mode
852 mFocusStateMapper.processRequestTriggers(request, mParams);
853
854 if (holder.hasJpegTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700855 doJpegCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700856 if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700857 Log.e(TAG, "Hit timeout for jpeg callback!");
Ruben Brunke663cb772014-09-16 13:18:31 -0700858 mCaptureCollector.failNextJpeg();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700859 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700860 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700861
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700862 } catch (IOException e) {
Ruben Brunke3c04342015-02-04 17:26:17 -0800863 Log.e(TAG, "Received device exception during capture call: ", e);
Ruben Brunke663cb772014-09-16 13:18:31 -0700864 mDeviceState.setError(
865 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
866 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700867 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700868 Log.e(TAG, "Interrupted during capture: ", e);
869 mDeviceState.setError(
870 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
871 break;
Ruben Brunke3c04342015-02-04 17:26:17 -0800872 } catch (RuntimeException e) {
873 Log.e(TAG, "Received device exception during capture call: ", e);
874 mDeviceState.setError(
875 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
876 break;
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700877 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700878
879 if (paramsChanged) {
880 if (DEBUG) {
881 Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
882 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700883 try {
884 mParams = mCamera.getParameters();
885 } catch (RuntimeException e) {
886 Log.e(TAG, "Received device exception: ", e);
887 mDeviceState.setError(
888 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
889 break;
890 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700891
892 // Update parameters to the latest that we think the camera is using
893 mLastRequest.setParameters(mParams);
894 }
Igor Murashkin83d86392014-07-18 14:37:19 -0700895
896 MutableLong timestampMutable = new MutableLong(/*value*/0L);
897 try {
898 boolean success = mCaptureCollector.waitForRequestCompleted(holder,
899 REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
900 /*out*/timestampMutable);
901
902 if (!success) {
903 Log.e(TAG, "Timed out while waiting for request to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700904 mCaptureCollector.failAll();
Igor Murashkin83d86392014-07-18 14:37:19 -0700905 }
906 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700907 Log.e(TAG, "Interrupted waiting for request completion: ", e);
908 mDeviceState.setError(
909 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
910 break;
Igor Murashkin83d86392014-07-18 14:37:19 -0700911 }
912
913 CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
Igor Murashkin56678d82014-07-28 10:50:08 -0700914 mLastRequest, timestampMutable.value);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700915 /*
916 * Order matters: The default result mapper is state-less; the
917 * other mappers carry state and may override keys set by the default
918 * mapper with their own values.
919 */
920
Igor Murashkin83d86392014-07-18 14:37:19 -0700921 // Update AF state
922 mFocusStateMapper.mapResultTriggers(result);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700923 // Update face-related results
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700924 mFaceDetectMapper.mapResultFaces(result, mLastRequest);
Igor Murashkin83d86392014-07-18 14:37:19 -0700925
Ruben Brunke663cb772014-09-16 13:18:31 -0700926 if (!holder.requestFailed()) {
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -0700927 mDeviceState.setCaptureResult(holder, result);
Ruben Brunke663cb772014-09-16 13:18:31 -0700928 }
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700929
930 if (holder.isOutputAbandoned()) {
931 anyRequestOutputAbandoned = true;
932 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700933 }
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700934
935 // Stop the repeating request if any of its output surfaces is abandoned.
Shuzhen Wang234ba3e2017-08-11 09:11:23 -0700936 if (anyRequestOutputAbandoned && burstHolder.isRepeating()) {
937 long lastFrameNumber = cancelRepeating(burstHolder.getRequestId());
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700938 if (DEBUG) {
939 Log.d(TAG, "Stopped repeating request. Last frame number is " +
940 lastFrameNumber);
941 }
Yin-Chia Yeh8cd12e92017-09-05 18:14:21 -0700942 mDeviceState.setRepeatingRequestError(lastFrameNumber,
943 burstHolder.getRequestId());
Chien-Yu Chen2da496f2016-04-14 13:33:00 -0700944 }
945
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700946 if (DEBUG) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700947 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
948 Log.d(TAG, "Capture request took " + totalTime + " ns");
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700949 mRequestCounter.countAndLog();
950 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700951 break;
952 case MSG_CLEANUP:
953 mCleanup = true;
Ruben Brunk91838de2014-07-16 17:24:17 -0700954 try {
955 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
956 TimeUnit.MILLISECONDS);
957 if (!success) {
958 Log.e(TAG, "Timed out while queueing cleanup request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700959 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700960 }
961 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700962 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
963 mDeviceState.setError(
964 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
Ruben Brunk91838de2014-07-16 17:24:17 -0700965 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700966 if (mGLThreadManager != null) {
967 mGLThreadManager.quit();
Ruben Brunke3c04342015-02-04 17:26:17 -0800968 mGLThreadManager = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700969 }
970 if (mCamera != null) {
971 mCamera.release();
Ruben Brunke3c04342015-02-04 17:26:17 -0800972 mCamera = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700973 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700974 resetJpegSurfaceFormats(mCallbackOutputs);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700975 break;
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700976 case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
977 // OK: Ignore message.
978 break;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700979 default:
980 throw new AssertionError("Unhandled message " + msg.what +
981 " on RequestThread.");
982 }
983 return true;
984 }
985 };
986
987 /**
988 * Create a new RequestThreadManager.
989 *
990 * @param cameraId the id of the camera to use.
991 * @param camera an open camera object. The RequestThreadManager takes ownership of this camera
992 * object, and is responsible for closing it.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700993 * @param characteristics the static camera characteristics corresponding to this camera device
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700994 * @param deviceState a {@link CameraDeviceState} state machine.
995 */
Igor Murashkindf6242e2014-07-01 18:06:13 -0700996 public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700997 CameraDeviceState deviceState) {
Igor Murashkindf6242e2014-07-01 18:06:13 -0700998 mCamera = checkNotNull(camera, "camera must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700999 mCameraId = cameraId;
Igor Murashkindf6242e2014-07-01 18:06:13 -07001000 mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001001 String name = String.format("RequestThread-%d", cameraId);
1002 TAG = name;
Igor Murashkindf6242e2014-07-01 18:06:13 -07001003 mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
Igor Murashkin83d86392014-07-18 14:37:19 -07001004 mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -07001005 mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
Ruben Brunk91838de2014-07-16 17:24:17 -07001006 mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001007 mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
Ruben Brunk4aed87a2014-09-21 18:35:31 -07001008 mCamera.setErrorCallback(mErrorCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001009 }
1010
1011 /**
1012 * Start the request thread.
1013 */
1014 public void start() {
1015 mRequestThread.start();
1016 }
1017
1018 /**
Ruben Brunke663cb772014-09-16 13:18:31 -07001019 * Flush any pending requests.
1020 *
1021 * @return the last frame number.
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001022 */
Ruben Brunke663cb772014-09-16 13:18:31 -07001023 public long flush() {
1024 Log.i(TAG, "Flushing all pending requests.");
1025 long lastFrame = mRequestQueue.stopRepeating();
1026 mCaptureCollector.failAll();
1027 return lastFrame;
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001028 }
1029
1030 /**
1031 * Quit the request thread, and clean up everything.
1032 */
1033 public void quit() {
Ruben Brunke3c04342015-02-04 17:26:17 -08001034 if (!mQuit.getAndSet(true)) { // Avoid sending messages on dead thread's handler.
1035 Handler handler = mRequestThread.waitAndGetHandler();
1036 handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
1037 mRequestThread.quitSafely();
1038 try {
1039 mRequestThread.join();
1040 } catch (InterruptedException e) {
1041 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
1042 mRequestThread.getName(), mRequestThread.getId()));
1043 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -07001044 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001045 }
1046
1047 /**
1048 * Submit the given burst of requests to be captured.
1049 *
1050 * <p>If the burst is repeating, replace the current repeating burst.</p>
1051 *
1052 * @param requests the burst of requests to add to the queue.
1053 * @param repeating true if the burst is repeating.
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001054 * @return the submission info, including the new request id, and the last frame number, which
1055 * contains either the frame number of the last frame that will be returned for this request,
1056 * or the frame number of the last frame that will be returned for the current repeating
1057 * request if this burst is set to be repeating.
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001058 */
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001059 public SubmitInfo submitCaptureRequests(CaptureRequest[] requests, boolean repeating) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001060 Handler handler = mRequestThread.waitAndGetHandler();
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001061 SubmitInfo info;
Ruben Brunk4aed87a2014-09-21 18:35:31 -07001062 synchronized (mIdleLock) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001063 info = mRequestQueue.submit(requests, repeating);
Ruben Brunk4aed87a2014-09-21 18:35:31 -07001064 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
1065 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001066 return info;
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001067 }
1068
1069 /**
1070 * Cancel a repeating request.
1071 *
1072 * @param requestId the id of the repeating request to cancel.
1073 * @return the last frame to be returned from the HAL for the given repeating request, or
1074 * {@code INVALID_FRAME} if none exists.
1075 */
1076 public long cancelRepeating(int requestId) {
1077 return mRequestQueue.stopRepeating(requestId);
1078 }
1079
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001080 /**
Igor Murashkin49b2b132014-06-18 19:03:00 -07001081 * Configure with the current list of output Surfaces.
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001082 *
1083 * <p>
1084 * This operation blocks until the configuration is complete.
1085 * </p>
1086 *
Igor Murashkin49b2b132014-06-18 19:03:00 -07001087 * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
1088 *
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001089 * @param outputs a {@link java.util.Collection} of outputs to configure.
1090 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -08001091 public void configure(Collection<Pair<Surface, Size>> outputs) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001092 Handler handler = mRequestThread.waitAndGetHandler();
1093 final ConditionVariable condition = new ConditionVariable(/*closed*/false);
1094 ConfigureHolder holder = new ConfigureHolder(condition, outputs);
1095 handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
1096 condition.block();
1097 }
1098}