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