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
17package android.hardware.camera2.legacy;
18
19import android.graphics.SurfaceTexture;
20import android.hardware.Camera;
21import android.hardware.camera2.CameraCharacteristics;
22import android.hardware.camera2.CaptureRequest;
23import android.hardware.camera2.impl.CameraDeviceImpl;
24import android.hardware.camera2.utils.SubmitInfo;
25import android.hardware.camera2.utils.SizeAreaComparator;
26import 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;
32import android.util.MutableLong;
33import android.util.Pair;
34import android.util.Size;
35import android.view.Surface;
36
37import java.io.IOException;
38import java.util.ArrayList;
39import java.util.Collection;
40import java.util.Collections;
41import java.util.Iterator;
42import java.util.List;
43import java.util.concurrent.TimeUnit;
44import java.util.concurrent.atomic.AtomicBoolean;
45
46import 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")
60public 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
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
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
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
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
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
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
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
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
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
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     */
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
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
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     */
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     */
998    public void start() {
999        mRequestThread.start();
1000    }
1001
1002    /**
1003     * Flush any pending requests.
1004     *
1005     * @return the last frame number.
1006     */
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     */
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     */
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     */
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     */
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