blob: a7ea89cab303468e118db8e280e337eb3cb64fbb [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;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070024import android.hardware.camera2.utils.LongParcelable;
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 Brunkfeb50af2014-05-09 19:58:49 -070041import java.util.List;
Ruben Brunk91838de2014-07-16 17:24:17 -070042import java.util.concurrent.TimeUnit;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070043
Igor Murashkindf6242e2014-07-01 18:06:13 -070044import static com.android.internal.util.Preconditions.*;
45
Ruben Brunkfeb50af2014-05-09 19:58:49 -070046/**
47 * This class executes requests to the {@link Camera}.
48 *
49 * <p>
50 * The main components of this class are:
51 * - A message queue of requests to the {@link Camera}.
52 * - A thread that consumes requests to the {@link Camera} and executes them.
53 * - A {@link GLThreadManager} that draws to the configured output {@link Surface}s.
54 * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations.
55 * </p>
56 */
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070057@SuppressWarnings("deprecation")
Ruben Brunkfeb50af2014-05-09 19:58:49 -070058public class RequestThreadManager {
59 private final String TAG;
60 private final int mCameraId;
61 private final RequestHandlerThread mRequestThread;
62
63 private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070064 // For slightly more spammy messages that will get repeated every frame
65 private static final boolean VERBOSE =
66 Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.VERBOSE);
Ruben Brunkfeb50af2014-05-09 19:58:49 -070067 private final Camera mCamera;
Igor Murashkindf6242e2014-07-01 18:06:13 -070068 private final CameraCharacteristics mCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070069
70 private final CameraDeviceState mDeviceState;
Ruben Brunk91838de2014-07-16 17:24:17 -070071 private final CaptureCollector mCaptureCollector;
Igor Murashkin83d86392014-07-18 14:37:19 -070072 private final LegacyFocusStateMapper mFocusStateMapper;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070073 private final LegacyFaceDetectMapper mFaceDetectMapper;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070074
75 private static final int MSG_CONFIGURE_OUTPUTS = 1;
76 private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
77 private static final int MSG_CLEANUP = 3;
78
Ruben Brunk91838de2014-07-16 17:24:17 -070079 private static final int MAX_IN_FLIGHT_REQUESTS = 2;
80
Igor Murashkin3e2c14f2014-09-24 13:48:09 -070081 private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms
Igor Murashkin5096def2014-06-24 10:49:11 -070082 private static final int JPEG_FRAME_TIMEOUT = 3000; // ms (same as CTS for API2)
Igor Murashkin83d86392014-07-18 14:37:19 -070083 private static final int REQUEST_COMPLETE_TIMEOUT = 3000; // ms (same as JPEG timeout)
Ruben Brunkfeb50af2014-05-09 19:58:49 -070084
Ruben Brunkd85e1a62014-06-11 10:35:45 -070085 private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070086 private boolean mPreviewRunning = false;
87
Ruben Brunk7f2372b2014-07-02 11:05:08 -070088 private final List<Surface> mPreviewOutputs = new ArrayList<>();
89 private final List<Surface> mCallbackOutputs = new ArrayList<>();
Ruben Brunkfeb50af2014-05-09 19:58:49 -070090 private GLThreadManager mGLThreadManager;
91 private SurfaceTexture mPreviewTexture;
Ruben Brunk3e4fed22014-06-18 17:08:42 -070092 private Camera.Parameters mParams;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070093
Ruben Brunk0fd198a2014-09-23 23:35:43 -070094 private final List<Long> mJpegSurfaceIds = new ArrayList<>();
95
Ruben Brunkd85e1a62014-06-11 10:35:45 -070096 private Size mIntermediateBufferSize;
97
Ruben Brunk0fd198a2014-09-23 23:35:43 -070098 private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds);
Igor Murashkindf6242e2014-07-01 18:06:13 -070099 private LegacyRequest mLastRequest = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700100 private SurfaceTexture mDummyTexture;
101 private Surface mDummySurface;
102
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700103 private final Object mIdleLock = new Object();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700104 private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700105 private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700106
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700107 // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write
108 // limitations for (b/17379185).
109 private static final boolean USE_BLOB_FORMAT_OVERRIDE = true;
110
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700111 /**
112 * Container object for Configure messages.
113 */
114 private static class ConfigureHolder {
115 public final ConditionVariable condition;
116 public final Collection<Surface> surfaces;
117
118 public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces) {
119 this.condition = condition;
120 this.surfaces = surfaces;
121 }
122 }
123
124 /**
125 * Counter class used to calculate and log the current FPS of frame production.
126 */
127 public static class FpsCounter {
128 //TODO: Hook this up to SystTrace?
129 private static final String TAG = "FpsCounter";
130 private int mFrameCount = 0;
131 private long mLastTime = 0;
132 private long mLastPrintTime = 0;
133 private double mLastFps = 0;
134 private final String mStreamType;
135 private static final long NANO_PER_SECOND = 1000000000; //ns
136
137 public FpsCounter(String streamType) {
138 mStreamType = streamType;
139 }
140
141 public synchronized void countFrame() {
142 mFrameCount++;
143 long nextTime = SystemClock.elapsedRealtimeNanos();
144 if (mLastTime == 0) {
145 mLastTime = nextTime;
146 }
147 if (nextTime > mLastTime + NANO_PER_SECOND) {
148 long elapsed = nextTime - mLastTime;
149 mLastFps = mFrameCount * (NANO_PER_SECOND / (double) elapsed);
150 mFrameCount = 0;
151 mLastTime = nextTime;
152 }
153 }
154
155 public synchronized double checkFps() {
156 return mLastFps;
157 }
158
159 public synchronized void staggeredLog() {
160 if (mLastTime > mLastPrintTime + 5 * NANO_PER_SECOND) {
161 mLastPrintTime = mLastTime;
162 Log.d(TAG, "FPS for " + mStreamType + " stream: " + mLastFps );
163 }
164 }
165
166 public synchronized void countAndLog() {
167 countFrame();
168 staggeredLog();
169 }
170 }
171 /**
172 * Fake preview for jpeg captures when there is no active preview
173 */
174 private void createDummySurface() {
175 if (mDummyTexture == null || mDummySurface == null) {
176 mDummyTexture = new SurfaceTexture(/*ignored*/0);
177 // TODO: use smallest default sizes
178 mDummyTexture.setDefaultBufferSize(640, 480);
179 mDummySurface = new Surface(mDummyTexture);
180 }
181 }
182
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700183 private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
184 @Override
185 public void onError(int i, Camera camera) {
186 Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
187 mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
188 }
189 };
190
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700191 private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700192
193 private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
194 @Override
195 public void onPictureTaken(byte[] data, Camera camera) {
196 Log.i(TAG, "Received jpeg.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700197 Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced();
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700198 if (captureInfo == null || captureInfo.first == null) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700199 Log.e(TAG, "Dropping jpeg frame.");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700200 return;
201 }
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700202 RequestHolder holder = captureInfo.first;
203 long timestamp = captureInfo.second;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700204 for (Surface s : holder.getHolderTargets()) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700205 try {
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700206 if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700207 Log.i(TAG, "Producing jpeg buffer...");
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700208
209 int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
210 totalSize += ((totalSize - 1) & ~0x3) + 4; // align to next octonibble
211
212 LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1);
Ruben Brunk91838de2014-07-16 17:24:17 -0700213 LegacyCameraDevice.setNextTimestamp(s, timestamp);
Ruben Brunkef14da32014-06-24 16:06:54 -0700214 LegacyCameraDevice.produceFrame(s, data, data.length, /*height*/1,
215 CameraMetadataNative.NATIVE_JPEG_FORMAT);
216 }
217 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
218 Log.w(TAG, "Surface abandoned, dropping frame. ", e);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700219 }
220 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700221
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700222 mReceivedJpeg.open();
223 }
224 };
225
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700226 private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
227 @Override
228 public void onShutter() {
Ruben Brunk91838de2014-07-16 17:24:17 -0700229 mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700230 }
231 };
232
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700233 private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
234 new SurfaceTexture.OnFrameAvailableListener() {
235 @Override
236 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700237 if (DEBUG) {
238 mPrevCounter.countAndLog();
239 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700240 mGLThreadManager.queueNewFrame();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700241 }
242 };
243
244 private void stopPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700245 if (VERBOSE) {
246 Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
247 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700248 if (mPreviewRunning) {
249 mCamera.stopPreview();
250 mPreviewRunning = false;
251 }
252 }
253
254 private void startPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700255 if (VERBOSE) {
256 Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
257 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700258 if (!mPreviewRunning) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700259 // XX: CameraClient:;startPreview is not getting called after a stop
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700260 mCamera.startPreview();
261 mPreviewRunning = true;
262 }
263 }
264
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700265 private void doJpegCapturePrepare(RequestHolder request) throws IOException {
266 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
Igor Murashkina296fec2014-06-23 14:44:09 -0700267
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700268 if (!mPreviewRunning) {
Igor Murashkina296fec2014-06-23 14:44:09 -0700269 if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
270
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700271 createDummySurface();
272 mCamera.setPreviewTexture(mDummyTexture);
273 startPreview();
274 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700275 }
276
277 private void doJpegCapture(RequestHolder request) {
278 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
279
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700280 mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700281 mPreviewRunning = false;
282 }
283
284 private void doPreviewCapture(RequestHolder request) throws IOException {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700285 if (VERBOSE) {
286 Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
287 }
288
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700289 if (mPreviewRunning) {
290 return; // Already running
291 }
292
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700293 if (mPreviewTexture == null) {
294 throw new IllegalStateException(
295 "Preview capture called with no preview surfaces configured.");
296 }
297
298 mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
299 mIntermediateBufferSize.getHeight());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700300 mCamera.setPreviewTexture(mPreviewTexture);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700301
302 startPreview();
303 }
304
Ruben Brunke663cb772014-09-16 13:18:31 -0700305 private void configureOutputs(Collection<Surface> outputs) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700306 if (DEBUG) {
307 String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700308 Log.d(TAG, "configureOutputs with " + outputsStr);
309 }
310
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700311 stopPreview();
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700312 /*
313 * Try to release the previous preview's surface texture earlier if we end up
314 * using a different one; this also reduces the likelihood of getting into a deadlock
315 * when disconnecting from the old previous texture at a later time.
316 */
Ruben Brunke663cb772014-09-16 13:18:31 -0700317 try {
318 mCamera.setPreviewTexture(/*surfaceTexture*/null);
319 } catch (IOException e) {
320 Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
321 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700322
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700323 if (mGLThreadManager != null) {
324 mGLThreadManager.waitUntilStarted();
325 mGLThreadManager.ignoreNewFrames();
326 mGLThreadManager.waitUntilIdle();
327 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700328 resetJpegSurfaceFormats(mCallbackOutputs);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700329 mPreviewOutputs.clear();
330 mCallbackOutputs.clear();
Eino-Ville Talvala52571b92014-09-25 12:52:20 -0700331 mJpegSurfaceIds.clear();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700332 mPreviewTexture = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700333
Ruben Brunk28c49c92014-06-16 18:43:59 -0700334 int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
335 int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700336 if (outputs != null) {
337 for (Surface s : outputs) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700338 try {
339 int format = LegacyCameraDevice.detectSurfaceType(s);
Ruben Brunk28c49c92014-06-16 18:43:59 -0700340 LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
Ruben Brunkef14da32014-06-24 16:06:54 -0700341 switch (format) {
342 case CameraMetadataNative.NATIVE_JPEG_FORMAT:
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700343 if (USE_BLOB_FORMAT_OVERRIDE) {
344 // Override to RGBA_8888 format.
345 LegacyCameraDevice.setSurfaceFormat(s,
346 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
347 }
348 mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
Ruben Brunkef14da32014-06-24 16:06:54 -0700349 mCallbackOutputs.add(s);
350 break;
351 default:
352 mPreviewOutputs.add(s);
353 break;
354 }
355 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
356 Log.w(TAG, "Surface abandoned, skipping...", e);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700357 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700358 }
359 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700360 try {
361 mParams = mCamera.getParameters();
362 } catch (RuntimeException e) {
363 Log.e(TAG, "Received device exception: ", e);
364 mDeviceState.setError(
365 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
366 return;
367 }
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700368
369 List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
370 int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
371 if (DEBUG) {
372 Log.d(TAG, "doPreviewCapture - Selected range [" +
373 bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
374 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
375 }
376 mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
377 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700378
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700379 if (mPreviewOutputs.size() > 0) {
380 List<Size> outputSizes = new ArrayList<>(outputs.size());
381 for (Surface s : mPreviewOutputs) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700382 try {
383 Size size = LegacyCameraDevice.getSurfaceSize(s);
384 outputSizes.add(size);
385 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
386 Log.w(TAG, "Surface abandoned, skipping...", e);
387 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700388 }
389
Igor Murashkindf6242e2014-07-01 18:06:13 -0700390 Size largestOutput = SizeAreaComparator.findLargestByArea(outputSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700391
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700392 // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700393 Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700394
Igor Murashkindf6242e2014-07-01 18:06:13 -0700395 List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
396 mParams.getSupportedPreviewSizes());
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700397
398 // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
399 // of the configured output dimensions. If none exists, fall back to using the largest
400 // supported preview size.
401 long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
Igor Murashkindf6242e2014-07-01 18:06:13 -0700402 Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700403 for (Size s : supportedPreviewSizes) {
404 long currArea = s.getWidth() * s.getHeight();
405 long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
406 if (checkAspectRatiosMatch(largestJpegDimen, s) && (currArea < bestArea &&
407 currArea >= largestOutputArea)) {
408 bestPreviewDimen = s;
409 }
410 }
411
412 mIntermediateBufferSize = bestPreviewDimen;
Ruben Brunk059d7662014-09-09 18:46:00 -0700413 mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
414 mIntermediateBufferSize.getHeight());
415
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700416 if (DEBUG) {
417 Log.d(TAG, "Intermediate buffer selected with dimens: " +
418 bestPreviewDimen.toString());
419 }
420 } else {
421 mIntermediateBufferSize = null;
422 if (DEBUG) {
423 Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
424 }
425 }
426
Igor Murashkina296fec2014-06-23 14:44:09 -0700427 Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs, mParams);
428 if (smallestSupportedJpegSize != null) {
429 /*
430 * Set takePicture size to the smallest supported JPEG size large enough
431 * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
432 */
433
434 Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
435 mParams.setPictureSize(
436 smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
437 }
438
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700439 // TODO: Detect and optimize single-output paths here to skip stream teeing.
440 if (mGLThreadManager == null) {
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700441 mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700442 mGLThreadManager.start();
443 }
444 mGLThreadManager.waitUntilStarted();
Ruben Brunk91838de2014-07-16 17:24:17 -0700445 mGLThreadManager.setConfigurationAndWait(mPreviewOutputs, mCaptureCollector);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700446 mGLThreadManager.allowNewFrames();
447 mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700448 if (mPreviewTexture != null) {
449 mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
450 }
Igor Murashkin5096def2014-06-24 10:49:11 -0700451
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700452 mCamera.setParameters(mParams);
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700453 }
454
455 private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
456 if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
457 return;
458 }
459 for(Surface s : surfaces) {
460 try {
461 LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
462 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
463 Log.w(TAG, "Surface abandoned, skipping...", e);
464 }
465 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700466 }
467
Igor Murashkina296fec2014-06-23 14:44:09 -0700468 /**
469 * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
470 * than all of the configured {@code JPEG} outputs (by both width and height).
471 *
472 * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
473 * still satisfies the above constraint.</p>
474 *
475 * <p>As a result, the returned size is guaranteed to be usable without needing
476 * to upscale any of the outputs. If only one {@code JPEG} surface is used,
477 * then no scaling/cropping is necessary between the taken picture and
478 * the {@code JPEG} output surface.</p>
479 *
480 * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
481 * @param params api1 parameters (used for reading only)
482 *
483 * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
484 * {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
485 * surfaces.
486 */
487 private Size calculatePictureSize(
488 Collection<Surface> callbackOutputs, Camera.Parameters params) {
489 /*
490 * Find the largest JPEG size (if any), from the configured outputs:
491 * - the api1 picture size should be set to the smallest legal size that's at least as large
492 * as the largest configured JPEG size
493 */
494 List<Size> configuredJpegSizes = new ArrayList<Size>();
495 for (Surface callbackSurface : callbackOutputs) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700496 try {
Igor Murashkina296fec2014-06-23 14:44:09 -0700497
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700498 if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700499 continue; // Ignore non-JPEG callback formats
500 }
501
502 Size jpegSize = LegacyCameraDevice.getSurfaceSize(callbackSurface);
503 configuredJpegSizes.add(jpegSize);
504 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
505 Log.w(TAG, "Surface abandoned, skipping...", e);
Igor Murashkina296fec2014-06-23 14:44:09 -0700506 }
Igor Murashkina296fec2014-06-23 14:44:09 -0700507 }
508 if (!configuredJpegSizes.isEmpty()) {
509 /*
510 * Find the largest configured JPEG width, and height, independently
511 * of the rest.
512 *
513 * The rest of the JPEG streams can be cropped out of this smallest bounding
514 * rectangle.
515 */
516 int maxConfiguredJpegWidth = -1;
517 int maxConfiguredJpegHeight = -1;
518 for (Size jpegSize : configuredJpegSizes) {
519 maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
520 jpegSize.getWidth() : maxConfiguredJpegWidth;
521 maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
522 jpegSize.getHeight() : maxConfiguredJpegHeight;
523 }
524 Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
525
Igor Murashkindf6242e2014-07-01 18:06:13 -0700526 List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
527 params.getSupportedPictureSizes());
Igor Murashkina296fec2014-06-23 14:44:09 -0700528
529 /*
530 * Find the smallest supported JPEG size that can fit the smallest bounding
531 * rectangle for the configured JPEG sizes.
532 */
533 List<Size> candidateSupportedJpegSizes = new ArrayList<>();
534 for (Size supportedJpegSize : supportedJpegSizes) {
535 if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
536 supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
537 candidateSupportedJpegSizes.add(supportedJpegSize);
538 }
539 }
540
541 if (candidateSupportedJpegSizes.isEmpty()) {
542 throw new AssertionError(
543 "Could not find any supported JPEG sizes large enough to fit " +
544 smallestBoundJpegSize);
545 }
546
547 Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
548 new SizeAreaComparator());
549
550 if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
551 Log.w(TAG,
552 String.format(
553 "configureOutputs - Will need to crop picture %s into "
554 + "smallest bound size %s",
555 smallestSupportedJpegSize, smallestBoundJpegSize));
556 }
557
558 return smallestSupportedJpegSize;
559 }
560
561 return null;
562 }
563
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700564 private static boolean checkAspectRatiosMatch(Size a, Size b) {
565 float aAspect = a.getWidth() / (float) a.getHeight();
566 float bAspect = b.getWidth() / (float) b.getHeight();
567
568 return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
569 }
570
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700571 // Calculate the highest FPS range supported
572 private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
573 if (frameRates.size() == 0) {
574 Log.e(TAG, "No supported frame rates returned!");
575 return null;
576 }
577
578 int bestMin = 0;
579 int bestMax = 0;
580 int bestIndex = 0;
581 int index = 0;
582 for (int[] rate : frameRates) {
583 int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
584 int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
585 if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
586 bestMin = minFps;
587 bestMax = maxFps;
588 bestIndex = index;
589 }
590 index++;
591 }
592
593 return frameRates.get(bestIndex);
594 }
595
596 private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
597 private boolean mCleanup = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700598 private final LegacyResultMapper mMapper = new LegacyResultMapper();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700599
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700600 @Override
601 public boolean handleMessage(Message msg) {
602 if (mCleanup) {
603 return true;
604 }
605
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700606 if (DEBUG) {
607 Log.d(TAG, "Request thread handling message:" + msg.what);
608 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700609 long startTime = 0;
610 if (DEBUG) {
611 startTime = SystemClock.elapsedRealtimeNanos();
612 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700613 switch (msg.what) {
614 case MSG_CONFIGURE_OUTPUTS:
615 ConfigureHolder config = (ConfigureHolder) msg.obj;
Igor Murashkin49b2b132014-06-18 19:03:00 -0700616 int sizes = config.surfaces != null ? config.surfaces.size() : 0;
Ruben Brunke663cb772014-09-16 13:18:31 -0700617 Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700618
619 try {
620 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
621 TimeUnit.MILLISECONDS);
622 if (!success) {
623 Log.e(TAG, "Timed out while queueing configure request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700624 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700625 }
626 } catch (InterruptedException e) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700627 Log.e(TAG, "Interrupted while waiting for requests to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700628 mDeviceState.setError(
629 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
630 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700631 }
632
Ruben Brunke663cb772014-09-16 13:18:31 -0700633 configureOutputs(config.surfaces);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700634 config.condition.open();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700635 if (DEBUG) {
636 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
637 Log.d(TAG, "Configure took " + totalTime + " ns");
638 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700639 break;
640 case MSG_SUBMIT_CAPTURE_REQUEST:
641 Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
642
643 // Get the next burst from the request queue.
644 Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
Ruben Brunke663cb772014-09-16 13:18:31 -0700645
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700646 if (nextBurst == null) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700647 // If there are no further requests queued, wait for any currently executing
648 // requests to complete, then switch to idle state.
Ruben Brunk91838de2014-07-16 17:24:17 -0700649 try {
650 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
651 TimeUnit.MILLISECONDS);
652 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700653 Log.e(TAG,
654 "Timed out while waiting for prior requests to complete.");
655 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700656 }
657 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700658 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
659 mDeviceState.setError(
660 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
661 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700662 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700663
664 synchronized (mIdleLock) {
665 // Retry the the request queue.
666 nextBurst = mRequestQueue.getNext();
667
668 // If we still have no queued requests, go idle.
669 if (nextBurst == null) {
670 mDeviceState.setIdle();
671 break;
672 }
673 }
674 }
675
676 if (nextBurst != null) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700677 // Queue another capture if we did not get the last burst.
678 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
679 }
680
681 // Complete each request in the burst
682 List<RequestHolder> requests =
683 nextBurst.first.produceRequestHolders(nextBurst.second);
684 for (RequestHolder holder : requests) {
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700685 CaptureRequest request = holder.getRequest();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700686
687 boolean paramsChanged = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700688
Ruben Brunke663cb772014-09-16 13:18:31 -0700689 // Only update parameters if the request has changed
Igor Murashkindf6242e2014-07-01 18:06:13 -0700690 if (mLastRequest == null || mLastRequest.captureRequest != request) {
691
692 // The intermediate buffer is sometimes null, but we always need
Ruben Brunke663cb772014-09-16 13:18:31 -0700693 // the Camera1 API configured preview size
Igor Murashkindf6242e2014-07-01 18:06:13 -0700694 Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
695
Ruben Brunke663cb772014-09-16 13:18:31 -0700696 LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
697 request, previewSize, mParams); // params are copied
Igor Murashkindf6242e2014-07-01 18:06:13 -0700698
Ruben Brunke663cb772014-09-16 13:18:31 -0700699
Igor Murashkindf6242e2014-07-01 18:06:13 -0700700 // Parameters are mutated as a side-effect
701 LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
702
Ruben Brunke663cb772014-09-16 13:18:31 -0700703 // If the parameters have changed, set them in the Camera1 API.
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700704 if (!mParams.same(legacyRequest.parameters)) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700705 try {
706 mCamera.setParameters(legacyRequest.parameters);
707 } catch (RuntimeException e) {
708 // If setting the parameters failed, report a request error to
709 // the camera client, and skip any further work for this request
710 Log.e(TAG, "Exception while setting camera parameters: ", e);
711 holder.failRequest();
712 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
713 CameraDeviceImpl.CameraDeviceCallbacks.
714 ERROR_CAMERA_REQUEST);
715 continue;
716 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700717 paramsChanged = true;
Ruben Brunke663cb772014-09-16 13:18:31 -0700718 mParams = legacyRequest.parameters;
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700719 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700720
721 mLastRequest = legacyRequest;
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700722 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700723
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700724 try {
Ruben Brunk91838de2014-07-16 17:24:17 -0700725 boolean success = mCaptureCollector.queueRequest(holder,
726 mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
727
728 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700729 // Report a request error if we timed out while queuing this.
Ruben Brunk91838de2014-07-16 17:24:17 -0700730 Log.e(TAG, "Timed out while queueing capture request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700731 holder.failRequest();
732 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
733 CameraDeviceImpl.CameraDeviceCallbacks.
734 ERROR_CAMERA_REQUEST);
735 continue;
Ruben Brunk91838de2014-07-16 17:24:17 -0700736 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700737
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700738 // Starting the preview needs to happen before enabling
739 // face detection or auto focus
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700740 if (holder.hasPreviewTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700741 doPreviewCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700742 }
743 if (holder.hasJpegTargets()) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700744 while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
745 TimeUnit.MILLISECONDS)) {
746 // Fail preview requests until the queue is empty.
747 Log.e(TAG, "Timed out while waiting for preview requests to " +
748 "complete.");
749 mCaptureCollector.failNextPreview();
Ruben Brunk91838de2014-07-16 17:24:17 -0700750 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700751 mReceivedJpeg.close();
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700752 doJpegCapturePrepare(holder);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700753 }
754
755 /*
756 * Do all the actions that require a preview to have been started
757 */
758
759 // Toggle face detection on/off
760 // - do this before AF to give AF a chance to use faces
761 mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
762
763 // Unconditionally process AF triggers, since they're non-idempotent
764 // - must be done after setting the most-up-to-date AF mode
765 mFocusStateMapper.processRequestTriggers(request, mParams);
766
767 if (holder.hasJpegTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700768 doJpegCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700769 if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700770 Log.e(TAG, "Hit timeout for jpeg callback!");
Ruben Brunke663cb772014-09-16 13:18:31 -0700771 mCaptureCollector.failNextJpeg();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700772 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700773 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700774
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700775 } catch (IOException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700776 Log.e(TAG, "Received device exception: ", e);
777 mDeviceState.setError(
778 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
779 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700780 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700781 Log.e(TAG, "Interrupted during capture: ", e);
782 mDeviceState.setError(
783 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
784 break;
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700785 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700786
787 if (paramsChanged) {
788 if (DEBUG) {
789 Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
790 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700791 try {
792 mParams = mCamera.getParameters();
793 } catch (RuntimeException e) {
794 Log.e(TAG, "Received device exception: ", e);
795 mDeviceState.setError(
796 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
797 break;
798 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700799
800 // Update parameters to the latest that we think the camera is using
801 mLastRequest.setParameters(mParams);
802 }
Igor Murashkin83d86392014-07-18 14:37:19 -0700803
804 MutableLong timestampMutable = new MutableLong(/*value*/0L);
805 try {
806 boolean success = mCaptureCollector.waitForRequestCompleted(holder,
807 REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
808 /*out*/timestampMutable);
809
810 if (!success) {
811 Log.e(TAG, "Timed out while waiting for request to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700812 mCaptureCollector.failAll();
Igor Murashkin83d86392014-07-18 14:37:19 -0700813 }
814 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700815 Log.e(TAG, "Interrupted waiting for request completion: ", e);
816 mDeviceState.setError(
817 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
818 break;
Igor Murashkin83d86392014-07-18 14:37:19 -0700819 }
820
821 CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
Igor Murashkin56678d82014-07-28 10:50:08 -0700822 mLastRequest, timestampMutable.value);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700823 /*
824 * Order matters: The default result mapper is state-less; the
825 * other mappers carry state and may override keys set by the default
826 * mapper with their own values.
827 */
828
Igor Murashkin83d86392014-07-18 14:37:19 -0700829 // Update AF state
830 mFocusStateMapper.mapResultTriggers(result);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700831 // Update face-related results
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700832 mFaceDetectMapper.mapResultFaces(result, mLastRequest);
Igor Murashkin83d86392014-07-18 14:37:19 -0700833
Ruben Brunke663cb772014-09-16 13:18:31 -0700834 if (!holder.requestFailed()) {
835 mDeviceState.setCaptureResult(holder, result,
836 CameraDeviceState.NO_CAPTURE_ERROR);
837 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700838 }
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700839 if (DEBUG) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700840 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
841 Log.d(TAG, "Capture request took " + totalTime + " ns");
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700842 mRequestCounter.countAndLog();
843 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700844 break;
845 case MSG_CLEANUP:
846 mCleanup = true;
Ruben Brunk91838de2014-07-16 17:24:17 -0700847 try {
848 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
849 TimeUnit.MILLISECONDS);
850 if (!success) {
851 Log.e(TAG, "Timed out while queueing cleanup request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700852 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700853 }
854 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700855 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
856 mDeviceState.setError(
857 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
Ruben Brunk91838de2014-07-16 17:24:17 -0700858 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700859 if (mGLThreadManager != null) {
860 mGLThreadManager.quit();
861 }
862 if (mCamera != null) {
863 mCamera.release();
864 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700865 resetJpegSurfaceFormats(mCallbackOutputs);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700866 break;
867 default:
868 throw new AssertionError("Unhandled message " + msg.what +
869 " on RequestThread.");
870 }
871 return true;
872 }
873 };
874
875 /**
876 * Create a new RequestThreadManager.
877 *
878 * @param cameraId the id of the camera to use.
879 * @param camera an open camera object. The RequestThreadManager takes ownership of this camera
880 * object, and is responsible for closing it.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700881 * @param characteristics the static camera characteristics corresponding to this camera device
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700882 * @param deviceState a {@link CameraDeviceState} state machine.
883 */
Igor Murashkindf6242e2014-07-01 18:06:13 -0700884 public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700885 CameraDeviceState deviceState) {
Igor Murashkindf6242e2014-07-01 18:06:13 -0700886 mCamera = checkNotNull(camera, "camera must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700887 mCameraId = cameraId;
Igor Murashkindf6242e2014-07-01 18:06:13 -0700888 mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700889 String name = String.format("RequestThread-%d", cameraId);
890 TAG = name;
Igor Murashkindf6242e2014-07-01 18:06:13 -0700891 mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
Igor Murashkin83d86392014-07-18 14:37:19 -0700892 mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700893 mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
Ruben Brunk91838de2014-07-16 17:24:17 -0700894 mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700895 mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700896 mCamera.setErrorCallback(mErrorCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700897 }
898
899 /**
900 * Start the request thread.
901 */
902 public void start() {
903 mRequestThread.start();
904 }
905
906 /**
Ruben Brunke663cb772014-09-16 13:18:31 -0700907 * Flush any pending requests.
908 *
909 * @return the last frame number.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700910 */
Ruben Brunke663cb772014-09-16 13:18:31 -0700911 public long flush() {
912 Log.i(TAG, "Flushing all pending requests.");
913 long lastFrame = mRequestQueue.stopRepeating();
914 mCaptureCollector.failAll();
915 return lastFrame;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700916 }
917
918 /**
919 * Quit the request thread, and clean up everything.
920 */
921 public void quit() {
922 Handler handler = mRequestThread.waitAndGetHandler();
923 handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
924 mRequestThread.quitSafely();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700925 try {
926 mRequestThread.join();
927 } catch (InterruptedException e) {
928 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
929 mRequestThread.getName(), mRequestThread.getId()));
930 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700931 }
932
933 /**
934 * Submit the given burst of requests to be captured.
935 *
936 * <p>If the burst is repeating, replace the current repeating burst.</p>
937 *
938 * @param requests the burst of requests to add to the queue.
939 * @param repeating true if the burst is repeating.
940 * @param frameNumber an output argument that contains either the frame number of the last frame
941 * that will be returned for this request, or the frame number of the last
942 * frame that will be returned for the current repeating request if this
943 * burst is set to be repeating.
944 * @return the request id.
945 */
946 public int submitCaptureRequests(List<CaptureRequest> requests, boolean repeating,
947 /*out*/LongParcelable frameNumber) {
948 Handler handler = mRequestThread.waitAndGetHandler();
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700949 int ret;
950 synchronized (mIdleLock) {
951 ret = mRequestQueue.submit(requests, repeating, frameNumber);
952 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
953 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700954 return ret;
955 }
956
957 /**
958 * Cancel a repeating request.
959 *
960 * @param requestId the id of the repeating request to cancel.
961 * @return the last frame to be returned from the HAL for the given repeating request, or
962 * {@code INVALID_FRAME} if none exists.
963 */
964 public long cancelRepeating(int requestId) {
965 return mRequestQueue.stopRepeating(requestId);
966 }
967
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700968 /**
Igor Murashkin49b2b132014-06-18 19:03:00 -0700969 * Configure with the current list of output Surfaces.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700970 *
971 * <p>
972 * This operation blocks until the configuration is complete.
973 * </p>
974 *
Igor Murashkin49b2b132014-06-18 19:03:00 -0700975 * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
976 *
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700977 * @param outputs a {@link java.util.Collection} of outputs to configure.
978 */
979 public void configure(Collection<Surface> outputs) {
980 Handler handler = mRequestThread.waitAndGetHandler();
981 final ConditionVariable condition = new ConditionVariable(/*closed*/false);
982 ConfigureHolder holder = new ConfigureHolder(condition, outputs);
983 handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
984 condition.block();
985 }
986}