blob: e8ce3ec7aaa91ef61a7eea28e85e7c837c8801da [file] [log] [blame]
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.camera2.legacy;
18
Ruben Brunkfeb50af2014-05-09 19:58:49 -070019import android.graphics.SurfaceTexture;
20import android.hardware.Camera;
Igor Murashkindf6242e2014-07-01 18:06:13 -070021import android.hardware.camera2.CameraCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070022import android.hardware.camera2.CaptureRequest;
Ruben Brunke663cb772014-09-16 13:18:31 -070023import android.hardware.camera2.impl.CameraDeviceImpl;
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -080024import android.hardware.camera2.utils.SubmitInfo;
Igor Murashkindf6242e2014-07-01 18:06:13 -070025import android.hardware.camera2.utils.SizeAreaComparator;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070026import android.hardware.camera2.impl.CameraMetadataNative;
27import android.os.ConditionVariable;
28import android.os.Handler;
29import android.os.Message;
30import android.os.SystemClock;
31import android.util.Log;
Igor Murashkin83d86392014-07-18 14:37:19 -070032import android.util.MutableLong;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070033import android.util.Pair;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070034import android.util.Size;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070035import android.view.Surface;
36
Ruben Brunkfeb50af2014-05-09 19:58:49 -070037import java.io.IOException;
38import java.util.ArrayList;
39import java.util.Collection;
Ruben Brunkd85e1a62014-06-11 10:35:45 -070040import java.util.Collections;
Ruben Brunkf4a637d2014-11-20 18:01:36 -080041import java.util.Iterator;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070042import java.util.List;
Ruben Brunk91838de2014-07-16 17:24:17 -070043import java.util.concurrent.TimeUnit;
Ruben Brunke3c04342015-02-04 17:26:17 -080044import java.util.concurrent.atomic.AtomicBoolean;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070045
Igor Murashkindf6242e2014-07-01 18:06:13 -070046import static com.android.internal.util.Preconditions.*;
47
Ruben Brunkfeb50af2014-05-09 19:58:49 -070048/**
49 * This class executes requests to the {@link Camera}.
50 *
51 * <p>
52 * The main components of this class are:
53 * - A message queue of requests to the {@link Camera}.
54 * - A thread that consumes requests to the {@link Camera} and executes them.
55 * - A {@link GLThreadManager} that draws to the configured output {@link Surface}s.
56 * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations.
57 * </p>
58 */
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070059@SuppressWarnings("deprecation")
Ruben Brunkfeb50af2014-05-09 19:58:49 -070060public class RequestThreadManager {
61 private final String TAG;
62 private final int mCameraId;
63 private final RequestHandlerThread mRequestThread;
64
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070065 private static final boolean DEBUG = false;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070066 // For slightly more spammy messages that will get repeated every frame
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070067 private static final boolean VERBOSE = false;
Ruben Brunke3c04342015-02-04 17:26:17 -080068 private Camera mCamera;
Igor Murashkindf6242e2014-07-01 18:06:13 -070069 private final CameraCharacteristics mCharacteristics;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070070
71 private final CameraDeviceState mDeviceState;
Ruben Brunk91838de2014-07-16 17:24:17 -070072 private final CaptureCollector mCaptureCollector;
Igor Murashkin83d86392014-07-18 14:37:19 -070073 private final LegacyFocusStateMapper mFocusStateMapper;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070074 private final LegacyFaceDetectMapper mFaceDetectMapper;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070075
76 private static final int MSG_CONFIGURE_OUTPUTS = 1;
77 private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
78 private static final int MSG_CLEANUP = 3;
79
Ruben Brunk91838de2014-07-16 17:24:17 -070080 private static final int MAX_IN_FLIGHT_REQUESTS = 2;
81
Igor Murashkin3e2c14f2014-09-24 13:48:09 -070082 private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms
Ruben Brunke3c04342015-02-04 17:26:17 -080083 private static final int JPEG_FRAME_TIMEOUT = 4000; // ms (same as CTS for API2)
Ruben Brunk45441f32015-02-05 11:55:40 -080084 private static final int REQUEST_COMPLETE_TIMEOUT = JPEG_FRAME_TIMEOUT;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070085
Ruben Brunkd85e1a62014-06-11 10:35:45 -070086 private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070087 private boolean mPreviewRunning = false;
88
Ruben Brunk7f2372b2014-07-02 11:05:08 -070089 private final List<Surface> mPreviewOutputs = new ArrayList<>();
90 private final List<Surface> mCallbackOutputs = new ArrayList<>();
Ruben Brunkfeb50af2014-05-09 19:58:49 -070091 private GLThreadManager mGLThreadManager;
92 private SurfaceTexture mPreviewTexture;
Ruben Brunk3e4fed22014-06-18 17:08:42 -070093 private Camera.Parameters mParams;
Ruben Brunkfeb50af2014-05-09 19:58:49 -070094
Ruben Brunk0fd198a2014-09-23 23:35:43 -070095 private final List<Long> mJpegSurfaceIds = new ArrayList<>();
96
Ruben Brunkd85e1a62014-06-11 10:35:45 -070097 private Size mIntermediateBufferSize;
98
Ruben Brunk0fd198a2014-09-23 23:35:43 -070099 private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds);
Igor Murashkindf6242e2014-07-01 18:06:13 -0700100 private LegacyRequest mLastRequest = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700101 private SurfaceTexture mDummyTexture;
102 private Surface mDummySurface;
103
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700104 private final Object mIdleLock = new Object();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700105 private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700106 private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700107
Ruben Brunke3c04342015-02-04 17:26:17 -0800108 private final AtomicBoolean mQuit = new AtomicBoolean(false);
109
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700110 // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write
111 // limitations for (b/17379185).
112 private static final boolean USE_BLOB_FORMAT_OVERRIDE = true;
113
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700114 /**
115 * Container object for Configure messages.
116 */
117 private static class ConfigureHolder {
118 public final ConditionVariable condition;
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800119 public final Collection<Pair<Surface, Size>> surfaces;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700120
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800121 public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface,
122 Size>> surfaces) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700123 this.condition = condition;
124 this.surfaces = surfaces;
125 }
126 }
127
128 /**
129 * Counter class used to calculate and log the current FPS of frame production.
130 */
131 public static class FpsCounter {
132 //TODO: Hook this up to SystTrace?
133 private static final String TAG = "FpsCounter";
134 private int mFrameCount = 0;
135 private long mLastTime = 0;
136 private long mLastPrintTime = 0;
137 private double mLastFps = 0;
138 private final String mStreamType;
139 private static final long NANO_PER_SECOND = 1000000000; //ns
140
141 public FpsCounter(String streamType) {
142 mStreamType = streamType;
143 }
144
145 public synchronized void countFrame() {
146 mFrameCount++;
147 long nextTime = SystemClock.elapsedRealtimeNanos();
148 if (mLastTime == 0) {
149 mLastTime = nextTime;
150 }
151 if (nextTime > mLastTime + NANO_PER_SECOND) {
152 long elapsed = nextTime - mLastTime;
153 mLastFps = mFrameCount * (NANO_PER_SECOND / (double) elapsed);
154 mFrameCount = 0;
155 mLastTime = nextTime;
156 }
157 }
158
159 public synchronized double checkFps() {
160 return mLastFps;
161 }
162
163 public synchronized void staggeredLog() {
164 if (mLastTime > mLastPrintTime + 5 * NANO_PER_SECOND) {
165 mLastPrintTime = mLastTime;
166 Log.d(TAG, "FPS for " + mStreamType + " stream: " + mLastFps );
167 }
168 }
169
170 public synchronized void countAndLog() {
171 countFrame();
172 staggeredLog();
173 }
174 }
175 /**
176 * Fake preview for jpeg captures when there is no active preview
177 */
178 private void createDummySurface() {
179 if (mDummyTexture == null || mDummySurface == null) {
180 mDummyTexture = new SurfaceTexture(/*ignored*/0);
181 // TODO: use smallest default sizes
182 mDummyTexture.setDefaultBufferSize(640, 480);
183 mDummySurface = new Surface(mDummyTexture);
184 }
185 }
186
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700187 private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
188 @Override
189 public void onError(int i, Camera camera) {
Ruben Brunkb44864e2015-06-12 14:39:12 -0700190 switch(i) {
191 case Camera.CAMERA_ERROR_EVICTED: {
192 flush();
193 mDeviceState.setError(
194 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED);
195 } break;
196 default: {
197 Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
198 mDeviceState.setError(
199 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
200 } break;
201 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700202 }
203 };
204
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700205 private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700206
207 private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
208 @Override
209 public void onPictureTaken(byte[] data, Camera camera) {
210 Log.i(TAG, "Received jpeg.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700211 Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced();
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700212 if (captureInfo == null || captureInfo.first == null) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700213 Log.e(TAG, "Dropping jpeg frame.");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700214 return;
215 }
Ruben Brunk2c3d7c52014-09-25 11:48:27 -0700216 RequestHolder holder = captureInfo.first;
217 long timestamp = captureInfo.second;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700218 for (Surface s : holder.getHolderTargets()) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700219 try {
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700220 if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700221 Log.i(TAG, "Producing jpeg buffer...");
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700222
223 int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
Ruben Brunk31798f32014-09-25 19:56:54 -0700224 totalSize = (totalSize + 3) & ~0x3; // round up to nearest octonibble
Ruben Brunk0c798842014-09-30 03:42:13 -0700225 LegacyCameraDevice.setNextTimestamp(s, timestamp);
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700226
Ruben Brunk31798f32014-09-25 19:56:54 -0700227 if (USE_BLOB_FORMAT_OVERRIDE) {
228 // Override to RGBA_8888 format.
229 LegacyCameraDevice.setSurfaceFormat(s,
230 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
Ruben Brunk0c798842014-09-30 03:42:13 -0700231
232 int dimen = (int) Math.ceil(Math.sqrt(totalSize));
233 dimen = (dimen + 0xf) & ~0xf; // round up to nearest multiple of 16
234 LegacyCameraDevice.setSurfaceDimens(s, dimen, dimen);
235 LegacyCameraDevice.produceFrame(s, data, dimen, dimen,
236 CameraMetadataNative.NATIVE_JPEG_FORMAT);
237 } else {
238 LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1);
239 LegacyCameraDevice.produceFrame(s, data, totalSize, /*height*/1,
240 CameraMetadataNative.NATIVE_JPEG_FORMAT);
Ruben Brunk31798f32014-09-25 19:56:54 -0700241 }
Ruben Brunkef14da32014-06-24 16:06:54 -0700242 }
243 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
244 Log.w(TAG, "Surface abandoned, dropping frame. ", e);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700245 }
246 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700247
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700248 mReceivedJpeg.open();
249 }
250 };
251
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700252 private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
253 @Override
254 public void onShutter() {
Ruben Brunk91838de2014-07-16 17:24:17 -0700255 mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos());
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700256 }
257 };
258
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700259 private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
260 new SurfaceTexture.OnFrameAvailableListener() {
261 @Override
262 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700263 if (DEBUG) {
264 mPrevCounter.countAndLog();
265 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700266 mGLThreadManager.queueNewFrame();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700267 }
268 };
269
270 private void stopPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700271 if (VERBOSE) {
272 Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
273 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700274 if (mPreviewRunning) {
275 mCamera.stopPreview();
276 mPreviewRunning = false;
277 }
278 }
279
280 private void startPreview() {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700281 if (VERBOSE) {
282 Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
283 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700284 if (!mPreviewRunning) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700285 // XX: CameraClient:;startPreview is not getting called after a stop
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700286 mCamera.startPreview();
287 mPreviewRunning = true;
288 }
289 }
290
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700291 private void doJpegCapturePrepare(RequestHolder request) throws IOException {
292 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
Igor Murashkina296fec2014-06-23 14:44:09 -0700293
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700294 if (!mPreviewRunning) {
Igor Murashkina296fec2014-06-23 14:44:09 -0700295 if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
296
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700297 createDummySurface();
298 mCamera.setPreviewTexture(mDummyTexture);
299 startPreview();
300 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700301 }
302
303 private void doJpegCapture(RequestHolder request) {
304 if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
305
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700306 mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700307 mPreviewRunning = false;
308 }
309
310 private void doPreviewCapture(RequestHolder request) throws IOException {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700311 if (VERBOSE) {
312 Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
313 }
314
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700315 if (mPreviewRunning) {
316 return; // Already running
317 }
318
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700319 if (mPreviewTexture == null) {
320 throw new IllegalStateException(
321 "Preview capture called with no preview surfaces configured.");
322 }
323
324 mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
325 mIntermediateBufferSize.getHeight());
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700326 mCamera.setPreviewTexture(mPreviewTexture);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700327
328 startPreview();
329 }
330
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800331 private void configureOutputs(Collection<Pair<Surface, Size>> outputs) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700332 if (DEBUG) {
333 String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700334 Log.d(TAG, "configureOutputs with " + outputsStr);
335 }
336
Ruben Brunke3c04342015-02-04 17:26:17 -0800337 try {
338 stopPreview();
339 } catch (RuntimeException e) {
340 Log.e(TAG, "Received device exception in configure call: ", e);
341 mDeviceState.setError(
342 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
343 return;
344 }
345
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700346 /*
347 * Try to release the previous preview's surface texture earlier if we end up
348 * using a different one; this also reduces the likelihood of getting into a deadlock
349 * when disconnecting from the old previous texture at a later time.
350 */
Ruben Brunke663cb772014-09-16 13:18:31 -0700351 try {
352 mCamera.setPreviewTexture(/*surfaceTexture*/null);
353 } catch (IOException e) {
354 Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
Ruben Brunke3c04342015-02-04 17:26:17 -0800355 } catch (RuntimeException e) {
356 Log.e(TAG, "Received device exception in configure call: ", e);
357 mDeviceState.setError(
358 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
359 return;
Ruben Brunke663cb772014-09-16 13:18:31 -0700360 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700361
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700362 if (mGLThreadManager != null) {
363 mGLThreadManager.waitUntilStarted();
364 mGLThreadManager.ignoreNewFrames();
365 mGLThreadManager.waitUntilIdle();
366 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700367 resetJpegSurfaceFormats(mCallbackOutputs);
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800368
369 for (Surface s : mCallbackOutputs) {
370 try {
371 LegacyCameraDevice.disconnectSurface(s);
372 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
373 Log.w(TAG, "Surface abandoned, skipping...", e);
374 }
375 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700376 mPreviewOutputs.clear();
377 mCallbackOutputs.clear();
Eino-Ville Talvala52571b92014-09-25 12:52:20 -0700378 mJpegSurfaceIds.clear();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700379 mPreviewTexture = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700380
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800381 List<Size> previewOutputSizes = new ArrayList<>();
382 List<Size> callbackOutputSizes = new ArrayList<>();
383
Ruben Brunk28c49c92014-06-16 18:43:59 -0700384 int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
385 int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700386 if (outputs != null) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800387 for (Pair<Surface, Size> outPair : outputs) {
388 Surface s = outPair.first;
389 Size outSize = outPair.second;
Ruben Brunkef14da32014-06-24 16:06:54 -0700390 try {
391 int format = LegacyCameraDevice.detectSurfaceType(s);
Ruben Brunk28c49c92014-06-16 18:43:59 -0700392 LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
Ruben Brunkef14da32014-06-24 16:06:54 -0700393 switch (format) {
394 case CameraMetadataNative.NATIVE_JPEG_FORMAT:
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700395 if (USE_BLOB_FORMAT_OVERRIDE) {
396 // Override to RGBA_8888 format.
397 LegacyCameraDevice.setSurfaceFormat(s,
398 LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
399 }
400 mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
Ruben Brunkef14da32014-06-24 16:06:54 -0700401 mCallbackOutputs.add(s);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800402 callbackOutputSizes.add(outSize);
Chien-Yu Chen29c36302016-03-02 15:34:00 -0800403
404 // LegacyCameraDevice is the producer of JPEG output surfaces
405 // so LegacyCameraDevice needs to connect to the surfaces.
406 LegacyCameraDevice.connectSurface(s);
Ruben Brunkef14da32014-06-24 16:06:54 -0700407 break;
408 default:
Ruben Brunka94c6032015-06-10 16:44:28 -0700409 LegacyCameraDevice.setScalingMode(s, LegacyCameraDevice.
410 NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
Ruben Brunkef14da32014-06-24 16:06:54 -0700411 mPreviewOutputs.add(s);
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800412 previewOutputSizes.add(outSize);
Ruben Brunkef14da32014-06-24 16:06:54 -0700413 break;
414 }
415 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
416 Log.w(TAG, "Surface abandoned, skipping...", e);
Igor Murashkin49b2b132014-06-18 19:03:00 -0700417 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700418 }
419 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700420 try {
421 mParams = mCamera.getParameters();
422 } catch (RuntimeException e) {
423 Log.e(TAG, "Received device exception: ", e);
424 mDeviceState.setError(
425 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
426 return;
427 }
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700428
429 List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
430 int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
431 if (DEBUG) {
432 Log.d(TAG, "doPreviewCapture - Selected range [" +
433 bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
434 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
435 }
436 mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
437 bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
Ruben Brunk7f2372b2014-07-02 11:05:08 -0700438
Ruben Brunkc64e80d2015-06-10 17:14:45 -0700439 Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs,
440 callbackOutputSizes, mParams);
441
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800442 if (previewOutputSizes.size() > 0) {
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700443
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800444 Size largestOutput = SizeAreaComparator.findLargestByArea(previewOutputSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700445
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700446 // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700447 Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700448
Ruben Brunkc64e80d2015-06-10 17:14:45 -0700449 Size chosenJpegDimen = (smallestSupportedJpegSize != null) ? smallestSupportedJpegSize
450 : largestJpegDimen;
451
Igor Murashkindf6242e2014-07-01 18:06:13 -0700452 List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
453 mParams.getSupportedPreviewSizes());
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700454
455 // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
456 // of the configured output dimensions. If none exists, fall back to using the largest
457 // supported preview size.
458 long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
Igor Murashkindf6242e2014-07-01 18:06:13 -0700459 Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700460 for (Size s : supportedPreviewSizes) {
461 long currArea = s.getWidth() * s.getHeight();
462 long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
Ruben Brunkc64e80d2015-06-10 17:14:45 -0700463 if (checkAspectRatiosMatch(chosenJpegDimen, s) && (currArea < bestArea &&
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700464 currArea >= largestOutputArea)) {
465 bestPreviewDimen = s;
466 }
467 }
468
469 mIntermediateBufferSize = bestPreviewDimen;
Ruben Brunk059d7662014-09-09 18:46:00 -0700470 mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
471 mIntermediateBufferSize.getHeight());
472
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700473 if (DEBUG) {
474 Log.d(TAG, "Intermediate buffer selected with dimens: " +
475 bestPreviewDimen.toString());
476 }
477 } else {
478 mIntermediateBufferSize = null;
479 if (DEBUG) {
480 Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
481 }
482 }
483
Igor Murashkina296fec2014-06-23 14:44:09 -0700484 if (smallestSupportedJpegSize != null) {
485 /*
486 * Set takePicture size to the smallest supported JPEG size large enough
487 * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
488 */
489
490 Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
491 mParams.setPictureSize(
492 smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
493 }
494
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700495 // TODO: Detect and optimize single-output paths here to skip stream teeing.
496 if (mGLThreadManager == null) {
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700497 mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700498 mGLThreadManager.start();
499 }
500 mGLThreadManager.waitUntilStarted();
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800501 List<Pair<Surface, Size>> previews = new ArrayList<>();
502 Iterator<Size> previewSizeIter = previewOutputSizes.iterator();
503 for (Surface p : mPreviewOutputs) {
504 previews.add(new Pair<>(p, previewSizeIter.next()));
505 }
506 mGLThreadManager.setConfigurationAndWait(previews, mCaptureCollector);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700507 mGLThreadManager.allowNewFrames();
508 mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700509 if (mPreviewTexture != null) {
510 mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
511 }
Igor Murashkin5096def2014-06-24 10:49:11 -0700512
Ruben Brunke3c04342015-02-04 17:26:17 -0800513 try {
514 mCamera.setParameters(mParams);
515 } catch (RuntimeException e) {
516 Log.e(TAG, "Received device exception while configuring: ", e);
517 mDeviceState.setError(
518 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
519
520 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700521 }
522
523 private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
524 if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
525 return;
526 }
527 for(Surface s : surfaces) {
Ruben Brunk443ab2c2015-03-12 20:54:03 -0700528 if (s == null || !s.isValid()) {
529 Log.w(TAG, "Jpeg surface is invalid, skipping...");
530 continue;
531 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700532 try {
533 LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
534 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
535 Log.w(TAG, "Surface abandoned, skipping...", e);
536 }
537 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700538 }
539
Igor Murashkina296fec2014-06-23 14:44:09 -0700540 /**
541 * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
542 * than all of the configured {@code JPEG} outputs (by both width and height).
543 *
544 * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
545 * still satisfies the above constraint.</p>
546 *
547 * <p>As a result, the returned size is guaranteed to be usable without needing
548 * to upscale any of the outputs. If only one {@code JPEG} surface is used,
549 * then no scaling/cropping is necessary between the taken picture and
550 * the {@code JPEG} output surface.</p>
551 *
552 * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
553 * @param params api1 parameters (used for reading only)
554 *
555 * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
556 * {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
557 * surfaces.
558 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800559 private Size calculatePictureSize( List<Surface> callbackOutputs,
560 List<Size> callbackSizes, Camera.Parameters params) {
Igor Murashkina296fec2014-06-23 14:44:09 -0700561 /*
562 * Find the largest JPEG size (if any), from the configured outputs:
563 * - the api1 picture size should be set to the smallest legal size that's at least as large
564 * as the largest configured JPEG size
565 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800566 if (callbackOutputs.size() != callbackSizes.size()) {
567 throw new IllegalStateException("Input collections must be same length");
568 }
569 List<Size> configuredJpegSizes = new ArrayList<>();
570 Iterator<Size> sizeIterator = callbackSizes.iterator();
Igor Murashkina296fec2014-06-23 14:44:09 -0700571 for (Surface callbackSurface : callbackOutputs) {
Ruben Brunkf4a637d2014-11-20 18:01:36 -0800572 Size jpegSize = sizeIterator.next();
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700573 if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
Ruben Brunkef14da32014-06-24 16:06:54 -0700574 continue; // Ignore non-JPEG callback formats
575 }
576
Ruben Brunkef14da32014-06-24 16:06:54 -0700577 configuredJpegSizes.add(jpegSize);
Igor Murashkina296fec2014-06-23 14:44:09 -0700578 }
579 if (!configuredJpegSizes.isEmpty()) {
580 /*
581 * Find the largest configured JPEG width, and height, independently
582 * of the rest.
583 *
584 * The rest of the JPEG streams can be cropped out of this smallest bounding
585 * rectangle.
586 */
587 int maxConfiguredJpegWidth = -1;
588 int maxConfiguredJpegHeight = -1;
589 for (Size jpegSize : configuredJpegSizes) {
590 maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
591 jpegSize.getWidth() : maxConfiguredJpegWidth;
592 maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
593 jpegSize.getHeight() : maxConfiguredJpegHeight;
594 }
595 Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
596
Igor Murashkindf6242e2014-07-01 18:06:13 -0700597 List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
598 params.getSupportedPictureSizes());
Igor Murashkina296fec2014-06-23 14:44:09 -0700599
600 /*
601 * Find the smallest supported JPEG size that can fit the smallest bounding
602 * rectangle for the configured JPEG sizes.
603 */
604 List<Size> candidateSupportedJpegSizes = new ArrayList<>();
605 for (Size supportedJpegSize : supportedJpegSizes) {
606 if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
607 supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
608 candidateSupportedJpegSizes.add(supportedJpegSize);
609 }
610 }
611
612 if (candidateSupportedJpegSizes.isEmpty()) {
613 throw new AssertionError(
614 "Could not find any supported JPEG sizes large enough to fit " +
615 smallestBoundJpegSize);
616 }
617
618 Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
619 new SizeAreaComparator());
620
621 if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
622 Log.w(TAG,
623 String.format(
624 "configureOutputs - Will need to crop picture %s into "
625 + "smallest bound size %s",
626 smallestSupportedJpegSize, smallestBoundJpegSize));
627 }
628
629 return smallestSupportedJpegSize;
630 }
631
632 return null;
633 }
634
Ruben Brunkd85e1a62014-06-11 10:35:45 -0700635 private static boolean checkAspectRatiosMatch(Size a, Size b) {
636 float aAspect = a.getWidth() / (float) a.getHeight();
637 float bAspect = b.getWidth() / (float) b.getHeight();
638
639 return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
640 }
641
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700642 // Calculate the highest FPS range supported
643 private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
644 if (frameRates.size() == 0) {
645 Log.e(TAG, "No supported frame rates returned!");
646 return null;
647 }
648
649 int bestMin = 0;
650 int bestMax = 0;
651 int bestIndex = 0;
652 int index = 0;
653 for (int[] rate : frameRates) {
654 int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
655 int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
656 if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
657 bestMin = minFps;
658 bestMax = maxFps;
659 bestIndex = index;
660 }
661 index++;
662 }
663
664 return frameRates.get(bestIndex);
665 }
666
667 private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
668 private boolean mCleanup = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700669 private final LegacyResultMapper mMapper = new LegacyResultMapper();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700670
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700671 @Override
672 public boolean handleMessage(Message msg) {
673 if (mCleanup) {
674 return true;
675 }
676
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700677 if (DEBUG) {
678 Log.d(TAG, "Request thread handling message:" + msg.what);
679 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700680 long startTime = 0;
681 if (DEBUG) {
682 startTime = SystemClock.elapsedRealtimeNanos();
683 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700684 switch (msg.what) {
685 case MSG_CONFIGURE_OUTPUTS:
686 ConfigureHolder config = (ConfigureHolder) msg.obj;
Igor Murashkin49b2b132014-06-18 19:03:00 -0700687 int sizes = config.surfaces != null ? config.surfaces.size() : 0;
Ruben Brunke663cb772014-09-16 13:18:31 -0700688 Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
Ruben Brunk91838de2014-07-16 17:24:17 -0700689
690 try {
691 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
692 TimeUnit.MILLISECONDS);
693 if (!success) {
694 Log.e(TAG, "Timed out while queueing configure request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700695 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700696 }
697 } catch (InterruptedException e) {
Ruben Brunk91838de2014-07-16 17:24:17 -0700698 Log.e(TAG, "Interrupted while waiting for requests to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700699 mDeviceState.setError(
700 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
701 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700702 }
703
Ruben Brunke663cb772014-09-16 13:18:31 -0700704 configureOutputs(config.surfaces);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700705 config.condition.open();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700706 if (DEBUG) {
707 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
708 Log.d(TAG, "Configure took " + totalTime + " ns");
709 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700710 break;
711 case MSG_SUBMIT_CAPTURE_REQUEST:
712 Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
713
714 // Get the next burst from the request queue.
715 Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
Ruben Brunke663cb772014-09-16 13:18:31 -0700716
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700717 if (nextBurst == null) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700718 // If there are no further requests queued, wait for any currently executing
719 // requests to complete, then switch to idle state.
Ruben Brunk91838de2014-07-16 17:24:17 -0700720 try {
721 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
722 TimeUnit.MILLISECONDS);
723 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700724 Log.e(TAG,
725 "Timed out while waiting for prior requests to complete.");
726 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700727 }
728 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700729 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
730 mDeviceState.setError(
731 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
732 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700733 }
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700734
735 synchronized (mIdleLock) {
736 // Retry the the request queue.
737 nextBurst = mRequestQueue.getNext();
738
739 // If we still have no queued requests, go idle.
740 if (nextBurst == null) {
741 mDeviceState.setIdle();
742 break;
743 }
744 }
745 }
746
747 if (nextBurst != null) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700748 // Queue another capture if we did not get the last burst.
749 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
750 }
751
752 // Complete each request in the burst
753 List<RequestHolder> requests =
754 nextBurst.first.produceRequestHolders(nextBurst.second);
755 for (RequestHolder holder : requests) {
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700756 CaptureRequest request = holder.getRequest();
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700757
758 boolean paramsChanged = false;
Igor Murashkin83d86392014-07-18 14:37:19 -0700759
Ruben Brunke663cb772014-09-16 13:18:31 -0700760 // Only update parameters if the request has changed
Igor Murashkindf6242e2014-07-01 18:06:13 -0700761 if (mLastRequest == null || mLastRequest.captureRequest != request) {
762
763 // The intermediate buffer is sometimes null, but we always need
Ruben Brunke663cb772014-09-16 13:18:31 -0700764 // the Camera1 API configured preview size
Igor Murashkindf6242e2014-07-01 18:06:13 -0700765 Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
766
Ruben Brunke663cb772014-09-16 13:18:31 -0700767 LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
768 request, previewSize, mParams); // params are copied
Igor Murashkindf6242e2014-07-01 18:06:13 -0700769
Ruben Brunke663cb772014-09-16 13:18:31 -0700770
Igor Murashkindf6242e2014-07-01 18:06:13 -0700771 // Parameters are mutated as a side-effect
772 LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
773
Ruben Brunke663cb772014-09-16 13:18:31 -0700774 // If the parameters have changed, set them in the Camera1 API.
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700775 if (!mParams.same(legacyRequest.parameters)) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700776 try {
777 mCamera.setParameters(legacyRequest.parameters);
778 } catch (RuntimeException e) {
779 // If setting the parameters failed, report a request error to
780 // the camera client, and skip any further work for this request
781 Log.e(TAG, "Exception while setting camera parameters: ", e);
782 holder.failRequest();
783 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
784 CameraDeviceImpl.CameraDeviceCallbacks.
785 ERROR_CAMERA_REQUEST);
786 continue;
787 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700788 paramsChanged = true;
Ruben Brunke663cb772014-09-16 13:18:31 -0700789 mParams = legacyRequest.parameters;
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700790 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700791
792 mLastRequest = legacyRequest;
Ruben Brunk5776aaf2014-06-19 13:42:40 -0700793 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700794
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700795 try {
Ruben Brunk91838de2014-07-16 17:24:17 -0700796 boolean success = mCaptureCollector.queueRequest(holder,
797 mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
798
799 if (!success) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700800 // Report a request error if we timed out while queuing this.
Ruben Brunk91838de2014-07-16 17:24:17 -0700801 Log.e(TAG, "Timed out while queueing capture request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700802 holder.failRequest();
803 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
804 CameraDeviceImpl.CameraDeviceCallbacks.
805 ERROR_CAMERA_REQUEST);
806 continue;
Ruben Brunk91838de2014-07-16 17:24:17 -0700807 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700808
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700809 // Starting the preview needs to happen before enabling
810 // face detection or auto focus
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700811 if (holder.hasPreviewTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700812 doPreviewCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700813 }
814 if (holder.hasJpegTargets()) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700815 while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
816 TimeUnit.MILLISECONDS)) {
817 // Fail preview requests until the queue is empty.
818 Log.e(TAG, "Timed out while waiting for preview requests to " +
819 "complete.");
820 mCaptureCollector.failNextPreview();
Ruben Brunk91838de2014-07-16 17:24:17 -0700821 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700822 mReceivedJpeg.close();
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700823 doJpegCapturePrepare(holder);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700824 }
825
826 /*
827 * Do all the actions that require a preview to have been started
828 */
829
830 // Toggle face detection on/off
831 // - do this before AF to give AF a chance to use faces
832 mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
833
834 // Unconditionally process AF triggers, since they're non-idempotent
835 // - must be done after setting the most-up-to-date AF mode
836 mFocusStateMapper.processRequestTriggers(request, mParams);
837
838 if (holder.hasJpegTargets()) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700839 doJpegCapture(holder);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700840 if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700841 Log.e(TAG, "Hit timeout for jpeg callback!");
Ruben Brunke663cb772014-09-16 13:18:31 -0700842 mCaptureCollector.failNextJpeg();
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700843 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700844 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700845
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700846 } catch (IOException e) {
Ruben Brunke3c04342015-02-04 17:26:17 -0800847 Log.e(TAG, "Received device exception during capture call: ", e);
Ruben Brunke663cb772014-09-16 13:18:31 -0700848 mDeviceState.setError(
849 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
850 break;
Ruben Brunk91838de2014-07-16 17:24:17 -0700851 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700852 Log.e(TAG, "Interrupted during capture: ", e);
853 mDeviceState.setError(
854 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
855 break;
Ruben Brunke3c04342015-02-04 17:26:17 -0800856 } catch (RuntimeException e) {
857 Log.e(TAG, "Received device exception during capture call: ", e);
858 mDeviceState.setError(
859 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
860 break;
Ruben Brunk91b9aab2014-06-20 00:24:56 -0700861 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700862
863 if (paramsChanged) {
864 if (DEBUG) {
865 Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
866 }
Eino-Ville Talvalaeecc9042014-09-23 16:37:31 -0700867 try {
868 mParams = mCamera.getParameters();
869 } catch (RuntimeException e) {
870 Log.e(TAG, "Received device exception: ", e);
871 mDeviceState.setError(
872 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
873 break;
874 }
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700875
876 // Update parameters to the latest that we think the camera is using
877 mLastRequest.setParameters(mParams);
878 }
Igor Murashkin83d86392014-07-18 14:37:19 -0700879
880 MutableLong timestampMutable = new MutableLong(/*value*/0L);
881 try {
882 boolean success = mCaptureCollector.waitForRequestCompleted(holder,
883 REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
884 /*out*/timestampMutable);
885
886 if (!success) {
887 Log.e(TAG, "Timed out while waiting for request to complete.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700888 mCaptureCollector.failAll();
Igor Murashkin83d86392014-07-18 14:37:19 -0700889 }
890 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700891 Log.e(TAG, "Interrupted waiting for request completion: ", e);
892 mDeviceState.setError(
893 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
894 break;
Igor Murashkin83d86392014-07-18 14:37:19 -0700895 }
896
897 CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
Igor Murashkin56678d82014-07-28 10:50:08 -0700898 mLastRequest, timestampMutable.value);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700899 /*
900 * Order matters: The default result mapper is state-less; the
901 * other mappers carry state and may override keys set by the default
902 * mapper with their own values.
903 */
904
Igor Murashkin83d86392014-07-18 14:37:19 -0700905 // Update AF state
906 mFocusStateMapper.mapResultTriggers(result);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700907 // Update face-related results
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700908 mFaceDetectMapper.mapResultFaces(result, mLastRequest);
Igor Murashkin83d86392014-07-18 14:37:19 -0700909
Ruben Brunke663cb772014-09-16 13:18:31 -0700910 if (!holder.requestFailed()) {
911 mDeviceState.setCaptureResult(holder, result,
912 CameraDeviceState.NO_CAPTURE_ERROR);
913 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700914 }
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700915 if (DEBUG) {
Ruben Brunkd1f113d2014-07-11 11:46:20 -0700916 long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
917 Log.d(TAG, "Capture request took " + totalTime + " ns");
Ruben Brunk3e4fed22014-06-18 17:08:42 -0700918 mRequestCounter.countAndLog();
919 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700920 break;
921 case MSG_CLEANUP:
922 mCleanup = true;
Ruben Brunk91838de2014-07-16 17:24:17 -0700923 try {
924 boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
925 TimeUnit.MILLISECONDS);
926 if (!success) {
927 Log.e(TAG, "Timed out while queueing cleanup request.");
Ruben Brunke663cb772014-09-16 13:18:31 -0700928 mCaptureCollector.failAll();
Ruben Brunk91838de2014-07-16 17:24:17 -0700929 }
930 } catch (InterruptedException e) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700931 Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
932 mDeviceState.setError(
933 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
Ruben Brunk91838de2014-07-16 17:24:17 -0700934 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700935 if (mGLThreadManager != null) {
936 mGLThreadManager.quit();
Ruben Brunke3c04342015-02-04 17:26:17 -0800937 mGLThreadManager = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700938 }
939 if (mCamera != null) {
940 mCamera.release();
Ruben Brunke3c04342015-02-04 17:26:17 -0800941 mCamera = null;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700942 }
Ruben Brunk0fd198a2014-09-23 23:35:43 -0700943 resetJpegSurfaceFormats(mCallbackOutputs);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700944 break;
Igor Murashkin51dcfd652014-09-25 16:55:01 -0700945 case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
946 // OK: Ignore message.
947 break;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700948 default:
949 throw new AssertionError("Unhandled message " + msg.what +
950 " on RequestThread.");
951 }
952 return true;
953 }
954 };
955
956 /**
957 * Create a new RequestThreadManager.
958 *
959 * @param cameraId the id of the camera to use.
960 * @param camera an open camera object. The RequestThreadManager takes ownership of this camera
961 * object, and is responsible for closing it.
Igor Murashkindf6242e2014-07-01 18:06:13 -0700962 * @param characteristics the static camera characteristics corresponding to this camera device
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700963 * @param deviceState a {@link CameraDeviceState} state machine.
964 */
Igor Murashkindf6242e2014-07-01 18:06:13 -0700965 public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700966 CameraDeviceState deviceState) {
Igor Murashkindf6242e2014-07-01 18:06:13 -0700967 mCamera = checkNotNull(camera, "camera must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700968 mCameraId = cameraId;
Igor Murashkindf6242e2014-07-01 18:06:13 -0700969 mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700970 String name = String.format("RequestThread-%d", cameraId);
971 TAG = name;
Igor Murashkindf6242e2014-07-01 18:06:13 -0700972 mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
Igor Murashkin83d86392014-07-18 14:37:19 -0700973 mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700974 mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
Ruben Brunk91838de2014-07-16 17:24:17 -0700975 mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700976 mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
Ruben Brunk4aed87a2014-09-21 18:35:31 -0700977 mCamera.setErrorCallback(mErrorCallback);
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700978 }
979
980 /**
981 * Start the request thread.
982 */
983 public void start() {
984 mRequestThread.start();
985 }
986
987 /**
Ruben Brunke663cb772014-09-16 13:18:31 -0700988 * Flush any pending requests.
989 *
990 * @return the last frame number.
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700991 */
Ruben Brunke663cb772014-09-16 13:18:31 -0700992 public long flush() {
993 Log.i(TAG, "Flushing all pending requests.");
994 long lastFrame = mRequestQueue.stopRepeating();
995 mCaptureCollector.failAll();
996 return lastFrame;
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700997 }
998
999 /**
1000 * Quit the request thread, and clean up everything.
1001 */
1002 public void quit() {
Ruben Brunke3c04342015-02-04 17:26:17 -08001003 if (!mQuit.getAndSet(true)) { // Avoid sending messages on dead thread's handler.
1004 Handler handler = mRequestThread.waitAndGetHandler();
1005 handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
1006 mRequestThread.quitSafely();
1007 try {
1008 mRequestThread.join();
1009 } catch (InterruptedException e) {
1010 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
1011 mRequestThread.getName(), mRequestThread.getId()));
1012 }
Ruben Brunkd85e1a62014-06-11 10:35:45 -07001013 }
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001014 }
1015
1016 /**
1017 * Submit the given burst of requests to be captured.
1018 *
1019 * <p>If the burst is repeating, replace the current repeating burst.</p>
1020 *
1021 * @param requests the burst of requests to add to the queue.
1022 * @param repeating true if the burst is repeating.
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001023 * @return the submission info, including the new request id, and the last frame number, which
1024 * contains either the frame number of the last frame that will be returned for this request,
1025 * or the frame number of the last frame that will be returned for the current repeating
1026 * request if this burst is set to be repeating.
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001027 */
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001028 public SubmitInfo submitCaptureRequests(CaptureRequest[] requests, boolean repeating) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001029 Handler handler = mRequestThread.waitAndGetHandler();
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001030 SubmitInfo info;
Ruben Brunk4aed87a2014-09-21 18:35:31 -07001031 synchronized (mIdleLock) {
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001032 info = mRequestQueue.submit(requests, repeating);
Ruben Brunk4aed87a2014-09-21 18:35:31 -07001033 handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
1034 }
Eino-Ville Talvala5d2d7782015-12-17 16:50:50 -08001035 return info;
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001036 }
1037
1038 /**
1039 * Cancel a repeating request.
1040 *
1041 * @param requestId the id of the repeating request to cancel.
1042 * @return the last frame to be returned from the HAL for the given repeating request, or
1043 * {@code INVALID_FRAME} if none exists.
1044 */
1045 public long cancelRepeating(int requestId) {
1046 return mRequestQueue.stopRepeating(requestId);
1047 }
1048
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001049 /**
Igor Murashkin49b2b132014-06-18 19:03:00 -07001050 * Configure with the current list of output Surfaces.
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001051 *
1052 * <p>
1053 * This operation blocks until the configuration is complete.
1054 * </p>
1055 *
Igor Murashkin49b2b132014-06-18 19:03:00 -07001056 * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
1057 *
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001058 * @param outputs a {@link java.util.Collection} of outputs to configure.
1059 */
Ruben Brunkf4a637d2014-11-20 18:01:36 -08001060 public void configure(Collection<Pair<Surface, Size>> outputs) {
Ruben Brunkfeb50af2014-05-09 19:58:49 -07001061 Handler handler = mRequestThread.waitAndGetHandler();
1062 final ConditionVariable condition = new ConditionVariable(/*closed*/false);
1063 ConfigureHolder holder = new ConfigureHolder(condition, outputs);
1064 handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
1065 condition.block();
1066 }
1067}