RequestThreadManager.java revision 2c3d7c5f68993531a4f5f91c1335cac8ca448e17
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 = 300; // 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        mParams = mCamera.getParameters();
360
361        List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
362        int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
363        if (DEBUG) {
364            Log.d(TAG, "doPreviewCapture - Selected range [" +
365                    bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
366                    bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
367        }
368        mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
369                bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
370
371        if (mPreviewOutputs.size() > 0) {
372            List<Size> outputSizes = new ArrayList<>(outputs.size());
373            for (Surface s : mPreviewOutputs) {
374                try {
375                    Size size = LegacyCameraDevice.getSurfaceSize(s);
376                    outputSizes.add(size);
377                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
378                    Log.w(TAG, "Surface abandoned, skipping...", e);
379                }
380            }
381
382            Size largestOutput = SizeAreaComparator.findLargestByArea(outputSizes);
383
384            // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
385            Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
386
387            List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
388                    mParams.getSupportedPreviewSizes());
389
390            // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
391            // of the configured output dimensions.  If none exists, fall back to using the largest
392            // supported preview size.
393            long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
394            Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
395            for (Size s : supportedPreviewSizes) {
396                long currArea = s.getWidth() * s.getHeight();
397                long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
398                if (checkAspectRatiosMatch(largestJpegDimen, s) && (currArea < bestArea &&
399                        currArea >= largestOutputArea)) {
400                    bestPreviewDimen = s;
401                }
402            }
403
404            mIntermediateBufferSize = bestPreviewDimen;
405            mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
406                    mIntermediateBufferSize.getHeight());
407
408            if (DEBUG) {
409                Log.d(TAG, "Intermediate buffer selected with dimens: " +
410                        bestPreviewDimen.toString());
411            }
412        } else {
413            mIntermediateBufferSize = null;
414            if (DEBUG) {
415                Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
416            }
417        }
418
419        Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs, mParams);
420        if (smallestSupportedJpegSize != null) {
421            /*
422             * Set takePicture size to the smallest supported JPEG size large enough
423             * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
424             */
425
426            Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
427            mParams.setPictureSize(
428                    smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
429        }
430
431        // TODO: Detect and optimize single-output paths here to skip stream teeing.
432        if (mGLThreadManager == null) {
433            mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
434            mGLThreadManager.start();
435        }
436        mGLThreadManager.waitUntilStarted();
437        mGLThreadManager.setConfigurationAndWait(mPreviewOutputs, mCaptureCollector);
438        mGLThreadManager.allowNewFrames();
439        mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
440        if (mPreviewTexture != null) {
441            mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
442        }
443
444        mCamera.setParameters(mParams);
445    }
446
447    private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
448        if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
449            return;
450        }
451        for(Surface s : surfaces) {
452            try {
453                LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
454            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
455                Log.w(TAG, "Surface abandoned, skipping...", e);
456            }
457        }
458    }
459
460    /**
461     * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
462     * than all of the configured {@code JPEG} outputs (by both width and height).
463     *
464     * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
465     * still satisfies the above constraint.</p>
466     *
467     * <p>As a result, the returned size is guaranteed to be usable without needing
468     * to upscale any of the outputs. If only one {@code JPEG} surface is used,
469     * then no scaling/cropping is necessary between the taken picture and
470     * the {@code JPEG} output surface.</p>
471     *
472     * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
473     * @param params api1 parameters (used for reading only)
474     *
475     * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
476     *          {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
477     *          surfaces.
478     */
479    private Size calculatePictureSize(
480            Collection<Surface> callbackOutputs, Camera.Parameters params) {
481        /*
482         * Find the largest JPEG size (if any), from the configured outputs:
483         * - the api1 picture size should be set to the smallest legal size that's at least as large
484         *   as the largest configured JPEG size
485         */
486        List<Size> configuredJpegSizes = new ArrayList<Size>();
487        for (Surface callbackSurface : callbackOutputs) {
488            try {
489
490                if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
491                    continue; // Ignore non-JPEG callback formats
492                }
493
494                Size jpegSize = LegacyCameraDevice.getSurfaceSize(callbackSurface);
495                configuredJpegSizes.add(jpegSize);
496            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
497                Log.w(TAG, "Surface abandoned, skipping...", e);
498            }
499        }
500        if (!configuredJpegSizes.isEmpty()) {
501            /*
502             * Find the largest configured JPEG width, and height, independently
503             * of the rest.
504             *
505             * The rest of the JPEG streams can be cropped out of this smallest bounding
506             * rectangle.
507             */
508            int maxConfiguredJpegWidth = -1;
509            int maxConfiguredJpegHeight = -1;
510            for (Size jpegSize : configuredJpegSizes) {
511                maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
512                        jpegSize.getWidth() : maxConfiguredJpegWidth;
513                maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
514                        jpegSize.getHeight() : maxConfiguredJpegHeight;
515            }
516            Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
517
518            List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
519                    params.getSupportedPictureSizes());
520
521            /*
522             * Find the smallest supported JPEG size that can fit the smallest bounding
523             * rectangle for the configured JPEG sizes.
524             */
525            List<Size> candidateSupportedJpegSizes = new ArrayList<>();
526            for (Size supportedJpegSize : supportedJpegSizes) {
527                if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
528                    supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
529                    candidateSupportedJpegSizes.add(supportedJpegSize);
530                }
531            }
532
533            if (candidateSupportedJpegSizes.isEmpty()) {
534                throw new AssertionError(
535                        "Could not find any supported JPEG sizes large enough to fit " +
536                        smallestBoundJpegSize);
537            }
538
539            Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
540                    new SizeAreaComparator());
541
542            if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
543                Log.w(TAG,
544                        String.format(
545                                "configureOutputs - Will need to crop picture %s into "
546                                + "smallest bound size %s",
547                                smallestSupportedJpegSize, smallestBoundJpegSize));
548            }
549
550            return smallestSupportedJpegSize;
551        }
552
553        return null;
554    }
555
556    private static boolean checkAspectRatiosMatch(Size a, Size b) {
557        float aAspect = a.getWidth() / (float) a.getHeight();
558        float bAspect = b.getWidth() / (float) b.getHeight();
559
560        return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
561    }
562
563    // Calculate the highest FPS range supported
564    private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
565        if (frameRates.size() == 0) {
566            Log.e(TAG, "No supported frame rates returned!");
567            return null;
568        }
569
570        int bestMin = 0;
571        int bestMax = 0;
572        int bestIndex = 0;
573        int index = 0;
574        for (int[] rate : frameRates) {
575            int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
576            int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
577            if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
578                bestMin = minFps;
579                bestMax = maxFps;
580                bestIndex = index;
581            }
582            index++;
583        }
584
585        return frameRates.get(bestIndex);
586    }
587
588    private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
589        private boolean mCleanup = false;
590        private final LegacyResultMapper mMapper = new LegacyResultMapper();
591
592        @Override
593        public boolean handleMessage(Message msg) {
594            if (mCleanup) {
595                return true;
596            }
597
598            if (DEBUG) {
599                Log.d(TAG, "Request thread handling message:" + msg.what);
600            }
601            long startTime = 0;
602            if (DEBUG) {
603                startTime = SystemClock.elapsedRealtimeNanos();
604            }
605            switch (msg.what) {
606                case MSG_CONFIGURE_OUTPUTS:
607                    ConfigureHolder config = (ConfigureHolder) msg.obj;
608                    int sizes = config.surfaces != null ? config.surfaces.size() : 0;
609                    Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
610
611                    try {
612                        boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
613                                TimeUnit.MILLISECONDS);
614                        if (!success) {
615                            Log.e(TAG, "Timed out while queueing configure request.");
616                            mCaptureCollector.failAll();
617                        }
618                    } catch (InterruptedException e) {
619                        Log.e(TAG, "Interrupted while waiting for requests to complete.");
620                        mDeviceState.setError(
621                                CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
622                        break;
623                    }
624
625                    configureOutputs(config.surfaces);
626                    config.condition.open();
627                    if (DEBUG) {
628                        long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
629                        Log.d(TAG, "Configure took " + totalTime + " ns");
630                    }
631                    break;
632                case MSG_SUBMIT_CAPTURE_REQUEST:
633                    Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
634
635                    // Get the next burst from the request queue.
636                    Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
637
638                    if (nextBurst == null) {
639                        // If there are no further requests queued, wait for any currently executing
640                        // requests to complete, then switch to idle state.
641                        try {
642                            boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
643                                    TimeUnit.MILLISECONDS);
644                            if (!success) {
645                                Log.e(TAG,
646                                        "Timed out while waiting for prior requests to complete.");
647                                mCaptureCollector.failAll();
648                            }
649                        } catch (InterruptedException e) {
650                            Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
651                            mDeviceState.setError(
652                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
653                            break;
654                        }
655
656                        synchronized (mIdleLock) {
657                            // Retry the the request queue.
658                            nextBurst = mRequestQueue.getNext();
659
660                            // If we still have no queued requests, go idle.
661                            if (nextBurst == null) {
662                                mDeviceState.setIdle();
663                                break;
664                            }
665                        }
666                    }
667
668                    if (nextBurst != null) {
669                        // Queue another capture if we did not get the last burst.
670                        handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
671                    }
672
673                    // Complete each request in the burst
674                    List<RequestHolder> requests =
675                            nextBurst.first.produceRequestHolders(nextBurst.second);
676                    for (RequestHolder holder : requests) {
677                        CaptureRequest request = holder.getRequest();
678
679                        boolean paramsChanged = false;
680
681                        // Only update parameters if the request has changed
682                        if (mLastRequest == null || mLastRequest.captureRequest != request) {
683
684                            // The intermediate buffer is sometimes null, but we always need
685                            // the Camera1 API configured preview size
686                            Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
687
688                            LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
689                                    request, previewSize, mParams); // params are copied
690
691
692                            // Parameters are mutated as a side-effect
693                            LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
694
695                            // If the parameters have changed, set them in the Camera1 API.
696                            if (!mParams.same(legacyRequest.parameters)) {
697                                try {
698                                    mCamera.setParameters(legacyRequest.parameters);
699                                } catch (RuntimeException e) {
700                                    // If setting the parameters failed, report a request error to
701                                    // the camera client, and skip any further work for this request
702                                    Log.e(TAG, "Exception while setting camera parameters: ", e);
703                                    holder.failRequest();
704                                    mDeviceState.setCaptureStart(holder, /*timestamp*/0,
705                                            CameraDeviceImpl.CameraDeviceCallbacks.
706                                                    ERROR_CAMERA_REQUEST);
707                                    continue;
708                                }
709                                paramsChanged = true;
710                                mParams = legacyRequest.parameters;
711                            }
712
713                            mLastRequest = legacyRequest;
714                        }
715
716                        try {
717                            boolean success = mCaptureCollector.queueRequest(holder,
718                                    mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
719
720                            if (!success) {
721                                // Report a request error if we timed out while queuing this.
722                                Log.e(TAG, "Timed out while queueing capture request.");
723                                holder.failRequest();
724                                mDeviceState.setCaptureStart(holder, /*timestamp*/0,
725                                        CameraDeviceImpl.CameraDeviceCallbacks.
726                                                ERROR_CAMERA_REQUEST);
727                                continue;
728                            }
729
730                            // Starting the preview needs to happen before enabling
731                            // face detection or auto focus
732                            if (holder.hasPreviewTargets()) {
733                                doPreviewCapture(holder);
734                            }
735                            if (holder.hasJpegTargets()) {
736                                while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
737                                        TimeUnit.MILLISECONDS)) {
738                                    // Fail preview requests until the queue is empty.
739                                    Log.e(TAG, "Timed out while waiting for preview requests to " +
740                                            "complete.");
741                                    mCaptureCollector.failNextPreview();
742                                }
743                                mReceivedJpeg.close();
744                                doJpegCapturePrepare(holder);
745                            }
746
747                            /*
748                             * Do all the actions that require a preview to have been started
749                             */
750
751                            // Toggle face detection on/off
752                            // - do this before AF to give AF a chance to use faces
753                            mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
754
755                            // Unconditionally process AF triggers, since they're non-idempotent
756                            // - must be done after setting the most-up-to-date AF mode
757                            mFocusStateMapper.processRequestTriggers(request, mParams);
758
759                            if (holder.hasJpegTargets()) {
760                                doJpegCapture(holder);
761                                if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
762                                    Log.e(TAG, "Hit timeout for jpeg callback!");
763                                    mCaptureCollector.failNextJpeg();
764                                }
765                            }
766
767                        } catch (IOException e) {
768                            Log.e(TAG, "Received device exception: ", e);
769                            mDeviceState.setError(
770                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
771                            break;
772                        } catch (InterruptedException e) {
773                            Log.e(TAG, "Interrupted during capture: ", e);
774                            mDeviceState.setError(
775                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
776                            break;
777                        }
778
779                        if (paramsChanged) {
780                            if (DEBUG) {
781                                Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
782                            }
783                            mParams = mCamera.getParameters();
784
785                            // Update parameters to the latest that we think the camera is using
786                            mLastRequest.setParameters(mParams);
787                        }
788
789                        MutableLong timestampMutable = new MutableLong(/*value*/0L);
790                        try {
791                            boolean success = mCaptureCollector.waitForRequestCompleted(holder,
792                                    REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
793                                    /*out*/timestampMutable);
794
795                            if (!success) {
796                                Log.e(TAG, "Timed out while waiting for request to complete.");
797                                mCaptureCollector.failAll();
798                            }
799                        } catch (InterruptedException e) {
800                            Log.e(TAG, "Interrupted waiting for request completion: ", e);
801                            mDeviceState.setError(
802                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
803                            break;
804                        }
805
806                        CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
807                                mLastRequest, timestampMutable.value);
808                        /*
809                         * Order matters: The default result mapper is state-less; the
810                         * other mappers carry state and may override keys set by the default
811                         * mapper with their own values.
812                         */
813
814                        // Update AF state
815                        mFocusStateMapper.mapResultTriggers(result);
816                        // Update face-related results
817                        mFaceDetectMapper.mapResultFaces(result, mLastRequest);
818
819                        if (!holder.requestFailed()) {
820                            mDeviceState.setCaptureResult(holder, result,
821                                    CameraDeviceState.NO_CAPTURE_ERROR);
822                        }
823                    }
824                    if (DEBUG) {
825                        long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
826                        Log.d(TAG, "Capture request took " + totalTime + " ns");
827                        mRequestCounter.countAndLog();
828                    }
829                    break;
830                case MSG_CLEANUP:
831                    mCleanup = true;
832                    try {
833                        boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
834                                TimeUnit.MILLISECONDS);
835                        if (!success) {
836                            Log.e(TAG, "Timed out while queueing cleanup request.");
837                            mCaptureCollector.failAll();
838                        }
839                    } catch (InterruptedException e) {
840                        Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
841                        mDeviceState.setError(
842                                CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
843                    }
844                    if (mGLThreadManager != null) {
845                        mGLThreadManager.quit();
846                    }
847                    if (mCamera != null) {
848                        mCamera.release();
849                    }
850                    resetJpegSurfaceFormats(mCallbackOutputs);
851                    break;
852                default:
853                    throw new AssertionError("Unhandled message " + msg.what +
854                            " on RequestThread.");
855            }
856            return true;
857        }
858    };
859
860    /**
861     * Create a new RequestThreadManager.
862     *
863     * @param cameraId the id of the camera to use.
864     * @param camera an open camera object.  The RequestThreadManager takes ownership of this camera
865     *               object, and is responsible for closing it.
866     * @param characteristics the static camera characteristics corresponding to this camera device
867     * @param deviceState a {@link CameraDeviceState} state machine.
868     */
869    public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
870                                CameraDeviceState deviceState) {
871        mCamera = checkNotNull(camera, "camera must not be null");
872        mCameraId = cameraId;
873        mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
874        String name = String.format("RequestThread-%d", cameraId);
875        TAG = name;
876        mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
877        mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
878        mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
879        mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
880        mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
881        mCamera.setErrorCallback(mErrorCallback);
882    }
883
884    /**
885     * Start the request thread.
886     */
887    public void start() {
888        mRequestThread.start();
889    }
890
891    /**
892     * Flush any pending requests.
893     *
894     * @return the last frame number.
895     */
896    public long flush() {
897        Log.i(TAG, "Flushing all pending requests.");
898        long lastFrame = mRequestQueue.stopRepeating();
899        mCaptureCollector.failAll();
900        return lastFrame;
901    }
902
903    /**
904     * Quit the request thread, and clean up everything.
905     */
906    public void quit() {
907        Handler handler = mRequestThread.waitAndGetHandler();
908        handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
909        mRequestThread.quitSafely();
910        try {
911            mRequestThread.join();
912        } catch (InterruptedException e) {
913            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
914                    mRequestThread.getName(), mRequestThread.getId()));
915        }
916    }
917
918    /**
919     * Submit the given burst of requests to be captured.
920     *
921     * <p>If the burst is repeating, replace the current repeating burst.</p>
922     *
923     * @param requests the burst of requests to add to the queue.
924     * @param repeating true if the burst is repeating.
925     * @param frameNumber an output argument that contains either the frame number of the last frame
926     *                    that will be returned for this request, or the frame number of the last
927     *                    frame that will be returned for the current repeating request if this
928     *                    burst is set to be repeating.
929     * @return the request id.
930     */
931    public int submitCaptureRequests(List<CaptureRequest> requests, boolean repeating,
932            /*out*/LongParcelable frameNumber) {
933        Handler handler = mRequestThread.waitAndGetHandler();
934        int ret;
935        synchronized (mIdleLock) {
936            ret = mRequestQueue.submit(requests, repeating, frameNumber);
937            handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
938        }
939        return ret;
940    }
941
942    /**
943     * Cancel a repeating request.
944     *
945     * @param requestId the id of the repeating request to cancel.
946     * @return the last frame to be returned from the HAL for the given repeating request, or
947     *          {@code INVALID_FRAME} if none exists.
948     */
949    public long cancelRepeating(int requestId) {
950        return mRequestQueue.stopRepeating(requestId);
951    }
952
953    /**
954     * Configure with the current list of output Surfaces.
955     *
956     * <p>
957     * This operation blocks until the configuration is complete.
958     * </p>
959     *
960     * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
961     *
962     * @param outputs a {@link java.util.Collection} of outputs to configure.
963     */
964    public void configure(Collection<Surface> outputs) {
965        Handler handler = mRequestThread.waitAndGetHandler();
966        final ConditionVariable condition = new ConditionVariable(/*closed*/false);
967        ConfigureHolder holder = new ConfigureHolder(condition, outputs);
968        handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
969        condition.block();
970    }
971}
972