• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                     // Check for the case where we might have an error after a frame number gap
96                     // caused by other types of capture requests
97                     int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT;
98                     int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT;
99                     if (mPendingFrameNumbersWithOtherType[otherType1].isEmpty() &&
100                             mPendingFrameNumbersWithOtherType[otherType2].isEmpty()) {
101                         long errorGapNumber = Math.max(mCompletedFrameNumber[otherType1],
102                                 mCompletedFrameNumber[otherType2]) + 1;
103                         if ((errorGapNumber > mCompletedFrameNumber[requestType] + 1) &&
104                                 (errorGapNumber == errorFrameNumber)) {
105                             removeError = true;
106                         }
107                     }
108                 }
109             }
110             if (removeError) {
111                 mCompletedFrameNumber[requestType] = errorFrameNumber;
112                 mPartialResults.remove(errorFrameNumber);
113                 iter.remove();
114             }
115         }
116     }
117 
118     /**
119      * This function is called every time when a result or an error is received.
120      * @param frameNumber the frame number corresponding to the result or error
121      * @param isError true if it is an error, false if it is not an error
122      * @param requestType the type of capture request: Reprocess, ZslStill, or Regular.
123      */
updateTracker(long frameNumber, boolean isError, int requestType)124     public void updateTracker(long frameNumber, boolean isError, int requestType) {
125         if (isError) {
126             mFutureErrorMap.put(frameNumber, requestType);
127         } else {
128             try {
129                 updateCompletedFrameNumber(frameNumber, requestType);
130             } catch (IllegalArgumentException e) {
131                 Log.e(TAG, e.getMessage());
132             }
133         }
134         update();
135     }
136 
137     /**
138      * This function is called every time a result has been completed.
139      *
140      * <p>It keeps a track of all the partial results already created for a particular
141      * frame number.</p>
142      *
143      * @param frameNumber the frame number corresponding to the result
144      * @param result the total or partial result
145      * @param partial {@true} if the result is partial, {@code false} if total
146      * @param requestType the type of capture request: Reprocess, ZslStill, or Regular.
147      */
updateTracker(long frameNumber, CaptureResult result, boolean partial, int requestType)148     public void updateTracker(long frameNumber, CaptureResult result, boolean partial,
149             int requestType) {
150         if (!partial) {
151             // Update the total result's frame status as being successful
152             updateTracker(frameNumber, /*isError*/false, requestType);
153             // Don't keep a list of total results, we don't need to track them
154             return;
155         }
156 
157         if (result == null) {
158             // Do not record blank results; this also means there will be no total result
159             // so it doesn't matter that the partials were not recorded
160             return;
161         }
162 
163         // Partial results must be aggregated in-order for that frame number
164         List<CaptureResult> partials = mPartialResults.get(frameNumber);
165         if (partials == null) {
166             partials = new ArrayList<>();
167             mPartialResults.put(frameNumber, partials);
168         }
169 
170         partials.add(result);
171     }
172 
173     /**
174      * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}.
175      *
176      * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker}
177      * is called again with new partials for that frame number).</p>
178      *
179      * @param frameNumber the frame number corresponding to the result
180      * @return a list of partial results for that frame with at least 1 element,
181      *         or {@code null} if there were no partials recorded for that frame
182      */
popPartialResults(long frameNumber)183     public List<CaptureResult> popPartialResults(long frameNumber) {
184         return mPartialResults.remove(frameNumber);
185     }
186 
getCompletedFrameNumber()187     public long getCompletedFrameNumber() {
188         return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REGULAR];
189     }
190 
getCompletedReprocessFrameNumber()191     public long getCompletedReprocessFrameNumber() {
192         return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REPROCESS];
193     }
194 
getCompletedZslStillFrameNumber()195     public long getCompletedZslStillFrameNumber() {
196         return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_ZSL_STILL];
197     }
198 
199     /**
200      * Update the completed frame number for results of 3 categories
201      * (Regular/Reprocess/ZslStill).
202      *
203      * It validates that all previous frames of the same category have arrived.
204      *
205      * If there is a gap since previous frame number of the same category, assume the frames in
206      * the gap are other categories and store them in the pending frame number queue to check
207      * against when frames of those categories arrive.
208      */
updateCompletedFrameNumber(long frameNumber, int requestType)209     private void updateCompletedFrameNumber(long frameNumber,
210             int requestType) throws IllegalArgumentException {
211         if (frameNumber <= mCompletedFrameNumber[requestType]) {
212             throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
213         }
214 
215         // Assume there are only 3 different types of capture requests.
216         int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT;
217         int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT;
218         long maxOtherFrameNumberSeen =
219                 Math.max(mCompletedFrameNumber[otherType1], mCompletedFrameNumber[otherType2]);
220         if (frameNumber < maxOtherFrameNumberSeen) {
221             // if frame number is smaller than completed frame numbers of other categories,
222             // it must be:
223             // - the head of mPendingFrameNumbers for this category, or
224             // - in one of other mPendingFrameNumbersWithOtherType
225             if (!mPendingFrameNumbers[requestType].isEmpty()) {
226                 // frame number must be head of current type of mPendingFrameNumbers if
227                 // mPendingFrameNumbers isn't empty.
228                 Long pendingFrameNumberSameType = mPendingFrameNumbers[requestType].element();
229                 if (frameNumber == pendingFrameNumberSameType) {
230                     // frame number matches the head of the pending frame number queue.
231                     // Do this before the inequality checks since this is likely to be the common
232                     // case.
233                     mPendingFrameNumbers[requestType].remove();
234                 } else if (frameNumber < pendingFrameNumberSameType) {
235                     throw new IllegalArgumentException("frame number " + frameNumber
236                             + " is a repeat");
237                 } else {
238                     throw new IllegalArgumentException("frame number " + frameNumber
239                             + " comes out of order. Expecting "
240                             + pendingFrameNumberSameType);
241                 }
242             } else {
243                 // frame number must be in one of the other mPendingFrameNumbersWithOtherType.
244                 int index1 = mPendingFrameNumbersWithOtherType[otherType1].indexOf(frameNumber);
245                 int index2 = mPendingFrameNumbersWithOtherType[otherType2].indexOf(frameNumber);
246                 boolean inSkippedOther1 = index1 != -1;
247                 boolean inSkippedOther2 = index2 != -1;
248                 if (!(inSkippedOther1 ^ inSkippedOther2)) {
249                     throw new IllegalArgumentException("frame number " + frameNumber
250                             + " is a repeat or invalid");
251                 }
252 
253                 // We know the category of frame numbers in pendingFrameNumbersWithOtherType leading
254                 // up to the current frame number. The destination is the type which isn't the
255                 // requestType* and isn't the src. Move them into the correct pendingFrameNumbers.
256                 // * : This is since frameNumber is the first frame of requestType that we've
257                 // received in the 'others' list, since for each request type frames come in order.
258                 // All the frames before frameNumber are of the same type. They're not of
259                 // 'requestType', neither of the type of the 'others' list they were found in. The
260                 // remaining option is the 3rd type.
261                 LinkedList<Long> srcList, dstList;
262                 int index;
263                 if (inSkippedOther1) {
264                     srcList = mPendingFrameNumbersWithOtherType[otherType1];
265                     dstList = mPendingFrameNumbers[otherType2];
266                     index = index1;
267                 } else {
268                     srcList = mPendingFrameNumbersWithOtherType[otherType2];
269                     dstList = mPendingFrameNumbers[otherType1];
270                     index = index2;
271                 }
272                 for (int i = 0; i < index; i++) {
273                     dstList.add(srcList.removeFirst());
274                 }
275 
276                 // Remove current frame number from pendingFrameNumbersWithOtherType
277                 srcList.remove();
278             }
279         } else {
280             // there is a gap of unseen frame numbers which should belong to the other
281             // 2 categories. Put all the pending frame numbers in the queue.
282             for (long i =
283                     Math.max(maxOtherFrameNumberSeen, mCompletedFrameNumber[requestType]) + 1;
284                     i < frameNumber; i++) {
285                 mPendingFrameNumbersWithOtherType[requestType].add(i);
286             }
287         }
288 
289         mCompletedFrameNumber[requestType] = frameNumber;
290     }
291 }
292 
293