1/* 2 * Copyright (C) 2011 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 18package android.filterfw.core; 19 20import android.filterfw.core.Frame; 21import android.filterfw.core.FrameFormat; 22import android.filterfw.core.SimpleFrameManager; 23 24import java.util.Map; 25import java.util.SortedMap; 26import java.util.TreeMap; 27 28/** 29 * @hide 30 */ 31public class CachedFrameManager extends SimpleFrameManager { 32 33 private SortedMap<Integer, Frame> mAvailableFrames; 34 private int mStorageCapacity = 24 * 1024 * 1024; // Cap default storage to 24MB 35 private int mStorageSize = 0; 36 private int mTimeStamp = 0; 37 38 public CachedFrameManager() { 39 super(); 40 mAvailableFrames = new TreeMap<Integer, Frame>(); 41 } 42 43 @Override 44 public Frame newFrame(FrameFormat format) { 45 Frame result = findAvailableFrame(format, Frame.NO_BINDING, 0); 46 if (result == null) { 47 result = super.newFrame(format); 48 } 49 result.setTimestamp(Frame.TIMESTAMP_NOT_SET); 50 return result; 51 } 52 53 @Override 54 public Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId) { 55 Frame result = findAvailableFrame(format, bindingType, bindingId); 56 if (result == null) { 57 result = super.newBoundFrame(format, bindingType, bindingId); 58 } 59 result.setTimestamp(Frame.TIMESTAMP_NOT_SET); 60 return result; 61 } 62 63 @Override 64 public Frame retainFrame(Frame frame) { 65 return super.retainFrame(frame); 66 } 67 68 @Override 69 public Frame releaseFrame(Frame frame) { 70 if (frame.isReusable()) { 71 int refCount = frame.decRefCount(); 72 if (refCount == 0 && frame.hasNativeAllocation()) { 73 if (!storeFrame(frame)) { 74 frame.releaseNativeAllocation(); 75 } 76 return null; 77 } else if (refCount < 0) { 78 throw new RuntimeException("Frame reference count dropped below 0!"); 79 } 80 } else { 81 super.releaseFrame(frame); 82 } 83 return frame; 84 } 85 86 public void clearCache() { 87 for (Frame frame : mAvailableFrames.values()) { 88 frame.releaseNativeAllocation(); 89 } 90 mAvailableFrames.clear(); 91 } 92 93 @Override 94 public void tearDown() { 95 clearCache(); 96 } 97 98 private boolean storeFrame(Frame frame) { 99 synchronized(mAvailableFrames) { 100 // Make sure this frame alone does not exceed capacity 101 int frameSize = frame.getFormat().getSize(); 102 if (frameSize > mStorageCapacity) { 103 return false; 104 } 105 106 // Drop frames if adding this frame would exceed capacity 107 int newStorageSize = mStorageSize + frameSize; 108 while (newStorageSize > mStorageCapacity) { 109 dropOldestFrame(); 110 newStorageSize = mStorageSize + frameSize; 111 } 112 113 // Store new frame 114 frame.onFrameStore(); 115 mStorageSize = newStorageSize; 116 mAvailableFrames.put(mTimeStamp, frame); 117 ++mTimeStamp; 118 return true; 119 } 120 } 121 122 private void dropOldestFrame() { 123 int oldest = mAvailableFrames.firstKey(); 124 Frame frame = mAvailableFrames.get(oldest); 125 mStorageSize -= frame.getFormat().getSize(); 126 frame.releaseNativeAllocation(); 127 mAvailableFrames.remove(oldest); 128 } 129 130 private Frame findAvailableFrame(FrameFormat format, int bindingType, long bindingId) { 131 // Look for a frame that is compatible with the requested format 132 synchronized(mAvailableFrames) { 133 for (Map.Entry<Integer, Frame> entry : mAvailableFrames.entrySet()) { 134 Frame frame = entry.getValue(); 135 // Check that format is compatible 136 if (frame.getFormat().isReplaceableBy(format)) { 137 // Check that binding is compatible (if frame is bound) 138 if ((bindingType == frame.getBindingType()) 139 && (bindingType == Frame.NO_BINDING || bindingId == frame.getBindingId())) { 140 // We found one! Take it out of the set of available frames and attach the 141 // requested format to it. 142 super.retainFrame(frame); 143 mAvailableFrames.remove(entry.getKey()); 144 frame.onFrameFetch(); 145 frame.reset(format); 146 mStorageSize -= format.getSize(); 147 return frame; 148 } 149 } 150 } 151 } 152 return null; 153 } 154 155} 156