/** * As long as the processing thread is active, this executes detection on frames continuously. * The next pending frame is either immediately available or hasn't been received yet. Once it * is available, we transfer the frame info to local variables and run detection on that frame. * It immediately loops back for the next frame without pausing. * * <p>If detection takes longer than the time in between new frames from the camera, this will * mean that this loop will run without ever waiting on a frame, avoiding any context switching * or frame acquisition time latency. * * <p>If you find that this is using more CPU than you'd like, you should probably decrease the * FPS setting above to allow for some idle time in between frames. */ @Override public void run() { Frame outputFrame; ByteBuffer data; while (true) { synchronized (mLock) { if (mActive && (mPendingFrameData == null)) { try { // Wait for the next frame to be received from the camera, since we // don't have it yet. mLock.wait(); } catch (InterruptedException e) { Log.d(TAG, mContext.getString(R.string.frame_processing_loop_terminated), e); return; } } if (!mActive) { // Exit the loop once this camera source is stopped or released. We check // this here, immediately after the wait() above, to handle the case where // setActive(false) had been called, triggering the termination of this // loop. return; } outputFrame = new Frame.Builder() .setImageData( mPendingFrameData, mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.NV21) .setId(mPendingFrameId) .setTimestampMillis(mPendingTimeMillis) .setRotation(mRotation) .build(); // Hold onto the frame data locally, so that we can use this for detection // below. We need to clear mPendingFrameData to ensure that this buffer isn't // recycled back to the camera before we are done using that data. data = mPendingFrameData; mPendingFrameData = null; } // The code below needs to run outside of synchronization, because this will allow // the camera to add pending frame(s) while we are running detection on the current // frame. try { mDetector.receiveFrame(outputFrame); } catch (Throwable t) { Log.e(TAG, mContext.getString(R.string.exception_from_receiver), t); } finally { mCamera.addCallbackBuffer(data.array()); } } }
/** * Releases the underlying receiver. This is only safe to do after the associated thread has * completed, which is managed in camera source's release method above. */ @SuppressLint("Assert") void release() { assert (mProcessingThread.getState() == State.TERMINATED); mDetector.release(); mDetector = null; }