1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package androidx.media.filterfw; 16 17 18 /** 19 * A FrameType instance specifies the data format of a Frame. 20 * 21 * FrameTypes are used mainly by Filters to specify the data type they intend to consume or produce. 22 * When filters are connected, their FrameType information is analyzed and checked for 23 * compatibility. This allows Filter writers to assume a certain data input type. It also helps 24 * filter-graph designers determine which filters can be hooked up to one another. 25 * 26 * A FrameType generally consists of an element type and number of dimensions. The currently 27 * supported element types are: 28 * 29 * <ul> 30 * <li>int8, int16, int32, in64</li> 31 * <li>float32, float64</li> 32 * <li>rgba8888</li> 33 * <li>object</li> 34 * <li>don't-care</li> 35 * </ul> 36 * 37 * If the object element type is used, class information may be appended to the FrameType to 38 * indicate what class of objects are expected. When constructing an object based FrameType, you 39 * have the option of either specifying a type that represents a single object of that class, or 40 * an array of objects (see the {@link #single()} and {@link #array()} constructors). A single 41 * object has a dimensionality of 0, while an array has a dimensionality of 1. 42 * 43 * When constructing a non-object type, you have the option of creating a 1D or 2D buffer, or 44 * a 2D image (see the {@link #buffer1D(int)}, {@link #buffer2D(int)}, and 45 * {@link #image2D(int, int)} constructors). To optimize access, provide access hints when making 46 * an image type. 47 * 48 * Finally, it is possible to create a wild-card type with the {@link #any()} constructor. This 49 * type matches any other type. Note, that this is a more general type than a {@code single(Object)} 50 * type that matches only object-base types (of any Object subclass). You may also specify the 51 * leave the element of any type unspecified by using the {@code ELEMENT_DONTCARE} constant. 52 * 53 * When a graph is connected the types between outputs and inputs are merged to a queue-type. All 54 * Frames in this queue will be of that type. In order for a merge to succeed the following 55 * conditions must hold: 56 * 57 * <ul> 58 * <li>The element types must be identical.</li> 59 * <li>The dimensions must match (except for singles and arrays, see below).</li> 60 * <li>For object-based types: The classes must be compatible.</li> 61 * <li>If one of the types is a wild-card, both types are always compatible.</li> 62 * </ul> 63 * 64 * Class compatibility is determined in an optimistic fashion, i.e. one class must be the subclass 65 * of the other. It does not matter which of the types is the subclass of the other. For instance, 66 * if one Filter outputs a type of class {@code Object}, and the consumer expects a Filter of type 67 * {@code Bitmap}, the connection is considered compatible. (Of course if at runtime a non-Bitmap 68 * object is produced, this will cause a runtime exception to be thrown). 69 * 70 * For convenience, single and array object-based types are compatible with one another. This 71 * in turn means that Frames with a single object can be accessed as an array with a single entry, 72 * and array based Frames can be accessed as a single object of the array class. For this reason 73 * you should prefer consuming objects as array types (if it makes sense for that specific port), 74 * as this will allow your Filter to handle multiple objects in one Frame while not giving up the 75 * possibility to deal with singles. 76 * TODO: This needs to be reworked. An array(int) should not be interchangeable with a single(int), 77 * but rather with a single(int[]). Use ArraySelectFilter for the former! 78 * 79 * After the types are merged, the queue-type must be a fully specified type. This means that the 80 * type must have its element and dimensions specified. This ensures that filters that need to 81 * query their input or output types receive meaningful information. 82 */ 83 public final class FrameType { 84 85 public final static int ELEMENT_DONTCARE = 0; 86 public final static int ELEMENT_OBJECT = 1; 87 88 public final static int ELEMENT_INT8 = 100; 89 public final static int ELEMENT_INT16 = 101; 90 public final static int ELEMENT_INT32 = 102; 91 public final static int ELEMENT_INT64 = 103; 92 93 public final static int ELEMENT_FLOAT32 = 200; 94 public final static int ELEMENT_FLOAT64 = 201; 95 96 public final static int ELEMENT_RGBA8888 = 301; 97 98 public final static int READ_CPU = 0x01; 99 public final static int READ_GPU = 0x02; 100 public final static int READ_ALLOCATION = 0x04; 101 public final static int WRITE_CPU = 0x08; 102 public final static int WRITE_GPU = 0x10; 103 public final static int WRITE_ALLOCATION = 0x20; 104 105 private final static int ACCESS_UNKNOWN = 0x00; 106 107 private final int mElementId; 108 private final int mDimensions; 109 private final int mAccessHints; 110 private final Class<?> mClass; 111 112 private static SimpleCache<String, FrameType> mTypeCache = 113 new SimpleCache<String, FrameType>(64); 114 115 /** 116 * Constructs a wild-card FrameType that matches any other FrameType. 117 * @return The wild-card FrameType instance. 118 */ 119 public static FrameType any() { 120 return FrameType.fetchType(ELEMENT_DONTCARE, -1, ACCESS_UNKNOWN); 121 } 122 123 /** 124 * Constructs an object-based single FrameType that matches object-based FrameTypes of any 125 * class. 126 * @return A single object-based FrameType instance. 127 */ 128 public static FrameType single() { 129 return FrameType.fetchType(null, 0); 130 } 131 132 /** 133 * Constructs an object-based single FrameType of the specified class. 134 * @param clazz The class of the FrameType. 135 * @return A single object-base FrameType instance of the specified class. 136 */ 137 public static FrameType single(Class<?> clazz) { 138 return FrameType.fetchType(clazz, 0); 139 } 140 141 /** 142 * Constructs an object-based array FrameType that matches object-based FrameTypes of any class. 143 * @return An array object-based FrameType instance. 144 */ 145 public static FrameType array() { 146 return FrameType.fetchType(null, 1); 147 } 148 149 /** 150 * Constructs an object-based array FrameType with elements of the specified class. 151 * @param clazz The class of the array elements (not the array type). 152 * @return An array object-based FrameType instance of the specified class. 153 */ 154 public static FrameType array(Class<?> clazz) { 155 return FrameType.fetchType(clazz, 1); 156 } 157 158 /** 159 * Constructs a one-dimensional buffer type of the specified element. 160 * @param elementType One of the {@code ELEMENT} constants. 161 * @return A 1D buffer FrameType instance. 162 */ 163 public static FrameType buffer1D(int elementType) { 164 return FrameType.fetchType(elementType, 1, ACCESS_UNKNOWN); 165 } 166 167 /** 168 * Constructs a two-dimensional buffer type of the specified element. 169 * @param elementType One of the {@code ELEMENT} constants. 170 * @return A 2D buffer FrameType instance. 171 */ 172 public static FrameType buffer2D(int elementType) { 173 return FrameType.fetchType(elementType, 2, ACCESS_UNKNOWN); 174 } 175 176 /** 177 * Constructs a two-dimensional image type of the specified element. 178 * @param elementType One of the {@code ELEMENT} constants. 179 * @param accessHint A bit-mask of access flags (see {@code READ} and {@code WRITE} constants). 180 * @return A 2D image FrameType instance. 181 */ 182 public static FrameType image2D(int elementType, int accessHint) { 183 return FrameType.fetchType(elementType, 2, accessHint); 184 } 185 186 /** 187 * Converts the current array type to a single type. 188 * The type must be an object-based type. If the type is already a single type, this does 189 * nothing. 190 * @return type as a single type. 191 */ 192 public FrameType asSingle() { 193 if (mElementId != ELEMENT_OBJECT) { 194 throw new RuntimeException("Calling asSingle() on non-object type!"); 195 } 196 return FrameType.fetchType(mClass, 0); 197 } 198 199 /** 200 * Converts the current single type to an array type. 201 * The type must be an object-based type. If the type is already an array type, this does 202 * nothing. 203 * @return type as an array type. 204 */ 205 public FrameType asArray() { 206 if (mElementId != ELEMENT_OBJECT) { 207 throw new RuntimeException("Calling asArray() on non-object type!"); 208 } 209 return FrameType.fetchType(mClass, 1); 210 } 211 212 /** 213 * Returns the FrameType's class specifier, or null if no class was set or the receiver is not 214 * an object-based type. 215 * @return The FrameType's class specifier or null. 216 */ 217 public Class<?> getContentClass() { 218 return mClass; 219 } 220 221 /** 222 * Returns the FrameType's element id. 223 * @return The element id constant. 224 */ 225 public int getElementId() { 226 return mElementId; 227 } 228 229 /** 230 * Returns the number of bytes of the FrameType's element, or 0 if no such size can be 231 * determined. 232 * @return The number of bytes of the FrameType's element. 233 */ 234 public int getElementSize() { 235 switch (mElementId) { 236 case ELEMENT_INT8: 237 return 1; 238 case ELEMENT_INT16: 239 return 2; 240 case ELEMENT_INT32: 241 case ELEMENT_FLOAT32: 242 case ELEMENT_RGBA8888: 243 return 4; 244 case ELEMENT_INT64: 245 case ELEMENT_FLOAT64: 246 return 4; 247 default: 248 return 0; 249 } 250 } 251 252 /** 253 * Returns the access hints bit-mask of the FrameType. 254 * @return The access hints bit-mask of the FrameType. 255 */ 256 public int getAccessHints() { 257 return mAccessHints; 258 } 259 260 /** 261 * Returns the number of dimensions of the FrameType or -1 if no dimensions were set. 262 * @return The number of dimensions of the FrameType. 263 */ 264 public int getNumberOfDimensions() { 265 return mDimensions; 266 } 267 268 /** 269 * Returns true, if the FrameType is fully specified. 270 * 271 * A FrameType is fully specified if its element and dimensions are specified. 272 * 273 * @return true, if the FrameType is fully specified. 274 */ 275 public boolean isSpecified() { 276 return mElementId != ELEMENT_DONTCARE && mDimensions >= 0; 277 } 278 279 @Override 280 public boolean equals(Object object) { 281 if (object instanceof FrameType) { 282 FrameType type = (FrameType) object; 283 return mElementId == type.mElementId && mDimensions == type.mDimensions 284 && mAccessHints == type.mAccessHints && mClass == type.mClass; 285 } 286 return false; 287 } 288 289 @Override 290 public int hashCode() { 291 return mElementId ^ mDimensions ^ mAccessHints ^ mClass.hashCode(); 292 } 293 294 @Override 295 public String toString() { 296 String result = elementToString(mElementId, mClass) + "[" + mDimensions + "]"; 297 if ((mAccessHints & READ_CPU) != 0) { 298 result += "(rcpu)"; 299 } 300 if ((mAccessHints & READ_GPU) != 0) { 301 result += "(rgpu)"; 302 } 303 if ((mAccessHints & READ_ALLOCATION) != 0) { 304 result += "(ralloc)"; 305 } 306 if ((mAccessHints & WRITE_CPU) != 0) { 307 result += "(wcpu)"; 308 } 309 if ((mAccessHints & WRITE_GPU) != 0) { 310 result += "(wgpu)"; 311 } 312 if ((mAccessHints & WRITE_ALLOCATION) != 0) { 313 result += "(walloc)"; 314 } 315 return result; 316 } 317 318 String keyString() { 319 return keyValueForType(mElementId, mDimensions, mAccessHints, mClass); 320 } 321 322 static FrameType tryMerge(FrameType writer, FrameType reader) { 323 if (writer.mElementId == ELEMENT_DONTCARE) { 324 return reader; 325 } else if (reader.mElementId == ELEMENT_DONTCARE) { 326 return writer; 327 } else if (writer.mElementId == ELEMENT_OBJECT && reader.mElementId == ELEMENT_OBJECT) { 328 return tryMergeObjectTypes(writer, reader); 329 } else if (writer.mDimensions > 0 && writer.mElementId == reader.mElementId) { 330 return tryMergeBuffers(writer, reader); 331 } else { 332 return null; 333 } 334 } 335 336 static FrameType tryMergeObjectTypes(FrameType writer, FrameType reader) { 337 int dimensions = Math.max(writer.mDimensions, reader.mDimensions); 338 Class<?> mergedClass = mergeClasses(writer.mClass, reader.mClass); 339 boolean success = mergedClass != null || writer.mClass == null; 340 return success ? FrameType.fetchType(mergedClass, dimensions) : null; 341 } 342 343 static FrameType tryMergeBuffers(FrameType writer, FrameType reader) { 344 if (writer.mDimensions == reader.mDimensions) { 345 int accessHints = writer.mAccessHints | reader.mAccessHints; 346 return FrameType.fetchType(writer.mElementId, writer.mDimensions, accessHints); 347 } 348 return null; 349 } 350 351 static FrameType merge(FrameType writer, FrameType reader) { 352 FrameType result = tryMerge(writer, reader); 353 if (result == null) { 354 throw new RuntimeException( 355 "Incompatible types in connection: " + writer + " vs. " + reader + "!"); 356 } 357 return result; 358 } 359 360 private static String keyValueForType(int elemId, int dims, int hints, Class<?> clazz) { 361 return elemId + ":" + dims + ":" + hints + ":" + (clazz != null ? clazz.getName() : "0"); 362 } 363 364 private static String elementToString(int elemId, Class<?> clazz) { 365 switch (elemId) { 366 case ELEMENT_INT8: 367 return "int8"; 368 case ELEMENT_INT16: 369 return "int16"; 370 case ELEMENT_INT32: 371 return "int32"; 372 case ELEMENT_INT64: 373 return "int64"; 374 case ELEMENT_FLOAT32: 375 return "float32"; 376 case ELEMENT_FLOAT64: 377 return "float64"; 378 case ELEMENT_RGBA8888: 379 return "rgba8888"; 380 case ELEMENT_OBJECT: 381 return "<" + (clazz == null ? "*" : clazz.getSimpleName()) + ">"; 382 case ELEMENT_DONTCARE: 383 return "*"; 384 default: 385 return "?"; 386 } 387 } 388 389 private static Class<?> mergeClasses(Class<?> classA, Class<?> classB) { 390 // Return the most specialized class. 391 if (classA == null) { 392 return classB; 393 } else if (classB == null) { 394 return classA; 395 } else if (classA.isAssignableFrom(classB)) { 396 return classB; 397 } else if (classB.isAssignableFrom(classA)) { 398 return classA; 399 } else { 400 return null; 401 } 402 } 403 404 private static FrameType fetchType(int elementId, int dimensions, int accessHints) { 405 return fetchType(elementId, dimensions, accessHints, null); 406 } 407 408 private static FrameType fetchType(Class<?> clazz, int dimensions) { 409 return fetchType(ELEMENT_OBJECT, dimensions, ACCESS_UNKNOWN, clazz); 410 } 411 412 private static FrameType fetchType( 413 int elementId, int dimensions, int accessHints, Class<?> clazz) { 414 String typeKey = FrameType.keyValueForType(elementId, dimensions, accessHints, clazz); 415 FrameType type = mTypeCache.get(typeKey); 416 if (type == null) { 417 type = new FrameType(elementId, dimensions, accessHints, clazz); 418 mTypeCache.put(typeKey, type); 419 } 420 return type; 421 } 422 423 private FrameType(int elementId, int dimensions, int accessHints, Class<?> clazz) { 424 mElementId = elementId; 425 mDimensions = dimensions; 426 mClass = clazz; 427 mAccessHints = accessHints; 428 } 429 430 } 431