blob: 113927c4c4b2ff4ad7087c43e74b498f2b7a396f [file] [log] [blame]
Ruben Brunk91838de2014-07-16 17:24:17 -07001/*
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 */
16package android.hardware.camera2.legacy;
17
Ruben Brunke663cb772014-09-16 13:18:31 -070018import android.hardware.camera2.impl.CameraDeviceImpl;
Ruben Brunk91838de2014-07-16 17:24:17 -070019import android.util.Log;
Igor Murashkin83d86392014-07-18 14:37:19 -070020import android.util.MutableLong;
Ruben Brunk91838de2014-07-16 17:24:17 -070021import android.util.Pair;
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -070022import android.view.Surface;
Ruben Brunk91838de2014-07-16 17:24:17 -070023import java.util.ArrayDeque;
Igor Murashkin83d86392014-07-18 14:37:19 -070024import java.util.ArrayList;
Ruben Brunke663cb772014-09-16 13:18:31 -070025import java.util.TreeSet;
Ruben Brunk91838de2014-07-16 17:24:17 -070026import java.util.concurrent.TimeUnit;
27import java.util.concurrent.locks.Condition;
28import 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 */
34public class CaptureCollector {
35 private static final String TAG = "CaptureCollector";
36
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070037 private static final boolean DEBUG = false;
Ruben Brunk91838de2014-07-16 17:24:17 -070038
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
Ruben Brunke663cb772014-09-16 13:18:31 -070049 private class CaptureHolder implements Comparable<CaptureHolder>{
Ruben Brunk91838de2014-07-16 17:24:17 -070050 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;
Ruben Brunke663cb772014-09-16 13:18:31 -070058 private boolean mFailedJpeg = false;
59 private boolean mFailedPreview = false;
60 private boolean mCompleted = false;
61 private boolean mPreviewCompleted = false;
Ruben Brunk91838de2014-07-16 17:24:17 -070062
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() {
Ruben Brunke663cb772014-09-16 13:18:31 -070083 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.
Eino-Ville Talvala385f9e22016-03-31 16:47:14 -070098 for (Surface targetSurface : mRequest.getRequest().getTargets() ) {
99 try {
100 if (mRequest.jpegType(targetSurface)) {
101 if (mFailedJpeg) {
102 CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
103 /*result*/null,
104 CameraDeviceImpl.CameraDeviceCallbacks.
105 ERROR_CAMERA_BUFFER,
106 targetSurface);
107 }
108 } else {
109 // preview buffer
110 if (mFailedPreview) {
111 CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
112 /*result*/null,
113 CameraDeviceImpl.CameraDeviceCallbacks.
114 ERROR_CAMERA_BUFFER,
115 targetSurface);
116 }
117 }
118 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
119 Log.e(TAG, "Unexpected exception when querying Surface: " + e);
Ruben Brunke663cb772014-09-16 13:18:31 -0700120 }
121 }
122 }
Ruben Brunk971251f2014-07-25 15:12:33 -0700123 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700124 CaptureCollector.this.onRequestCompleted(CaptureHolder.this);
125 mCompleted = true;
Ruben Brunk91838de2014-07-16 17:24:17 -0700126 }
127 }
128
129 public void setJpegTimestamp(long timestamp) {
130 if (DEBUG) {
131 Log.d(TAG, "setJpegTimestamp - called for request " + mRequest.getRequestId());
132 }
133 if (!needsJpeg) {
134 throw new IllegalStateException(
135 "setJpegTimestamp called for capture with no jpeg targets.");
136 }
137 if (isCompleted()) {
138 throw new IllegalStateException(
139 "setJpegTimestamp called on already completed request.");
140 }
141
142 mReceivedFlags |= FLAG_RECEIVED_JPEG_TS;
143
144 if (mTimestamp == 0) {
145 mTimestamp = timestamp;
146 }
147
148 if (!mHasStarted) {
149 mHasStarted = true;
Ruben Brunke663cb772014-09-16 13:18:31 -0700150 CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
151 CameraDeviceState.NO_CAPTURE_ERROR);
Ruben Brunk91838de2014-07-16 17:24:17 -0700152 }
153
154 tryComplete();
155 }
156
157 public void setJpegProduced() {
158 if (DEBUG) {
159 Log.d(TAG, "setJpegProduced - called for request " + mRequest.getRequestId());
160 }
161 if (!needsJpeg) {
162 throw new IllegalStateException(
163 "setJpegProduced called for capture with no jpeg targets.");
164 }
165 if (isCompleted()) {
166 throw new IllegalStateException(
167 "setJpegProduced called on already completed request.");
168 }
169
170 mReceivedFlags |= FLAG_RECEIVED_JPEG;
171 tryComplete();
172 }
173
Ruben Brunke663cb772014-09-16 13:18:31 -0700174 public void setJpegFailed() {
175 if (DEBUG) {
176 Log.d(TAG, "setJpegFailed - called for request " + mRequest.getRequestId());
177 }
178 if (!needsJpeg || isJpegCompleted()) {
179 return;
180 }
181 mFailedJpeg = true;
182
183 mReceivedFlags |= FLAG_RECEIVED_JPEG;
184 mReceivedFlags |= FLAG_RECEIVED_JPEG_TS;
185 tryComplete();
186 }
187
Ruben Brunk91838de2014-07-16 17:24:17 -0700188 public void setPreviewTimestamp(long timestamp) {
189 if (DEBUG) {
190 Log.d(TAG, "setPreviewTimestamp - called for request " + mRequest.getRequestId());
191 }
192 if (!needsPreview) {
193 throw new IllegalStateException(
194 "setPreviewTimestamp called for capture with no preview targets.");
195 }
196 if (isCompleted()) {
197 throw new IllegalStateException(
198 "setPreviewTimestamp called on already completed request.");
199 }
200
201 mReceivedFlags |= FLAG_RECEIVED_PREVIEW_TS;
202
203 if (mTimestamp == 0) {
204 mTimestamp = timestamp;
205 }
206
207 if (!needsJpeg) {
208 if (!mHasStarted) {
209 mHasStarted = true;
Ruben Brunke663cb772014-09-16 13:18:31 -0700210 CaptureCollector.this.mDeviceState.setCaptureStart(mRequest, mTimestamp,
211 CameraDeviceState.NO_CAPTURE_ERROR);
Ruben Brunk91838de2014-07-16 17:24:17 -0700212 }
213 }
214
215 tryComplete();
216 }
217
218 public void setPreviewProduced() {
219 if (DEBUG) {
220 Log.d(TAG, "setPreviewProduced - called for request " + mRequest.getRequestId());
221 }
222 if (!needsPreview) {
223 throw new IllegalStateException(
224 "setPreviewProduced called for capture with no preview targets.");
225 }
226 if (isCompleted()) {
227 throw new IllegalStateException(
228 "setPreviewProduced called on already completed request.");
229 }
230
231 mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
232 tryComplete();
233 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700234
235 public void setPreviewFailed() {
236 if (DEBUG) {
237 Log.d(TAG, "setPreviewFailed - called for request " + mRequest.getRequestId());
238 }
239 if (!needsPreview || isPreviewCompleted()) {
240 return;
241 }
242 mFailedPreview = true;
243
244 mReceivedFlags |= FLAG_RECEIVED_PREVIEW;
245 mReceivedFlags |= FLAG_RECEIVED_PREVIEW_TS;
246 tryComplete();
247 }
248
249 // Comparison and equals based on frame number.
250 @Override
251 public int compareTo(CaptureHolder captureHolder) {
252 return (mRequest.getFrameNumber() > captureHolder.mRequest.getFrameNumber()) ? 1 :
253 ((mRequest.getFrameNumber() == captureHolder.mRequest.getFrameNumber()) ? 0 :
254 -1);
255 }
256
257 // Comparison and equals based on frame number.
258 @Override
259 public boolean equals(Object o) {
260 return o instanceof CaptureHolder && compareTo((CaptureHolder) o) == 0;
261 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700262 }
263
Ruben Brunke663cb772014-09-16 13:18:31 -0700264 private final TreeSet<CaptureHolder> mActiveRequests;
Ruben Brunk91838de2014-07-16 17:24:17 -0700265 private final ArrayDeque<CaptureHolder> mJpegCaptureQueue;
266 private final ArrayDeque<CaptureHolder> mJpegProduceQueue;
267 private final ArrayDeque<CaptureHolder> mPreviewCaptureQueue;
268 private final ArrayDeque<CaptureHolder> mPreviewProduceQueue;
Igor Murashkin83d86392014-07-18 14:37:19 -0700269 private final ArrayList<CaptureHolder> mCompletedRequests = new ArrayList<>();
Ruben Brunk91838de2014-07-16 17:24:17 -0700270
271 private final ReentrantLock mLock = new ReentrantLock();
272 private final Condition mIsEmpty;
273 private final Condition mPreviewsEmpty;
274 private final Condition mNotFull;
275 private final CameraDeviceState mDeviceState;
Ruben Brunk91838de2014-07-16 17:24:17 -0700276 private int mInFlight = 0;
277 private int mInFlightPreviews = 0;
278 private final int mMaxInFlight;
279
280 /**
281 * Create a new {@link CaptureCollector} that can modify the given {@link CameraDeviceState}.
282 *
283 * @param maxInFlight max allowed in-flight requests.
284 * @param deviceState the {@link CameraDeviceState} to update as requests are processed.
285 */
286 public CaptureCollector(int maxInFlight, CameraDeviceState deviceState) {
287 mMaxInFlight = maxInFlight;
288 mJpegCaptureQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
289 mJpegProduceQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
290 mPreviewCaptureQueue = new ArrayDeque<>(mMaxInFlight);
291 mPreviewProduceQueue = new ArrayDeque<>(mMaxInFlight);
Ruben Brunke663cb772014-09-16 13:18:31 -0700292 mActiveRequests = new TreeSet<>();
Ruben Brunk91838de2014-07-16 17:24:17 -0700293 mIsEmpty = mLock.newCondition();
294 mNotFull = mLock.newCondition();
295 mPreviewsEmpty = mLock.newCondition();
296 mDeviceState = deviceState;
297 }
298
299 /**
300 * Queue a new request.
301 *
302 * <p>
303 * For requests that use the Camera1 API preview output stream, this will block if there are
304 * already {@code maxInFlight} requests in progress (until at least one prior request has
305 * completed). For requests that use the Camera1 API jpeg callbacks, this will block until
306 * all prior requests have been completed to avoid stopping preview for
307 * {@link android.hardware.Camera#takePicture} before prior preview requests have been
308 * completed.
309 * </p>
310 * @param holder the {@link RequestHolder} for this request.
311 * @param legacy the {@link LegacyRequest} for this request; this will not be mutated.
312 * @param timeout a timeout to use for this call.
313 * @param unit the units to use for the timeout.
314 * @return {@code false} if this method timed out.
315 * @throws InterruptedException if this thread is interrupted.
316 */
317 public boolean queueRequest(RequestHolder holder, LegacyRequest legacy, long timeout,
318 TimeUnit unit)
319 throws InterruptedException {
320 CaptureHolder h = new CaptureHolder(holder, legacy);
321 long nanos = unit.toNanos(timeout);
322 final ReentrantLock lock = this.mLock;
323 lock.lock();
324 try {
325 if (DEBUG) {
326 Log.d(TAG, "queueRequest for request " + holder.getRequestId() +
327 " - " + mInFlight + " requests remain in flight.");
328 }
Ruben Brunk3fe9eba2014-07-24 13:33:47 -0700329
330 if (!(h.needsJpeg || h.needsPreview)) {
331 throw new IllegalStateException("Request must target at least one output surface!");
332 }
333
Ruben Brunk91838de2014-07-16 17:24:17 -0700334 if (h.needsJpeg) {
335 // Wait for all current requests to finish before queueing jpeg.
336 while (mInFlight > 0) {
337 if (nanos <= 0) {
338 return false;
339 }
340 nanos = mIsEmpty.awaitNanos(nanos);
341 }
342 mJpegCaptureQueue.add(h);
343 mJpegProduceQueue.add(h);
344 }
345 if (h.needsPreview) {
346 while (mInFlight >= mMaxInFlight) {
347 if (nanos <= 0) {
348 return false;
349 }
350 nanos = mNotFull.awaitNanos(nanos);
351 }
352 mPreviewCaptureQueue.add(h);
353 mPreviewProduceQueue.add(h);
354 mInFlightPreviews++;
355 }
Ruben Brunke663cb772014-09-16 13:18:31 -0700356 mActiveRequests.add(h);
Ruben Brunk91838de2014-07-16 17:24:17 -0700357
358 mInFlight++;
359 return true;
360 } finally {
361 lock.unlock();
362 }
363 }
364
365 /**
366 * Wait all queued requests to complete.
367 *
368 * @param timeout a timeout to use for this call.
369 * @param unit the units to use for the timeout.
370 * @return {@code false} if this method timed out.
371 * @throws InterruptedException if this thread is interrupted.
372 */
373 public boolean waitForEmpty(long timeout, TimeUnit unit) throws InterruptedException {
374 long nanos = unit.toNanos(timeout);
375 final ReentrantLock lock = this.mLock;
376 lock.lock();
377 try {
378 while (mInFlight > 0) {
379 if (nanos <= 0) {
380 return false;
381 }
382 nanos = mIsEmpty.awaitNanos(nanos);
383 }
384 return true;
385 } finally {
386 lock.unlock();
387 }
388 }
389
390 /**
391 * Wait all queued requests that use the Camera1 API preview output to complete.
392 *
393 * @param timeout a timeout to use for this call.
394 * @param unit the units to use for the timeout.
395 * @return {@code false} if this method timed out.
396 * @throws InterruptedException if this thread is interrupted.
397 */
398 public boolean waitForPreviewsEmpty(long timeout, TimeUnit unit) throws InterruptedException {
399 long nanos = unit.toNanos(timeout);
400 final ReentrantLock lock = this.mLock;
401 lock.lock();
402 try {
403 while (mInFlightPreviews > 0) {
404 if (nanos <= 0) {
405 return false;
406 }
407 nanos = mPreviewsEmpty.awaitNanos(nanos);
408 }
409 return true;
410 } finally {
411 lock.unlock();
412 }
413 }
414
415 /**
Igor Murashkin83d86392014-07-18 14:37:19 -0700416 * Wait for the specified request to be completed (all buffers available).
417 *
418 * <p>May not wait for the same request more than once, since a successful wait
419 * will erase the history of that request.</p>
420 *
421 * @param holder the {@link RequestHolder} for this request.
422 * @param timeout a timeout to use for this call.
423 * @param unit the units to use for the timeout.
424 * @param timestamp the timestamp of the request will be written out to here, in ns
425 *
426 * @return {@code false} if this method timed out.
427 *
428 * @throws InterruptedException if this thread is interrupted.
429 */
430 public boolean waitForRequestCompleted(RequestHolder holder, long timeout, TimeUnit unit,
431 MutableLong timestamp)
432 throws InterruptedException {
433 long nanos = unit.toNanos(timeout);
434 final ReentrantLock lock = this.mLock;
435 lock.lock();
436 try {
437 while (!removeRequestIfCompleted(holder, /*out*/timestamp)) {
438 if (nanos <= 0) {
439 return false;
440 }
441 nanos = mNotFull.awaitNanos(nanos);
442 }
443 return true;
444 } finally {
445 lock.unlock();
446 }
447 }
448
449 private boolean removeRequestIfCompleted(RequestHolder holder, MutableLong timestamp) {
450 int i = 0;
451 for (CaptureHolder h : mCompletedRequests) {
452 if (h.mRequest.equals(holder)) {
453 timestamp.value = h.mTimestamp;
454 mCompletedRequests.remove(i);
455 return true;
456 }
457 i++;
458 }
459
460 return false;
461 }
462
463 /**
Ruben Brunk91838de2014-07-16 17:24:17 -0700464 * Called to alert the {@link CaptureCollector} that the jpeg capture has begun.
465 *
466 * @param timestamp the time of the jpeg capture.
467 * @return the {@link RequestHolder} for the request associated with this capture.
468 */
469 public RequestHolder jpegCaptured(long timestamp) {
470 final ReentrantLock lock = this.mLock;
471 lock.lock();
472 try {
473 CaptureHolder h = mJpegCaptureQueue.poll();
474 if (h == null) {
475 Log.w(TAG, "jpegCaptured called with no jpeg request on queue!");
476 return null;
477 }
478 h.setJpegTimestamp(timestamp);
479 return h.mRequest;
480 } finally {
481 lock.unlock();
482 }
483 }
484
485 /**
486 * Called to alert the {@link CaptureCollector} that the jpeg capture has completed.
487 *
488 * @return a pair containing the {@link RequestHolder} and the timestamp of the capture.
489 */
490 public Pair<RequestHolder, Long> jpegProduced() {
491 final ReentrantLock lock = this.mLock;
492 lock.lock();
493 try {
494 CaptureHolder h = mJpegProduceQueue.poll();
495 if (h == null) {
496 Log.w(TAG, "jpegProduced called with no jpeg request on queue!");
497 return null;
498 }
499 h.setJpegProduced();
500 return new Pair<>(h.mRequest, h.mTimestamp);
501 } finally {
502 lock.unlock();
503 }
504 }
505
506 /**
507 * Check if there are any pending capture requests that use the Camera1 API preview output.
508 *
509 * @return {@code true} if there are pending preview requests.
510 */
511 public boolean hasPendingPreviewCaptures() {
512 final ReentrantLock lock = this.mLock;
513 lock.lock();
514 try {
515 return !mPreviewCaptureQueue.isEmpty();
516 } finally {
517 lock.unlock();
518 }
519 }
520
521 /**
522 * Called to alert the {@link CaptureCollector} that the preview capture has begun.
523 *
524 * @param timestamp the time of the preview capture.
525 * @return a pair containing the {@link RequestHolder} and the timestamp of the capture.
526 */
527 public Pair<RequestHolder, Long> previewCaptured(long timestamp) {
528 final ReentrantLock lock = this.mLock;
529 lock.lock();
530 try {
531 CaptureHolder h = mPreviewCaptureQueue.poll();
532 if (h == null) {
Ruben Brunke663cb772014-09-16 13:18:31 -0700533 if (DEBUG) {
534 Log.d(TAG, "previewCaptured called with no preview request on queue!");
535 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700536 return null;
537 }
538 h.setPreviewTimestamp(timestamp);
539 return new Pair<>(h.mRequest, h.mTimestamp);
540 } finally {
541 lock.unlock();
542 }
543 }
544
545 /**
546 * Called to alert the {@link CaptureCollector} that the preview capture has completed.
547 *
548 * @return the {@link RequestHolder} for the request associated with this capture.
549 */
550 public RequestHolder previewProduced() {
551 final ReentrantLock lock = this.mLock;
552 lock.lock();
553 try {
554 CaptureHolder h = mPreviewProduceQueue.poll();
555 if (h == null) {
556 Log.w(TAG, "previewProduced called with no preview request on queue!");
557 return null;
558 }
559 h.setPreviewProduced();
560 return h.mRequest;
561 } finally {
562 lock.unlock();
563 }
564 }
565
Ruben Brunke663cb772014-09-16 13:18:31 -0700566 /**
567 * Called to alert the {@link CaptureCollector} that the next pending preview capture has failed.
568 */
569 public void failNextPreview() {
570 final ReentrantLock lock = this.mLock;
571 lock.lock();
572 try {
573 CaptureHolder h1 = mPreviewCaptureQueue.peek();
574 CaptureHolder h2 = mPreviewProduceQueue.peek();
575
576 // Find the request with the lowest frame number.
577 CaptureHolder h = (h1 == null) ? h2 :
578 ((h2 == null) ? h1 :
579 ((h1.compareTo(h2) <= 0) ? h1 :
580 h2));
581
582 if (h != null) {
583 mPreviewCaptureQueue.remove(h);
584 mPreviewProduceQueue.remove(h);
585 mActiveRequests.remove(h);
586 h.setPreviewFailed();
587 }
588 } finally {
589 lock.unlock();
590 }
591 }
592
593 /**
594 * Called to alert the {@link CaptureCollector} that the next pending jpeg capture has failed.
595 */
596 public void failNextJpeg() {
597 final ReentrantLock lock = this.mLock;
598 lock.lock();
599 try {
600 CaptureHolder h1 = mJpegCaptureQueue.peek();
601 CaptureHolder h2 = mJpegProduceQueue.peek();
602
603 // Find the request with the lowest frame number.
604 CaptureHolder h = (h1 == null) ? h2 :
605 ((h2 == null) ? h1 :
606 ((h1.compareTo(h2) <= 0) ? h1 :
607 h2));
608
609 if (h != null) {
610 mJpegCaptureQueue.remove(h);
611 mJpegProduceQueue.remove(h);
612 mActiveRequests.remove(h);
613 h.setJpegFailed();
614 }
615 } finally {
616 lock.unlock();
617 }
618 }
619
620 /**
621 * Called to alert the {@link CaptureCollector} all pending captures have failed.
622 */
623 public void failAll() {
624 final ReentrantLock lock = this.mLock;
625 lock.lock();
626 try {
627 CaptureHolder h;
628 while ((h = mActiveRequests.pollFirst()) != null) {
629 h.setPreviewFailed();
630 h.setJpegFailed();
631 }
632 mPreviewCaptureQueue.clear();
633 mPreviewProduceQueue.clear();
634 mJpegCaptureQueue.clear();
635 mJpegProduceQueue.clear();
636 } finally {
637 lock.unlock();
638 }
639 }
640
Ruben Brunk91838de2014-07-16 17:24:17 -0700641 private void onPreviewCompleted() {
642 mInFlightPreviews--;
643 if (mInFlightPreviews < 0) {
644 throw new IllegalStateException(
645 "More preview captures completed than requests queued.");
646 }
647 if (mInFlightPreviews == 0) {
648 mPreviewsEmpty.signalAll();
649 }
650 }
651
Igor Murashkin83d86392014-07-18 14:37:19 -0700652 private void onRequestCompleted(CaptureHolder capture) {
653 RequestHolder request = capture.mRequest;
654
Ruben Brunk91838de2014-07-16 17:24:17 -0700655 mInFlight--;
656 if (DEBUG) {
657 Log.d(TAG, "Completed request " + request.getRequestId() +
658 ", " + mInFlight + " requests remain in flight.");
659 }
660 if (mInFlight < 0) {
661 throw new IllegalStateException(
662 "More captures completed than requests queued.");
663 }
Igor Murashkin83d86392014-07-18 14:37:19 -0700664
665 mCompletedRequests.add(capture);
Ruben Brunke663cb772014-09-16 13:18:31 -0700666 mActiveRequests.remove(capture);
Igor Murashkin83d86392014-07-18 14:37:19 -0700667
Ruben Brunk91838de2014-07-16 17:24:17 -0700668 mNotFull.signalAll();
669 if (mInFlight == 0) {
670 mIsEmpty.signalAll();
671 }
Ruben Brunk91838de2014-07-16 17:24:17 -0700672 }
673}