1 /*
2  * Copyright (C) 2019 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  */
16 
17 package android.hardware.camera2.impl;
18 
19 import android.hardware.camera2.CameraCaptureSession;
20 import android.hardware.camera2.CaptureRequest;
21 import android.hardware.camera2.CaptureResult;
22 import android.util.Log;
23 
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.TreeMap;
30 
31 /**
32  * This class tracks the last frame number for submitted requests.
33  */
34 public class FrameNumberTracker {
35     private static final String TAG = "FrameNumberTracker";
36 
37     /** the completed frame number for each type of capture results */
38     private long[] mCompletedFrameNumber = new long[CaptureRequest.REQUEST_TYPE_COUNT];
39 
40     /** the frame numbers that don't belong to each type of capture results and are yet to be seen
41      * through an updateTracker() call. Each list holds a list of frame numbers that should appear
42      * with request types other than that, to which the list corresponds.
43      */
44     private final LinkedList<Long>[] mPendingFrameNumbersWithOtherType =
45             new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT];
46 
47     /** the frame numbers that belong to each type of capture results which should appear, but
48      * haven't yet.*/
49     private final LinkedList<Long>[] mPendingFrameNumbers =
50             new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT];
51 
52     /** frame number -> request type */
53     private final TreeMap<Long, Integer> mFutureErrorMap = new TreeMap<Long, Integer>();
54     /** Map frame numbers to list of partial results */
55     private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
56 
FrameNumberTracker()57     public FrameNumberTracker() {
58         for (int i = 0; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
59             mCompletedFrameNumber[i] = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED;
60             mPendingFrameNumbersWithOtherType[i] = new LinkedList<Long>();
61             mPendingFrameNumbers[i] = new LinkedList<Long>();
62         }
63     }
64 
update()65     private void update() {
66         Iterator iter = mFutureErrorMap.entrySet().iterator();
67         while (iter.hasNext()) {
68             TreeMap.Entry pair = (TreeMap.Entry)iter.next();
69             Long errorFrameNumber = (Long)pair.getKey();
70             int requestType = (int) pair.getValue();
71             Boolean removeError = false;
72             if (errorFrameNumber == mCompletedFrameNumber[requestType] + 1) {
73                 removeError = true;
74             }
75             // The error frame number could have also either been in the pending list or one of the
76             // 'other' pending lists.
77             if (!mPendingFrameNumbers[requestType].isEmpty()) {
78                 if (errorFrameNumber == mPendingFrameNumbers[requestType].element()) {
79                     mPendingFrameNumbers[requestType].remove();
80                     removeError = true;
81                 }
82             } else {
83                 for (int i = 1; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
84                     int otherType = (requestType + i) % CaptureRequest.REQUEST_TYPE_COUNT;
85                     if (!mPendingFrameNumbersWithOtherType[otherType].isEmpty() && errorFrameNumber
86                             == mPendingFrameNumbersWithOtherType[otherType].element()) {
87                         mPendingFrameNumbersWithOtherType[otherType].remove();
88                         removeError = true;
89                         break;
90                     }
91                 }
92             }
93             if (removeError) {
94                 mCompletedFrameNumber[requestType] = errorFrameNumber;
95                 mPartialResults.remove(errorFrameNumber);
96                 iter.remove();
97             }
98         }
99     }
100 
101     /**
102      * This function is called every time when a result or an error is received.
103      * @param frameNumber the frame number corresponding to the result or error
104      * @param isError true if it is an error, false if it is not an error
105      * @param requestType the type of capture request: Reprocess, ZslStill, or Regular.
106      */
updateTracker(long frameNumber, boolean isError, int requestType)107     public void updateTracker(long frameNumber, boolean isError, int requestType) {
108         if (isError) {
109             mFutureErrorMap.put(frameNumber, requestType);
110         } else {
111             try {
112                 updateCompletedFrameNumber(frameNumber, requestType);
113             } catch (IllegalArgumentException e) {
114                 Log.e(TAG, e.getMessage());
115             }
116         }
117         update();
118     }
119 
120     /**
121      * This function is called every time a result has been completed.
122      *
123      * <p>It keeps a track of all the partial results already created for a particular
124      * frame number.</p>
125      *
126      * @param frameNumber the frame number corresponding to the result
127      * @param result the total or partial result
128      * @param partial {@true} if the result is partial, {@code false} if total
129      * @param requestType the type of capture request: Reprocess, ZslStill, or Regular.
130      */
updateTracker(long frameNumber, CaptureResult result, boolean partial, int requestType)131     public void updateTracker(long frameNumber, CaptureResult result, boolean partial,
132             int requestType) {
133         if (!partial) {
134             // Update the total result's frame status as being successful
135             updateTracker(frameNumber, /*isError*/false, requestType);
136             // Don't keep a list of total results, we don't need to track them
137             return;
138         }
139 
140         if (result == null) {
141             // Do not record blank results; this also means there will be no total result
142             // so it doesn't matter that the partials were not recorded
143             return;
144         }
145 
146         // Partial results must be aggregated in-order for that frame number
147         List<CaptureResult> partials = mPartialResults.get(frameNumber);
148         if (partials == null) {
149             partials = new ArrayList<>();
150             mPartialResults.put(frameNumber, partials);
151         }
152 
153         partials.add(result);
154     }
155 
156     /**
157      * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
158      *
159      * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
160      * is called again with new partials for that frame number).</p>
161      *
162      * @param frameNumber the frame number corresponding to the result
163      * @return a list of partial results for that frame with at least 1 element,
164      *         or {@code null} if there were no partials recorded for that frame
165      */
popPartialResults(long frameNumber)166     public List<CaptureResult> popPartialResults(long frameNumber) {
167         return mPartialResults.remove(frameNumber);
168     }
169 
getCompletedFrameNumber()170     public long getCompletedFrameNumber() {
171         return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REGULAR];
172     }
173 
getCompletedReprocessFrameNumber()174     public long getCompletedReprocessFrameNumber() {
175         return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REPROCESS];
176     }
177 
getCompletedZslStillFrameNumber()178     public long getCompletedZslStillFrameNumber() {
179         return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_ZSL_STILL];
180     }
181 
182     /**
183      * Update the completed frame number for results of 3 categories
184      * (Regular/Reprocess/ZslStill).
185      *
186      * It validates that all previous frames of the same category have arrived.
187      *
188      * If there is a gap since previous frame number of the same category, assume the frames in
189      * the gap are other categories and store them in the pending frame number queue to check
190      * against when frames of those categories arrive.
191      */
updateCompletedFrameNumber(long frameNumber, int requestType)192     private void updateCompletedFrameNumber(long frameNumber,
193             int requestType) throws IllegalArgumentException {
194         if (frameNumber <= mCompletedFrameNumber[requestType]) {
195             throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
196         }
197 
198         // Assume there are only 3 different types of capture requests.
199         int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT;
200         int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT;
201         long maxOtherFrameNumberSeen =
202                 Math.max(mCompletedFrameNumber[otherType1], mCompletedFrameNumber[otherType2]);
203         if (frameNumber < maxOtherFrameNumberSeen) {
204             // if frame number is smaller than completed frame numbers of other categories,
205             // it must be:
206             // - the head of mPendingFrameNumbers for this category, or
207             // - in one of other mPendingFrameNumbersWithOtherType
208             if (!mPendingFrameNumbers[requestType].isEmpty()) {
209                 // frame number must be head of current type of mPendingFrameNumbers if
210                 // mPendingFrameNumbers isn't empty.
211                 Long pendingFrameNumberSameType = mPendingFrameNumbers[requestType].element();
212                 if (frameNumber == pendingFrameNumberSameType) {
213                     // frame number matches the head of the pending frame number queue.
214                     // Do this before the inequality checks since this is likely to be the common
215                     // case.
216                     mPendingFrameNumbers[requestType].remove();
217                 } else if (frameNumber < pendingFrameNumberSameType) {
218                     throw new IllegalArgumentException("frame number " + frameNumber
219                             + " is a repeat");
220                 } else {
221                     throw new IllegalArgumentException("frame number " + frameNumber
222                             + " comes out of order. Expecting "
223                             + pendingFrameNumberSameType);
224                 }
225             } else {
226                 // frame number must be in one of the other mPendingFrameNumbersWithOtherType.
227                 int index1 = mPendingFrameNumbersWithOtherType[otherType1].indexOf(frameNumber);
228                 int index2 = mPendingFrameNumbersWithOtherType[otherType2].indexOf(frameNumber);
229                 boolean inSkippedOther1 = index1 != -1;
230                 boolean inSkippedOther2 = index2 != -1;
231                 if (!(inSkippedOther1 ^ inSkippedOther2)) {
232                     throw new IllegalArgumentException("frame number " + frameNumber
233                             + " is a repeat or invalid");
234                 }
235 
236                 // We know the category of frame numbers in pendingFrameNumbersWithOtherType leading
237                 // up to the current frame number. The destination is the type which isn't the
238                 // requestType* and isn't the src. Move them into the correct pendingFrameNumbers.
239                 // * : This is since frameNumber is the first frame of requestType that we've
240                 // received in the 'others' list, since for each request type frames come in order.
241                 // All the frames before frameNumber are of the same type. They're not of
242                 // 'requestType', neither of the type of the 'others' list they were found in. The
243                 // remaining option is the 3rd type.
244                 LinkedList<Long> srcList, dstList;
245                 int index;
246                 if (inSkippedOther1) {
247                     srcList = mPendingFrameNumbersWithOtherType[otherType1];
248                     dstList = mPendingFrameNumbers[otherType2];
249                     index = index1;
250                 } else {
251                     srcList = mPendingFrameNumbersWithOtherType[otherType2];
252                     dstList = mPendingFrameNumbers[otherType1];
253                     index = index2;
254                 }
255                 for (int i = 0; i < index; i++) {
256                     dstList.add(srcList.removeFirst());
257                 }
258 
259                 // Remove current frame number from pendingFrameNumbersWithOtherType
260                 srcList.remove();
261             }
262         } else {
263             // there is a gap of unseen frame numbers which should belong to the other
264             // 2 categories. Put all the pending frame numbers in the queue.
265             for (long i =
266                     Math.max(maxOtherFrameNumberSeen, mCompletedFrameNumber[requestType]) + 1;
267                     i < frameNumber; i++) {
268                 mPendingFrameNumbersWithOtherType[requestType].add(i);
269             }
270         }
271 
272         mCompletedFrameNumber[requestType] = frameNumber;
273     }
274 }
275 
276