RequestThreadManager.java revision eecc904f13e7a105f5548c953e4caa306fe06f0d
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.LongParcelable;
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.List;
42import java.util.concurrent.TimeUnit;
43
44import static com.android.internal.util.Preconditions.*;
45
46/**
47 * This class executes requests to the {@link Camera}.
48 *
49 * <p>
50 * The main components of this class are:
51 * - A message queue of requests to the {@link Camera}.
52 * - A thread that consumes requests to the {@link Camera} and executes them.
53 * - A {@link GLThreadManager} that draws to the configured output {@link Surface}s.
54 * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations.
55 * </p>
56 */
57@SuppressWarnings("deprecation")
58public class RequestThreadManager {
59    private final String TAG;
60    private final int mCameraId;
61    private final RequestHandlerThread mRequestThread;
62
63    private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
64    // For slightly more spammy messages that will get repeated every frame
65    private static final boolean VERBOSE =
66            Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.VERBOSE);
67    private final Camera mCamera;
68    private final CameraCharacteristics mCharacteristics;
69
70    private final CameraDeviceState mDeviceState;
71    private final CaptureCollector mCaptureCollector;
72    private final LegacyFocusStateMapper mFocusStateMapper;
73    private final LegacyFaceDetectMapper mFaceDetectMapper;
74
75    private static final int MSG_CONFIGURE_OUTPUTS = 1;
76    private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
77    private static final int MSG_CLEANUP = 3;
78
79    private static final int MAX_IN_FLIGHT_REQUESTS = 2;
80
81    private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms
82    private static final int JPEG_FRAME_TIMEOUT = 3000; // ms (same as CTS for API2)
83    private static final int REQUEST_COMPLETE_TIMEOUT = 3000; // ms (same as JPEG timeout)
84
85    private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
86    private boolean mPreviewRunning = false;
87
88    private final List<Surface> mPreviewOutputs = new ArrayList<>();
89    private final List<Surface> mCallbackOutputs = new ArrayList<>();
90    private GLThreadManager mGLThreadManager;
91    private SurfaceTexture mPreviewTexture;
92    private Camera.Parameters mParams;
93
94    private final List<Long> mJpegSurfaceIds = new ArrayList<>();
95
96    private Size mIntermediateBufferSize;
97
98    private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds);
99    private LegacyRequest mLastRequest = null;
100    private SurfaceTexture mDummyTexture;
101    private Surface mDummySurface;
102
103    private final Object mIdleLock = new Object();
104    private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
105    private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
106
107    // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write
108    // limitations for (b/17379185).
109    private static final boolean USE_BLOB_FORMAT_OVERRIDE = true;
110
111    /**
112     * Container object for Configure messages.
113     */
114    private static class ConfigureHolder {
115        public final ConditionVariable condition;
116        public final Collection<Surface> surfaces;
117
118        public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces) {
119            this.condition = condition;
120            this.surfaces = surfaces;
121        }
122    }
123
124    /**
125     * Counter class used to calculate and log the current FPS of frame production.
126     */
127    public static class FpsCounter {
128        //TODO: Hook this up to SystTrace?
129        private static final String TAG = "FpsCounter";
130        private int mFrameCount = 0;
131        private long mLastTime = 0;
132        private long mLastPrintTime = 0;
133        private double mLastFps = 0;
134        private final String mStreamType;
135        private static final long NANO_PER_SECOND = 1000000000; //ns
136
137        public FpsCounter(String streamType) {
138            mStreamType = streamType;
139        }
140
141        public synchronized void countFrame() {
142            mFrameCount++;
143            long nextTime = SystemClock.elapsedRealtimeNanos();
144            if (mLastTime == 0) {
145                mLastTime = nextTime;
146            }
147            if (nextTime > mLastTime + NANO_PER_SECOND) {
148                long elapsed = nextTime - mLastTime;
149                mLastFps = mFrameCount * (NANO_PER_SECOND / (double) elapsed);
150                mFrameCount = 0;
151                mLastTime = nextTime;
152            }
153        }
154
155        public synchronized double checkFps() {
156            return mLastFps;
157        }
158
159        public synchronized void staggeredLog() {
160            if (mLastTime > mLastPrintTime + 5 * NANO_PER_SECOND) {
161                mLastPrintTime = mLastTime;
162                Log.d(TAG, "FPS for " + mStreamType + " stream: " + mLastFps );
163            }
164        }
165
166        public synchronized void countAndLog() {
167            countFrame();
168            staggeredLog();
169        }
170    }
171    /**
172     * Fake preview for jpeg captures when there is no active preview
173     */
174    private void createDummySurface() {
175        if (mDummyTexture == null || mDummySurface == null) {
176            mDummyTexture = new SurfaceTexture(/*ignored*/0);
177            // TODO: use smallest default sizes
178            mDummyTexture.setDefaultBufferSize(640, 480);
179            mDummySurface = new Surface(mDummyTexture);
180        }
181    }
182
183    private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
184        @Override
185        public void onError(int i, Camera camera) {
186            Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
187            mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
188        }
189    };
190
191    private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
192
193    private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
194        @Override
195        public void onPictureTaken(byte[] data, Camera camera) {
196            Log.i(TAG, "Received jpeg.");
197            Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced();
198            if (captureInfo == null || captureInfo.first == null) {
199                Log.e(TAG, "Dropping jpeg frame.");
200                return;
201            }
202            RequestHolder holder = captureInfo.first;
203            long timestamp = captureInfo.second;
204            for (Surface s : holder.getHolderTargets()) {
205                try {
206                    if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
207                        Log.i(TAG, "Producing jpeg buffer...");
208
209                        int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
210                        totalSize += ((totalSize - 1) & ~0x3) + 4; // align to next octonibble
211
212                        LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1);
213                        LegacyCameraDevice.setNextTimestamp(s, timestamp);
214                        LegacyCameraDevice.produceFrame(s, data, data.length, /*height*/1,
215                                CameraMetadataNative.NATIVE_JPEG_FORMAT);
216                    }
217                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
218                    Log.w(TAG, "Surface abandoned, dropping frame. ", e);
219                }
220            }
221
222            mReceivedJpeg.open();
223        }
224    };
225
226    private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
227        @Override
228        public void onShutter() {
229            mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos());
230        }
231    };
232
233    private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
234            new SurfaceTexture.OnFrameAvailableListener() {
235                @Override
236                public void onFrameAvailable(SurfaceTexture surfaceTexture) {
237                    if (DEBUG) {
238                        mPrevCounter.countAndLog();
239                    }
240                    mGLThreadManager.queueNewFrame();
241                }
242            };
243
244    private void stopPreview() {
245        if (VERBOSE) {
246            Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
247        }
248        if (mPreviewRunning) {
249            mCamera.stopPreview();
250            mPreviewRunning = false;
251        }
252    }
253
254    private void startPreview() {
255        if (VERBOSE) {
256            Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
257        }
258        if (!mPreviewRunning) {
259            // XX: CameraClient:;startPreview is not getting called after a stop
260            mCamera.startPreview();
261            mPreviewRunning = true;
262        }
263    }
264
265    private void doJpegCapturePrepare(RequestHolder request) throws IOException {
266        if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
267
268        if (!mPreviewRunning) {
269            if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
270
271            createDummySurface();
272            mCamera.setPreviewTexture(mDummyTexture);
273            startPreview();
274        }
275    }
276
277    private void doJpegCapture(RequestHolder request) {
278        if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
279
280        mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
281        mPreviewRunning = false;
282    }
283
284    private void doPreviewCapture(RequestHolder request) throws IOException {
285        if (VERBOSE) {
286            Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
287        }
288
289        if (mPreviewRunning) {
290            return; // Already running
291        }
292
293        if (mPreviewTexture == null) {
294            throw new IllegalStateException(
295                    "Preview capture called with no preview surfaces configured.");
296        }
297
298        mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
299                mIntermediateBufferSize.getHeight());
300        mCamera.setPreviewTexture(mPreviewTexture);
301
302        startPreview();
303    }
304
305    private void configureOutputs(Collection<Surface> outputs) {
306        if (DEBUG) {
307            String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
308            Log.d(TAG, "configureOutputs with " + outputsStr);
309        }
310
311        stopPreview();
312        /*
313         * Try to release the previous preview's surface texture earlier if we end up
314         * using a different one; this also reduces the likelihood of getting into a deadlock
315         * when disconnecting from the old previous texture at a later time.
316         */
317        try {
318            mCamera.setPreviewTexture(/*surfaceTexture*/null);
319        } catch (IOException e) {
320            Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
321        }
322
323        if (mGLThreadManager != null) {
324            mGLThreadManager.waitUntilStarted();
325            mGLThreadManager.ignoreNewFrames();
326            mGLThreadManager.waitUntilIdle();
327        }
328        resetJpegSurfaceFormats(mCallbackOutputs);
329        mPreviewOutputs.clear();
330        mCallbackOutputs.clear();
331        mPreviewTexture = null;
332
333        int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
334        int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
335        if (outputs != null) {
336            for (Surface s : outputs) {
337                try {
338                    int format = LegacyCameraDevice.detectSurfaceType(s);
339                    LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
340                    switch (format) {
341                        case CameraMetadataNative.NATIVE_JPEG_FORMAT:
342                            if (USE_BLOB_FORMAT_OVERRIDE) {
343                                // Override to RGBA_8888 format.
344                                LegacyCameraDevice.setSurfaceFormat(s,
345                                        LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
346                            }
347                            mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
348                            mCallbackOutputs.add(s);
349                            break;
350                        default:
351                            mPreviewOutputs.add(s);
352                            break;
353                    }
354                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
355                    Log.w(TAG, "Surface abandoned, skipping...", e);
356                }
357            }
358        }
359        try {
360            mParams = mCamera.getParameters();
361        } catch (RuntimeException e) {
362            Log.e(TAG, "Received device exception: ", e);
363            mDeviceState.setError(
364                CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
365            return;
366        }
367
368        List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
369        int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
370        if (DEBUG) {
371            Log.d(TAG, "doPreviewCapture - Selected range [" +
372                    bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
373                    bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
374        }
375        mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
376                bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
377
378        if (mPreviewOutputs.size() > 0) {
379            List<Size> outputSizes = new ArrayList<>(outputs.size());
380            for (Surface s : mPreviewOutputs) {
381                try {
382                    Size size = LegacyCameraDevice.getSurfaceSize(s);
383                    outputSizes.add(size);
384                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
385                    Log.w(TAG, "Surface abandoned, skipping...", e);
386                }
387            }
388
389            Size largestOutput = SizeAreaComparator.findLargestByArea(outputSizes);
390
391            // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
392            Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
393
394            List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
395                    mParams.getSupportedPreviewSizes());
396
397            // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
398            // of the configured output dimensions.  If none exists, fall back to using the largest
399            // supported preview size.
400            long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
401            Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
402            for (Size s : supportedPreviewSizes) {
403                long currArea = s.getWidth() * s.getHeight();
404                long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
405                if (checkAspectRatiosMatch(largestJpegDimen, s) && (currArea < bestArea &&
406                        currArea >= largestOutputArea)) {
407                    bestPreviewDimen = s;
408                }
409            }
410
411            mIntermediateBufferSize = bestPreviewDimen;
412            mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
413                    mIntermediateBufferSize.getHeight());
414
415            if (DEBUG) {
416                Log.d(TAG, "Intermediate buffer selected with dimens: " +
417                        bestPreviewDimen.toString());
418            }
419        } else {
420            mIntermediateBufferSize = null;
421            if (DEBUG) {
422                Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
423            }
424        }
425
426        Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs, mParams);
427        if (smallestSupportedJpegSize != null) {
428            /*
429             * Set takePicture size to the smallest supported JPEG size large enough
430             * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
431             */
432
433            Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
434            mParams.setPictureSize(
435                    smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
436        }
437
438        // TODO: Detect and optimize single-output paths here to skip stream teeing.
439        if (mGLThreadManager == null) {
440            mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
441            mGLThreadManager.start();
442        }
443        mGLThreadManager.waitUntilStarted();
444        mGLThreadManager.setConfigurationAndWait(mPreviewOutputs, mCaptureCollector);
445        mGLThreadManager.allowNewFrames();
446        mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
447        if (mPreviewTexture != null) {
448            mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
449        }
450
451        mCamera.setParameters(mParams);
452    }
453
454    private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
455        if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
456            return;
457        }
458        for(Surface s : surfaces) {
459            try {
460                LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
461            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
462                Log.w(TAG, "Surface abandoned, skipping...", e);
463            }
464        }
465    }
466
467    /**
468     * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
469     * than all of the configured {@code JPEG} outputs (by both width and height).
470     *
471     * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
472     * still satisfies the above constraint.</p>
473     *
474     * <p>As a result, the returned size is guaranteed to be usable without needing
475     * to upscale any of the outputs. If only one {@code JPEG} surface is used,
476     * then no scaling/cropping is necessary between the taken picture and
477     * the {@code JPEG} output surface.</p>
478     *
479     * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
480     * @param params api1 parameters (used for reading only)
481     *
482     * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
483     *          {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
484     *          surfaces.
485     */
486    private Size calculatePictureSize(
487            Collection<Surface> callbackOutputs, Camera.Parameters params) {
488        /*
489         * Find the largest JPEG size (if any), from the configured outputs:
490         * - the api1 picture size should be set to the smallest legal size that's at least as large
491         *   as the largest configured JPEG size
492         */
493        List<Size> configuredJpegSizes = new ArrayList<Size>();
494        for (Surface callbackSurface : callbackOutputs) {
495            try {
496
497                if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
498                    continue; // Ignore non-JPEG callback formats
499                }
500
501                Size jpegSize = LegacyCameraDevice.getSurfaceSize(callbackSurface);
502                configuredJpegSizes.add(jpegSize);
503            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
504                Log.w(TAG, "Surface abandoned, skipping...", e);
505            }
506        }
507        if (!configuredJpegSizes.isEmpty()) {
508            /*
509             * Find the largest configured JPEG width, and height, independently
510             * of the rest.
511             *
512             * The rest of the JPEG streams can be cropped out of this smallest bounding
513             * rectangle.
514             */
515            int maxConfiguredJpegWidth = -1;
516            int maxConfiguredJpegHeight = -1;
517            for (Size jpegSize : configuredJpegSizes) {
518                maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
519                        jpegSize.getWidth() : maxConfiguredJpegWidth;
520                maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
521                        jpegSize.getHeight() : maxConfiguredJpegHeight;
522            }
523            Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
524
525            List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
526                    params.getSupportedPictureSizes());
527
528            /*
529             * Find the smallest supported JPEG size that can fit the smallest bounding
530             * rectangle for the configured JPEG sizes.
531             */
532            List<Size> candidateSupportedJpegSizes = new ArrayList<>();
533            for (Size supportedJpegSize : supportedJpegSizes) {
534                if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
535                    supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
536                    candidateSupportedJpegSizes.add(supportedJpegSize);
537                }
538            }
539
540            if (candidateSupportedJpegSizes.isEmpty()) {
541                throw new AssertionError(
542                        "Could not find any supported JPEG sizes large enough to fit " +
543                        smallestBoundJpegSize);
544            }
545
546            Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
547                    new SizeAreaComparator());
548
549            if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
550                Log.w(TAG,
551                        String.format(
552                                "configureOutputs - Will need to crop picture %s into "
553                                + "smallest bound size %s",
554                                smallestSupportedJpegSize, smallestBoundJpegSize));
555            }
556
557            return smallestSupportedJpegSize;
558        }
559
560        return null;
561    }
562
563    private static boolean checkAspectRatiosMatch(Size a, Size b) {
564        float aAspect = a.getWidth() / (float) a.getHeight();
565        float bAspect = b.getWidth() / (float) b.getHeight();
566
567        return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
568    }
569
570    // Calculate the highest FPS range supported
571    private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
572        if (frameRates.size() == 0) {
573            Log.e(TAG, "No supported frame rates returned!");
574            return null;
575        }
576
577        int bestMin = 0;
578        int bestMax = 0;
579        int bestIndex = 0;
580        int index = 0;
581        for (int[] rate : frameRates) {
582            int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
583            int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
584            if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
585                bestMin = minFps;
586                bestMax = maxFps;
587                bestIndex = index;
588            }
589            index++;
590        }
591
592        return frameRates.get(bestIndex);
593    }
594
595    private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
596        private boolean mCleanup = false;
597        private final LegacyResultMapper mMapper = new LegacyResultMapper();
598
599        @Override
600        public boolean handleMessage(Message msg) {
601            if (mCleanup) {
602                return true;
603            }
604
605            if (DEBUG) {
606                Log.d(TAG, "Request thread handling message:" + msg.what);
607            }
608            long startTime = 0;
609            if (DEBUG) {
610                startTime = SystemClock.elapsedRealtimeNanos();
611            }
612            switch (msg.what) {
613                case MSG_CONFIGURE_OUTPUTS:
614                    ConfigureHolder config = (ConfigureHolder) msg.obj;
615                    int sizes = config.surfaces != null ? config.surfaces.size() : 0;
616                    Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
617
618                    try {
619                        boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
620                                TimeUnit.MILLISECONDS);
621                        if (!success) {
622                            Log.e(TAG, "Timed out while queueing configure request.");
623                            mCaptureCollector.failAll();
624                        }
625                    } catch (InterruptedException e) {
626                        Log.e(TAG, "Interrupted while waiting for requests to complete.");
627                        mDeviceState.setError(
628                                CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
629                        break;
630                    }
631
632                    configureOutputs(config.surfaces);
633                    config.condition.open();
634                    if (DEBUG) {
635                        long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
636                        Log.d(TAG, "Configure took " + totalTime + " ns");
637                    }
638                    break;
639                case MSG_SUBMIT_CAPTURE_REQUEST:
640                    Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
641
642                    // Get the next burst from the request queue.
643                    Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
644
645                    if (nextBurst == null) {
646                        // If there are no further requests queued, wait for any currently executing
647                        // requests to complete, then switch to idle state.
648                        try {
649                            boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
650                                    TimeUnit.MILLISECONDS);
651                            if (!success) {
652                                Log.e(TAG,
653                                        "Timed out while waiting for prior requests to complete.");
654                                mCaptureCollector.failAll();
655                            }
656                        } catch (InterruptedException e) {
657                            Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
658                            mDeviceState.setError(
659                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
660                            break;
661                        }
662
663                        synchronized (mIdleLock) {
664                            // Retry the the request queue.
665                            nextBurst = mRequestQueue.getNext();
666
667                            // If we still have no queued requests, go idle.
668                            if (nextBurst == null) {
669                                mDeviceState.setIdle();
670                                break;
671                            }
672                        }
673                    }
674
675                    if (nextBurst != null) {
676                        // Queue another capture if we did not get the last burst.
677                        handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
678                    }
679
680                    // Complete each request in the burst
681                    List<RequestHolder> requests =
682                            nextBurst.first.produceRequestHolders(nextBurst.second);
683                    for (RequestHolder holder : requests) {
684                        CaptureRequest request = holder.getRequest();
685
686                        boolean paramsChanged = false;
687
688                        // Only update parameters if the request has changed
689                        if (mLastRequest == null || mLastRequest.captureRequest != request) {
690
691                            // The intermediate buffer is sometimes null, but we always need
692                            // the Camera1 API configured preview size
693                            Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
694
695                            LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
696                                    request, previewSize, mParams); // params are copied
697
698
699                            // Parameters are mutated as a side-effect
700                            LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
701
702                            // If the parameters have changed, set them in the Camera1 API.
703                            if (!mParams.same(legacyRequest.parameters)) {
704                                try {
705                                    mCamera.setParameters(legacyRequest.parameters);
706                                } catch (RuntimeException e) {
707                                    // If setting the parameters failed, report a request error to
708                                    // the camera client, and skip any further work for this request
709                                    Log.e(TAG, "Exception while setting camera parameters: ", e);
710                                    holder.failRequest();
711                                    mDeviceState.setCaptureStart(holder, /*timestamp*/0,
712                                            CameraDeviceImpl.CameraDeviceCallbacks.
713                                                    ERROR_CAMERA_REQUEST);
714                                    continue;
715                                }
716                                paramsChanged = true;
717                                mParams = legacyRequest.parameters;
718                            }
719
720                            mLastRequest = legacyRequest;
721                        }
722
723                        try {
724                            boolean success = mCaptureCollector.queueRequest(holder,
725                                    mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
726
727                            if (!success) {
728                                // Report a request error if we timed out while queuing this.
729                                Log.e(TAG, "Timed out while queueing capture request.");
730                                holder.failRequest();
731                                mDeviceState.setCaptureStart(holder, /*timestamp*/0,
732                                        CameraDeviceImpl.CameraDeviceCallbacks.
733                                                ERROR_CAMERA_REQUEST);
734                                continue;
735                            }
736
737                            // Starting the preview needs to happen before enabling
738                            // face detection or auto focus
739                            if (holder.hasPreviewTargets()) {
740                                doPreviewCapture(holder);
741                            }
742                            if (holder.hasJpegTargets()) {
743                                while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
744                                        TimeUnit.MILLISECONDS)) {
745                                    // Fail preview requests until the queue is empty.
746                                    Log.e(TAG, "Timed out while waiting for preview requests to " +
747                                            "complete.");
748                                    mCaptureCollector.failNextPreview();
749                                }
750                                mReceivedJpeg.close();
751                                doJpegCapturePrepare(holder);
752                            }
753
754                            /*
755                             * Do all the actions that require a preview to have been started
756                             */
757
758                            // Toggle face detection on/off
759                            // - do this before AF to give AF a chance to use faces
760                            mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
761
762                            // Unconditionally process AF triggers, since they're non-idempotent
763                            // - must be done after setting the most-up-to-date AF mode
764                            mFocusStateMapper.processRequestTriggers(request, mParams);
765
766                            if (holder.hasJpegTargets()) {
767                                doJpegCapture(holder);
768                                if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
769                                    Log.e(TAG, "Hit timeout for jpeg callback!");
770                                    mCaptureCollector.failNextJpeg();
771                                }
772                            }
773
774                        } catch (IOException e) {
775                            Log.e(TAG, "Received device exception: ", e);
776                            mDeviceState.setError(
777                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
778                            break;
779                        } catch (InterruptedException e) {
780                            Log.e(TAG, "Interrupted during capture: ", e);
781                            mDeviceState.setError(
782                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
783                            break;
784                        }
785
786                        if (paramsChanged) {
787                            if (DEBUG) {
788                                Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
789                            }
790                            try {
791                                mParams = mCamera.getParameters();
792                            } catch (RuntimeException e) {
793                                Log.e(TAG, "Received device exception: ", e);
794                                mDeviceState.setError(
795                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
796                                break;
797                            }
798
799                            // Update parameters to the latest that we think the camera is using
800                            mLastRequest.setParameters(mParams);
801                        }
802
803                        MutableLong timestampMutable = new MutableLong(/*value*/0L);
804                        try {
805                            boolean success = mCaptureCollector.waitForRequestCompleted(holder,
806                                    REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
807                                    /*out*/timestampMutable);
808
809                            if (!success) {
810                                Log.e(TAG, "Timed out while waiting for request to complete.");
811                                mCaptureCollector.failAll();
812                            }
813                        } catch (InterruptedException e) {
814                            Log.e(TAG, "Interrupted waiting for request completion: ", e);
815                            mDeviceState.setError(
816                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
817                            break;
818                        }
819
820                        CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
821                                mLastRequest, timestampMutable.value);
822                        /*
823                         * Order matters: The default result mapper is state-less; the
824                         * other mappers carry state and may override keys set by the default
825                         * mapper with their own values.
826                         */
827
828                        // Update AF state
829                        mFocusStateMapper.mapResultTriggers(result);
830                        // Update face-related results
831                        mFaceDetectMapper.mapResultFaces(result, mLastRequest);
832
833                        if (!holder.requestFailed()) {
834                            mDeviceState.setCaptureResult(holder, result,
835                                    CameraDeviceState.NO_CAPTURE_ERROR);
836                        }
837                    }
838                    if (DEBUG) {
839                        long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
840                        Log.d(TAG, "Capture request took " + totalTime + " ns");
841                        mRequestCounter.countAndLog();
842                    }
843                    break;
844                case MSG_CLEANUP:
845                    mCleanup = true;
846                    try {
847                        boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
848                                TimeUnit.MILLISECONDS);
849                        if (!success) {
850                            Log.e(TAG, "Timed out while queueing cleanup request.");
851                            mCaptureCollector.failAll();
852                        }
853                    } catch (InterruptedException e) {
854                        Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
855                        mDeviceState.setError(
856                                CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
857                    }
858                    if (mGLThreadManager != null) {
859                        mGLThreadManager.quit();
860                    }
861                    if (mCamera != null) {
862                        mCamera.release();
863                    }
864                    resetJpegSurfaceFormats(mCallbackOutputs);
865                    break;
866                default:
867                    throw new AssertionError("Unhandled message " + msg.what +
868                            " on RequestThread.");
869            }
870            return true;
871        }
872    };
873
874    /**
875     * Create a new RequestThreadManager.
876     *
877     * @param cameraId the id of the camera to use.
878     * @param camera an open camera object.  The RequestThreadManager takes ownership of this camera
879     *               object, and is responsible for closing it.
880     * @param characteristics the static camera characteristics corresponding to this camera device
881     * @param deviceState a {@link CameraDeviceState} state machine.
882     */
883    public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
884                                CameraDeviceState deviceState) {
885        mCamera = checkNotNull(camera, "camera must not be null");
886        mCameraId = cameraId;
887        mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
888        String name = String.format("RequestThread-%d", cameraId);
889        TAG = name;
890        mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
891        mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
892        mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
893        mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
894        mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
895        mCamera.setErrorCallback(mErrorCallback);
896    }
897
898    /**
899     * Start the request thread.
900     */
901    public void start() {
902        mRequestThread.start();
903    }
904
905    /**
906     * Flush any pending requests.
907     *
908     * @return the last frame number.
909     */
910    public long flush() {
911        Log.i(TAG, "Flushing all pending requests.");
912        long lastFrame = mRequestQueue.stopRepeating();
913        mCaptureCollector.failAll();
914        return lastFrame;
915    }
916
917    /**
918     * Quit the request thread, and clean up everything.
919     */
920    public void quit() {
921        Handler handler = mRequestThread.waitAndGetHandler();
922        handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
923        mRequestThread.quitSafely();
924        try {
925            mRequestThread.join();
926        } catch (InterruptedException e) {
927            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
928                    mRequestThread.getName(), mRequestThread.getId()));
929        }
930    }
931
932    /**
933     * Submit the given burst of requests to be captured.
934     *
935     * <p>If the burst is repeating, replace the current repeating burst.</p>
936     *
937     * @param requests the burst of requests to add to the queue.
938     * @param repeating true if the burst is repeating.
939     * @param frameNumber an output argument that contains either the frame number of the last frame
940     *                    that will be returned for this request, or the frame number of the last
941     *                    frame that will be returned for the current repeating request if this
942     *                    burst is set to be repeating.
943     * @return the request id.
944     */
945    public int submitCaptureRequests(List<CaptureRequest> requests, boolean repeating,
946            /*out*/LongParcelable frameNumber) {
947        Handler handler = mRequestThread.waitAndGetHandler();
948        int ret;
949        synchronized (mIdleLock) {
950            ret = mRequestQueue.submit(requests, repeating, frameNumber);
951            handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
952        }
953        return ret;
954    }
955
956    /**
957     * Cancel a repeating request.
958     *
959     * @param requestId the id of the repeating request to cancel.
960     * @return the last frame to be returned from the HAL for the given repeating request, or
961     *          {@code INVALID_FRAME} if none exists.
962     */
963    public long cancelRepeating(int requestId) {
964        return mRequestQueue.stopRepeating(requestId);
965    }
966
967    /**
968     * Configure with the current list of output Surfaces.
969     *
970     * <p>
971     * This operation blocks until the configuration is complete.
972     * </p>
973     *
974     * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
975     *
976     * @param outputs a {@link java.util.Collection} of outputs to configure.
977     */
978    public void configure(Collection<Surface> outputs) {
979        Handler handler = mRequestThread.waitAndGetHandler();
980        final ConditionVariable condition = new ConditionVariable(/*closed*/false);
981        ConfigureHolder holder = new ConfigureHolder(condition, outputs);
982        handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
983        condition.block();
984    }
985}
986