content: /*
   * Copyright (C) 2014 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package android.hardware.camera2.legacy;
  
  import android.hardware.camera2.CaptureRequest;
  import android.hardware.camera2.utils.SubmitInfo;
  import android.util.Log;
  
  import java.util.ArrayDeque;
  import java.util.List;
  
  /**
   * A queue of bursts of requests.
   *
   * <p>This queue maintains the count of frames that have been produced, and is thread safe.</p>
   */
  public class RequestQueue {
      private static final String TAG = "RequestQueue";
  
      private static final long INVALID_FRAME = -1;
  
      private BurstHolder mRepeatingRequest = null;
      private final ArrayDeque<BurstHolder> mRequestQueue = new ArrayDeque<BurstHolder>();
  
      private long mCurrentFrameNumber = 0;
      private long mCurrentRepeatingFrameNumber = INVALID_FRAME;
      private int mCurrentRequestId = 0;
      private final List<Long> mJpegSurfaceIds;
  
      public final class RequestQueueEntry {
          private final BurstHolder mBurstHolder;
          private final Long mFrameNumber;
          private final boolean mQueueEmpty;
  
          public BurstHolder getBurstHolder() {
              return mBurstHolder;
          }
          public Long getFrameNumber() {
              return mFrameNumber;
          }
          public boolean isQueueEmpty() {
              return mQueueEmpty;
          }
  
          public RequestQueueEntry(BurstHolder burstHolder, Long frameNumber, boolean queueEmpty) {
              mBurstHolder = burstHolder;
              mFrameNumber = frameNumber;
              mQueueEmpty = queueEmpty;
          }
      }
  
      public RequestQueue(List<Long> jpegSurfaceIds) {
          mJpegSurfaceIds = jpegSurfaceIds;
      }
  
      /**
       * Return and remove the next burst on the queue.
       *
       * <p>If a repeating burst is returned, it will not be removed.</p>
       *
       * @return an entry containing the next burst, the current frame number, and flag about whether
       * request queue becomes empty. Null if no burst exists.
       */
      public synchronized RequestQueueEntry getNext() {
          BurstHolder next = mRequestQueue.poll();
          boolean queueEmptied = (next != null && mRequestQueue.size() == 0);
          if (next == null && mRepeatingRequest != null) {
              next = mRepeatingRequest;
              mCurrentRepeatingFrameNumber = mCurrentFrameNumber +
                      next.getNumberOfRequests();
          }
  
          if (next == null) {
              return null;
          }
  
          RequestQueueEntry ret =  new RequestQueueEntry(next, mCurrentFrameNumber, queueEmptied);
          mCurrentFrameNumber += next.getNumberOfRequests();
          return ret;
      }
  
      /**
       * Cancel a repeating request.
       *
       * @param requestId the id of the repeating request to cancel.
       * @return the last frame to be returned from the HAL for the given repeating request, or
       *          {@code INVALID_FRAME} if none exists.
       */
      public synchronized long stopRepeating(int requestId) {
          long ret = INVALID_FRAME;
          if (mRepeatingRequest != null && mRepeatingRequest.getRequestId() == requestId) {
              mRepeatingRequest = null;
              ret = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
                      mCurrentRepeatingFrameNumber - 1;
              mCurrentRepeatingFrameNumber = INVALID_FRAME;
              Log.i(TAG, "Repeating capture request cancelled.");
          } else {
              Log.e(TAG, "cancel failed: no repeating request exists for request id: " + requestId);
          }
          return ret;
      }
  
      /**
       * Cancel a repeating request.
       *
       * @return the last frame to be returned from the HAL for the given repeating request, or
       *          {@code INVALID_FRAME} if none exists.
       */
      public synchronized long stopRepeating() {
          if (mRepeatingRequest == null) {
              Log.e(TAG, "cancel failed: no repeating request exists.");
              return INVALID_FRAME;
          }
          return stopRepeating(mRepeatingRequest.getRequestId());
      }
  
      /**
       * Add a the given burst to the queue.
       *
       * <p>If the burst is repeating, replace the current repeating burst.</p>
       *
       * @param requests the burst of requests to add to the queue.
       * @param repeating true if the burst is repeating.
       * @return the submission info, including the new request id, and the last frame number, which
       *   contains either the frame number of the last frame that will be returned for this request,
       *   or the frame number of the last frame that will be returned for the current repeating
       *   request if this burst is set to be repeating.
       */
      public synchronized SubmitInfo submit(CaptureRequest[] requests, boolean repeating) {
          int requestId = mCurrentRequestId++;
          BurstHolder burst = new BurstHolder(requestId, repeating, requests, mJpegSurfaceIds);
          long lastFrame = INVALID_FRAME;
          if (burst.isRepeating()) {
              Log.i(TAG, "Repeating capture request set.");
              if (mRepeatingRequest != null) {
                  lastFrame = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
                          mCurrentRepeatingFrameNumber - 1;
              }
              mCurrentRepeatingFrameNumber = INVALID_FRAME;
              mRepeatingRequest = burst;
          } else {
              mRequestQueue.offer(burst);
              lastFrame = calculateLastFrame(burst.getRequestId());
          }
          SubmitInfo info = new SubmitInfo(requestId, lastFrame);
          return info;
      }
  
      private long calculateLastFrame(int requestId) {
          long total = mCurrentFrameNumber;
          for (BurstHolder b : mRequestQueue) {
              total += b.getNumberOfRequests();
              if (b.getRequestId() == requestId) {
                  return total - 1;
              }
          }
          throw new IllegalStateException(
                  "At least one request must be in the queue to calculate frame number");
      }
  
  }
  