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