FileDocCategorySizeDatePackage
CaptureCollector.javaAPI DocAndroid 5.1 API24343Thu Mar 12 22:22:10 GMT 2015android.hardware.camera2.legacy

CaptureCollector

public class CaptureCollector extends Object
Collect timestamps and state for each {@link CaptureRequest} as it passes through the Legacy camera pipeline.

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final int
FLAG_RECEIVED_JPEG
private static final int
FLAG_RECEIVED_JPEG_TS
private static final int
FLAG_RECEIVED_PREVIEW
private static final int
FLAG_RECEIVED_PREVIEW_TS
private static final int
FLAG_RECEIVED_ALL_JPEG
private static final int
FLAG_RECEIVED_ALL_PREVIEW
private static final int
MAX_JPEGS_IN_FLIGHT
private final TreeSet
mActiveRequests
private final ArrayDeque
mJpegCaptureQueue
private final ArrayDeque
mJpegProduceQueue
private final ArrayDeque
mPreviewCaptureQueue
private final ArrayDeque
mPreviewProduceQueue
private final ArrayList
mCompletedRequests
private final ReentrantLock
mLock
private final Condition
mIsEmpty
private final Condition
mPreviewsEmpty
private final Condition
mNotFull
private final CameraDeviceState
mDeviceState
private int
mInFlight
private int
mInFlightPreviews
private final int
mMaxInFlight
Constructors Summary
public CaptureCollector(int maxInFlight, CameraDeviceState deviceState)
Create a new {@link CaptureCollector} that can modify the given {@link CameraDeviceState}.

param
maxInFlight max allowed in-flight requests.
param
deviceState the {@link CameraDeviceState} to update as requests are processed.


                                      
         
        mMaxInFlight = maxInFlight;
        mJpegCaptureQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
        mJpegProduceQueue = new ArrayDeque<>(MAX_JPEGS_IN_FLIGHT);
        mPreviewCaptureQueue = new ArrayDeque<>(mMaxInFlight);
        mPreviewProduceQueue = new ArrayDeque<>(mMaxInFlight);
        mActiveRequests = new TreeSet<>();
        mIsEmpty = mLock.newCondition();
        mNotFull = mLock.newCondition();
        mPreviewsEmpty = mLock.newCondition();
        mDeviceState = deviceState;
    
Methods Summary
public voidfailAll()
Called to alert the {@link CaptureCollector} all pending captures have failed.

        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h;
            while ((h = mActiveRequests.pollFirst()) != null) {
                h.setPreviewFailed();
                h.setJpegFailed();
            }
            mPreviewCaptureQueue.clear();
            mPreviewProduceQueue.clear();
            mJpegCaptureQueue.clear();
            mJpegProduceQueue.clear();
        } finally {
            lock.unlock();
        }
    
public voidfailNextJpeg()
Called to alert the {@link CaptureCollector} that the next pending jpeg capture has failed.

        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h1 = mJpegCaptureQueue.peek();
            CaptureHolder h2 = mJpegProduceQueue.peek();

            // Find the request with the lowest frame number.
            CaptureHolder h = (h1 == null) ? h2 :
                              ((h2 == null) ? h1 :
                              ((h1.compareTo(h2) <= 0) ? h1 :
                              h2));

            if (h != null) {
                mJpegCaptureQueue.remove(h);
                mJpegProduceQueue.remove(h);
                mActiveRequests.remove(h);
                h.setJpegFailed();
            }
        } finally {
            lock.unlock();
        }
    
public voidfailNextPreview()
Called to alert the {@link CaptureCollector} that the next pending preview capture has failed.

        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h1 = mPreviewCaptureQueue.peek();
            CaptureHolder h2 = mPreviewProduceQueue.peek();

            // Find the request with the lowest frame number.
            CaptureHolder h = (h1 == null) ? h2 :
                              ((h2 == null) ? h1 :
                              ((h1.compareTo(h2) <= 0) ? h1 :
                              h2));

            if (h != null) {
                mPreviewCaptureQueue.remove(h);
                mPreviewProduceQueue.remove(h);
                mActiveRequests.remove(h);
                h.setPreviewFailed();
            }
        } finally {
            lock.unlock();
        }
    
public booleanhasPendingPreviewCaptures()
Check if there are any pending capture requests that use the Camera1 API preview output.

return
{@code true} if there are pending preview requests.

        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            return !mPreviewCaptureQueue.isEmpty();
        } finally {
            lock.unlock();
        }
    
public RequestHolderjpegCaptured(long timestamp)
Called to alert the {@link CaptureCollector} that the jpeg capture has begun.

param
timestamp the time of the jpeg capture.
return
the {@link RequestHolder} for the request associated with this capture.

        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h = mJpegCaptureQueue.poll();
            if (h == null) {
                Log.w(TAG, "jpegCaptured called with no jpeg request on queue!");
                return null;
            }
            h.setJpegTimestamp(timestamp);
            return h.mRequest;
        } finally {
            lock.unlock();
        }
    
public android.util.PairjpegProduced()
Called to alert the {@link CaptureCollector} that the jpeg capture has completed.

return
a pair containing the {@link RequestHolder} and the timestamp of the capture.

        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h = mJpegProduceQueue.poll();
            if (h == null) {
                Log.w(TAG, "jpegProduced called with no jpeg request on queue!");
                return null;
            }
            h.setJpegProduced();
            return new Pair<>(h.mRequest, h.mTimestamp);
        } finally {
            lock.unlock();
        }
    
private voidonPreviewCompleted()

        mInFlightPreviews--;
        if (mInFlightPreviews < 0) {
            throw new IllegalStateException(
                    "More preview captures completed than requests queued.");
        }
        if (mInFlightPreviews == 0) {
            mPreviewsEmpty.signalAll();
        }
    
private voidonRequestCompleted(android.hardware.camera2.legacy.CaptureCollector$CaptureHolder capture)

        RequestHolder request = capture.mRequest;

        mInFlight--;
        if (DEBUG) {
            Log.d(TAG, "Completed request " + request.getRequestId() +
                    ", " + mInFlight + " requests remain in flight.");
        }
        if (mInFlight < 0) {
            throw new IllegalStateException(
                    "More captures completed than requests queued.");
        }

        mCompletedRequests.add(capture);
        mActiveRequests.remove(capture);

        mNotFull.signalAll();
        if (mInFlight == 0) {
            mIsEmpty.signalAll();
        }
    
public android.util.PairpreviewCaptured(long timestamp)
Called to alert the {@link CaptureCollector} that the preview capture has begun.

param
timestamp the time of the preview capture.
return
a pair containing the {@link RequestHolder} and the timestamp of the capture.

        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h = mPreviewCaptureQueue.poll();
            if (h == null) {
                if (DEBUG) {
                    Log.d(TAG, "previewCaptured called with no preview request on queue!");
                }
                return null;
            }
            h.setPreviewTimestamp(timestamp);
            return new Pair<>(h.mRequest, h.mTimestamp);
        } finally {
            lock.unlock();
        }
    
public RequestHolderpreviewProduced()
Called to alert the {@link CaptureCollector} that the preview capture has completed.

return
the {@link RequestHolder} for the request associated with this capture.

        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            CaptureHolder h = mPreviewProduceQueue.poll();
            if (h == null) {
                Log.w(TAG, "previewProduced called with no preview request on queue!");
                return null;
            }
            h.setPreviewProduced();
            return h.mRequest;
        } finally {
            lock.unlock();
        }
    
public booleanqueueRequest(RequestHolder holder, LegacyRequest legacy, long timeout, java.util.concurrent.TimeUnit unit)
Queue a new request.

For requests that use the Camera1 API preview output stream, this will block if there are already {@code maxInFlight} requests in progress (until at least one prior request has completed). For requests that use the Camera1 API jpeg callbacks, this will block until all prior requests have been completed to avoid stopping preview for {@link android.hardware.Camera#takePicture} before prior preview requests have been completed.

param
holder the {@link RequestHolder} for this request.
param
legacy the {@link LegacyRequest} for this request; this will not be mutated.
param
timeout a timeout to use for this call.
param
unit the units to use for the timeout.
return
{@code false} if this method timed out.
throws
InterruptedException if this thread is interrupted.

        CaptureHolder h = new CaptureHolder(holder, legacy);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            if (DEBUG) {
                Log.d(TAG, "queueRequest  for request " + holder.getRequestId() +
                        " - " + mInFlight + " requests remain in flight.");
            }

            if (!(h.needsJpeg || h.needsPreview)) {
                throw new IllegalStateException("Request must target at least one output surface!");
            }

            if (h.needsJpeg) {
                // Wait for all current requests to finish before queueing jpeg.
                while (mInFlight > 0) {
                    if (nanos <= 0) {
                        return false;
                    }
                    nanos = mIsEmpty.awaitNanos(nanos);
                }
                mJpegCaptureQueue.add(h);
                mJpegProduceQueue.add(h);
            }
            if (h.needsPreview) {
                while (mInFlight >= mMaxInFlight) {
                    if (nanos <= 0) {
                        return false;
                    }
                    nanos = mNotFull.awaitNanos(nanos);
                }
                mPreviewCaptureQueue.add(h);
                mPreviewProduceQueue.add(h);
                mInFlightPreviews++;
            }
            mActiveRequests.add(h);

            mInFlight++;
            return true;
        } finally {
            lock.unlock();
        }
    
private booleanremoveRequestIfCompleted(RequestHolder holder, android.util.MutableLong timestamp)

        int i = 0;
        for (CaptureHolder h : mCompletedRequests) {
            if (h.mRequest.equals(holder)) {
                timestamp.value = h.mTimestamp;
                mCompletedRequests.remove(i);
                return true;
            }
            i++;
        }

        return false;
    
public booleanwaitForEmpty(long timeout, java.util.concurrent.TimeUnit unit)
Wait all queued requests to complete.

param
timeout a timeout to use for this call.
param
unit the units to use for the timeout.
return
{@code false} if this method timed out.
throws
InterruptedException if this thread is interrupted.

        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            while (mInFlight > 0) {
                if (nanos <= 0) {
                    return false;
                }
                nanos = mIsEmpty.awaitNanos(nanos);
            }
            return true;
        } finally {
            lock.unlock();
        }
    
public booleanwaitForPreviewsEmpty(long timeout, java.util.concurrent.TimeUnit unit)
Wait all queued requests that use the Camera1 API preview output to complete.

param
timeout a timeout to use for this call.
param
unit the units to use for the timeout.
return
{@code false} if this method timed out.
throws
InterruptedException if this thread is interrupted.

        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            while (mInFlightPreviews > 0) {
                if (nanos <= 0) {
                    return false;
                }
                nanos = mPreviewsEmpty.awaitNanos(nanos);
            }
            return true;
        } finally {
            lock.unlock();
        }
    
public booleanwaitForRequestCompleted(RequestHolder holder, long timeout, java.util.concurrent.TimeUnit unit, android.util.MutableLong timestamp)
Wait for the specified request to be completed (all buffers available).

May not wait for the same request more than once, since a successful wait will erase the history of that request.

param
holder the {@link RequestHolder} for this request.
param
timeout a timeout to use for this call.
param
unit the units to use for the timeout.
param
timestamp the timestamp of the request will be written out to here, in ns
return
{@code false} if this method timed out.
throws
InterruptedException if this thread is interrupted.

        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.mLock;
        lock.lock();
        try {
            while (!removeRequestIfCompleted(holder, /*out*/timestamp)) {
                if (nanos <= 0) {
                    return false;
                }
                nanos = mNotFull.awaitNanos(nanos);
            }
            return true;
        } finally {
            lock.unlock();
        }