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 package android.hardware.camera2.legacy;
     17 
     18 import android.hardware.camera2.impl.CameraDeviceImpl;
     19 import android.util.Log;
     20 import android.util.MutableLong;
     21 import android.util.Pair;
     22 
     23 import java.util.ArrayDeque;
     24 import java.util.ArrayList;
     25 import java.util.TreeSet;
     26 import java.util.concurrent.TimeUnit;
     27 import java.util.concurrent.locks.Condition;
     28 import java.util.concurrent.locks.ReentrantLock;
     29 
     30 /**
     31  * Collect timestamps and state for each {@link CaptureRequest} as it passes through
     32  * the Legacy camera pipeline.
     33  */
     34 public class CaptureCollector {
     35     private static final String TAG = "CaptureCollector";
     36 
     37     private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
     38 
     39     private static final int FLAG_RECEIVED_JPEG = 1;
     40     private static final int FLAG_RECEIVED_JPEG_TS = 2;
     41     private static final int FLAG_RECEIVED_PREVIEW = 4;
     42     private static final int FLAG_RECEIVED_PREVIEW_TS = 8;
     43     private static final int FLAG_RECEIVED_ALL_JPEG = FLAG_RECEIVED_JPEG | FLAG_RECEIVED_JPEG_TS;
     44     private static final int FLAG_RECEIVED_ALL_PREVIEW = FLAG_RECEIVED_PREVIEW |
     45             FLAG_RECEIVED_PREVIEW_TS;
     46 
     47     private static final int MAX_JPEGS_IN_FLIGHT = 1;
     48 
     49     private class CaptureHolder implements Comparable<CaptureHolder>{
     50         private final RequestHolder mRequest;
     51         private final LegacyRequest mLegacy;
     52         public final boolean needsJpeg;
     53         public final boolean needsPreview;
     54 
     55         private long mTimestamp = 0;
     56         private int mReceivedFlags = 0;
     57         private boolean mHasStarted = false;
     58         private boolean mFailedJpeg = false;
     59         private boolean mFailedPreview = false;
     60         private boolean mCompleted = false;
     61         private boolean mPreviewCompleted = false;
     62 
     63         public CaptureHolder(RequestHolder request, LegacyRequest legacyHolder) {
     64             mRequest = request;
     65             mLegacy = legacyHolder;
     66             needsJpeg = request.hasJpegTargets();
     67             needsPreview = request.hasPreviewTargets();
     68         }
     69 
     70         public boolean isPreviewCompleted() {
     71             return (mReceivedFlags & FLAG_RECEIVED_ALL_PREVIEW) == FLAG_RECEIVED_ALL_PREVIEW;
     72         }
     73 
     74         public  boolean isJpegCompleted() {
     75             return (mReceivedFlags & FLAG_RECEIVED_ALL_JPEG) == FLAG_RECEIVED_ALL_JPEG;
     76         }
     77 
     78         public boolean isCompleted() {
     79             return (needsJpeg == isJpegCompleted()) && (needsPreview == isPreviewCompleted());
     80         }
     81 
     82         public void tryComplete() {
     83             if (!mPreviewCompleted && needsPreview && isPreviewCompleted()) {
     84                 CaptureCollector.this.onPreviewCompleted();
     85                 mPreviewCompleted = true;
     86             }
     87 
     88             if (isCompleted() && !mCompleted) {
     89                 if (mFailedPreview || mFailedJpeg) {
     90                     if (!mHasStarted) {
     91                         // Send a request error if the capture has not yet started.
     92                         mRequest.failRequest();
     93                         CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
     94                                 CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_REQUEST);
     95                     } else {
     96                         // Send buffer dropped errors for each pending buffer if the request has
     97                         // started.
     98                         if (mFailedPreview) {
     99                             Log.w(TAG, "Preview buffers dropped for request: " +
    100                                     mRequest.getRequestId());
    101                             for (int i = 0; i < mRequest.numPreviewTargets(); i++) {
    102                                 CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
    103                                     /*result*/null,
    104                                         CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER);
    105                             }
    106                         }
    107                         if (mFailedJpeg) {
    108                             Log.w(TAG, "Jpeg buffers dropped for request: " +
    109                                     mRequest.getRequestId());
    110                             for (int i = 0; i < mRequest.numJpegTargets(); i++) {
    111                                 CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
    112                                     /*result*/null,
    113                                         CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER);
    114                             }
    115                         }
    116                     }
    117                 }
    118                 CaptureCollector.this.onRequestCompleted(CaptureHolder.this);
    119                 mCompleted = true;
    120             }
    121         }
    122 
    123         public void setJpegTimestamp(long timestamp) {
    124             if (DEBUG) {
    125                 Log.d(TAG, "setJpegTimestamp - called for request " + mRequest.getRequestId());
    126             }
    127             if (!needsJpeg) {
    128                 throw new IllegalStateException(
    129                         "setJpegTimestamp called for capture with no jpeg targets.");
    130             }
    131             if (isCompleted()) {
    132                 throw new IllegalStateException(
    133                         "setJpegTimestamp called on already completed request.");
    134             }
    135 
    136             mReceivedFlags |= FLAG_RECEIVED_JPEG_TS;
    137 
    138             if (mTimestamp == 0) {
    139                 mTimestamp = timestamp;
    140             }
    141 
    142             if (!mHasStarted) {
    143                 mHasStarted = true;
    144                 CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
    145                         CameraDeviceState.NO_CAPTURE_ERROR);
    146             }
    147 
    148             tryComplete();
    149         }
    150 
    151         public void setJpegProduced() {
    152             if (DEBUG) {
    153                 Log.d(TAG, "setJpegProduced - called for request " + mRequest.getRequestId());
    154             }
    155             if (!needsJpeg) {
    156                 throw new IllegalStateException(
    157                         "setJpegProduced called for capture with no jpeg targets.");
    158             }
    159             if (isCompleted()) {
    160                 throw new IllegalStateException(
    161                         "setJpegProduced called on already completed request.");
    162             }
    163 
    164             mReceivedFlags |= FLAG_RECEIVED_JPEG;
    165             tryComplete();
    166         }
    167 
    168         public void setJpegFailed() {
    169             if (DEBUG) {
    170                 Log.d(TAG, "setJpegFailed - called for request " + mRequest.getRequestId());
    171             }
    172             if (!needsJpeg || isJpegCompleted()) {
    173                 return;
    174             }
    175             mFailedJpeg = true;
    176 
    177             mReceivedFlags |= FLAG_RECEIVED_JPEG;
    178             mReceivedFlags |= FLAG_RECEIVED_JPEG_TS;
    179             tryComplete();
    180         }
    181 
    182         public void setPreviewTimestamp(long timestamp) {
    183             if (DEBUG) {
    184                 Log.d(TAG, "setPreviewTimestamp - called for request " + mRequest.getRequestId());
    185             }
    186             if (!needsPreview) {
    187                 throw new IllegalStateException(
    188                         "setPreviewTimestamp called for capture with no preview targets.");
    189             }
    190             if (isCompleted()) {
    191                 throw new IllegalStateException(
    192                         "setPreviewTimestamp called on already completed request.");
    193             }
    194 
    195             mReceivedFlags |= FLAG_RECEIVED_PREVIEW_TS;
    196 
    197             if (mTimestamp == 0) {
    198                 mTimestamp = timestamp;
    199             }
    200 
    201             if (!needsJpeg) {
    202                 if (!mHasStarted) {
    203                     mHasStarted = true;
    204                     CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
    205                             CameraDeviceState.NO_CAPTURE_ERROR);
    206                 }
    207             }
    208 
    209             tryComplete();
    210         }
    211 
    212         public void setPreviewProduced() {
    213             if (DEBUG) {
    214                 Log.d(TAG, "setPreviewProduced - called for request " + mRequest.getRequestId());
    215             }
    216             if (!needsPreview) {
    217                 throw new IllegalStateException(
    218                         "setPreviewProduced called for capture with no preview targets.");
    219             }
    220             if (isCompleted()) {
    221                 throw new IllegalStateException(
    222                         "setPreviewProduced called on already completed request.");
    223             }
    224 
    225             mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
    226             tryComplete();
    227         }
    228 
    229         public void setPreviewFailed() {
    230             if (DEBUG) {
    231                 Log.d(TAG, "setPreviewFailed - called for request " + mRequest.getRequestId());
    232             }
    233             if (!needsPreview || isPreviewCompleted()) {
    234                 return;
    235             }
    236             mFailedPreview = true;
    237 
    238             mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
    239             mReceivedFlags |= FLAG_RECEIVED_PREVIEW_TS;
    240             tryComplete();
    241         }
    242 
    243         // Comparison and equals based on frame number.
    244         @Override
    245         public int compareTo(CaptureHolder captureHolder) {
    246             return (mRequest.getFrameNumber() > captureHolder.mRequest.getFrameNumber()) ? 1 :
    247                     ((mRequest.getFrameNumber() == captureHolder.mRequest.getFrameNumber()) ? 0 :
    248                             -1);
    249         }
    250 
    251         // Comparison and equals based on frame number.
    252         @Override
    253         public boolean equals(Object o) {
    254             return o instanceof CaptureHolder && compareTo((CaptureHolder) o) == 0;
    255         }
    256     }
    257 
    258     private final TreeSet<CaptureHolder> mActiveRequests;
    259     private final ArrayDeque<CaptureHolder> mJpegCaptureQueue;
    260     private final ArrayDeque<CaptureHolder> mJpegProduceQueue;
    261     private final ArrayDeque<CaptureHolder> mPreviewCaptureQueue;
    262     private final ArrayDeque<CaptureHolder> mPreviewProduceQueue;
    263     private final ArrayList<CaptureHolder> mCompletedRequests = new ArrayList<>();
    264 
    265     private final ReentrantLock mLock = new ReentrantLock();
    266     private final Condition mIsEmpty;
    267     private final Condition mPreviewsEmpty;
    268     private final Condition mNotFull;
    269     private final CameraDeviceState mDeviceState;
    270     private int mInFlight = 0;
    271     private int mInFlightPreviews = 0;
    272     private final int mMaxInFlight;
    273 
    274     /**
    275      * Create a new {@link CaptureCollector} that can modify the given {@link CameraDeviceState}.
    276      *
    277      * @param maxInFlight max allowed in-flight requests.
    278      * @param deviceState the {@link CameraDeviceState} to update as requests are processed.
    279      */
    280     public CaptureCollector(int maxInFlight, CameraDeviceState deviceState) {
    281         mMaxInFlight = maxInFlight;
    282         mJpegCaptureQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
    283         mJpegProduceQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
    284         mPreviewCaptureQueue = new ArrayDeque<>(mMaxInFlight);
    285         mPreviewProduceQueue = new ArrayDeque<>(mMaxInFlight);
    286         mActiveRequests = new TreeSet<>();
    287         mIsEmpty = mLock.newCondition();
    288         mNotFull = mLock.newCondition();
    289         mPreviewsEmpty = mLock.newCondition();
    290         mDeviceState = deviceState;
    291     }
    292 
    293     /**
    294      * Queue a new request.
    295      *
    296      * <p>
    297      * For requests that use the Camera1 API preview output stream, this will block if there are
    298      * already {@code maxInFlight} requests in progress (until at least one prior request has
    299      * completed). For requests that use the Camera1 API jpeg callbacks, this will block until
    300      * all prior requests have been completed to avoid stopping preview for
    301      * {@link android.hardware.Camera#takePicture} before prior preview requests have been
    302      * completed.
    303      * </p>
    304      * @param holder the {@link RequestHolder} for this request.
    305      * @param legacy the {@link LegacyRequest} for this request; this will not be mutated.
    306      * @param timeout a timeout to use for this call.
    307      * @param unit the units to use for the timeout.
    308      * @return {@code false} if this method timed out.
    309      * @throws InterruptedException if this thread is interrupted.
    310      */
    311     public boolean queueRequest(RequestHolder holder, LegacyRequest legacy, long timeout,
    312                                 TimeUnit unit)
    313             throws InterruptedException {
    314         CaptureHolder h = new CaptureHolder(holder, legacy);
    315         long nanos = unit.toNanos(timeout);
    316         final ReentrantLock lock = this.mLock;
    317         lock.lock();
    318         try {
    319             if (DEBUG) {
    320                 Log.d(TAG, "queueRequest  for request " + holder.getRequestId() +
    321                         " - " + mInFlight + " requests remain in flight.");
    322             }
    323 
    324             if (!(h.needsJpeg || h.needsPreview)) {
    325                 throw new IllegalStateException("Request must target at least one output surface!");
    326             }
    327 
    328             if (h.needsJpeg) {
    329                 // Wait for all current requests to finish before queueing jpeg.
    330                 while (mInFlight > 0) {
    331                     if (nanos <= 0) {
    332                         return false;
    333                     }
    334                     nanos = mIsEmpty.awaitNanos(nanos);
    335                 }
    336                 mJpegCaptureQueue.add(h);
    337                 mJpegProduceQueue.add(h);
    338             }
    339             if (h.needsPreview) {
    340                 while (mInFlight >= mMaxInFlight) {
    341                     if (nanos <= 0) {
    342                         return false;
    343                     }
    344                     nanos = mNotFull.awaitNanos(nanos);
    345                 }
    346                 mPreviewCaptureQueue.add(h);
    347                 mPreviewProduceQueue.add(h);
    348                 mInFlightPreviews++;
    349             }
    350             mActiveRequests.add(h);
    351 
    352             mInFlight++;
    353             return true;
    354         } finally {
    355             lock.unlock();
    356         }
    357     }
    358 
    359     /**
    360      * Wait all queued requests to complete.
    361      *
    362      * @param timeout a timeout to use for this call.
    363      * @param unit the units to use for the timeout.
    364      * @return {@code false} if this method timed out.
    365      * @throws InterruptedException if this thread is interrupted.
    366      */
    367     public boolean waitForEmpty(long timeout, TimeUnit unit) throws InterruptedException {
    368         long nanos = unit.toNanos(timeout);
    369         final ReentrantLock lock = this.mLock;
    370         lock.lock();
    371         try {
    372             while (mInFlight > 0) {
    373                 if (nanos <= 0) {
    374                     return false;
    375                 }
    376                 nanos = mIsEmpty.awaitNanos(nanos);
    377             }
    378             return true;
    379         } finally {
    380             lock.unlock();
    381         }
    382     }
    383 
    384     /**
    385      * Wait all queued requests that use the Camera1 API preview output to complete.
    386      *
    387      * @param timeout a timeout to use for this call.
    388      * @param unit the units to use for the timeout.
    389      * @return {@code false} if this method timed out.
    390      * @throws InterruptedException if this thread is interrupted.
    391      */
    392     public boolean waitForPreviewsEmpty(long timeout, TimeUnit unit) throws InterruptedException {
    393         long nanos = unit.toNanos(timeout);
    394         final ReentrantLock lock = this.mLock;
    395         lock.lock();
    396         try {
    397             while (mInFlightPreviews > 0) {
    398                 if (nanos <= 0) {
    399                     return false;
    400                 }
    401                 nanos = mPreviewsEmpty.awaitNanos(nanos);
    402             }
    403             return true;
    404         } finally {
    405             lock.unlock();
    406         }
    407     }
    408 
    409     /**
    410      * Wait for the specified request to be completed (all buffers available).
    411      *
    412      * <p>May not wait for the same request more than once, since a successful wait
    413      * will erase the history of that request.</p>
    414      *
    415      * @param holder the {@link RequestHolder} for this request.
    416      * @param timeout a timeout to use for this call.
    417      * @param unit the units to use for the timeout.
    418      * @param timestamp the timestamp of the request will be written out to here, in ns
    419      *
    420      * @return {@code false} if this method timed out.
    421      *
    422      * @throws InterruptedException if this thread is interrupted.
    423      */
    424     public boolean waitForRequestCompleted(RequestHolder holder, long timeout, TimeUnit unit,
    425             MutableLong timestamp)
    426             throws InterruptedException {
    427         long nanos = unit.toNanos(timeout);
    428         final ReentrantLock lock = this.mLock;
    429         lock.lock();
    430         try {
    431             while (!removeRequestIfCompleted(holder, /*out*/timestamp)) {
    432                 if (nanos <= 0) {
    433                     return false;
    434                 }
    435                 nanos = mNotFull.awaitNanos(nanos);
    436             }
    437             return true;
    438         } finally {
    439             lock.unlock();
    440         }
    441     }
    442 
    443     private boolean removeRequestIfCompleted(RequestHolder holder, MutableLong timestamp) {
    444         int i = 0;
    445         for (CaptureHolder h : mCompletedRequests) {
    446             if (h.mRequest.equals(holder)) {
    447                 timestamp.value = h.mTimestamp;
    448                 mCompletedRequests.remove(i);
    449                 return true;
    450             }
    451             i++;
    452         }
    453 
    454         return false;
    455     }
    456 
    457     /**
    458      * Called to alert the {@link CaptureCollector} that the jpeg capture has begun.
    459      *
    460      * @param timestamp the time of the jpeg capture.
    461      * @return the {@link RequestHolder} for the request associated with this capture.
    462      */
    463     public RequestHolder jpegCaptured(long timestamp) {
    464         final ReentrantLock lock = this.mLock;
    465         lock.lock();
    466         try {
    467             CaptureHolder h = mJpegCaptureQueue.poll();
    468             if (h == null) {
    469                 Log.w(TAG, "jpegCaptured called with no jpeg request on queue!");
    470                 return null;
    471             }
    472             h.setJpegTimestamp(timestamp);
    473             return h.mRequest;
    474         } finally {
    475             lock.unlock();
    476         }
    477     }
    478 
    479     /**
    480      * Called to alert the {@link CaptureCollector} that the jpeg capture has completed.
    481      *
    482      * @return a pair containing the {@link RequestHolder} and the timestamp of the capture.
    483      */
    484     public Pair<RequestHolder, Long> jpegProduced() {
    485         final ReentrantLock lock = this.mLock;
    486         lock.lock();
    487         try {
    488             CaptureHolder h = mJpegProduceQueue.poll();
    489             if (h == null) {
    490                 Log.w(TAG, "jpegProduced called with no jpeg request on queue!");
    491                 return null;
    492             }
    493             h.setJpegProduced();
    494             return new Pair<>(h.mRequest, h.mTimestamp);
    495         } finally {
    496             lock.unlock();
    497         }
    498     }
    499 
    500     /**
    501      * Check if there are any pending capture requests that use the Camera1 API preview output.
    502      *
    503      * @return {@code true} if there are pending preview requests.
    504      */
    505     public boolean hasPendingPreviewCaptures() {
    506         final ReentrantLock lock = this.mLock;
    507         lock.lock();
    508         try {
    509             return !mPreviewCaptureQueue.isEmpty();
    510         } finally {
    511             lock.unlock();
    512         }
    513     }
    514 
    515     /**
    516      * Called to alert the {@link CaptureCollector} that the preview capture has begun.
    517      *
    518      * @param timestamp the time of the preview capture.
    519      * @return a pair containing the {@link RequestHolder} and the timestamp of the capture.
    520      */
    521     public Pair<RequestHolder, Long> previewCaptured(long timestamp) {
    522         final ReentrantLock lock = this.mLock;
    523         lock.lock();
    524         try {
    525             CaptureHolder h = mPreviewCaptureQueue.poll();
    526             if (h == null) {
    527                 if (DEBUG) {
    528                     Log.d(TAG, "previewCaptured called with no preview request on queue!");
    529                 }
    530                 return null;
    531             }
    532             h.setPreviewTimestamp(timestamp);
    533             return new Pair<>(h.mRequest, h.mTimestamp);
    534         } finally {
    535             lock.unlock();
    536         }
    537     }
    538 
    539     /**
    540      * Called to alert the {@link CaptureCollector} that the preview capture has completed.
    541      *
    542      * @return the {@link RequestHolder} for the request associated with this capture.
    543      */
    544     public RequestHolder previewProduced() {
    545         final ReentrantLock lock = this.mLock;
    546         lock.lock();
    547         try {
    548             CaptureHolder h = mPreviewProduceQueue.poll();
    549             if (h == null) {
    550                 Log.w(TAG, "previewProduced called with no preview request on queue!");
    551                 return null;
    552             }
    553             h.setPreviewProduced();
    554             return h.mRequest;
    555         } finally {
    556             lock.unlock();
    557         }
    558     }
    559 
    560     /**
    561      * Called to alert the {@link CaptureCollector} that the next pending preview capture has failed.
    562      */
    563     public void failNextPreview() {
    564         final ReentrantLock lock = this.mLock;
    565         lock.lock();
    566         try {
    567             CaptureHolder h1 = mPreviewCaptureQueue.peek();
    568             CaptureHolder h2 = mPreviewProduceQueue.peek();
    569 
    570             // Find the request with the lowest frame number.
    571             CaptureHolder h = (h1 == null) ? h2 :
    572                               ((h2 == null) ? h1 :
    573                               ((h1.compareTo(h2) <= 0) ? h1 :
    574                               h2));
    575 
    576             if (h != null) {
    577                 mPreviewCaptureQueue.remove(h);
    578                 mPreviewProduceQueue.remove(h);
    579                 mActiveRequests.remove(h);
    580                 h.setPreviewFailed();
    581             }
    582         } finally {
    583             lock.unlock();
    584         }
    585     }
    586 
    587     /**
    588      * Called to alert the {@link CaptureCollector} that the next pending jpeg capture has failed.
    589      */
    590     public void failNextJpeg() {
    591         final ReentrantLock lock = this.mLock;
    592         lock.lock();
    593         try {
    594             CaptureHolder h1 = mJpegCaptureQueue.peek();
    595             CaptureHolder h2 = mJpegProduceQueue.peek();
    596 
    597             // Find the request with the lowest frame number.
    598             CaptureHolder h = (h1 == null) ? h2 :
    599                               ((h2 == null) ? h1 :
    600                               ((h1.compareTo(h2) <= 0) ? h1 :
    601                               h2));
    602 
    603             if (h != null) {
    604                 mJpegCaptureQueue.remove(h);
    605                 mJpegProduceQueue.remove(h);
    606                 mActiveRequests.remove(h);
    607                 h.setJpegFailed();
    608             }
    609         } finally {
    610             lock.unlock();
    611         }
    612     }
    613 
    614     /**
    615      * Called to alert the {@link CaptureCollector} all pending captures have failed.
    616      */
    617     public void failAll() {
    618         final ReentrantLock lock = this.mLock;
    619         lock.lock();
    620         try {
    621             CaptureHolder h;
    622             while ((h = mActiveRequests.pollFirst()) != null) {
    623                 h.setPreviewFailed();
    624                 h.setJpegFailed();
    625             }
    626             mPreviewCaptureQueue.clear();
    627             mPreviewProduceQueue.clear();
    628             mJpegCaptureQueue.clear();
    629             mJpegProduceQueue.clear();
    630         } finally {
    631             lock.unlock();
    632         }
    633     }
    634 
    635     private void onPreviewCompleted() {
    636         mInFlightPreviews--;
    637         if (mInFlightPreviews < 0) {
    638             throw new IllegalStateException(
    639                     "More preview captures completed than requests queued.");
    640         }
    641         if (mInFlightPreviews == 0) {
    642             mPreviewsEmpty.signalAll();
    643         }
    644     }
    645 
    646     private void onRequestCompleted(CaptureHolder capture) {
    647         RequestHolder request = capture.mRequest;
    648 
    649         mInFlight--;
    650         if (DEBUG) {
    651             Log.d(TAG, "Completed request " + request.getRequestId() +
    652                     ", " + mInFlight + " requests remain in flight.");
    653         }
    654         if (mInFlight < 0) {
    655             throw new IllegalStateException(
    656                     "More captures completed than requests queued.");
    657         }
    658 
    659         mCompletedRequests.add(capture);
    660         mActiveRequests.remove(capture);
    661 
    662         mNotFull.signalAll();
    663         if (mInFlight == 0) {
    664             mIsEmpty.signalAll();
    665         }
    666     }
    667 }
    668