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