1 /*
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 
17 package android.hardware.camera2.legacy;
18 
19 import android.graphics.SurfaceTexture;
20 import android.hardware.Camera;
21 import android.hardware.camera2.CameraCharacteristics;
22 import android.hardware.camera2.CaptureRequest;
23 import android.hardware.camera2.impl.CameraDeviceImpl;
24 import android.hardware.camera2.utils.SubmitInfo;
25 import android.hardware.camera2.utils.SizeAreaComparator;
26 import android.hardware.camera2.impl.CameraMetadataNative;
27 import android.os.ConditionVariable;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.os.SystemClock;
31 import android.util.Log;
32 import android.util.MutableLong;
33 import android.util.Pair;
34 import android.util.Size;
35 import android.view.Surface;
36 
37 import java.io.IOException;
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.Collections;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.concurrent.TimeUnit;
44 import java.util.concurrent.atomic.AtomicBoolean;
45 
46 import static com.android.internal.util.Preconditions.*;
47 
48 /**
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  */
59 @SuppressWarnings("deprecation")
60 public class RequestThreadManager {
61     private final String TAG;
62     private final int mCameraId;
63     private final RequestHandlerThread mRequestThread;
64 
65     private static final boolean DEBUG = false;
66     // For slightly more spammy messages that will get repeated every frame
67     private static final boolean VERBOSE = false;
68     private Camera mCamera;
69     private final CameraCharacteristics mCharacteristics;
70 
71     private final CameraDeviceState mDeviceState;
72     private final CaptureCollector mCaptureCollector;
73     private final LegacyFocusStateMapper mFocusStateMapper;
74     private final LegacyFaceDetectMapper mFaceDetectMapper;
75 
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 
80     private static final int MAX_IN_FLIGHT_REQUESTS = 2;
81 
82     private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms
83     private static final int JPEG_FRAME_TIMEOUT = 4000; // ms (same as CTS for API2)
84     private static final int REQUEST_COMPLETE_TIMEOUT = JPEG_FRAME_TIMEOUT;
85 
86     private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
87     private boolean mPreviewRunning = false;
88 
89     private final List<Surface> mPreviewOutputs = new ArrayList<>();
90     private final List<Surface> mCallbackOutputs = new ArrayList<>();
91     private GLThreadManager mGLThreadManager;
92     private SurfaceTexture mPreviewTexture;
93     private Camera.Parameters mParams;
94 
95     private final List<Long> mJpegSurfaceIds = new ArrayList<>();
96 
97     private Size mIntermediateBufferSize;
98 
99     private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds);
100     private LegacyRequest mLastRequest = null;
101     private SurfaceTexture mDummyTexture;
102     private Surface mDummySurface;
103 
104     private final Object mIdleLock = new Object();
105     private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
106     private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
107 
108     private final AtomicBoolean mQuit = new AtomicBoolean(false);
109 
110     // 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 
114     /**
115      * Container object for Configure messages.
116      */
117     private static class ConfigureHolder {
118         public final ConditionVariable condition;
119         public final Collection<Pair<Surface, Size>> surfaces;
120 
ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface, Size>> surfaces)121         public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface,
122                 Size>> surfaces) {
123             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 
FpsCounter(String streamType)141         public FpsCounter(String streamType) {
142             mStreamType = streamType;
143         }
144 
countFrame()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 
checkFps()159         public synchronized double checkFps() {
160             return mLastFps;
161         }
162 
staggeredLog()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 
countAndLog()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      */
createDummySurface()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 
187     private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
188         @Override
189         public void onError(int i, Camera camera) {
190             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             }
202         }
203     };
204 
205     private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
206 
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.");
211             Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced();
212             if (captureInfo == null || captureInfo.first == null) {
213                 Log.e(TAG, "Dropping jpeg frame.");
214                 return;
215             }
216             RequestHolder holder = captureInfo.first;
217             long timestamp = captureInfo.second;
218             for (Surface s : holder.getHolderTargets()) {
219                 try {
220                     if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
221                         Log.i(TAG, "Producing jpeg buffer...");
222 
223                         int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
224                         totalSize = (totalSize + 3) & ~0x3; // round up to nearest octonibble
225                         LegacyCameraDevice.setNextTimestamp(s, timestamp);
226 
227                         if (USE_BLOB_FORMAT_OVERRIDE) {
228                             // Override to RGBA_8888 format.
229                             LegacyCameraDevice.setSurfaceFormat(s,
230                                     LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
231 
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);
241                         }
242                     }
243                 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
244                     Log.w(TAG, "Surface abandoned, dropping frame. ", e);
245                 }
246             }
247 
248             mReceivedJpeg.open();
249         }
250     };
251 
252     private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
253         @Override
254         public void onShutter() {
255             mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos());
256         }
257     };
258 
259     private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
260             new SurfaceTexture.OnFrameAvailableListener() {
261                 @Override
262                 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
263                     if (DEBUG) {
264                         mPrevCounter.countAndLog();
265                     }
266                     mGLThreadManager.queueNewFrame();
267                 }
268             };
269 
stopPreview()270     private void stopPreview() {
271         if (VERBOSE) {
272             Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
273         }
274         if (mPreviewRunning) {
275             mCamera.stopPreview();
276             mPreviewRunning = false;
277         }
278     }
279 
startPreview()280     private void startPreview() {
281         if (VERBOSE) {
282             Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
283         }
284         if (!mPreviewRunning) {
285             // XX: CameraClient:;startPreview is not getting called after a stop
286             mCamera.startPreview();
287             mPreviewRunning = true;
288         }
289     }
290 
doJpegCapturePrepare(RequestHolder request)291     private void doJpegCapturePrepare(RequestHolder request) throws IOException {
292         if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
293 
294         if (!mPreviewRunning) {
295             if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
296 
297             createDummySurface();
298             mCamera.setPreviewTexture(mDummyTexture);
299             startPreview();
300         }
301     }
302 
doJpegCapture(RequestHolder request)303     private void doJpegCapture(RequestHolder request) {
304         if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
305 
306         mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
307         mPreviewRunning = false;
308     }
309 
doPreviewCapture(RequestHolder request)310     private void doPreviewCapture(RequestHolder request) throws IOException {
311         if (VERBOSE) {
312             Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
313         }
314 
315         if (mPreviewRunning) {
316             return; // Already running
317         }
318 
319         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());
326         mCamera.setPreviewTexture(mPreviewTexture);
327 
328         startPreview();
329     }
330 
configureOutputs(Collection<Pair<Surface, Size>> outputs)331     private void configureOutputs(Collection<Pair<Surface, Size>> outputs) {
332         if (DEBUG) {
333             String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
334             Log.d(TAG, "configureOutputs with " + outputsStr);
335         }
336 
337         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 
346         /*
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          */
351         try {
352             mCamera.setPreviewTexture(/*surfaceTexture*/null);
353         } catch (IOException e) {
354             Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
355         } 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;
360         }
361 
362         if (mGLThreadManager != null) {
363             mGLThreadManager.waitUntilStarted();
364             mGLThreadManager.ignoreNewFrames();
365             mGLThreadManager.waitUntilIdle();
366         }
367         resetJpegSurfaceFormats(mCallbackOutputs);
368 
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         }
376         mPreviewOutputs.clear();
377         mCallbackOutputs.clear();
378         mJpegSurfaceIds.clear();
379         mPreviewTexture = null;
380 
381         List<Size> previewOutputSizes = new ArrayList<>();
382         List<Size> callbackOutputSizes = new ArrayList<>();
383 
384         int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
385         int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
386         if (outputs != null) {
387             for (Pair<Surface, Size> outPair : outputs) {
388                 Surface s = outPair.first;
389                 Size outSize = outPair.second;
390                 try {
391                     int format = LegacyCameraDevice.detectSurfaceType(s);
392                     LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
393                     switch (format) {
394                         case CameraMetadataNative.NATIVE_JPEG_FORMAT:
395                             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));
401                             mCallbackOutputs.add(s);
402                             callbackOutputSizes.add(outSize);
403 
404                             // LegacyCameraDevice is the producer of JPEG output surfaces
405                             // so LegacyCameraDevice needs to connect to the surfaces.
406                             LegacyCameraDevice.connectSurface(s);
407                             break;
408                         default:
409                             LegacyCameraDevice.setScalingMode(s, LegacyCameraDevice.
410                                     NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
411                             mPreviewOutputs.add(s);
412                             previewOutputSizes.add(outSize);
413                             break;
414                     }
415                 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
416                     Log.w(TAG, "Surface abandoned, skipping...", e);
417                 }
418             }
419         }
420         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         }
428 
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]);
438 
439         Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs,
440                 callbackOutputSizes, mParams);
441 
442         if (previewOutputSizes.size() > 0) {
443 
444             Size largestOutput = SizeAreaComparator.findLargestByArea(previewOutputSizes);
445 
446             // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
447             Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
448 
449             Size chosenJpegDimen = (smallestSupportedJpegSize != null) ? smallestSupportedJpegSize
450                     : largestJpegDimen;
451 
452             List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
453                     mParams.getSupportedPreviewSizes());
454 
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();
459             Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
460             for (Size s : supportedPreviewSizes) {
461                 long currArea = s.getWidth() * s.getHeight();
462                 long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
463                 if (checkAspectRatiosMatch(chosenJpegDimen, s) && (currArea < bestArea &&
464                         currArea >= largestOutputArea)) {
465                     bestPreviewDimen = s;
466                 }
467             }
468 
469             mIntermediateBufferSize = bestPreviewDimen;
470             mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
471                     mIntermediateBufferSize.getHeight());
472 
473             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 
484         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 
495         // TODO: Detect and optimize single-output paths here to skip stream teeing.
496         if (mGLThreadManager == null) {
497             mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
498             mGLThreadManager.start();
499         }
500         mGLThreadManager.waitUntilStarted();
501         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);
507         mGLThreadManager.allowNewFrames();
508         mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
509         if (mPreviewTexture != null) {
510             mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
511         }
512 
513         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         }
521     }
522 
resetJpegSurfaceFormats(Collection<Surface> surfaces)523     private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
524         if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
525             return;
526         }
527         for(Surface s : surfaces) {
528             if (s == null || !s.isValid()) {
529                 Log.w(TAG, "Jpeg surface is invalid, skipping...");
530                 continue;
531             }
532             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         }
538     }
539 
540     /**
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      */
calculatePictureSize( List<Surface> callbackOutputs, List<Size> callbackSizes, Camera.Parameters params)559     private Size calculatePictureSize( List<Surface> callbackOutputs,
560                                        List<Size> callbackSizes, Camera.Parameters params) {
561         /*
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          */
566         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();
571         for (Surface callbackSurface : callbackOutputs) {
572             Size jpegSize = sizeIterator.next();
573                 if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
574                     continue; // Ignore non-JPEG callback formats
575                 }
576 
577                 configuredJpegSizes.add(jpegSize);
578         }
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 
597             List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
598                     params.getSupportedPictureSizes());
599 
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 
checkAspectRatiosMatch(Size a, Size b)635     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 
642     // Calculate the highest FPS range supported
getPhotoPreviewFpsRange(List<int[]> frameRates)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;
669         private final LegacyResultMapper mMapper = new LegacyResultMapper();
670 
671         @Override
672         public boolean handleMessage(Message msg) {
673             if (mCleanup) {
674                 return true;
675             }
676 
677             if (DEBUG) {
678                 Log.d(TAG, "Request thread handling message:" + msg.what);
679             }
680             long startTime = 0;
681             if (DEBUG) {
682                 startTime = SystemClock.elapsedRealtimeNanos();
683             }
684             switch (msg.what) {
685                 case MSG_CONFIGURE_OUTPUTS:
686                     ConfigureHolder config = (ConfigureHolder) msg.obj;
687                     int sizes = config.surfaces != null ? config.surfaces.size() : 0;
688                     Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
689 
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.");
695                             mCaptureCollector.failAll();
696                         }
697                     } catch (InterruptedException e) {
698                         Log.e(TAG, "Interrupted while waiting for requests to complete.");
699                         mDeviceState.setError(
700                                 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
701                         break;
702                     }
703 
704                     configureOutputs(config.surfaces);
705                     config.condition.open();
706                     if (DEBUG) {
707                         long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
708                         Log.d(TAG, "Configure took " + totalTime + " ns");
709                     }
710                     break;
711                 case MSG_SUBMIT_CAPTURE_REQUEST:
712                     Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
713                     boolean anyRequestOutputAbandoned = false;
714 
715                     // Get the next burst from the request queue.
716                     Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
717 
718                     if (nextBurst == null) {
719                         // If there are no further requests queued, wait for any currently executing
720                         // requests to complete, then switch to idle state.
721                         try {
722                             boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
723                                     TimeUnit.MILLISECONDS);
724                             if (!success) {
725                                 Log.e(TAG,
726                                         "Timed out while waiting for prior requests to complete.");
727                                 mCaptureCollector.failAll();
728                             }
729                         } catch (InterruptedException e) {
730                             Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
731                             mDeviceState.setError(
732                                     CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
733                             break;
734                         }
735 
736                         synchronized (mIdleLock) {
737                             // Retry the the request queue.
738                             nextBurst = mRequestQueue.getNext();
739 
740                             // If we still have no queued requests, go idle.
741                             if (nextBurst == null) {
742                                 mDeviceState.setIdle();
743                                 break;
744                             }
745                         }
746                     }
747 
748                     if (nextBurst != null) {
749                         // Queue another capture if we did not get the last burst.
750                         handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
751                     }
752 
753                     // Complete each request in the burst
754                     List<RequestHolder> requests =
755                             nextBurst.first.produceRequestHolders(nextBurst.second);
756                     for (RequestHolder holder : requests) {
757                         CaptureRequest request = holder.getRequest();
758 
759                         boolean paramsChanged = false;
760 
761                         // Only update parameters if the request has changed
762                         if (mLastRequest == null || mLastRequest.captureRequest != request) {
763 
764                             // The intermediate buffer is sometimes null, but we always need
765                             // the Camera1 API configured preview size
766                             Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
767 
768                             LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
769                                     request, previewSize, mParams); // params are copied
770 
771 
772                             // Parameters are mutated as a side-effect
773                             LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
774 
775                             // If the parameters have changed, set them in the Camera1 API.
776                             if (!mParams.same(legacyRequest.parameters)) {
777                                 try {
778                                     mCamera.setParameters(legacyRequest.parameters);
779                                 } catch (RuntimeException e) {
780                                     // If setting the parameters failed, report a request error to
781                                     // the camera client, and skip any further work for this request
782                                     Log.e(TAG, "Exception while setting camera parameters: ", e);
783                                     holder.failRequest();
784                                     mDeviceState.setCaptureStart(holder, /*timestamp*/0,
785                                             CameraDeviceImpl.CameraDeviceCallbacks.
786                                                     ERROR_CAMERA_REQUEST);
787                                     continue;
788                                 }
789                                 paramsChanged = true;
790                                 mParams = legacyRequest.parameters;
791                             }
792 
793                             mLastRequest = legacyRequest;
794                         }
795 
796                         try {
797                             boolean success = mCaptureCollector.queueRequest(holder,
798                                     mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
799 
800                             if (!success) {
801                                 // Report a request error if we timed out while queuing this.
802                                 Log.e(TAG, "Timed out while queueing capture request.");
803                                 holder.failRequest();
804                                 mDeviceState.setCaptureStart(holder, /*timestamp*/0,
805                                         CameraDeviceImpl.CameraDeviceCallbacks.
806                                                 ERROR_CAMERA_REQUEST);
807                                 continue;
808                             }
809 
810                             // Starting the preview needs to happen before enabling
811                             // face detection or auto focus
812                             if (holder.hasPreviewTargets()) {
813                                 doPreviewCapture(holder);
814                             }
815                             if (holder.hasJpegTargets()) {
816                                 while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
817                                         TimeUnit.MILLISECONDS)) {
818                                     // Fail preview requests until the queue is empty.
819                                     Log.e(TAG, "Timed out while waiting for preview requests to " +
820                                             "complete.");
821                                     mCaptureCollector.failNextPreview();
822                                 }
823                                 mReceivedJpeg.close();
824                                 doJpegCapturePrepare(holder);
825                             }
826 
827                             /*
828                              * Do all the actions that require a preview to have been started
829                              */
830 
831                             // Toggle face detection on/off
832                             // - do this before AF to give AF a chance to use faces
833                             mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
834 
835                             // Unconditionally process AF triggers, since they're non-idempotent
836                             // - must be done after setting the most-up-to-date AF mode
837                             mFocusStateMapper.processRequestTriggers(request, mParams);
838 
839                             if (holder.hasJpegTargets()) {
840                                 doJpegCapture(holder);
841                                 if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
842                                     Log.e(TAG, "Hit timeout for jpeg callback!");
843                                     mCaptureCollector.failNextJpeg();
844                                 }
845                             }
846 
847                         } catch (IOException e) {
848                             Log.e(TAG, "Received device exception during capture call: ", e);
849                             mDeviceState.setError(
850                                     CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
851                             break;
852                         } catch (InterruptedException e) {
853                             Log.e(TAG, "Interrupted during capture: ", e);
854                             mDeviceState.setError(
855                                     CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
856                             break;
857                         } catch (RuntimeException e) {
858                             Log.e(TAG, "Received device exception during capture call: ", e);
859                             mDeviceState.setError(
860                                     CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
861                             break;
862                         }
863 
864                         if (paramsChanged) {
865                             if (DEBUG) {
866                                 Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
867                             }
868                             try {
869                                 mParams = mCamera.getParameters();
870                             } catch (RuntimeException e) {
871                                 Log.e(TAG, "Received device exception: ", e);
872                                 mDeviceState.setError(
873                                     CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
874                                 break;
875                             }
876 
877                             // Update parameters to the latest that we think the camera is using
878                             mLastRequest.setParameters(mParams);
879                         }
880 
881                         MutableLong timestampMutable = new MutableLong(/*value*/0L);
882                         try {
883                             boolean success = mCaptureCollector.waitForRequestCompleted(holder,
884                                     REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
885                                     /*out*/timestampMutable);
886 
887                             if (!success) {
888                                 Log.e(TAG, "Timed out while waiting for request to complete.");
889                                 mCaptureCollector.failAll();
890                             }
891                         } catch (InterruptedException e) {
892                             Log.e(TAG, "Interrupted waiting for request completion: ", e);
893                             mDeviceState.setError(
894                                     CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
895                             break;
896                         }
897 
898                         CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
899                                 mLastRequest, timestampMutable.value);
900                         /*
901                          * Order matters: The default result mapper is state-less; the
902                          * other mappers carry state and may override keys set by the default
903                          * mapper with their own values.
904                          */
905 
906                         // Update AF state
907                         mFocusStateMapper.mapResultTriggers(result);
908                         // Update face-related results
909                         mFaceDetectMapper.mapResultFaces(result, mLastRequest);
910 
911                         if (!holder.requestFailed()) {
912                             mDeviceState.setCaptureResult(holder, result);
913                         }
914 
915                         if (holder.isOutputAbandoned()) {
916                             anyRequestOutputAbandoned = true;
917                         }
918                     }
919 
920                     // Stop the repeating request if any of its output surfaces is abandoned.
921                     if (anyRequestOutputAbandoned && nextBurst.first.isRepeating()) {
922                         long lastFrameNumber = cancelRepeating(nextBurst.first.getRequestId());
923                         if (DEBUG) {
924                             Log.d(TAG, "Stopped repeating request. Last frame number is " +
925                                     lastFrameNumber);
926                         }
927                         mDeviceState.setRepeatingRequestError(lastFrameNumber);
928                     }
929 
930                     if (DEBUG) {
931                         long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
932                         Log.d(TAG, "Capture request took " + totalTime + " ns");
933                         mRequestCounter.countAndLog();
934                     }
935                     break;
936                 case MSG_CLEANUP:
937                     mCleanup = true;
938                     try {
939                         boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
940                                 TimeUnit.MILLISECONDS);
941                         if (!success) {
942                             Log.e(TAG, "Timed out while queueing cleanup request.");
943                             mCaptureCollector.failAll();
944                         }
945                     } catch (InterruptedException e) {
946                         Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
947                         mDeviceState.setError(
948                                 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
949                     }
950                     if (mGLThreadManager != null) {
951                         mGLThreadManager.quit();
952                         mGLThreadManager = null;
953                     }
954                     if (mCamera != null) {
955                         mCamera.release();
956                         mCamera = null;
957                     }
958                     resetJpegSurfaceFormats(mCallbackOutputs);
959                     break;
960                 case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
961                     // OK: Ignore message.
962                     break;
963                 default:
964                     throw new AssertionError("Unhandled message " + msg.what +
965                             " on RequestThread.");
966             }
967             return true;
968         }
969     };
970 
971     /**
972      * Create a new RequestThreadManager.
973      *
974      * @param cameraId the id of the camera to use.
975      * @param camera an open camera object.  The RequestThreadManager takes ownership of this camera
976      *               object, and is responsible for closing it.
977      * @param characteristics the static camera characteristics corresponding to this camera device
978      * @param deviceState a {@link CameraDeviceState} state machine.
979      */
RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics, CameraDeviceState deviceState)980     public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
981                                 CameraDeviceState deviceState) {
982         mCamera = checkNotNull(camera, "camera must not be null");
983         mCameraId = cameraId;
984         mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
985         String name = String.format("RequestThread-%d", cameraId);
986         TAG = name;
987         mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
988         mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
989         mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
990         mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
991         mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
992         mCamera.setErrorCallback(mErrorCallback);
993     }
994 
995     /**
996      * Start the request thread.
997      */
start()998     public void start() {
999         mRequestThread.start();
1000     }
1001 
1002     /**
1003      * Flush any pending requests.
1004      *
1005      * @return the last frame number.
1006      */
flush()1007     public long flush() {
1008         Log.i(TAG, "Flushing all pending requests.");
1009         long lastFrame = mRequestQueue.stopRepeating();
1010         mCaptureCollector.failAll();
1011         return lastFrame;
1012     }
1013 
1014     /**
1015      * Quit the request thread, and clean up everything.
1016      */
quit()1017     public void quit() {
1018         if (!mQuit.getAndSet(true)) {  // Avoid sending messages on dead thread's handler.
1019             Handler handler = mRequestThread.waitAndGetHandler();
1020             handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
1021             mRequestThread.quitSafely();
1022             try {
1023                 mRequestThread.join();
1024             } catch (InterruptedException e) {
1025                 Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
1026                         mRequestThread.getName(), mRequestThread.getId()));
1027             }
1028         }
1029     }
1030 
1031     /**
1032      * Submit the given burst of requests to be captured.
1033      *
1034      * <p>If the burst is repeating, replace the current repeating burst.</p>
1035      *
1036      * @param requests the burst of requests to add to the queue.
1037      * @param repeating true if the burst is repeating.
1038      * @return the submission info, including the new request id, and the last frame number, which
1039      *   contains either the frame number of the last frame that will be returned for this request,
1040      *   or the frame number of the last frame that will be returned for the current repeating
1041      *   request if this burst is set to be repeating.
1042      */
submitCaptureRequests(CaptureRequest[] requests, boolean repeating)1043     public SubmitInfo submitCaptureRequests(CaptureRequest[] requests, boolean repeating) {
1044         Handler handler = mRequestThread.waitAndGetHandler();
1045         SubmitInfo info;
1046         synchronized (mIdleLock) {
1047             info = mRequestQueue.submit(requests, repeating);
1048             handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
1049         }
1050         return info;
1051     }
1052 
1053     /**
1054      * Cancel a repeating request.
1055      *
1056      * @param requestId the id of the repeating request to cancel.
1057      * @return the last frame to be returned from the HAL for the given repeating request, or
1058      *          {@code INVALID_FRAME} if none exists.
1059      */
cancelRepeating(int requestId)1060     public long cancelRepeating(int requestId) {
1061         return mRequestQueue.stopRepeating(requestId);
1062     }
1063 
1064     /**
1065      * Configure with the current list of output Surfaces.
1066      *
1067      * <p>
1068      * This operation blocks until the configuration is complete.
1069      * </p>
1070      *
1071      * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
1072      *
1073      * @param outputs a {@link java.util.Collection} of outputs to configure.
1074      */
configure(Collection<Pair<Surface, Size>> outputs)1075     public void configure(Collection<Pair<Surface, Size>> outputs) {
1076         Handler handler = mRequestThread.waitAndGetHandler();
1077         final ConditionVariable condition = new ConditionVariable(/*closed*/false);
1078         ConfigureHolder holder = new ConfigureHolder(condition, outputs);
1079         handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
1080         condition.block();
1081     }
1082 }
1083