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 */
16package android.hardware.camera2.legacy;
17
18import android.hardware.camera2.CaptureRequest;
19import android.hardware.camera2.utils.SubmitInfo;
20import android.util.Log;
21
22import java.util.ArrayDeque;
23import java.util.List;
24
25/**
26 * A queue of bursts of requests.
27 *
28 * <p>This queue maintains the count of frames that have been produced, and is thread safe.</p>
29 */
30public class RequestQueue {
31    private static final String TAG = "RequestQueue";
32
33    private static final long INVALID_FRAME = -1;
34
35    private BurstHolder mRepeatingRequest = null;
36    private final ArrayDeque<BurstHolder> mRequestQueue = new ArrayDeque<BurstHolder>();
37
38    private long mCurrentFrameNumber = 0;
39    private long mCurrentRepeatingFrameNumber = INVALID_FRAME;
40    private int mCurrentRequestId = 0;
41    private final List<Long> mJpegSurfaceIds;
42
43    public final class RequestQueueEntry {
44        private final BurstHolder mBurstHolder;
45        private final Long mFrameNumber;
46        private final boolean mQueueEmpty;
47
48        public BurstHolder getBurstHolder() {
49            return mBurstHolder;
50        }
51        public Long getFrameNumber() {
52            return mFrameNumber;
53        }
54        public boolean isQueueEmpty() {
55            return mQueueEmpty;
56        }
57
58        public RequestQueueEntry(BurstHolder burstHolder, Long frameNumber, boolean queueEmpty) {
59            mBurstHolder = burstHolder;
60            mFrameNumber = frameNumber;
61            mQueueEmpty = queueEmpty;
62        }
63    }
64
65    public RequestQueue(List<Long> jpegSurfaceIds) {
66        mJpegSurfaceIds = jpegSurfaceIds;
67    }
68
69    /**
70     * Return and remove the next burst on the queue.
71     *
72     * <p>If a repeating burst is returned, it will not be removed.</p>
73     *
74     * @return an entry containing the next burst, the current frame number, and flag about whether
75     * request queue becomes empty. Null if no burst exists.
76     */
77    public synchronized RequestQueueEntry getNext() {
78        BurstHolder next = mRequestQueue.poll();
79        boolean queueEmptied = (next != null && mRequestQueue.size() == 0);
80        if (next == null && mRepeatingRequest != null) {
81            next = mRepeatingRequest;
82            mCurrentRepeatingFrameNumber = mCurrentFrameNumber +
83                    next.getNumberOfRequests();
84        }
85
86        if (next == null) {
87            return null;
88        }
89
90        RequestQueueEntry ret =  new RequestQueueEntry(next, mCurrentFrameNumber, queueEmptied);
91        mCurrentFrameNumber += next.getNumberOfRequests();
92        return ret;
93    }
94
95    /**
96     * Cancel a repeating request.
97     *
98     * @param requestId the id of the repeating request to cancel.
99     * @return the last frame to be returned from the HAL for the given repeating request, or
100     *          {@code INVALID_FRAME} if none exists.
101     */
102    public synchronized long stopRepeating(int requestId) {
103        long ret = INVALID_FRAME;
104        if (mRepeatingRequest != null && mRepeatingRequest.getRequestId() == requestId) {
105            mRepeatingRequest = null;
106            ret = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
107                    mCurrentRepeatingFrameNumber - 1;
108            mCurrentRepeatingFrameNumber = INVALID_FRAME;
109            Log.i(TAG, "Repeating capture request cancelled.");
110        } else {
111            Log.e(TAG, "cancel failed: no repeating request exists for request id: " + requestId);
112        }
113        return ret;
114    }
115
116    /**
117     * Cancel a repeating request.
118     *
119     * @return the last frame to be returned from the HAL for the given repeating request, or
120     *          {@code INVALID_FRAME} if none exists.
121     */
122    public synchronized long stopRepeating() {
123        if (mRepeatingRequest == null) {
124            Log.e(TAG, "cancel failed: no repeating request exists.");
125            return INVALID_FRAME;
126        }
127        return stopRepeating(mRepeatingRequest.getRequestId());
128    }
129
130    /**
131     * Add a the given burst to the queue.
132     *
133     * <p>If the burst is repeating, replace the current repeating burst.</p>
134     *
135     * @param requests the burst of requests to add to the queue.
136     * @param repeating true if the burst is repeating.
137     * @return the submission info, including the new request id, and the last frame number, which
138     *   contains either the frame number of the last frame that will be returned for this request,
139     *   or the frame number of the last frame that will be returned for the current repeating
140     *   request if this burst is set to be repeating.
141     */
142    public synchronized SubmitInfo submit(CaptureRequest[] requests, boolean repeating) {
143        int requestId = mCurrentRequestId++;
144        BurstHolder burst = new BurstHolder(requestId, repeating, requests, mJpegSurfaceIds);
145        long lastFrame = INVALID_FRAME;
146        if (burst.isRepeating()) {
147            Log.i(TAG, "Repeating capture request set.");
148            if (mRepeatingRequest != null) {
149                lastFrame = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
150                        mCurrentRepeatingFrameNumber - 1;
151            }
152            mCurrentRepeatingFrameNumber = INVALID_FRAME;
153            mRepeatingRequest = burst;
154        } else {
155            mRequestQueue.offer(burst);
156            lastFrame = calculateLastFrame(burst.getRequestId());
157        }
158        SubmitInfo info = new SubmitInfo(requestId, lastFrame);
159        return info;
160    }
161
162    private long calculateLastFrame(int requestId) {
163        long total = mCurrentFrameNumber;
164        for (BurstHolder b : mRequestQueue) {
165            total += b.getNumberOfRequests();
166            if (b.getRequestId() == requestId) {
167                return total - 1;
168            }
169        }
170        throw new IllegalStateException(
171                "At least one request must be in the queue to calculate frame number");
172    }
173
174}
175