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