RequestThreadManager.java revision 3e2c14f3d64b66e155e623c6cda22848eb3f5314
150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee/*
250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * Copyright (C) 2014 The Android Open Source Project
350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee *
450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * Licensed under the Apache License, Version 2.0 (the "License");
550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * you may not use this file except in compliance with the License.
650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * You may obtain a copy of the License at
750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee *
850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee *      http://www.apache.org/licenses/LICENSE-2.0
950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee *
1050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * Unless required by applicable law or agreed to in writing, software
1150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * distributed under the License is distributed on an "AS IS" BASIS,
1250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * See the License for the specific language governing permissions and
1450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * limitations under the License.
1550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee */
1650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
17ef9f6f957d897ea0ed82114185b8fa3fefd4917bTyler Gunnpackage android.hardware.camera2.legacy;
1850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
1950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.graphics.SurfaceTexture;
2050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.hardware.Camera;
2150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.hardware.camera2.CameraCharacteristics;
2250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.hardware.camera2.CaptureRequest;
2350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.hardware.camera2.impl.CameraDeviceImpl;
24ef9f6f957d897ea0ed82114185b8fa3fefd4917bTyler Gunnimport android.hardware.camera2.utils.LongParcelable;
2550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.hardware.camera2.utils.SizeAreaComparator;
2650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.hardware.camera2.impl.CameraMetadataNative;
2750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.os.ConditionVariable;
28ef9f6f957d897ea0ed82114185b8fa3fefd4917bTyler Gunnimport android.os.Handler;
29ef9f6f957d897ea0ed82114185b8fa3fefd4917bTyler Gunnimport android.os.Message;
3050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.os.SystemClock;
3150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.util.Log;
3250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport android.util.MutableLong;
33b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awadimport android.util.Pair;
34b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awadimport android.util.Size;
35b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awadimport android.view.Surface;
36b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad
3750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport java.io.IOException;
3850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport java.util.ArrayList;
3950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport java.util.Collection;
4050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport java.util.Collections;
4150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport java.util.List;
4250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport java.util.concurrent.TimeUnit;
4350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
4450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Leeimport static com.android.internal.util.Preconditions.*;
4550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
46b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad/**
4750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * This class executes requests to the {@link Camera}.
4850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee *
4950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * <p>
5050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * The main components of this class are:
5150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * - A message queue of requests to the {@link Camera}.
5250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * - A thread that consumes requests to the {@link Camera} and executes them.
53b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad * - A {@link GLThreadManager} that draws to the configured output {@link Surface}s.
5450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations.
5550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee * </p>
5650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee */
5750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee@SuppressWarnings("deprecation")
58b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awadpublic class RequestThreadManager {
5950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final String TAG;
60b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad    private final int mCameraId;
6150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final RequestHandlerThread mRequestThread;
62b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad
6350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
64b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad    // For slightly more spammy messages that will get repeated every frame
6550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final boolean VERBOSE =
6650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.VERBOSE);
6750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final Camera mCamera;
68b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad    private final CameraCharacteristics mCharacteristics;
69b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad
7050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final CameraDeviceState mDeviceState;
7150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final CaptureCollector mCaptureCollector;
7250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final LegacyFocusStateMapper mFocusStateMapper;
7350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final LegacyFaceDetectMapper mFaceDetectMapper;
7450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
7550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final int MSG_CONFIGURE_OUTPUTS = 1;
7650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2;
7750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final int MSG_CLEANUP = 3;
7850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
7950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final int MAX_IN_FLIGHT_REQUESTS = 2;
8050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
8150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms
8250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final int JPEG_FRAME_TIMEOUT = 3000; // ms (same as CTS for API2)
8350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final int REQUEST_COMPLETE_TIMEOUT = 3000; // ms (same as JPEG timeout)
8450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
8550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
8650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private boolean mPreviewRunning = false;
8750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
8850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final List<Surface> mPreviewOutputs = new ArrayList<>();
8950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final List<Surface> mCallbackOutputs = new ArrayList<>();
9050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private GLThreadManager mGLThreadManager;
9150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private SurfaceTexture mPreviewTexture;
9250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private Camera.Parameters mParams;
9350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
9450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private Size mIntermediateBufferSize;
9550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
96b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad    private final RequestQueue mRequestQueue = new RequestQueue();
9750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private LegacyRequest mLastRequest = null;
9850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private SurfaceTexture mDummyTexture;
9950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private Surface mDummySurface;
10050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
10150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final Object mIdleLock = new Object();
10250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
10350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
10450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
10550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    /**
10650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee     * Container object for Configure messages.
10750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee     */
10850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private static class ConfigureHolder {
10950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        public final ConditionVariable condition;
11050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        public final Collection<Surface> surfaces;
11150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
11250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces) {
113b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad            this.condition = condition;
11450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            this.surfaces = surfaces;
11550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
11650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    }
11750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
11850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    /**
119b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad     * Counter class used to calculate and log the current FPS of frame production.
120b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad     */
12150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    public static class FpsCounter {
12250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        //TODO: Hook this up to SystTrace?
12350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        private static final String TAG = "FpsCounter";
12450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        private int mFrameCount = 0;
12550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        private long mLastTime = 0;
12650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        private long mLastPrintTime = 0;
12750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        private double mLastFps = 0;
12850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        private final String mStreamType;
12950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        private static final long NANO_PER_SECOND = 1000000000; //ns
13050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
13150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        public FpsCounter(String streamType) {
13250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mStreamType = streamType;
13350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
13450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
13550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        public synchronized void countFrame() {
13650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mFrameCount++;
13750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            long nextTime = SystemClock.elapsedRealtimeNanos();
13850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            if (mLastTime == 0) {
13950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                mLastTime = nextTime;
14050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            }
14150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            if (nextTime > mLastTime + NANO_PER_SECOND) {
14250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                long elapsed = nextTime - mLastTime;
14350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                mLastFps = mFrameCount * (NANO_PER_SECOND / (double) elapsed);
14450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                mFrameCount = 0;
14550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                mLastTime = nextTime;
146b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad            }
14750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
14850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
14950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        public synchronized double checkFps() {
15050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            return mLastFps;
15150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
15250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
15350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        public synchronized void staggeredLog() {
15450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            if (mLastTime > mLastPrintTime + 5 * NANO_PER_SECOND) {
155b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad                mLastPrintTime = mLastTime;
156b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad                Log.d(TAG, "FPS for " + mStreamType + " stream: " + mLastFps );
157b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad            }
15850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
15950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
160a64627c2d3330f6bee8055b2e51ffaf8a122ef2bIhab Awad        public synchronized void countAndLog() {
16150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            countFrame();
16250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            staggeredLog();
16350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
16450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    }
16550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    /**
16650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee     * Fake preview for jpeg captures when there is no active preview
16750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee     */
16850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private void createDummySurface() {
16950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        if (mDummyTexture == null || mDummySurface == null) {
17050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mDummyTexture = new SurfaceTexture(/*ignored*/0);
171b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad            // TODO: use smallest default sizes
17250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mDummyTexture.setDefaultBufferSize(640, 480);
17350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mDummySurface = new Surface(mDummyTexture);
17450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
17550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    }
17650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
17750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final Camera.ErrorCallback mErrorCallback = new Camera.ErrorCallback() {
17850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        @Override
179b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad        public void onError(int i, Camera camera) {
18050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            Log.e(TAG, "Received error " + i + " from the Camera1 ErrorCallback");
18150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mDeviceState.setError(CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
18250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
18350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    };
18450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
18550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final ConditionVariable mReceivedJpeg = new ConditionVariable(false);
18650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
187b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad    private final Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
18850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        @Override
18950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        public void onPictureTaken(byte[] data, Camera camera) {
19050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            Log.i(TAG, "Received jpeg.");
19150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            Pair<RequestHolder, Long> captureInfo = mCaptureCollector.jpegProduced();
19250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            RequestHolder holder = captureInfo.first;
19350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            long timestamp = captureInfo.second;
19450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            if (holder == null) {
195b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad                Log.e(TAG, "Dropping jpeg frame.");
19650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                return;
19750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            }
19850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            for (Surface s : holder.getHolderTargets()) {
19950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                try {
20050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                    if (RequestHolder.jpegType(s)) {
20150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                        Log.i(TAG, "Producing jpeg buffer...");
20250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                        LegacyCameraDevice.setSurfaceDimens(s, data.length +
203b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad                                LegacyCameraDevice.nativeGetJpegFooterSize(), /*height*/1);
20450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                        LegacyCameraDevice.setNextTimestamp(s, timestamp);
20550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                        LegacyCameraDevice.produceFrame(s, data, data.length, /*height*/1,
20650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                                CameraMetadataNative.NATIVE_JPEG_FORMAT);
20750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                    }
20850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
209b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad                    Log.w(TAG, "Surface abandoned, dropping frame. ", e);
21050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                }
211b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad            }
21250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
21350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mReceivedJpeg.open();
21450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
21550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    };
21650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
217b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad    private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
21850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        @Override
219b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad        public void onShutter() {
22050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mCaptureCollector.jpegCaptured(SystemClock.elapsedRealtimeNanos());
22150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
22250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    };
22350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
22450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
22550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            new SurfaceTexture.OnFrameAvailableListener() {
22650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                @Override
227b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad                public void onFrameAvailable(SurfaceTexture surfaceTexture) {
22850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                    if (DEBUG) {
22950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                        mPrevCounter.countAndLog();
23050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                    }
23150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                    mGLThreadManager.queueNewFrame();
23250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee                }
23350aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            };
23450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
235b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad    private void stopPreview() {
23650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        if (VERBOSE) {
23750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning);
23850aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
23950aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        if (mPreviewRunning) {
24050aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mCamera.stopPreview();
24150aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            mPreviewRunning = false;
24250aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        }
243b19a0bcdd8a5020c61a0d697f600fdc943c86f59Ihab Awad    }
24450aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee
24550aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee    private void startPreview() {
24650aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee        if (VERBOSE) {
24750aca23bd7f51a3cf32a1f7e32238cc1e26ca1a0Andrew Lee            Log.v(TAG, "startPreview - preview running? " + mPreviewRunning);
248        }
249        if (!mPreviewRunning) {
250            // XX: CameraClient:;startPreview is not getting called after a stop
251            mCamera.startPreview();
252            mPreviewRunning = true;
253        }
254    }
255
256    private void doJpegCapturePrepare(RequestHolder request) throws IOException {
257        if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning);
258
259        if (!mPreviewRunning) {
260            if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
261
262            createDummySurface();
263            mCamera.setPreviewTexture(mDummyTexture);
264            startPreview();
265        }
266    }
267
268    private void doJpegCapture(RequestHolder request) {
269        if (DEBUG) Log.d(TAG, "doJpegCapturePrepare");
270
271        mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
272        mPreviewRunning = false;
273    }
274
275    private void doPreviewCapture(RequestHolder request) throws IOException {
276        if (VERBOSE) {
277            Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning);
278        }
279
280        if (mPreviewRunning) {
281            return; // Already running
282        }
283
284        if (mPreviewTexture == null) {
285            throw new IllegalStateException(
286                    "Preview capture called with no preview surfaces configured.");
287        }
288
289        mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
290                mIntermediateBufferSize.getHeight());
291        mCamera.setPreviewTexture(mPreviewTexture);
292
293        startPreview();
294    }
295
296    private void configureOutputs(Collection<Surface> outputs) {
297        if (DEBUG) {
298            String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces");
299            Log.d(TAG, "configureOutputs with " + outputsStr);
300        }
301
302        stopPreview();
303        /*
304         * Try to release the previous preview's surface texture earlier if we end up
305         * using a different one; this also reduces the likelihood of getting into a deadlock
306         * when disconnecting from the old previous texture at a later time.
307         */
308        try {
309            mCamera.setPreviewTexture(/*surfaceTexture*/null);
310        } catch (IOException e) {
311            Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e);
312        }
313
314        if (mGLThreadManager != null) {
315            mGLThreadManager.waitUntilStarted();
316            mGLThreadManager.ignoreNewFrames();
317            mGLThreadManager.waitUntilIdle();
318        }
319        mPreviewOutputs.clear();
320        mCallbackOutputs.clear();
321        mPreviewTexture = null;
322
323        int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
324        int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
325        if (outputs != null) {
326            for (Surface s : outputs) {
327                try {
328                    int format = LegacyCameraDevice.detectSurfaceType(s);
329                    LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
330                    switch (format) {
331                        case CameraMetadataNative.NATIVE_JPEG_FORMAT:
332                            mCallbackOutputs.add(s);
333                            break;
334                        default:
335                            mPreviewOutputs.add(s);
336                            break;
337                    }
338                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
339                    Log.w(TAG, "Surface abandoned, skipping...", e);
340                }
341            }
342        }
343        mParams = mCamera.getParameters();
344
345        List<int[]> supportedFpsRanges = mParams.getSupportedPreviewFpsRange();
346        int[] bestRange = getPhotoPreviewFpsRange(supportedFpsRanges);
347        if (DEBUG) {
348            Log.d(TAG, "doPreviewCapture - Selected range [" +
349                    bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + "," +
350                    bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] + "]");
351        }
352        mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
353                bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
354
355        if (mPreviewOutputs.size() > 0) {
356            List<Size> outputSizes = new ArrayList<>(outputs.size());
357            for (Surface s : mPreviewOutputs) {
358                try {
359                    Size size = LegacyCameraDevice.getSurfaceSize(s);
360                    outputSizes.add(size);
361                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
362                    Log.w(TAG, "Surface abandoned, skipping...", e);
363                }
364            }
365
366            Size largestOutput = SizeAreaComparator.findLargestByArea(outputSizes);
367
368            // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
369            Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams);
370
371            List<Size> supportedPreviewSizes = ParameterUtils.convertSizeList(
372                    mParams.getSupportedPreviewSizes());
373
374            // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
375            // of the configured output dimensions.  If none exists, fall back to using the largest
376            // supported preview size.
377            long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
378            Size bestPreviewDimen = SizeAreaComparator.findLargestByArea(supportedPreviewSizes);
379            for (Size s : supportedPreviewSizes) {
380                long currArea = s.getWidth() * s.getHeight();
381                long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
382                if (checkAspectRatiosMatch(largestJpegDimen, s) && (currArea < bestArea &&
383                        currArea >= largestOutputArea)) {
384                    bestPreviewDimen = s;
385                }
386            }
387
388            mIntermediateBufferSize = bestPreviewDimen;
389            mParams.setPreviewSize(mIntermediateBufferSize.getWidth(),
390                    mIntermediateBufferSize.getHeight());
391
392            if (DEBUG) {
393                Log.d(TAG, "Intermediate buffer selected with dimens: " +
394                        bestPreviewDimen.toString());
395            }
396        } else {
397            mIntermediateBufferSize = null;
398            if (DEBUG) {
399                Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
400            }
401        }
402
403        Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs, mParams);
404        if (smallestSupportedJpegSize != null) {
405            /*
406             * Set takePicture size to the smallest supported JPEG size large enough
407             * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
408             */
409
410            Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
411            mParams.setPictureSize(
412                    smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
413        }
414
415        // TODO: Detect and optimize single-output paths here to skip stream teeing.
416        if (mGLThreadManager == null) {
417            mGLThreadManager = new GLThreadManager(mCameraId, facing, mDeviceState);
418            mGLThreadManager.start();
419        }
420        mGLThreadManager.waitUntilStarted();
421        mGLThreadManager.setConfigurationAndWait(mPreviewOutputs, mCaptureCollector);
422        mGLThreadManager.allowNewFrames();
423        mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
424        if (mPreviewTexture != null) {
425            mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
426        }
427
428        mCamera.setParameters(mParams);
429        // TODO: configure the JPEG surface with some arbitrary size
430        // using LegacyCameraDevice.nativeConfigureSurface
431    }
432
433    /**
434     * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
435     * than all of the configured {@code JPEG} outputs (by both width and height).
436     *
437     * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
438     * still satisfies the above constraint.</p>
439     *
440     * <p>As a result, the returned size is guaranteed to be usable without needing
441     * to upscale any of the outputs. If only one {@code JPEG} surface is used,
442     * then no scaling/cropping is necessary between the taken picture and
443     * the {@code JPEG} output surface.</p>
444     *
445     * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
446     * @param params api1 parameters (used for reading only)
447     *
448     * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
449     *          {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
450     *          surfaces.
451     */
452    private Size calculatePictureSize(
453            Collection<Surface> callbackOutputs, Camera.Parameters params) {
454        /*
455         * Find the largest JPEG size (if any), from the configured outputs:
456         * - the api1 picture size should be set to the smallest legal size that's at least as large
457         *   as the largest configured JPEG size
458         */
459        List<Size> configuredJpegSizes = new ArrayList<Size>();
460        for (Surface callbackSurface : callbackOutputs) {
461            try {
462                int format = LegacyCameraDevice.detectSurfaceType(callbackSurface);
463
464                if (format != CameraMetadataNative.NATIVE_JPEG_FORMAT) {
465                    continue; // Ignore non-JPEG callback formats
466                }
467
468                Size jpegSize = LegacyCameraDevice.getSurfaceSize(callbackSurface);
469                configuredJpegSizes.add(jpegSize);
470            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
471                Log.w(TAG, "Surface abandoned, skipping...", e);
472            }
473        }
474        if (!configuredJpegSizes.isEmpty()) {
475            /*
476             * Find the largest configured JPEG width, and height, independently
477             * of the rest.
478             *
479             * The rest of the JPEG streams can be cropped out of this smallest bounding
480             * rectangle.
481             */
482            int maxConfiguredJpegWidth = -1;
483            int maxConfiguredJpegHeight = -1;
484            for (Size jpegSize : configuredJpegSizes) {
485                maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
486                        jpegSize.getWidth() : maxConfiguredJpegWidth;
487                maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
488                        jpegSize.getHeight() : maxConfiguredJpegHeight;
489            }
490            Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
491
492            List<Size> supportedJpegSizes = ParameterUtils.convertSizeList(
493                    params.getSupportedPictureSizes());
494
495            /*
496             * Find the smallest supported JPEG size that can fit the smallest bounding
497             * rectangle for the configured JPEG sizes.
498             */
499            List<Size> candidateSupportedJpegSizes = new ArrayList<>();
500            for (Size supportedJpegSize : supportedJpegSizes) {
501                if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
502                    supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
503                    candidateSupportedJpegSizes.add(supportedJpegSize);
504                }
505            }
506
507            if (candidateSupportedJpegSizes.isEmpty()) {
508                throw new AssertionError(
509                        "Could not find any supported JPEG sizes large enough to fit " +
510                        smallestBoundJpegSize);
511            }
512
513            Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
514                    new SizeAreaComparator());
515
516            if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
517                Log.w(TAG,
518                        String.format(
519                                "configureOutputs - Will need to crop picture %s into "
520                                + "smallest bound size %s",
521                                smallestSupportedJpegSize, smallestBoundJpegSize));
522            }
523
524            return smallestSupportedJpegSize;
525        }
526
527        return null;
528    }
529
530    private static boolean checkAspectRatiosMatch(Size a, Size b) {
531        float aAspect = a.getWidth() / (float) a.getHeight();
532        float bAspect = b.getWidth() / (float) b.getHeight();
533
534        return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
535    }
536
537    // Calculate the highest FPS range supported
538    private int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
539        if (frameRates.size() == 0) {
540            Log.e(TAG, "No supported frame rates returned!");
541            return null;
542        }
543
544        int bestMin = 0;
545        int bestMax = 0;
546        int bestIndex = 0;
547        int index = 0;
548        for (int[] rate : frameRates) {
549            int minFps = rate[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
550            int maxFps = rate[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
551            if (maxFps > bestMax || (maxFps == bestMax && minFps > bestMin)) {
552                bestMin = minFps;
553                bestMax = maxFps;
554                bestIndex = index;
555            }
556            index++;
557        }
558
559        return frameRates.get(bestIndex);
560    }
561
562    private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
563        private boolean mCleanup = false;
564        private final LegacyResultMapper mMapper = new LegacyResultMapper();
565
566        @Override
567        public boolean handleMessage(Message msg) {
568            if (mCleanup) {
569                return true;
570            }
571
572            if (DEBUG) {
573                Log.d(TAG, "Request thread handling message:" + msg.what);
574            }
575            long startTime = 0;
576            if (DEBUG) {
577                startTime = SystemClock.elapsedRealtimeNanos();
578            }
579            switch (msg.what) {
580                case MSG_CONFIGURE_OUTPUTS:
581                    ConfigureHolder config = (ConfigureHolder) msg.obj;
582                    int sizes = config.surfaces != null ? config.surfaces.size() : 0;
583                    Log.i(TAG, "Configure outputs: " + sizes + " surfaces configured.");
584
585                    try {
586                        boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
587                                TimeUnit.MILLISECONDS);
588                        if (!success) {
589                            Log.e(TAG, "Timed out while queueing configure request.");
590                            mCaptureCollector.failAll();
591                        }
592                    } catch (InterruptedException e) {
593                        Log.e(TAG, "Interrupted while waiting for requests to complete.");
594                        mDeviceState.setError(
595                                CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
596                        break;
597                    }
598
599                    configureOutputs(config.surfaces);
600                    config.condition.open();
601                    if (DEBUG) {
602                        long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
603                        Log.d(TAG, "Configure took " + totalTime + " ns");
604                    }
605                    break;
606                case MSG_SUBMIT_CAPTURE_REQUEST:
607                    Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
608
609                    // Get the next burst from the request queue.
610                    Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
611
612                    if (nextBurst == null) {
613                        // If there are no further requests queued, wait for any currently executing
614                        // requests to complete, then switch to idle state.
615                        try {
616                            boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
617                                    TimeUnit.MILLISECONDS);
618                            if (!success) {
619                                Log.e(TAG,
620                                        "Timed out while waiting for prior requests to complete.");
621                                mCaptureCollector.failAll();
622                            }
623                        } catch (InterruptedException e) {
624                            Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
625                            mDeviceState.setError(
626                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
627                            break;
628                        }
629
630                        synchronized (mIdleLock) {
631                            // Retry the the request queue.
632                            nextBurst = mRequestQueue.getNext();
633
634                            // If we still have no queued requests, go idle.
635                            if (nextBurst == null) {
636                                mDeviceState.setIdle();
637                                break;
638                            }
639                        }
640                    }
641
642                    if (nextBurst != null) {
643                        // Queue another capture if we did not get the last burst.
644                        handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
645                    }
646
647                    // Complete each request in the burst
648                    List<RequestHolder> requests =
649                            nextBurst.first.produceRequestHolders(nextBurst.second);
650                    for (RequestHolder holder : requests) {
651                        CaptureRequest request = holder.getRequest();
652
653                        boolean paramsChanged = false;
654
655                        // Only update parameters if the request has changed
656                        if (mLastRequest == null || mLastRequest.captureRequest != request) {
657
658                            // The intermediate buffer is sometimes null, but we always need
659                            // the Camera1 API configured preview size
660                            Size previewSize = ParameterUtils.convertSize(mParams.getPreviewSize());
661
662                            LegacyRequest legacyRequest = new LegacyRequest(mCharacteristics,
663                                    request, previewSize, mParams); // params are copied
664
665
666                            // Parameters are mutated as a side-effect
667                            LegacyMetadataMapper.convertRequestMetadata(/*inout*/legacyRequest);
668
669                            // If the parameters have changed, set them in the Camera1 API.
670                            if (!mParams.same(legacyRequest.parameters)) {
671                                try {
672                                    mCamera.setParameters(legacyRequest.parameters);
673                                } catch (RuntimeException e) {
674                                    // If setting the parameters failed, report a request error to
675                                    // the camera client, and skip any further work for this request
676                                    Log.e(TAG, "Exception while setting camera parameters: ", e);
677                                    holder.failRequest();
678                                    mDeviceState.setCaptureStart(holder, /*timestamp*/0,
679                                            CameraDeviceImpl.CameraDeviceCallbacks.
680                                                    ERROR_CAMERA_REQUEST);
681                                    continue;
682                                }
683                                paramsChanged = true;
684                                mParams = legacyRequest.parameters;
685                            }
686
687                            mLastRequest = legacyRequest;
688                        }
689
690                        try {
691                            boolean success = mCaptureCollector.queueRequest(holder,
692                                    mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS);
693
694                            if (!success) {
695                                // Report a request error if we timed out while queuing this.
696                                Log.e(TAG, "Timed out while queueing capture request.");
697                                holder.failRequest();
698                                mDeviceState.setCaptureStart(holder, /*timestamp*/0,
699                                        CameraDeviceImpl.CameraDeviceCallbacks.
700                                                ERROR_CAMERA_REQUEST);
701                                continue;
702                            }
703
704                            // Starting the preview needs to happen before enabling
705                            // face detection or auto focus
706                            if (holder.hasPreviewTargets()) {
707                                doPreviewCapture(holder);
708                            }
709                            if (holder.hasJpegTargets()) {
710                                while(!mCaptureCollector.waitForPreviewsEmpty(PREVIEW_FRAME_TIMEOUT,
711                                        TimeUnit.MILLISECONDS)) {
712                                    // Fail preview requests until the queue is empty.
713                                    Log.e(TAG, "Timed out while waiting for preview requests to " +
714                                            "complete.");
715                                    mCaptureCollector.failNextPreview();
716                                }
717                                mReceivedJpeg.close();
718                                doJpegCapturePrepare(holder);
719                            }
720
721                            /*
722                             * Do all the actions that require a preview to have been started
723                             */
724
725                            // Toggle face detection on/off
726                            // - do this before AF to give AF a chance to use faces
727                            mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams);
728
729                            // Unconditionally process AF triggers, since they're non-idempotent
730                            // - must be done after setting the most-up-to-date AF mode
731                            mFocusStateMapper.processRequestTriggers(request, mParams);
732
733                            if (holder.hasJpegTargets()) {
734                                doJpegCapture(holder);
735                                if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
736                                    Log.e(TAG, "Hit timeout for jpeg callback!");
737                                    mCaptureCollector.failNextJpeg();
738                                }
739                            }
740
741                        } catch (IOException e) {
742                            Log.e(TAG, "Received device exception: ", e);
743                            mDeviceState.setError(
744                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
745                            break;
746                        } catch (InterruptedException e) {
747                            Log.e(TAG, "Interrupted during capture: ", e);
748                            mDeviceState.setError(
749                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
750                            break;
751                        }
752
753                        if (paramsChanged) {
754                            if (DEBUG) {
755                                Log.d(TAG, "Params changed -- getting new Parameters from HAL.");
756                            }
757                            mParams = mCamera.getParameters();
758
759                            // Update parameters to the latest that we think the camera is using
760                            mLastRequest.setParameters(mParams);
761                        }
762
763                        MutableLong timestampMutable = new MutableLong(/*value*/0L);
764                        try {
765                            boolean success = mCaptureCollector.waitForRequestCompleted(holder,
766                                    REQUEST_COMPLETE_TIMEOUT, TimeUnit.MILLISECONDS,
767                                    /*out*/timestampMutable);
768
769                            if (!success) {
770                                Log.e(TAG, "Timed out while waiting for request to complete.");
771                                mCaptureCollector.failAll();
772                            }
773                        } catch (InterruptedException e) {
774                            Log.e(TAG, "Interrupted waiting for request completion: ", e);
775                            mDeviceState.setError(
776                                    CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
777                            break;
778                        }
779
780                        CameraMetadataNative result = mMapper.cachedConvertResultMetadata(
781                                mLastRequest, timestampMutable.value);
782                        /*
783                         * Order matters: The default result mapper is state-less; the
784                         * other mappers carry state and may override keys set by the default
785                         * mapper with their own values.
786                         */
787
788                        // Update AF state
789                        mFocusStateMapper.mapResultTriggers(result);
790                        // Update face-related results
791                        mFaceDetectMapper.mapResultFaces(result, mLastRequest);
792
793                        if (!holder.requestFailed()) {
794                            mDeviceState.setCaptureResult(holder, result,
795                                    CameraDeviceState.NO_CAPTURE_ERROR);
796                        }
797                    }
798                    if (DEBUG) {
799                        long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
800                        Log.d(TAG, "Capture request took " + totalTime + " ns");
801                        mRequestCounter.countAndLog();
802                    }
803                    break;
804                case MSG_CLEANUP:
805                    mCleanup = true;
806                    try {
807                        boolean success = mCaptureCollector.waitForEmpty(JPEG_FRAME_TIMEOUT,
808                                TimeUnit.MILLISECONDS);
809                        if (!success) {
810                            Log.e(TAG, "Timed out while queueing cleanup request.");
811                            mCaptureCollector.failAll();
812                        }
813                    } catch (InterruptedException e) {
814                        Log.e(TAG, "Interrupted while waiting for requests to complete: ", e);
815                        mDeviceState.setError(
816                                CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE);
817                    }
818                    if (mGLThreadManager != null) {
819                        mGLThreadManager.quit();
820                    }
821                    if (mCamera != null) {
822                        mCamera.release();
823                    }
824                    break;
825                default:
826                    throw new AssertionError("Unhandled message " + msg.what +
827                            " on RequestThread.");
828            }
829            return true;
830        }
831    };
832
833    /**
834     * Create a new RequestThreadManager.
835     *
836     * @param cameraId the id of the camera to use.
837     * @param camera an open camera object.  The RequestThreadManager takes ownership of this camera
838     *               object, and is responsible for closing it.
839     * @param characteristics the static camera characteristics corresponding to this camera device
840     * @param deviceState a {@link CameraDeviceState} state machine.
841     */
842    public RequestThreadManager(int cameraId, Camera camera, CameraCharacteristics characteristics,
843                                CameraDeviceState deviceState) {
844        mCamera = checkNotNull(camera, "camera must not be null");
845        mCameraId = cameraId;
846        mCharacteristics = checkNotNull(characteristics, "characteristics must not be null");
847        String name = String.format("RequestThread-%d", cameraId);
848        TAG = name;
849        mDeviceState = checkNotNull(deviceState, "deviceState must not be null");
850        mFocusStateMapper = new LegacyFocusStateMapper(mCamera);
851        mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics);
852        mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState);
853        mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb);
854        mCamera.setErrorCallback(mErrorCallback);
855    }
856
857    /**
858     * Start the request thread.
859     */
860    public void start() {
861        mRequestThread.start();
862    }
863
864    /**
865     * Flush any pending requests.
866     *
867     * @return the last frame number.
868     */
869    public long flush() {
870        Log.i(TAG, "Flushing all pending requests.");
871        long lastFrame = mRequestQueue.stopRepeating();
872        mCaptureCollector.failAll();
873        return lastFrame;
874    }
875
876    /**
877     * Quit the request thread, and clean up everything.
878     */
879    public void quit() {
880        Handler handler = mRequestThread.waitAndGetHandler();
881        handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
882        mRequestThread.quitSafely();
883        try {
884            mRequestThread.join();
885        } catch (InterruptedException e) {
886            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
887                    mRequestThread.getName(), mRequestThread.getId()));
888        }
889    }
890
891    /**
892     * Submit the given burst of requests to be captured.
893     *
894     * <p>If the burst is repeating, replace the current repeating burst.</p>
895     *
896     * @param requests the burst of requests to add to the queue.
897     * @param repeating true if the burst is repeating.
898     * @param frameNumber an output argument that contains either the frame number of the last frame
899     *                    that will be returned for this request, or the frame number of the last
900     *                    frame that will be returned for the current repeating request if this
901     *                    burst is set to be repeating.
902     * @return the request id.
903     */
904    public int submitCaptureRequests(List<CaptureRequest> requests, boolean repeating,
905            /*out*/LongParcelable frameNumber) {
906        Handler handler = mRequestThread.waitAndGetHandler();
907        int ret;
908        synchronized (mIdleLock) {
909            ret = mRequestQueue.submit(requests, repeating, frameNumber);
910            handler.sendEmptyMessage(MSG_SUBMIT_CAPTURE_REQUEST);
911        }
912        return ret;
913    }
914
915    /**
916     * Cancel a repeating request.
917     *
918     * @param requestId the id of the repeating request to cancel.
919     * @return the last frame to be returned from the HAL for the given repeating request, or
920     *          {@code INVALID_FRAME} if none exists.
921     */
922    public long cancelRepeating(int requestId) {
923        return mRequestQueue.stopRepeating(requestId);
924    }
925
926    /**
927     * Configure with the current list of output Surfaces.
928     *
929     * <p>
930     * This operation blocks until the configuration is complete.
931     * </p>
932     *
933     * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
934     *
935     * @param outputs a {@link java.util.Collection} of outputs to configure.
936     */
937    public void configure(Collection<Surface> outputs) {
938        Handler handler = mRequestThread.waitAndGetHandler();
939        final ConditionVariable condition = new ConditionVariable(/*closed*/false);
940        ConfigureHolder holder = new ConfigureHolder(condition, outputs);
941        handler.sendMessage(handler.obtainMessage(MSG_CONFIGURE_OUTPUTS, 0, 0, holder));
942        condition.block();
943    }
944}
945