blob: f1f2f0c466c40c850d9ec7f1b501c15acf839d2c [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 Brunk0c798842014-09-30 03:42:13 -070019import android.graphics.ImageFormat;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070020import android.graphics.SurfaceTexture;
21import android.hardware.Camera;
Igor Murashkindf6242e2014-07-01 18:06:13 -070022import android.hardware.camera2.CameraCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070023import android.hardware.camera2.CaptureRequest;
Ruben Brunke663cb772014-09-16 13:18:31 -070024import android.hardware.camera2.impl.CameraDeviceImpl;
Ruben Brunk0c798842014-09-30 03:42:13 -070025import android.hardware.camera2.params.StreamConfigurationMap;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070026import android.hardware.camera2.utils.LongParcelable;
Igor Murashkindf6242e2014-07-01 18:06:13 -070027import android.hardware.camera2.utils.SizeAreaComparator;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070028import android.hardware.camera2.impl.CameraMetadataNative;
29import android.os.ConditionVariable;
30import android.os.Handler;
31import android.os.Message;
32import android.os.SystemClock;
33import android.util.Log;
Igor Murashkin83d86392014-07-18 14:37:19 -070034import android.util.MutableLong;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070035import android.util.Pair;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070036import android.util.Size;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070037import android.view.Surface;
38
Ruben Brunkfeb50af2014-05-09 19:58:49 -070039import java.io.IOException;
40import java.util.ArrayList;
Ruben Brunk0c798842014-09-30 03:42:13 -070041import java.util.Arrays;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070042import java.util.Collection;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070043import java.util.Collections;
Ruben Brunkf4a637d2014-11-20 18:01:36 -080044import java.util.Iterator;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070045import java.util.List;
Ruben Brunk91838de2014-07-16 17:24:17 -070046import java.util.concurrent.TimeUnit;
Ruben Brunke3c04342015-02-04 17:26:17 -080047import java.util.concurrent.atomic.AtomicBoolean;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070048
Igor Murashkindf6242e2014-07-01 18:06:13 -070049import static com.android.internal.util.Preconditions.*;
50
Ruben Brunkfeb50af2014-05-09 19:58:49 -070051/**
52 * This class executes requests to the {@link Camera}.
53 *
54 * <p>
55 * The main components of this class are:
56 * - A message queue of requests to the {@link Camera}.
57 * - A thread that consumes requests to the {@link Camera} and executes them.
58 * - A {@link GLThreadManager} that draws to the configured output {@link Surface}s.
59 * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations.
60 * </p>
61 */
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070062@SuppressWarnings("deprecation")
Ruben Brunkfeb50af2014-05-09 19:58:49 -070063public class RequestThreadManager {
64 private final String TAG;
65 private final int mCameraId;
66 private final RequestHandlerThread mRequestThread;
67
68 private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070069 // For slightly more spammy messages that will get repeated every frame
70 private static final boolean VERBOSE =
71 Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.VERBOSE);
Ruben Brunke3c04342015-02-04 17:26:17 -080072 private Camera mCamera;
Igor Murashkindf6242e2014-07-01 18:06:13 -070073 private final CameraCharacteristics mCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070074
75 private final CameraDeviceState mDeviceState;
Ruben Brunk91838de2014-07-16 17:24:17 -070076 private final CaptureCollector mCaptureCollector;
Igor Murashkin83d86392014-07-18 14:37:19 -070077 private final LegacyFocusStateMapper mFocusStateMapper;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070078 private final LegacyFaceDetectMapper mFaceDetectMapper;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070079
80 private static final int MSG_CONFIGURE_OUTPUTS = 1;
81 private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
82 private static final int MSG_CLEANUP = 3;
83
Ruben Brunk91838de2014-07-16 17:24:17 -070084 private static final int MAX_IN_FLIGHT_REQUESTS = 2;
85
Igor Murashkin3e2c14f2014-09-24 13:48:09 -070086 private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms
Ruben Brunke3c04342015-02-04 17:26:17 -080087 private static final int JPEG_FRAME_TIMEOUT = 4000; // ms (same as CTS for API2)
88 private static final int REQUEST_COMPLETE_TIMEOUT = JPEG_FRAME_TIMEOUT; // ms (same as JPEG timeout)
Ruben Brunkfeb50af2014-05-09 19:58:49 -070089
Ruben Brunkd85e1a62014-06-11 10:35:45 -070090 private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070091 private boolean mPreviewRunning = false;
92
Ruben Brunk7f2372b2014-07-02 11:05:08 -070093 private final List<Surface> mPreviewOutputs = new ArrayList<>();
94 private final List<Surface> mCallbackOutputs = new ArrayList<>();
Ruben Brunkfeb50af2014-05-09 19:58:49 -070095 private GLThreadManager mGLThreadManager;
96 private SurfaceTexture mPreviewTexture;
Ruben Brunk3e4fed22014-06-18 17:08:42 -070097 private Camera.Parameters mParams;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070098
Ruben Brunk0fd198a2014-09-23 23:35:43 -070099 private final List<Long> mJpegSurfaceIds = new ArrayList<>();
100
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700101 private Size mIntermediateBufferSize;
102
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700103 private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds);
Igor Murashkindf6242e2014-07-01 18:06:13 -0700104 private LegacyRequest mLastRequest = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700105 private SurfaceTexture mDummyTexture;
106 private Surface mDummySurface;
107
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700108 private final Object mIdleLock = new Object();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700109 private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700110 private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700111
Ruben Brunke3c04342015-02-04 17:26:17 -0800112 private final AtomicBoolean mQuit = new AtomicBoolean(false);
113
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700114 // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write
115 // limitations for (b/17379185).
116 private static final boolean USE_BLOB_FORMAT_OVERRIDE = true;
117
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700118 /**
119 * Container object for Configure messages.
120 */
121 private static class ConfigureHolder {
122 public final ConditionVariable condition;
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800123 public final Collection<Pair<Surface, Size>> surfaces;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700124
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800125 public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface,
126 Size>> surfaces) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700127 this.condition = condition;
128 this.surfaces = surfaces;
129 }
130 }
131
132 /**
133 * Counter class used to calculate and log the current FPS of frame production.
134 */
135 public static class FpsCounter {
136 //TODO: Hook this up to SystTrace?
137 private static final String TAG = "FpsCounter";
138 private int mFrameCount = 0;
139 private long mLastTime = 0;
140 private long mLastPrintTime = 0;
141 private double mLastFps = 0;
142 private final String mStreamType;
143 private static final long NANO_PER_SECOND = 1000000000; //ns
144
145 public FpsCounter(String streamType) {
146 mStreamType = streamType;
147 }
148
149 public synchronized void countFrame() {
150 mFrameCount++;
151 long nextTime = SystemClock.elapsedRealtimeNanos();
152 if (mLastTime == 0) {
153 mLastTime = nextTime;
154 }
155 if (nextTime > mLastTime + NANO_PER_SECOND) {
156 long elapsed = nextTime - mLastTime;
157 mLastFps = mFrameCount * (NANO_PER_SECOND / (double) elapsed);
158 mFrameCount = 0;
159 mLastTime = nextTime;
160 }
161 }
162
163 public synchronized double checkFps() {
164 return mLastFps;
165 }
166
167 public synchronized void staggeredLog() {
168 if (mLastTime > mLastPrintTime + 5 * NANO_PER_SECOND) {
169 mLastPrintTime = mLastTime;
170 Log.d(TAG, "FPS for " + mStreamType + " stream: " + mLastFps );
171 }
172 }
173
174 public synchronized void countAndLog() {
175 countFrame();
176 staggeredLog();
177 }
178 }
179 /**
180 * Fake preview for jpeg captures when there is no active preview
181 */
182 private void createDummySurface() {
183 if (mDummyTexture == null || mDummySurface == null) {
184 mDummyTexture = new SurfaceTexture(/*ignored*/0);
185 // TODO: use smallest default sizes
186 mDummyTexture.setDefaultBufferSize(640, 480);
187 mDummySurface = new Surface(mDummyTexture);
188 }
189 }
190
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700191 private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
192 @Override
193 public void onError(int i, Camera camera) {
194 Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
195 mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
196 }
197 };
198
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700199 private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700200
201 private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
202 @Override
203 public void onPictureTaken(byte[] data, Camera camera) {
204 Log.i(TAG, "Received jpeg.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700205 Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced();
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700206 if (captureInfo == null || captureInfo.first == null) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700207 Log.e(TAG, "Dropping jpeg frame.");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700208 return;
209 }
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700210 RequestHolder holder = captureInfo.first;
211 long timestamp = captureInfo.second;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700212 for (Surface s : holder.getHolderTargets()) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700213 try {
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700214 if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700215 Log.i(TAG, "Producing jpeg buffer...");
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700216
217 int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
Ruben Brunk31798f32014-09-25 19:56:54 -0700218 totalSize = (totalSize + 3) & ~0x3; // round up to nearest octonibble
Ruben Brunk0c798842014-09-30 03:42:13 -0700219 LegacyCameraDevice.setNextTimestamp(s, timestamp);
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700220
Ruben Brunk31798f32014-09-25 19:56:54 -0700221 if (USE_BLOB_FORMAT_OVERRIDE) {
222 // Override to RGBA_8888 format.
223 LegacyCameraDevice.setSurfaceFormat(s,
224 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
Ruben Brunk0c798842014-09-30 03:42:13 -0700225
226 int dimen = (int) Math.ceil(Math.sqrt(totalSize));
227 dimen = (dimen + 0xf) & ~0xf; // round up to nearest multiple of 16
228 LegacyCameraDevice.setSurfaceDimens(s, dimen, dimen);
229 LegacyCameraDevice.produceFrame(s, data, dimen, dimen,
230 CameraMetadataNative.NATIVE_JPEG_FORMAT);
231 } else {
232 LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1);
233 LegacyCameraDevice.produceFrame(s, data, totalSize, /*height*/1,
234 CameraMetadataNative.NATIVE_JPEG_FORMAT);
Ruben Brunk31798f32014-09-25 19:56:54 -0700235 }
Ruben Brunkef14da32014-06-24 16:06:54 -0700236 }
237 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
238 Log.w(TAG, "Surface abandoned, dropping frame. ", e);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700239 }
240 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700241
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700242 mReceivedJpeg.open();
243 }
244 };
245
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700246 private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
247 @Override
248 public void onShutter() {
Ruben Brunk91838de2014-07-16 17:24:17 -0700249 mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700250 }
251 };
252
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700253 private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
254 new SurfaceTexture.OnFrameAvailableListener() {
255 @Override
256 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700257 if (DEBUG) {
258 mPrevCounter.countAndLog();
259 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700260 mGLThreadManager.queueNewFrame();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700261 }
262 };
263
264 private void stopPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700265 if (VERBOSE) {
266 Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
267 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700268 if (mPreviewRunning) {
269 mCamera.stopPreview();
270 mPreviewRunning = false;
271 }
272 }
273
274 private void startPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700275 if (VERBOSE) {
276 Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
277 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700278 if (!mPreviewRunning) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700279 // XX: CameraClient:;startPreview is not getting called after a stop
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700280 mCamera.startPreview();
281 mPreviewRunning = true;
282 }
283 }
284
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700285 private void doJpegCapturePrepare(RequestHolder request) throws IOException {
286 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
Igor Murashkina296fec2014-06-23 14:44:09 -0700287
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700288 if (!mPreviewRunning) {
Igor Murashkina296fec2014-06-23 14:44:09 -0700289 if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
290
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700291 createDummySurface();
292 mCamera.setPreviewTexture(mDummyTexture);
293 startPreview();
294 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700295 }
296
297 private void doJpegCapture(RequestHolder request) {
298 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
299
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700300 mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700301 mPreviewRunning = false;
302 }
303
304 private void doPreviewCapture(RequestHolder request) throws IOException {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700305 if (VERBOSE) {
306 Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
307 }
308
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700309 if (mPreviewRunning) {
310 return; // Already running
311 }
312
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700313 if (mPreviewTexture == null) {
314 throw new IllegalStateException(
315 "Preview capture called with no preview surfaces configured.");
316 }
317
318 mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
319 mIntermediateBufferSize.getHeight());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700320 mCamera.setPreviewTexture(mPreviewTexture);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700321
322 startPreview();
323 }
324
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800325 private void configureOutputs(Collection<Pair<Surface, Size>> outputs) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700326 if (DEBUG) {
327 String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700328 Log.d(TAG, "configureOutputs with " + outputsStr);
329 }
330
Ruben Brunke3c04342015-02-04 17:26:17 -0800331 try {
332 stopPreview();
333 } catch (RuntimeException e) {
334 Log.e(TAG, "Received device exception in configure call: ", e);
335 mDeviceState.setError(
336 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
337 return;
338 }
339
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700340 /*
341 * Try to release the previous preview's surface texture earlier if we end up
342 * using a different one; this also reduces the likelihood of getting into a deadlock
343 * when disconnecting from the old previous texture at a later time.
344 */
Ruben Brunke663cb772014-09-16 13:18:31 -0700345 try {
346 mCamera.setPreviewTexture(/*surfaceTexture*/null);
347 } catch (IOException e) {
348 Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
Ruben Brunke3c04342015-02-04 17:26:17 -0800349 } catch (RuntimeException e) {
350 Log.e(TAG, "Received device exception in configure call: ", e);
351 mDeviceState.setError(
352 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
353 return;
Ruben Brunke663cb772014-09-16 13:18:31 -0700354 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700355
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700356 if (mGLThreadManager != null) {
357 mGLThreadManager.waitUntilStarted();
358 mGLThreadManager.ignoreNewFrames();
359 mGLThreadManager.waitUntilIdle();
360 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700361 resetJpegSurfaceFormats(mCallbackOutputs);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700362 mPreviewOutputs.clear();
363 mCallbackOutputs.clear();
Eino-Ville Talvala52571b92014-09-25 12:52:20 -0700364 mJpegSurfaceIds.clear();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700365 mPreviewTexture = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700366
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800367 List<Size> previewOutputSizes = new ArrayList<>();
368 List<Size> callbackOutputSizes = new ArrayList<>();
369
Ruben Brunk28c49c92014-06-16 18:43:59 -0700370 int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
371 int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700372 if (outputs != null) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800373 for (Pair<Surface, Size> outPair : outputs) {
374 Surface s = outPair.first;
375 Size outSize = outPair.second;
Ruben Brunkef14da32014-06-24 16:06:54 -0700376 try {
377 int format = LegacyCameraDevice.detectSurfaceType(s);
Ruben Brunk28c49c92014-06-16 18:43:59 -0700378 LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
Ruben Brunkef14da32014-06-24 16:06:54 -0700379 switch (format) {
380 case CameraMetadataNative.NATIVE_JPEG_FORMAT:
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700381 if (USE_BLOB_FORMAT_OVERRIDE) {
382 // Override to RGBA_8888 format.
383 LegacyCameraDevice.setSurfaceFormat(s,
384 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
385 }
386 mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
Ruben Brunkef14da32014-06-24 16:06:54 -0700387 mCallbackOutputs.add(s);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800388 callbackOutputSizes.add(outSize);
Ruben Brunkef14da32014-06-24 16:06:54 -0700389 break;
390 default:
391 mPreviewOutputs.add(s);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800392 previewOutputSizes.add(outSize);
Ruben Brunkef14da32014-06-24 16:06:54 -0700393 break;
394 }
395 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
396 Log.w(TAG, "Surface abandoned, skipping...", e);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700397 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700398 }
399 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700400 try {
401 mParams = mCamera.getParameters();
402 } catch (RuntimeException e) {
403 Log.e(TAG, "Received device exception: ", e);
404 mDeviceState.setError(
405 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
406 return;
407 }
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700408
409 List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
410 int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
411 if (DEBUG) {
412 Log.d(TAG, "doPreviewCapture - Selected range [" +
413 bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
414 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
415 }
416 mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
417 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700418
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800419 if (previewOutputSizes.size() > 0) {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700420
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800421 Size largestOutput = SizeAreaComparator.findLargestByArea(previewOutputSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700422
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700423 // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700424 Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700425
Igor Murashkindf6242e2014-07-01 18:06:13 -0700426 List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
427 mParams.getSupportedPreviewSizes());
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700428
429 // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
430 // of the configured output dimensions. If none exists, fall back to using the largest
431 // supported preview size.
432 long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
Igor Murashkindf6242e2014-07-01 18:06:13 -0700433 Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700434 for (Size s : supportedPreviewSizes) {
435 long currArea = s.getWidth() * s.getHeight();
436 long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
437 if (checkAspectRatiosMatch(largestJpegDimen, s) && (currArea < bestArea &&
438 currArea >= largestOutputArea)) {
439 bestPreviewDimen = s;
440 }
441 }
442
443 mIntermediateBufferSize = bestPreviewDimen;
Ruben Brunk059d7662014-09-09 18:46:00 -0700444 mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
445 mIntermediateBufferSize.getHeight());
446
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700447 if (DEBUG) {
448 Log.d(TAG, "Intermediate buffer selected with dimens: " +
449 bestPreviewDimen.toString());
450 }
451 } else {
452 mIntermediateBufferSize = null;
453 if (DEBUG) {
454 Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
455 }
456 }
457
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800458 Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs,
459 callbackOutputSizes, mParams);
Igor Murashkina296fec2014-06-23 14:44:09 -0700460 if (smallestSupportedJpegSize != null) {
461 /*
462 * Set takePicture size to the smallest supported JPEG size large enough
463 * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
464 */
465
466 Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
467 mParams.setPictureSize(
468 smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
469 }
470
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700471 // TODO: Detect and optimize single-output paths here to skip stream teeing.
472 if (mGLThreadManager == null) {
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700473 mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700474 mGLThreadManager.start();
475 }
476 mGLThreadManager.waitUntilStarted();
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800477 List<Pair<Surface, Size>> previews = new ArrayList<>();
478 Iterator<Size> previewSizeIter = previewOutputSizes.iterator();
479 for (Surface p : mPreviewOutputs) {
480 previews.add(new Pair<>(p, previewSizeIter.next()));
481 }
482 mGLThreadManager.setConfigurationAndWait(previews, mCaptureCollector);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700483 mGLThreadManager.allowNewFrames();
484 mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700485 if (mPreviewTexture != null) {
486 mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
487 }
Igor Murashkin5096def2014-06-24 10:49:11 -0700488
Ruben Brunke3c04342015-02-04 17:26:17 -0800489 try {
490 mCamera.setParameters(mParams);
491 } catch (RuntimeException e) {
492 Log.e(TAG, "Received device exception while configuring: ", e);
493 mDeviceState.setError(
494 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
495
496 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700497 }
498
499 private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
500 if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
501 return;
502 }
503 for(Surface s : surfaces) {
504 try {
505 LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
506 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
507 Log.w(TAG, "Surface abandoned, skipping...", e);
508 }
509 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700510 }
511
Igor Murashkina296fec2014-06-23 14:44:09 -0700512 /**
513 * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
514 * than all of the configured {@code JPEG} outputs (by both width and height).
515 *
516 * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
517 * still satisfies the above constraint.</p>
518 *
519 * <p>As a result, the returned size is guaranteed to be usable without needing
520 * to upscale any of the outputs. If only one {@code JPEG} surface is used,
521 * then no scaling/cropping is necessary between the taken picture and
522 * the {@code JPEG} output surface.</p>
523 *
524 * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
525 * @param params api1 parameters (used for reading only)
526 *
527 * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
528 * {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
529 * surfaces.
530 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800531 private Size calculatePictureSize( List<Surface> callbackOutputs,
532 List<Size> callbackSizes, Camera.Parameters params) {
Igor Murashkina296fec2014-06-23 14:44:09 -0700533 /*
534 * Find the largest JPEG size (if any), from the configured outputs:
535 * - the api1 picture size should be set to the smallest legal size that's at least as large
536 * as the largest configured JPEG size
537 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800538 if (callbackOutputs.size() != callbackSizes.size()) {
539 throw new IllegalStateException("Input collections must be same length");
540 }
541 List<Size> configuredJpegSizes = new ArrayList<>();
542 Iterator<Size> sizeIterator = callbackSizes.iterator();
Igor Murashkina296fec2014-06-23 14:44:09 -0700543 for (Surface callbackSurface : callbackOutputs) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800544 Size jpegSize = sizeIterator.next();
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700545 if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700546 continue; // Ignore non-JPEG callback formats
547 }
548
Ruben Brunkef14da32014-06-24 16:06:54 -0700549 configuredJpegSizes.add(jpegSize);
Igor Murashkina296fec2014-06-23 14:44:09 -0700550 }
551 if (!configuredJpegSizes.isEmpty()) {
552 /*
553 * Find the largest configured JPEG width, and height, independently
554 * of the rest.
555 *
556 * The rest of the JPEG streams can be cropped out of this smallest bounding
557 * rectangle.
558 */
559 int maxConfiguredJpegWidth = -1;
560 int maxConfiguredJpegHeight = -1;
561 for (Size jpegSize : configuredJpegSizes) {
562 maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
563 jpegSize.getWidth() : maxConfiguredJpegWidth;
564 maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
565 jpegSize.getHeight() : maxConfiguredJpegHeight;
566 }
567 Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
568
Igor Murashkindf6242e2014-07-01 18:06:13 -0700569 List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
570 params.getSupportedPictureSizes());
Igor Murashkina296fec2014-06-23 14:44:09 -0700571
572 /*
573 * Find the smallest supported JPEG size that can fit the smallest bounding
574 * rectangle for the configured JPEG sizes.
575 */
576 List<Size> candidateSupportedJpegSizes = new ArrayList<>();
577 for (Size supportedJpegSize : supportedJpegSizes) {
578 if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
579 supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
580 candidateSupportedJpegSizes.add(supportedJpegSize);
581 }
582 }
583
584 if (candidateSupportedJpegSizes.isEmpty()) {
585 throw new AssertionError(
586 "Could not find any supported JPEG sizes large enough to fit " +
587 smallestBoundJpegSize);
588 }
589
590 Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
591 new SizeAreaComparator());
592
593 if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
594 Log.w(TAG,
595 String.format(
596 "configureOutputs - Will need to crop picture %s into "
597 + "smallest bound size %s",
598 smallestSupportedJpegSize, smallestBoundJpegSize));
599 }
600
601 return smallestSupportedJpegSize;
602 }
603
604 return null;
605 }
606
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700607 private static boolean checkAspectRatiosMatch(Size a, Size b) {
608 float aAspect = a.getWidth() / (float) a.getHeight();
609 float bAspect = b.getWidth() / (float) b.getHeight();
610
611 return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
612 }
613
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700614 // Calculate the highest FPS range supported
615 private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
616 if (frameRates.size() == 0) {
617 Log.e(TAG, "No supported frame rates returned!");
618 return null;
619 }
620
621 int bestMin = 0;
622 int bestMax = 0;
623 int bestIndex = 0;
624 int index = 0;
625 for (int[] rate : frameRates) {
626 int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
627 int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
628 if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
629 bestMin = minFps;
630 bestMax = maxFps;
631 bestIndex = index;
632 }
633 index++;
634 }
635
636 return frameRates.get(bestIndex);
637 }
638
639 private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
640 private boolean mCleanup = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700641 private final LegacyResultMapper mMapper = new LegacyResultMapper();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700642
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700643 @Override
644 public boolean handleMessage(Message msg) {
645 if (mCleanup) {
646 return true;
647 }
648
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700649 if (DEBUG) {
650 Log.d(TAG, "Request thread handling message:" + msg.what);
651 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700652 long startTime = 0;
653 if (DEBUG) {
654 startTime = SystemClock.elapsedRealtimeNanos();
655 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700656 switch (msg.what) {
657 case MSG_CONFIGURE_OUTPUTS:
658 ConfigureHolder config = (ConfigureHolder) msg.obj;
Igor Murashkin49b2b132014-06-18 19:03:00 -0700659 int sizes = config.surfaces != null ? config.surfaces.size() : 0;
Ruben Brunke663cb772014-09-16 13:18:31 -0700660 Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700661
662 try {
663 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
664 TimeUnit.MILLISECONDS);
665 if (!success) {
666 Log.e(TAG, "Timed out while queueing configure request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700667 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700668 }
669 } catch (InterruptedException e) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700670 Log.e(TAG, "Interrupted while waiting for requests to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700671 mDeviceState.setError(
672 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
673 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700674 }
675
Ruben Brunke663cb772014-09-16 13:18:31 -0700676 configureOutputs(config.surfaces);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700677 config.condition.open();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700678 if (DEBUG) {
679 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
680 Log.d(TAG, "Configure took " + totalTime + " ns");
681 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700682 break;
683 case MSG_SUBMIT_CAPTURE_REQUEST:
684 Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
685
686 // Get the next burst from the request queue.
687 Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
Ruben Brunke663cb772014-09-16 13:18:31 -0700688
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700689 if (nextBurst == null) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700690 // If there are no further requests queued, wait for any currently executing
691 // requests to complete, then switch to idle state.
Ruben Brunk91838de2014-07-16 17:24:17 -0700692 try {
693 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
694 TimeUnit.MILLISECONDS);
695 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700696 Log.e(TAG,
697 "Timed out while waiting for prior requests to complete.");
698 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700699 }
700 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700701 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
702 mDeviceState.setError(
703 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
704 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700705 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700706
707 synchronized (mIdleLock) {
708 // Retry the the request queue.
709 nextBurst = mRequestQueue.getNext();
710
711 // If we still have no queued requests, go idle.
712 if (nextBurst == null) {
713 mDeviceState.setIdle();
714 break;
715 }
716 }
717 }
718
719 if (nextBurst != null) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700720 // Queue another capture if we did not get the last burst.
721 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
722 }
723
724 // Complete each request in the burst
725 List<RequestHolder> requests =
726 nextBurst.first.produceRequestHolders(nextBurst.second);
727 for (RequestHolder holder : requests) {
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700728 CaptureRequest request = holder.getRequest();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700729
730 boolean paramsChanged = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700731
Ruben Brunke663cb772014-09-16 13:18:31 -0700732 // Only update parameters if the request has changed
Igor Murashkindf6242e2014-07-01 18:06:13 -0700733 if (mLastRequest == null || mLastRequest.captureRequest != request) {
734
735 // The intermediate buffer is sometimes null, but we always need
Ruben Brunke663cb772014-09-16 13:18:31 -0700736 // the Camera1 API configured preview size
Igor Murashkindf6242e2014-07-01 18:06:13 -0700737 Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
738
Ruben Brunke663cb772014-09-16 13:18:31 -0700739 LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
740 request, previewSize, mParams); // params are copied
Igor Murashkindf6242e2014-07-01 18:06:13 -0700741
Ruben Brunke663cb772014-09-16 13:18:31 -0700742
Igor Murashkindf6242e2014-07-01 18:06:13 -0700743 // Parameters are mutated as a side-effect
744 LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
745
Ruben Brunke663cb772014-09-16 13:18:31 -0700746 // If the parameters have changed, set them in the Camera1 API.
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700747 if (!mParams.same(legacyRequest.parameters)) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700748 try {
749 mCamera.setParameters(legacyRequest.parameters);
750 } catch (RuntimeException e) {
751 // If setting the parameters failed, report a request error to
752 // the camera client, and skip any further work for this request
753 Log.e(TAG, "Exception while setting camera parameters: ", e);
754 holder.failRequest();
755 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
756 CameraDeviceImpl.CameraDeviceCallbacks.
757 ERROR_CAMERA_REQUEST);
758 continue;
759 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700760 paramsChanged = true;
Ruben Brunke663cb772014-09-16 13:18:31 -0700761 mParams = legacyRequest.parameters;
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700762 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700763
764 mLastRequest = legacyRequest;
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700765 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700766
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700767 try {
Ruben Brunk91838de2014-07-16 17:24:17 -0700768 boolean success = mCaptureCollector.queueRequest(holder,
769 mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
770
771 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700772 // Report a request error if we timed out while queuing this.
Ruben Brunk91838de2014-07-16 17:24:17 -0700773 Log.e(TAG, "Timed out while queueing capture request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700774 holder.failRequest();
775 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
776 CameraDeviceImpl.CameraDeviceCallbacks.
777 ERROR_CAMERA_REQUEST);
778 continue;
Ruben Brunk91838de2014-07-16 17:24:17 -0700779 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700780
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700781 // Starting the preview needs to happen before enabling
782 // face detection or auto focus
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700783 if (holder.hasPreviewTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700784 doPreviewCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700785 }
786 if (holder.hasJpegTargets()) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700787 while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
788 TimeUnit.MILLISECONDS)) {
789 // Fail preview requests until the queue is empty.
790 Log.e(TAG, "Timed out while waiting for preview requests to " +
791 "complete.");
792 mCaptureCollector.failNextPreview();
Ruben Brunk91838de2014-07-16 17:24:17 -0700793 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700794 mReceivedJpeg.close();
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700795 doJpegCapturePrepare(holder);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700796 }
797
798 /*
799 * Do all the actions that require a preview to have been started
800 */
801
802 // Toggle face detection on/off
803 // - do this before AF to give AF a chance to use faces
804 mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
805
806 // Unconditionally process AF triggers, since they're non-idempotent
807 // - must be done after setting the most-up-to-date AF mode
808 mFocusStateMapper.processRequestTriggers(request, mParams);
809
810 if (holder.hasJpegTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700811 doJpegCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700812 if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700813 Log.e(TAG, "Hit timeout for jpeg callback!");
Ruben Brunke663cb772014-09-16 13:18:31 -0700814 mCaptureCollector.failNextJpeg();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700815 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700816 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700817
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700818 } catch (IOException e) {
Ruben Brunke3c04342015-02-04 17:26:17 -0800819 Log.e(TAG, "Received device exception during capture call: ", e);
Ruben Brunke663cb772014-09-16 13:18:31 -0700820 mDeviceState.setError(
821 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
822 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700823 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700824 Log.e(TAG, "Interrupted during capture: ", e);
825 mDeviceState.setError(
826 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
827 break;
Ruben Brunke3c04342015-02-04 17:26:17 -0800828 } catch (RuntimeException e) {
829 Log.e(TAG, "Received device exception during capture call: ", e);
830 mDeviceState.setError(
831 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
832 break;
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700833 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700834
835 if (paramsChanged) {
836 if (DEBUG) {
837 Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
838 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700839 try {
840 mParams = mCamera.getParameters();
841 } catch (RuntimeException e) {
842 Log.e(TAG, "Received device exception: ", e);
843 mDeviceState.setError(
844 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
845 break;
846 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700847
848 // Update parameters to the latest that we think the camera is using
849 mLastRequest.setParameters(mParams);
850 }
Igor Murashkin83d86392014-07-18 14:37:19 -0700851
852 MutableLong timestampMutable = new MutableLong(/*value*/0L);
853 try {
854 boolean success = mCaptureCollector.waitForRequestCompleted(holder,
855 REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
856 /*out*/timestampMutable);
857
858 if (!success) {
859 Log.e(TAG, "Timed out while waiting for request to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700860 mCaptureCollector.failAll();
Igor Murashkin83d86392014-07-18 14:37:19 -0700861 }
862 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700863 Log.e(TAG, "Interrupted waiting for request completion: ", e);
864 mDeviceState.setError(
865 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
866 break;
Igor Murashkin83d86392014-07-18 14:37:19 -0700867 }
868
869 CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
Igor Murashkin56678d82014-07-28 10:50:08 -0700870 mLastRequest, timestampMutable.value);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700871 /*
872 * Order matters: The default result mapper is state-less; the
873 * other mappers carry state and may override keys set by the default
874 * mapper with their own values.
875 */
876
Igor Murashkin83d86392014-07-18 14:37:19 -0700877 // Update AF state
878 mFocusStateMapper.mapResultTriggers(result);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700879 // Update face-related results
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700880 mFaceDetectMapper.mapResultFaces(result, mLastRequest);
Igor Murashkin83d86392014-07-18 14:37:19 -0700881
Ruben Brunke663cb772014-09-16 13:18:31 -0700882 if (!holder.requestFailed()) {
883 mDeviceState.setCaptureResult(holder, result,
884 CameraDeviceState.NO_CAPTURE_ERROR);
885 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700886 }
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700887 if (DEBUG) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700888 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
889 Log.d(TAG, "Capture request took " + totalTime + " ns");
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700890 mRequestCounter.countAndLog();
891 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700892 break;
893 case MSG_CLEANUP:
894 mCleanup = true;
Ruben Brunk91838de2014-07-16 17:24:17 -0700895 try {
896 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
897 TimeUnit.MILLISECONDS);
898 if (!success) {
899 Log.e(TAG, "Timed out while queueing cleanup request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700900 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700901 }
902 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700903 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
904 mDeviceState.setError(
905 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
Ruben Brunk91838de2014-07-16 17:24:17 -0700906 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700907 if (mGLThreadManager != null) {
908 mGLThreadManager.quit();
Ruben Brunke3c04342015-02-04 17:26:17 -0800909 mGLThreadManager = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700910 }
911 if (mCamera != null) {
912 mCamera.release();
Ruben Brunke3c04342015-02-04 17:26:17 -0800913 mCamera = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700914 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700915 resetJpegSurfaceFormats(mCallbackOutputs);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700916 break;
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700917 case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
918 // OK: Ignore message.
919 break;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700920 default:
921 throw new AssertionError("Unhandled message " + msg.what +
922 " on RequestThread.");
923 }
924 return true;
925 }
926 };
927
928 /**
929 * Create a new RequestThreadManager.
930 *
931 * @param cameraId the id of the camera to use.
932 * @param camera an open camera object. The RequestThreadManager takes ownership of this camera
933 * object, and is responsible for closing it.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700934 * @param characteristics the static camera characteristics corresponding to this camera device
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700935 * @param deviceState a {@link CameraDeviceState} state machine.
936 */
Igor Murashkindf6242e2014-07-01 18:06:13 -0700937 public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700938 CameraDeviceState deviceState) {
Igor Murashkindf6242e2014-07-01 18:06:13 -0700939 mCamera = checkNotNull(camera, "camera must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700940 mCameraId = cameraId;
Igor Murashkindf6242e2014-07-01 18:06:13 -0700941 mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700942 String name = String.format("RequestThread-%d", cameraId);
943 TAG = name;
Igor Murashkindf6242e2014-07-01 18:06:13 -0700944 mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
Igor Murashkin83d86392014-07-18 14:37:19 -0700945 mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700946 mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
Ruben Brunk91838de2014-07-16 17:24:17 -0700947 mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700948 mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700949 mCamera.setErrorCallback(mErrorCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700950 }
951
952 /**
953 * Start the request thread.
954 */
955 public void start() {
956 mRequestThread.start();
957 }
958
959 /**
Ruben Brunke663cb772014-09-16 13:18:31 -0700960 * Flush any pending requests.
961 *
962 * @return the last frame number.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700963 */
Ruben Brunke663cb772014-09-16 13:18:31 -0700964 public long flush() {
965 Log.i(TAG, "Flushing all pending requests.");
966 long lastFrame = mRequestQueue.stopRepeating();
967 mCaptureCollector.failAll();
968 return lastFrame;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700969 }
970
971 /**
972 * Quit the request thread, and clean up everything.
973 */
974 public void quit() {
Ruben Brunke3c04342015-02-04 17:26:17 -0800975 if (!mQuit.getAndSet(true)) { // Avoid sending messages on dead thread's handler.
976 Handler handler = mRequestThread.waitAndGetHandler();
977 handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
978 mRequestThread.quitSafely();
979 try {
980 mRequestThread.join();
981 } catch (InterruptedException e) {
982 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
983 mRequestThread.getName(), mRequestThread.getId()));
984 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700985 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700986 }
987
988 /**
989 * Submit the given burst of requests to be captured.
990 *
991 * <p>If the burst is repeating, replace the current repeating burst.</p>
992 *
993 * @param requests the burst of requests to add to the queue.
994 * @param repeating true if the burst is repeating.
995 * @param frameNumber an output argument that contains either the frame number of the last frame
996 * that will be returned for this request, or the frame number of the last
997 * frame that will be returned for the current repeating request if this
998 * burst is set to be repeating.
999 * @return the request id.
1000 */
1001 public int submitCaptureRequests(List<CaptureRequest> requests, boolean repeating,
1002 /*out*/LongParcelable frameNumber) {
1003 Handler handler = mRequestThread.waitAndGetHandler();
Ruben Brunk4aed87a2014-09-21 18:35:31 -07001004 int ret;
1005 synchronized (mIdleLock) {
1006 ret = mRequestQueue.submit(requests, repeating, frameNumber);
1007 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
1008 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001009 return ret;
1010 }
1011
1012 /**
1013 * Cancel a repeating request.
1014 *
1015 * @param requestId the id of the repeating request to cancel.
1016 * @return the last frame to be returned from the HAL for the given repeating request, or
1017 * {@code INVALID_FRAME} if none exists.
1018 */
1019 public long cancelRepeating(int requestId) {
1020 return mRequestQueue.stopRepeating(requestId);
1021 }
1022
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001023 /**
Igor Murashkin49b2b132014-06-18 19:03:00 -07001024 * Configure with the current list of output Surfaces.
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001025 *
1026 * <p>
1027 * This operation blocks until the configuration is complete.
1028 * </p>
1029 *
Igor Murashkin49b2b132014-06-18 19:03:00 -07001030 * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
1031 *
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001032 * @param outputs a {@link java.util.Collection} of outputs to configure.
1033 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -08001034 public void configure(Collection<Pair<Surface, Size>> outputs) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001035 Handler handler = mRequestThread.waitAndGetHandler();
1036 final ConditionVariable condition = new ConditionVariable(/*closed*/false);
1037 ConfigureHolder holder = new ConfigureHolder(condition, outputs);
1038 handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
1039 condition.block();
1040 }
1041}