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